Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Efstratios Giannopoulos 2024-03-15 14:14:27 +02:00
commit 388857cdce
36 changed files with 529 additions and 138 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_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");
@ -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");

View File

@ -76,6 +76,7 @@ public final class Permission {
public static String CreateNewVersionDmp = "CreateNewVersionDmp";
public static String ExportDmp = "ExportDmp";
public static String FinalizeDmp = "FinalizeDmp";
public static String UndoFinalizeDmp = "UndoFinalizeDmp";
public static String AssignDmpUsers = "AssignDmpUsers";
public static String InviteDmpUsers = "InviteDmpUsers";

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.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<DescriptionValidationResult> validate(List<UUID> descriptionIds);
void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException;
ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException;

View File

@ -26,6 +26,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;
@ -365,6 +366,27 @@ public class DescriptionServiceImpl implements DescriptionService {
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){
PropertyDefinitionEntity data = new PropertyDefinitionEntity();
if (persist == null) return data;

View File

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

View File

@ -816,6 +816,72 @@ public class DmpServiceImpl implements DmpService {
return null;
}
public void finalize(UUID id, List<UUID> descriptionIds) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.FinalizeDmp);
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");
}
List<DescriptionEntity> descriptions = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(id).isActive(IsActive.Active).collect();
for (DescriptionEntity description: descriptions) {
if (descriptionIds.contains(description.getId())){
// description to be finalized
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);
} else if (description.getStatus().equals(DescriptionStatus.Draft)) {
// description to be canceled
description.setStatus(DescriptionStatus.Canceled);
this.deleterFactory.deleter(DescriptionDeleter.class).delete(List.of(description), true);
}
}
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 {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.UndoFinalizeDmp);
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");
}
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).types(EntityType.DMP).entityIds(dmp.getId());
if (entityDoiQuery != null && entityDoiQuery.count() > 0){
throw new MyApplicationException("DMP is deposited");
}
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<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
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.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<String, Object>("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<String, Object>("id", id),
@ -188,6 +185,21 @@ public class DescriptionController {
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}")
@Transactional
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);
}
@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")
@Transactional
@ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model")

View File

@ -439,6 +439,19 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
UndoFinalizeDmp:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
AssignDmpUsers:
roles:
- Admin

View File

@ -21,7 +21,7 @@
display: flex;
align-items: center;
justify-content: center;
width: 260px;
width: 270px;
background: #ffffff 0% 0% no-repeat padding-box;
box-shadow: 0px 0px 16px 2px #00000029;
border-right-width: 0px;

View File

