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

This commit is contained in:
Efstratios Giannopoulos 2024-04-03 18:35:52 +03:00
commit ea6ea25116
18 changed files with 260 additions and 85 deletions

View File

@ -219,8 +219,7 @@ public class DescriptionPersist {
.must(() -> !this.isNull(item.getProperties()))
.failOn(DescriptionPersist._properties).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionPersist._properties}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> item.getStatus() == DescriptionStatus.Finalized)
.must(() -> this.isDescriptionTemplateMultiplicityValid(finalDmpBlueprintEntity, item.getDmpId(), item.getDescriptionTemplateId()))
.must(() -> this.isDescriptionTemplateMaxMultiplicityValid(finalDmpBlueprintEntity, item.getDmpId(), item.getDmpDescriptionTemplateId(), this.isValidGuid(item.getId())))
.failOn(DescriptionPersist._descriptionTemplateId).failWith(messageSource.getMessage("Validation.InvalidDescriptionTemplateMultiplicity", new Object[]{DescriptionPersist._descriptionTemplateId}, LocaleContextHolder.getLocale()))
// this.refSpec()
// .iff(() -> !this.isNull(item.getProperties()))
@ -235,26 +234,28 @@ public class DescriptionPersist {
);
}
private boolean isDescriptionTemplateMultiplicityValid(DmpBlueprintEntity dmpBlueprintEntity, UUID dmpId, UUID descriptionTemplateId){
private boolean isDescriptionTemplateMaxMultiplicityValid(DmpBlueprintEntity dmpBlueprintEntity, UUID dmpId, UUID dmpDescriptionTemplateId, Boolean isUpdate){
eu.eudat.commons.types.dmpblueprint.DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(eu.eudat.commons.types.dmpblueprint.DefinitionEntity.class, dmpBlueprintEntity.getDefinition());
if (definition == null || this.isListNullOrEmpty(definition.getSections())) return true;
DmpDescriptionTemplateEntity dmpDescriptionTemplateEntity = this.queryFactory.query(DmpDescriptionTemplateQuery.class).ids(dmpDescriptionTemplateId).isActive(IsActive.Active).dmpIds(dmpId).first();
if (dmpDescriptionTemplateEntity == null) return true;
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(dmpId).dmpDescriptionTemplateIds(dmpDescriptionTemplateId).isActive(IsActive.Active).collect();
for (SectionEntity section: definition.getSections()) {
if (section.getHasTemplates() && !this.isListNullOrEmpty(section.getDescriptionTemplates())){
int descriptionsCount = 0;
if (dmpDescriptionTemplateEntity.getSectionId().equals(section.getId()) && section.getHasTemplates() && !this.isListNullOrEmpty(section.getDescriptionTemplates())){
int descriptionsCount;
if (isUpdate) descriptionsCount = -1;
else descriptionsCount = 0;
for (eu.eudat.commons.types.dmpblueprint.DescriptionTemplateEntity sectionDescriptionTemplate: section.getDescriptionTemplates()) {
if (sectionDescriptionTemplate.getMaxMultiplicity() == null && sectionDescriptionTemplate.getMinMultiplicity() == null ) continue;
DmpDescriptionTemplateQuery dmpDescriptionTemplateQuery = this.queryFactory.query(DmpDescriptionTemplateQuery.class).isActive(IsActive.Active).dmpIds(dmpId).sectionIds(section.getId());
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(dmpId).dmpDescriptionTemplateSubQuery(dmpDescriptionTemplateQuery).isActive(IsActive.Active).collect();
if (this.isListNullOrEmpty(descriptionEntities)) continue;
for (DescriptionEntity description: descriptionEntities){
if (description.getDescriptionTemplateId().equals(descriptionTemplateId)) descriptionsCount++;
if (sectionDescriptionTemplate.getDescriptionTemplateGroupId().equals(dmpDescriptionTemplateEntity.getDescriptionTemplateGroupId())){
for (DescriptionEntity description: descriptionEntities){
if (description.getDmpDescriptionTemplateId().equals(dmpDescriptionTemplateEntity.getId())) descriptionsCount++;
}
if (sectionDescriptionTemplate.getMaxMultiplicity() != null && sectionDescriptionTemplate.getMaxMultiplicity() <= descriptionsCount) return false;
}
if (sectionDescriptionTemplate.getMinMultiplicity() != null && sectionDescriptionTemplate.getMinMultiplicity() >= descriptionsCount) return false;
if (sectionDescriptionTemplate.getMaxMultiplicity() != null && sectionDescriptionTemplate.getMaxMultiplicity() <= descriptionsCount) return false;
}

View File

@ -54,6 +54,8 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
private final UserScope userScope;
private final AuthorizationService authService;
private final QueryUtilsService queryUtilsService;
private Collection<UUID> dmpDescriptionTemplateIds;
public DescriptionQuery(UserScope userScope, AuthorizationService authService, QueryUtilsService queryUtilsService) {
this.userScope = userScope;
this.authService = authService;
@ -110,7 +112,20 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
return this;
}
public DescriptionQuery dmpDescriptionTemplateIds(UUID value) {
this.dmpDescriptionTemplateIds = List.of(value);
return this;
}
public DescriptionQuery dmpDescriptionTemplateIds(UUID... value) {
this.dmpDescriptionTemplateIds = Arrays.asList(value);
return this;
}
public DescriptionQuery dmpDescriptionTemplateIds(Collection<UUID> values) {
this.dmpDescriptionTemplateIds = values;
return this;
}
public DescriptionQuery finalizedAfter(Instant value) {
this.finalizedAfter = value;
@ -287,6 +302,12 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
QueryContext<DmpEntity, UUID> subQuery = this.applySubQuery(this.dmpQuery, queryContext, UUID.class, dmpEntityRoot -> dmpEntityRoot.get(DmpEntity._id));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpId)).value(subQuery.Query));
}
if (this.dmpDescriptionTemplateIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId));
for (UUID item : this.dmpDescriptionTemplateIds)
inClause.value(item);
predicates.add(inClause);
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);

View File

@ -358,7 +358,9 @@ public class PrefillingSourceServiceImpl implements PrefillingSourceService {
eu.eudat.commons.types.descriptiontemplate.DefinitionEntity descriptionTemplateDefinition = this.xmlHandlingService.fromXml(eu.eudat.commons.types.descriptiontemplate.DefinitionEntity.class, descriptionTemplateEntity.getDefinition());
Description description = new Description();
description.setDescriptionTemplate(this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(fieldSet, descriptionTemplateEntity));
FieldSet descriptionTemplateFields = fieldSet.extractPrefixed(this.conventionService.asPrefix(Description._descriptionTemplate));
description.setDescriptionTemplate(this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(descriptionTemplateFields, descriptionTemplateEntity));
return mapPrefilledEntityToDescription(description, descriptionTemplateDefinition, prefillingSourceDefinition, prefillingSourceEntity.getLabel(), externalData.getResults().getFirst());//TODO
}

View File

@ -130,7 +130,8 @@ export class DmpBlueprintService {
lookup.project = {
fields: [
nameof<DmpBlueprint>(x => x.id),
nameof<DmpBlueprint>(x => x.label)
nameof<DmpBlueprint>(x => x.label),
nameof<DmpBlueprint>(x => x.version)
]
};
lookup.order = { items: [nameof<DmpBlueprint>(x => x.label)] };

View File

@ -283,6 +283,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.formGroup.get('status').setValue(DescriptionTemplateStatus.Finalized);
if(this.isNewVersion) this.isNewVersion = false;
this.persistEntity();
}});
}

