Merge remote-tracking branch 'origin/dmp-refactoring' into dmp-refactoring

This commit is contained in:
amentis 2024-04-04 17:08:27 +03:00
commit 805702459f
12 changed files with 247 additions and 44 deletions

View File

@ -0,0 +1,72 @@
package eu.eudat.model.persist;
import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class UpdateDescriptionTemplatePersist {
private UUID id;
public static final String _id = "id";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
private UUID descriptionTemplateGroupId;
public static final String _descriptionTemplateGroupId = "descriptionTemplateGroupId";
public UUID getDescriptionTemplateGroupId() {
return descriptionTemplateGroupId;
}
public void setDescriptionTemplateGroupId(UUID descriptionTemplateGroupId) {
this.descriptionTemplateGroupId = descriptionTemplateGroupId;
}
@Component(UpdateDescriptionTemplatePersistValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UpdateDescriptionTemplatePersistValidator extends BaseValidator<UpdateDescriptionTemplatePersist> {
public static final String ValidatorName = "UpdateDescriptionTemplatePersistValidator";
private final MessageSource messageSource;
protected UpdateDescriptionTemplatePersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<UpdateDescriptionTemplatePersist> modelClass() {
return UpdateDescriptionTemplatePersist.class;
}
@Override
protected List<Specification> specifications(UpdateDescriptionTemplatePersist item) {
return Arrays.asList(
this.spec()
.must(() -> this.isValidGuid(item.getId()))
.failOn(UpdateDescriptionTemplatePersist._id)
.failWith(messageSource.getMessage("Validation_Required", new Object[]{UpdateDescriptionTemplatePersist._id}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -18,6 +18,7 @@ import eu.eudat.model.censorship.DescriptionCensor;
import eu.eudat.model.censorship.PublicDescriptionCensor; import eu.eudat.model.censorship.PublicDescriptionCensor;
import eu.eudat.model.persist.DescriptionPersist; import eu.eudat.model.persist.DescriptionPersist;
import eu.eudat.model.persist.DescriptionStatusPersist; import eu.eudat.model.persist.DescriptionStatusPersist;
import eu.eudat.model.persist.UpdateDescriptionTemplatePersist;
import eu.eudat.model.result.QueryResult; import eu.eudat.model.result.QueryResult;
import eu.eudat.query.DescriptionQuery; import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpQuery; import eu.eudat.query.DmpQuery;
@ -267,4 +268,15 @@ public class DescriptionController {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + storageFile.getName() + (storageFile.getExtension().startsWith(".") ? "" : ".") + storageFile.getExtension() + "\"") .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + storageFile.getName() + (storageFile.getExtension().startsWith(".") ? "" : ".") + storageFile.getExtension() + "\"")
.body(new ByteArrayResource(file)); .body(new ByteArrayResource(file));
} }
@PostMapping("update-description-template")
// @Transactional
@ValidationFilterAnnotation(validator = UpdateDescriptionTemplatePersist.UpdateDescriptionTemplatePersistValidator.ValidatorName, argumentName = "model")
public Boolean updateDescriptionTemplate(@RequestBody UpdateDescriptionTemplatePersist model) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException {
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model));
// TODO
return true;
}
} }

View File

@ -149,3 +149,8 @@ export interface DescriptionSectionPermissionResolver {
sectionIds: Guid[]; sectionIds: Guid[];
permissions: string[]; permissions: string[];
} }
export interface UpdateDescriptionTemplatePersist {
id: Guid;
descriptionTemplateGroupId: Guid;
}

View File

