import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { CultureInfo } from '@app/core/model/culture-info'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { UserListingModel } from '@app/core/model/user/user-listing'; import { AuthService } from '@app/core/services/auth/auth.service'; import { CultureService } from '@app/core/services/culture/culture-service'; import { UserService } from '@app/core/services/user/user.service'; import { BaseComponent } from '@common/base/base.component'; import { TranslateService } from '@ngx-translate/core'; import * as moment from 'moment-timezone'; import { Observable, of } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { LanguageService } from '@app/core/services/language/language.service'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { Oauth2DialogService } from '../misc/oauth2-dialog/service/oauth2-dialog.service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; import { UserProfileEditorModel } from './user-profile-editor.model'; import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { AddAccountDialogComponent } from './add-account/add-account-dialog.component'; import { MatDialog } from '@angular/material/dialog'; import { UserCredentialModel } from '@app/core/model/user/user-credential'; import { AuthProvider } from '@app/core/common/enum/auth-provider'; import { MergeEmailConfirmationService } from '@app/core/services/merge-email-confirmation/merge-email-confirmation.service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; import {PopupNotificationDialogComponent} from "@app/library/notification/popup/popup-notification.component"; const availableLanguages: any[] = require('../../../assets/resources/language.json'); @Component({ selector: 'app-user-profile', templateUrl: './user-profile.component.html', styleUrls: ['./user-profile.component.scss'], }) export class UserProfileComponent extends BaseComponent implements OnInit, OnDestroy { userProfileEditorModel: UserProfileEditorModel; user: Observable; userCredentials: UserCredentialModel[]; currentUserId: string; cultures: Observable; timezones: Observable; editMode = false; languages = availableLanguages; zenodoToken: string; zenodoEmail: String; roleOrganizationEnum = RoleOrganizationType; authProviderEnum = AuthProvider; private oauthLock: boolean; errorMessages = []; nestedCount = []; nestedIndex = 0; organisationsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { filterFn: this.filterOrganisations.bind(this), initialItems: (excludedItems: any[]) => this.filterOrganisations('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), displayFn: (item) => item['name'], titleFn: (item) => item['name'], subtitleFn: (item) => item['tag'] ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item['tag'] : (item['key'] ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item['key'] : this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.NO-SOURCE')) }; formGroup: FormGroup; constructor( private userService: UserService, private route: ActivatedRoute, private router: Router, private authService: AuthService, private language: TranslateService, private cultureService: CultureService, private externalSourcesService: ExternalSourcesService, private authentication: AuthService, private languageService: LanguageService, private configurationService: ConfigurationService, private oauth2DialogService: Oauth2DialogService, private uiNotificationService: UiNotificationService, private dialog: MatDialog, public enumUtils: EnumUtils, private mergeEmailConfirmation: MergeEmailConfirmationService, private httpClient: HttpClient, private matomoService: MatomoService ) { super(); } ngOnInit() { this.matomoService.trackPageView('User Profile'); this.route.params .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { this.currentUserId = this.authService.current().id; const userId = !params['id'] ? 'me' : params['id']; this.user = this.userService.getUser(userId).pipe(map(result => { //result['additionalinfo'] = JSON.parse(result['additionalinfo']); //this.zenodoToken = result['additionalinfo']['zenodoToken']; this.zenodoEmail = result['zenodoEmail']; this.userProfileEditorModel = new UserProfileEditorModel().fromModel(result); this.formGroup = this.userProfileEditorModel.buildForm(); // this.formGroup = new FormBuilder().group({ // language: new FormControl(result['language'] ? availableLanguages.filter(x => x.value === result['language']['value']).pop() : '', [Validators.required]), // timezone: new FormControl(result['timezone'], [Validators.required]), // culture: new FormControl(result['culture'], [Validators.required]) // }); //this.formGroup.get('language').valueChanges.pipe(takeUntil(this._destroyed)).subscribe(x => { if (x) this.translate.use(x.value) }) this.formGroup.get('timezone').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { if (x) { this.timezones = this._filterTimezone(x); } }); this.formGroup.get('culture').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { if (x) { this.cultures = this._filterCulture(x); } }); // this.initializeDisabledFormGroup(); this.unlock(); return result; })); this.userService.getEmails(userId).pipe(takeUntil(this._destroyed)) .subscribe(result => { this.userCredentials = result; }); }); } ngOnDestroy(): void { } logout(): void { this.authentication.logout(); } getUserRole(dmp: DmpModel) { if (dmp.creator.id === this.currentUserId) { return this.language.instant('USER-PROFILE.DMPS.CREATOR'); } else if (dmp.associatedUsers.map(x => x.id).indexOf(this.currentUserId) !== -1) { return this.language.instant('USER-PROFILE.DMPS.MEMBER'); } } showAllDmps() { this.router.navigate(['/plans']); } navigateToDmp(dmp: DmpModel) { this.router.navigate(['/plans/edit/' + dmp.id]); } private filterOrganisations(value: string): Observable { return this.externalSourcesService.searchDMPOrganizations(value); } private _filterTimezone(value: string): Observable { if (value && typeof value === 'string') { const filterValue = value.toLowerCase(); return of(moment.tz.names().filter(option => option.toLowerCase().includes(filterValue))); } else { return of(moment.tz.names()); } } private _filterCulture(value: string): Observable { if (value && typeof value === 'string') { const filterValue = value.toLowerCase(); return of(this.cultureService.getCultureValues().filter(option => option.displayName.toLowerCase().includes(filterValue))); } else { return of(this.cultureService.getCultureValues()); } } displayFn(culture?: CultureInfo): string | undefined { return culture ? culture.displayName + '-' + culture.nativeName : undefined; } save() { if (!this.formGroup.valid) { this.printErrors(this.formGroup); this.showValidationErrorsDialog(); this.nestedCount = []; this.nestedIndex = 0; this.errorMessages = []; return; } this.userService.updateUserSettings(this.formGroup.value) .pipe(takeUntil(this._destroyed)) .subscribe( x => { this.editMode = false; this.languageService.changeLanguage(this.formGroup.value.language.value); this.authService.current().culture = this.formGroup.value.culture.name; this.authService.me() .pipe(takeUntil(this._destroyed)) .subscribe(result => { this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); this.router.navigate(['/profile']); }); // .subscribe(result => window.location.reload()); }, error => { console.log(error); }); } public unlock() { this.editMode = true; this.formGroup.enable(); } public initializeDisabledFormGroup() { this.formGroup.disable(); } public lock() { if (!this.formGroup.valid) { return; } this.userService.updateUserSettings(this.formGroup.value) .pipe(takeUntil(this._destroyed)) .subscribe( x => { this.editMode = false; this.languageService.changeLanguage(this.formGroup.value.language.value); this.authService.current().culture = this.formGroup.value.culture.name; this.formGroup.disable(); this.authService.me() .pipe(takeUntil(this._destroyed)) .subscribe(result => this.router.navigate(['/profile'])); // .subscribe(result => window.location.reload()); }, error => { console.log(error); }); } private showValidationErrorsDialog(projectOnly?: boolean) { const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { disableClose: true, autoFocus: false, restoreFocus: false, data: { errorMessages: this.errorMessages, projectOnly: projectOnly }, }); } public applyFallbackAvatar(ev: Event) { (ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png'; } public hasZenodo(): boolean { return !isNullOrUndefined(this.zenodoEmail) && this.zenodoEmail !== ""; } public loginToZenodo() { this.showOauth2Dialog(this.getAccessUrl()); } getAccessUrl(): string { const redirectUri = this.configurationService.app + 'oauth2'; const url = this.configurationService.loginProviders.zenodoConfiguration.oauthUrl + '?client_id=' + this.configurationService.loginProviders.zenodoConfiguration.clientId + '&response_type=code&scope=deposit:write+deposit:actions+user:email&state=astate&redirect_uri=' + redirectUri; return url; } showOauth2Dialog(url: string) { this.oauth2DialogService.login(url) .pipe(takeUntil(this._destroyed)) .subscribe(result => { if (result !== undefined) { if (result.oauthCode !== undefined && result.oauthCode !== null && !this.oauthLock) { this.userService.registerDOIToken(result.oauthCode, this.configurationService.app + 'oauth2') .pipe(takeUntil(this._destroyed)) .subscribe(() => this.router.navigate(['/reload']).then(() => this.router.navigate(['/profile']))); this.oauthLock = true; } } else { this.oauthLock = false; } }); } public RemoveZenodo() { this.userService.deleteDOIToken() .pipe(takeUntil(this._destroyed)) .subscribe(() => this.router.navigate(['/reload']).then(() => this.router.navigate(['/profile']))); } public removeAccount() { } public addAccount() { const dialogRef = this.dialog.open(AddAccountDialogComponent, { restoreFocus: false, autoFocus: false, width: '653px', maxHeight: '90vh', data: { } }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { this.mergeEmailConfirmation.sendConfirmationEmail(result).pipe(takeUntil(this._destroyed)) .subscribe(res => { this.dialog.open(PopupNotificationDialogComponent, { data: { title: this.language.instant('USER-PROFILE.MERGING-EMAILS-DIALOG.TITLE'), message: this.language.instant('USER-PROFILE.MERGING-EMAILS-DIALOG.MESSAGE') }, maxWidth: '30em' }); }, err => { }); } }); } printErrors(rootform: FormGroup) { if (!rootform.valid) { Object.keys(rootform.controls).forEach(key => { const errors = rootform.get(key).errors; if (errors !== null) { let numbering: string = ''; for (let j = 0; j < this.nestedCount.length; j++) { numbering += this.nestedCount[j]; if (j < this.nestedIndex) { numbering += '.'; } else { break; } } Object.keys(errors).forEach(keyError => { if (typeof errors[keyError] === 'boolean') { this.errorMessages.push(numbering + ' ' + key + ' is ' + keyError); } else { this.errorMessages.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); } }); } else { if (rootform.get(key) instanceof FormGroup) { this.printErrors(rootform.get(key)); } else if (rootform.get(key) instanceof FormArray) { this.nestedIndex++; this.nestedCount[this.nestedIndex] = 0; for (let childForm of (rootform.get(key)).controls) { this.nestedCount[this.nestedIndex]++; this.printErrors(childForm); } this.nestedCount[this.nestedIndex] = 0; this.nestedIndex--; } } }); } } }