diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index bf97bb4b07..9e31e31ddb 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -139,6 +139,15 @@ "collection.edit.logo.notifications.delete.error.title": "Error deleting logo", "collection.edit.logo.upload": "Drop a Collection Logo to upload", "collection.edit.notifications.success": "Successfully edited the Collection", + "collection.edit.return": "Return", + "collection.edit.tabs.curate.head": "Curate", + "collection.edit.tabs.curate.title": "Collection Edit - Curate", + "collection.edit.tabs.metadata.head": "Edit Metadata", + "collection.edit.tabs.metadata.title": "Collection Edit - Metadata", + "collection.edit.tabs.roles.head": "Assign Roles", + "collection.edit.tabs.roles.title": "Collection Edit - Roles", + "collection.edit.tabs.source.head": "Content Source", + "collection.edit.tabs.source.title": "Collection Edit - Content Source", "collection.form.abstract": "Short Description", "collection.form.description": "Introductory text (HTML)", "collection.form.errors.title.required": "Please enter a collection name", @@ -171,6 +180,13 @@ "community.edit.logo.notifications.delete.error.title": "Error deleting logo", "community.edit.logo.upload": "Drop a Community Logo to upload", "community.edit.notifications.success": "Successfully edited the Community", + "community.edit.return": "Return", + "community.edit.tabs.curate.head": "Curate", + "community.edit.tabs.curate.title": "Community Edit - Curate", + "community.edit.tabs.metadata.head": "Edit Metadata", + "community.edit.tabs.metadata.title": "Community Edit - Metadata", + "community.edit.tabs.roles.head": "Assign Roles", + "community.edit.tabs.roles.title": "Community Edit - Roles", "community.form.abstract": "Short Description", "community.form.description": "Introductory text (HTML)", "community.form.errors.title.required": "Please enter a community name", diff --git a/src/app/+collection-page/collection-page-routing.module.ts b/src/app/+collection-page/collection-page-routing.module.ts index cdbd7650b2..ad142e8fcf 100644 --- a/src/app/+collection-page/collection-page-routing.module.ts +++ b/src/app/+collection-page/collection-page-routing.module.ts @@ -5,7 +5,6 @@ import { CollectionPageComponent } from './collection-page.component'; import { CollectionPageResolver } from './collection-page.resolver'; import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; -import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component'; import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard'; import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component'; import { URLCombiner } from '../core/url-combiner/url-combiner'; @@ -38,12 +37,8 @@ const COLLECTION_EDIT_PATH = ':id/edit'; }, { path: COLLECTION_EDIT_PATH, - pathMatch: 'full', - component: EditCollectionPageComponent, - canActivate: [AuthenticatedGuard], - resolve: { - dso: CollectionPageResolver - } + loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule', + canActivate: [AuthenticatedGuard] }, { path: ':id/delete', diff --git a/src/app/+collection-page/collection-page.module.ts b/src/app/+collection-page/collection-page.module.ts index bdeffa34f3..f7059deda0 100644 --- a/src/app/+collection-page/collection-page.module.ts +++ b/src/app/+collection-page/collection-page.module.ts @@ -7,7 +7,6 @@ import { CollectionPageComponent } from './collection-page.component'; import { CollectionPageRoutingModule } from './collection-page-routing.module'; import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component'; import { CollectionFormComponent } from './collection-form/collection-form.component'; -import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component'; import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component'; import { SearchService } from '../+search-page/search-service/search.service'; @@ -20,10 +19,12 @@ import { SearchService } from '../+search-page/search-service/search.service'; declarations: [ CollectionPageComponent, CreateCollectionPageComponent, - EditCollectionPageComponent, DeleteCollectionPageComponent, CollectionFormComponent ], + exports: [ + CollectionFormComponent + ], providers: [ SearchService ] diff --git a/src/app/shared/mocks/mock-store.ts b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.html similarity index 100% rename from src/app/shared/mocks/mock-store.ts rename to src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.html diff --git a/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts new file mode 100644 index 0000000000..d7deaea982 --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +/** + * Component for managing a collection's curation tasks + */ +@Component({ + selector: 'ds-collection-curate', + templateUrl: './collection-curate.component.html', +}) +export class CollectionCurateComponent { + /* TODO: Implement Collection Edit - Curate */ +} diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html new file mode 100644 index 0000000000..dbfeab98ad --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html @@ -0,0 +1,6 @@ + +{{'collection.edit.delete' + | translate}} diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts new file mode 100644 index 0000000000..71cb06394f --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts @@ -0,0 +1,42 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../../shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { ActivatedRoute } from '@angular/router'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { CollectionMetadataComponent } from './collection-metadata.component'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub'; + +describe('CollectionMetadataComponent', () => { + let comp: CollectionMetadataComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], + declarations: [CollectionMetadataComponent], + providers: [ + { provide: CollectionDataService, useValue: {} }, + { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CollectionMetadataComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe('frontendURL', () => { + it('should have the right frontendURL set', () => { + expect((comp as any).frontendURL).toEqual('/collections/'); + }) + }); +}); diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts new file mode 100644 index 0000000000..af2ab7d0a7 --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; +import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; +import { Collection } from '../../../core/shared/collection.model'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; + +/** + * Component for editing a collection's metadata + */ +@Component({ + selector: 'ds-collection-metadata', + templateUrl: './collection-metadata.component.html', +}) +export class CollectionMetadataComponent extends ComcolMetadataComponent { + protected frontendURL = '/collections/'; + protected type = Collection.type; + + public constructor( + protected collectionDataService: CollectionDataService, + protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + protected translate: TranslateService + ) { + super(collectionDataService, router, route, notificationsService, translate); + } +} diff --git a/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.html b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts new file mode 100644 index 0000000000..39f72fd2ce --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +/** + * Component for managing a collection's roles + */ +@Component({ + selector: 'ds-collection-roles', + templateUrl: './collection-roles.component.html', +}) +export class CollectionRolesComponent { + /* TODO: Implement Collection Edit - Roles */ +} diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts new file mode 100644 index 0000000000..6ec5be884d --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +/** + * Component for managing the content source of the collection + */ +@Component({ + selector: 'ds-collection-source', + templateUrl: './collection-source.component.html', +}) +export class CollectionSourceComponent { + /* TODO: Implement Collection Edit - Content Source */ +} diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html deleted file mode 100644 index b4429d0f7c..0000000000 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
-
- - - {{'collection.edit.delete' - | translate}} -
-
-
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss deleted file mode 100644 index 8b13789179..0000000000 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts index 8f82c30ff4..9f915d2d7a 100644 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts @@ -8,21 +8,34 @@ import { EditCollectionPageComponent } from './edit-collection-page.component'; import { SharedModule } from '../../shared/shared.module'; import { CollectionDataService } from '../../core/data/collection-data.service'; import { of as observableOf } from 'rxjs'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub'; describe('EditCollectionPageComponent', () => { let comp: EditCollectionPageComponent; let fixture: ComponentFixture; + const routeStub = { + data: observableOf({ + dso: { payload: {} } + }), + routeConfig: { + children: [] + }, + snapshot: { + firstChild: { + routeConfig: { + path: 'mockUrl' + } + } + } + }; + beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], declarations: [EditCollectionPageComponent], providers: [ { provide: CollectionDataService, useValue: {} }, - { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -34,9 +47,9 @@ describe('EditCollectionPageComponent', () => { fixture.detectChanges(); }); - describe('frontendURL', () => { - it('should have the right frontendURL set', () => { - expect((comp as any).frontendURL).toEqual('/collections/'); + describe('type', () => { + it('should have the right type set', () => { + expect((comp as any).type).toEqual('collection'); }) }); }); diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts index 310fbb1427..21671fe112 100644 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts @@ -2,29 +2,30 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; import { Collection } from '../../core/shared/collection.model'; -import { CollectionDataService } from '../../core/data/collection-data.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { TranslateService } from '@ngx-translate/core'; +import { getCollectionPageRoute } from '../collection-page-routing.module'; /** * Component that represents the page where a user can edit an existing Collection */ @Component({ selector: 'ds-edit-collection', - styleUrls: ['./edit-collection-page.component.scss'], - templateUrl: './edit-collection-page.component.html' + templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' }) export class EditCollectionPageComponent extends EditComColPageComponent { - protected frontendURL = '/collections/'; - protected type = Collection.type; + protected type = 'collection'; public constructor( - protected collectionDataService: CollectionDataService, protected router: Router, - protected route: ActivatedRoute, - protected notificationsService: NotificationsService, - protected translate: TranslateService + protected route: ActivatedRoute ) { - super(collectionDataService, router, route, notificationsService, translate); + super(router, route); + } + + /** + * Get the collection page url + * @param collection The collection for which the url is requested + */ + getPageUrl(collection: Collection): string { + return getCollectionPageRoute(collection.id) } } 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 new file mode 100644 index 0000000000..f442aae4d6 --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { EditCollectionPageComponent } from './edit-collection-page.component'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '../../shared/shared.module'; +import { EditCollectionPageRoutingModule } from './edit-collection-page.routing.module'; +import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component'; +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'; + +/** + * Module that contains all components related to the Edit Collection page administrator functionality + */ +@NgModule({ + imports: [ + CommonModule, + SharedModule, + EditCollectionPageRoutingModule, + CollectionPageModule + ], + declarations: [ + EditCollectionPageComponent, + CollectionMetadataComponent, + CollectionRolesComponent, + CollectionCurateComponent, + CollectionSourceComponent + ] +}) +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 new file mode 100644 index 0000000000..fcfced9d81 --- /dev/null +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts @@ -0,0 +1,61 @@ +import { RouterModule } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { EditCollectionPageComponent } from './edit-collection-page.component'; +import { CollectionPageResolver } from '../collection-page.resolver'; +import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component'; +import { CollectionRolesComponent } from './collection-roles/collection-roles.component'; +import { CollectionSourceComponent } from './collection-source/collection-source.component'; +import { CollectionCurateComponent } from './collection-curate/collection-curate.component'; + +/** + * Routing module that handles the routing for the Edit Collection page administrator functionality + */ +@NgModule({ + imports: [ + RouterModule.forChild([ + { + path: '', + component: EditCollectionPageComponent, + resolve: { + dso: CollectionPageResolver + }, + children: [ + { + path: '', + redirectTo: 'metadata', + pathMatch: 'full' + }, + { + path: 'metadata', + component: CollectionMetadataComponent, + data: { + title: 'collection.edit.tabs.metadata.title', + hideReturnButton: true + } + }, + { + path: 'roles', + component: CollectionRolesComponent, + data: { title: 'collection.edit.tabs.roles.title' } + }, + { + path: 'source', + component: CollectionSourceComponent, + data: { title: 'collection.edit.tabs.source.title' } + }, + { + path: 'curate', + component: CollectionCurateComponent, + data: { title: 'collection.edit.tabs.curate.title' } + } + ] + } + ]) + ], + providers: [ + CollectionPageResolver, + ] +}) +export class EditCollectionPageRoutingModule { + +} diff --git a/src/app/+community-page/community-page-routing.module.ts b/src/app/+community-page/community-page-routing.module.ts index cecd17ec10..df548e0617 100644 --- a/src/app/+community-page/community-page-routing.module.ts +++ b/src/app/+community-page/community-page-routing.module.ts @@ -5,7 +5,6 @@ import { CommunityPageComponent } from './community-page.component'; import { CommunityPageResolver } from './community-page.resolver'; import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; -import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component'; import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard'; import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component'; import { URLCombiner } from '../core/url-combiner/url-combiner'; @@ -38,12 +37,8 @@ const COMMUNITY_EDIT_PATH = ':id/edit'; }, { path: COMMUNITY_EDIT_PATH, - pathMatch: 'full', - component: EditCommunityPageComponent, - canActivate: [AuthenticatedGuard], - resolve: { - dso: CommunityPageResolver - } + loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule', + canActivate: [AuthenticatedGuard] }, { path: ':id/delete', diff --git a/src/app/+community-page/community-page.module.ts b/src/app/+community-page/community-page.module.ts index 6d63cadcc8..534c96989e 100644 --- a/src/app/+community-page/community-page.module.ts +++ b/src/app/+community-page/community-page.module.ts @@ -9,7 +9,6 @@ import { CommunityPageRoutingModule } from './community-page-routing.module'; import {CommunityPageSubCommunityListComponent} from './sub-community-list/community-page-sub-community-list.component'; import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component'; import { CommunityFormComponent } from './community-form/community-form.component'; -import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component'; import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component'; @NgModule({ @@ -23,9 +22,11 @@ import { DeleteCommunityPageComponent } from './delete-community-page/delete-com CommunityPageSubCollectionListComponent, CommunityPageSubCommunityListComponent, CreateCommunityPageComponent, - EditCommunityPageComponent, DeleteCommunityPageComponent, CommunityFormComponent + ], + exports: [ + CommunityFormComponent ] }) diff --git a/src/app/+community-page/edit-community-page/community-curate/community-curate.component.html b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts new file mode 100644 index 0000000000..6151d3fe9a --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +/** + * Component for managing a community's curation tasks + */ +@Component({ + selector: 'ds-community-curate', + templateUrl: './community-curate.component.html', +}) +export class CommunityCurateComponent { + /* TODO: Implement Community Edit - Curate */ +} diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html new file mode 100644 index 0000000000..71c37f5697 --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html @@ -0,0 +1,6 @@ + +{{'community.edit.delete' + | translate}} diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts new file mode 100644 index 0000000000..abeafb4e23 --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts @@ -0,0 +1,42 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../../shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { CommunityMetadataComponent } from './community-metadata.component'; +import { CommunityDataService } from '../../../core/data/community-data.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub'; + +describe('CommunityMetadataComponent', () => { + let comp: CommunityMetadataComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], + declarations: [CommunityMetadataComponent], + providers: [ + { provide: CommunityDataService, useValue: {} }, + { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommunityMetadataComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe('frontendURL', () => { + it('should have the right frontendURL set', () => { + expect((comp as any).frontendURL).toEqual('/communities/'); + }) + }); +}); diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts new file mode 100644 index 0000000000..c4bb88289f --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; +import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Community } from '../../../core/shared/community.model'; +import { CommunityDataService } from '../../../core/data/community-data.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; + +/** + * Component for editing a community's metadata + */ +@Component({ + selector: 'ds-community-metadata', + templateUrl: './community-metadata.component.html', +}) +export class CommunityMetadataComponent extends ComcolMetadataComponent { + protected frontendURL = '/communities/'; + protected type = Community.type; + + public constructor( + protected communityDataService: CommunityDataService, + protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + protected translate: TranslateService + ) { + super(communityDataService, router, route, notificationsService, translate); + } +} diff --git a/src/app/+community-page/edit-community-page/community-roles/community-roles.component.html b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts new file mode 100644 index 0000000000..afa1fe14d1 --- /dev/null +++ b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +/** + * Component for managing a community's roles + */ +@Component({ + selector: 'ds-community-roles', + templateUrl: './community-roles.component.html', +}) +export class CommunityRolesComponent { + /* TODO: Implement Community Edit - Roles */ +} diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.html b/src/app/+community-page/edit-community-page/edit-community-page.component.html deleted file mode 100644 index ba3a3ce32b..0000000000 --- a/src/app/+community-page/edit-community-page/edit-community-page.component.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
-
- - - {{'community.edit.delete' - | translate}} -
-
-
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.scss b/src/app/+community-page/edit-community-page/edit-community-page.component.scss deleted file mode 100644 index 8b13789179..0000000000 --- a/src/app/+community-page/edit-community-page/edit-community-page.component.scss +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts b/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts index 86b1647c90..b61924dd00 100644 --- a/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts +++ b/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts @@ -8,21 +8,34 @@ import { SharedModule } from '../../shared/shared.module'; import { of as observableOf } from 'rxjs'; import { EditCommunityPageComponent } from './edit-community-page.component'; import { CommunityDataService } from '../../core/data/community-data.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub'; describe('EditCommunityPageComponent', () => { let comp: EditCommunityPageComponent; let fixture: ComponentFixture; + const routeStub = { + data: observableOf({ + dso: { payload: {} } + }), + routeConfig: { + children: [] + }, + snapshot: { + firstChild: { + routeConfig: { + path: 'mockUrl' + } + } + } + }; + beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], declarations: [EditCommunityPageComponent], providers: [ { provide: CommunityDataService, useValue: {} }, - { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -34,9 +47,9 @@ describe('EditCommunityPageComponent', () => { fixture.detectChanges(); }); - describe('frontendURL', () => { - it('should have the right frontendURL set', () => { - expect((comp as any).frontendURL).toEqual('/communities/'); + describe('type', () => { + it('should have the right type set', () => { + expect((comp as any).type).toEqual('community'); }) }); }); diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.ts b/src/app/+community-page/edit-community-page/edit-community-page.component.ts index c9eea974f7..a8d4d32b7d 100644 --- a/src/app/+community-page/edit-community-page/edit-community-page.component.ts +++ b/src/app/+community-page/edit-community-page/edit-community-page.component.ts @@ -1,30 +1,31 @@ import { Component } from '@angular/core'; import { Community } from '../../core/shared/community.model'; -import { CommunityDataService } from '../../core/data/community-data.service'; import { ActivatedRoute, Router } from '@angular/router'; import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { TranslateService } from '@ngx-translate/core'; +import { getCommunityPageRoute } from '../community-page-routing.module'; /** * Component that represents the page where a user can edit an existing Community */ @Component({ selector: 'ds-edit-community', - styleUrls: ['./edit-community-page.component.scss'], - templateUrl: './edit-community-page.component.html' + templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html' }) export class EditCommunityPageComponent extends EditComColPageComponent { - protected frontendURL = '/communities/'; - protected type = Community.type; + protected type = 'community'; public constructor( - protected communityDataService: CommunityDataService, protected router: Router, - protected route: ActivatedRoute, - protected notificationsService: NotificationsService, - protected translate: TranslateService + protected route: ActivatedRoute ) { - super(communityDataService, router, route, notificationsService, translate); + super(router, route); + } + + /** + * Get the community page url + * @param community The community for which the url is requested + */ + getPageUrl(community: Community): string { + return getCommunityPageRoute(community.id) } } 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 new file mode 100644 index 0000000000..f9a1e11a14 --- /dev/null +++ b/src/app/+community-page/edit-community-page/edit-community-page.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '../../shared/shared.module'; +import { EditCommunityPageRoutingModule } from './edit-community-page.routing.module'; +import { CommunityPageModule } from '../community-page.module'; +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'; + +/** + * Module that contains all components related to the Edit Community page administrator functionality + */ +@NgModule({ + imports: [ + CommonModule, + SharedModule, + EditCommunityPageRoutingModule, + CommunityPageModule + ], + declarations: [ + EditCommunityPageComponent, + CommunityCurateComponent, + CommunityMetadataComponent, + CommunityRolesComponent + ] +}) +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 new file mode 100644 index 0000000000..527b3c018f --- /dev/null +++ b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts @@ -0,0 +1,52 @@ +import { CommunityPageResolver } from '../community-page.resolver'; +import { EditCommunityPageComponent } from './edit-community-page.component'; +import { RouterModule } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { CommunityMetadataComponent } from './community-metadata/community-metadata.component'; +import { CommunityRolesComponent } from './community-roles/community-roles.component'; +import { CommunityCurateComponent } from './community-curate/community-curate.component'; + +/** + * Routing module that handles the routing for the Edit Community page administrator functionality + */ +@NgModule({ + imports: [ + RouterModule.forChild([ + { + path: '', + component: EditCommunityPageComponent, + resolve: { + dso: CommunityPageResolver + }, + children: [ + { + path: '', + redirectTo: 'metadata', + pathMatch: 'full' + }, + { + path: 'metadata', + component: CommunityMetadataComponent, + data: { title: 'community.edit.tabs.metadata.title' } + }, + { + path: 'roles', + component: CommunityRolesComponent, + data: { title: 'community.edit.tabs.roles.title' } + }, + { + path: 'curate', + component: CommunityCurateComponent, + data: { title: 'community.edit.tabs.curate.title' } + } + ] + } + ]) + ], + providers: [ + CommunityPageResolver, + ] +}) +export class EditCommunityPageRoutingModule { + +} diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 3748ebca9d..3293711d73 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -38,8 +38,8 @@ describe('EditRelationshipListComponent', () => { relationshipType = Object.assign(new RelationshipType(), { id: '1', uuid: '1', - leftLabel: 'isAuthorOfPublication', - rightLabel: 'isPublicationOfAuthor' + leftwardType: 'isAuthorOfPublication', + rightwardType: 'isPublicationOfAuthor' }); relationships = [ @@ -119,7 +119,7 @@ describe('EditRelationshipListComponent', () => { de = fixture.debugElement; comp.item = item; comp.url = url; - comp.relationshipLabel = relationshipType.leftLabel; + comp.relationshipLabel = relationshipType.leftwardType; fixture.detectChanges(); }); diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts index 3306d8eb01..e98da94c9d 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts @@ -34,8 +34,8 @@ describe('EditRelationshipComponent', () => { relationshipType = Object.assign(new RelationshipType(), { id: '1', uuid: '1', - leftLabel: 'isAuthorOfPublication', - rightLabel: 'isPublicationOfAuthor' + leftwardType: 'isAuthorOfPublication', + rightwardType: 'isPublicationOfAuthor' }); relationships = [ diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts index b1a4e11371..48bc28a1b9 100644 --- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts @@ -68,8 +68,8 @@ describe('ItemRelationshipsComponent', () => { relationshipType = Object.assign(new RelationshipType(), { id: '1', uuid: '1', - leftLabel: 'isAuthorOfPublication', - rightLabel: 'isPublicationOfAuthor' + leftwardType: 'isAuthorOfPublication', + rightwardType: 'isPublicationOfAuthor' }); relationships = [ diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index b4eda2abfb..9f120a87dd 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -60,10 +60,10 @@ export const filterRelationsByTypeLabel = (label: string, thisId?: string) => return relatedItems$.pipe( map((arr) => relsCurrentPage.filter((rel: Relationship, idx: number) => hasValue(relTypesCurrentPage[idx]) && ( - (hasNoValue(thisId) && (relTypesCurrentPage[idx].leftLabel === label || - relTypesCurrentPage[idx].rightLabel === label)) || - (thisId === arr[idx][0].id && relTypesCurrentPage[idx].leftLabel === label) || - (thisId === arr[idx][1].id && relTypesCurrentPage[idx].rightLabel === label) + (hasNoValue(thisId) && (relTypesCurrentPage[idx].leftwardType === label || + relTypesCurrentPage[idx].rightwardType === label)) || + (thisId === arr[idx][0].id && relTypesCurrentPage[idx].leftwardType === label) || + (thisId === arr[idx][1].id && relTypesCurrentPage[idx].rightwardType === label) ) )) ); diff --git a/src/app/core/cache/models/items/normalized-relationship-type.model.ts b/src/app/core/cache/models/items/normalized-relationship-type.model.ts index 800b27cd7e..23c3333a9b 100644 --- a/src/app/core/cache/models/items/normalized-relationship-type.model.ts +++ b/src/app/core/cache/models/items/normalized-relationship-type.model.ts @@ -23,7 +23,7 @@ export class NormalizedRelationshipType extends NormalizedObject { const relationshipType = Object.assign(new RelationshipType(), { id: '1', uuid: '1', - leftLabel: 'isAuthorOfPublication', - rightLabel: 'isPublicationOfAuthor' + leftwardType: 'isAuthorOfPublication', + rightwardType: 'isPublicationOfAuthor' }); const relationship1 = Object.assign(new Relationship(), { @@ -129,7 +128,7 @@ describe('RelationshipService', () => { describe('getItemRelationshipLabels', () => { it('should return the correct labels', () => { service.getItemRelationshipLabels(item).subscribe((result) => { - expect(result).toEqual([relationshipType.rightLabel]); + expect(result).toEqual([relationshipType.rightwardType]); }); }); }); @@ -144,7 +143,7 @@ describe('RelationshipService', () => { describe('getRelatedItemsByLabel', () => { it('should return the related items by label', () => { - service.getRelatedItemsByLabel(item, relationshipType.rightLabel).subscribe((result) => { + service.getRelatedItemsByLabel(item, relationshipType.rightwardType).subscribe((result) => { expect(result).toEqual(relatedItems); }); }); diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 1699b6a27d..b07e4b714c 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -182,9 +182,9 @@ export class RelationshipService { map(([leftItems, rightItems, relTypesCurrentPage]) => { return relTypesCurrentPage.map((type, index) => { if (leftItems[index].uuid === item.uuid) { - return type.leftLabel; + return type.leftwardType; } else { - return type.rightLabel; + return type.rightwardType; } }); }), diff --git a/src/app/core/shared/item-relationships/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts index 98454bc000..06ac94b041 100644 --- a/src/app/core/shared/item-relationships/relationship-type.model.ts +++ b/src/app/core/shared/item-relationships/relationship-type.model.ts @@ -33,7 +33,7 @@ export class RelationshipType implements CacheableObject { /** * The label that describes the Relation to the left of this RelationshipType */ - leftLabel: string; + leftwardType: string; /** * The maximum amount of Relationships allowed to the left of this RelationshipType @@ -48,7 +48,7 @@ export class RelationshipType implements CacheableObject { /** * The label that describes the Relation to the right of this RelationshipType */ - rightLabel: string; + rightwardType: string; /** * The maximum amount of Relationships allowed to the right of this RelationshipType diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts new file mode 100644 index 0000000000..5711aa4e70 --- /dev/null +++ b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts @@ -0,0 +1,189 @@ +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Community } from '../../../../core/shared/community.model'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../../shared.module'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { DataService } from '../../../../core/data/data.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComcolMetadataComponent } from './comcol-metadata.component'; +import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../testing/utils'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { NotificationsServiceStub } from '../../../testing/notifications-service-stub'; +import { NotificationsService } from '../../../notifications/notifications.service'; + +describe('ComColMetadataComponent', () => { + let comp: ComcolMetadataComponent; + let fixture: ComponentFixture>; + let dsoDataService: CommunityDataService; + let router: Router; + + let community; + let newCommunity; + let communityDataServiceStub; + let routerStub; + let routeStub; + + const logoEndpoint = 'rest/api/logo/endpoint'; + + function initializeVars() { + community = Object.assign(new Community(), { + uuid: 'a20da287-e174-466a-9926-f66b9300d347', + metadata: [{ + key: 'dc.title', + value: 'test community' + }] + }); + + newCommunity = Object.assign(new Community(), { + uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48', + metadata: [{ + key: 'dc.title', + value: 'new community' + }] + }); + + communityDataServiceStub = { + update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity), + getLogoEndpoint: () => observableOf(logoEndpoint) + }; + + routerStub = { + navigate: (commands) => commands + }; + + routeStub = { + parent: { + data: observableOf({ + dso: new RemoteData(false, false, true, null, community) + }) + } + }; + + } + + beforeEach(async(() => { + initializeVars(); + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], + providers: [ + { provide: ComColDataService, useValue: communityDataServiceStub }, + { provide: Router, useValue: routerStub }, + { provide: ActivatedRoute, useValue: routeStub }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ComcolMetadataComponent); + comp = fixture.componentInstance; + (comp as any).type = Community.type; + fixture.detectChanges(); + dsoDataService = (comp as any).dsoDataService; + router = (comp as any).router; + }); + + describe('onSubmit', () => { + let data; + + describe('with an empty queue in the uploader', () => { + beforeEach(() => { + data = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'test' + }] + }), + uploader: { + options: { + url: '' + }, + queue: [], + /* tslint:disable:no-empty */ + uploadAll: () => {} + /* tslint:enable:no-empty */ + } + } + }); + + it('should navigate when successful', () => { + spyOn(router, 'navigate'); + comp.onSubmit(data); + fixture.detectChanges(); + expect(router.navigate).toHaveBeenCalled(); + }); + + it('should not navigate on failure', () => { + spyOn(router, 'navigate'); + spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity)); + comp.onSubmit(data); + fixture.detectChanges(); + expect(router.navigate).not.toHaveBeenCalled(); + }); + }); + + describe('with at least one item in the uploader\'s queue', () => { + beforeEach(() => { + data = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'test' + }] + }), + uploader: { + options: { + url: '' + }, + queue: [ + {} + ], + /* tslint:disable:no-empty */ + uploadAll: () => {} + /* tslint:enable:no-empty */ + } + } + }); + + it('should not navigate', () => { + spyOn(router, 'navigate'); + comp.onSubmit(data); + fixture.detectChanges(); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should set the uploader\'s url to the logo\'s endpoint', () => { + comp.onSubmit(data); + fixture.detectChanges(); + expect(data.uploader.options.url).toEqual(logoEndpoint); + }); + + it('should call the uploader\'s uploadAll', () => { + spyOn(data.uploader, 'uploadAll'); + comp.onSubmit(data); + fixture.detectChanges(); + expect(data.uploader.uploadAll).toHaveBeenCalled(); + }); + }); + }); + + describe('navigateToHomePage', () => { + beforeEach(() => { + spyOn(router, 'navigate'); + comp.navigateToHomePage(); + }); + + it('should navigate', () => { + expect(router.navigate).toHaveBeenCalled(); + }); + }); + +}); diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts new file mode 100644 index 0000000000..2e4f4b745c --- /dev/null +++ b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit } from '@angular/core'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { ActivatedRoute, Router } from '@angular/router'; +import { first, map, take } from 'rxjs/operators'; +import { getSucceededRemoteData } from '../../../../core/shared/operators'; +import { isNotUndefined } from '../../../empty.util'; +import { DataService } from '../../../../core/data/data.service'; +import { ResourceType } from '../../../../core/shared/resource-type'; +import { ComColDataService } from '../../../../core/data/comcol-data.service'; +import { NotificationsService } from '../../../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'ds-comcol-metadata', + template: '' +}) +export class ComcolMetadataComponent implements OnInit { + /** + * Frontend endpoint for this type of DSO + */ + protected frontendURL: string; + /** + * The initial DSO object + */ + public dsoRD$: Observable>; + + /** + * The type of the dso + */ + protected type: ResourceType; + + public constructor( + protected dsoDataService: ComColDataService, + protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + protected translate: TranslateService + ) { + } + + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso)); + } + + /** + * Updates an existing DSO based on the submitted user data and navigates to the edited object's home page + * @param event The event returned by the community/collection form. Contains the new dso and logo uploader + */ + onSubmit(event) { + const dso = event.dso; + const uploader = event.uploader; + + this.dsoDataService.update(dso) + .pipe(getSucceededRemoteData()) + .subscribe((dsoRD: RemoteData) => { + if (isNotUndefined(dsoRD)) { + const newUUID = dsoRD.payload.uuid; + if (uploader.queue.length > 0) { + this.dsoDataService.getLogoEndpoint(newUUID).pipe(take(1)).subscribe((href: string) => { + uploader.options.url = href; + uploader.uploadAll(); + }); + } else { + this.router.navigate([this.frontendURL + newUUID]); + } + this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.notifications.success')); + } + }); + } + + /** + * Navigate to the home page of the object + */ + navigateToHomePage() { + this.dsoRD$.pipe( + getSucceededRemoteData(), + take(1) + ).subscribe((dsoRD: RemoteData) => { + this.router.navigate([this.frontendURL + dsoRD.payload.id]); + }); + } +} diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html new file mode 100644 index 0000000000..aa6290ea9f --- /dev/null +++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html @@ -0,0 +1,24 @@ +
+
+
+

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

+ +
+
+
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts index f2b45e8ed4..d1b87db7ae 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts +++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts @@ -1,5 +1,4 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { CommunityDataService } from '../../../core/data/community-data.service'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; @@ -10,29 +9,16 @@ import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { EditComColPageComponent } from './edit-comcol-page.component'; -import { - createFailedRemoteDataObject$, - createSuccessfulRemoteDataObject$ -} from '../../testing/utils'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { RemoteData } from '../../../core/data/remote-data'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationsServiceStub } from '../../testing/notifications-service-stub'; describe('EditComColPageComponent', () => { let comp: EditComColPageComponent; let fixture: ComponentFixture>; - let dsoDataService: CommunityDataService; let router: Router; let community; - let newCommunity; - let communityDataServiceStub; let routerStub; let routeStub; - const logoEndpoint = 'rest/api/logo/endpoint'; - function initializeVars() { community = Object.assign(new Community(), { uuid: 'a20da287-e174-466a-9926-f66b9300d347', @@ -42,27 +28,33 @@ describe('EditComColPageComponent', () => { }] }); - newCommunity = Object.assign(new Community(), { - uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48', - metadata: [{ - key: 'dc.title', - value: 'new community' - }] - }); - - communityDataServiceStub = { - update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity), - getLogoEndpoint: () => observableOf(logoEndpoint) - }; - routerStub = { - navigate: (commands) => commands + navigate: (commands) => commands, + events: observableOf({}), + url: 'mockUrl' }; routeStub = { data: observableOf({ - dso: new RemoteData(false, false, true, null, community) - }) + dso: community + }), + routeConfig: { + children: [ + { + path: 'mockUrl', + data: { + hideReturnButton: false + } + } + ] + }, + snapshot: { + firstChild: { + routeConfig: { + path: 'mockUrl' + } + } + } }; } @@ -72,10 +64,8 @@ describe('EditComColPageComponent', () => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], providers: [ - { provide: ComColDataService, useValue: communityDataServiceStub }, { provide: Router, useValue: routerStub }, { provide: ActivatedRoute, useValue: routeStub }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -84,105 +74,17 @@ describe('EditComColPageComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(EditComColPageComponent); comp = fixture.componentInstance; - (comp as any).type = Community.type; fixture.detectChanges(); - dsoDataService = (comp as any).dsoDataService; router = (comp as any).router; }); - describe('onSubmit', () => { - let data; - - describe('with an empty queue in the uploader', () => { - beforeEach(() => { - data = { - dso: Object.assign(new Community(), { - metadata: [{ - key: 'dc.title', - value: 'test' - }] - }), - uploader: { - options: { - url: '' - }, - queue: [], - /* tslint:disable:no-empty */ - uploadAll: () => {} - /* tslint:enable:no-empty */ - } - } - }); - - it('should navigate when successful', () => { - spyOn(router, 'navigate'); - comp.onSubmit(data); - fixture.detectChanges(); - expect(router.navigate).toHaveBeenCalled(); - }); - - it('should not navigate on failure', () => { - spyOn(router, 'navigate'); - spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity)); - comp.onSubmit(data); - fixture.detectChanges(); - expect(router.navigate).not.toHaveBeenCalled(); - }); - }); - - describe('with at least one item in the uploader\'s queue', () => { - beforeEach(() => { - data = { - dso: Object.assign(new Community(), { - metadata: [{ - key: 'dc.title', - value: 'test' - }] - }), - uploader: { - options: { - url: '' - }, - queue: [ - {} - ], - /* tslint:disable:no-empty */ - uploadAll: () => {} - /* tslint:enable:no-empty */ - } - } - }); - - it('should not navigate', () => { - spyOn(router, 'navigate'); - comp.onSubmit(data); - fixture.detectChanges(); - expect(router.navigate).not.toHaveBeenCalled(); - }); - - it('should set the uploader\'s url to the logo\'s endpoint', () => { - comp.onSubmit(data); - fixture.detectChanges(); - expect(data.uploader.options.url).toEqual(logoEndpoint); - }); - - it('should call the uploader\'s uploadAll', () => { - spyOn(data.uploader, 'uploadAll'); - comp.onSubmit(data); - fixture.detectChanges(); - expect(data.uploader.uploadAll).toHaveBeenCalled(); - }); - }); - }); - - describe('navigateToHomePage', () => { + describe('getPageUrl', () => { + let url; beforeEach(() => { - spyOn(router, 'navigate'); - comp.navigateToHomePage(); + url = comp.getPageUrl(community); }); - - it('should navigate', () => { - expect(router.navigate).toHaveBeenCalled(); + it('should return the current url as a fallback', () => { + expect(url).toEqual(routerStub.url); }); }); }); diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts index 19ba578cd6..b1b37f265e 100644 --- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts +++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts @@ -2,15 +2,11 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { RemoteData } from '../../../core/data/remote-data'; -import { isNotUndefined } from '../../empty.util'; -import { first, map, take } from 'rxjs/operators'; +import { isNotEmpty, isNotUndefined } from '../../empty.util'; +import { first, map } from 'rxjs/operators'; import { getSucceededRemoteData } from '../../../core/shared/operators'; import { DataService } from '../../../core/data/data.service'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { ComColDataService } from '../../../core/data/comcol-data.service'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { TranslateService } from '@ngx-translate/core'; -import { ResourceType } from '../../../core/shared/resource-type'; /** * Component representing the edit page for communities and collections @@ -21,67 +17,54 @@ import { ResourceType } from '../../../core/shared/resource-type'; }) export class EditComColPageComponent implements OnInit { /** - * Frontend endpoint for this type of DSO + * The type of DSpaceObject (used to create i18n messages) */ - protected frontendURL: string; + protected type: string; + /** - * The initial DSO object + * The current page outlet string + */ + public currentPage: string; + + /** + * All possible page outlet strings + */ + public pages: string[]; + + /** + * The DSO to render the edit page for */ public dsoRD$: Observable>; /** - * The type of the dso + * Hide the default return button? */ - protected type: ResourceType; + public hideReturnButton: boolean; public constructor( - protected dsoDataService: ComColDataService, protected router: Router, - protected route: ActivatedRoute, - protected notificationsService: NotificationsService, - protected translate: TranslateService + protected route: ActivatedRoute ) { + this.router.events.subscribe(() => { + this.currentPage = this.route.snapshot.firstChild.routeConfig.path; + this.hideReturnButton = this.route.routeConfig.children + .find((child: any) => child.path === this.currentPage).data.hideReturnButton; + }); } ngOnInit(): void { + this.pages = this.route.routeConfig.children + .map((child: any) => child.path) + .filter((path: string) => isNotEmpty(path)); // ignore reroutes this.dsoRD$ = this.route.data.pipe(first(), map((data) => data.dso)); } /** - * Updates an existing DSO based on the submitted user data and navigates to the edited object's home page - * @param event The event returned by the community/collection form. Contains the new dso and logo uploader + * Get the dso's page url + * This method is expected to be overridden in the edit community/collection page components + * @param dso The DSpaceObject for which the url is requested */ - onSubmit(event) { - const dso = event.dso; - const uploader = event.uploader; - - this.dsoDataService.update(dso) - .pipe(getSucceededRemoteData()) - .subscribe((dsoRD: RemoteData) => { - if (isNotUndefined(dsoRD)) { - const newUUID = dsoRD.payload.uuid; - if (uploader.queue.length > 0) { - this.dsoDataService.getLogoEndpoint(newUUID).pipe(take(1)).subscribe((href: string) => { - uploader.options.url = href; - uploader.uploadAll(); - }); - } else { - this.router.navigate([this.frontendURL + newUUID]); - } - this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.notifications.success')); - } - }); - } - - /** - * Navigate to the home page of the object - */ - navigateToHomePage() { - this.dsoRD$.pipe( - getSucceededRemoteData(), - take(1) - ).subscribe((dsoRD: RemoteData) => { - this.router.navigate([this.frontendURL + dsoRD.payload.id]); - }); + getPageUrl(dso: TDomain): string { + return this.router.url; } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 2367652dd3..8054bea56e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -138,6 +138,8 @@ import { RoleDirective } from './roles/role.directive'; import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; import { ItemDetailPreviewFieldComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; +import { AbstractTrackableComponent } from './trackable/abstract-trackable.component'; +import { ComcolMetadataComponent } from './comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.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 { TypedItemSearchResultGridElementComponent } from './object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component'; @@ -266,6 +268,8 @@ const COMPONENTS = [ TypedItemSearchResultGridElementComponent, ItemTypeSwitcherComponent, BrowseByComponent, + AbstractTrackableComponent, + ComcolMetadataComponent, ItemTypeBadgeComponent ]; @@ -321,6 +325,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ const PROVIDERS = [ TruncatableService, MockAdminGuard, + AbstractTrackableComponent, { provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn diff --git a/src/app/shared/trackable/abstract-trackable.component.spec.ts b/src/app/shared/trackable/abstract-trackable.component.spec.ts new file mode 100644 index 0000000000..3755092263 --- /dev/null +++ b/src/app/shared/trackable/abstract-trackable.component.spec.ts @@ -0,0 +1,101 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { AbstractTrackableComponent } from './abstract-trackable.component'; +import { INotification, Notification } from '../notifications/models/notification.model'; +import { NotificationType } from '../notifications/models/notification-type'; +import { of as observableOf } from 'rxjs'; +import { TranslateModule } from '@ngx-translate/core'; +import { ObjectUpdatesService } from '../../core/data/object-updates/object-updates.service'; +import { NotificationsService } from '../notifications/notifications.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { TestScheduler } from 'rxjs/testing'; +import { getTestScheduler } from 'jasmine-marbles'; + +describe('AbstractTrackableComponent', () => { + let comp: AbstractTrackableComponent; + let fixture: ComponentFixture; + let objectUpdatesService; + let scheduler: TestScheduler; + + const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info'); + const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning'); + const successNotification: INotification = new Notification('id', NotificationType.Success, 'success'); + + const notificationsService = jasmine.createSpyObj('notificationsService', + { + info: infoNotification, + warning: warningNotification, + success: successNotification + } + ); + + const url = 'http://test-url.com/test-url'; + + beforeEach(async(() => { + objectUpdatesService = jasmine.createSpyObj('objectUpdatesService', + { + saveAddFieldUpdate: {}, + discardFieldUpdates: {}, + reinstateFieldUpdates: observableOf(true), + initialize: {}, + hasUpdates: observableOf(true), + isReinstatable: observableOf(false), // should always return something --> its in ngOnInit + isValidPage: observableOf(true) + } + ); + + scheduler = getTestScheduler(); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [AbstractTrackableComponent], + providers: [ + {provide: ObjectUpdatesService, useValue: objectUpdatesService}, + {provide: NotificationsService, useValue: notificationsService}, + ], schemas: [ + NO_ERRORS_SCHEMA + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AbstractTrackableComponent); + comp = fixture.componentInstance; + comp.url = url; + + fixture.detectChanges(); + }); + + it('should discard object updates', () => { + comp.discard(); + + expect(objectUpdatesService.discardFieldUpdates).toHaveBeenCalledWith(url, infoNotification); + }); + it('should undo the discard of object updates', () => { + comp.reinstate(); + + expect(objectUpdatesService.reinstateFieldUpdates).toHaveBeenCalledWith(url); + }); + + describe('isReinstatable', () => { + beforeEach(() => { + objectUpdatesService.isReinstatable.and.returnValue(observableOf(true)); + }); + + it('should return an observable that emits true', () => { + const expected = '(a|)'; + scheduler.expectObservable(comp.isReinstatable()).toBe(expected, {a: true}); + }); + }); + + describe('hasChanges', () => { + beforeEach(() => { + objectUpdatesService.hasUpdates.and.returnValue(observableOf(true)); + }); + + it('should return an observable that emits true', () => { + const expected = '(a|)'; + scheduler.expectObservable(comp.hasChanges()).toBe(expected, {a: true}); + }); + }); + +}); diff --git a/src/app/shared/trackable/abstract-trackable.component.ts b/src/app/shared/trackable/abstract-trackable.component.ts new file mode 100644 index 0000000000..cd1b425f10 --- /dev/null +++ b/src/app/shared/trackable/abstract-trackable.component.ts @@ -0,0 +1,78 @@ +import { ObjectUpdatesService } from '../../core/data/object-updates/object-updates.service'; +import { NotificationsService } from '../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { Component } from '@angular/core'; + +/** + * Abstract Component that is able to track changes made in the inheriting component using the ObjectUpdateService + */ +@Component({ + selector: 'ds-abstract-trackable', + template: '' +}) +export class AbstractTrackableComponent { + + /** + * The time span for being able to undo discarding changes + */ + public discardTimeOut: number; + public message: string; + public url: string; + public notificationsPrefix = 'static-pages.form.notification'; + + constructor( + public objectUpdatesService: ObjectUpdatesService, + public notificationsService: NotificationsService, + public translateService: TranslateService, + ) { + + } + + /** + * Request the object updates service to discard all current changes to this item + * Shows a notification to remind the user that they can undo this + */ + discard() { + const undoNotification = this.notificationsService.info(this.getNotificationTitle('discarded'), this.getNotificationContent('discarded'), {timeOut: this.discardTimeOut}); + this.objectUpdatesService.discardFieldUpdates(this.url, undoNotification); + } + + /** + * Request the object updates service to undo discarding all changes to this item + */ + reinstate() { + this.objectUpdatesService.reinstateFieldUpdates(this.url); + } + + /** + * Checks whether or not the object is currently reinstatable + */ + isReinstatable(): Observable { + return this.objectUpdatesService.isReinstatable(this.url); + } + + /** + * Checks whether or not there are currently updates for this object + */ + hasChanges(): Observable { + return this.objectUpdatesService.hasUpdates(this.url); + } + + /** + * Get translated notification title + * @param key + */ + private getNotificationTitle(key: string) { + return this.translateService.instant(this.notificationsPrefix + key + '.title'); + } + + /** + * Get translated notification content + * @param key + */ + private getNotificationContent(key: string) { + return this.translateService.instant(this.notificationsPrefix + key + '.content'); + + } +}