From 3045130b27c247a6ff0c326a7f63653eed3529e1 Mon Sep 17 00:00:00 2001 From: amentis Date: Fri, 15 Mar 2024 09:39:36 +0200 Subject: [PATCH] add dmp finalize --- .../java/eu/eudat/audit/AuditableAction.java | 6 +- .../enums/DescriptionValidationOutput.java | 30 ++++++++ .../model/DescriptionValidationResult.java | 40 ++++++++++ .../model/DescriptionsToBeFinalized.java | 17 +++++ .../description/DescriptionService.java | 4 + .../description/DescriptionServiceImpl.java | 22 ++++++ .../java/eu/eudat/service/dmp/DmpService.java | 4 + .../eu/eudat/service/dmp/DmpServiceImpl.java | 64 ++++++++++++++++ .../controllers/DescriptionController.java | 24 ++++-- .../eu/eudat/controllers/DmpController.java | 31 ++++++++ .../description/description.service.ts | 9 ++- .../src/app/core/services/dmp/dmp.service.ts | 18 +++++ ...cription-base-fields-editor.component.html | 2 +- .../editor/description-editor.model.ts | 4 +- .../listing/description-listing.component.ts | 5 +- .../dmp-editor.resolver.ts | 1 + .../dmp-finalize-dialog.component.html | 6 +- .../dmp-finalize-dialog.component.ts | 14 +++- .../ui/dmp/listing/dmp-listing.component.ts | 11 ++- .../ui/dmp/overview/dmp-overview.component.ts | 75 ++++++++++--------- dmp-frontend/src/assets/i18n/en.json | 7 +- 21 files changed, 338 insertions(+), 56 deletions(-) create mode 100644 dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionValidationOutput.java create mode 100644 dmp-backend/core/src/main/java/eu/eudat/model/DescriptionValidationResult.java create mode 100644 dmp-backend/core/src/main/java/eu/eudat/model/DescriptionsToBeFinalized.java diff --git a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java index 47280bc7a..ec12294f8 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java +++ b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java @@ -42,7 +42,10 @@ public class AuditableAction { public static final EventId Dmp_PublicQuery = new EventId(5010, "Dmp_PublicQuery"); public static final EventId Dmp_Export = new EventId(5011, "Dmp_Export"); public static final EventId Dmp_PublicLookup = new EventId(5012, "Dmp_PublicLookup"); - + public static final EventId Dmp_Finalize = new EventId(5013, "Dmp_Finalize"); + public static final EventId Dmp_Undo_Finalize = new EventId(5014, "Dmp_Undo_Finalize"); + + public static final EventId Description_Query = new EventId(6000, "Description_Query"); public static final EventId Description_Lookup = new EventId(6001, "Description_Lookup"); public static final EventId Description_Persist = new EventId(6002, "Description_Persist"); @@ -52,6 +55,7 @@ public class AuditableAction { public static final EventId Description_PersistStatus = new EventId(6006, "Description_PersistStatus"); public static final EventId Description_UploadFieldFiles = new EventId(6007, "Description_UploadFieldFiles"); public static final EventId Description_GetFieldFile = new EventId(6008, "Description_GetFieldFile"); + public static final EventId Description_Validate = new EventId(6009, "Description_Validate"); public static final EventId Reference_Query = new EventId(7000, "Reference_Query"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionValidationOutput.java b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionValidationOutput.java new file mode 100644 index 000000000..18a37354c --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionValidationOutput.java @@ -0,0 +1,30 @@ +package eu.eudat.commons.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import eu.eudat.data.converters.enums.DatabaseEnum; + +import java.util.Map; + +public enum DescriptionValidationOutput implements DatabaseEnum { + + Valid((short) 1), + Invalid((short) 2); + + private final Short value; + + DescriptionValidationOutput(Short value) { + this.value = value; + } + + @JsonValue + public Short getValue() { + return value; + } + + private static final Map map = EnumUtils.getEnumValueMap(DescriptionValidationOutput.class); + + public static DescriptionValidationOutput of(Short i) { + return map.get(i); + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionValidationResult.java b/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionValidationResult.java new file mode 100644 index 000000000..56dab938f --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionValidationResult.java @@ -0,0 +1,40 @@ +package eu.eudat.model; + +import eu.eudat.commons.enums.DescriptionValidationOutput; + +import java.util.UUID; + +public class DescriptionValidationResult { + + private UUID descriptionId; + + public static final String _id = "id"; + + private DescriptionValidationOutput result; + + public static final String _result = "result"; + + public DescriptionValidationResult() { + } + + public DescriptionValidationResult(UUID descriptionId, DescriptionValidationOutput result) { + this.descriptionId = descriptionId; + this.result = result; + } + + public UUID getDescriptionId() { + return descriptionId; + } + + public void setDescriptionId(UUID descriptionId) { + this.descriptionId = descriptionId; + } + + public DescriptionValidationOutput getResult() { + return result; + } + + public void setResult(DescriptionValidationOutput result) { + this.result = result; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionsToBeFinalized.java b/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionsToBeFinalized.java new file mode 100644 index 000000000..22476ae34 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/DescriptionsToBeFinalized.java @@ -0,0 +1,17 @@ +package eu.eudat.model; + +import java.util.List; +import java.util.UUID; + +public class DescriptionsToBeFinalized { + + private List descriptionIds; + + public List getDescriptionIds() { + return descriptionIds; + } + + public void setDescriptionIds(List descriptionIds) { + this.descriptionIds = descriptionIds; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionService.java b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionService.java index e6075416f..ebef5ee36 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionService.java @@ -2,6 +2,7 @@ package eu.eudat.service.description; import eu.eudat.data.StorageFileEntity; import eu.eudat.model.Description; +import eu.eudat.model.DescriptionValidationResult; import eu.eudat.model.StorageFile; import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.model.persist.DescriptionPersist; @@ -16,6 +17,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.management.InvalidApplicationException; import java.io.IOException; +import java.util.List; import java.util.UUID; public interface DescriptionService { @@ -25,6 +27,8 @@ public interface DescriptionService { void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException; + List validate(List descriptionIds); + void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException; ResponseEntity export(UUID id, String exportType) throws InvalidApplicationException, IOException; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java index 25050e258..f168b4c4d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/description/DescriptionServiceImpl.java @@ -25,6 +25,7 @@ import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import eu.eudat.model.*; import eu.eudat.model.builder.DescriptionBuilder; +import eu.eudat.model.builder.DescriptionTemplateBuilder; import eu.eudat.model.deleter.DescriptionDeleter; import eu.eudat.model.deleter.DescriptionReferenceDeleter; import eu.eudat.model.deleter.DescriptionTagDeleter; @@ -360,6 +361,27 @@ public class DescriptionServiceImpl implements DescriptionService { return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data); } + public List validate(List descriptionIds){ + List descriptionValidationResults = new ArrayList<>(); + + List descriptions = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionIds).isActive(IsActive.Active).collect(); + if (descriptions == null){ + return null; + } + + for (DescriptionEntity description: descriptions) { + DescriptionValidationResult descriptionValidationResult = new DescriptionValidationResult(description.getId(), DescriptionValidationOutput.Invalid); + + //TODO description template + if (!this.conventionService.isNullOrEmpty(description.getLabel()) && description.getDmpId() != null && description.getDescriptionTemplateId() != null && description.getStatus() != null && !this.conventionService.isNullOrEmpty(description.getProperties())){ + descriptionValidationResult.setResult(DescriptionValidationOutput.Valid); + } + descriptionValidationResults.add(descriptionValidationResult); + + } + return descriptionValidationResults; + } + private @NotNull PropertyDefinitionEntity buildPropertyDefinitionEntity(PropertyDefinitionPersist persist, eu.eudat.commons.types.descriptiontemplate.DefinitionEntity definition, Map> fieldToReferenceMap){ PropertyDefinitionEntity data = new PropertyDefinitionEntity(); if (persist == null) return data; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java index 3e405ea24..50b6dec11 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java @@ -24,6 +24,10 @@ public interface DmpService { void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException; + void finalize(UUID id, List descriptionIds) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException; + + void undoFinalize(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException; + Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException; Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java index 530f61038..ea8ace216 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java @@ -816,6 +816,70 @@ public class DmpServiceImpl implements DmpService { return null; } + public void finalize(UUID id, List descriptionIds) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException { + DmpEntity dmp = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id).isActive(IsActive.Active).first(); + + if (dmp == null){ + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + } + + if (dmp.getStatus().equals(DmpStatus.Finalized)){ + throw new MyApplicationException("DMP is already finalized"); + } + + if (!this.conventionService.isListNullOrEmpty(descriptionIds)){ + List descriptionsToBeFinalised = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionIds).dmpIds(id).isActive(IsActive.Active).collect(); + for (DescriptionEntity description: descriptionsToBeFinalised) { + if (description.getStatus().equals(DescriptionStatus.Finalized)){ + throw new MyApplicationException("Description is already finalized"); + } + description.setStatus(DescriptionStatus.Finalized); + description.setUpdatedAt(Instant.now()); + description.setFinalizedAt(Instant.now()); + this.entityManager.merge(description); + } + + List descriptionsToBeCanceled = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).excludedIds(descriptionIds).dmpIds(id).isActive(IsActive.Active).collect(); + for (DescriptionEntity description: descriptionsToBeCanceled) { + description.setStatus(DescriptionStatus.Canceled); + } + this.deleterFactory.deleter(DescriptionDeleter.class).delete(descriptionsToBeCanceled, true); + } else { + List descriptions = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionIds).dmpIds(id).statuses(DescriptionStatus.Finalized).isActive(IsActive.Active).collect(); + if (descriptions == null){ + throw new MyApplicationException("Dmp don't have Finalized Descriptions"); + } + } + + dmp.setStatus(DmpStatus.Finalized); + dmp.setUpdatedAt(Instant.now()); + dmp.setFinalizedAt(Instant.now()); + + this.entityManager.merge(dmp); + this.entityManager.flush(); + this.elasticService.persistDmp(dmp); + this.sendNotification(dmp); + } + + public void undoFinalize(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException { + DmpEntity dmp = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id).isActive(IsActive.Active).firstAs(fields); + + if (dmp == null){ + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + } + + if (dmp.getStatus().equals(DmpStatus.Draft)){ + throw new MyApplicationException("DMP is already drafted"); + } + + dmp.setStatus(DmpStatus.Draft); + dmp.setUpdatedAt(Instant.now()); + + this.entityManager.merge(dmp); + this.entityManager.flush(); + this.sendNotification(dmp); + } + // invites public void inviteUserOrAssignUsers(UUID id, List users) throws InvalidApplicationException, JAXBException, IOException { this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.InviteDmpUsers); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DescriptionController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DescriptionController.java index d33850bb9..b155696ea 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DescriptionController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DescriptionController.java @@ -7,14 +7,11 @@ import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.IsActive; import eu.eudat.convention.ConventionService; import eu.eudat.data.StorageFileEntity; -import eu.eudat.model.StorageFile; +import eu.eudat.model.*; import eu.eudat.model.builder.PublicDescriptionBuilder; import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.service.storage.StorageFileService; import gr.cite.tools.validation.ValidationFilterAnnotation; -import eu.eudat.model.Description; -import eu.eudat.model.Dmp; -import eu.eudat.model.PublicDescription; import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.censorship.DescriptionCensor; import eu.eudat.model.censorship.PublicDescriptionCensor; @@ -115,7 +112,7 @@ public class DescriptionController { DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(EnumSet.of(Public)).ids(id).dmpSubQuery(this.queryFactory.query(DmpQuery.class).isActive(IsActive.Active).statuses(DmpStatus.Finalized).accessTypes(DmpAccessType.Public)); PublicDescription model = this.builderFactory.builder(PublicDescriptionBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet)); - if (model == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (model == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, PublicDescription.class.getSimpleName()}, LocaleContextHolder.getLocale())); this.auditService.track(AuditableAction.Description_PublicLookup, Map.ofEntries( new AbstractMap.SimpleEntry("id", id), @@ -148,7 +145,7 @@ public class DescriptionController { DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id); Description model = this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(fieldSet, query.firstAs(fieldSet)); if (model == null) - throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); this.auditService.track(AuditableAction.Description_Lookup, Map.ofEntries( new AbstractMap.SimpleEntry("id", id), @@ -188,6 +185,21 @@ public class DescriptionController { return persisted; } + @GetMapping("validate") + public List validate(@RequestParam(value="descriptionIds") List descriptionIds) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + logger.debug(new MapLogEntry("validating" + Description.class.getSimpleName()).And("descriptionIds", descriptionIds)); + + this.censorFactory.censor(DescriptionCensor.class).censor(null, null); + + List descriptionValidationResults = this.descriptionService.validate(descriptionIds); + + this.auditService.track(AuditableAction.Description_Validate, Map.ofEntries( + new AbstractMap.SimpleEntry("descriptionIds", descriptionIds) + )); + + return descriptionValidationResults; + } + @DeleteMapping("{id}") @Transactional public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException, IOException { diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java index b1d068870..eff6a60a9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java @@ -171,6 +171,37 @@ public class DmpController { this.auditService.track(AuditableAction.Dmp_Delete, "id", id); } + @PostMapping("finalize/{id}") + @Transactional + public boolean finalize(@PathVariable("id") UUID id, @RequestBody DescriptionsToBeFinalized descriptions) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException { + logger.debug(new MapLogEntry("finalizing" + Dmp.class.getSimpleName()).And("id", id).And("descriptionIds", descriptions.getDescriptionIds())); + + this.dmpService.finalize(id, descriptions.getDescriptionIds()); + + this.auditService.track(AuditableAction.Dmp_Finalize, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("descriptionIds", descriptions.getDescriptionIds()) + )); + + return true; + } + + @GetMapping("undo-finalize/{id}") + @Transactional + public boolean undoFinalize(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException { + logger.debug(new MapLogEntry("undo-finalizing" + Dmp.class.getSimpleName()).And("id", id)); + + this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null); + + this.dmpService.undoFinalize(id, fieldSet); + + this.auditService.track(AuditableAction.Dmp_Undo_Finalize, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id) + )); + + return true; + } + @PostMapping("clone") @Transactional @ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model") diff --git a/dmp-frontend/src/app/core/services/description/description.service.ts b/dmp-frontend/src/app/core/services/description/description.service.ts index b363ad425..ea122fbb1 100644 --- a/dmp-frontend/src/app/core/services/description/description.service.ts +++ b/dmp-frontend/src/app/core/services/description/description.service.ts @@ -91,8 +91,13 @@ export class DescriptionService { catchError((error: any) => throwError(error))); } - public validate(descriptionIds: Guid[]): Observable { - return of(new Array()); + validate(descriptionIds: Guid[]): Observable { + const url = `${this.apiBase}/validate`; + const options = {params: { descriptionIds: descriptionIds} }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); } // public downloadPDF(id: string): Observable> { diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 410a38920..772eff79a 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -90,6 +90,24 @@ export class DmpService { catchError((error: any) => throwError(error))); } + finalize(id: Guid, descriptionIds: Guid[] = []): Observable { + const url = `${this.apiBase}/finalize/${id}`; + + return this.http + .post(url, {descriptionIds: descriptionIds}).pipe( + catchError((error: any) => throwError(error))); + } + + undoFinalize(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/undo-finalize/${id}`; + + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + clone(item: CloneDmpPersist, reqFields: string[] = []): Observable { const url = `${this.apiBase}/clone`; const options = { params: { f: reqFields } }; diff --git a/dmp-frontend/src/app/ui/description/editor/description-base-fields-editor/description-base-fields-editor.component.html b/dmp-frontend/src/app/ui/description/editor/description-base-fields-editor/description-base-fields-editor.component.html index 42892b5bd..f55816bba 100644 --- a/dmp-frontend/src/app/ui/description/editor/description-base-fields-editor/description-base-fields-editor.component.html +++ b/dmp-frontend/src/app/ui/description/editor/description-base-fields-editor/description-base-fields-editor.component.html @@ -36,7 +36,7 @@
-
1.3 {{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.TAGS' | translate}}*
+
1.3 {{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.TAGS' | translate}}
diff --git a/dmp-frontend/src/app/ui/description/editor/description-editor.model.ts b/dmp-frontend/src/app/ui/description/editor/description-editor.model.ts index 125bf9237..9e02581ac 100644 --- a/dmp-frontend/src/app/ui/description/editor/description-editor.model.ts +++ b/dmp-frontend/src/app/ui/description/editor/description-editor.model.ts @@ -75,8 +75,8 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti baseValidationArray.push({ key: 'dmpDescriptionTemplateId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'dmpDescriptionTemplateId')] }); baseValidationArray.push({ key: 'descriptionTemplateId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplateId')] }); baseValidationArray.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] }); - baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); - baseValidationArray.push({ key: 'tags', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'tags')] }); + baseValidationArray.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] }); + baseValidationArray.push({ key: 'tags', validators: [BackendErrorValidator(this.validationErrorModel, 'tags')] }); baseValidationArray.push({ key: 'hash', validators: [] }); baseContext.validation = baseValidationArray; diff --git a/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts index d8e74ab0a..d7ccefe31 100644 --- a/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts +++ b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts @@ -201,6 +201,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit [nameof(x => x.descriptionTemplate), nameof(x => x.groupId)].join('.'), [nameof(x => x.dmp), nameof(x => x.id)].join('.'), [nameof(x => x.dmp), nameof(x => x.label)].join('.'), + [nameof(x => x.dmp), nameof(x => x.status)].join('.'), [nameof(x => x.dmp), nameof(x => x.accessType)].join('.'), [nameof(x => x.dmp), nameof(x => x.dmpUsers), nameof(x => x.id)].join('.'), [nameof(x => x.dmp), nameof(x => x.dmpUsers), nameof(x => x.user.id)].join('.'), @@ -232,7 +233,9 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit if (!result) { return []; } this.totalCount = result.count; if (lookup?.page?.offset === 0) this.listingItems = []; - this.listingItems.push(...result.items); + result.items.forEach(description => { + if (description.status != DescriptionStatus.Canceled) this.listingItems.push(description); + }) this.hasListingItems = true; }); } diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts index a015d5cd2..ebe776c9e 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts @@ -37,6 +37,7 @@ export class DmpEditorResolver extends BaseEditorResolver { nameof(x => x.version), nameof(x => x.updatedAt), nameof(x => x.publicAfter), + nameof(x => x.creator), nameof(x => x.hash), [nameof(x => x.authorizationFlags), AppPermission.EditDmp].join('.'), diff --git a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html index 11cb41d9f..9316e8c3a 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html +++ b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html @@ -25,12 +25,12 @@ -
+
bookmark bookmark

- {{ 'TYPES.DATASET-STATUS.DRAFT' | translate }} + {{ 'TYPES.DESCRIPTION-STATUS.DRAFT' | translate }} ({{'DMP-FINALISE-DIALOG.INVALID' | translate}}) @@ -41,7 +41,7 @@

{{ description.label }}

-
{{ 'DMP-FINALISE-DIALOG.EMPTY' | translate }}
+
{{ 'DMP-FINALISE-DIALOG.EMPTY' | translate }}
diff --git a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts index 892d9091d..eb4469a25 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts @@ -43,19 +43,21 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit } onSubmit() { - this.dialogRef.close(this.descriptionsToBeFinalized); + this.dialogRef.close({ descriptionsToBeFinalized: this.descriptionsToBeFinalized } as DmpFinalizeDialogOutput); } getFinalizedDescriptions() { + if (!this.dmp.descriptions) return []; return this.dmp.descriptions.filter(x => x.status === DescriptionStatus.Finalized); } close() { - this.dialogRef.close(null); + this.dialogRef.close({ cancelled: true } as DmpFinalizeDialogOutput); } validateDescriptions(dmp: Dmp) { - if (!dmp.descriptions.some(x => x.status == DescriptionStatus.Draft)) return; + if (!dmp.descriptions?.some(x => x.status == DescriptionStatus.Draft)) return; + this.descriptionService.validate(dmp.descriptions.filter(x => x.status == DescriptionStatus.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed), ).subscribe(result => { this.validationResults = result; @@ -63,6 +65,7 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit } get validDraftDescriptions() { + if (!this.dmp.descriptions) return []; return this.dmp.descriptions.filter(x => this.validationResults.some(y => y.descriptionId == x.id && y.result == DescriptionValidationOutput.Valid)); } } @@ -76,3 +79,8 @@ export enum DescriptionValidationOutput { Valid = 1, Invalid = 2 } + +export interface DmpFinalizeDialogOutput { + cancelled?: boolean; + descriptionsToBeFinalized?: Guid[]; +} diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts index 56619aef4..d625b4566 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts @@ -32,6 +32,8 @@ import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { Reference } from '@app/core/model/reference/reference'; import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { DescriptionStatus } from '@app/core/common/enum/description-status'; @Component({ selector: 'app-dmp-listing-component', @@ -197,6 +199,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr [nameof(x => x.descriptions), nameof(x => x.id)].join('.'), [nameof(x => x.descriptions), nameof(x => x.label)].join('.'), + [nameof(x => x.descriptions), nameof(x => x.status)].join('.'), [nameof(x => x.descriptions), nameof(x => x.isActive)].join('.'), [nameof(x => x.blueprint), nameof(x => x.id)].join('.'), @@ -239,7 +242,13 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr this.totalCount = result.count; if (lookup?.page?.offset === 0) this.listingItems = []; result.items.forEach(x=> { - x.descriptions = x.descriptions?.filter(x=> x.isActive === IsActive.Active); + if (x.descriptions) { + if (x.status == DmpStatus.Finalized) { + x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized); + } else { + x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled); + } + } x.dmpUsers = x.dmpUsers.filter(x=> x.isActive === IsActive.Active); this.listingItems.push(x); }) diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts index 5399d4ddb..feaf3f4f4 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts @@ -44,7 +44,8 @@ import { NewVersionDmpDialogComponent } from '../new-version-dialog/dmp-new-vers import { AppPermission } from '@app/core/common/enum/permission.enum'; import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { IsActive } from '@app/core/common/enum/is-active.enum'; -import { DmpFinalizeDialogComponent } from '../dmp-finalize-dialog/dmp-finalize-dialog.component'; +import { DmpFinalizeDialogComponent, DmpFinalizeDialogOutput } from '../dmp-finalize-dialog/dmp-finalize-dialog.component'; +import { DmpEditorResolver } from '../dmp-editor-blueprint/dmp-editor.resolver'; @Component({ selector: 'app-dmp-overview', @@ -117,7 +118,13 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .subscribe(data => { this.dmp = data; this.dmp.dmpUsers = data.dmpUsers.filter(x => x.isActive === IsActive.Active); - if (this.dmp.descriptions) this.dmp.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active); + if (this.dmp.descriptions) { + if (this.dmp.status == DmpStatus.Finalized) { + this.dmp.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized); + } else { + this.dmp.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled); + } + } this.selectedBlueprint = data.blueprint; this.researchers = this.referenceService.getReferencesForTypes(this.dmp?.dmpReferences, [this.referenceTypeService.getResearcherReferenceType()]); if (!this.hasDoi()) { @@ -502,17 +509,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { dmp: this.dmp } }); - dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((descriptionsToBeFinalized: Guid[]) => { - if (descriptionsToBeFinalized && descriptionsToBeFinalized.length > 0) { + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: DmpFinalizeDialogOutput) => { + if (result && !result.cancelled) { - // this.dmpService.finalize(descriptionsToBeFinalized, this.dmp.id) - // .pipe(takeUntil(this._destroyed)) - // .subscribe( - // complete => { - - // }, - // error => this.onUpdateCallbackError(error) - // ); + this.dmpService.finalize(this.dmp.id, result.descriptionsToBeFinalized) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.reloadPage(); + this.onUpdateCallbackSuccess() + }, (error: any) => { + this.onUpdateCallbackError(error) + }); } }); @@ -603,28 +610,26 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { } reverseFinalization() { - //TODO: add this - // const dialogRef = this.dialog.open(ConfirmationDialogComponent, { - // restoreFocus: false, - // data: { - // message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.UNFINALIZE-ITEM'), - // confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), - // cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), - // isDeleteConfirmation: false - // } - // }); - // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { - // if (result) { - // this.dmpService.unfinalize(this.dmp.id).pipe(takeUntil(this._destroyed)) - // .subscribe( - // complete => { - // this.dmp.status = DmpStatus.Draft; - // this.onUpdateCallbackSuccess() - // }, - // error => this.onUpdateCallbackError(error) - // ); - // } - // }); + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.TITLE'), + confirmButton: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.CONFIRM'), + cancelButton: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.NEGATIVE'), + isDeleteConfirmation: false + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.dmpService.undoFinalize(this.dmp.id, DmpEditorResolver.lookupFields()).pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.reloadPage(); + this.onUpdateCallbackSuccess() + }, (error: any) => { + this.onUpdateCallbackError(error) + }); + } + }); } goBack(): void { @@ -727,7 +732,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { nameof(x => x.groupId), nameof(x => x.version), nameof(x => x.updatedAt), - nameof(x => x.updatedAt), + nameof(x => x.entityDois), [nameof(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'), [nameof(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'), [nameof(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'), diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 98c8c2e76..9c54d226d 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -956,6 +956,11 @@ "INVITE-SHORT": "Invite", "REMOVE-AUTHOR": "Remove" }, + "UNDO-FINALIZATION-DIALOG": { + "TITLE": "Undo Finalization?", + "CONFIRM": "Yes", + "NEGATIVE": "No" + }, "COLLABORATORS": "Collaborators", "PRIVATE": "Private", "UNLOCKED": "Unlocked", @@ -2306,7 +2311,7 @@ }, "DESCRIPTION-STATUS": { "DRAFT": "Draft", - "FINALISED": "Finalized", + "FINALIZED": "Finalized", "CANCELED": "Canceled" }, "DMP-USER-ROLE": {