diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 02c63d316d8..3cbd08f1325 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -34,9 +34,6 @@ -
- -
diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 6d5262d9338..671bf28fd1c 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -21,9 +21,6 @@ -
- -
diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts index abfe618174b..e28a416f230 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts @@ -1,6 +1,6 @@ import { TestBed, waitForAsync } from '@angular/core/testing'; import { MenuServiceStub } from '../testing/menu-service.stub'; -import { of as observableOf } from 'rxjs'; +import { combineLatest, map, of as observableOf } from 'rxjs'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; @@ -16,10 +16,13 @@ import { Item } from '../../core/shared/item.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { MenuID } from '../menu/menu-id.model'; import { MenuItemType } from '../menu/menu-item-type.model'; -import { TextMenuItemModel } from '../menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../menu/menu-item/models/link.model'; import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; import { NotificationsService } from '../notifications/notifications.service'; +import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { Community } from '../../core/shared/community.model'; +import { Collection } from '../../core/shared/collection.model'; +import flatten from 'lodash/flatten'; describe('DSOEditMenuResolver', () => { @@ -37,25 +40,44 @@ describe('DSOEditMenuResolver', () => { let notificationsService; let translate; - const route = { - data: { - menu: { - 'statistics': [{ - id: 'statistics-dummy-1', - active: false, - visible: true, - model: null - }] - } - }, - params: {id: 'test-uuid'}, + const dsoRoute = (dso: DSpaceObject) => { + return { + data: { + menu: { + 'statistics': [{ + id: 'statistics-dummy-1', + active: false, + visible: true, + model: null + }] + } + }, + params: {id: dso.uuid}, + }; }; const state = { url: 'test-url' }; - const testObject = Object.assign(new Item(), {uuid: 'test-uuid', type: 'item', _links: {self: {href: 'self-link'}}}); + const testCommunity: Community = Object.assign(new Community(), { + uuid: 'test-community-uuid', + type: 'community', + _links: {self: {href: 'self-link'}}, + }); + const testCollection: Collection = Object.assign(new Collection(), { + uuid: 'test-collection-uuid', + type: 'collection', + _links: {self: {href: 'self-link'}}, + }); + const testItem: Item = Object.assign(new Item(), { + uuid: 'test-item-uuid', + type: 'item', + _links: {self: {href: 'self-link'}}, + }); + + let testObject: DSpaceObject; + let route; const dummySections1 = [{ id: 'dummy-1', @@ -90,6 +112,10 @@ describe('DSOEditMenuResolver', () => { }]; beforeEach(waitForAsync(() => { + // test with Items unless specified otherwise + testObject = testItem; + route = dsoRoute(testItem); + menuService = new MenuServiceStub(); spyOn(menuService, 'getMenu').and.returnValue(observableOf(MENU_STATE)); @@ -154,16 +180,17 @@ describe('DSOEditMenuResolver', () => { { ...route.data.menu, [MenuID.DSO_EDIT]: [ - ...dummySections1.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})), - ...dummySections2.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})) + ...dummySections1.map((menu) => Object.assign(menu, {id: menu.id + '-test-item-uuid'})), + ...dummySections2.map((menu) => Object.assign(menu, {id: menu.id + '-test-item-uuid'})) ] } ); - expect(dSpaceObjectDataService.findById).toHaveBeenCalledWith('test-uuid', true, false); + expect(dSpaceObjectDataService.findById).toHaveBeenCalledWith('test-item-uuid', true, false); expect(resolver.getDsoMenus).toHaveBeenCalled(); done(); }); }); + it('should create all menus when a dso is found based on the route scope query param when no id param is present', (done) => { spyOn(resolver, 'getDsoMenus').and.returnValue( [observableOf(dummySections1), observableOf(dummySections2)] @@ -198,6 +225,7 @@ describe('DSOEditMenuResolver', () => { done(); }); }); + it('should return the statistics menu when no dso is found', (done) => { (dSpaceObjectDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$()); @@ -211,49 +239,165 @@ describe('DSOEditMenuResolver', () => { }); }); }); + describe('getDsoMenus', () => { - it('should return as first part the item version, orcid and claim list ', (done) => { - const result = resolver.getDsoMenus(testObject, route, state); - result[0].subscribe((menuList) => { - expect(menuList.length).toEqual(3); - expect(menuList[0].id).toEqual('orcid-dso'); - expect(menuList[0].active).toEqual(false); - // Visible should be false due to the item not being of type person - expect(menuList[0].visible).toEqual(false); - expect(menuList[0].model.type).toEqual(MenuItemType.LINK); - - expect(menuList[1].id).toEqual('version-dso'); - expect(menuList[1].active).toEqual(false); - expect(menuList[1].visible).toEqual(true); - expect(menuList[1].model.type).toEqual(MenuItemType.ONCLICK); - expect((menuList[1].model as TextMenuItemModel).text).toEqual('message'); - expect(menuList[1].model.disabled).toEqual(false); - expect(menuList[1].icon).toEqual('code-branch'); - - expect(menuList[2].id).toEqual('claim-dso'); - expect(menuList[2].active).toEqual(false); - // Visible should be false due to the item not being of type person - expect(menuList[2].visible).toEqual(false); - expect(menuList[2].model.type).toEqual(MenuItemType.ONCLICK); - expect((menuList[2].model as TextMenuItemModel).text).toEqual('item.page.claim.button'); - done(); + describe('for Communities', () => { + beforeEach(() => { + testObject = testCommunity; + dSpaceObjectDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(testCommunity)); + route = dsoRoute(testCommunity); + }); + + it('should not return Item-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const orcidEntry = menu.find(entry => entry.id === 'orcid-dso'); + expect(orcidEntry).toBeFalsy(); + + const versionEntry = menu.find(entry => entry.id === 'version-dso'); + expect(versionEntry).toBeFalsy(); + + const claimEntry = menu.find(entry => entry.id === 'claim-dso'); + expect(claimEntry).toBeFalsy(); + + done(); + }); + }); + + it('should return Community/Collection-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const subscribeEntry = menu.find(entry => entry.id === 'subscribe'); + expect(subscribeEntry).toBeTruthy(); + expect(subscribeEntry.active).toBeFalse(); + expect(subscribeEntry.visible).toBeTrue(); + expect(subscribeEntry.model.type).toEqual(MenuItemType.ONCLICK); + done(); + }); }); + it('should return as third part the common list ', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const editEntry = menu.find(entry => entry.id === 'edit-dso'); + expect(editEntry).toBeTruthy(); + expect(editEntry.active).toBeFalse(); + expect(editEntry.visible).toBeTrue(); + expect(editEntry.model.type).toEqual(MenuItemType.LINK); + expect((editEntry.model as LinkMenuItemModel).link).toEqual( + '/communities/test-community-uuid/edit/metadata' + ); + done(); + }); + }); }); - it('should return as second part the common list ', (done) => { - const result = resolver.getDsoMenus(testObject, route, state); - result[1].subscribe((menuList) => { - expect(menuList.length).toEqual(1); - expect(menuList[0].id).toEqual('edit-dso'); - expect(menuList[0].active).toEqual(false); - expect(menuList[0].visible).toEqual(true); - expect(menuList[0].model.type).toEqual(MenuItemType.LINK); - expect((menuList[0].model as LinkMenuItemModel).text).toEqual('item.page.edit'); - expect((menuList[0].model as LinkMenuItemModel).link).toEqual('/items/test-uuid/edit/metadata'); - expect(menuList[0].icon).toEqual('pencil-alt'); - done(); + + describe('for Collections', () => { + beforeEach(() => { + testObject = testCollection; + dSpaceObjectDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(testCollection)); + route = dsoRoute(testCollection); + }); + + it('should not return Item-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const orcidEntry = menu.find(entry => entry.id === 'orcid-dso'); + expect(orcidEntry).toBeFalsy(); + + const versionEntry = menu.find(entry => entry.id === 'version-dso'); + expect(versionEntry).toBeFalsy(); + + const claimEntry = menu.find(entry => entry.id === 'claim-dso'); + expect(claimEntry).toBeFalsy(); + + done(); + }); + }); + + it('should return Community/Collection-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const subscribeEntry = menu.find(entry => entry.id === 'subscribe'); + expect(subscribeEntry).toBeTruthy(); + expect(subscribeEntry.active).toBeFalse(); + expect(subscribeEntry.visible).toBeTrue(); + expect(subscribeEntry.model.type).toEqual(MenuItemType.ONCLICK); + done(); + }); + }); + + it('should return as third part the common list ', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const editEntry = menu.find(entry => entry.id === 'edit-dso'); + expect(editEntry).toBeTruthy(); + expect(editEntry.active).toBeFalse(); + expect(editEntry.visible).toBeTrue(); + expect(editEntry.model.type).toEqual(MenuItemType.LINK); + expect((editEntry.model as LinkMenuItemModel).link).toEqual( + '/collections/test-collection-uuid/edit/metadata' + ); + done(); + }); }); + }); + + describe('for Items', () => { + beforeEach(() => { + testObject = testItem; + dSpaceObjectDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(testItem)); + route = dsoRoute(testItem); + }); + + it('should return Item-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const orcidEntry = menu.find(entry => entry.id === 'orcid-dso'); + expect(orcidEntry).toBeTruthy(); + expect(orcidEntry.active).toBeFalse(); + expect(orcidEntry.visible).toBeFalse(); + expect(orcidEntry.model.type).toEqual(MenuItemType.LINK); + + const versionEntry = menu.find(entry => entry.id === 'version-dso'); + expect(versionEntry).toBeTruthy(); + expect(versionEntry.active).toBeFalse(); + expect(versionEntry.visible).toBeTrue(); + expect(versionEntry.model.type).toEqual(MenuItemType.ONCLICK); + expect(versionEntry.model.disabled).toBeFalse(); + const claimEntry = menu.find(entry => entry.id === 'claim-dso'); + expect(claimEntry).toBeTruthy(); + expect(claimEntry.active).toBeFalse(); + expect(claimEntry.visible).toBeFalse(); + expect(claimEntry.model.type).toEqual(MenuItemType.ONCLICK); + done(); + }); + }); + + it('should not return Community/Collection-specific entries', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const subscribeEntry = menu.find(entry => entry.id === 'subscribe'); + expect(subscribeEntry).toBeFalsy(); + done(); + }); + }); + + it('should return as third part the common list ', (done) => { + const result = resolver.getDsoMenus(testObject, route, state); + combineLatest(result).pipe(map(flatten)).subscribe((menu) => { + const editEntry = menu.find(entry => entry.id === 'edit-dso'); + expect(editEntry).toBeTruthy(); + expect(editEntry.active).toBeFalse(); + expect(editEntry.visible).toBeTrue(); + expect(editEntry.model.type).toEqual(MenuItemType.LINK); + expect((editEntry.model as LinkMenuItemModel).link).toEqual( + '/items/test-item-uuid/edit/metadata' + ); + done(); + }); + }); }); }); }); diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts index 80a69c2830d..1ade4578405 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -21,6 +21,9 @@ import { getDSORoute } from '../../app-routing-paths'; import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; import { NotificationsService } from '../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; +import { SubscriptionModalComponent } from '../subscriptions/subscription-modal/subscription-modal.component'; +import { Community } from '../../core/shared/community.model'; +import { Collection } from '../../core/shared/collection.model'; /** * Creates the menus for the dspace object pages @@ -84,6 +87,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection getDsoMenus(dso, route, state): Observable[] { return [ this.getItemMenu(dso), + this.getComColMenu(dso), this.getCommonMenu(dso, state) ]; } @@ -178,6 +182,39 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection } } + /** + * Get Community/Collection-specific menus + */ + protected getComColMenu(dso): Observable { + if (dso instanceof Community || dso instanceof Collection) { + return combineLatest([ + this.authorizationService.isAuthorized(FeatureID.CanSubscribe, dso.self), + ]).pipe( + map(([canSubscribe]) => { + return [ + { + id: 'subscribe', + active: false, + visible: canSubscribe, + model: { + type: MenuItemType.ONCLICK, + text: 'subscriptions.tooltip', + function: () => { + const modalRef = this.modalService.open(SubscriptionModalComponent); + modalRef.componentInstance.dso = dso; + } + } as OnClickMenuItemModel, + icon: 'bell', + index: 4 + }, + ]; + }) + ); + } else { + return observableOf([]); + } + } + /** * Claim a researcher by creating a profile * Shows notifications and/or hides the menu section on success/error diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html deleted file mode 100644 index 15135009fcd..00000000000 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.html +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.scss b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts deleted file mode 100644 index 726854778dc..00000000000 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DsoPageSubscriptionButtonComponent } from './dso-page-subscription-button.component'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { of as observableOf } from 'rxjs'; -import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; -import { By } from '@angular/platform-browser'; -import { DebugElement } from '@angular/core'; -import { Item } from '../../../core/shared/item.model'; -import { ITEM } from '../../../core/shared/item.resource-type'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; - -describe('DsoPageSubscriptionButtonComponent', () => { - let component: DsoPageSubscriptionButtonComponent; - let fixture: ComponentFixture; - let de: DebugElement; - - const authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: jasmine.createSpy('isAuthorized') // observableOf(true) - }); - - const mockItem = Object.assign(new Item(), { - id: 'fake-id', - uuid: 'fake-id', - handle: 'fake/handle', - lastModified: '2018', - type: ITEM, - _links: { - self: { - href: 'https://localhost:8000/items/fake-id' - } - } - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - NgbModalModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }) - ], - declarations: [ DsoPageSubscriptionButtonComponent ], - providers: [ - { provide: AuthorizationDataService, useValue: authorizationService }, - ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DsoPageSubscriptionButtonComponent); - component = fixture.componentInstance; - de = fixture.debugElement; - component.dso = mockItem; - }); - - describe('when is authorized', () => { - beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(true)); - fixture.detectChanges(); - }); - - it('should display subscription button', () => { - expect(de.query(By.css(' [data-test="subscription-button"]'))).toBeTruthy(); - }); - }); - - describe('when is not authorized', () => { - beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(false)); - fixture.detectChanges(); - }); - - it('should not display subscription button', () => { - expect(de.query(By.css(' [data-test="subscription-button"]'))).toBeNull(); - }); - }); -}); diff --git a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts b/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts deleted file mode 100644 index 54cd9e6bb0d..00000000000 --- a/src/app/shared/dso-page/dso-page-subscription-button/dso-page-subscription-button.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { Observable, of } from 'rxjs'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; - -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; -import { SubscriptionModalComponent } from '../../subscriptions/subscription-modal/subscription-modal.component'; -import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; - -@Component({ - selector: 'ds-dso-page-subscription-button', - templateUrl: './dso-page-subscription-button.component.html', - styleUrls: ['./dso-page-subscription-button.component.scss'] -}) -/** - * Display a button that opens the modal to manage subscriptions - */ -export class DsoPageSubscriptionButtonComponent implements OnInit { - - /** - * Whether the current user is authorized to edit the DSpaceObject - */ - isAuthorized$: Observable = of(false); - - /** - * Reference to NgbModal - */ - public modalRef: NgbModalRef; - - /** - * DSpaceObject that is being viewed - */ - @Input() dso: DSpaceObject; - - constructor( - protected authorizationService: AuthorizationDataService, - private modalService: NgbModal, - ) { - } - - /** - * check if the current DSpaceObject can be subscribed by the user - */ - ngOnInit(): void { - this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanSubscribe, this.dso.self); - } - - /** - * Open the modal to subscribe to the related DSpaceObject - */ - public openSubscriptionModal() { - this.modalRef = this.modalService.open(SubscriptionModalComponent); - this.modalRef.componentInstance.dso = this.dso; - } - -} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 0f7871f7f9b..c6e2ddc3f37 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -273,9 +273,6 @@ import { AdvancedClaimedTaskActionRatingComponent } from './mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component'; import { ClaimedTaskActionsDeclineTaskComponent } from './mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component'; -import { - DsoPageSubscriptionButtonComponent -} from './dso-page/dso-page-subscription-button/dso-page-subscription-button.component'; import { EpersonGroupListComponent } from './eperson-group-list/eperson-group-list.component'; import { EpersonSearchBoxComponent } from './eperson-group-list/eperson-search-box/eperson-search-box.component'; import { GroupSearchBoxComponent } from './eperson-group-list/group-search-box/group-search-box.component'; @@ -395,7 +392,6 @@ const COMPONENTS = [ ItemPageTitleFieldComponent, ThemedSearchNavbarComponent, ListableNotificationObjectComponent, - DsoPageSubscriptionButtonComponent, MetadataFieldWrapperComponent, ContextHelpWrapperComponent, EpersonGroupListComponent,