From 042e9a06e80ace5593f4dc43937139847712001b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 18:21:13 +0100 Subject: [PATCH 01/39] Added authorizations edit page for community --- .../community-authorizations.component.html | 3 + ...community-authorizations.component.spec.ts | 71 +++++++++++++++++++ .../community-authorizations.component.ts | 38 ++++++++++ .../edit-community-page.module.ts | 4 +- .../edit-community-page.routing.module.ts | 7 +- 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.html create mode 100644 src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts create mode 100644 src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.ts diff --git a/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.html b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.html new file mode 100644 index 0000000000..abf2f87141 --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts new file mode 100644 index 0000000000..d29aab630d --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts @@ -0,0 +1,71 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; + +import { cold } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; + +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { CommunityAuthorizationsComponent } from './community-authorizations.component'; +import { Collection } from '../../../core/shared/collection.model'; + +describe('CommunityAuthorizationsComponent', () => { + let comp: CommunityAuthorizationsComponent; + let fixture: ComponentFixture>; + + const community = Object.assign(new Collection(), { + uuid: 'community', + id: 'community', + _links: { + self: { href: 'community-selflink' } + } + }); + + const communityRD = createSuccessfulRemoteDataObject(community); + + const routeStub = { + parent: { + data: observableOf({ + dso: communityRD + }) + } + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule + ], + declarations: [CommunityAuthorizationsComponent], + providers: [ + { provide: ActivatedRoute, useValue: routeStub }, + ChangeDetectorRef, + CommunityAuthorizationsComponent, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommunityAuthorizationsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + comp = null; + fixture.destroy(); + }); + + it('should create', () => { + expect(comp).toBeTruthy(); + }); + + it('should init dso remote data properly', (done) => { + const expected = cold('(a|)', { a: communityRD }); + expect(comp.dsoRD$).toBeObservable(expected); + done(); + }); +}); 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 new file mode 100644 index 0000000000..17b29fd7e2 --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.ts @@ -0,0 +1,38 @@ +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'; + +@Component({ + selector: 'ds-community-authorizations', + templateUrl: './community-authorizations.component.html', +}) +/** + * Component that handles the community Authorizations + */ +export class CommunityAuthorizationsComponent implements OnInit { + + /** + * The initial DSO object + */ + public dsoRD$: Observable>; + + /** + * Initialize instance variables + * + * @param {ActivatedRoute} route + */ + constructor( + private route: ActivatedRoute + ) { + } + + /** + * Initialize the component, setting up the community + */ + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso)); + } +} 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 f9a1e11a14..9c4a97eb5e 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 @@ -7,6 +7,7 @@ import { EditCommunityPageComponent } from './edit-community-page.component'; import { CommunityCurateComponent } from './community-curate/community-curate.component'; import { CommunityMetadataComponent } from './community-metadata/community-metadata.component'; import { CommunityRolesComponent } from './community-roles/community-roles.component'; +import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component'; /** * Module that contains all components related to the Edit Community page administrator functionality @@ -22,7 +23,8 @@ import { CommunityRolesComponent } from './community-roles/community-roles.compo EditCommunityPageComponent, CommunityCurateComponent, CommunityMetadataComponent, - CommunityRolesComponent + CommunityRolesComponent, + CommunityAuthorizationsComponent ] }) export class EditCommunityPageModule { diff --git a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts index 3197e00829..9f34f4db04 100644 --- a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts +++ b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts @@ -1,4 +1,3 @@ -import { CommunityPageResolver } from '../community-page.resolver'; import { EditCommunityPageComponent } from './edit-community-page.component'; import { RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; @@ -6,6 +5,7 @@ import { CommunityMetadataComponent } from './community-metadata/community-metad import { CommunityRolesComponent } from './community-roles/community-roles.component'; import { CommunityCurateComponent } from './community-curate/community-curate.component'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component'; /** * Routing module that handles the routing for the Edit Community page administrator functionality @@ -44,6 +44,11 @@ import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.r path: 'curate', component: CommunityCurateComponent, data: { title: 'community.edit.tabs.curate.title', showBreadcrumbs: true } + }, + { + path: 'authorizations', + component: CommunityAuthorizationsComponent, + data: { title: 'community.edit.tabs.authorizations.title', showBreadcrumbs: true } } ] } From 29df9c609e9b5c4461d72ab1093f2d80dd4c5513 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 18:21:37 +0100 Subject: [PATCH 02/39] Added authorizations edit page for collections --- .../collection-authorizations.component.html | 3 + ...ollection-authorizations.component.spec.ts | 71 +++++++++++++++++++ .../collection-authorizations.component.ts | 40 +++++++++++ .../edit-collection-page.module.ts | 4 +- .../edit-collection-page.routing.module.ts | 8 ++- 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.html create mode 100644 src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts create mode 100644 src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts diff --git a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.html b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.html new file mode 100644 index 0000000000..fa66186ece --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts new file mode 100644 index 0000000000..6333bf5568 --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts @@ -0,0 +1,71 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; + +import { cold } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; + +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { CollectionAuthorizationsComponent } from './collection-authorizations.component'; +import { Collection } from '../../../core/shared/collection.model'; +import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; + +describe('CollectionAuthorizationsComponent', () => { + let comp: CollectionAuthorizationsComponent; + let fixture: ComponentFixture>; + + const collection = Object.assign(new Collection(), { + uuid: 'collection', + id: 'collection', + _links: { + self: { href: 'collection-selflink' } + } + }); + + const collectionRD = createSuccessfulRemoteDataObject(collection); + + const routeStub = { + parent: { + data: observableOf({ + dso: collectionRD + }) + } + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule + ], + declarations: [CollectionAuthorizationsComponent], + providers: [ + { provide: ActivatedRoute, useValue: routeStub }, + ChangeDetectorRef, + CollectionAuthorizationsComponent, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CollectionAuthorizationsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + comp = null; + fixture.destroy(); + }); + + it('should create', () => { + expect(comp).toBeTruthy(); + }); + + it('should init dso remote data properly', (done) => { + const expected = cold('(a|)', { a: collectionRD }); + expect(comp.dsoRD$).toBeObservable(expected); + done(); + }); +}); diff --git a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts new file mode 100644 index 0000000000..dd7f20729e --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; + +import { RemoteData } from '../../../core/data/remote-data'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; + +@Component({ + selector: 'ds-collection-authorizations', + templateUrl: './collection-authorizations.component.html', +}) +/** + * Component that handles the Collection Authorizations + */ +export class CollectionAuthorizationsComponent implements OnInit { + + /** + * The initial DSO object + */ + public dsoRD$: Observable>; + + /** + * Initialize instance variables + * + * @param {ActivatedRoute} route + */ + constructor( + private route: ActivatedRoute + ) { + } + + /** + * Initialize the component, setting up the collection + */ + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso)); + } +} 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 f442aae4d6..6ca0948512 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 @@ -8,6 +8,7 @@ import { CollectionPageModule } from '../collection-page.module'; import { CollectionRolesComponent } from './collection-roles/collection-roles.component'; import { CollectionCurateComponent } from './collection-curate/collection-curate.component'; import { CollectionSourceComponent } from './collection-source/collection-source.component'; +import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component'; /** * Module that contains all components related to the Edit Collection page administrator functionality @@ -24,7 +25,8 @@ import { CollectionSourceComponent } from './collection-source/collection-source CollectionMetadataComponent, CollectionRolesComponent, CollectionCurateComponent, - CollectionSourceComponent + CollectionSourceComponent, + CollectionAuthorizationsComponent ] }) export class EditCollectionPageModule { diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts index 0569de9cd9..30172090a6 100644 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts @@ -5,6 +5,7 @@ import { CollectionMetadataComponent } from './collection-metadata/collection-me import { CollectionRolesComponent } from './collection-roles/collection-roles.component'; import { CollectionSourceComponent } from './collection-source/collection-source.component'; import { CollectionCurateComponent } from './collection-curate/collection-curate.component'; +import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; /** @@ -49,7 +50,12 @@ import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.r path: 'curate', component: CollectionCurateComponent, data: { title: 'collection.edit.tabs.curate.title', showBreadcrumbs: true } - } + }, + { + path: 'authorizations', + component: CollectionAuthorizationsComponent, + data: { title: 'collection.edit.tabs.authorizations.title', showBreadcrumbs: true } + }, ] } ]) From 8b6f99e2bec17303f37b00d8e567f683c1d71b55 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 18:22:42 +0100 Subject: [PATCH 03/39] Added i18n labels for communities/collections authorizations edit page --- src/assets/i18n/en.json5 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e7335694f1..4f633cbae2 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -674,6 +674,10 @@ "collection.edit.tabs.curate.title": "Collection Edit - Curate", + "collection.edit.tabs.authorizations.head": "Authorizations", + + "collection.edit.tabs.authorizations.title": "Collection Edit - Authorizations", + "collection.edit.tabs.metadata.head": "Edit Metadata", "collection.edit.tabs.metadata.title": "Collection Edit - Metadata", @@ -859,6 +863,10 @@ "community.edit.tabs.roles.title": "Community Edit - Roles", + "community.edit.tabs.authorizations.head": "Authorizations", + + "community.edit.tabs.authorizations.title": "Community Edit - Authorizations", + "comcol-role.edit.no-group": "None", @@ -2569,6 +2577,10 @@ "resource-policies.add.for.item": "Add a new Item policy", + "resource-policies.add.for.community": "Add a new Community policy", + + "resource-policies.add.for.collection": "Add a new Collection policy", + "resource-policies.create.page.heading": "Create new resource policy for ", "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.", @@ -2651,6 +2663,10 @@ "resource-policies.table.headers.title.for.item": "Policies for Item", + "resource-policies.table.headers.title.for.community": "Policies for Community", + + "resource-policies.table.headers.title.for.collection": "Policies for Collection", + "search.description": "", From 317b61b3a422d62e7551c47a0b7b7127f9ccf2c0 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 19:13:50 +0100 Subject: [PATCH 04/39] moved ResourcePolicyEditComponent and ResourcePolicyCreateComponent to shared.module.ts --- src/app/+item-page/edit-item-page/edit-item-page.module.ts | 6 +----- src/app/shared/shared.module.ts | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) 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 c3bd9eb924..17010acab4 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 @@ -30,8 +30,6 @@ import { PaginatedDragAndDropBitstreamListComponent } from './item-bitstreams/it import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component'; -import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; -import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe'; /** @@ -71,9 +69,7 @@ import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe'; ItemMoveComponent, ItemEditBitstreamDragHandleComponent, VirtualMetadataComponent, - ItemAuthorizationsComponent, - ResourcePolicyEditComponent, - ResourcePolicyCreateComponent, + ItemAuthorizationsComponent ], providers: [ BundleDataService, diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 5f35ed4ceb..768a3a680c 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -210,6 +210,8 @@ import { CollectionDropdownComponent } from './collection-dropdown/collection-dr 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 { ResourcePolicyEditComponent } from './resource-policies/edit/resource-policy-edit.component'; +import { ResourcePolicyCreateComponent } from './resource-policies/create/resource-policy-create.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -396,6 +398,8 @@ const COMPONENTS = [ ImpersonateNavbarComponent, ResourcePoliciesComponent, ResourcePolicyFormComponent, + ResourcePolicyEditComponent, + ResourcePolicyCreateComponent, EpersonGroupListComponent, EpersonSearchBoxComponent, GroupSearchBoxComponent, From e0c4b836d9ed1121a6eae45998b9554c97af783f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 19:24:32 +0100 Subject: [PATCH 05/39] Add route to create/edit a community policies --- ...community-authorizations.component.spec.ts | 8 ++-- .../community-authorizations.component.ts | 2 +- .../edit-community-page.routing.module.ts | 37 ++++++++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts index d29aab630d..dd10bdce29 100644 --- a/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts +++ b/src/app/+community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts @@ -27,9 +27,11 @@ describe('CommunityAuthorizationsComponent', () => { const routeStub = { parent: { - data: observableOf({ - dso: communityRD - }) + parent: { + data: observableOf({ + dso: communityRD + }) + } } }; 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 17b29fd7e2..8b241af667 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 @@ -33,6 +33,6 @@ export class CommunityAuthorizationsComponent impl * Initialize the component, setting up the community */ ngOnInit(): void { - this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso)); + this.dsoRD$ = this.route.parent.parent.data.pipe(first(), map((data) => data.dso)); } } diff --git a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts index 9f34f4db04..440fa01a30 100644 --- a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts +++ b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts @@ -6,6 +6,10 @@ import { CommunityRolesComponent } from './community-roles/community-roles.compo import { CommunityCurateComponent } from './community-curate/community-curate.component'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { CommunityAuthorizationsComponent } from './community-authorizations/community-authorizations.component'; +import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver'; +import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; +import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; +import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; /** * Routing module that handles the routing for the Edit Community page administrator functionality @@ -45,15 +49,46 @@ import { CommunityAuthorizationsComponent } from './community-authorizations/com component: CommunityCurateComponent, data: { title: 'community.edit.tabs.curate.title', showBreadcrumbs: true } }, - { + /*{ path: 'authorizations', component: CommunityAuthorizationsComponent, data: { title: 'community.edit.tabs.authorizations.title', showBreadcrumbs: true } + },*/ + { + path: 'authorizations', + data: { showBreadcrumbs: true }, + children: [ + { + path: 'create', + resolve: { + resourcePolicyTarget: ResourcePolicyTargetResolver + }, + component: ResourcePolicyCreateComponent, + data: { title: 'resource-policies.create.page.title' } + }, + { + path: 'edit', + resolve: { + resourcePolicy: ResourcePolicyResolver + }, + component: ResourcePolicyEditComponent, + data: { title: 'resource-policies.edit.page.title' } + }, + { + path: '', + component: CommunityAuthorizationsComponent, + data: { title: 'community.edit.tabs.authorizations.title', showBreadcrumbs: true, hideReturnButton: true } + } + ] } ] } ]) ], + providers: [ + ResourcePolicyResolver, + ResourcePolicyTargetResolver + ] }) export class EditCommunityPageRoutingModule { From 926cce8286f420f97675d01e28f26e2ba8cc06c9 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 9 Nov 2020 19:24:43 +0100 Subject: [PATCH 06/39] Add route to create/edit a collection policies --- ...ollection-authorizations.component.spec.ts | 8 ++-- .../collection-authorizations.component.ts | 2 +- .../edit-collection-page.routing.module.ts | 39 ++++++++++++++++++- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts index 6333bf5568..39f1ee6551 100644 --- a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts +++ b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts @@ -27,9 +27,11 @@ describe('CollectionAuthorizationsComponent', () => { const routeStub = { parent: { - data: observableOf({ - dso: collectionRD - }) + parent: { + data: observableOf({ + dso: collectionRD + }) + } } }; diff --git a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts index dd7f20729e..d1b59a0c90 100644 --- a/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts +++ b/src/app/+collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.ts @@ -35,6 +35,6 @@ export class CollectionAuthorizationsComponent imp * Initialize the component, setting up the collection */ ngOnInit(): void { - this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso)); + this.dsoRD$ = this.route.parent.parent.data.pipe(first(), map((data) => data.dso)); } } diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts index 30172090a6..818f064104 100644 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts @@ -7,6 +7,10 @@ import { CollectionSourceComponent } from './collection-source/collection-source import { CollectionCurateComponent } from './collection-curate/collection-curate.component'; import { CollectionAuthorizationsComponent } from './collection-authorizations/collection-authorizations.component'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver'; +import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; +import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver'; +import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; /** * Routing module that handles the routing for the Edit Collection page administrator functionality @@ -51,14 +55,45 @@ import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.r component: CollectionCurateComponent, data: { title: 'collection.edit.tabs.curate.title', showBreadcrumbs: true } }, - { +/* { path: 'authorizations', component: CollectionAuthorizationsComponent, data: { title: 'collection.edit.tabs.authorizations.title', showBreadcrumbs: true } - }, + },*/ + { + path: 'authorizations', + data: { showBreadcrumbs: true }, + children: [ + { + path: 'create', + resolve: { + resourcePolicyTarget: ResourcePolicyTargetResolver + }, + component: ResourcePolicyCreateComponent, + data: { title: 'resource-policies.create.page.title' } + }, + { + path: 'edit', + resolve: { + resourcePolicy: ResourcePolicyResolver + }, + component: ResourcePolicyEditComponent, + data: { title: 'resource-policies.edit.page.title' } + }, + { + path: '', + component: CollectionAuthorizationsComponent, + data: { title: 'collection.edit.tabs.authorizations.title', showBreadcrumbs: true } + } + ] + } ] } ]) + ], + providers: [ + ResourcePolicyResolver, + ResourcePolicyTargetResolver ] }) export class EditCollectionPageRoutingModule { From db7514fd2561f906e1b6989860bb99e81435b8f2 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 Nov 2020 09:53:30 +0100 Subject: [PATCH 07/39] Fix issue where the creation of resource policies failed even if successfully --- .../create/resource-policy-create.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/shared/resource-policies/create/resource-policy-create.component.ts b/src/app/shared/resource-policies/create/resource-policy-create.component.ts index 017e67841c..2873ba71e5 100644 --- a/src/app/shared/resource-policies/create/resource-policy-create.component.ts +++ b/src/app/shared/resource-policies/create/resource-policy-create.component.ts @@ -101,7 +101,9 @@ export class ResourcePolicyCreateComponent implements OnInit { first((response: RemoteData) => !response.isResponsePending) ).subscribe((responseRD: RemoteData) => { this.processing$.next(false); - if (responseRD.hasSucceeded) { + // NOTE Currently due to a bug a successful 201 response has failed + // TODO review it when https://github.com/DSpace/dspace-angular/issues/739 is fixed + if (responseRD.hasSucceeded || responseRD.statusCode === 201) { this.notificationsService.success(null, this.translate.get('resource-policies.create.page.success.content')); this.redirectToAuthorizationsPage(); } else { From 0072486f962a87ea31fc46e59b5b89b8191b5f83 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 10 Nov 2020 09:53:44 +0100 Subject: [PATCH 08/39] Fix issue where the edit of resource policies failed even if successfully --- .../edit/resource-policy-edit.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts b/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts index 3c933dc607..dce33ea122 100644 --- a/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts +++ b/src/app/shared/resource-policies/edit/resource-policy-edit.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; -import { first, map, take } from 'rxjs/operators'; +import { map, take } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { ResourcePolicyService } from '../../../core/resource-policy/resource-policy.service'; @@ -12,6 +12,7 @@ import { ResourcePolicy } from '../../../core/resource-policy/models/resource-po import { ResourcePolicyEvent } from '../form/resource-policy-form.component'; import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-policy.resource-type'; import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../+item-page/edit-item-page/edit-item-page.routing-paths'; +import { getSucceededRemoteWithNotEmptyData } from '../../../core/shared/operators'; @Component({ selector: 'ds-resource-policy-edit', @@ -88,7 +89,8 @@ export class ResourcePolicyEditComponent implements OnInit { _links: this.resourcePolicy._links }); this.resourcePolicyService.update(updatedObject).pipe( - first((response: RemoteData) => !response.isResponsePending) + getSucceededRemoteWithNotEmptyData(), + take(1) ).subscribe((responseRD: RemoteData) => { this.processing$.next(false); if (responseRD.hasSucceeded) { From 6f9e8bd3bf4a7606e9ac8523748833f5efb1bda3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 17 Nov 2020 13:15:24 +0100 Subject: [PATCH 09/39] 74606: Ensure the correct operators are used for filter queries --- .../search-authority-filter.component.ts | 18 -------- .../search-facet-option.component.ts | 17 ++----- ...earch-facet-selected-option.component.html | 2 +- .../search-facet-selected-option.component.ts | 15 +------ .../search-facet-filter.component.ts | 5 ++- .../search-text-filter.component.ts | 21 +++++++++ .../search-labels/search-labels.component.ts | 13 +++++- src/app/shared/search/search.utils.ts | 44 +++++++++++++++++++ 8 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 src/app/shared/search/search.utils.ts 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 5dc930f67f..7a20fd5ff0 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,9 +1,7 @@ import { Component, OnInit } from '@angular/core'; - import { FilterType } from '../../../filter-type.model'; import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { renderFacetFor } from '../search-filter-type-decorator'; -import { FacetValue } from '../../../facet-value.model'; @Component({ selector: 'ds-search-authority-filter', @@ -17,20 +15,4 @@ import { FacetValue } from '../../../facet-value.model'; */ @renderFacetFor(FilterType.authority) export class SearchAuthorityFilterComponent extends SearchFacetFilterComponent implements OnInit { - - /** - * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved - * Retrieve facet value from search link - */ - protected getFacetValue(facet: FacetValue): string { - const search = facet._links.search.href; - const hashes = search.slice(search.indexOf('?') + 1).split('&'); - const params = {}; - hashes.map((hash) => { - const [key, val] = hash.split('='); - params[key] = decodeURIComponent(val) - }); - - return params[this.filterConfig.paramName]; - } } 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 04e810edda..dac98e7627 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 @@ -10,6 +10,7 @@ import { SearchConfigurationService } from '../../../../../../core/shared/search import { hasValue } from '../../../../../empty.util'; import { FilterType } from '../../../../filter-type.model'; import { currentPath } from '../../../../../utils/route.utils'; +import { getFacetValueForType } from '../../../../search.utils'; @Component({ selector: 'ds-search-facet-option', @@ -102,7 +103,7 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { */ private updateAddParams(selectedValues: FacetValue[]): void { this.addQueryParams = { - [this.filterConfig.paramName]: [...selectedValues.map((facetValue: FacetValue) => facetValue.label), this.getFacetValue()], + [this.filterConfig.paramName]: [...selectedValues.map((facetValue: FacetValue) => getFacetValueForType(facetValue, this.filterConfig)), this.getFacetValue()], page: 1 }; } @@ -112,19 +113,7 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { * Retrieve facet value related to facet type */ private getFacetValue(): string { - if (this.filterConfig.type === FilterType.authority) { - const search = this.filterValue._links.search.href; - const hashes = search.slice(search.indexOf('?') + 1).split('&'); - const params = {}; - hashes.map((hash) => { - const [key, val] = hash.split('='); - params[key] = decodeURIComponent(val) - }); - - return params[this.filterConfig.paramName]; - } else { - return this.filterValue.value; - } + return getFacetValueForType(this.filterValue, this.filterConfig); } /** diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html index a27a5d3d86..4bcfc02966 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html @@ -3,6 +3,6 @@ [queryParams]="removeQueryParams" queryParamsHandling="merge"> - {{ 'search.filters.' + filterConfig.name + '.' + selectedValue.value | translate: {default: selectedValue.value} }} + {{ 'search.filters.' + filterConfig.name + '.' + selectedValue.value | translate: {default: selectedValue.label} }} 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 f58a903b0c..e54b228f7f 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 @@ -9,6 +9,7 @@ import { SearchConfigurationService } from '../../../../../../core/shared/search import { FacetValue } from '../../../../facet-value.model'; import { FilterType } from '../../../../filter-type.model'; import { currentPath } from '../../../../../utils/route.utils'; +import { getFacetValueForType } from '../../../../search.utils'; @Component({ selector: 'ds-search-facet-selected-option', @@ -101,19 +102,7 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy { * Retrieve facet value related to facet type */ private getFacetValue(facetValue: FacetValue): string { - if (this.filterConfig.type === FilterType.authority) { - const search = facetValue._links.search.href; - const hashes = search.slice(search.indexOf('?') + 1).split('&'); - const params = {}; - hashes.map((hash) => { - const [key, val] = hash.split('='); - params[key] = decodeURIComponent(val) - }); - - return params[this.filterConfig.paramName]; - } else { - return facetValue.value; - } + return getFacetValueForType(facetValue, this.filterConfig); } /** 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 df0c53f543..af4a92781d 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 @@ -25,6 +25,7 @@ import { InputSuggestion } from '../../../../input-suggestions/input-suggestions import { SearchOptions } from '../../../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'; @Component({ selector: 'ds-search-facet-filter', @@ -148,7 +149,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { if (hasValue(fValue)) { return fValue; } - return Object.assign(new FacetValue(), { label: value, value: value }); + return Object.assign(new FacetValue(), { label: stripOperatorFromFilterValue(value), value: value }); }); }) ); @@ -303,7 +304,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { * Retrieve facet value */ protected getFacetValue(facet: FacetValue): string { - return facet.value; + return getFacetValueForType(facet, this.filterConfig); } /** 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 678cbbfdd5..379c73157f 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 @@ -5,6 +5,10 @@ import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { renderFacetFor } from '../search-filter-type-decorator'; +import { + addOperatorToFilterValue, + stripOperatorFromFilterValue +} from '../../../search.utils'; /** * This component renders a simple item page. @@ -24,4 +28,21 @@ import { renderFacetFor } from '../search-filter-type-decorator'; */ @renderFacetFor(FilterType.text) export class SearchTextFilterComponent extends SearchFacetFilterComponent implements OnInit { + /** + * Submits a new active custom value to the filter from the input field + * Overwritten method from parent component, adds the "query" operator to the received data before passing it on + * @param data The string from the input field + */ + onSubmit(data: any) { + super.onSubmit(addOperatorToFilterValue(data, 'query')); + } + + /** + * On click, set the input's value to the clicked data + * Overwritten method from parent component, strips any operator from the received data before passing it on + * @param data The value of the option that was clicked + */ + onClick(data: any) { + super.onClick(stripOperatorFromFilterValue(data)); + } } diff --git a/src/app/shared/search/search-labels/search-labels.component.ts b/src/app/shared/search/search-labels/search-labels.component.ts index 154b888269..2322c80acf 100644 --- a/src/app/shared/search/search-labels/search-labels.component.ts +++ b/src/app/shared/search/search-labels/search-labels.component.ts @@ -3,6 +3,8 @@ import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.c import { Observable } from 'rxjs'; import { Params, Router } from '@angular/router'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { map } from 'rxjs/operators'; +import { stripOperatorFromFilterValue } from '../search.utils'; @Component({ selector: 'ds-search-labels', @@ -30,6 +32,15 @@ export class SearchLabelsComponent { constructor( protected router: Router, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) { - this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters(); + this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters().pipe( + map((params) => { + const labels = {}; + Object.keys(params) + .forEach((key) => { + labels[key] = [...params[key].map((value) => stripOperatorFromFilterValue(value))]; + }); + return labels; + }) + ); } } diff --git a/src/app/shared/search/search.utils.ts b/src/app/shared/search/search.utils.ts new file mode 100644 index 0000000000..05a9711435 --- /dev/null +++ b/src/app/shared/search/search.utils.ts @@ -0,0 +1,44 @@ +import { FacetValue } from './facet-value.model'; +import { SearchFilterConfig } from './search-filter-config.model'; +import { isNotEmpty } from '../empty.util'; + +/** + * Get a facet's value by matching its parameter in the search href, this will include the operator of the facet value + * If the {@link FacetValue} doesn't contain a search link, its raw value will be returned as a fallback + * @param facetValue + * @param searchFilterConfig + */ +export function getFacetValueForType(facetValue: FacetValue, searchFilterConfig: SearchFilterConfig): string { + const regex = new RegExp(`[?|&]${searchFilterConfig.paramName}=(${facetValue.value}[^&]*)`, 'g'); + if (isNotEmpty(facetValue._links)) { + const values = regex.exec(facetValue._links.search.href); + if (isNotEmpty(values)) { + return values[1]; + } + } + return addOperatorToFilterValue(facetValue.value, 'equals'); +} + +/** + * Strip the operator from a filter value + * Warning: This expects the value to end with an operator, otherwise it might strip unwanted content + * @param value + */ +export function stripOperatorFromFilterValue(value: string) { + if (value.lastIndexOf(',') > -1) { + return value.substring(0, value.lastIndexOf(',')); + } + return value; +} + +/** + * Add an operator to a string + * @param value + * @param operator + */ +export function addOperatorToFilterValue(value: string, operator: string) { + if (!value.endsWith(`,${operator}`)) { + return `${value},${operator}`; + } + return value; +} From 05406e6919b2d55ec1e061ef84d2836d64de31a9 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 17 Nov 2020 15:43:05 +0100 Subject: [PATCH 10/39] 74606: Search Utils test cases + test fixes --- .../search-facet-option.component.spec.ts | 4 +- .../search-facet-filter.component.spec.ts | 2 +- src/app/shared/search/search.utils.spec.ts | 52 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/app/shared/search/search.utils.spec.ts 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 4f85411d85..ba6eceeede 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 @@ -130,7 +130,7 @@ describe('SearchFacetOptionComponent', () => { comp.addQueryParams = {}; (comp as any).updateAddParams(selectedValues); expect(comp.addQueryParams).toEqual({ - [mockFilterConfig.paramName]: [value1, value.value], + [mockFilterConfig.paramName]: [`${value1},${operator}`, value.value + ',equals'], page: 1 }); }); @@ -145,7 +145,7 @@ describe('SearchFacetOptionComponent', () => { comp.addQueryParams = {}; (comp as any).updateAddParams(selectedValues); expect(comp.addQueryParams).toEqual({ - [mockAuthorityFilterConfig.paramName]: [value1, `${value2},${operator}`], + [mockAuthorityFilterConfig.paramName]: [value1 + ',equals', `${value2},${operator}`], page: 1 }); }); 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 376aa7ba3a..54548b0ddf 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 @@ -208,7 +208,7 @@ describe('SearchFacetFilterComponent', () => { it('should call navigate on the router with the right searchlink and parameters', () => { expect(router.navigate).toHaveBeenCalledWith(searchUrl.split('/'), { - queryParams: { [mockFilterConfig.paramName]: [...selectedValues, testValue] }, + queryParams: { [mockFilterConfig.paramName]: [...selectedValues.map((value) => `${value},equals`), testValue] }, queryParamsHandling: 'merge' }); }); diff --git a/src/app/shared/search/search.utils.spec.ts b/src/app/shared/search/search.utils.spec.ts new file mode 100644 index 0000000000..a14085c2d3 --- /dev/null +++ b/src/app/shared/search/search.utils.spec.ts @@ -0,0 +1,52 @@ +import { FacetValue } from './facet-value.model'; +import { SearchFilterConfig } from './search-filter-config.model'; +import { addOperatorToFilterValue, getFacetValueForType, stripOperatorFromFilterValue } from './search.utils'; + +describe('Search Utils', () => { + describe('getFacetValueForType', () => { + let facetValueWithSearchHref: FacetValue; + let facetValueWithoutSearchHref: FacetValue; + let searchFilterConfig: SearchFilterConfig; + + beforeEach(() => { + facetValueWithSearchHref = Object.assign(new FacetValue(), { + value: 'Value with search href', + _links: { + search: { + href: 'rest/api/search?f.otherFacet=Other facet value,operator&f.facetName=Value with search href,operator' + } + } + }); + facetValueWithoutSearchHref = Object.assign(new FacetValue(), { + value: 'Value without search href' + }); + searchFilterConfig = Object.assign(new SearchFilterConfig(), { + name: 'facetName' + }); + }); + + it('should retrieve the correct value from the search href', () => { + expect(getFacetValueForType(facetValueWithSearchHref, searchFilterConfig)).toEqual('Value with search href,operator'); + }); + + it('should return the facet value with an equals operator by default', () => { + expect(getFacetValueForType(facetValueWithoutSearchHref, searchFilterConfig)).toEqual('Value without search href,equals'); + }); + }); + + describe('stripOperatorFromFilterValue', () => { + it('should strip the operator from the value', () => { + expect(stripOperatorFromFilterValue('value,operator')).toEqual('value'); + }); + }); + + describe('addOperatorToFilterValue', () => { + it('should add the operator to the value', () => { + expect(addOperatorToFilterValue('value', 'operator')).toEqual('value,operator'); + }); + + it('shouldn\'t add the operator to the value if it already contains the operator', () => { + expect(addOperatorToFilterValue('value,operator', 'operator')).toEqual('value,operator'); + }); + }); +}); From ab755d3fd99c2ef9b0e1095e7573102a12684519 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 18 Nov 2020 14:05:36 +0100 Subject: [PATCH 11/39] 74612: 403 page + redirect to login on unauthorized --- .../collection-page.component.ts | 8 +-- .../community-page.component.ts | 8 +-- .../full/full-item-page.component.spec.ts | 11 +++- .../full/full-item-page.component.ts | 5 +- .../simple/item-page.component.spec.ts | 10 +++- .../+item-page/simple/item-page.component.ts | 6 ++- src/app/app-routing-paths.ts | 6 +++ src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 6 ++- .../core/services/server-response.service.ts | 4 ++ src/app/core/shared/operators.spec.ts | 51 ++++++++++++++++--- src/app/core/shared/operators.ts | 24 ++++++--- src/app/forbidden/forbidden.component.html | 10 ++++ src/app/forbidden/forbidden.component.scss | 0 src/app/forbidden/forbidden.component.ts | 32 ++++++++++++ .../detail/process-detail.component.spec.ts | 9 +++- .../detail/process-detail.component.ts | 8 +-- ...llection-statistics-page.component.spec.ts | 7 +++ .../collection-statistics-page.component.ts | 3 ++ ...ommunity-statistics-page.component.spec.ts | 7 +++ .../community-statistics-page.component.ts | 3 ++ .../item-statistics-page.component.spec.ts | 7 +++ .../item-statistics-page.component.ts | 3 ++ .../site-statistics-page.component.spec.ts | 7 +++ .../site-statistics-page.component.ts | 3 ++ .../statistics-page.component.ts | 6 ++- src/assets/i18n/en.json5 | 8 +++ 27 files changed, 221 insertions(+), 35 deletions(-) create mode 100644 src/app/forbidden/forbidden.component.html create mode 100644 src/app/forbidden/forbidden.component.scss create mode 100644 src/app/forbidden/forbidden.component.ts diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index a390bda4cb..c99ba34936 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -17,13 +17,14 @@ import { DSpaceObjectType } from '../core/shared/dspace-object-type.model'; import { Item } from '../core/shared/item.model'; import { getSucceededRemoteData, - redirectOn404Or401, + redirectOn4xx, toDSpaceObjectListRD } from '../core/shared/operators'; import { fadeIn, fadeInOut } from '../shared/animations/fade'; import { hasNoValue, hasValue, isNotEmpty } from '../shared/empty.util'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { AuthService } from '../core/auth/auth.service'; @Component({ selector: 'ds-collection-page', @@ -51,7 +52,8 @@ export class CollectionPageComponent implements OnInit { private searchService: SearchService, private metadata: MetadataService, private route: ActivatedRoute, - private router: Router + private router: Router, + private authService: AuthService, ) { this.paginationConfig = new PaginationComponentOptions(); this.paginationConfig.id = 'collection-page-pagination'; @@ -63,7 +65,7 @@ export class CollectionPageComponent implements OnInit { ngOnInit(): void { this.collectionRD$ = this.route.data.pipe( map((data) => data.dso as RemoteData), - redirectOn404Or401(this.router), + redirectOn4xx(this.router, this.authService), take(1) ); this.logoRD$ = this.collectionRD$.pipe( diff --git a/src/app/+community-page/community-page.component.ts b/src/app/+community-page/community-page.component.ts index 7c93b1bf4e..f65da14644 100644 --- a/src/app/+community-page/community-page.component.ts +++ b/src/app/+community-page/community-page.component.ts @@ -13,7 +13,8 @@ import { MetadataService } from '../core/metadata/metadata.service'; import { fadeInOut } from '../shared/animations/fade'; import { hasValue } from '../shared/empty.util'; -import { redirectOn404Or401 } from '../core/shared/operators'; +import { redirectOn4xx } from '../core/shared/operators'; +import { AuthService } from '../core/auth/auth.service'; @Component({ selector: 'ds-community-page', @@ -39,7 +40,8 @@ export class CommunityPageComponent implements OnInit { private communityDataService: CommunityDataService, private metadata: MetadataService, private route: ActivatedRoute, - private router: Router + private router: Router, + private authService: AuthService, ) { } @@ -47,7 +49,7 @@ export class CommunityPageComponent implements OnInit { ngOnInit(): void { this.communityRD$ = this.route.data.pipe( map((data) => data.dso as RemoteData), - redirectOn404Or401(this.router) + redirectOn4xx(this.router, this.authService) ); this.logoRD$ = this.communityRD$.pipe( map((rd: RemoteData) => rd.payload), diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts index 0512ea2fef..6cba9cb8ff 100644 --- a/src/app/+item-page/full/full-item-page.component.spec.ts +++ b/src/app/+item-page/full/full-item-page.component.spec.ts @@ -21,6 +21,7 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { AuthService } from '../../core/auth/auth.service'; const mockItem: Item = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), @@ -46,7 +47,14 @@ describe('FullItemPageComponent', () => { let comp: FullItemPageComponent; let fixture: ComponentFixture; + let authService: AuthService; + beforeEach(async(() => { + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ loader: { @@ -58,7 +66,8 @@ describe('FullItemPageComponent', () => { providers: [ {provide: ActivatedRoute, useValue: routeStub}, {provide: ItemDataService, useValue: {}}, - {provide: MetadataService, useValue: metadataServiceStub} + {provide: MetadataService, useValue: metadataServiceStub}, + { provide: AuthService, useValue: authService }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index b2a42b7c6f..741f1e76a7 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -15,6 +15,7 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; +import { AuthService } from '../../core/auth/auth.service'; /** * This component renders a simple item page. @@ -35,8 +36,8 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit { metadata$: Observable; - constructor(route: ActivatedRoute, router: Router, items: ItemDataService, metadataService: MetadataService) { - super(route, router, items, metadataService); + constructor(route: ActivatedRoute, router: Router, items: ItemDataService, metadataService: MetadataService, authService: AuthService) { + super(route, router, items, metadataService, authService); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts index 6c26e75908..1695b0d3b9 100644 --- a/src/app/+item-page/simple/item-page.component.spec.ts +++ b/src/app/+item-page/simple/item-page.component.spec.ts @@ -20,6 +20,7 @@ import { createFailedRemoteDataObject$, createPendingRemoteDataObject$, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { AuthService } from '../../core/auth/auth.service'; const mockItem: Item = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), @@ -30,6 +31,7 @@ const mockItem: Item = Object.assign(new Item(), { describe('ItemPageComponent', () => { let comp: ItemPageComponent; let fixture: ComponentFixture; + let authService: AuthService; const mockMetadataService = { /* tslint:disable:no-empty */ @@ -41,6 +43,11 @@ describe('ItemPageComponent', () => { }); beforeEach(async(() => { + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ loader: { @@ -53,7 +60,8 @@ describe('ItemPageComponent', () => { {provide: ActivatedRoute, useValue: mockRoute}, {provide: ItemDataService, useValue: {}}, {provide: MetadataService, useValue: mockMetadataService}, - {provide: Router, useValue: {}} + {provide: Router, useValue: {}}, + { provide: AuthService, useValue: authService }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 87d2294ff9..03912d258e 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -11,8 +11,9 @@ import { Item } from '../../core/shared/item.model'; import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; -import { redirectOn404Or401 } from '../../core/shared/operators'; +import { redirectOn4xx } from '../../core/shared/operators'; import { ViewMode } from '../../core/shared/view-mode.model'; +import { AuthService } from '../../core/auth/auth.service'; /** * This component renders a simple item page. @@ -48,6 +49,7 @@ export class ItemPageComponent implements OnInit { private router: Router, private items: ItemDataService, private metadataService: MetadataService, + private authService: AuthService, ) { } /** @@ -56,7 +58,7 @@ export class ItemPageComponent implements OnInit { ngOnInit(): void { this.itemRD$ = this.route.data.pipe( map((data) => data.item as RemoteData), - redirectOn404Or401(this.router) + redirectOn4xx(this.router, this.authService) ); this.metadataService.processRemoteData(this.itemRD$); } diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 2b57a1957c..39f3da8ed3 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -61,6 +61,12 @@ export function getUnauthorizedRoute() { return `/${UNAUTHORIZED_PATH}`; } +export const FORBIDDEN_PATH = '403'; + +export function getForbiddenRoute() { + return `/${FORBIDDEN_PATH}`; +} + export const PAGE_NOT_FOUND_PATH = '404'; export function getPageNotFoundRoute() { diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index ecb27efbb3..8eb380e364 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -14,7 +14,7 @@ import { PROFILE_MODULE_PATH, ADMIN_MODULE_PATH, BITSTREAM_MODULE_PATH, - INFO_MODULE_PATH + INFO_MODULE_PATH, FORBIDDEN_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'; @@ -22,6 +22,7 @@ import { ITEM_MODULE_PATH } from './+item-page/item-page-routing-paths'; import { ReloadGuard } from './core/reload/reload.guard'; import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard'; import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard'; +import { ForbiddenComponent } from './forbidden/forbidden.component'; @NgModule({ imports: [ @@ -69,6 +70,7 @@ import { SiteRegisterGuard } from './core/data/feature-authorization/feature-aut { path: 'processes', loadChildren: './process-page/process-page.module#ProcessPageModule', canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: INFO_MODULE_PATH, loadChildren: './info/info.module#InfoModule' }, { path: UNAUTHORIZED_PATH, component: UnauthorizedComponent }, + { path: FORBIDDEN_PATH, component: ForbiddenComponent }, { path: 'statistics', loadChildren: './statistics-page/statistics-page-routing.module#StatisticsPageRoutingModule', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f1cdd5f2e5..013336a506 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -42,6 +42,7 @@ import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component'; import { environment } from '../environments/environment'; import { BrowserModule } from '@angular/platform-browser'; import { UnauthorizedComponent } from './unauthorized/unauthorized.component'; +import { ForbiddenComponent } from './forbidden/forbidden.component'; export function getBase() { return environment.ui.nameSpace; @@ -116,6 +117,9 @@ const DECLARATIONS = [ NotificationComponent, NotificationsBoardComponent, SearchNavbarComponent, + BreadcrumbsComponent, + UnauthorizedComponent, + ForbiddenComponent, ]; const EXPORTS = [ @@ -133,8 +137,6 @@ const EXPORTS = [ ], declarations: [ ...DECLARATIONS, - BreadcrumbsComponent, - UnauthorizedComponent, ], exports: [ ...EXPORTS diff --git a/src/app/core/services/server-response.service.ts b/src/app/core/services/server-response.service.ts index 10da2a3379..adf2ecf4d2 100644 --- a/src/app/core/services/server-response.service.ts +++ b/src/app/core/services/server-response.service.ts @@ -24,6 +24,10 @@ export class ServerResponseService { return this.setStatus(401, message) } + setForbidden(message = 'Forbidden'): this { + return this.setStatus(403, message) + } + setNotFound(message = 'Not found'): this { return this.setStatus(404, message) } diff --git a/src/app/core/shared/operators.spec.ts b/src/app/core/shared/operators.spec.ts index 8acf5ea860..cc25aeec10 100644 --- a/src/app/core/shared/operators.spec.ts +++ b/src/app/core/shared/operators.spec.ts @@ -14,7 +14,7 @@ import { getResourceLinksFromResponse, getResponseFromEntry, getSucceededRemoteData, - redirectOn404Or401 + redirectOn4xx } from './operators'; import { RemoteData } from '../data/remote-data'; import { RemoteDataError } from '../data/remote-data-error'; @@ -200,39 +200,74 @@ describe('Core Module - RxJS Operators', () => { }); }); - describe('redirectOn404Or401', () => { + describe('redirectOn4xx', () => { let router; + let authService; + beforeEach(() => { router = jasmine.createSpyObj('router', ['navigateByUrl']); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); }); it('should call navigateByUrl to a 404 page, when the remote data contains a 404 error', () => { const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(404, 'Not Found', 'Object was not found')); - observableOf(testRD).pipe(redirectOn404Or401(router)).subscribe(); + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); expect(router.navigateByUrl).toHaveBeenCalledWith('/404', { skipLocationChange: true }); }); + it('should call navigateByUrl to a 403 page, when the remote data contains a 403 error', () => { + const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(403, 'Forbidden', 'Forbidden access')); + + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); + expect(router.navigateByUrl).toHaveBeenCalledWith('/403', { skipLocationChange: true }); + }); + it('should call navigateByUrl to a 401 page, when the remote data contains a 401 error', () => { const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(401, 'Unauthorized', 'The current user is unauthorized')); - observableOf(testRD).pipe(redirectOn404Or401(router)).subscribe(); + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); expect(router.navigateByUrl).toHaveBeenCalledWith('/401', { skipLocationChange: true }); }); - it('should not call navigateByUrl to a 404 or 401 page, when the remote data contains another error than a 404 or 401', () => { + it('should not call navigateByUrl to a 404, 403 or 401 page, when the remote data contains another error than a 404, 403 or 401', () => { const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(500, 'Server Error', 'Something went wrong')); - observableOf(testRD).pipe(redirectOn404Or401(router)).subscribe(); + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); expect(router.navigateByUrl).not.toHaveBeenCalled(); }); - it('should not call navigateByUrl to a 404 or 401 page, when the remote data contains no error', () => { + it('should not call navigateByUrl to a 404, 403 or 401 page, when the remote data contains no error', () => { const testRD = createSuccessfulRemoteDataObject(undefined); - observableOf(testRD).pipe(redirectOn404Or401(router)).subscribe(); + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); expect(router.navigateByUrl).not.toHaveBeenCalled(); }); + + describe('when the user is not authenticated', () => { + beforeEach(() => { + (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false)); + }); + + it('should set the redirect url and navigate to login when the remote data contains a 401 error', () => { + const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(401, 'Unauthorized', 'The current user is unauthorized')); + + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); + expect(authService.setRedirectUrl).toHaveBeenCalled(); + expect(router.navigateByUrl).toHaveBeenCalledWith('login'); + }); + + it('should set the redirect url and navigate to login when the remote data contains a 403 error', () => { + const testRD = createFailedRemoteDataObject(undefined, new RemoteDataError(403, 'Forbidden', 'Forbidden access')); + + observableOf(testRD).pipe(redirectOn4xx(router, authService)).subscribe(); + expect(authService.setRedirectUrl).toHaveBeenCalled(); + expect(router.navigateByUrl).toHaveBeenCalledWith('login'); + }); + }); }); describe('getResponseFromEntry', () => { diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 29e41907e1..5d8d3609a2 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -13,8 +13,9 @@ import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { BrowseDefinition } from './browse-definition.model'; import { DSpaceObject } from './dspace-object.model'; -import { getPageNotFoundRoute, getUnauthorizedRoute } from '../../app-routing-paths'; +import { getForbiddenRoute, getPageNotFoundRoute, getUnauthorizedRoute } from '../../app-routing-paths'; import { getEndUserAgreementPath } from '../../info/info-routing-paths'; +import { AuthService } from '../auth/auth.service'; /** * This file contains custom RxJS operators that can be used in multiple places @@ -174,18 +175,29 @@ export const getAllSucceededRemoteListPayload = () => * Operator that checks if a remote data object returned a 401 or 404 error * When it does contain such an error, it will redirect the user to the related error page, without altering the current URL * @param router The router used to navigate to a new page + * @param authService Service to check if the user is authenticated */ -export const redirectOn404Or401 = (router: Router) => +export const redirectOn4xx = (router: Router, authService: AuthService) => (source: Observable>): Observable> => - source.pipe( - tap((rd: RemoteData) => { + observableCombineLatest(source, authService.isAuthenticated()).pipe( + map(([rd, isAuthenticated]: [RemoteData, boolean]) => { if (rd.hasFailed) { if (rd.error.statusCode === 404) { router.navigateByUrl(getPageNotFoundRoute(), {skipLocationChange: true}); - } else if (rd.error.statusCode === 401) { - router.navigateByUrl(getUnauthorizedRoute(), {skipLocationChange: true}); + } else if (rd.error.statusCode === 403 || rd.error.statusCode === 401) { + if (isAuthenticated) { + if (rd.error.statusCode === 403) { + router.navigateByUrl(getForbiddenRoute(), {skipLocationChange: true}); + } else if (rd.error.statusCode === 401) { + router.navigateByUrl(getUnauthorizedRoute(), {skipLocationChange: true}); + } + } else { + authService.setRedirectUrl(router.url); + router.navigateByUrl('login'); + } } } + return rd; })); /** diff --git a/src/app/forbidden/forbidden.component.html b/src/app/forbidden/forbidden.component.html new file mode 100644 index 0000000000..067aad0048 --- /dev/null +++ b/src/app/forbidden/forbidden.component.html @@ -0,0 +1,10 @@ +
+

403

+

{{"403.forbidden" | translate}}

+
+

{{"403.help" | translate}}

+
+

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

+
diff --git a/src/app/forbidden/forbidden.component.scss b/src/app/forbidden/forbidden.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/forbidden/forbidden.component.ts b/src/app/forbidden/forbidden.component.ts new file mode 100644 index 0000000000..b622a0f066 --- /dev/null +++ b/src/app/forbidden/forbidden.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthService } from '../core/auth/auth.service'; +import { ServerResponseService } from '../core/services/server-response.service'; + +/** + * This component representing the `Forbidden` DSpace page. + */ +@Component({ + selector: 'ds-forbidden', + templateUrl: './forbidden.component.html', + styleUrls: ['./forbidden.component.scss'] +}) +export class ForbiddenComponent implements OnInit { + + /** + * Initialize instance variables + * + * @param {AuthService} authService + * @param {ServerResponseService} responseService + */ + constructor(private authService: AuthService, private responseService: ServerResponseService) { + this.responseService.setForbidden(); + } + + /** + * Remove redirect url from the state + */ + ngOnInit(): void { + this.authService.clearRedirectUrl(); + } + +} diff --git a/src/app/process-page/detail/process-detail.component.spec.ts b/src/app/process-page/detail/process-detail.component.spec.ts index dff481fdc6..2006609f01 100644 --- a/src/app/process-page/detail/process-detail.component.spec.ts +++ b/src/app/process-page/detail/process-detail.component.spec.ts @@ -15,6 +15,7 @@ import { ProcessDataService } from '../../core/data/processes/process-data.servi import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { AuthService } from '../../core/auth/auth.service'; describe('ProcessDetailComponent', () => { let component: ProcessDetailComponent; @@ -22,6 +23,7 @@ describe('ProcessDetailComponent', () => { let processService: ProcessDataService; let nameService: DSONameService; + let authService: AuthService; let process: Process; let fileName: string; @@ -65,6 +67,10 @@ describe('ProcessDetailComponent', () => { nameService = jasmine.createSpyObj('nameService', { getName: fileName }); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); } beforeEach(async(() => { @@ -75,7 +81,8 @@ describe('ProcessDetailComponent', () => { providers: [ { provide: ActivatedRoute, useValue: { data: observableOf({ process: createSuccessfulRemoteDataObject(process) }) } }, { provide: ProcessDataService, useValue: processService }, - { provide: DSONameService, useValue: nameService } + { provide: DSONameService, useValue: nameService }, + { provide: AuthService, useValue: authService }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/process-page/detail/process-detail.component.ts b/src/app/process-page/detail/process-detail.component.ts index c4610b70e9..dd89724f0e 100644 --- a/src/app/process-page/detail/process-detail.component.ts +++ b/src/app/process-page/detail/process-detail.component.ts @@ -4,12 +4,13 @@ import { Observable } from 'rxjs/internal/Observable'; import { RemoteData } from '../../core/data/remote-data'; import { Process } from '../processes/process.model'; import { map, switchMap } from 'rxjs/operators'; -import { getFirstSucceededRemoteDataPayload, redirectOn404Or401 } from '../../core/shared/operators'; +import { getFirstSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators'; import { AlertType } from '../../shared/alert/aletr-type'; import { ProcessDataService } from '../../core/data/processes/process-data.service'; import { PaginatedList } from '../../core/data/paginated-list'; import { Bitstream } from '../../core/shared/bitstream.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { AuthService } from '../../core/auth/auth.service'; @Component({ selector: 'ds-process-detail', @@ -39,7 +40,8 @@ export class ProcessDetailComponent implements OnInit { constructor(protected route: ActivatedRoute, protected router: Router, protected processService: ProcessDataService, - protected nameService: DSONameService) { + protected nameService: DSONameService, + protected authService: AuthService) { } /** @@ -49,7 +51,7 @@ export class ProcessDetailComponent implements OnInit { ngOnInit(): void { this.processRD$ = this.route.data.pipe( map((data) => data.process as RemoteData), - redirectOn404Or401(this.router) + redirectOn4xx(this.router, this.authService) ); this.filesRD$ = this.processRD$.pipe( diff --git a/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.spec.ts b/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.spec.ts index 110757670c..cf6a8b7cff 100644 --- a/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.spec.ts +++ b/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.spec.ts @@ -14,6 +14,7 @@ import { SharedModule } from '../../shared/shared.module'; import { CommonModule } from '@angular/common'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; +import { AuthService } from '../../core/auth/auth.service'; describe('CollectionStatisticsPageComponent', () => { @@ -59,6 +60,11 @@ describe('CollectionStatisticsPageComponent', () => { getName: () => observableOf('test dso name'), }; + const authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -75,6 +81,7 @@ describe('CollectionStatisticsPageComponent', () => { { provide: UsageReportService, useValue: usageReportService }, { provide: DSpaceObjectDataService, useValue: {} }, { provide: DSONameService, useValue: nameService }, + { provide: AuthService, useValue: authService }, ], }) .compileComponents(); diff --git a/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts b/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts index 05f4641d81..41ee47ff88 100644 --- a/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts +++ b/src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts @@ -4,6 +4,7 @@ import { UsageReportService } from '../../core/statistics/usage-report-data.serv import { ActivatedRoute , Router} from '@angular/router'; import { Collection } from '../../core/shared/collection.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { AuthService } from '../../core/auth/auth.service'; /** * Component representing the statistics page for a collection. @@ -30,12 +31,14 @@ export class CollectionStatisticsPageComponent extends StatisticsPageComponent { @@ -59,6 +60,11 @@ describe('CommunityStatisticsPageComponent', () => { getName: () => observableOf('test dso name'), }; + const authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -75,6 +81,7 @@ describe('CommunityStatisticsPageComponent', () => { { provide: UsageReportService, useValue: usageReportService }, { provide: DSpaceObjectDataService, useValue: {} }, { provide: DSONameService, useValue: nameService }, + { provide: AuthService, useValue: authService }, ], }) .compileComponents(); diff --git a/src/app/statistics-page/community-statistics-page/community-statistics-page.component.ts b/src/app/statistics-page/community-statistics-page/community-statistics-page.component.ts index 65d5fe88e5..1a1c8fdf0e 100644 --- a/src/app/statistics-page/community-statistics-page/community-statistics-page.component.ts +++ b/src/app/statistics-page/community-statistics-page/community-statistics-page.component.ts @@ -4,6 +4,7 @@ import { UsageReportService } from '../../core/statistics/usage-report-data.serv import { ActivatedRoute, Router } from '@angular/router'; import { Community } from '../../core/shared/community.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { AuthService } from '../../core/auth/auth.service'; /** * Component representing the statistics page for a community. @@ -30,12 +31,14 @@ export class CommunityStatisticsPageComponent extends StatisticsPageComponent { @@ -59,6 +60,11 @@ describe('ItemStatisticsPageComponent', () => { getName: () => observableOf('test dso name'), }; + const authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -75,6 +81,7 @@ describe('ItemStatisticsPageComponent', () => { { provide: UsageReportService, useValue: usageReportService }, { provide: DSpaceObjectDataService, useValue: {} }, { provide: DSONameService, useValue: nameService }, + { provide: AuthService, useValue: authService }, ], }) .compileComponents(); diff --git a/src/app/statistics-page/item-statistics-page/item-statistics-page.component.ts b/src/app/statistics-page/item-statistics-page/item-statistics-page.component.ts index fb9ced4520..69a8361a46 100644 --- a/src/app/statistics-page/item-statistics-page/item-statistics-page.component.ts +++ b/src/app/statistics-page/item-statistics-page/item-statistics-page.component.ts @@ -4,6 +4,7 @@ import { UsageReportService } from '../../core/statistics/usage-report-data.serv import { ActivatedRoute, Router } from '@angular/router'; import { Item } from '../../core/shared/item.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { AuthService } from '../../core/auth/auth.service'; /** * Component representing the statistics page for an item. @@ -31,12 +32,14 @@ export class ItemStatisticsPageComponent extends StatisticsPageComponent { protected router: Router, protected usageReportService: UsageReportService, protected nameService: DSONameService, + protected authService: AuthService ) { super( route, router, usageReportService, nameService, + authService, ); } } diff --git a/src/app/statistics-page/site-statistics-page/site-statistics-page.component.spec.ts b/src/app/statistics-page/site-statistics-page/site-statistics-page.component.spec.ts index 6f2247b433..1a3babc570 100644 --- a/src/app/statistics-page/site-statistics-page/site-statistics-page.component.spec.ts +++ b/src/app/statistics-page/site-statistics-page/site-statistics-page.component.spec.ts @@ -14,6 +14,7 @@ import { CommonModule } from '@angular/common'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { SiteDataService } from '../../core/data/site-data.service'; +import { AuthService } from '../../core/auth/auth.service'; describe('SiteStatisticsPageComponent', () => { @@ -55,6 +56,11 @@ describe('SiteStatisticsPageComponent', () => { })) }; + const authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + setRedirectUrl: {} + }); + TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), @@ -72,6 +78,7 @@ describe('SiteStatisticsPageComponent', () => { { provide: DSpaceObjectDataService, useValue: {} }, { provide: DSONameService, useValue: nameService }, { provide: SiteDataService, useValue: siteService }, + { provide: AuthService, useValue: authService }, ], }) .compileComponents(); diff --git a/src/app/statistics-page/site-statistics-page/site-statistics-page.component.ts b/src/app/statistics-page/site-statistics-page/site-statistics-page.component.ts index fd1319723c..59f1be330c 100644 --- a/src/app/statistics-page/site-statistics-page/site-statistics-page.component.ts +++ b/src/app/statistics-page/site-statistics-page/site-statistics-page.component.ts @@ -6,6 +6,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Site } from '../../core/shared/site.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { switchMap } from 'rxjs/operators'; +import { AuthService } from '../../core/auth/auth.service'; /** * Component representing the site-wide statistics page. @@ -30,12 +31,14 @@ export class SiteStatisticsPageComponent extends StatisticsPageComponent { protected usageReportService: UsageReportService, protected nameService: DSONameService, protected siteService: SiteDataService, + protected authService: AuthService, ) { super( route, router, usageReportService, nameService, + authService, ); } diff --git a/src/app/statistics-page/statistics-page/statistics-page.component.ts b/src/app/statistics-page/statistics-page/statistics-page.component.ts index 31cb74eb08..6fa10b437c 100644 --- a/src/app/statistics-page/statistics-page/statistics-page.component.ts +++ b/src/app/statistics-page/statistics-page/statistics-page.component.ts @@ -4,10 +4,11 @@ import { UsageReportService } from '../../core/statistics/usage-report-data.serv import { map, switchMap } from 'rxjs/operators'; import { UsageReport } from '../../core/statistics/models/usage-report.model'; import { RemoteData } from '../../core/data/remote-data'; -import { getRemoteDataPayload, getSucceededRemoteData, redirectOn404Or401 } from '../../core/shared/operators'; +import { getRemoteDataPayload, getSucceededRemoteData, redirectOn4xx } from '../../core/shared/operators'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { ActivatedRoute, Router } from '@angular/router'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { AuthService } from '../../core/auth/auth.service'; /** * Class representing an abstract statistics page component. @@ -36,6 +37,7 @@ export abstract class StatisticsPageComponent implements protected router: Router, protected usageReportService: UsageReportService, protected nameService: DSONameService, + protected authService: AuthService, ) { } @@ -55,7 +57,7 @@ export abstract class StatisticsPageComponent implements protected getScope$(): Observable { return this.route.data.pipe( map((data) => data.scope as RemoteData), - redirectOn404Or401(this.router), + redirectOn4xx(this.router, this.authService), getSucceededRemoteData(), getRemoteDataPayload(), ); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index de4c3eb552..fb14806734 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -8,6 +8,14 @@ + "403.help": "You don't have permission to access this page. You can use the button below to get back to the home page.", + + "403.link.home-page": "Take me to the home page", + + "403.forbidden": "forbidden", + + + "404.help": "We can't find the page you're looking for. The page may have been moved or deleted. You can use the button below to get back to the home page. ", "404.link.home-page": "Take me to the home page", From cb6381f7ee9e4f835b16a207c06e92c4adaa20a5 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 18 Nov 2020 17:59:22 +0100 Subject: [PATCH 12/39] 74606: Move stripping of suggestions to SearchFacetFilter --- .../search-facet-filter.component.ts | 2 +- .../search-text-filter/search-text-filter.component.ts | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) 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 af4a92781d..9ff8663a65 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 @@ -288,7 +288,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { return rd.payload.page.map((facet) => { return { displayValue: this.getDisplayValue(facet, data), - value: this.getFacetValue(facet) + value: stripOperatorFromFilterValue(this.getFacetValue(facet)) } }) } 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 379c73157f..b2b44ce219 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 @@ -7,7 +7,6 @@ import { import { renderFacetFor } from '../search-filter-type-decorator'; import { addOperatorToFilterValue, - stripOperatorFromFilterValue } from '../../../search.utils'; /** @@ -36,13 +35,4 @@ export class SearchTextFilterComponent extends SearchFacetFilterComponent implem onSubmit(data: any) { super.onSubmit(addOperatorToFilterValue(data, 'query')); } - - /** - * On click, set the input's value to the clicked data - * Overwritten method from parent component, strips any operator from the received data before passing it on - * @param data The value of the option that was clicked - */ - onClick(data: any) { - super.onClick(stripOperatorFromFilterValue(data)); - } } From c38cee7561c72a4ee07ab298ee109fc7304efd60 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 17 Nov 2020 11:36:39 +0100 Subject: [PATCH 13/39] 74572: Separate components for untyped items --- ...in-workflow-grid-element.component.spec.ts | 6 +- src/app/+item-page/item-page.module.ts | 5 +- .../publication/publication.component.spec.ts | 2 +- .../publication/publication.component.ts | 1 - .../untyped-item/untyped-item.component.html | 67 +++++++++++ .../untyped-item/untyped-item.component.scss | 1 + .../untyped-item.component.spec.ts | 112 ++++++++++++++++++ .../untyped-item/untyped-item.component.ts | 20 ++++ src/app/core/shared/item.model.ts | 5 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-list-element.component.html | 2 +- ...-search-result-list-element.component.html | 2 +- ...-search-result-list-element.component.html | 2 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-grid-element.component.html | 2 +- ...arch-result-grid-element.component.spec.ts | 2 +- ...-search-result-list-element.component.html | 2 +- ...-search-result-list-element.component.html | 2 +- ...-search-result-list-element.component.html | 2 +- ...-object-component-loader.component.spec.ts | 8 +- .../item/item-grid-element.component.html | 4 + .../item-grid-element.component.scss} | 0 .../item-grid-element.component.spec.ts} | 12 +- .../item-grid-element.component.ts} | 8 +- .../publication-grid-element.component.html | 4 - ...-search-result-grid-element.component.html | 1 + ...-search-result-grid-element.component.html | 1 + ...search-result-grid-element.component.html} | 2 +- ...search-result-grid-element.component.scss} | 0 ...rch-result-grid-element.component.spec.ts} | 4 +- ...m-search-result-grid-element.component.ts} | 8 +- .../item/item-list-element.component.html | 1 + .../item-list-element.component.scss} | 0 .../item-list-element.component.spec.ts} | 12 +- .../item-list-element.component.ts} | 8 +- .../publication-list-element.component.html | 1 - .../item-type-badge.component.html | 3 - .../item-type-badge.component.ts | 16 --- .../item-list-preview.component.html | 2 +- .../item-list-preview.component.spec.ts | 2 +- ...-search-result-list-element.component.html | 1 + ...-search-result-list-element.component.html | 1 + ...search-result-list-element.component.html} | 2 +- ...search-result-list-element.component.scss} | 0 ...rch-result-list-element.component.spec.ts} | 14 +-- ...m-search-result-list-element.component.ts} | 8 +- .../type-badge/type-badge.component.html | 3 + .../type-badge.component.spec.ts} | 16 +-- .../type-badge/type-badge.component.ts | 41 +++++++ src/app/shared/shared.module.ts | 22 ++-- src/assets/i18n/en.json5 | 30 +++++ 59 files changed, 375 insertions(+), 115 deletions(-) create mode 100644 src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html create mode 100644 src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.scss create mode 100644 src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts create mode 100644 src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.ts create mode 100644 src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.html rename src/app/shared/object-grid/item-grid-element/item-types/{publication/publication-grid-element.component.scss => item/item-grid-element.component.scss} (100%) rename src/app/shared/object-grid/item-grid-element/item-types/{publication/publication-grid-element.component.spec.ts => item/item-grid-element.component.spec.ts} (85%) rename src/app/shared/object-grid/item-grid-element/item-types/{publication/publication-grid-element.component.ts => item/item-grid-element.component.ts} (74%) delete mode 100644 src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.html rename src/app/shared/object-grid/search-result-grid-element/item-search-result/{publication/publication-search-result-grid-element.component.html => item/item-search-result-grid-element.component.html} (96%) rename src/app/shared/object-grid/search-result-grid-element/item-search-result/{publication/publication-search-result-grid-element.component.scss => item/item-search-result-grid-element.component.scss} (100%) rename src/app/shared/object-grid/search-result-grid-element/item-search-result/{publication/publication-search-result-grid-element.component.spec.ts => item/item-search-result-grid-element.component.spec.ts} (95%) rename src/app/shared/object-grid/search-result-grid-element/item-search-result/{publication/publication-search-result-grid-element.component.ts => item/item-search-result-grid-element.component.ts} (71%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.html rename src/app/shared/object-list/item-list-element/item-types/{publication/publication-list-element.component.scss => item/item-list-element.component.scss} (100%) rename src/app/shared/object-list/item-list-element/item-types/{publication/publication-list-element.component.spec.ts => item/item-list-element.component.spec.ts} (83%) rename src/app/shared/object-list/item-list-element/item-types/{publication/publication-list-element.component.ts => item/item-list-element.component.ts} (72%) delete mode 100644 src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html delete mode 100644 src/app/shared/object-list/item-type-badge/item-type-badge.component.html delete mode 100644 src/app/shared/object-list/item-type-badge/item-type-badge.component.ts rename src/app/shared/object-list/search-result-list-element/item-search-result/item-types/{publication/publication-search-result-list-element.component.html => item/item-search-result-list-element.component.html} (95%) rename src/app/shared/object-list/search-result-list-element/item-search-result/item-types/{publication/publication-search-result-list-element.component.scss => item/item-search-result-list-element.component.scss} (100%) rename src/app/shared/object-list/search-result-list-element/item-search-result/item-types/{publication/publication-search-result-list-element.component.spec.ts => item/item-search-result-list-element.component.spec.ts} (90%) rename src/app/shared/object-list/search-result-list-element/item-search-result/item-types/{publication/publication-search-result-list-element.component.ts => item/item-search-result-list-element.component.ts} (70%) create mode 100644 src/app/shared/object-list/type-badge/type-badge.component.html rename src/app/shared/object-list/{item-type-badge/item-type-badge.component.spec.ts => type-badge/type-badge.component.spec.ts} (83%) create mode 100644 src/app/shared/object-list/type-badge/type-badge.component.ts diff --git a/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts b/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts index 6436f2d873..bb5abb15d6 100644 --- a/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts +++ b/src/app/+admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts @@ -12,7 +12,7 @@ import { WorkflowItem } from '../../../../../core/submission/models/workflowitem import { LinkService } from '../../../../../core/cache/builders/link.service'; import { followLink } from '../../../../../shared/utils/follow-link-config.model'; import { Item } from '../../../../../core/shared/item.model'; -import { PublicationGridElementComponent } from '../../../../../shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component'; +import { ItemGridElementComponent } from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component'; import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; @@ -43,7 +43,7 @@ describe('WorkflowItemAdminWorkflowGridElementComponent', () => { init(); TestBed.configureTestingModule( { - declarations: [WorkflowItemSearchResultAdminWorkflowGridElementComponent, PublicationGridElementComponent, ListableObjectDirective], + declarations: [WorkflowItemSearchResultAdminWorkflowGridElementComponent, ItemGridElementComponent, ListableObjectDirective], imports: [ NoopAnimationsModule, TranslateModule.forRoot(), @@ -60,7 +60,7 @@ describe('WorkflowItemAdminWorkflowGridElementComponent', () => { }) .overrideComponent(WorkflowItemSearchResultAdminWorkflowGridElementComponent, { set: { - entryComponents: [PublicationGridElementComponent] + entryComponents: [ItemGridElementComponent] } }) .compileComponents(); diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index 4c3a64e117..29ee9ecc59 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -30,6 +30,7 @@ import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.c import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { AbstractIncrementalListComponent } from './simple/abstract-incremental-list/abstract-incremental-list.component'; +import { UntypedItemComponent } from './simple/item-types/untyped-item/untyped-item.component'; @NgModule({ imports: [ @@ -54,6 +55,7 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental- CollectionsComponent, FullFileSectionComponent, PublicationComponent, + UntypedItemComponent, RelatedItemsComponent, ItemComponent, GenericItemPageFieldComponent, @@ -75,7 +77,8 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental- TabbedRelatedEntitiesSearchComponent ], entryComponents: [ - PublicationComponent + PublicationComponent, + UntypedItemComponent ] }) export class ItemPageModule { diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts index 1d340e2e73..0f34d50fa2 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts @@ -36,7 +36,7 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('PublicationComponent', () => { +describe('UntypedItemComponent', () => { let comp: PublicationComponent; let fixture: ComponentFixture; diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index f01d0f72d3..a731205df3 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -9,7 +9,6 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh */ @listableObjectComponent('Publication', ViewMode.StandalonePage) -@listableObjectComponent(Item, ViewMode.StandalonePage) @Component({ selector: 'ds-publication', styleUrls: ['./publication.component.scss'], diff --git a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html new file mode 100644 index 0000000000..d65f20f030 --- /dev/null +++ b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -0,0 +1,67 @@ +
+

+ {{'item.page.titleprefix' | translate}} +

+
+ +
+
+
+
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + +
+
diff --git a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.scss b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts new file mode 100644 index 0000000000..83585dead5 --- /dev/null +++ b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts @@ -0,0 +1,112 @@ +import { HttpClient } from '@angular/common/http'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { Store } from '@ngrx/store'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; +import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service'; +import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { RelationshipService } from '../../../../core/data/relationship.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Bitstream } from '../../../../core/shared/bitstream.model'; +import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; +import { Item } from '../../../../core/shared/item.model'; +import { MetadataMap } from '../../../../core/shared/metadata.models'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { UUIDService } from '../../../../core/shared/uuid.service'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { NotificationsService } from '../../../../shared/notifications/notifications.service'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { createRelationshipsObservable } from '../shared/item.component.spec'; +import { UntypedItemComponent } from './untyped-item.component'; + +const mockItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), + metadata: new MetadataMap(), + relationships: createRelationshipsObservable() +}); + +describe('UntypedItemComponent', () => { + let comp: UntypedItemComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + const mockBitstreamDataService = { + getThumbnailFor(item: Item): Observable> { + return createSuccessfulRemoteDataObject$(new Bitstream()); + } + }; + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + })], + declarations: [UntypedItemComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + { provide: ItemDataService, useValue: {} }, + { provide: TruncatableService, useValue: {} }, + { provide: RelationshipService, useValue: {} }, + { provide: ObjectCacheService, useValue: {} }, + { provide: UUIDService, useValue: {} }, + { provide: Store, useValue: {} }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: CommunityDataService, useValue: {} }, + { provide: HALEndpointService, useValue: {} }, + { provide: NotificationsService, useValue: {} }, + { provide: HttpClient, useValue: {} }, + { provide: DSOChangeAnalyzer, useValue: {} }, + { provide: DefaultChangeAnalyzer, useValue: {} }, + { provide: BitstreamDataService, useValue: mockBitstreamDataService }, + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(UntypedItemComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(UntypedItemComponent); + comp = fixture.componentInstance; + comp.object = mockItem; + fixture.detectChanges(); + })); + + it('should contain a component to display the date', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-date-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the author', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-author-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the abstract', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-abstract-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the uri', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-uri-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the collections', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-collections')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + +}); diff --git a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.ts b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.ts new file mode 100644 index 0000000000..3183c42a28 --- /dev/null +++ b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.ts @@ -0,0 +1,20 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { ItemComponent } from '../shared/item.component'; +import { ViewMode } from '../../../../core/shared/view-mode.model'; +import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; + +/** + * Component that represents a publication Item page + */ + +@listableObjectComponent(Item, ViewMode.StandalonePage) +@Component({ + selector: 'ds-untyped-item', + styleUrls: ['./untyped-item.component.scss'], + templateUrl: './untyped-item.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UntypedItemComponent extends ItemComponent { + +} diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 7f6cf9fe13..bc3703427d 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -1,7 +1,6 @@ import { autoserialize, autoserializeAs, deserialize, inheritSerialization } from 'cerialize'; import { Observable } from 'rxjs/internal/Observable'; import { isEmpty } from '../../shared/empty.util'; -import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; import { link, typedObject } from '../cache/builders/build-decorators'; import { PaginatedList } from '../data/paginated-list'; @@ -105,9 +104,9 @@ export class Item extends DSpaceObject implements ChildHALResource { * Method that returns as which type of object this object should be rendered */ getRenderTypes(): Array> { - let entityType = this.firstMetadataValue('relationship.type'); + const entityType = this.firstMetadataValue('relationship.type'); if (isEmpty(entityType)) { - entityType = DEFAULT_ENTITY_TYPE; + return super.getRenderTypes(); } return [entityType, ...super.getRenderTypes()]; } diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html index 1ae772a3c3..2ac997bf4d 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html @@ -17,7 +17,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.spec.ts index f65fd97cb0..315df8bdc4 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; import { JournalIssueSearchResultGridElementComponent } from './journal-issue-search-result-grid-element.component'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html index 75d29781b7..09ca2cb755 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html @@ -17,7 +17,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.spec.ts index a7d5acdd00..b28561451c 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; import { JournalVolumeSearchResultGridElementComponent } from './journal-volume-search-result-grid-element.component'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html index 8c7e5c2f44..584ff37a87 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html @@ -17,7 +17,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.spec.ts index 180b7f4600..9ced5a9e71 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; import { JournalSearchResultGridElementComponent } from './journal-search-result-grid-element.component'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 45482972ec..3ddf3611d1 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -1,4 +1,4 @@ - + + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.spec.ts b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.spec.ts index 8dec83295e..5580b98b1e 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.spec.ts @@ -4,7 +4,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; import { OrgUnitSearchResultGridElementComponent } from './org-unit-search-result-grid-element.component'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html index 1b45c7c4f9..c8470b5725 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html @@ -17,7 +17,7 @@
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.spec.ts b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.spec.ts index f56d6c76af..d31d5b092c 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; import { PersonSearchResultGridElementComponent } from './person-search-result-grid-element.component'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html index ac3c3ea453..95fc98e06f 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html @@ -17,7 +17,7 @@
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.spec.ts b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.spec.ts index 5a25eea955..4cc93ea0bb 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.spec.ts @@ -4,7 +4,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { PaginatedList } from '../../../../../core/data/paginated-list'; import { PageInfo } from '../../../../../core/shared/page-info.model'; import { ProjectSearchResultGridElementComponent } from './project-search-result-grid-element.component'; -import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec'; +import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 5f570cb021..c0d98ccf43 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -1,4 +1,4 @@ - +
+ - + diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts index e2d9b17f1c..de510af0bb 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts @@ -6,7 +6,7 @@ import { GenericConstructor } from '../../../../core/shared/generic-constructor' import { Context } from '../../../../core/shared/context.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import * as listableObjectDecorators from './listable-object.decorator'; -import { PublicationListElementComponent } from '../../../object-list/item-list-element/item-types/publication/publication-list-element.component'; +import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component'; import { ListableObjectDirective } from './listable-object.directive'; import { spyOnExported } from '../../../testing/utils.test'; @@ -27,13 +27,13 @@ describe('ListableObjectComponentLoaderComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [], - declarations: [ListableObjectComponentLoaderComponent, PublicationListElementComponent, ListableObjectDirective], + declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective], schemas: [NO_ERRORS_SCHEMA], providers: [ComponentFactoryResolver] }).overrideComponent(ListableObjectComponentLoaderComponent, { set: { changeDetection: ChangeDetectionStrategy.Default, - entryComponents: [PublicationListElementComponent] + entryComponents: [ItemListElementComponent] } }).compileComponents(); })); @@ -45,7 +45,7 @@ describe('ListableObjectComponentLoaderComponent', () => { comp.object = new TestType(); comp.viewMode = testViewMode; comp.context = testContext; - spyOnExported(listableObjectDecorators, 'getListableObjectComponent').and.returnValue(PublicationListElementComponent); + spyOnExported(listableObjectDecorators, 'getListableObjectComponent').and.returnValue(ItemListElementComponent); fixture.detectChanges(); })); diff --git a/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.html b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.html new file mode 100644 index 0000000000..434c4b5b5c --- /dev/null +++ b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.scss b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.scss similarity index 100% rename from src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.scss rename to src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.scss diff --git a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec.ts b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts similarity index 85% rename from src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec.ts rename to src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts index 84f926bbe3..8400023ff4 100644 --- a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts @@ -4,7 +4,7 @@ import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { PublicationGridElementComponent } from './publication-grid-element.component'; +import { ItemGridElementComponent } from './item-grid-element.component'; import { of as observableOf } from 'rxjs/internal/observable/of'; import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; @@ -41,7 +41,7 @@ const mockItem = Object.assign(new Item(), { } }); -describe('PublicationGridElementComponent', () => { +describe('ItemGridElementComponent', () => { let comp; let fixture; @@ -52,18 +52,18 @@ describe('PublicationGridElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [NoopAnimationsModule], - declarations: [PublicationGridElementComponent, TruncatePipe], + declarations: [ItemGridElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(PublicationGridElementComponent, { + }).overrideComponent(ItemGridElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(PublicationGridElementComponent); + fixture = TestBed.createComponent(ItemGridElementComponent); comp = fixture.componentInstance; })); @@ -74,7 +74,7 @@ describe('PublicationGridElementComponent', () => { }); it(`should contain a PublicationGridElementComponent`, () => { - const publicationGridElement = fixture.debugElement.query(By.css(`ds-publication-search-result-grid-element`)); + const publicationGridElement = fixture.debugElement.query(By.css(`ds-item-search-result-grid-element`)); expect(publicationGridElement).not.toBeNull(); }); }); diff --git a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.ts b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.ts similarity index 74% rename from src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.ts rename to src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.ts index 8450150dcb..c8a58c5a89 100644 --- a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.ts +++ b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.ts @@ -8,13 +8,13 @@ import { Item } from '../../../../../core/shared/item.model'; @listableObjectComponent('Publication', ViewMode.GridElement) @listableObjectComponent(Item, ViewMode.GridElement) @Component({ - selector: 'ds-publication-grid-element', - styleUrls: ['./publication-grid-element.component.scss'], - templateUrl: './publication-grid-element.component.html', + selector: 'ds-item-grid-element', + styleUrls: ['./item-grid-element.component.scss'], + templateUrl: './item-grid-element.component.html', animations: [focusShadow] }) /** * The component for displaying a grid element for an item of the type Publication */ -export class PublicationGridElementComponent extends AbstractListableElementComponent { +export class ItemGridElementComponent extends AbstractListableElementComponent { } diff --git a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.html b/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.html deleted file mode 100644 index e35deda539..0000000000 --- a/src/app/shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html index a2933fd0ec..3c9487185e 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html @@ -8,6 +8,7 @@
+

{{dso.name}}

{{dso.shortDescription}}

diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html index 8d5f288498..86ce429fe7 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html @@ -8,6 +8,7 @@
+

{{dso.name}}

{{dso.shortDescription}}

diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html similarity index 96% rename from src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.html rename to src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html index 3c2d54b003..b7c429c00b 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html @@ -15,7 +15,7 @@
- +

diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.scss b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.scss similarity index 100% rename from src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.scss rename to src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.scss diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts similarity index 95% rename from src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec.ts rename to src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts index 0688b7c60e..4f86b6146c 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts @@ -24,7 +24,7 @@ import { ItemSearchResult } from '../../../../object-collection/shared/item-sear import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; -import { PublicationSearchResultGridElementComponent } from './publication-search-result-grid-element.component'; +import { ItemSearchResultGridElementComponent } from './item-search-result-grid-element.component'; const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; @@ -72,7 +72,7 @@ mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), { } }); -describe('PublicationGridElementComponent', getEntityGridElementTestComponent(PublicationSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['authors', 'date', 'abstract'])); +describe('ItemGridElementComponent', getEntityGridElementTestComponent(ItemSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['authors', 'date', 'abstract'])); /** * Create test cases for a grid component of an entity. diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts similarity index 71% rename from src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.ts rename to src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts index c96e73d365..e729756452 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts @@ -9,13 +9,13 @@ import { ItemSearchResult } from '../../../../object-collection/shared/item-sear @listableObjectComponent('PublicationSearchResult', ViewMode.GridElement) @listableObjectComponent(ItemSearchResult, ViewMode.GridElement) @Component({ - selector: 'ds-publication-search-result-grid-element', - styleUrls: ['./publication-search-result-grid-element.component.scss'], - templateUrl: './publication-search-result-grid-element.component.html', + selector: 'ds-item-search-result-grid-element', + styleUrls: ['./item-search-result-grid-element.component.scss'], + templateUrl: './item-search-result-grid-element.component.html', animations: [focusShadow] }) /** * The component for displaying a grid element for an item search result of the type Publication */ -export class PublicationSearchResultGridElementComponent extends SearchResultGridElementComponent { +export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.html new file mode 100644 index 0000000000..3877e2f335 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts similarity index 83% rename from src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts index 85b964c083..6897b81f0a 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts @@ -1,7 +1,7 @@ import { async, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { PublicationListElementComponent } from './publication-list-element.component'; +import { ItemListElementComponent } from './item-list-element.component'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; @@ -43,7 +43,7 @@ const mockItem: Item = Object.assign(new Item(), { } }); -describe('PublicationListElementComponent', () => { +describe('ItemListElementComponent', () => { let comp; let fixture; @@ -53,18 +53,18 @@ describe('PublicationListElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [PublicationListElementComponent, TruncatePipe], + declarations: [ItemListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(PublicationListElementComponent, { + }).overrideComponent(ItemListElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(PublicationListElementComponent); + fixture = TestBed.createComponent(ItemListElementComponent); comp = fixture.componentInstance; })); @@ -75,7 +75,7 @@ describe('PublicationListElementComponent', () => { }); it(`should contain a PublicationListElementComponent`, () => { - const publicationListElement = fixture.debugElement.query(By.css(`ds-publication-search-result-list-element`)); + const publicationListElement = fixture.debugElement.query(By.css(`ds-item-search-result-list-element`)); expect(publicationListElement).not.toBeNull(); }); }); diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.ts similarity index 72% rename from src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts rename to src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.ts index 59d0249aef..a3bd2a1ce0 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.ts @@ -7,12 +7,12 @@ import { Item } from '../../../../../core/shared/item.model'; @listableObjectComponent('Publication', ViewMode.ListElement) @listableObjectComponent(Item, ViewMode.ListElement) @Component({ - selector: 'ds-publication-list-element', - styleUrls: ['./publication-list-element.component.scss'], - templateUrl: './publication-list-element.component.html' + selector: 'ds-item-list-element', + styleUrls: ['./item-list-element.component.scss'], + templateUrl: './item-list-element.component.html' }) /** * The component for displaying a list element for an item of the type Publication */ -export class PublicationListElementComponent extends AbstractListableElementComponent { +export class ItemListElementComponent extends AbstractListableElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html deleted file mode 100644 index dcebcfd56a..0000000000 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/shared/object-list/item-type-badge/item-type-badge.component.html b/src/app/shared/object-list/item-type-badge/item-type-badge.component.html deleted file mode 100644 index 947970b7e1..0000000000 --- a/src/app/shared/object-list/item-type-badge/item-type-badge.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- {{ type.toLowerCase() + '.listelement.badge' | translate }} -
diff --git a/src/app/shared/object-list/item-type-badge/item-type-badge.component.ts b/src/app/shared/object-list/item-type-badge/item-type-badge.component.ts deleted file mode 100644 index 88fe2d7f54..0000000000 --- a/src/app/shared/object-list/item-type-badge/item-type-badge.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; - -@Component({ - selector: 'ds-item-type-badge', - templateUrl: './item-type-badge.component.html' -}) -/** - * Component rendering the type of an item as a badge - */ -export class ItemTypeBadgeComponent { - /** - * The component used to retrieve the type from - */ - @Input() object: DSpaceObject; -} diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index e1478b5206..bf7f04b9ac 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -2,7 +2,7 @@ - +

diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts index 56cf840f4a..af6c44786b 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts @@ -153,7 +153,7 @@ describe('ItemListPreviewComponent', () => { }); it('should show the entity type span', () => { - const entityField = fixture.debugElement.query(By.css('ds-item-type-badge')); + const entityField = fixture.debugElement.query(By.css('ds-type-badge')); expect(entityField).not.toBeNull(); }); }); diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html index 3c787c47ce..1c081e2805 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html index 32834fefee..08b02d123a 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html similarity index 95% rename from src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.html rename to src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 1bc723200a..880f4393cd 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -1,4 +1,4 @@ - + ; +let publicationListElementComponent: ItemSearchResultListElementComponent; +let fixture: ComponentFixture; const mockItemWithMetadata: ItemSearchResult = Object.assign(new ItemSearchResult(), { indexableObject: @@ -64,22 +64,22 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign(new ItemSearchRe }) }); -describe('PublicationListElementComponent', () => { +describe('ItemListElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [PublicationSearchResultListElementComponent, TruncatePipe], + declarations: [ItemSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} } ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(PublicationSearchResultListElementComponent, { + }).overrideComponent(ItemSearchResultListElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(PublicationSearchResultListElementComponent); + fixture = TestBed.createComponent(ItemSearchResultListElementComponent); publicationListElementComponent = fixture.componentInstance; })); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts similarity index 70% rename from src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.ts rename to src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts index 3e0db60b4c..b4b658cb96 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts @@ -8,12 +8,12 @@ import { Item } from '../../../../../../core/shared/item.model'; @listableObjectComponent('PublicationSearchResult', ViewMode.ListElement) @listableObjectComponent(ItemSearchResult, ViewMode.ListElement) @Component({ - selector: 'ds-publication-search-result-list-element', - styleUrls: ['./publication-search-result-list-element.component.scss'], - templateUrl: './publication-search-result-list-element.component.html' + selector: 'ds-item-search-result-list-element', + styleUrls: ['./item-search-result-list-element.component.scss'], + templateUrl: './item-search-result-list-element.component.html' }) /** * The component for displaying a list element for an item search result of the type Publication */ -export class PublicationSearchResultListElementComponent extends SearchResultListElementComponent { +export class ItemSearchResultListElementComponent extends SearchResultListElementComponent { } diff --git a/src/app/shared/object-list/type-badge/type-badge.component.html b/src/app/shared/object-list/type-badge/type-badge.component.html new file mode 100644 index 0000000000..18aeeb4bca --- /dev/null +++ b/src/app/shared/object-list/type-badge/type-badge.component.html @@ -0,0 +1,3 @@ +
+ {{ typeMessage | translate }} +
diff --git a/src/app/shared/object-list/item-type-badge/item-type-badge.component.spec.ts b/src/app/shared/object-list/type-badge/type-badge.component.spec.ts similarity index 83% rename from src/app/shared/object-list/item-type-badge/item-type-badge.component.spec.ts rename to src/app/shared/object-list/type-badge/type-badge.component.spec.ts index f75a49363f..e3a1d64f9f 100644 --- a/src/app/shared/object-list/item-type-badge/item-type-badge.component.spec.ts +++ b/src/app/shared/object-list/type-badge/type-badge.component.spec.ts @@ -5,11 +5,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { TruncatePipe } from '../../utils/truncate.pipe'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ItemTypeBadgeComponent } from './item-type-badge.component'; import { By } from '@angular/platform-browser'; +import { TypeBadgeComponent } from './type-badge.component'; -let comp: ItemTypeBadgeComponent; -let fixture: ComponentFixture; +let comp: TypeBadgeComponent; +let fixture: ComponentFixture; const type = 'authorOfPublication'; @@ -41,15 +41,15 @@ describe('ItemTypeBadgeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot()], - declarations: [ItemTypeBadgeComponent, TruncatePipe], + declarations: [TypeBadgeComponent, TruncatePipe], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ItemTypeBadgeComponent, { + }).overrideComponent(TypeBadgeComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(ItemTypeBadgeComponent); + fixture = TestBed.createComponent(TypeBadgeComponent); comp = fixture.componentInstance; })); @@ -71,9 +71,9 @@ describe('ItemTypeBadgeComponent', () => { fixture.detectChanges(); }); - it('should not show a badge', () => { + it('should show an item badge', () => { const badge = fixture.debugElement.query(By.css('span.badge')); - expect(badge).toBeNull(); + expect(badge.nativeElement.textContent).toContain('item'); }); }); }); diff --git a/src/app/shared/object-list/type-badge/type-badge.component.ts b/src/app/shared/object-list/type-badge/type-badge.component.ts new file mode 100644 index 0000000000..e730f2d407 --- /dev/null +++ b/src/app/shared/object-list/type-badge/type-badge.component.ts @@ -0,0 +1,41 @@ +import { Component, Input } from '@angular/core'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { isEmpty } from '../../empty.util'; + +@Component({ + selector: 'ds-type-badge', + templateUrl: './type-badge.component.html' +}) +/** + * Component rendering the type of an item as a badge + */ +export class TypeBadgeComponent { + + private _object: DSpaceObject; + private _typeMessage: string; + + /** + * The component used to retrieve the type from + */ + @Input() set object(object: DSpaceObject) { + this._object = object; + + const renderTypes = this._object.getRenderTypes(); + if (!isEmpty(renderTypes.length)) { + const renderType = renderTypes[0]; + if (renderType instanceof Function) { + this._typeMessage = `${(renderType as any).name.toLowerCase()}.listelement.badge`; + } else { + this._typeMessage = `${renderType.toLowerCase()}.listelement.badge`; + } + } + } + + get object(): DSpaceObject { + return this._object; + } + + get typeMessage(): string { + return this._typeMessage; + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 9bbe9351b0..6bddaa56aa 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -21,7 +21,7 @@ import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role 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'; -import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component'; +import { ItemListElementComponent } from './object-list/item-list-element/item-types/item/item-list-element.component'; import { EnumKeysPipe } from './utils/enum-keys-pipe'; import { FileSizePipe } from './utils/file-size-pipe'; import { MetadataFieldValidator } from './utils/metadatafield-validator.directive'; @@ -160,13 +160,12 @@ import { ItemSelectComponent } from './object-select/item-select/item-select.com import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component'; import { FilterInputSuggestionsComponent } from './input-suggestions/filter-suggestions/filter-input-suggestions.component'; import { DsoInputSuggestionsComponent } from './input-suggestions/dso-input-suggestions/dso-input-suggestions.component'; -import { PublicationGridElementComponent } from './object-grid/item-grid-element/item-types/publication/publication-grid-element.component'; -import { ItemTypeBadgeComponent } from './object-list/item-type-badge/item-type-badge.component'; +import { ItemGridElementComponent } from './object-grid/item-grid-element/item-types/item/item-grid-element.component'; +import { TypeBadgeComponent } from './object-list/type-badge/type-badge.component'; import { MetadataRepresentationLoaderComponent } from './metadata-representation/metadata-representation-loader.component'; import { MetadataRepresentationDirective } from './metadata-representation/metadata-representation.directive'; import { ListableObjectComponentLoaderComponent } from './object-collection/shared/listable-object/listable-object-component-loader.component'; -import { PublicationSearchResultListElementComponent } from './object-list/search-result-list-element/item-search-result/item-types/publication/publication-search-result-list-element.component'; -import { PublicationSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.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'; @@ -217,6 +216,7 @@ import { CommunitySidebarSearchListElementComponent } from './object-list/sideba import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component'; import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component'; import { HoverClassDirective } from './hover-class.directive'; +import { ItemSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -381,7 +381,7 @@ const COMPONENTS = [ BrowseByComponent, AbstractTrackableComponent, ComcolMetadataComponent, - ItemTypeBadgeComponent, + TypeBadgeComponent, BrowseByComponent, AbstractTrackableComponent, CustomSwitchComponent, @@ -397,7 +397,7 @@ const COMPONENTS = [ LogInPasswordComponent, LogInContainerComponent, ItemVersionsComponent, - PublicationSearchResultListElementComponent, + ItemSearchResultListElementComponent, ItemVersionsNoticeComponent, ModifyItemOverviewComponent, ImpersonateNavbarComponent, @@ -426,10 +426,10 @@ const ENTRY_COMPONENTS = [ CommunitySearchResultGridElementComponent, CollectionSearchResultGridElementComponent, SearchResultGridElementComponent, - PublicationListElementComponent, - PublicationGridElementComponent, - PublicationSearchResultListElementComponent, - PublicationSearchResultGridElementComponent, + ItemListElementComponent, + ItemGridElementComponent, + ItemSearchResultListElementComponent, + ItemSearchResultGridElementComponent, BrowseEntryListElementComponent, SearchResultDetailElementComponent, SearchResultGridElementComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 9b73918e77..93fc70319e 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -760,6 +760,10 @@ + "collection.listelement.badge": "Collection", + + + "collection.page.browse.recent.head": "Recent Submissions", "collection.page.browse.recent.empty": "No items to show", @@ -863,6 +867,10 @@ + "community.listelement.badge": "Community", + + + "comcol-role.edit.no-group": "None", "comcol-role.edit.create": "Create", @@ -1676,6 +1684,28 @@ + "item.listelement.badge": "Item", + + "item.page.description": "Description", + + "item.page.edit": "Edit this item", + + "item.page.journal-issn": "Journal ISSN", + + "item.page.journal-title": "Journal Title", + + "item.page.publisher": "Publisher", + + "item.page.titleprefix": "Item: ", + + "item.page.volume-title": "Volume Title", + + "item.search.results.head": "Item Search Results", + + "item.search.title": "DSpace Angular :: Item Search", + + + "item.page.abstract": "Abstract", "item.page.author": "Authors", From c1fba059cae52845557eb89869db932e10223e24 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 19 Nov 2020 12:28:54 +0100 Subject: [PATCH 14/39] 74572: Fix typo --- .../simple/item-types/publication/publication.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts index 0f34d50fa2..1d340e2e73 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts @@ -36,7 +36,7 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('UntypedItemComponent', () => { +describe('PublicationComponent', () => { let comp: PublicationComponent; let fixture: ComponentFixture; From 9c2ca90c322c842b8f9de26d36f038ab52792823 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 20 Nov 2020 11:11:58 +0100 Subject: [PATCH 15/39] Remove unused import --- .../simple/item-types/publication/publication.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index a731205df3..5ace8d0473 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -1,5 +1,4 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Item } from '../../../../core/shared/item.model'; import { ItemComponent } from '../shared/item.component'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; From b2369b237e841e4345cc85aeb80612c9b214b4da Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 24 Nov 2020 10:36:50 +0100 Subject: [PATCH 16/39] 74612: Redirect bugfix --- src/app/core/auth/auth.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 06906346ed..8eea9f8938 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -453,7 +453,7 @@ export class AuthService { * Clear redirect url */ clearRedirectUrl() { - this.store.dispatch(new SetRedirectUrlAction('')); + this.store.dispatch(new SetRedirectUrlAction(undefined)); this.storage.remove(REDIRECT_COOKIE); } From 6b69f66a8dce40b8f9ae434099d708ec54007a8c Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 24 Nov 2020 11:58:16 +0100 Subject: [PATCH 17/39] 74572: Update type badge to not rely on constructor name --- .../simple/item-types/untyped-item/untyped-item.component.html | 2 +- src/app/shared/object-list/type-badge/type-badge.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html index d65f20f030..dcb73fad10 100644 --- a/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/+item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -1,6 +1,6 @@

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

diff --git a/src/app/shared/object-list/type-badge/type-badge.component.ts b/src/app/shared/object-list/type-badge/type-badge.component.ts index e730f2d407..227346a3dc 100644 --- a/src/app/shared/object-list/type-badge/type-badge.component.ts +++ b/src/app/shared/object-list/type-badge/type-badge.component.ts @@ -24,7 +24,7 @@ export class TypeBadgeComponent { if (!isEmpty(renderTypes.length)) { const renderType = renderTypes[0]; if (renderType instanceof Function) { - this._typeMessage = `${(renderType as any).name.toLowerCase()}.listelement.badge`; + this._typeMessage = `${object.type.toString().toLowerCase()}.listelement.badge`; } else { this._typeMessage = `${renderType.toLowerCase()}.listelement.badge`; } From 8486fecc17235e0d54dd7f1b73dc20a5e906673a Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 24 Nov 2020 12:03:34 +0100 Subject: [PATCH 18/39] 74612: Post-merge test fixes --- .../process-page/detail/process-detail.component.spec.ts | 6 ------ src/app/shared/mocks/auth.service.mock.ts | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/process-page/detail/process-detail.component.spec.ts b/src/app/process-page/detail/process-detail.component.spec.ts index 94282f886e..b81eedabad 100644 --- a/src/app/process-page/detail/process-detail.component.spec.ts +++ b/src/app/process-page/detail/process-detail.component.spec.ts @@ -37,7 +37,6 @@ describe('ProcessDetailComponent', () => { let nameService: DSONameService; let bitstreamDataService: BitstreamDataService; let httpClient: HttpClient; - let authService: AuthService; let process: Process; let fileName: string; @@ -105,10 +104,6 @@ describe('ProcessDetailComponent', () => { httpClient = jasmine.createSpyObj('httpClient', { get: observableOf(processOutput) }); - authService = jasmine.createSpyObj('authService', { - isAuthenticated: observableOf(true), - setRedirectUrl: {} - }); } beforeEach(async(() => { @@ -126,7 +121,6 @@ describe('ProcessDetailComponent', () => { { provide: DSONameService, useValue: nameService }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: HttpClient, useValue: httpClient }, - { provide: AuthService, useValue: authService }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); diff --git a/src/app/shared/mocks/auth.service.mock.ts b/src/app/shared/mocks/auth.service.mock.ts index 0f3a7d13d1..064af7dade 100644 --- a/src/app/shared/mocks/auth.service.mock.ts +++ b/src/app/shared/mocks/auth.service.mock.ts @@ -12,4 +12,11 @@ export class AuthServiceMock { public getShortlivedToken(): Observable { return observableOf('token'); } + + public isAuthenticated(): Observable { + return observableOf(true); + } + + public setRedirectUrl(url: string) { + } } From 1903530f77cf2ab83089aeb16a9b38185b7ecdee Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Mon, 19 Oct 2020 12:36:18 +0200 Subject: [PATCH 19/39] 74179: Deleting groups notifications, authorisation check, button on edit group page --- .../group-form/group-form.component.html | 5 + .../group-form/group-form.component.spec.ts | 6 ++ .../group-form/group-form.component.ts | 67 ++++++++++++- .../groups-registry.component.html | 26 ++--- .../groups-registry.component.spec.ts | 8 +- .../groups-registry.component.ts | 96 +++++++++++++++---- src/app/core/eperson/group-data.service.ts | 11 ++- .../core/eperson/models/group-dto.model.ts | 17 ++++ src/assets/i18n/en.json5 | 20 +++- 9 files changed, 215 insertions(+), 41 deletions(-) create mode 100644 src/app/core/eperson/models/group-dto.model.ts diff --git a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.html b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.html index 2f76e45429..2742b1db50 100644 --- a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.html +++ b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.html @@ -9,6 +9,11 @@ +
+ +

{{messagePrefix + '.head.edit' | translate}}

diff --git a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts index 5878500f1d..eab9114900 100644 --- a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts +++ b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.spec.ts @@ -14,6 +14,7 @@ import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-d import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { RestResponse } from '../../../../core/cache/response.models'; import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; +import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; @@ -40,6 +41,7 @@ describe('GroupFormComponent', () => { let builderService: FormBuilderService; let ePersonDataServiceStub: any; let groupsDataServiceStub: any; + let authorizationService: AuthorizationDataService; let router; let groups; @@ -88,6 +90,9 @@ describe('GroupFormComponent', () => { return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])) } }; + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); builderService = getMockFormBuilderService(); translateService = getMockTranslateService(); router = new RouterMock(); @@ -115,6 +120,7 @@ describe('GroupFormComponent', () => { { provide: HALEndpointService, useValue: {} }, { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }), params: observableOf({}) } }, { provide: Router, useValue: router }, + { provide: AuthorizationDataService, useValue: authorizationService }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts index d81eac1e92..55d520cafb 100644 --- a/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts +++ b/src/app/+admin/admin-access-control/group-registry/group-form/group-form.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { DynamicFormControlModel, DynamicFormLayout, @@ -8,15 +9,20 @@ import { DynamicTextAreaModel } from '@ng-dynamic-forms/core'; import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs/internal/Observable'; import { combineLatest } from 'rxjs/internal/observable/combineLatest'; import { Subscription } from 'rxjs/internal/Subscription'; -import { take } from 'rxjs/operators'; +import { switchMap, take } from 'rxjs/operators'; import { RestResponse } from '../../../../core/cache/response.models'; +import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../../../core/data/feature-authorization/feature-id'; import { PaginatedList } from '../../../../core/data/paginated-list'; +import { RequestService } from '../../../../core/data/request.service'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { Group } from '../../../../core/eperson/models/group.model'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; +import { ConfirmationModalComponent } from '../../../../shared/confirmation-modal/confirmation-modal.component'; import { hasValue, isNotEmpty } from '../../../../shared/empty.util'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; @@ -89,19 +95,34 @@ export class GroupFormComponent implements OnInit, OnDestroy { */ groupBeingEdited: Group; + /** + * Observable whether or not the logged in user is allowed to delete the Group + */ + canDelete$: Observable; + constructor(public groupDataService: GroupDataService, private ePersonDataService: EPersonDataService, private formBuilderService: FormBuilderService, private translateService: TranslateService, private notificationsService: NotificationsService, private route: ActivatedRoute, - protected router: Router) { + protected router: Router, + private authorizationService: AuthorizationDataService, + private modalService: NgbModal, + public requestService: RequestService) { } ngOnInit() { + this.initialisePage(); + } + + initialisePage() { this.subs.push(this.route.params.subscribe((params) => { this.setActiveGroup(params.groupId) })); + this.canDelete$ = this.groupDataService.getActiveGroup().pipe( + switchMap((group: Group) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(group) ? group.self : undefined)) + ); combineLatest( this.translateService.get(`${this.messagePrefix}.groupName`), this.translateService.get(`${this.messagePrefix}.groupDescription`), @@ -269,6 +290,48 @@ export class GroupFormComponent implements OnInit, OnDestroy { }); } + /** + * Deletes the Group from the Repository. The Group will be the only that this form is showing. + * It'll either show a success or error message depending on whether the delete was successful or not. + */ + delete() { + this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((group: Group) => { + const modalRef = this.modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.dso = group; + modalRef.componentInstance.headerLabel = this.messagePrefix + '.delete-group.modal.header'; + modalRef.componentInstance.infoLabel = this.messagePrefix + '.delete-group.modal.info'; + modalRef.componentInstance.cancelLabel = this.messagePrefix + '.delete-group.modal.cancel'; + modalRef.componentInstance.confirmLabel = this.messagePrefix + '.delete-group.modal.confirm'; + modalRef.componentInstance.response.pipe(take(1)).subscribe((confirm: boolean) => { + if (confirm) { + if (hasValue(group.id)) { + this.groupDataService.deleteGroup(group).pipe(take(1)) + .subscribe(([success, optionalErrorMessage]: [boolean, string]) => { + if (success) { + this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.deleted.success', { name: group.name })); + this.reset(); + this.onCancel(); + } else { + this.notificationsService.error( + this.translateService.get(this.messagePrefix + '.notification.deleted.failure.title', { name: group.name }), + this.translateService.get(this.messagePrefix + '.notification.deleted.failure.content', { cause: optionalErrorMessage })); + } + }) + }} + }); + }) + } + + /** + * This method will ensure that the page gets reset and that the cache is cleared + */ + reset() { + this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((group: Group) => { + this.requestService.removeByHrefSubstring(group.self); + }); + this.initialisePage(); + } + /** * Cancel the current edit when component is destroyed & unsub all subscriptions */ diff --git a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.html b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.html index 3bd7d7ac4f..067f59a4c0 100644 --- a/src/app/+admin/admin-access-control/group-registry/groups-registry.component.html +++ b/src/app/+admin/admin-access-control/group-registry/groups-registry.component.html @@ -30,10 +30,10 @@ @@ -50,21 +50,21 @@ - - {{group.id}} - {{group.name}} - {{(getMembers(group) | async)?.payload?.totalElements + (getSubgroups(group) | async)?.payload?.totalElements}} + + {{groupDto.group.id}} + {{groupDto.group.name}} + {{(getMembers(groupDto.group) | async)?.payload?.totalElements + (getSubgroups(groupDto.group) | async)?.payload?.totalElements}}
- -
@@ -75,7 +75,7 @@
-