View File

@ -550,6 +550,7 @@ export class DmpBlueprintEditorComponent extends BaseEditor<DmpBlueprintEditorMo
finalize() {
if (this.checkValidity()) {
this.formGroup.get('status').setValue(DmpBlueprintStatus.Finalized);
if(this.isNewVersion) this.isNewVersion = false;
this.formSubmit();
}
}

View File

@ -43,6 +43,9 @@ import { FormValidationErrorsDialogComponent } from '@common/forms/form-validati
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
import { DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
@Component({
selector: 'app-description-editor-component',
@ -55,24 +58,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
isNew = true;
isDeleted = false;
item: Description;
// showInactiveDetails = false;
// selectedSystemFields: Array<DescriptionSystemFieldType> = [];
// DescriptionSectionFieldCategory = DescriptionSectionFieldCategory;
// DescriptionSystemFieldType = DescriptionSystemFieldType;
// public DescriptionSystemFieldTypeEnum = this.enumUtils.getEnumValues<DescriptionSystemFieldType>(DescriptionSystemFieldType);
// DescriptionExtraFieldDataType = DescriptionExtraFieldDataType;
// public DescriptionExtraFieldDataTypeEnum = this.enumUtils.getEnumValues<DescriptionExtraFieldDataType>(DescriptionExtraFieldDataType);
// blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
// filterFn: this.filterDescriptionTempaltes.bind(this),
// initialItems: (excludedItems: any[]) => this.filterDescriptionTempaltes('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
// displayFn: (item: DatasetProfileModel) => item.label,
// titleFn: (item: DatasetProfileModel) => item.label,
// subtitleFn: (item: DatasetProfileModel) => item.description,
// popupItemActionIcon: 'visibility'
// };
// hasChanges = false;
fieldsetIdWithFocus: string;
viewOnly = false;
@ -211,10 +196,14 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
},
panelClass: 'custom-modalbox'
});
dialogRef.afterClosed().subscribe(result => {
dialogRef.afterClosed().subscribe((result: Description) => {
if (result) {
result.dmp = this.item.dmp;
result.dmpDescriptionTemplate = this.item.dmpDescriptionTemplate;
const sectionId = this.item.dmpDescriptionTemplate.sectionId;
result.dmpDescriptionTemplate = this.item.dmp.dmpDescriptionTemplates.find(x => x.sectionId == sectionId && x.descriptionTemplateGroupId == result.descriptionTemplate.groupId);
this.prepareForm(result);
// this.descriptionModel = this.descriptionModel.fromModel(result);
// this.descriptionModel.dmp = data;
@ -551,6 +540,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
try {
this.editorModel = data ? new DescriptionEditorModel().fromModel(data, data.descriptionTemplate) : new DescriptionEditorModel();
this.item = data;
if (data && data.dmpDescriptionTemplate?.sectionId && data.dmp?.blueprint?.definition?.sections?.length > 0 && data.dmp?.descriptions?.length > 0){
const section = data.dmp?.blueprint?.definition?.sections.find(x => x.id == data.dmpDescriptionTemplate?.sectionId);
if(section.hasTemplates) {
const notAvailableDescriptionTemplates = this.calculateMultiplicityRejectedDmpDescriptionTemplates(section, data.dmp.descriptions.filter(x => x.isActive == IsActive.Active));
this.item.dmp.dmpDescriptionTemplates = data.dmp.dmpDescriptionTemplates.filter(x => !notAvailableDescriptionTemplates.map(y => y.id).includes(x.id) )
}
}
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
} catch (error) {
@ -588,6 +584,26 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
}
calculateMultiplicityRejectedDmpDescriptionTemplates(section: DmpBlueprintDefinitionSection, descriptions: Description[]) : DmpDescriptionTemplate[]{
if (section.descriptionTemplates?.length > 0){
descriptions = descriptions?.filter(x => x?.dmpDescriptionTemplate?.sectionId === section.id) || [];
let rejectedDmpDescriptionTemplates: DmpDescriptionTemplate[] = [];
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
if (sectionDescriptionTemplate.maxMultiplicity != null){
const commonDescriptions = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId);
if (commonDescriptions && commonDescriptions.length >= sectionDescriptionTemplate.maxMultiplicity) {
rejectedDmpDescriptionTemplates = commonDescriptions.map(x => x.dmpDescriptionTemplate);
}
}
})
return rejectedDmpDescriptionTemplates;
} else{
return [];
}
}
refreshData(): void {
this.getItem(this.editorModel.id, (data: Description) => this.prepareForm(data));
}

View File

@ -156,7 +156,7 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateGroupId)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.label)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.minMultiplicity)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.maxMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.maxMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
@ -165,6 +165,13 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.currentDescriptionTemplate), nameof<DescriptionTemplate>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.currentDescriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.currentDescriptionTemplate), nameof<DescriptionTemplate>(x => x.version)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.descriptionTemplateGroupId)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.isActive)].join('.'),
]
}

