import { Component, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; import { DmpBlueprintDefinition, SystemFieldType } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint'; import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria'; import { DmpProfileService } from '@app/core/services/dmp/dmp-profile.service'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { DmpBlueprintEditor } from '@app/ui/admin/dmp-profile/editor/dmp-blueprint-editor.model'; import { debounceTime, filter, map, switchMap, takeUntil, tap } from 'rxjs/operators'; import { DmpEditorModel } from '../editor/dmp-editor.model'; import { ExtraPropertiesFormModel } from '../editor/general-tab/extra-properties-form.model'; import { FunderFormModel } from '../editor/grant-tab/funder-form-model'; import { GrantTabModel } from '../editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '../editor/grant-tab/project-form-model'; import { AuthService } from '@app/core/services/auth/auth.service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { LanguageInfo } from '@app/core/model/language-info'; import { UserModel } from '@app/core/model/user/user'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { TranslateService } from '@ngx-translate/core'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; import { Observable, interval } from 'rxjs'; import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; import { OrganisationService } from '@app/core/services/organisation/organisation.service'; import { MatDialog } from '@angular/material/dialog'; import { AddResearcherComponent } from '../editor/add-researcher/add-researcher.component'; import { AddOrganizationComponent } from '../editor/add-organization/add-organization.component'; import { RequestItem } from '@app/core/query/request-item'; import { LicenseCriteria } from '@app/core/query/license/license-criteria'; import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; import { DmpService } from '@app/core/services/dmp/dmp.service'; import { AvailableProfilesComponent } from '../editor/available-profiles/available-profiles.component'; import { DatasetPreviewDialogComponent } from '../dataset-preview/dataset-preview-dialog.component'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { DmpToDatasetDialogComponent } from '../dmp-to-dataset/dmp-to-dataset-dialog.component'; import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; import { FormService } from '@common/forms/form-service'; import { DmpDatasetProfile } from '@app/core/model/dmp/dmp-dataset-profile/dmp-dataset-profile'; import { DmpDatasetProfileSectionsFormModel } from '@app/core/model/dmp/dmp-dataset-profile/dmp-dataset-profile-sections-form.model'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { LockService } from '@app/core/services/lock/lock.service'; import { Principal } from '@app/core/model/auth/principal'; import { Role } from '@app/core/common/enum/role'; import { LockModel } from '@app/core/model/lock/lock.model'; import { Guid } from '@common/types/guid'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; import { GrantEditorModel } from '@app/ui/grant/editor/grant-editor.model'; import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate.component'; interface Visible { value: boolean; name: string; } @Component({ selector: 'app-dmp-editor-blueprint', templateUrl: './dmp-editor-blueprint.component.html', styleUrls: ['./dmp-editor-blueprint.component.scss'] }) export class DmpEditorBlueprintComponent extends CheckDeactivateBaseComponent implements OnInit { canDeactivate(): boolean { return !this.isDirty(); } saving = false; isNew = true; isUserOwner: boolean = true; isNewVersion = false; isFinalized = false; isClone = false; hasChanges = false; isDiscarded = false; isCreateNew = false; isCreateNewProject = false; isCreateNewFunder = false; dmp: DmpEditorModel; dmpSectionIndex: number = 0; formGroup: FormGroup = null; formGroupRawValue: any; datasets = new FormArray([]); associatedUsers: Array; people: Array; lock: LockModel; lockStatus: Boolean = false; step: number = 0; stepsBeforeDatasets: number = 4; maxStep: number = 4; scrollTop: number; hintErrors: boolean = false; selectedDmpBlueprintDefinition: DmpBlueprintDefinition = null; sectionTemplates: Array> = new Array>(); private associates: UserModel[] = []; visibles: Visible[] = [ { value: true, name: 'DMP-EDITOR.VISIBILITY.PUBLIC' }, { value: false, name: 'DMP-EDITOR.VISIBILITY.RESTRICTED' } ] licenseAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { filterFn: this.licenseSearch.bind(this), initialItems: (excludedItems: any[]) => this.licenseSearch('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), displayFn: (item) => item['name'], titleFn: (item) => item['name'] }; profilesAutoCompleteConfiguration: MultipleAutoCompleteConfiguration; constructor( private dmpProfileService: DmpProfileService, private authService: AuthService, private route: ActivatedRoute, private router: Router, private configurationService: ConfigurationService, private languageInfoService: LanguageInfoService, private language: TranslateService, private externalSourcesService: ExternalSourcesService, private organizationService: OrganisationService, private dmpService: DmpService, private uiNotificationService: UiNotificationService, private formService: FormService, private dialog: MatDialog, private lockService: LockService, private matomoService: MatomoService ) { super(); } ngOnInit(): void { this.matomoService.trackPageView('DMP Editor'); this.route.params .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { const itemId = params['id']; if (itemId != null) { this.isNew = false; this.dmpService.getSingle(itemId).pipe(map(data => data as DmpModel)) .pipe(takeUntil(this._destroyed)) .subscribe(async data => { this.lockService.checkLockStatus(data.id).pipe(takeUntil(this._destroyed)).subscribe(lockStatus => { this.lockStatus = lockStatus; this.dmp = new DmpEditorModel(); this.dmp.grant = new GrantTabModel(); this.dmp.project = new ProjectFormModel(); this.dmp.funder = new FunderFormModel(); this.dmp.extraProperties = new ExtraPropertiesFormModel(); this.dmp.fromModel(data); this.formGroup = this.dmp.buildForm(); this.datasets = this.formGroup.get('datasets') as FormArray; this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); if (!isNullOrUndefined(this.formGroup.get('profile').value)) { this.dmpProfileService.getSingleBlueprint(this.formGroup.get('profile').value.id) .pipe(takeUntil(this._destroyed)) .subscribe(result => { this.selectedDmpBlueprintDefinition = result.definition; this.formGroup.get('profile').setValue(result); this.maxStep = this.selectedDmpBlueprintDefinition.sections.length; this.step = 1; this.addProfiles(this.dmp.profiles); }); } this.maxStep = this.formGroup.get('datasets') ? this.maxStep + this.formGroup.get('datasets').value.length - 1 : this.maxStep; this.setIsUserOwner(); if (!this.isUserOwner) { if(this.isUserMember()){ this.router.navigate(['plans', 'overview', itemId]); return; } this.isFinalized = true; this.formGroup.disable(); } if (this.dmp.status === DmpStatus.Finalized || lockStatus) { this.isFinalized = true; this.formGroup.disable(); } if (this.authService.current() != null) { if (!lockStatus) { this.lock = new LockModel(data.id, this.getUserFromDMP()); this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => { this.lock.id = Guid.parse(result); interval(this.configurationService.lockInterval).pipe(takeUntil(this._destroyed)).subscribe(() => this.pumpLock()); }); } } this.associatedUsers = data.associatedUsers; this.people = data.users; this.formGroup.valueChanges.pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); }); if(this.lockStatus){ this.dialog.open(PopupNotificationDialogComponent,{data:{ title:this.language.instant('DMP-EDITOR.LOCKED.TITLE'), message:this.language.instant('DMP-EDITOR.LOCKED.MESSAGE') }, maxWidth:'30em'}); } }); }); } else { this.dmp = new DmpEditorModel(); this.dmp.grant = new GrantTabModel(); this.dmp.project = new ProjectFormModel(); this.dmp.funder = new FunderFormModel(); this.dmp.extraProperties = new ExtraPropertiesFormModel(); this.dmp.extraProperties.visible = false; this.dmp.extraProperties.contact = this.authService.current().id; this.formGroup = this.dmp.buildForm(); this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); if (!isNullOrUndefined(this.formGroup.get('profile').value)) { this.dmpProfileService.getSingleBlueprint(this.formGroup.get('profile').value.id) .pipe(takeUntil(this._destroyed)) .subscribe(result => { this.selectedDmpBlueprintDefinition = result.definition; this.formGroup.get('profile').setValue(result); this.maxStep = this.selectedDmpBlueprintDefinition.sections.length; this.step = 1; this.addProfiles(); }); } this.registerFormEventsForDmpBlueprint(); if (!this.isUserOwner) { this.formGroup.disable(); } if (isNullOrUndefined(this.formGroup.get('extraProperties').get('publicDate').value)) { this.formGroup.get('extraProperties').get('publicDate').patchValue(new Date()); } const principal = this.authService.current(); let associate: UserModel = { id: principal.id, name: principal.name, appRoles: principal.authorities, email: principal.email }; this.associates.push(associate); if (isNullOrUndefined(this.formGroup.get('extraProperties').get('contact').value)) { this.formGroup.get('extraProperties').get('contact').patchValue(associate.id); } if (isNullOrUndefined(this.formGroup.get('extraProperties').get('language').value)) { this.formGroup.get('extraProperties').get('language').patchValue('en'); } try{ const profiles = this.formGroup.get('profiles').value as DmpDatasetProfile[]; profiles.sort((a,b)=>a.label.localeCompare(b.label)); }catch{ console.info('Could not sort profiles'); } } }); this.profilesAutoCompleteConfiguration = { filterFn: this.filterProfiles.bind(this), initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), displayFn: (item) => item['label'], titleFn: (item) => item['label'], subtitleFn: (item) => item['description'], popupItemActionIcon: 'visibility' }; } setIsUserOwner() { if (this.dmp) { const principal: Principal = this.authService.current(); this.isUserOwner = !!this.dmp.users.find(x => (x.role === Role.Owner) && (x.id === principal.id) ); } } isUserMember(): boolean{ try{ const principal: Principal = this.authService.current(); return !!this.dmp.users.find(x => (x.role === Role.Member) && (x.id === principal.id) ); }catch{ return false; } } getUserFromDMP(): any { if (this.dmp) { const principal: Principal = this.authService.current(); return this.dmp.users.find(x => x.id === principal.id); } } private pumpLock() { this.lock.touchedAt = new Date(); this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => this.lock.id = Guid.parse(result)); } public isDirty(): boolean { return this.formGroup && this.formGroup.dirty && this.hasChanges; } public discard() { let messageText = ""; let confirmButtonText = ""; let cancelButtonText = ""; if (this.isNew) { messageText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-MESSAGE'); confirmButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-CONFIRM'); cancelButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-DENY'); } else { messageText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-MESSAGE'); confirmButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-CONFIRM'); cancelButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-DENY'); } const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false, data: { message: messageText, confirmButton: confirmButtonText, cancelButton: cancelButtonText, isDeleteConfirmation: true }, maxWidth: '40em' }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { // this.backToDmp(this.formGroup.get('dmp').value.id) setTimeout(x => { this.discardChanges(); }); } }); } public discardChanges() { this.isDiscarded = true; this.hasChanges = false; if (!this.isNew) { let grantControl; if (this.formGroup.get('grant').get('existGrant')) { grantControl = new GrantTabModel(); grantControl.fromModel(this.formGroup.get('grant').get('existGrant').value); } else { grantControl = new GrantEditorModel(); grantControl.fromModel(this.formGroup.get('grant').value); } grantControl.buildForm() this.formGroup.patchValue(JSON.parse(JSON.stringify(this.formGroupRawValue))); if (this.formGroup.get('grant').get('existGrant')) { this.formGroup.get('grant').get('existGrant').setValue(grantControl.existGrant); } else { this.formGroup.get('grant').setValue(grantControl); } } else { this.formGroup.reset(); this.formGroup.get("status").setValue(DmpStatus.Draft); this.formGroup.get('extraProperties').get('visible').setValue(false); this.formGroup.get('extraProperties').get('contact').setValue(this.authService.current().id); this.formGroup.get('associatedUsers').setValue([]); } this.isDiscarded = false; } save() { this.formSubmit(false); } formSubmit(addNew?: boolean, showAddDatasetDialog?: boolean): void { this.saving = true; this.formService.touchAllFormFields(this.formGroup); if(!this._isDMPDescriptionValid()){ const errmess = this._buildDMPDescriptionErrorMessages(); this.showValidationErrorsDialog(undefined, errmess); this.hintErrors = true; this.saving = false; return; } this.onSubmit(addNew, showAddDatasetDialog); } public formChanged() { if (!this.isDiscarded) { this.hasChanges = true; } } selectDefaultBlueprint() { this.dmpProfileService.getSingleBlueprint('86635178-36a6-484f-9057-a934e4eeecd5') .pipe(takeUntil(this._destroyed)) .subscribe(result => { this.selectedDmpBlueprintDefinition = result.definition; this.formGroup.get('profile').setValue(result); this.maxStep = this.selectedDmpBlueprintDefinition.sections.length; this.nextStep(); }); } selectBlueprint() { this.maxStep = this.selectedDmpBlueprintDefinition.sections.length; this.nextStep(); } nextStep() { this.step = this.step < this.maxStep ? this.step + 1 : this.step; this.resetScroll(); // if (this.step >= this.stepsBeforeDatasets) { // this.datasetId = this.datasets.at(this.step - this.stepsBeforeDatasets).get('id').value; // } } previousStep() { this.step = this.step !== 0 ? this.step - 1 : this.step; this.resetScroll(); // if (this.step >= this.stepsBeforeDatasets) { // this.datasetId = this.datasets.at(this.step - this.stepsBeforeDatasets).get('id').value; // } } changeStep(index: number, dataset?: FormControl) { this.step = index; this.resetScroll(); // if (dataset) { this.datasetId = dataset.get('id').value }; } private resetScroll() { document.getElementById('editor-form').scrollTop = 0; } hasProfile(): boolean { return this.formGroup.get('profiles') && this.formGroup.get('profiles').value && this.formGroup.get('profiles').value.length > 0; } addDataset(dmpSectionIndex: number) { this.saving = true; if(!this._isDMPDescriptionValid()){ const errmess = this._buildDMPDescriptionErrorMessages(); this.showValidationErrorsDialog(undefined, errmess); this.hintErrors = true; this.saving = false; return; } // const showDialog = this.hasProfile() && this.isNew; this.dmpSectionIndex = dmpSectionIndex; this.onSubmit(true, false); // this.formSubmit(true, false); // Add dataset to list // if (!this.formGroup.get('datasets')) { // this.formGroup.addControl('datasets', new FormBuilder().array(new Array())); // } // this.formGroup.get('datasets')['controls'].push(new DatasetWizardEditorModel().buildForm()); // this.datasets = this.formGroup.get('datasets') as FormArray; // this.step = this.stepsBeforeDatasets + this.formGroup.get('datasets')['controls'].length - 1; // this.maxStep = this.maxStep + this.formGroup.get('datasets')['controls'].length - 1; } onSubmit(addNew?: boolean, showAddDatasetDialog?: boolean): void { this.scrollTop = document.getElementById('editor-form').scrollTop; // return; this.dmpService.createDmp(this.formGroup.getRawValue()) .pipe(takeUntil(this._destroyed)) .subscribe( complete => { this.formGroup.get('id').setValue(complete.id); this.formGroup.get('modified').setValue(complete.modified); this.hasChanges = false; if (showAddDatasetDialog) { this.addDatasetOpenDialog(complete); } if (addNew) { this.onCallbackSuccessAddNew(complete); } else { this.onCallbackSuccess(complete) } }, error => { this.formGroup.get('status').setValue(DmpStatus.Draft); this.onCallbackError(error); } ); // this.dmpService.createDmpWithDatasets(this.formGroup.getRawValue()) // .pipe(takeUntil(this._destroyed)) // .subscribe( // complete => { // if (showAddDatasetDialog) { // this.addDatasetOpenDialog(complete); // } // else if (this.step < this.stepsBeforeDatasets) { this.onCallbackSuccess(complete) } // else { this.onCallbackSuccess(complete, this.datasetId) } // }, // error => { // this.formGroup.get('status').setValue(DmpStatus.Draft); // this.onCallbackError(error); // } // ) } addDatasetOpenDialog(dmp: DmpModel) { const dialogRef = this.dialog.open(ConfirmationDialogComponent, { maxWidth: '500px', restoreFocus: false, data: { message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ADD-DATASET'), confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.NO'), isDeleteConfirmation: false } }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { // this.router.navigate(['datasets/new/' + id]); this.addDataset(this.dmpSectionIndex); } else { dmp.id != null ? this.router.navigate(['/plans', 'edit', dmp.id]) : this.router.navigate(['/plans']); } }); } onCallbackSuccess(dmp?: DmpModel, datasetId?: string): void { // On save keep editor position this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); if (dmp) { if(this.isNew){ this.router.navigate(['/plans', 'edit', dmp.id]); } let dmpEditorModel: DmpEditorModel; dmpEditorModel = new DmpEditorModel(); dmpEditorModel.grant = new GrantTabModel(); dmpEditorModel.project = new ProjectFormModel(); dmpEditorModel.funder = new FunderFormModel(); dmpEditorModel.extraProperties = new ExtraPropertiesFormModel(); dmpEditorModel.fromModel(dmp); this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); this.associatedUsers = dmp.associatedUsers; this.people = dmp.users; setTimeout(() => { this.formGroup = null; }); setTimeout(() => { this.formGroup = dmpEditorModel.buildForm(); this.formGroup.valueChanges.pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); });}); setTimeout(() => { document.getElementById('editor-form').scrollTop = this.scrollTop; }); this.saving = false; this.isNew = false; } else { this.router.navigate(['/reload']).then(() => { this.router.navigate(['/plans']); }); } // Uncomment to not keep editor position on save // if (dmp.id != null) { // datasetId ? this.router.navigate(['/reload']).then(() => { this.router.navigate(['/plans', 'edit', dmp.id], { queryParams: { dataset: datasetId } }); }) : this.router.navigate(['/reload']).then(() => { this.router.navigate(['/plans', 'edit', dmp.id]); }) // } else { // this.router.navigate(['/reload']).then(() => { this.router.navigate(['/plans']); }); // } } onCallbackError(error: any) { this.uiNotificationService.snackBarNotification(error.error.message, SnackBarNotificationLevel.Error); this.setErrorModel(error.error); this.saving = false; //this.validateAllFormFields(this.formGroup); } public setErrorModel(validationErrorModel: ValidationErrorModel) { Object.keys(validationErrorModel).forEach(item => { (this.dmp.validationErrorModel)[item] = (validationErrorModel)[item]; }); } onCallbackSuccessAddNew(dmp?: DmpModel) { // this.editDataset(dmp.id, true, this.isNew && !this.formGroup.get('datasets').value.length); this.editDataset(dmp.id, true, false); this.saving = false; } editDataset(id: string, isNew: boolean, showModal:boolean = false) { if(showModal){ const dialogRef = this.dialog.open(DmpToDatasetDialogComponent, { width: '500px', autoFocus: false, restoreFocus: false, }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { if (isNew) { this.router.navigate(['/datasets', 'new', id, this.dmpSectionIndex]); } else { this.router.navigate(['/datasets', 'edit', id]); } } }); }else{ if (isNew) { this.router.navigate(['/datasets', 'new', id, this.dmpSectionIndex]); } else { this.router.navigate(['/datasets', 'edit', id]); } } } //checks if the dpm is valid not taking into account the datasets validity private _isDMPDescriptionValid():boolean{ const form: FormGroup = this.formGroup; if(form.controls){ return Object.keys(form.controls) .map(controlName=>{//get validity of each control if(controlName === 'datasets'){//we dont care if datasets are valid return true; } return !form.get(controlName).invalid;//!! in case the control is disabled, we consider it valid }) .reduce((isFormValid,isControlValid)=>{//aggregate validities return isControlValid && isFormValid; }, true); } return true; } private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) { if(errmess){ const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { disableClose: true, autoFocus: false, restoreFocus: false, data: { errorMessages:errmess, projectOnly: projectOnly }, }); }else{ const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { disableClose: true, autoFocus: false, restoreFocus: false, data: { formGroup: this.formGroup, projectOnly: projectOnly }, }); } } private _buildDMPDescriptionErrorMessages(): string[]{//not including datasets const errmess: string[] = []; Object.keys(this.formGroup.controls).forEach(controlName=>{ if(controlName != 'datasets' && this.formGroup.get(controlName).invalid){ errmess.push(...this._buildErrorMessagesForAbstractControl(this.formGroup.get(controlName), controlName)); } }) return errmess; } // takes as an input an abstract control and gets its error messages[] private _buildErrorMessagesForAbstractControl(aControl: AbstractControl, controlName: string):string[]{ const errmess:string[] = []; if(aControl.invalid){ if(aControl.errors){ //check if has placeholder if( (aControl).nativeElement !== undefined && (aControl).nativeElement !== null){ const placeholder = this._getPlaceHolder(aControl); if(placeholder){ controlName = placeholder; } } const errorMessage = this._getErrorMessage(aControl, controlName); errmess.push(...errorMessage); } /*in case the aControl is FormControl then the it should have provided its error messages above. No need to check case of FormControl below*/ if(aControl instanceof FormGroup){ const fg = aControl as FormGroup; //check children Object.keys(fg.controls).forEach(controlName=>{ errmess.push(...this._buildErrorMessagesForAbstractControl(fg.get(controlName), controlName)); }); }else if(aControl instanceof FormArray){ const fa = aControl as FormArray; fa.controls.forEach((control,index)=>{ errmess.push(... this._buildErrorMessagesForAbstractControl(control, `${controlName} --> ${index+1}`)); }); } } return errmess; } private _getErrorMessage(formControl: AbstractControl, name: string): string[] { const errors: string[] = []; Object.keys(formControl.errors).forEach(key => { if (key === 'required') { errors.push(this.language.instant(name + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); } // if (key === 'required') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + this.getPlaceHolder(formControl) + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')); } else if (key === 'email') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.EMAIL')); } else if (key === 'min') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MIN-VALUE', { 'min': formControl.getError('min').min })); } else if (key === 'max') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MAX-VALUE', { 'max': formControl.getError('max').max })); } else { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + formControl.errors[key].message); } }); return errors; } private _getPlaceHolder(formControl: any): string { if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea' || formControl.nativeElement.localName === 'richTextarea') { return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'mat-select') { return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-single-auto-complete') { return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') { return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } } filterProfiles(value: string): Observable { const request = new DataTableRequest(null, null, { fields: ['+label'] }); const criteria = new DatasetProfileCriteria(); criteria.like = value; request.criteria = criteria; return this.dmpService.searchDMPProfiles(request); } registerFormEventsForDmpBlueprint(): void { this.formGroup.get('profile').valueChanges .pipe( takeUntil(this._destroyed)) .subscribe(Option => { if (Option instanceof Object) { this.selectedDmpBlueprintDefinition = Option.definition; this.addProfiles(); } else { this.selectedDmpBlueprintDefinition = null; } }) } private addProfiles(profiles?: DmpDatasetProfile[]) { for(let i = 0; i < this.selectedDmpBlueprintDefinition.sections.length; i++){ this.sectionTemplates.push(new Array()); } const templates: Array = new Array(); this.selectedDmpBlueprintDefinition.sections.forEach(section => { if (profiles !== undefined) { profiles.filter(profile => profile.data.dmpSectionIndex.includes(section.ordinal - 1)).forEach(profile => this.sectionTemplates[section.ordinal - 1].push({id: profile.descriptionTemplateId, label: profile.label, description: ""})); } else { section.descriptionTemplates.forEach(template => { this.sectionTemplates[section.ordinal - 1].push({id: template.descriptionTemplateId, label: template.label, description: ""}) let found: DmpDatasetProfile = templates.find(dmpDatasetProfile => dmpDatasetProfile.id == template.descriptionTemplateId); if (found === undefined) { let data: DmpDatasetProfileSectionsFormModel= new DmpDatasetProfileSectionsFormModel(); data.dmpSectionIndex.push(section.ordinal - 1); let id = null; if (profiles !== undefined) { let existedProfile = profiles.find(profile => profile.descriptionTemplateId == template.descriptionTemplateId); if (existedProfile !== undefined) { id = existedProfile.id; } } let profile: DmpDatasetProfile = { id: id, descriptionTemplateId: template.descriptionTemplateId, label: template.label, data: data }; templates.push(profile); } else { found.data.dmpSectionIndex.push(section.ordinal - 1); } }); } }); (profiles !== undefined) ? this.formGroup.get('profiles').setValue(profiles) : this.formGroup.get('profiles').setValue(templates); } dmpBlueprintAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { filterFn: this.dmpBlueprintSearch.bind(this), initialItems: (extraData) => this.dmpBlueprintSearch(''), displayFn: (item) => item['label'], titleFn: (item) => item['label'] }; dmpBlueprintSearch(query: string) { let fields: Array = new Array(); var request = new DataTableRequest(0, 10, { fields: fields }); request.criteria = new DmpBlueprintCriteria(); return this.dmpProfileService.getPagedBlueprint(request).pipe(map(x => x.data)); } getLanguageInfos(): LanguageInfo[] { return this.languageInfoService.getLanguageInfoValues(); } getAssociates(): UserModel[] { let associates: UserModel[]; if (this.formGroup.get('associatedUsers').value && this.formGroup.get('associatedUsers').value.length > 0) { associates = []; } else { associates = this.associates; } //associates = (this.formGroup.get('researchers').value as any[]); associates = associates.concat(this.formGroup.get('associatedUsers').value); return associates; } 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')) }; researchersAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { filterFn: this.filterResearchers.bind(this), initialItems: (excludedItems: any[]) => this.filterResearchers('').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')) }; // Researchers filterResearchers(value: string): Observable { return this.externalSourcesService.searchDMPResearchers({ criteria: { name: value, like: null } }); } addResearcher(event: MouseEvent) { event.stopPropagation(); const dialogRef = this.dialog.open(AddResearcherComponent, { data: this.formGroup.get('researchers') }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { const fullName = result.firstName + " " + result.lastName; const newItem = { label: null, name: fullName, id: null, status: 0, key: "Internal", reference: result.reference }; const researchersArray = this.formGroup.get('researchers').value || []; researchersArray.push(newItem); this.formGroup.get('researchers').setValue(researchersArray); } }); } // Organizations showOrganizationCreator(): boolean { return this.configurationService.allowOrganizationCreator; } filterOrganisations(value: string): Observable { return this.organizationService.searchGeneralOrganisations({ criteria: { labelLike: value } }); } cantAddOrganizations(): boolean { if (!isNullOrUndefined(this.formGroup.get('organizations'))) { return this.formGroup.get('organiztions').disabled; } else { return false; } } addOrganization(event: MouseEvent) { event.stopPropagation(); const dialogRef = this.dialog.open(AddOrganizationComponent, { data: this.formGroup.get('organisations') }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { const fullName = result.name; const newItem = { label: null, name: fullName, id: null, status: 0, key: "Internal", reference: result.reference }; const organizationsArray = this.formGroup.get('organisations').value || []; organizationsArray.push(newItem); this.formGroup.get('organisations').setValue(organizationsArray); } }); } showToggleButton() { return (!this.isFinalized && this.isUserOwner) || this.isClone; } licenseSearch(query: string): Observable { const request = new RequestItem(); request.criteria = new LicenseCriteria(); request.criteria.like = query; request.criteria.type = ''; return this.externalSourcesService.searchLicense(request); } allAvailableProfiles(event: MouseEvent) { event.stopPropagation(); const dialogRef = this.dialog.open(AvailableProfilesComponent, { data: { profiles: this.formGroup.get('profiles') } }); return false; } onRemoveTemplate(event) { let found = false; const profiles = this.formGroup.get('profiles').value; this.formGroup.get('datasets')['controls'].forEach(element => { if (element.get('profile').value.id === event.id) { found = true; this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-REMOVE-TEMPLATE'), SnackBarNotificationLevel.Success); } }); if (found) { this.formGroup.get('profiles').setValue(profiles); this.profilesAutoCompleteConfiguration = { filterFn: this.filterProfiles.bind(this), initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), displayFn: (item) => item['label'], titleFn: (item) => item['label'], subtitleFn: (item) => item['description'], popupItemActionIcon: 'visibility' }; } } onPreviewTemplate(event) { const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { width: '590px', minHeight: '200px', restoreFocus: false, data: { template: event }, panelClass: 'custom-modalbox' }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { let profiles = this.formGroup.get('profiles').value; profiles.push(event); this.formGroup.get('profiles').setValue(profiles); this.profilesAutoCompleteConfiguration = { filterFn: this.filterProfiles.bind(this), initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), displayFn: (item) => item['label'], titleFn: (item) => item['label'], subtitleFn: (item) => item['description'], popupItemActionIcon: 'visibility' }; } }); } onOptionSelected(event, sectionIndex: number){ try{ const profiles = this.formGroup.get('profiles').value as DmpDatasetProfile[]; let found = profiles.find((value) => value.id === event.id); if(found !== undefined) { if(found.data.dmpSectionIndex.indexOf(sectionIndex) === -1){ found.data.dmpSectionIndex.push(sectionIndex); } else{ this.sectionTemplates[sectionIndex].pop(); } } else{ let dmpDatasetProfileSection: DmpDatasetProfileSectionsFormModel = new DmpDatasetProfileSectionsFormModel(); dmpDatasetProfileSection.dmpSectionIndex = [sectionIndex]; profiles.push({id: null, descriptionTemplateId: event.id, label: event.label, data: dmpDatasetProfileSection}); } this.formGroup.get('profiles').setValue(profiles); // const profileCounts: Map = new Map(); // profiles.forEach((value) => profileCounts.set(value.id, (profileCounts.get(value.id) !== undefined ? profileCounts.get(value.id): 0 ) + 1)); // const duplicateProfiles = profiles.filter((value) => { // let isOk = profileCounts.get(value.id) > 1; // if (isOk) { // profileCounts.set(value.id, 0); // } // return isOk; // }); // duplicateProfiles.forEach((value) => profiles.splice(profiles.lastIndexOf(value), 1)); // profiles.sort((a,b)=> a.label.localeCompare(b.label)); }catch{ console.info('Could not sort Dataset Templates') } } }