@ -91,8 +91,13 @@ export class DescriptionService {
catchError((error: any) => throwError(error)));
}
public validate(descriptionIds: Guid[]): Observable<DescriptionValidationResult[]> {
return of(new Array<DescriptionValidationResult>());
validate(descriptionIds: Guid[]): Observable<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>> {

View File

@ -90,6 +90,24 @@ export class DmpService {
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> {
const url = `${this.apiBase}/clone`;
const options = { params: { f: reqFields } };

View File

@ -148,10 +148,10 @@
<div class="personal-usage">{{'DASHBOARD.PUBLIC-USAGE' | translate}}</div>
<div [ngClass]="{'counter': dashboardStatistics?.dmpCount != 0, 'counter-zero': dashboardStatistics?.dmpCount == 0}">
{{dashboardStatistics?.dmpCount}}</div>
<a [routerLink]="['/plans']" class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
<a [routerLink]="['/explore-plans']" class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
<div [ngClass]="{'counter': dashboardStatistics?.descriptionCount != 0, 'counter-zero': dashboardStatistics?.descriptionCount == 0}">
{{dashboardStatistics?.descriptionCount}}</div>
<a [routerLink]="['/descriptions']" class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
<a [routerLink]="['/explore-descriptions']" class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
<div [ngClass]="{'counter': grantCount != 0, 'counter-zero': grantCount == 0}">
{{grantCount}}</div>
<a href="#" class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>

View File

@ -20,6 +20,9 @@ import { debounceTime, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { AuthService } from '../../../core/services/auth/auth.service';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
@Component({
selector: 'app-drafts',
@ -30,7 +33,7 @@ export class DraftsComponent extends BaseComponent implements OnInit {
lookup: RecentActivityItemLookup = new RecentActivityItemLookup();
pageSize: number = 5;
listingItems: RecentActivityItem[];
listingItems: RecentActivityItem[] = [];
public formGroup = new UntypedFormBuilder().group({
like: new UntypedFormControl(),
@ -131,8 +134,11 @@ export class DraftsComponent extends BaseComponent implements OnInit {
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.version)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.groupId)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.role)].join('.'),
@ -147,6 +153,7 @@ export class DraftsComponent extends BaseComponent implements OnInit {
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
@ -170,7 +177,22 @@ export class DraftsComponent extends BaseComponent implements OnInit {
.getMyRecentActivityItems(this.lookup)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
this.listingItems = response;
response.forEach(item => {
if (item.dmp){
if (item.dmp.descriptions) {
if (item.dmp.status == DmpStatus.Finalized) {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatus.Canceled);
}
}
item.dmp.dmpUsers = item.dmp.dmpUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(item);
}
if (item.description){
if (item.description.status != DescriptionStatus.Canceled) this.listingItems.push(item);
}
})
//this.totalCount = response.totalCount;

View File

@ -2,6 +2,9 @@ import { Location } from '@angular/common';
import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { RecentActivityItem } from '@app/core/model/dashboard/recent-activity-item';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
@ -30,7 +33,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
lookup: RecentActivityItemLookup = new RecentActivityItemLookup();
pageSize: number = 5;
listingItems: RecentActivityItem[];
listingItems: RecentActivityItem[]= [];
public formGroup = new UntypedFormBuilder().group({
like: new UntypedFormControl(),
@ -130,8 +133,11 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.version)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.groupId)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.role)].join('.'),
@ -146,6 +152,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
@ -168,7 +175,22 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
.getMyRecentActivityItems(this.lookup)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
this.listingItems = response;
response.forEach(item => {
if (item.dmp){
if (item.dmp.descriptions) {
if (item.dmp.status == DmpStatus.Finalized) {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatus.Canceled);
}
}
item.dmp.dmpUsers = item.dmp.dmpUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(item);
}
if (item.description){
if (item.description.status != DescriptionStatus.Canceled) this.listingItems.push(item);
}
})
//this.totalCount = response.totalCount;

View File

@ -18,6 +18,7 @@ import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
@Component({
selector: 'app-recent-edited-description-activity',
@ -28,7 +29,7 @@ export class RecentEditedDescriptionActivityComponent extends BaseComponent impl
lookup: RecentActivityItemLookup = new RecentActivityItemLookup();
pageSize: number = 5;
listingItems: Description[];
listingItems: Description[] = [];
public formGroup = new UntypedFormBuilder().group({
@ -148,7 +149,11 @@ export class RecentEditedDescriptionActivityComponent extends BaseComponent impl
.getMyRecentActivityItems(this.lookup)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
this.listingItems = response.map(x => x.description);
response.forEach(item => {
if (item.description){
if (item.description.status != DescriptionStatus.Canceled) this.listingItems.push(item.description);
}
})
// this.totalCount = response.count;

View File

@ -2,6 +2,9 @@ import { Location } from '@angular/common';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { RecentActivityItem } from '@app/core/model/dashboard/recent-activity-item';
import { Description } from '@app/core/model/description/description';
@ -33,13 +36,12 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O
@ViewChild("dmps") resultsContainer;
listingItems: Dmp[];
listingItems: Dmp[]= [];
isDraft: boolean;
totalCount: number;
startIndex: number = 0;
offsetLess: number = 0;
hasMoreResults: boolean = true;
dmpFormGroup: UntypedFormGroup;
public formGroup = new UntypedFormBuilder().group({
like: new UntypedFormControl(),
@ -137,6 +139,8 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
[nameof<RecentActivityItem>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.role)].join('.'),
@ -153,7 +157,20 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O
.getMyRecentActivityItems(this.lookup)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
this.listingItems = response.map(x => x.dmp);
response.forEach(item => {
if (item.dmp){
item.dmp.dmpUsers = item.dmp.dmpUsers.filter(x => x.isActive === IsActive.Active);
if (item.dmp.descriptions) {
if (item.dmp.status == DmpStatus.Finalized) {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
item.dmp.descriptions = item.dmp.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatus.Canceled);
}
}
this.listingItems.push(item.dmp);
}
})
//this.totalCount = response.totalCount;

View File

@ -36,7 +36,7 @@
<!-- Tags -->
<div class="row">
<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">
<app-tags-field-component [form]="formGroup.get('tags')"></app-tags-field-component>
</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: '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;

View File

@ -207,6 +207,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
[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.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.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
@ -238,7 +239,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;
});
}

View File

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

View File

@ -25,12 +25,12 @@
</mat-panel-title>
<mat-panel-description></mat-panel-description>
</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">
<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>
<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)">
({{'DMP-FINALISE-DIALOG.INVALID' | translate}})
</ng-container>
@ -41,7 +41,7 @@
<h4 *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-11 ml-auto mt-1 mb-4">{{ description.label }}</h4>
</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-accordion>

View File

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

View File

@ -7,9 +7,9 @@
<mat-icon class="close-icon">close</mat-icon>
</div>
</div>
<div mat-dialog-content class="row content">
<div mat-dialog-content class="row">
<div>
<app-user-field-component [form]="formGroup" [validationErrorModel]="editorModel.validationErrorModel" [sections]="selectedBlueprint.definition.sections"></app-user-field-component>
<app-user-field-component [form]="formGroup" [validationErrorModel]="editorModel.validationErrorModel" [sections]="selectedBlueprint.definition.sections" [initializeUsers]="'true'" [enableSorting]="'false'"></app-user-field-component>
</div>
<div class="col mt-2">
<button mat-raised-button *ngIf="hasValue()" (click)="send()" type="button" class="invite-btn">{{'DMP-USER-INVITATION-DIALOG.ACTIONS.INVITE' | translate}}</button>

View File

@ -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<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.status)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].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;
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);
})

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 { 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<Dmp>(x => x.groupId),
nameof<Dmp>(x => x.version),
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.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'),

View File

@ -1,9 +1,26 @@
<div cdkDropList class="col-12" (cdkDropListDropped)="dropUsers($event)">
<div *ngFor="let user of form.get('users').controls; let userIndex=index;" cdkDrag class="row align-items-center" [cdkDragDisabled]="form.disabled">
<div class="col-auto">
<div *ngIf="enableSorting==true; else sortingDisabled" class="col-12" cdkDropList (cdkDropListDropped)="dropUsers($event)">
hello!
<ng-container *ngTemplateOutlet="userForm"></ng-container>
</div>
<ng-template class="col-12" #sortingDisabled>
<ng-container *ngTemplateOutlet="userForm"></ng-container>
</ng-template>
<div class="row">
<div class="col">
<button mat-icon-button (click)="addUser()" [disabled]="form.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
<ng-template #userForm>
<div *ngFor="let user of form.get('users').controls; let userIndex=index;" cdkDrag class="user-fields-wrapper" [cdkDragDisabled]="form.disabled">
<div class="">
<span style="font-size: 15px;">{{userIndex + 1}}</span>
</div>
<div class="col-auto d-flex"><mat-icon [ngClass]="{'drag-handle-disabled': form.disabled}" cdkDragHandle class="drag-handle">drag_indicator</mat-icon></div>
<div *ngIf="enableSorting==true" class="col-auto d-flex"><mat-icon [ngClass]="{'drag-handle-disabled': form.disabled}" cdkDragHandle class="drag-handle">drag_indicator</mat-icon></div>
<div class="col-auto">
<mat-button-toggle-group name="fontStyle" aria-label="Font Style" [formControl]="user.get('userType')">
<div *ngFor="let userType of dmpUserTypeEnumValues">
@ -11,9 +28,7 @@
</div>
</mat-button-toggle-group>
</div>
<div class="col pt-4">
<div class="row">
<div class="col" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
<div class="col-auto" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.USER' | translate}}*</mat-label>
<app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete>
@ -21,7 +36,7 @@
<mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
<div class="col-auto" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.EMAIL' | translate}}*</mat-label>
<input matInput type="text" name="email" [formControl]="user.get('email')">
@ -29,7 +44,7 @@
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col">
<div class="col-auto">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.USER-ROLE' | translate}}</mat-label>
<mat-select [formControl]="user.get('role')">
@ -39,7 +54,7 @@
<mat-error *ngIf="user.get('role').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col" *ngIf="sections">
<div class="col-auto" *ngIf="sections">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.SECTION' | translate}}</mat-label>
<mat-select [formControl]="user.get('sectionId')">
@ -51,21 +66,13 @@
<mat-error *ngIf="user.get('sectionId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' |translate}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
<div class="col-auto">
<button mat-icon-button class="action-list-icon" matTooltip="{{'DMP-EDITOR.ACTIONS1.REMOVE-USER' | translate}}" (click)="removeUser(userIndex)" [disabled]="form.disabled">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
<mat-error *ngIf="form.get('users').dirty && form.get('users').hasError('required')">{{'DMP-EDITOR.USERS-REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="form.get('users').hasError('backendError')">{{form.get('users').getError('backendError').message}}</mat-error>
</div>
<div class="row">
<div class="col">
<button mat-icon-button (click)="addUser()" [disabled]="form.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
</ng-template>

View File

@ -7,3 +7,14 @@
cursor: auto;
color: rgba(0, 0, 0, 0.38);;
}
.user-fields-wrapper {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: baseline;
@media (width <= 1350px) {
flex-direction: column;
}
}

View File

@ -24,6 +24,8 @@ export class UserFieldComponent extends BaseComponent implements OnInit {
@Input() required: boolean = false;
@Input() placeholder: string;
@Input() sections: DmpBlueprintDefinitionSection[] = null;
@Input() initializeUsers: boolean = false;
@Input() enableSorting: boolean = true;
dmpUserTypeEnum = DmpUserType;
dmpUserTypeEnumValues = this.enumUtils.getEnumValues<DmpUserType>(DmpUserType);
@ -37,6 +39,10 @@ export class UserFieldComponent extends BaseComponent implements OnInit {
) { super(); }
ngOnInit() {
console.log('sorting mode: ', this.enableSorting);
if(this.initializeUsers) {
this.addUser();
}
}
addUser(): void {

View File

@ -243,7 +243,7 @@ export class NavbarComponent extends BaseComponent implements OnInit {
autoFocus: false,
closeOnNavigation: true,
disableClose: false,
position: { top: '64px', right: '1em' },
position: { top: '71px', right: '1em' },
panelClass: 'custom-userbox'
});
}

View File

@ -1,14 +1,15 @@
<form class="mb-0">
<div class="dropdown-top"></div>
<div class="dropdown-options">
<mat-divider class="col-12 top-divider"></mat-divider>
<div class="col-12 profile-settings">
<div class="dropdown-top"></div>
<form>
<div class="mt-2 col-12 dropdown-options">
<mat-divider class="top-divider"></mat-divider>
<div class="profile-settings">
<a mat-button class="profile mt-2" (click)="navigateToProfile()">{{'USER-DIALOG.USER-PROFILE-SETTINGS' | translate}}</a>
<a mat-button class="profile mb-2" (click)="navigateToMyDmps()">{{'USER-PROFILE.ASSOCIATED-DMPS' | translate}}</a>
</div>
<mat-divider class="col-12"></mat-divider>
<div class="col-12">
<a mat-button class="logout mb-2 mt-2" (click)="logout()">
<mat-divider></mat-divider>
<div>
<a mat-button class="logout mt-2" (click)="logout()">
{{ 'USER-DIALOG.LOG-OUT' | translate }}
</a>
</div>

View File

@ -16,7 +16,6 @@ $mat-card-header-size: 40px !default;
color: #212121;
height: 1.875rem;
line-height: 1.875rem;
width: 100%;
text-align: left;
}
@ -32,7 +31,8 @@ $mat-card-header-size: 40px !default;
border-bottom: 0.625rem solid #FFFFFF;
border-left: 0.625rem solid transparent;
border-right: 0.625rem solid transparent;
margin: 0 87%;
position: fixed;
transform: translate(11.85rem, -0.6rem);
}
.dropdown-options {

View File

@ -383,10 +383,12 @@
},
"ANNOTATION-DIALOG": {
"TITLE": "Comments",
"SUCCESS": "Comment successfully saved",
"THREADS": {
"NEW-THREAD": "New comment",
"FROM-USER": "From",
"SEND": "Send comment",
"REPLY": "Reply",
"PROTECTION": {
"TITLE": "Visibility"
}
@ -956,6 +958,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 +2313,7 @@
},
"DESCRIPTION-STATUS": {
"DRAFT": "Draft",
"FINALISED": "Finalized",
"FINALIZED": "Finalized",
"CANCELED": "Canceled"
},
"DMP-USER-ROLE": {