View File

@ -17,6 +17,10 @@ import { TranslateService } from "@ngx-translate/core";
import { UUID } from "crypto";
import { Observable } from "rxjs";
import { map, takeUntil } from "rxjs/operators";
import { DescriptionEditorResolver } from "../description-editor.resolver";
import { nameof } from "ts-simple-nameof";
import { Description } from "@app/core/model/description/description";
import { IsActive } from "@app/core/common/enum/is-active.enum";
@Component({
selector: 'prefill-description-component',
@ -47,7 +51,7 @@ export class PrefillDescriptionDialogComponent extends BaseComponent implements
this.dmp = data.dmp;
this.dmpSectionId = data.dmpSectionId;
this.availableDescriptionTemplates = this.dmp.dmpDescriptionTemplates.filter(x => x.sectionId == this.dmpSectionId).map(x => x.currentDescriptionTemplate);
this.availableDescriptionTemplates = this.dmp.dmpDescriptionTemplates.filter(x => x.sectionId == this.dmpSectionId && x.isActive == IsActive.Active).map(x => x.currentDescriptionTemplate);
}
ngOnInit() {
@ -212,7 +216,7 @@ export class PrefillDescriptionDialogComponent extends BaseComponent implements
// }
const formData = this.formService.getValue(this.prefillForm.value) as DescriptionProfilingRequest;
this.prefillingSourceService.generate(formData, DescriptionTemplateEditorResolver.lookupFields())
this.prefillingSourceService.generate(formData, DescriptionEditorResolver.descriptionTemplateLookupFields(nameof<Description>(x => x.descriptionTemplate)))
.pipe(takeUntil(this._destroyed)).subscribe(description => {
if (description) {
this.closeDialog(description);
@ -226,4 +230,4 @@ export class PrefillDescriptionDialogComponent extends BaseComponent implements
closeDialog(result = null): void {
this.dialogRef.close(result);
}
}
}

View File

@ -96,11 +96,17 @@
</li>
</ol>
<ul *ngIf="item.id && section.hasTemplates && canEditSection(section.id) && !formGroup.disabled" class="add-description-option">
<li>
<li *ngIf="canAddDescription(section)">
<a class="add-description-action" [routerLink]="['/descriptions/edit/' + item.id + '/' + section.id]">
<mat-icon>add</mat-icon>{{'DMP-EDITOR.ACTIONS.ADD-DESCRIPTION-IN-SECTION' | translate}}
</a>
</li>
<li *ngIf="!canAddDescription(section)">
<a [ngClass]="{'drag-handle-disabled': true}" class="add-description-action">
<mat-icon>add</mat-icon>{{'DMP-EDITOR.ACTIONS.ADD-DESCRIPTION-IN-SECTION' | translate}}
</a>
<small class="text-danger">{{'DMP-EDITOR.DESCRIPTION-TEMPLATES-MAX-MULTIPLICITY'| translate}}</small>
</li>
</ul>
</div>
</div>

View File

@ -18,7 +18,7 @@ import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionSectionPermissionResolver } from '@app/core/model/description/description';
import { DmpBlueprint } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { DmpBlueprint, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpPersist } from '@app/core/model/dmp/dmp';
import { LanguageInfo } from '@app/core/model/language-info';
import { AuthService } from '@app/core/services/auth/auth.service';
@ -88,6 +88,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
displayFn: (item: DmpBlueprint) => item.label,
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version,
titleFn: (item: DmpBlueprint) => item.label,
valueAssign: (item: DmpBlueprint) => item.id,
};
@ -446,6 +447,32 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
});
}
canAddDescription(section: DmpBlueprintDefinitionSection ): boolean{
if(section.hasTemplates){
if (section.descriptionTemplates?.length > 0){
const descriptions = this.descriptionsInSection(section.id)
let multiplicityValidResults :boolean[] = [];
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
if (sectionDescriptionTemplate.maxMultiplicity != null){
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
else multiplicityValidResults.push(true);
}else{
multiplicityValidResults.push(true);
}
})
if(multiplicityValidResults.includes(true)) return true
else return false;
}else{
return true;
}
}else{
return false;
}
}
//
//
// Description Template

