mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-11 20:13:07 +00:00
95632: Refactor orcid/claim person buttons to dso-menu-resolver
This commit is contained in:
@@ -3,8 +3,8 @@ import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Operation, ReplaceOperation } from 'fast-json-patch';
|
||||
import { Observable } from 'rxjs';
|
||||
import { find, map } from 'rxjs/operators';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { find, map, mergeMap } from 'rxjs/operators';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
@@ -130,6 +130,24 @@ export class ResearcherProfileDataService extends IdentifiableDataService<Resear
|
||||
return this.rdbService.buildFromRequestUUID(requestId, followLink('item'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a researcher profile starting from an external source URI and returns the related item's ID
|
||||
* Emits null if the researcher profile doesn't exist after sending out the request
|
||||
* @param sourceUri
|
||||
*/
|
||||
createFromExternalSourceAndReturnRelatedItemId(sourceUri: string): Observable<string> {
|
||||
return this.createFromExternalSource(sourceUri).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
mergeMap((rd: RemoteData<ResearcherProfile>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
return this.findRelatedItemId(rd.payload);
|
||||
} else {
|
||||
return observableOf(null);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new object on the server, and store the response in the object cache
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MenuServiceStub } from '../testing/menu-service.stub';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AdminSidebarComponent } from '../../admin/admin-sidebar/admin-sidebar.component';
|
||||
@@ -18,6 +18,8 @@ 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';
|
||||
|
||||
describe('DSOEditMenuResolver', () => {
|
||||
|
||||
@@ -31,6 +33,9 @@ describe('DSOEditMenuResolver', () => {
|
||||
let menuService;
|
||||
let authorizationService;
|
||||
let dsoVersioningModalService;
|
||||
let researcherProfileService;
|
||||
let notificationsService;
|
||||
let translate;
|
||||
|
||||
const route = {
|
||||
data: {
|
||||
@@ -99,6 +104,16 @@ describe('DSOEditMenuResolver', () => {
|
||||
getVersioningTooltipMessage: observableOf('message'),
|
||||
openCreateVersionModal: {}
|
||||
});
|
||||
researcherProfileService = jasmine.createSpyObj('researcherProfileService', {
|
||||
createFromExternalSourceAndReturnRelatedItemId: observableOf('mock-id'),
|
||||
});
|
||||
translate = jasmine.createSpyObj('translate', {
|
||||
get: observableOf('translated-message'),
|
||||
});
|
||||
notificationsService = jasmine.createSpyObj('notificationsService', {
|
||||
success: {},
|
||||
error: {},
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
|
||||
@@ -108,6 +123,9 @@ describe('DSOEditMenuResolver', () => {
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||
{provide: DsoVersioningModalService, useValue: dsoVersioningModalService},
|
||||
{provide: ResearcherProfileDataService, useValue: researcherProfileService},
|
||||
{provide: TranslateService, useValue: translate},
|
||||
{provide: NotificationsService, useValue: notificationsService},
|
||||
{
|
||||
provide: NgbModal, useValue: {
|
||||
open: () => {/*comment*/
|
||||
@@ -194,17 +212,30 @@ describe('DSOEditMenuResolver', () => {
|
||||
});
|
||||
});
|
||||
describe('getDsoMenus', () => {
|
||||
it('should return as first part the item version list ', (done) => {
|
||||
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(1);
|
||||
expect(menuList[0].id).toEqual('version-dso');
|
||||
expect(menuList.length).toEqual(3);
|
||||
expect(menuList[0].id).toEqual('orcid-dso');
|
||||
expect(menuList[0].active).toEqual(false);
|
||||
expect(menuList[0].visible).toEqual(true);
|
||||
expect(menuList[0].model.type).toEqual(MenuItemType.ONCLICK);
|
||||
expect((menuList[0].model as TextMenuItemModel).text).toEqual('message');
|
||||
expect(menuList[0].model.disabled).toEqual(false);
|
||||
expect(menuList[0].icon).toEqual('code-branch');
|
||||
// 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();
|
||||
});
|
||||
|
||||
|
@@ -9,15 +9,18 @@ import { Item } from '../../core/shared/item.model';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { OnClickMenuItemModel } from '../menu/menu-item/models/onclick.model';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||
import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service';
|
||||
import { hasNoValue, hasValue } from '../empty.util';
|
||||
import { hasNoValue, hasValue, isNotEmpty } from '../empty.util';
|
||||
import { MenuID } from '../menu/menu-id.model';
|
||||
import { MenuItemType } from '../menu/menu-item-type.model';
|
||||
import { MenuSection } from '../menu/menu-section.model';
|
||||
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';
|
||||
|
||||
/**
|
||||
* Creates the menus for the dspace object pages
|
||||
@@ -33,6 +36,9 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
protected authorizationService: AuthorizationDataService,
|
||||
protected modalService: NgbModal,
|
||||
protected dsoVersioningModalService: DsoVersioningModalService,
|
||||
protected researcherProfileService: ResearcherProfileDataService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -96,7 +102,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
link: new URLCombiner(getDSORoute(dso), 'edit', 'metadata').toString()
|
||||
} as LinkMenuItemModel,
|
||||
icon: 'pencil-alt',
|
||||
index: 1
|
||||
index: 2
|
||||
},
|
||||
];
|
||||
})
|
||||
@@ -104,17 +110,32 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item sepcific menus
|
||||
* Get item specific menus
|
||||
*/
|
||||
protected getItemMenu(dso): Observable<MenuSection[]> {
|
||||
if (dso instanceof Item) {
|
||||
return combineLatest([
|
||||
this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, dso.self),
|
||||
this.dsoVersioningModalService.isNewVersionButtonDisabled(dso),
|
||||
this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create')
|
||||
this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create'),
|
||||
this.authorizationService.isAuthorized(FeatureID.CanSynchronizeWithORCID, dso.self),
|
||||
this.authorizationService.isAuthorized(FeatureID.CanClaimItem, dso.self),
|
||||
]).pipe(
|
||||
map(([canCreateVersion, disableVersioning, versionTooltip]) => {
|
||||
map(([canCreateVersion, disableVersioning, versionTooltip, canSynchronizeWithOrcid, canClaimItem]) => {
|
||||
const isPerson = this.getDsoType(dso) === 'person';
|
||||
return [
|
||||
{
|
||||
id: 'orcid-dso',
|
||||
active: false,
|
||||
visible: isPerson && canSynchronizeWithOrcid,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: 'item.page.orcid.tooltip',
|
||||
link: new URLCombiner(getDSORoute(dso), 'orcid').toString()
|
||||
} as LinkMenuItemModel,
|
||||
icon: 'orcid fab fa-lg',
|
||||
index: 0
|
||||
},
|
||||
{
|
||||
id: 'version-dso',
|
||||
active: false,
|
||||
@@ -128,7 +149,21 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
}
|
||||
} as OnClickMenuItemModel,
|
||||
icon: 'code-branch',
|
||||
index: 0
|
||||
index: 1
|
||||
},
|
||||
{
|
||||
id: 'claim-dso',
|
||||
active: false,
|
||||
visible: isPerson && canClaimItem,
|
||||
model: {
|
||||
type: MenuItemType.ONCLICK,
|
||||
text: 'item.page.claim.button',
|
||||
function: () => {
|
||||
this.claimResearcher(dso);
|
||||
}
|
||||
} as OnClickMenuItemModel,
|
||||
icon: 'hand-paper',
|
||||
index: 3
|
||||
},
|
||||
];
|
||||
}),
|
||||
@@ -138,6 +173,26 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim a researcher by creating a profile
|
||||
* Shows notifications and/or hides the menu section on success/error
|
||||
*/
|
||||
protected claimResearcher(dso) {
|
||||
this.researcherProfileService.createFromExternalSourceAndReturnRelatedItemId(dso.self)
|
||||
.subscribe((id: string) => {
|
||||
if (isNotEmpty(id)) {
|
||||
this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'),
|
||||
this.translate.get('researcherprofile.success.claim.body'));
|
||||
this.authorizationService.invalidateAuthorizationsRequestCache();
|
||||
this.menuService.hideMenuSection(MenuID.DSO_EDIT, 'claim-dso-' + dso.uuid);
|
||||
} else {
|
||||
this.notificationsService.error(
|
||||
this.translate.get('researcherprofile.error.claim.title'),
|
||||
this.translate.get('researcherprofile.error.claim.body'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dso or entity type for an object to be used in generic messages
|
||||
*/
|
||||
|
@@ -1,6 +0,0 @@
|
||||
<a *ngIf="isAuthorized | async"
|
||||
[ngbTooltip]="'item.page.orcid.tooltip' | translate"
|
||||
[routerLink]="[pageRoute, 'orcid']"
|
||||
class="btn btn-dark btn-sm"
|
||||
role="button" ><i class="fab fa-orcid fa-lg"></i>
|
||||
</a>
|
@@ -1,76 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { DsoPageOrcidButtonComponent } from './dso-page-orcid-button.component';
|
||||
|
||||
describe('DsoPageOrcidButtonComponent', () => {
|
||||
let component: DsoPageOrcidButtonComponent;
|
||||
let fixture: ComponentFixture<DsoPageOrcidButtonComponent>;
|
||||
|
||||
let authorizationService: AuthorizationDataService;
|
||||
let dso: DSpaceObject;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
dso = Object.assign(new Item(), {
|
||||
id: 'test-item',
|
||||
_links: {
|
||||
self: { href: 'test-item-selflink' }
|
||||
}
|
||||
});
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DsoPageOrcidButtonComponent],
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
||||
providers: [
|
||||
{ provide: AuthorizationDataService, useValue: authorizationService }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DsoPageOrcidButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.dso = dso;
|
||||
component.pageRoute = 'test';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should check the authorization of the current user', () => {
|
||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanSynchronizeWithORCID, dso.self);
|
||||
});
|
||||
|
||||
describe('when the user is authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,39 +0,0 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dso-page-orcid-button',
|
||||
templateUrl: './dso-page-orcid-button.component.html',
|
||||
styleUrls: ['./dso-page-orcid-button.component.scss']
|
||||
})
|
||||
export class DsoPageOrcidButtonComponent implements OnInit {
|
||||
/**
|
||||
* The DSpaceObject to display a button to the edit page for
|
||||
*/
|
||||
@Input() dso: DSpaceObject;
|
||||
|
||||
/**
|
||||
* The prefix of the route to the edit page (before the object's UUID, e.g. "items")
|
||||
*/
|
||||
@Input() pageRoute: string;
|
||||
|
||||
/**
|
||||
* Whether or not the current user is authorized to edit the DSpaceObject
|
||||
*/
|
||||
isAuthorized: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
constructor(protected authorizationService: AuthorizationDataService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.authorizationService.isAuthorized(FeatureID.CanSynchronizeWithORCID, this.dso.self).pipe(take(1)).subscribe((isAuthorized: boolean) => {
|
||||
this.isAuthorized.next(isAuthorized);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
<button *ngIf="(isClaimable() | async)"
|
||||
[ngbTooltip]="'item.page.claim.tooltip' | translate"
|
||||
class="edit-button btn btn-dark btn-sm"
|
||||
data-test="item-claim"
|
||||
(click)="claim()">
|
||||
{{'item.page.claim.button' | translate }}
|
||||
</button>
|
@@ -1,186 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { PersonPageClaimButtonComponent } from './person-page-claim-button.component';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
|
||||
import { ResearcherProfileDataService } from '../../../core/profile/researcher-profile-data.service';
|
||||
import { RouteService } from '../../../core/services/route.service';
|
||||
import { routeServiceStub } from '../../testing/route-service.stub';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model';
|
||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
describe('PersonPageClaimButtonComponent', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let component: PersonPageClaimButtonComponent;
|
||||
let fixture: ComponentFixture<PersonPageClaimButtonComponent>;
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
metadata: {
|
||||
'person.email': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'fake@email.com'
|
||||
}
|
||||
],
|
||||
'person.birthDate': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: '1993'
|
||||
}
|
||||
],
|
||||
'person.jobTitle': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Developer'
|
||||
}
|
||||
],
|
||||
'person.familyName': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Doe'
|
||||
}
|
||||
],
|
||||
'person.givenName': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'John'
|
||||
}
|
||||
]
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const mockResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), {
|
||||
id: 'test-id',
|
||||
visible: true,
|
||||
type: 'profile',
|
||||
_links: {
|
||||
item: {
|
||||
href: 'https://rest.api/rest/api/profiles/test-id/item'
|
||||
},
|
||||
self: {
|
||||
href: 'https://rest.api/rest/api/profiles/test-id'
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const notificationsService = new NotificationsServiceStub();
|
||||
|
||||
const authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||
isAuthorized: jasmine.createSpy('isAuthorized')
|
||||
});
|
||||
|
||||
const researcherProfileService = jasmine.createSpyObj('researcherProfileService', {
|
||||
createFromExternalSource: jasmine.createSpy('createFromExternalSource'),
|
||||
findRelatedItemId: jasmine.createSpy('findRelatedItemId'),
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [PersonPageClaimButtonComponent],
|
||||
providers: [
|
||||
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
||||
{ provide: NotificationsService, useValue: notificationsService },
|
||||
{ provide: ResearcherProfileDataService, useValue: researcherProfileService },
|
||||
{ provide: RouteService, useValue: routeServiceStub },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PersonPageClaimButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockItem;
|
||||
});
|
||||
|
||||
describe('when item can be claimed', () => {
|
||||
beforeEach(() => {
|
||||
authorizationDataService.isAuthorized.and.returnValue(observableOf(true));
|
||||
researcherProfileService.createFromExternalSource.calls.reset();
|
||||
researcherProfileService.findRelatedItemId.calls.reset();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create claim button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('[data-test="item-claim"]'));
|
||||
expect(btn).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('claim', () => {
|
||||
describe('when successfully', () => {
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
researcherProfileService.createFromExternalSource.and.returnValue(createSuccessfulRemoteDataObject$(mockResearcherProfile));
|
||||
researcherProfileService.findRelatedItemId.and.returnValue(observableOf('test-id'));
|
||||
});
|
||||
|
||||
it('should display success notification', () => {
|
||||
scheduler.schedule(() => component.claim());
|
||||
scheduler.flush();
|
||||
|
||||
expect(researcherProfileService.findRelatedItemId).toHaveBeenCalled();
|
||||
expect(notificationsService.success).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when not successfully', () => {
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
researcherProfileService.createFromExternalSource.and.returnValue(createFailedRemoteDataObject$());
|
||||
});
|
||||
|
||||
it('should display success notification', () => {
|
||||
scheduler.schedule(() => component.claim());
|
||||
scheduler.flush();
|
||||
|
||||
expect(researcherProfileService.findRelatedItemId).not.toHaveBeenCalled();
|
||||
expect(notificationsService.error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when item cannot be claimed', () => {
|
||||
beforeEach(() => {
|
||||
authorizationDataService.isAuthorized.and.returnValue(observableOf(false));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create claim button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('[data-test="item-claim"]'));
|
||||
expect(btn).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -1,84 +0,0 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||
import { mergeMap, take } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { RouteService } from '../../../core/services/route.service';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { ResearcherProfileDataService } from '../../../core/profile/researcher-profile-data.service';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model';
|
||||
import { isNotEmpty } from '../../empty.util';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-person-page-claim-button',
|
||||
templateUrl: './person-page-claim-button.component.html',
|
||||
styleUrls: ['./person-page-claim-button.component.scss']
|
||||
})
|
||||
export class PersonPageClaimButtonComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The target person item to claim
|
||||
*/
|
||||
@Input() object: DSpaceObject;
|
||||
|
||||
/**
|
||||
* A boolean representing if item can be claimed or not
|
||||
*/
|
||||
claimable$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(protected routeService: RouteService,
|
||||
protected authorizationService: AuthorizationDataService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
protected researcherProfileService: ResearcherProfileDataService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.object._links.self.href, null, false).pipe(
|
||||
take(1)
|
||||
).subscribe((isAuthorized: boolean) => {
|
||||
this.claimable$.next(isAuthorized);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new researcher profile claiming the current item.
|
||||
*/
|
||||
claim() {
|
||||
this.researcherProfileService.createFromExternalSource(this.object._links.self.href).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
mergeMap((rd: RemoteData<ResearcherProfile>) => {
|
||||
if (rd.hasSucceeded) {
|
||||
return this.researcherProfileService.findRelatedItemId(rd.payload);
|
||||
} else {
|
||||
return observableOf(null);
|
||||
}
|
||||
}))
|
||||
.subscribe((id: string) => {
|
||||
if (isNotEmpty(id)) {
|
||||
this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'),
|
||||
this.translate.get('researcherprofile.success.claim.body'));
|
||||
this.claimable$.next(false);
|
||||
} else {
|
||||
this.notificationsService.error(
|
||||
this.translate.get('researcherprofile.error.claim.title'),
|
||||
this.translate.get('researcherprofile.error.claim.body'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the item is claimable, false otherwise.
|
||||
*/
|
||||
isClaimable(): Observable<boolean> {
|
||||
return this.claimable$;
|
||||
}
|
||||
|
||||
}
|
@@ -11,9 +11,9 @@ import {
|
||||
DeactivateMenuSectionAction,
|
||||
ExpandMenuAction,
|
||||
ExpandMenuPreviewAction,
|
||||
HideMenuAction,
|
||||
HideMenuAction, HideMenuSectionAction,
|
||||
RemoveMenuSectionAction,
|
||||
ShowMenuAction,
|
||||
ShowMenuAction, ShowMenuSectionAction,
|
||||
ToggleActiveMenuSectionAction,
|
||||
ToggleMenuAction,
|
||||
} from './menu.actions';
|
||||
@@ -240,6 +240,15 @@ export class MenuService {
|
||||
this.store.dispatch(new ShowMenuAction(menuID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a given menu section
|
||||
* @param {MenuID} menuID The ID of the menu
|
||||
* @param id The ID of the section
|
||||
*/
|
||||
showMenuSection(menuID: MenuID, id: string): void {
|
||||
this.store.dispatch(new ShowMenuSectionAction(menuID, id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a given menu
|
||||
* @param {MenuID} menuID The ID of the menu
|
||||
@@ -248,6 +257,15 @@ export class MenuService {
|
||||
this.store.dispatch(new HideMenuAction(menuID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a given menu section
|
||||
* @param {MenuID} menuID The ID of the menu
|
||||
* @param id The ID of the section
|
||||
*/
|
||||
hideMenuSection(menuID: MenuID, id: string): void {
|
||||
this.store.dispatch(new HideMenuSectionAction(menuID, id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a given menu section when it's currently inactive or deactivate it when it's currently active
|
||||
* @param {MenuID} menuID The ID of the menu
|
||||
|
@@ -305,11 +305,9 @@ import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'
|
||||
import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component';
|
||||
import { RSSComponent } from './rss-feed/rss.component';
|
||||
import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component';
|
||||
import { DsoPageOrcidButtonComponent } from './dso-page/dso-page-orcid-button/dso-page-orcid-button.component';
|
||||
import { LogInOrcidComponent } from './log-in/methods/orcid/log-in-orcid.component';
|
||||
import { BrowserOnlyPipe } from './utils/browser-only.pipe';
|
||||
import { ThemedLoadingComponent } from './loading/themed-loading.component';
|
||||
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
|
||||
import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component';
|
||||
import {
|
||||
ItemPageTitleFieldComponent
|
||||
@@ -578,12 +576,10 @@ const ENTRY_COMPONENTS = [
|
||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||
MetadataFieldWrapperComponent,
|
||||
MetadataValuesComponent,
|
||||
PersonPageClaimButtonComponent,
|
||||
ItemAlertsComponent,
|
||||
GenericItemPageFieldComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
RelatedItemsComponent,
|
||||
DsoPageOrcidButtonComponent,
|
||||
DsoEditMenuSectionComponent,
|
||||
DsoEditMenuComponent,
|
||||
DsoEditMenuExpandableSectionComponent,
|
||||
|
Reference in New Issue
Block a user