import { Component, OnInit, ViewChild } from '@angular/core'; 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'; import { TranslateService } from '@ngx-translate/core'; import { Group } from '../core/eperson/models/group.model'; import { RemoteData } from '../core/data/remote-data'; import { PaginatedList } from '../core/data/paginated-list.model'; import { filter, switchMap, tap } from 'rxjs/operators'; import { EPersonDataService } from '../core/eperson/eperson-data.service'; import { getAllSucceededRemoteData, getRemoteDataPayload, getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { hasValue, isNotEmpty } from '../shared/empty.util'; import { followLink } from '../shared/utils/follow-link-config.model'; 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', styleUrls: ['./profile-page.component.scss'], templateUrl: './profile-page.component.html' }) /** * Component for a user to edit their profile information */ export class ProfilePageComponent implements OnInit { /** * A reference to the metadata form component */ @ViewChild(ProfilePageMetadataFormComponent) metadataForm: ProfilePageMetadataFormComponent; /** * The authenticated user as observable */ user$: Observable; /** * The groups the user belongs to */ groupsRD$: Observable>>; /** * Prefix for the notification messages of this component */ NOTIFICATIONS_PREFIX = 'profile.notifications.'; /** * Prefix for the notification messages of this security form */ PASSWORD_NOTIFICATIONS_PREFIX = 'profile.security.form.notifications.'; /** * The validity of the password filled in, in the security form */ private invalidSecurity: boolean; /** * The password filled in, in the security form */ private password: string; /** * The authenticated user */ private currentUser: EPerson; canChangePassword$: Observable; isResearcherProfileEnabled$: BehaviorSubject = new BehaviorSubject(false); constructor(private authService: AuthService, private notificationsService: NotificationsService, private translate: TranslateService, private epersonService: EPersonDataService, private authorizationService: AuthorizationDataService, private configurationService: ConfigurationDataService) { } ngOnInit(): void { this.user$ = this.authService.getAuthenticatedUserFromStore().pipe( filter((user: EPerson) => hasValue(user.id)), switchMap((user: EPerson) => this.epersonService.findById(user.id, true, true, followLink('groups'))), getAllSucceededRemoteData(), getRemoteDataPayload(), tap((user: EPerson) => this.currentUser = user) ); 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)); } /** * Fire an update on both the metadata and security forms * Show a warning notification when no changes were made in both forms */ updateProfile() { const metadataChanged = this.metadataForm.updateProfile(); const securityChanged = this.updateSecurity(); if (!metadataChanged && !securityChanged) { this.notificationsService.warning( this.translate.instant(this.NOTIFICATIONS_PREFIX + 'warning.no-changes.title'), this.translate.instant(this.NOTIFICATIONS_PREFIX + 'warning.no-changes.content') ); } } /** * Sets the validity of the password based on an emitted of the form * @param $event */ setInvalid($event: boolean) { this.invalidSecurity = $event; } /** * Update the user's security details * * Sends a patch request for changing the user's password when a new password is present and the password confirmation * matches the new password. * Nothing happens when no passwords are filled in. * An error notification is displayed when the password confirmation does not match the new password. * * Returns false when the password was empty */ updateSecurity() { const passEntered = isNotEmpty(this.password); if (this.invalidSecurity) { this.notificationsService.error(this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.general')); } if (!this.invalidSecurity && passEntered) { const operation = {op: 'add', path: '/password', value: this.password} as Operation; this.epersonService.patch(this.currentUser, [operation]).pipe( getFirstCompletedRemoteData() ).subscribe((response: RemoteData) => { if (response.hasSucceeded) { this.notificationsService.success( this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'success.title'), this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'success.content') ); } else { this.notificationsService.error( this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.title'), response.errorMessage ); } }); } return passEntered; } /** * Set the password value based on the value emitted from the security form * @param $event */ setPasswordValue($event: string) { this.password = $event; } /** * Submit of the security form that triggers the updateProfile method */ submit() { this.updateProfile(); } isResearcherProfileEnabled(){ return this.isResearcherProfileEnabled$; } }