add dmp finalize

This commit is contained in:
amentis 2024-03-15 09:39:36 +02:00
parent 5b23cc0aa6
commit 3045130b27
21 changed files with 338 additions and 56 deletions

View File

@ -42,6 +42,9 @@ public class AuditableAction {
public static final EventId Dmp_PublicQuery = new EventId(5010, "Dmp_PublicQuery"); 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_Export = new EventId(5011, "Dmp_Export");
public static final EventId Dmp_PublicLookup = new EventId(5012, "Dmp_PublicLookup"); 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_Query = new EventId(6000, "Description_Query");
public static final EventId Description_Lookup = new EventId(6001, "Description_Lookup"); public static final EventId Description_Lookup = new EventId(6001, "Description_Lookup");
@ -52,6 +55,7 @@ public class AuditableAction {
public static final EventId Description_PersistStatus = new EventId(6006, "Description_PersistStatus"); 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_UploadFieldFiles = new EventId(6007, "Description_UploadFieldFiles");
public static final EventId Description_GetFieldFile = new EventId(6008, "Description_GetFieldFile"); 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"); public static final EventId Reference_Query = new EventId(7000, "Reference_Query");

View File

@ -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<Short> {
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<Short, DescriptionValidationOutput> map = EnumUtils.getEnumValueMap(DescriptionValidationOutput.class);
public static DescriptionValidationOutput of(Short i) {
return map.get(i);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,17 @@
package eu.eudat.model;
import java.util.List;
import java.util.UUID;
public class DescriptionsToBeFinalized {
private List<UUID> descriptionIds;
public List<UUID> getDescriptionIds() {
return descriptionIds;
}
public void setDescriptionIds(List<UUID> descriptionIds) {
this.descriptionIds = descriptionIds;
}
}

View File

@ -2,6 +2,7 @@ package eu.eudat.service.description;
import eu.eudat.data.StorageFileEntity; import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.Description; import eu.eudat.model.Description;
import eu.eudat.model.DescriptionValidationResult;
import eu.eudat.model.StorageFile; import eu.eudat.model.StorageFile;
import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.model.persist.DescriptionPersist; import eu.eudat.model.persist.DescriptionPersist;
@ -16,6 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface DescriptionService { public interface DescriptionService {
@ -25,6 +27,8 @@ public interface DescriptionService {
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException;
List<DescriptionValidationResult> validate(List<UUID> descriptionIds);
void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException; void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException;
ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException; ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException;

View File

@ -25,6 +25,7 @@ import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.builder.DescriptionBuilder;
import eu.eudat.model.builder.DescriptionTemplateBuilder;
import eu.eudat.model.deleter.DescriptionDeleter; import eu.eudat.model.deleter.DescriptionDeleter;
import eu.eudat.model.deleter.DescriptionReferenceDeleter; import eu.eudat.model.deleter.DescriptionReferenceDeleter;
import eu.eudat.model.deleter.DescriptionTagDeleter; 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); return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
} }
public List<DescriptionValidationResult> validate(List<UUID> descriptionIds){
List<DescriptionValidationResult> descriptionValidationResults = new ArrayList<>();
List<DescriptionEntity> 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<String, List<UUID>> fieldToReferenceMap){ private @NotNull PropertyDefinitionEntity buildPropertyDefinitionEntity(PropertyDefinitionPersist persist, eu.eudat.commons.types.descriptiontemplate.DefinitionEntity definition, Map<String, List<UUID>> fieldToReferenceMap){
PropertyDefinitionEntity data = new PropertyDefinitionEntity(); PropertyDefinitionEntity data = new PropertyDefinitionEntity();
if (persist == null) return data; if (persist == null) return data;

View File

@ -24,6 +24,10 @@ public interface DmpService {
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException;
void finalize(UUID id, List<UUID> 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 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; Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException;

View File

@ -816,6 +816,70 @@ public class DmpServiceImpl implements DmpService {
return null; return null;
} }
public void finalize(UUID id, List<UUID> 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<DescriptionEntity> 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<DescriptionEntity> 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<DescriptionEntity> 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 // invites
public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException { public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.InviteDmpUsers); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.InviteDmpUsers);

View File

@ -7,14 +7,11 @@ import eu.eudat.commons.enums.DmpStatus;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.data.StorageFileEntity; import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.StorageFile; import eu.eudat.model.*;
import eu.eudat.model.builder.PublicDescriptionBuilder; import eu.eudat.model.builder.PublicDescriptionBuilder;
import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.service.storage.StorageFileService; import eu.eudat.service.storage.StorageFileService;
import gr.cite.tools.validation.ValidationFilterAnnotation; 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.builder.DescriptionBuilder;
import eu.eudat.model.censorship.DescriptionCensor; import eu.eudat.model.censorship.DescriptionCensor;
import eu.eudat.model.censorship.PublicDescriptionCensor; 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)); 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)); 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( this.auditService.track(AuditableAction.Description_PublicLookup, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id), new AbstractMap.SimpleEntry<String, Object>("id", id),
@ -148,7 +145,7 @@ public class DescriptionController {
DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id); 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)); Description model = this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(fieldSet, query.firstAs(fieldSet));
if (model == null) 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( this.auditService.track(AuditableAction.Description_Lookup, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id), new AbstractMap.SimpleEntry<String, Object>("id", id),
@ -188,6 +185,21 @@ public class DescriptionController {
return persisted; return persisted;
} }
@GetMapping("validate")
public List<DescriptionValidationResult> validate(@RequestParam(value="descriptionIds") List<UUID> 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<DescriptionValidationResult> descriptionValidationResults = this.descriptionService.validate(descriptionIds);
this.auditService.track(AuditableAction.Description_Validate, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("descriptionIds", descriptionIds)
));
return descriptionValidationResults;
}
@DeleteMapping("{id}") @DeleteMapping("{id}")
@Transactional @Transactional
public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException, IOException { public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {

View File

@ -171,6 +171,37 @@ public class DmpController {
this.auditService.track(AuditableAction.Dmp_Delete, "id", id); 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<String, Object>("id", id),
new AbstractMap.SimpleEntry<String, Object>("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<String, Object>("id", id)
));
return true;
}
@PostMapping("clone") @PostMapping("clone")
@Transactional @Transactional
@ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model") @ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model")

View File

@ -91,8 +91,13 @@ export class DescriptionService {
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
public validate(descriptionIds: Guid[]): Observable<DescriptionValidationResult[]> { validate(descriptionIds: Guid[]): Observable<DescriptionValidationResult[]> {
return of(new Array<DescriptionValidationResult>()); const url = `${this.apiBase}/validate`;
const options = {params: { descriptionIds: descriptionIds} };
return this.http
.get<DescriptionValidationResult[]>(url, options).pipe(
catchError((error: any) => throwError(error)));
} }
// public downloadPDF(id: string): Observable<HttpResponse<Blob>> { // public downloadPDF(id: string): Observable<HttpResponse<Blob>> {

View File

@ -90,6 +90,24 @@ export class DmpService {
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
finalize(id: Guid, descriptionIds: Guid[] = []): Observable<Boolean> {
const url = `${this.apiBase}/finalize/${id}`;
return this.http
.post<Boolean>(url, {descriptionIds: descriptionIds}).pipe(
catchError((error: any) => throwError(error)));
}
undoFinalize(id: Guid, reqFields: string[] = []): Observable<Boolean> {
const url = `${this.apiBase}/undo-finalize/${id}`;
const options = { params: { f: reqFields } };
return this.http
.get<Boolean>(url, options).pipe(
catchError((error: any) => throwError(error)));
}
clone(item: CloneDmpPersist, reqFields: string[] = []): Observable<Dmp> { clone(item: CloneDmpPersist, reqFields: string[] = []): Observable<Dmp> {
const url = `${this.apiBase}/clone`; const url = `${this.apiBase}/clone`;
const options = { params: { f: reqFields } }; const options = { params: { f: reqFields } };

View File

@ -36,7 +36,7 @@
<!-- Tags --> <!-- Tags -->
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="heading">1.3 {{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.TAGS' | translate}}*</div> <div class="heading">1.3 {{'DESCRIPTION-EDITOR.BASE-INFO.FIELDS.TAGS' | translate}}</div>
<div class="profile-form"> <div class="profile-form">
<app-tags-field-component [form]="formGroup.get('tags')"></app-tags-field-component> <app-tags-field-component [form]="formGroup.get('tags')"></app-tags-field-component>
</div> </div>

View File

@ -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: 'dmpDescriptionTemplateId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'dmpDescriptionTemplateId')] });
baseValidationArray.push({ key: 'descriptionTemplateId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplateId')] }); 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: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); baseValidationArray.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] });
baseValidationArray.push({ key: 'tags', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'tags')] }); baseValidationArray.push({ key: 'tags', validators: [BackendErrorValidator(this.validationErrorModel, 'tags')] });
baseValidationArray.push({ key: 'hash', validators: [] }); baseValidationArray.push({ key: 'hash', validators: [] });
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;