@ -1,7 +1,7 @@
import { HttpClient, HttpHeaders, HttpParamsOptions, HttpResponse } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParamsOptions, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { Description, DescriptionPersist, DescriptionSectionPermissionResolver, DescriptionStatusPersist, PublicDescription } from '@app/core/model/description/description'; import { Description, DescriptionPersist, DescriptionSectionPermissionResolver, DescriptionStatusPersist, PublicDescription, UpdateDescriptionTemplatePersist } from '@app/core/model/description/description';
import { DescriptionLookup } from '@app/core/query/description.lookup'; import { DescriptionLookup } from '@app/core/query/description.lookup';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
@ -114,6 +114,13 @@ export class DescriptionService {
return this.httpClient.get(`${this.apiBase}/${id}/export/${format}`, { responseType: 'blob', observe: 'response', headers: this.headers }); return this.httpClient.get(`${this.apiBase}/${id}/export/${format}`, { responseType: 'blob', observe: 'response', headers: this.headers });
} }
public updateDescriptionTemplate(item: UpdateDescriptionTemplatePersist): Observable<boolean> {
const url = `${this.apiBase}/update-description-template`;
return this.http.post<boolean>(url, item).pipe(
catchError((error: any) => throwError(error)));
}
// //
// Autocomplete Commons // Autocomplete Commons
// //

View File

@ -1,5 +1,5 @@
<div class="confirmation-dialog"> <div class="container-fluid">
<div class="row d-flex"> <div class="row d-flex mt-2">
<div class="confirmation-message col-auto align-self-center"> <div class="confirmation-message col-auto align-self-center">
<h4>{{ data.message }}</h4> <h4>{{ data.message }}</h4>
</div> </div>
@ -7,35 +7,38 @@
<mat-icon class="close-btn" (click)="cancel()">close</mat-icon> <mat-icon class="close-btn" (click)="cancel()">close</mat-icon>
</div> </div>
</div> </div>
<div class="row"> <mat-dialog-content>
<div class="col-12"> <div class="row">
<ngx-dropzone class="drop-file" (change)="selectXML($event)" [accept]="'text/xml'" [multiple]="false"> <div class="col-12">
<ngx-dropzone-preview class="file-preview" [removable]="true" *ngIf="hasBlueprint()" (removed)="onRemove()"> <ngx-dropzone class="drop-file" (change)="selectXML($event)" [accept]="'text/xml'" [multiple]="false">
<ngx-dropzone-label class="file-label">{{ selectedFileName }}</ngx-dropzone-label> <ngx-dropzone-preview class="file-preview" [removable]="true" *ngIf="hasBlueprint()" (removed)="onRemove()">
</ngx-dropzone-preview> <ngx-dropzone-label class="file-label">{{ selectedFileName }}</ngx-dropzone-label>
</ngx-dropzone> </ngx-dropzone-preview>
</ngx-dropzone>
</div>
</div> </div>
</div> <div class="row">
<div class="row"> <div class="col-12 d-flex justify-content-center attach-btn">
<div class="col-12 d-flex justify-content-center attach-btn"> <button mat-button type="button" class="col-auto attach-file" (click)="imgFileInput.click()">
<button mat-button type="button" class="col-auto attach-file" (click)="imgFileInput.click()"> <mat-icon class="mr-2">input</mat-icon>
<mat-icon class="mr-2">input</mat-icon> <span *ngIf="!hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.UPLOAD-FILE' | translate}}</span>
<span *ngIf="!hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.UPLOAD-FILE' | translate}}</span> <span *ngIf="hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.REPLACE-FILE' | translate}}</span>
<span *ngIf="hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.REPLACE-FILE' | translate}}</span> </button>
</button> <input class="hidden" type="file" #imgFileInput (change)="selectXML($event)" accept="text/xml" />
<input class="hidden" type="file" #imgFileInput (change)="selectXML($event)" accept="text/xml" /> </div>
</div> </div>
</div> <div class="row">
<div class="row"> <mat-form-field class="col-12">
<mat-form-field class="col-12"> <input matInput placeholder="{{'DMP-BLUEPRINT-LISTING.IMPORT.UPLOAD-XML-NAME'| translate}}" name="datasetBlueprintName" [(ngModel)]="data.name">
<input matInput placeholder="{{'DMP-BLUEPRINT-LISTING.IMPORT.UPLOAD-XML-NAME'| translate}}" name="datasetBlueprintName" [(ngModel)]="data.name"> </mat-form-field>
</mat-form-field> <div class="col-auto">
<div class="col-auto"> <button mat-button type="button" class="cancel-btn" (click)="cancel()">{{ data.cancelButton }}</button>
<button mat-button type="button" class="cancel-btn" (click)="cancel()">{{ data.cancelButton }}</button> </div>
<div class="col"></div>
<div class="col-auto">
<button mat-button color="primary" class="next-btn" type="button" (click)="confirm()" [disabled]="!hasBlueprint()">{{ data.confirmButton }}</button>
</div>
</div> </div>
<div class="col"></div> </mat-dialog-content>
<div class="col-auto">
<button mat-button color="primary" class="next-btn" type="button" (click)="confirm()" [disabled]="!hasBlueprint()">{{ data.confirmButton }}</button>
</div>
</div>
</div> </div>

