diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 029c75d9cb..a2082831e0 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -28,4 +28,6 @@ export enum FeatureID { CanCreateVersion = 'canCreateVersion', CanViewUsageStatistics = 'canViewUsageStatistics', CanSendFeedback = 'canSendFeedback', + ShowClaimItem = 'showClaimItem', + CanClaimItem = 'canClaimItem', } diff --git a/src/app/core/profile/researcher-profile.service.ts b/src/app/core/profile/researcher-profile.service.ts index c1f8952d39..c61c5ca9f9 100644 --- a/src/app/core/profile/researcher-profile.service.ts +++ b/src/app/core/profile/researcher-profile.service.ts @@ -156,84 +156,6 @@ export class ResearcherProfileService { return this.dataService.patch(researcherProfile, operations); } - /** - * Check if the given item is linked to an ORCID profile. - * - * @param item the item to check - * @returns the check result - */ - isLinkedToOrcid(item: Item): boolean { - return item.hasMetadata('cris.orcid.authenticated'); - } - - /** - * Returns true if only the admin users can disconnect a researcher profile from ORCID. - * - * @returns the check result - */ - onlyAdminCanDisconnectProfileFromOrcid(): Observable { - return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe( - map((property) => property.values.map( (value) => value.toLowerCase()).includes('only_admin')) - ); - } - - /** - * Returns true if the profile's owner can disconnect that profile from ORCID. - * - * @returns the check result - */ - ownerCanDisconnectProfileFromOrcid(): Observable { - return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe( - map((property) => { - const values = property.values.map( (value) => value.toLowerCase()); - return values.includes('only_owner') || values.includes('admin_and_owner'); - }) - ); - } - - /** - * Returns true if the admin users can disconnect a researcher profile from ORCID. - * - * @returns the check result - */ - adminCanDisconnectProfileFromOrcid(): Observable { - return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe( - map((property) => { - const values = property.values.map( (value) => value.toLowerCase()); - return values.includes('only_admin') || values.includes('admin_and_owner'); - }) - ); - } - - /** - * If the given item represents a profile unlink it from ORCID. - */ - unlinkOrcid(item: Item): Observable> { - - const operations: RemoveOperation[] = [{ - path:'/orcid', - op:'remove' - }]; - - return this.findById(item.firstMetadata('cris.owner').authority).pipe( - switchMap((profile) => this.patch(profile, operations)), - getFinishedRemoteData() - ); - } - - getOrcidAuthorizeUrl(profile: Item): Observable { - return combineLatest([ - this.configurationService.findByPropertyName('orcid.authorize-url').pipe(getFirstSucceededRemoteDataPayload()), - this.configurationService.findByPropertyName('orcid.application-client-id').pipe(getFirstSucceededRemoteDataPayload()), - this.configurationService.findByPropertyName('orcid.scope').pipe(getFirstSucceededRemoteDataPayload())] - ).pipe( - map(([authorizeUrl, clientId, scopes]) => { - const redirectUri = environment.rest.baseUrl + '/api/cris/orcid/' + profile.id + '/?url=' + encodeURIComponent(this.router.url); - return authorizeUrl.values[0] + '?client_id=' + clientId.values[0] + '&redirect_uri=' + redirectUri + '&response_type=code&scope=' - + scopes.values.join(' '); - })); - } - /** * Creates a researcher profile starting from an external source URI * @param sourceUri URI of source item of researcher profile. @@ -258,10 +180,4 @@ export class ResearcherProfileService { return this.rdbService.buildFromRequestUUID(requestId); } - private getOrcidDisconnectionAllowedUsersConfiguration(): Observable { - return this.configurationService.findByPropertyName('orcid.disconnection.allowed-users').pipe( - getFirstSucceededRemoteDataPayload() - ); - } - } diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index 7cc8ff92c4..bfa25ec009 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -10,6 +10,7 @@
+
- + \ No newline at end of file diff --git a/src/app/item-page/full/full-item-page.component.ts b/src/app/item-page/full/full-item-page.component.ts index 369769c77d..99cf083bc5 100644 --- a/src/app/item-page/full/full-item-page.component.ts +++ b/src/app/item-page/full/full-item-page.component.ts @@ -16,6 +16,10 @@ import { hasValue } from '../../shared/empty.util'; import { AuthService } from '../../core/auth/auth.service'; import { Location } from '@angular/common'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; +import { CollectionDataService } from '../../core/data/collection-data.service'; /** @@ -48,8 +52,11 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit, items: ItemDataService, authService: AuthService, authorizationService: AuthorizationDataService, + translate: TranslateService, + notificationsService: NotificationsService, + researcherProfileService: ResearcherProfileService, private _location: Location) { - super(route, router, items, authService, authorizationService); + super(route, router, items, authService, authorizationService, translate, notificationsService, researcherProfileService); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index 95fbd7a2e0..8da5186270 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -1,20 +1,26 @@ -import { map } from 'rxjs/operators'; +import { map, mergeMap, take, tap } from 'rxjs/operators'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { ItemDataService } from '../../core/data/item-data.service'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { fadeInOut } from '../../shared/animations/fade'; -import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators'; +import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData, redirectOn4xx } from '../../core/shared/operators'; import { ViewMode } from '../../core/shared/view-mode.model'; import { AuthService } from '../../core/auth/auth.service'; import { getItemPageRoute } from '../item-page-routing-paths'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { TranslateService } from '@ngx-translate/core'; +import { ResearcherProfileService } from '../../core/profile/researcher-profile.service'; +import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model'; +import { isNotUndefined } from '../../shared/empty.util'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; + /** * This component renders a simple item page. @@ -55,13 +61,27 @@ export class ItemPageComponent implements OnInit { */ isAdmin$: Observable; + itemUrl: string; + + public claimable$: BehaviorSubject = new BehaviorSubject(false); + public isProcessing$: BehaviorSubject = new BehaviorSubject(false); + constructor( protected route: ActivatedRoute, private router: Router, private items: ItemDataService, private authService: AuthService, - private authorizationService: AuthorizationDataService - ) { } + private authorizationService: AuthorizationDataService, + private translate: TranslateService, + private notificationsService: NotificationsService, + private researcherProfileService: ResearcherProfileService + ) { + this.route.data.pipe( + map((data) => data.dso as RemoteData) + ).subscribe((data: RemoteData) => { + this.itemUrl = data?.payload?.self + }); + } /** * Initialize instance variables @@ -77,5 +97,56 @@ export class ItemPageComponent implements OnInit { ); this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf); + + this.authorizationService.isAuthorized(FeatureID.ShowClaimItem, this.itemUrl).pipe( + take(1) + ).subscribe((isAuthorized: boolean) => { + this.claimable$.next(isAuthorized) + }); + } + + claim() { + this.isProcessing$.next(true); + + this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.itemUrl).pipe( + take(1) + ).subscribe((isAuthorized: boolean) => { + if (!isAuthorized) { + this.notificationsService.warning(this.translate.get('researcherprofile.claim.not-authorized')); + this.isProcessing$.next(false); + } else { + this.createFromExternalSource(); + } + }); + + } + + createFromExternalSource() { + this.researcherProfileService.createFromExternalSource(this.itemUrl).pipe( + tap((rd: any) => { + if (!rd.hasSucceeded) { + this.isProcessing$.next(false); + } + }), + getFirstSucceededRemoteData(), + mergeMap((rd: RemoteData) => { + return this.researcherProfileService.findRelatedItemId(rd.payload); + })) + .subscribe((id: string) => { + if (isNotUndefined(id)) { + this.notificationsService.success(this.translate.get('researcherprofile.success.claim.title'), + this.translate.get('researcherprofile.success.claim.body')); + this.claimable$.next(false); + this.isProcessing$.next(false); + } else { + this.notificationsService.error( + this.translate.get('researcherprofile.error.claim.title'), + this.translate.get('researcherprofile.error.claim.body')); + } + }); + } + + isClaimable(): Observable { + return this.claimable$; } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d0ff85ba51..05047cefa7 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2130,6 +2130,8 @@ "item.page.version.hasDraft": "A new version cannot be created because there is an inprogress submission in the version history", + "item.page.claim.button": "Claim", + "item.preview.dc.identifier.uri": "Identifier:", "item.preview.dc.contributor.author": "Authors:",