View File

@ -201,6 +201,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
[nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'), [nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.id)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.id)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.label)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.label)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.status)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.accessType)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.accessType)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
@ -232,7 +233,9 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
if (!result) { return []; } if (!result) { return []; }
this.totalCount = result.count; this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = []; 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; this.hasListingItems = true;
}); });
} }

View File

@ -37,6 +37,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
nameof<Dmp>(x => x.version), nameof<Dmp>(x => x.version),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.updatedAt),
nameof<Dmp>(x => x.publicAfter), nameof<Dmp>(x => x.publicAfter),
nameof<Dmp>(x => x.creator),
nameof<Dmp>(x => x.hash), nameof<Dmp>(x => x.hash),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'), [nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),

View File

@ -25,12 +25,12 @@
</mat-panel-title> </mat-panel-title>
<mat-panel-description></mat-panel-description> <mat-panel-description></mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<div *ngIf="dmp.descriptions.length > 0"> <div *ngIf="dmp.descriptions && dmp.descriptions.length > 0">
<div *ngFor="let description of dmp.descriptions" class="row pl-3 descriptions"> <div *ngFor="let description of dmp.descriptions" class="row pl-3 descriptions">
<mat-icon *ngIf="description.status == descriptionStatusEnum.Draft" class="col-1 draft-bookmark">bookmark</mat-icon> <mat-icon *ngIf="description.status == descriptionStatusEnum.Draft" class="col-1 draft-bookmark">bookmark</mat-icon>
<mat-icon *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-1 finalized-bookmark">bookmark</mat-icon> <mat-icon *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-1 finalized-bookmark">bookmark</mat-icon>
<h4 *ngIf="description.status == descriptionStatusEnum.Draft" class="col-11 ml-auto mt-1 mb-4"> <h4 *ngIf="description.status == descriptionStatusEnum.Draft" class="col-11 ml-auto mt-1 mb-4">
<span>{{ 'TYPES.DATASET-STATUS.DRAFT' | translate }} <span>{{ 'TYPES.DESCRIPTION-STATUS.DRAFT' | translate }}
<ng-container *ngIf="!isDescriptionValid(description.id)"> <ng-container *ngIf="!isDescriptionValid(description.id)">
({{'DMP-FINALISE-DIALOG.INVALID' | translate}}) ({{'DMP-FINALISE-DIALOG.INVALID' | translate}})
</ng-container> </ng-container>
@ -41,7 +41,7 @@
<h4 *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-11 ml-auto mt-1 mb-4">{{ description.label }}</h4> <h4 *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-11 ml-auto mt-1 mb-4">{{ description.label }}</h4>
</div> </div>
</div> </div>
<div *ngIf="dmp.descriptions.length === 0" class="emptyList">{{ 'DMP-FINALISE-DIALOG.EMPTY' | translate }} </div> <div *ngIf="!dmp.descriptions" class="emptyList">{{ 'DMP-FINALISE-DIALOG.EMPTY' | translate }} </div>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>