View File

@ -61,6 +61,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.descriptionTemplateGroupId)].join('.'),
[nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
@ -104,6 +105,8 @@ export class DmpEditorResolver extends BaseEditorResolver {
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.ordinal)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.hasTemplates)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateGroupId)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.minMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.maxMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.category)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.label)].join('.'),

View File

@ -34,7 +34,7 @@
</div>
<div class="col-12 title-form">
<mat-form-field class="w-100">
<app-single-auto-complete [required]="false" [formControl]="formGroup.get('blueprintId')" placeholder="{{'DMP-NEW-VERSION-DIALOG.FIELDS.BLUEPRINT-PLACEHOLDER' | translate}}" [configuration]="dmpBlueprintService.singleAutocompleteConfiguration">
<app-single-auto-complete [required]="false" [formControl]="formGroup.get('blueprintId')" placeholder="{{'DMP-NEW-VERSION-DIALOG.FIELDS.BLUEPRINT-PLACEHOLDER' | translate}}" [configuration]="singleAutocompleteBlueprintConfiguration">
</app-single-auto-complete>
</mat-form-field>
</div>

View File

@ -6,10 +6,13 @@ import { DmpService } from '@app/core/services/dmp/dmp.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseComponent } from '@common/base/base.component';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { map, takeUntil } from 'rxjs/operators';
import { DmpNewVersionDialogEditorModel } from './dmp-new-version-dialog.editor.model';
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { DmpEditorResolver } from '../dmp-editor-blueprint/dmp-editor.resolver';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { DmpBlueprint } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status';
@Component({
selector: 'app-dmp-new-version-dialog',
@ -22,6 +25,16 @@ export class NewVersionDmpDialogComponent extends BaseComponent {
editorModel: DmpNewVersionDialogEditorModel;
formGroup: UntypedFormGroup;
singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
displayFn: (item: DmpBlueprint) => item.label,
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version,
titleFn: (item: DmpBlueprint) => item.label,
valueAssign: (item: DmpBlueprint) => item.id,
};
constructor(
public dialogRef: MatDialogRef<NewVersionDmpDialogComponent>,
private dmpService: DmpService,

View File

@ -1,43 +1,45 @@
<form *ngIf="formGroup" [formGroup]="formGroup">
<div class="row d-flex flex-row">
<div mat-dialog-title class="col-auto">
{{'REFERENCE-FIELD.REFERENCE-DIALOG-EDITOR.TITLE' | translate}} {{label}}
</div>
<div class="col-auto ml-auto close-btn" (click)="close()">
<mat-icon>close</mat-icon>
</div>
</div>
<div mat-dialog-content class="row">
<div *ngFor="let field of systemFields;">
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{field}}</mat-label>
<input matInput type="text" [name]="field" [formControl]="formGroup.get(field)">
<mat-error *ngIf="formGroup.get(field).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<ng-container *ngIf="field == systemFields[0]">
<i matSuffix class="fa fa-spinner fa-spin" *ngIf="formGroup.get(field).pending && !referenceExists"></i>
<mat-icon *ngIf="!formGroup.get(field).pending && formGroup.get(field).dirty && formGroup.get(field).valid && !referenceExists" class="text-success" matSuffix>check</mat-icon>
<mat-icon *ngIf="!formGroup.get(field).pending && formGroup.get(field).dirty && formGroup.get(field).invalid || referenceExists" class="text-danger" matSuffix>clear</mat-icon>
<small *ngIf="referenceExists" class="text-danger">{{'REFERENCE-FIELD.REFERENCE-DIALOG-EDITOR.IDENTIFIER-EXISTS' | translate}}</small>
</ng-container>
</mat-form-field>
<div class="container-fluid">
<form *ngIf="formGroup" [formGroup]="formGroup">
<div class="row d-flex flex-row">
<div mat-dialog-title class="col-auto">
{{'REFERENCE-FIELD.REFERENCE-DIALOG-EDITOR.TITLE' | translate}} {{label}}
</div>
<div class="col-auto ml-auto close-btn" (click)="close()">
<mat-icon>close</mat-icon>
</div>
</div>
<ng-container *ngIf="referenceType && referenceType.definition && referenceType.definition.fields && referenceType.definition.fields.length > 0">
<div *ngFor="let field of referenceType.definition.fields;">
<div mat-dialog-content class="row">
<div *ngFor="let field of systemFields;">
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{field.label}}</mat-label>
<input matInput type="text" [name]="field.code" [formControl]="formGroup.get(field.code)">
<mat-error *ngIf="formGroup.get(field.code).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-label>{{field}}</mat-label>
<input matInput type="text" [name]="field" [formControl]="formGroup.get(field)">
<mat-error *ngIf="formGroup.get(field).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<ng-container *ngIf="field == systemFields[0]">
<i matSuffix class="fa fa-spinner fa-spin" *ngIf="formGroup.get(field).pending && !referenceExists"></i>
<mat-icon *ngIf="!formGroup.get(field).pending && formGroup.get(field).dirty && formGroup.get(field).valid && !referenceExists" class="text-success" matSuffix>check</mat-icon>
<mat-icon *ngIf="!formGroup.get(field).pending && formGroup.get(field).dirty && formGroup.get(field).invalid || referenceExists" class="text-danger" matSuffix>clear</mat-icon>
<small *ngIf="referenceExists" class="text-danger">{{'REFERENCE-FIELD.REFERENCE-DIALOG-EDITOR.IDENTIFIER-EXISTS' | translate}}</small>
</ng-container>
</mat-form-field>
</div>
</div>
</ng-container>
</div>
<div mat-dialog-actions class="row">
<div class="ml-auto col-auto"><button mat-raised-button type="button" mat-dialog-close>Cancel</button></div>
<div class="col-auto"><button mat-button color="primary" [disabled]="!isFormValid" (click)="send()" type="button">Save</button></div>
</div>
</form>
<ng-container *ngIf="referenceType && referenceType.definition && referenceType.definition.fields && referenceType.definition.fields.length > 0">
<div *ngFor="let field of referenceType.definition.fields;">
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{field.label}}</mat-label>
<input matInput type="text" [name]="field.code" [formControl]="formGroup.get(field.code)">
<mat-error *ngIf="formGroup.get(field.code).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
</ng-container>
</div>
<div mat-dialog-actions class="row">
<div class="ml-auto col-auto"><button mat-raised-button type="button" mat-dialog-close>Cancel</button></div>
<div class="col-auto"><button mat-button color="primary" [disabled]="!isFormValid" (click)="send()" type="button">Save</button></div>
</div>
</form>
</div>

