mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge branch 'CST-5307' into CST-5249_suggestion
This commit is contained in:
@@ -2,6 +2,9 @@ import { Component } from '@angular/core';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
||||
import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
|
||||
import {TruncatableService} from '../../../../../shared/truncatable/truncatable.service';
|
||||
import {DSONameService} from '../../../../../core/breadcrumbs/dso-name.service';
|
||||
import {isNotEmpty} from '../../../../../shared/empty.util';
|
||||
|
||||
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement)
|
||||
@Component({
|
||||
@@ -14,9 +17,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje
|
||||
*/
|
||||
export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent {
|
||||
|
||||
public constructor(protected truncatableService: TruncatableService, protected dsoNameService: DSONameService) {
|
||||
super(truncatableService, dsoNameService);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.value ?
|
||||
this.value :
|
||||
this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName');
|
||||
let personName = this.dsoNameService.getName(this.dso);
|
||||
if (isNotEmpty(this.firstMetadataValue('person.familyName')) && isNotEmpty(this.firstMetadataValue('person.givenName'))) {
|
||||
personName = this.firstMetadataValue('person.familyName') + ', ' + this.firstMetadataValue('person.givenName');
|
||||
}
|
||||
return personName;
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<div class="d-flex flex-row">
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
|
||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="getTitleMetadataValues()" [separator]="', '"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2 space-children-mr">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
|
||||
<button class="edit-button btn btn-dark btn-sm" *ngIf="(isClaimable() | async)" (click)="claim()"> {{"item.page.claim.button" | translate }} </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@@ -54,7 +54,12 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
}
|
||||
]
|
||||
},
|
||||
relationships: createRelationshipsObservable()
|
||||
relationships: createRelationshipsObservable(),
|
||||
_links: {
|
||||
self : {
|
||||
href: 'item-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent));
|
||||
|
@@ -1,7 +1,20 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import {MetadataValue} from '../../../../core/shared/metadata.models';
|
||||
import {FeatureID} from '../../../../core/data/feature-authorization/feature-id';
|
||||
import {mergeMap, take} from 'rxjs/operators';
|
||||
import {getFirstSucceededRemoteData} from '../../../../core/shared/operators';
|
||||
import {RemoteData} from '../../../../core/data/remote-data';
|
||||
import {ResearcherProfile} from '../../../../core/profile/model/researcher-profile.model';
|
||||
import {isNotUndefined} from '../../../../shared/empty.util';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {RouteService} from '../../../../core/services/route.service';
|
||||
import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service';
|
||||
import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service';
|
||||
import {NotificationsService} from '../../../../shared/notifications/notifications.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
|
||||
@listableObjectComponent('Person', ViewMode.StandalonePage)
|
||||
@Component({
|
||||
@@ -12,5 +25,81 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item of the type Person
|
||||
*/
|
||||
export class PersonComponent extends ItemComponent {
|
||||
export class PersonComponent extends ItemComponent implements OnInit {
|
||||
|
||||
claimable$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(protected routeService: RouteService,
|
||||
protected authorizationService: AuthorizationDataService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
protected researcherProfileService: ResearcherProfileService) {
|
||||
super(routeService);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.authorizationService.isAuthorized(FeatureID.ShowClaimItem, this.object._links.self.href).pipe(
|
||||
take(1)
|
||||
).subscribe((isAuthorized: boolean) => {
|
||||
this.claimable$.next(isAuthorized);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
claim() {
|
||||
|
||||
this.authorizationService.isAuthorized(FeatureID.CanClaimItem, this.object._links.self.href).pipe(
|
||||
take(1)
|
||||
).subscribe((isAuthorized: boolean) => {
|
||||
if (!isAuthorized) {
|
||||
this.notificationsService.warning(this.translate.get('researcherprofile.claim.not-authorized'));
|
||||
} else {
|
||||
this.createFromExternalSource();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
createFromExternalSource() {
|
||||
this.researcherProfileService.createFromExternalSource(this.object._links.self.href).pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
mergeMap((rd: RemoteData<ResearcherProfile>) => {
|
||||
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);
|
||||
} else {
|
||||
this.notificationsService.error(
|
||||
this.translate.get('researcherprofile.error.claim.title'),
|
||||
this.translate.get('researcherprofile.error.claim.body'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isClaimable(): Observable<boolean> {
|
||||
return this.claimable$;
|
||||
}
|
||||
|
||||
getTitleMetadataValues(): MetadataValue[]{
|
||||
const metadataValues = [];
|
||||
const familyName = this.object?.firstMetadata('person.familyName');
|
||||
const givenName = this.object?.firstMetadata('person.givenName');
|
||||
const title = this.object?.firstMetadata('dc.title');
|
||||
if (familyName){
|
||||
metadataValues.push(familyName);
|
||||
}
|
||||
if (givenName){
|
||||
metadataValues.push(givenName);
|
||||
}
|
||||
if (metadataValues.length === 0 && title){
|
||||
metadataValues.push(title);
|
||||
}
|
||||
return metadataValues;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@
|
||||
<div class="pl-2 space-children-mr">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item"
|
||||
[tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
<button class="edit-button btn btn-dark btn-sm" *ngIf="(isClaimable() | async)" (click)="claim()"> {{"item.page.claim.button" | translate }} </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
||||
@@ -43,4 +42,4 @@
|
||||
</div>
|
||||
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>
|
||||
<ds-loading *ngIf="itemRD?.isLoading" message="{{'loading.item' | translate}}"></ds-loading>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -16,10 +16,6 @@ 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';
|
||||
|
||||
|
||||
/**
|
||||
@@ -52,11 +48,8 @@ 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, translate, notificationsService, researcherProfileService);
|
||||
super(route, router, items, authService, authorizationService);
|
||||
}
|
||||
|
||||
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
||||
|
@@ -64,24 +64,13 @@ export class ItemPageComponent implements OnInit {
|
||||
|
||||
itemUrl: string;
|
||||
|
||||
public claimable$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public isProcessing$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private items: ItemDataService,
|
||||
private authService: AuthService,
|
||||
private authorizationService: AuthorizationDataService,
|
||||
private translate: TranslateService,
|
||||
private notificationsService: NotificationsService,
|
||||
private researcherProfileService: ResearcherProfileService
|
||||
private authorizationService: AuthorizationDataService
|
||||
) {
|
||||
this.route.data.pipe(
|
||||
map((data) => data.dso as RemoteData<Item>)
|
||||
).subscribe((data: RemoteData<Item>) => {
|
||||
this.itemUrl = data?.payload?.self;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,55 +88,5 @@ 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<ResearcherProfile>) => {
|
||||
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<boolean> {
|
||||
return this.claimable$;
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import {Observable, of as observableOf} from 'rxjs';
|
||||
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
@@ -32,6 +32,8 @@ import { ItemComponent } from './item.component';
|
||||
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
||||
import { RouteService } from '../../../../core/services/route.service';
|
||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
||||
import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service';
|
||||
import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service';
|
||||
|
||||
export const iiifEnabled = Object.assign(new MetadataValue(),{
|
||||
'value': 'true',
|
||||
@@ -69,6 +71,11 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
return createSuccessfulRemoteDataObject$(new Bitstream());
|
||||
}
|
||||
};
|
||||
|
||||
const authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -92,7 +99,9 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
{ provide: NotificationsService, useValue: {} },
|
||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||
{ provide: RouteService, useValue: {} }
|
||||
{ provide: RouteService, useValue: {} },
|
||||
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||
{ provide: ResearcherProfileService, useValue: {} }
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<div *ngIf="!researcherProfile">
|
||||
<p>{{'researcher.profile.not.associated' | translate}}</p>
|
||||
</div>
|
||||
<button class="btn btn-primary"
|
||||
<button class="btn btn-primary mr-2"
|
||||
[disabled]="researcherProfile || (isProcessingCreate() | async)"
|
||||
(click)="createProfile()">
|
||||
<span *ngIf="(isProcessingCreate() | async)">
|
||||
@@ -20,7 +20,7 @@
|
||||
</span>
|
||||
</button>
|
||||
<ng-container *ngIf="researcherProfile">
|
||||
<button class="btn btn-primary" [disabled]="!researcherProfile" (click)="viewProfile(researcherProfile)">
|
||||
<button class="btn btn-primary mr-2" [disabled]="!researcherProfile" (click)="viewProfile(researcherProfile)">
|
||||
<i class="fas fa-info-circle"></i> {{'researcher.profile.view' | translate}}
|
||||
</button>
|
||||
<button class="btn btn-danger" [disabled]="!researcherProfile" (click)="deleteProfile(researcherProfile)">
|
||||
|
@@ -1,15 +1,16 @@
|
||||
<ng-container *ngVar="(user$ | async) as user">
|
||||
<div class="container" *ngIf="user">
|
||||
<h3 class="mb-4">{{'profile.head' | translate}}</h3>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">{{'profile.card.researcher' | translate}}</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<ds-profile-page-researcher-form [user]="user"></ds-profile-page-researcher-form>
|
||||
<ng-container *ngIf="isResearcherProfileEnabled() | async">
|
||||
<h3 class="mb-4">{{'profile.head' | translate}}</h3>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">{{'profile.card.researcher' | translate}}</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<ds-profile-page-researcher-form [user]="user" ></ds-profile-page-researcher-form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <ds-suggestions-notification></ds-suggestions-notification> -->
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">{{'profile.card.identify' | translate}}</div>
|
||||
<div class="card-body">
|
||||
|
@@ -20,6 +20,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {ConfigurationDataService} from '../core/data/configuration-data.service';
|
||||
import {ConfigurationProperty} from '../core/shared/configuration-property.model';
|
||||
|
||||
describe('ProfilePageComponent', () => {
|
||||
let component: ProfilePageComponent;
|
||||
@@ -80,6 +82,14 @@ describe('ProfilePageComponent', () => {
|
||||
{ provide: EPersonDataService, useValue: epersonService },
|
||||
{ provide: NotificationsService, useValue: notificationsService },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: ConfigurationDataService, useValue: jasmine.createSpyObj('configurationDataService', {
|
||||
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
||||
name: 'researcher-profile.entity-type',
|
||||
values: [
|
||||
'Person'
|
||||
]
|
||||
}))
|
||||
})},
|
||||
{ provide: AuthorizationDataService, useValue: jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword }) },
|
||||
provideMockStore({ initialState }),
|
||||
],
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import { EPerson } from '../core/eperson/models/eperson.model';
|
||||
import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
@@ -12,7 +12,7 @@ import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getRemoteDataPayload,
|
||||
getFirstCompletedRemoteData
|
||||
getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload
|
||||
} from '../core/shared/operators';
|
||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
@@ -20,6 +20,7 @@ import { AuthService } from '../core/auth/auth.service';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||
import {ConfigurationDataService} from '../core/data/configuration-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-profile-page',
|
||||
@@ -71,11 +72,14 @@ export class ProfilePageComponent implements OnInit {
|
||||
private currentUser: EPerson;
|
||||
canChangePassword$: Observable<boolean>;
|
||||
|
||||
isResearcherProfileEnabled$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private translate: TranslateService,
|
||||
private epersonService: EPersonDataService,
|
||||
private authorizationService: AuthorizationDataService) {
|
||||
private authorizationService: AuthorizationDataService,
|
||||
private configurationService: ConfigurationDataService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -88,6 +92,10 @@ export class ProfilePageComponent implements OnInit {
|
||||
);
|
||||
this.groupsRD$ = this.user$.pipe(switchMap((user: EPerson) => user.groups));
|
||||
this.canChangePassword$ = this.user$.pipe(switchMap((user: EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href)));
|
||||
|
||||
this.configurationService.findByPropertyName('researcher-profile.entity-type').pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
).subscribe(() => this.isResearcherProfileEnabled$.next(true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,4 +171,9 @@ export class ProfilePageComponent implements OnInit {
|
||||
submit() {
|
||||
this.updateProfile();
|
||||
}
|
||||
|
||||
isResearcherProfileEnabled(){
|
||||
return this.isResearcherProfileEnabled$;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user