View File

@ -43,19 +43,21 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit
} }
onSubmit() { onSubmit() {
this.dialogRef.close(this.descriptionsToBeFinalized); this.dialogRef.close({ descriptionsToBeFinalized: this.descriptionsToBeFinalized } as DmpFinalizeDialogOutput);
} }
getFinalizedDescriptions() { getFinalizedDescriptions() {
if (!this.dmp.descriptions) return [];
return this.dmp.descriptions.filter(x => x.status === DescriptionStatus.Finalized); return this.dmp.descriptions.filter(x => x.status === DescriptionStatus.Finalized);
} }
close() { close() {
this.dialogRef.close(null); this.dialogRef.close({ cancelled: true } as DmpFinalizeDialogOutput);
} }
validateDescriptions(dmp: Dmp) { 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), this.descriptionService.validate(dmp.descriptions.filter(x => x.status == DescriptionStatus.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed),
).subscribe(result => { ).subscribe(result => {
this.validationResults = result; this.validationResults = result;
@ -63,6 +65,7 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit
} }
get validDraftDescriptions() { 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)); 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, Valid = 1,
Invalid = 2 Invalid = 2
} }
export interface DmpFinalizeDialogOutput {
cancelled?: boolean;
descriptionsToBeFinalized?: Guid[];
}

View File

@ -32,6 +32,8 @@ import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { Reference } from '@app/core/model/reference/reference'; import { Reference } from '@app/core/model/reference/reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { AppPermission } from '@app/core/common/enum/permission.enum'; 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({ @Component({
selector: 'app-dmp-listing-component', selector: 'app-dmp-listing-component',
@ -197,6 +199,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'), [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'), [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'), [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.id)].join('.'), [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.id)].join('.'),
@ -239,7 +242,13 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
this.totalCount = result.count; this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = []; if (lookup?.page?.offset === 0) this.listingItems = [];
result.items.forEach(x=> { 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); x.dmpUsers = x.dmpUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(x); this.listingItems.push(x);
}) })