View File

@ -50,8 +50,10 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select placeholder="{{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE-PLACEHOLDER'| translate}}" [required]="true" [compareWith]="compareWith" [formControl]="formGroup.get('descriptionTemplateId')"> <mat-select placeholder="{{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE-PLACEHOLDER'| translate}}" [required]="true" [compareWith]="compareWith" [formControl]="formGroup.get('descriptionTemplateId')">
<mat-option *ngFor="let descriptionTemplate of availableDescriptionTemplates" [value]="descriptionTemplate.id" class="multiline-mat-option"> <mat-option *ngFor="let descriptionTemplate of availableDescriptionTemplates" [value]="descriptionTemplate.id" class="multiline-mat-option">
<div>{{descriptionTemplate.label}}</div> <div>
<div>{{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE-VERSION'| translate}} {{descriptionTemplate.version}}</div> <span>{{descriptionTemplate.label}}, </span>
<span>{{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE-VERSION'| translate}} {{descriptionTemplate.version}}</span>
</div>
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('descriptionTemplateId').hasError('backendError')">{{formGroup.get('descriptionTemplateId').getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('descriptionTemplateId').hasError('backendError')">{{formGroup.get('descriptionTemplateId').getError('backendError').message}}</mat-error>

View File

@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template'; import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description'; import { Description } from '@app/core/model/description/description';
@ -10,6 +11,7 @@ import { DescriptionService } from '@app/core/services/description/description.s
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { BaseComponent } from '@common/base/base.component'; import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component';
@Component({ @Component({
selector: 'app-description-base-fields-editor-component', selector: 'app-description-base-fields-editor-component',
@ -23,20 +25,55 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
availableDescriptionTemplates: DescriptionTemplate[] = []; availableDescriptionTemplates: DescriptionTemplate[] = [];
viewOnly = false; //TODO: not used. viewOnly = false; //TODO: not used.
constructor( constructor(private dialog: MatDialog,
) { super(); } private descriptionService: DescriptionService) {
super();
}
ngOnInit() { ngOnInit() {
const dmpDescriptionTemplates: DmpDescriptionTemplate[] = this.description.dmp.dmpDescriptionTemplates.filter(x => x.sectionId == this.description.dmpDescriptionTemplate.sectionId && x.isActive == IsActive.Active); this.loadDescriptionTemplates();
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
//Check if the used tempalte in included in the current list. If not add it.
if (this.description.descriptionTemplate && currentVersionsOfDescriptionTemplates.find(x => x.id == this.description?.descriptionTemplate?.id) == null) {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate)
}
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
} }
public compareWith(object1: any, object2: any) { public compareWith(object1: any, object2: any) {
return object1 && object2 && object1.id === object2.id; return object1 && object2 && object1.id === object2.id;
} }
private loadDescriptionTemplates(): void {
const dmpDescriptionTemplates: DmpDescriptionTemplate[] = this.description.dmp.dmpDescriptionTemplates.filter(x => x.sectionId == this.description.dmpDescriptionTemplate.sectionId && x.isActive == IsActive.Active);
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
if (this.description.descriptionTemplate && currentVersionsOfDescriptionTemplates.find(x => x.id == this.description?.descriptionTemplate?.id) == null) {
if (this.description.status === 0) {
this.openDeprecatedDescriptionTemplateDialog();
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
}
}
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
}
private openDeprecatedDescriptionTemplateDialog(): void {
const dialogRef = this.dialog.open(DeprecatedDescriptionTemplateDialog, {
data: {
label: this.description.descriptionTemplate.label
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(
result => {
if(result) {
this.descriptionService.updateDescriptionTemplate({
id: this.description.id,
descriptionTemplateGroupId: this.description.descriptionTemplate.groupId
})
.subscribe(
result => {
if (result) {
this.loadDescriptionTemplates();
}
},
error => console.error(error));
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
}
});
}
} }

View File

@ -0,0 +1,26 @@
<div class="container-fluid">
<div class="row d-flex mt-2">
<div class="col-auto align-self-center">
<h4> {{ 'DESCRIPTION-EDITOR.DEPRECATED-DESCRIPTION-TEMPLATE.TITLE' | translate}} {{ data.label }}</h4>
</div>
<div class="col-auto ml-auto">
<mat-icon class="close-btn" (click)="cancel()">close</mat-icon>
</div>
</div>
<mat-dialog-content>
<div class="row">
<div class="col-12">
<span>{{ 'DESCRIPTION-EDITOR.DEPRECATED-DESCRIPTION-TEMPLATE.MESSAGE' | translate}}</span>
</div>
</div>
<div class="row mt-2">
<div class="col-auto">
<button type="button" class="default-btn" (click)="cancel()"><span>{{ 'DESCRIPTION-EDITOR.DEPRECATED-DESCRIPTION-TEMPLATE.ACTIONS.CANCEL' | translate}}</span></button>
</div>
<div class="col"></div>
<div class="col-auto">
<button type="button" class="normal-btn" (click)="update()"><span>{{ 'DESCRIPTION-EDITOR.DEPRECATED-DESCRIPTION-TEMPLATE.ACTIONS.UPDATE' | translate}}</span></button>
</div>
</div>
</mat-dialog-content>
</div>

View File

@ -0,0 +1,25 @@
import { Component, Inject, OnInit } from "@angular/core";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
@Component({
selector: 'app-deprecated-description-template-dialog',
templateUrl: 'deprecated-description-template-dialog.component.html',
styleUrls: ['deprecated-description-template-dialog.component.scss']
})
export class DeprecatedDescriptionTemplateDialog implements OnInit {
constructor(
public dialogRef: MatDialogRef<DeprecatedDescriptionTemplateDialog>,
@Inject(MAT_DIALOG_DATA) public data: any
) { }
ngOnInit(): void {
}
cancel(): void {
this.dialogRef.close();
}
update(): void {
this.dialogRef.close(true);
}
}

View File

@ -14,6 +14,7 @@ import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-ed
import { TagsFieldModule } from '@app/ui/tag/tags-field/tags-field.module'; import { TagsFieldModule } from '@app/ui/tag/tags-field/tags-field.module';
import { PrefillDescriptionDialogComponent } from './prefill-description/prefill-description.component'; import { PrefillDescriptionDialogComponent } from './prefill-description/prefill-description.component';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
import { DeprecatedDescriptionTemplateDialog } from './description-base-fields-editor/dialog-description-template/deprecated-description-template-dialog.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -32,7 +33,8 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod
declarations: [ declarations: [
DescriptionEditorComponent, DescriptionEditorComponent,
DescriptionBaseFieldsEditorComponent, DescriptionBaseFieldsEditorComponent,
PrefillDescriptionDialogComponent PrefillDescriptionDialogComponent,
DeprecatedDescriptionTemplateDialog
], ],
exports: [ exports: [
], ],

View File

@ -746,6 +746,14 @@
"LOCKED-DIALOG": { "LOCKED-DIALOG": {
"TITLE": "Description is locked", "TITLE": "Description is locked",
"MESSAGE": "Somebody else is modifying the description at this moment. You may view the description but you cannot make any changes. If you would like to modify it please come back later." "MESSAGE": "Somebody else is modifying the description at this moment. You may view the description but you cannot make any changes. If you would like to modify it please come back later."
},
"DEPRECATED-DESCRIPTION-TEMPLATE": {
"TITLE": "Found a newer version of ",
"MESSAGE": "The selected description template has become obsolete. Would you like to update it?",
"ACTIONS": {
"CANCEL": "Cancel",
"UPDATE": "Update"
}
} }
}, },
"DESCRIPTION-COPY-DIALOG": { "DESCRIPTION-COPY-DIALOG": {