View File

@ -3,7 +3,31 @@
<div class="profile">
<div class="container-fluid">
<div *ngIf="user | async as userProfile; else loading" class="user-profile">
<div class="col user-profile-title">{{'USER-DIALOG.USER-PROFILE' | translate}}</div>
<div class="row user-profile-title">
<div class="col mb-2">
{{'USER-DIALOG.USER-PROFILE' | translate}}
</div>
<div *ngIf="tenantFormGroup" class="col-auto mr-5">
<div class="row">
<div class="col">
<mat-form-field class="w-100">
<mat-label>Tenant</mat-label>
<mat-select placeholder="Tenant" [formControl]="this.tenantFormGroup.get('tenantCode')">
<ng-container *ngFor="let tenant of tenants | async">
<mat-option [value]="tenant">{{ tenant }}</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
<div class="col-12 col-lg-auto mt-1">
<button mat-mini-fab (click)="switchTenant()">
<mat-icon class="mat-mini-fab-icon">refresh</mat-icon>
</button>
</div>
</div>
</div>
</div>
<div class="row profile-content" [formGroup]="formGroup">
<div class="col">
<div class="row mb-4">

View File

@ -1,6 +1,6 @@
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { FormBuilder, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type';
@ -22,12 +22,16 @@ import { FormValidationErrorsDialogComponent } from '@common/forms/form-validati
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import * as moment from 'moment-timezone';
import { Observable, of } from 'rxjs';
import { Observable, from, of } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { AddAccountDialogComponent } from './add-account/add-account-dialog.component';
import { UserProfileEditorModel } from './user-profile-editor.model';
import { nameof } from 'ts-simple-nameof';
import { Guid } from '@common/types/guid';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { PrincipalService } from '@app/core/services/http/principal.service';
import { KeycloakService } from 'keycloak-angular';
@Component({
selector: 'app-user-profile',
@ -51,6 +55,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
errorMessages = [];
nestedCount = [];
nestedIndex = 0;
tenants: Observable<Array<string>>;
organisationsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
filterFn: this.filterOrganisations.bind(this),
@ -61,6 +66,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
};
formGroup: UntypedFormGroup;
tenantFormGroup: UntypedFormGroup;
constructor(
private userService: UserService,
private route: ActivatedRoute,
@ -75,7 +82,10 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
private dialog: MatDialog,
public enumUtils: EnumUtils,
private httpClient: HttpClient,
private matomoService: MatomoService
private matomoService: MatomoService,
private formBuilder: UntypedFormBuilder,
private keycloakService: KeycloakService,
private principalService: PrincipalService
) {
super();
this.languages = this.languageService.getAvailableLanguagesCodes();
@ -100,6 +110,9 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
}
ngOnInit() {
this.tenantFormGroup = this.formBuilder.group({
tenantCode: [this.authService.selectedTenant(), [Validators.required]]
});
this.matomoService.trackPageView('User Profile');
this.route.params
.pipe(takeUntil(this._destroyed))
@ -149,6 +162,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
.pipe(takeUntil(this._destroyed))
.subscribe(x => { if (x) { this.cultures = this._filterCulture(x); } });
// this.initializeDisabledFormGroup();
this.tenants = this.loadUserTenants();
this.unlock();
return result;
}));
@ -407,4 +422,29 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
}
}
// Switch Tenant
loadUserTenants(): Observable<Array<string>> {
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]
};
return this.principalService.myTenants({ params: params });
}
switchTenant(): void {
if (this.tenantFormGroup.valid === false) return;
const selectedTenant = this.tenantFormGroup.get('tenantCode').value;
this.formSubmit(selectedTenant);
this.loadUser();
}
formSubmit(selectedTenant: string): void {
this.authService.selectedTenant(selectedTenant);
}
loadUser(): void {
const returnUrl = '/profile';
this.authService.prepareAuthRequest(from(this.keycloakService.getToken()), {}).pipe(takeUntil(this._destroyed)).subscribe(() => this.authService.onAuthenticateSuccess(returnUrl), (error) => this.authService.onAuthenticateError(error));
}
}

View File

@ -112,6 +112,10 @@
},
"CRITERIA": {
"LIKE": "Search..."
},
"START-NEW-DMP-DIALOG": {
"UPLOAD-FILE": "Upoad File",
"REPLACE-FILE": "Replace File"
}
},
"HYBRID-LISTING": {
@ -1291,6 +1295,7 @@
"LOCKED": "Locked",
"DESCRIPTION": "Description",
"NO-TEMPLATE-MESSAGE": "If you can't find a template or if you want to create a personalized template for your institution, research community or training needs, please contact us.",
"DESCRIPTION-TEMPLATES-MAX-MULTIPLICITY": "Description Templates has reached the maximun multiplicity",
"FIELDS": {
"TITLE": "Title of Plan",
"DESCRIPTION": "Description",
@ -1304,7 +1309,8 @@
"EMAIL": "Email",
"USER": "User",
"USER-ROLE": "Role",
"SECTION": "Specific DMP Section"
"SECTION": "Specific DMP Section",
"DMP-BLUEPRINT-VERSION": "Version"
},
"ACTIONS": {
"DISCARD": "Discard",