View File

@ -44,7 +44,8 @@ import { NewVersionDmpDialogComponent } from '../new-version-dialog/dmp-new-vers
import { AppPermission } from '@app/core/common/enum/permission.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum';
import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { IsActive } from '@app/core/common/enum/is-active.enum'; 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({ @Component({
selector: 'app-dmp-overview', selector: 'app-dmp-overview',
@ -117,7 +118,13 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
.subscribe(data => { .subscribe(data => {
this.dmp = data; this.dmp = data;
this.dmp.dmpUsers = data.dmpUsers.filter(x => x.isActive === IsActive.Active); 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.selectedBlueprint = data.blueprint;
this.researchers = this.referenceService.getReferencesForTypes(this.dmp?.dmpReferences, [this.referenceTypeService.getResearcherReferenceType()]); this.researchers = this.referenceService.getReferencesForTypes(this.dmp?.dmpReferences, [this.referenceTypeService.getResearcherReferenceType()]);
if (!this.hasDoi()) { if (!this.hasDoi()) {
@ -502,17 +509,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
dmp: this.dmp dmp: this.dmp
} }
}); });
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((descriptionsToBeFinalized: Guid[]) => { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: DmpFinalizeDialogOutput) => {
if (descriptionsToBeFinalized && descriptionsToBeFinalized.length > 0) { if (result && !result.cancelled) {
// this.dmpService.finalize(descriptionsToBeFinalized, this.dmp.id) this.dmpService.finalize(this.dmp.id, result.descriptionsToBeFinalized)
// .pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
// .subscribe( .subscribe(data => {
// complete => { this.reloadPage();
this.onUpdateCallbackSuccess()
// }, }, (error: any) => {
// error => this.onUpdateCallbackError(error) this.onUpdateCallbackError(error)
// ); });
} }
}); });
@ -603,28 +610,26 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
} }
reverseFinalization() { reverseFinalization() {
//TODO: add this const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
// const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false,
// restoreFocus: false, data: {
// data: { message: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.TITLE'),
// message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.UNFINALIZE-ITEM'), confirmButton: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.CONFIRM'),
// confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), cancelButton: this.language.instant('DMP-OVERVIEW.UNDO-FINALIZATION-DIALOG.NEGATIVE'),
// cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), isDeleteConfirmation: false
// isDeleteConfirmation: false }
// } });
// }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
// dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) {
// if (result) { this.dmpService.undoFinalize(this.dmp.id, DmpEditorResolver.lookupFields()).pipe(takeUntil(this._destroyed))
// this.dmpService.unfinalize(this.dmp.id).pipe(takeUntil(this._destroyed)) .subscribe(data => {
// .subscribe( this.reloadPage();
// complete => { this.onUpdateCallbackSuccess()
// this.dmp.status = DmpStatus.Draft; }, (error: any) => {
// this.onUpdateCallbackSuccess() this.onUpdateCallbackError(error)
// }, });
// error => this.onUpdateCallbackError(error) }
// ); });
// }
// });
} }
goBack(): void { goBack(): void {
@ -727,7 +732,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
nameof<Dmp>(x => x.groupId), nameof<Dmp>(x => x.groupId),
nameof<Dmp>(x => x.version), nameof<Dmp>(x => x.version),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.updatedAt),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.entityDois),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'), [nameof<Dmp>(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'), [nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'), [nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'),

View File

@ -956,6 +956,11 @@
"INVITE-SHORT": "Invite", "INVITE-SHORT": "Invite",
"REMOVE-AUTHOR": "Remove" "REMOVE-AUTHOR": "Remove"
}, },
"UNDO-FINALIZATION-DIALOG": {
"TITLE": "Undo Finalization?",
"CONFIRM": "Yes",
"NEGATIVE": "No"
},
"COLLABORATORS": "Collaborators", "COLLABORATORS": "Collaborators",
"PRIVATE": "Private", "PRIVATE": "Private",
"UNLOCKED": "Unlocked", "UNLOCKED": "Unlocked",
@ -2306,7 +2311,7 @@
}, },
"DESCRIPTION-STATUS": { "DESCRIPTION-STATUS": {
"DRAFT": "Draft", "DRAFT": "Draft",
"FINALISED": "Finalized", "FINALIZED": "Finalized",
"CANCELED": "Canceled" "CANCELED": "Canceled"
}, },
"DMP-USER-ROLE": { "DMP-USER-ROLE": {