Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring
This commit is contained in:
commit
e322b6adbc
|
@ -308,4 +308,14 @@ public class ErrorThesaurusProperties {
|
||||||
public void setImportDescriptionWithoutDmpDescriptionTemplate(ErrorDescription importDescriptionWithoutDmpDescriptionTemplate) {
|
public void setImportDescriptionWithoutDmpDescriptionTemplate(ErrorDescription importDescriptionWithoutDmpDescriptionTemplate) {
|
||||||
this.importDescriptionWithoutDmpDescriptionTemplate = importDescriptionWithoutDmpDescriptionTemplate;
|
this.importDescriptionWithoutDmpDescriptionTemplate = importDescriptionWithoutDmpDescriptionTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ErrorDescription duplicateDmpUser;
|
||||||
|
|
||||||
|
public ErrorDescription getDuplicateDmpUser() {
|
||||||
|
return duplicateDmpUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDuplicateDmpUser(ErrorDescription duplicateDmpUser) {
|
||||||
|
this.duplicateDmpUser = duplicateDmpUser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,12 +81,6 @@ public class DescriptionTemplateDeleter implements Deleter {
|
||||||
UserDescriptionTemplateDeleter deleter = this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class);
|
UserDescriptionTemplateDeleter deleter = this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class);
|
||||||
deleter.delete(items);
|
deleter.delete(items);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
logger.debug("checking related - {}", DmpDescriptionTemplateEntity.class.getSimpleName());
|
|
||||||
List<DmpDescriptionTemplateEntity> items = this.queryFactory.query(DmpDescriptionTemplateQuery.class).descriptionTemplateGroupIds(groupIds).collect();
|
|
||||||
DmpDescriptionTemplateDeleter deleter = this.deleterFactory.deleter(DmpDescriptionTemplateDeleter.class);
|
|
||||||
deleter.delete(items);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO can not delete profile if has Datasets
|
//TODO can not delete profile if has Datasets
|
||||||
|
|
||||||
|
@ -94,7 +88,6 @@ public class DescriptionTemplateDeleter implements Deleter {
|
||||||
|
|
||||||
for (DescriptionTemplateEntity item : data) {
|
for (DescriptionTemplateEntity item : data) {
|
||||||
logger.trace("deleting item {}", item.getId());
|
logger.trace("deleting item {}", item.getId());
|
||||||
if(item.getVersionStatus().equals(DescriptionTemplateVersionStatus.Current)) throw new MyApplicationException("Description is current can not deleted");
|
|
||||||
item.setIsActive(IsActive.Inactive);
|
item.setIsActive(IsActive.Inactive);
|
||||||
item.setUpdatedAt(now);
|
item.setUpdatedAt(now);
|
||||||
logger.trace("updating item");
|
logger.trace("updating item");
|
||||||
|
|
|
@ -1145,7 +1145,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
||||||
List<TagEntity> tagsEntities = this.queryFactory.query(TagQuery.class).disableTracking().descriptionTagSubQuery(descriptionTagQuery).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).collect();
|
List<TagEntity> tagsEntities = this.queryFactory.query(TagQuery.class).disableTracking().descriptionTagSubQuery(descriptionTagQuery).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).collect();
|
||||||
if (!this.conventionService.isListNullOrEmpty(tagsEntities)) xml.setTags(tagsEntities.stream().map(TagEntity::getLabel).collect(Collectors.toList()));
|
if (!this.conventionService.isListNullOrEmpty(tagsEntities)) xml.setTags(tagsEntities.stream().map(TagEntity::getLabel).collect(Collectors.toList()));
|
||||||
|
|
||||||
DescriptionTemplateEntity descriptionTemplateEntity = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(data.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).first();
|
DescriptionTemplateEntity descriptionTemplateEntity = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(data.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).first();
|
||||||
if (descriptionTemplateEntity != null) {
|
if (descriptionTemplateEntity != null) {
|
||||||
xml.setDescriptionTemplate(this.descriptionTemplateService.exportXmlEntity(descriptionTemplateEntity.getId(), true));
|
xml.setDescriptionTemplate(this.descriptionTemplateService.exportXmlEntity(descriptionTemplateEntity.getId(), true));
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,8 +572,8 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
if (previousFinalized != null){
|
if (previousFinalized != null){
|
||||||
previousFinalized.setVersionStatus(DescriptionTemplateVersionStatus.Current);
|
previousFinalized.setVersionStatus(DescriptionTemplateVersionStatus.Current);
|
||||||
this.entityManager.merge(previousFinalized);
|
this.entityManager.merge(previousFinalized);
|
||||||
|
data.setVersionStatus(DescriptionTemplateVersionStatus.NotFinalized);
|
||||||
}
|
}
|
||||||
data.setVersionStatus(DescriptionTemplateVersionStatus.NotFinalized);
|
|
||||||
this.entityManager.merge(data);
|
this.entityManager.merge(data);
|
||||||
this.entityManager.flush();
|
this.entityManager.flush();
|
||||||
}
|
}
|
||||||
|
@ -908,7 +908,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
logger.debug(new MapLogEntry("exportXml").And("id", id));
|
logger.debug(new MapLogEntry("exportXml").And("id", id));
|
||||||
|
|
||||||
if (!ignoreAuthorize) this.authorizationService.authorizeForce(Permission.ExportDescriptionTemplate);
|
if (!ignoreAuthorize) this.authorizationService.authorizeForce(Permission.ExportDescriptionTemplate);
|
||||||
DescriptionTemplateEntity data = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).isActive(IsActive.Active).first();
|
DescriptionTemplateEntity data = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).first();
|
||||||
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
DefinitionEntity definition = this.xmlHandlingService.fromXml(DefinitionEntity.class, data.getDefinition());
|
DefinitionEntity definition = this.xmlHandlingService.fromXml(DefinitionEntity.class, data.getDefinition());
|
||||||
|
@ -921,7 +921,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
||||||
logger.debug(new MapLogEntry("exportXml").And("id", id));
|
logger.debug(new MapLogEntry("exportXml").And("id", id));
|
||||||
|
|
||||||
this.authorizationService.authorizeForce(Permission.ExportDescriptionTemplate);
|
this.authorizationService.authorizeForce(Permission.ExportDescriptionTemplate);
|
||||||
DescriptionTemplateEntity data = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).isActive(IsActive.Active).first();
|
DescriptionTemplateEntity data = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).first();
|
||||||
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
String xml = this.xmlHandlingService.toXml(this.exportXmlEntity(id, false));
|
String xml = this.xmlHandlingService.toXml(this.exportXmlEntity(id, false));
|
||||||
|
|
|
@ -768,7 +768,9 @@ public class DmpServiceImpl implements DmpService {
|
||||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(dmpId)), Permission.AssignDmpUsers);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(dmpId)), Permission.AssignDmpUsers);
|
||||||
|
|
||||||
if (!disableDelete && (model == null || model.stream().noneMatch(x-> x.getUser() != null && DmpUserRole.Owner.equals(x.getRole())))) throw new MyApplicationException("At least one owner required");
|
if (!disableDelete && (model == null || model.stream().noneMatch(x-> x.getUser() != null && DmpUserRole.Owner.equals(x.getRole())))) throw new MyApplicationException("At least one owner required");
|
||||||
|
|
||||||
|
this.checkDuplicateDmpUser(model);
|
||||||
|
|
||||||
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, dmpId, true);
|
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, dmpId, true);
|
||||||
if (dmpEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (dmpEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
@ -812,6 +814,20 @@ public class DmpServiceImpl implements DmpService {
|
||||||
return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted);
|
return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkDuplicateDmpUser(List<DmpUserPersist> model){
|
||||||
|
for (DmpUserPersist user: model) {
|
||||||
|
List<DmpUserPersist> duplicateUser = null;
|
||||||
|
if (user.getUser() != null){
|
||||||
|
duplicateUser = model.stream().filter(x -> x.getUser().equals(user.getUser()) && x.getRole().equals(user.getRole()) && Objects.equals(user.getSectionId(), x.getSectionId())).collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
duplicateUser = model.stream().filter(x -> x.getEmail().equals(user.getEmail()) && x.getRole().equals(user.getRole()) && Objects.equals(user.getSectionId(), x.getSectionId())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
if (duplicateUser.size() > 1) {
|
||||||
|
throw new MyValidationException(this.errors.getDuplicateDmpUser().getCode(), this.errors.getDuplicateDmpUser().getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
|
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
|
||||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getDmpId())), Permission.AssignDmpUsers);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getDmpId())), Permission.AssignDmpUsers);
|
||||||
|
|
|
@ -37,6 +37,8 @@ public interface UserService {
|
||||||
|
|
||||||
void sendRemoveCredentialConfirmation(RemoveCredentialRequestPersist model) throws InvalidApplicationException, JAXBException;
|
void sendRemoveCredentialConfirmation(RemoveCredentialRequestPersist model) throws InvalidApplicationException, JAXBException;
|
||||||
|
|
||||||
|
boolean doesTokenBelongToLoggedInUser(String token) throws InvalidApplicationException, IOException;
|
||||||
|
|
||||||
void confirmMergeAccount(String token) throws InvalidApplicationException, IOException;
|
void confirmMergeAccount(String token) throws InvalidApplicationException, IOException;
|
||||||
|
|
||||||
void confirmRemoveCredential(String token) throws InvalidApplicationException;
|
void confirmRemoveCredential(String token) throws InvalidApplicationException;
|
||||||
|
|
|
@ -601,6 +601,12 @@ public class UserServiceImpl implements UserService {
|
||||||
return (String.format("%02d", hour) + ":" + String.format("%02d", min) + ":" + String.format("%02d", sec));
|
return (String.format("%02d", hour) + ":" + String.format("%02d", min) + ":" + String.format("%02d", sec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean doesTokenBelongToLoggedInUser(String token) throws IOException, InvalidApplicationException {
|
||||||
|
UserEntity userToBeMerge = this.getUserEntityFromToken(token);
|
||||||
|
|
||||||
|
return this.userScope.getUserIdSafe().equals(userToBeMerge.getId());
|
||||||
|
}
|
||||||
|
|
||||||
public void confirmMergeAccount(String token) throws IOException, InvalidApplicationException {
|
public void confirmMergeAccount(String token) throws IOException, InvalidApplicationException {
|
||||||
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.MergeAccount).isActive(IsActive.Active).first();
|
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.MergeAccount).isActive(IsActive.Active).first();
|
||||||
if (action == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (action == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
@ -842,4 +848,22 @@ public class UserServiceImpl implements UserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserEntity getUserEntityFromToken(String token) throws MyForbiddenException, MyNotFoundException {
|
||||||
|
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.MergeAccount).isActive(IsActive.Active).first();
|
||||||
|
if (action == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{token, ActionConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
this.checkActionState(action);
|
||||||
|
|
||||||
|
MergeAccountConfirmationEntity mergeAccountConfirmationEntity = this.xmlHandlingService.fromXmlSafe(MergeAccountConfirmationEntity.class, action.getData());
|
||||||
|
if (mergeAccountConfirmationEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{action.getId(), MergeAccountConfirmationEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
UserContactInfoEntity userContactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).values(mergeAccountConfirmationEntity.getEmail()).types(ContactInfoType.Email).first();
|
||||||
|
if (userContactInfoEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{mergeAccountConfirmationEntity.getEmail(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
UserEntity userToBeMerge = this.queryFactory.query(UserQuery.class).ids(userContactInfoEntity.getUserId()).isActive(IsActive.Active).first();
|
||||||
|
|
||||||
|
if (userToBeMerge == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{userContactInfoEntity.getUserId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
return userToBeMerge;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,6 +23,7 @@ import org.opencdmp.authorization.AuthorizationFlags;
|
||||||
import org.opencdmp.commons.enums.DmpAccessType;
|
import org.opencdmp.commons.enums.DmpAccessType;
|
||||||
import org.opencdmp.commons.enums.DmpStatus;
|
import org.opencdmp.commons.enums.DmpStatus;
|
||||||
import org.opencdmp.commons.enums.IsActive;
|
import org.opencdmp.commons.enums.IsActive;
|
||||||
|
import org.opencdmp.controllers.swagger.SwaggerHelpers;
|
||||||
import org.opencdmp.convention.ConventionService;
|
import org.opencdmp.convention.ConventionService;
|
||||||
import org.opencdmp.data.StorageFileEntity;
|
import org.opencdmp.data.StorageFileEntity;
|
||||||
import org.opencdmp.model.DescriptionValidationResult;
|
import org.opencdmp.model.DescriptionValidationResult;
|
||||||
|
@ -151,16 +152,16 @@ public class DescriptionController {
|
||||||
@PostMapping("query")
|
@PostMapping("query")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Query all descriptions",
|
summary = "Query all descriptions",
|
||||||
description = SwaggerHelpers.endpoint_query,
|
description = SwaggerHelpers.Description.endpoint_query,
|
||||||
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
description = SwaggerHelpers.endpoint_query_request_body,
|
description = SwaggerHelpers.Description.endpoint_query_request_body,
|
||||||
content = {
|
content = {
|
||||||
@Content(
|
@Content(
|
||||||
examples = {
|
examples = {
|
||||||
@ExampleObject(
|
@ExampleObject(
|
||||||
name = "Pagination and projection",
|
name = "Pagination and projection",
|
||||||
description = "Simple paginated request using a property projection list and pagination info",
|
description = "Simple paginated request using a property projection list and pagination info",
|
||||||
value = SwaggerHelpers.endpoint_query_request_body_example
|
value = SwaggerHelpers.Description.endpoint_query_request_body_example
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -184,8 +185,8 @@ public class DescriptionController {
|
||||||
@Operation(summary = "Fetch a specific description by id")
|
@Operation(summary = "Fetch a specific description by id")
|
||||||
public Description get(
|
public Description get(
|
||||||
@Parameter(name = "id", description = "The id of a description to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
@Parameter(name = "id", description = "The id of a description to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
@Parameter(name = "fieldSet", description = "This is an object containing a list of the properties you wish to include in the response, similar to the 'project' attribute on queries.", required = true) FieldSet fieldSet)
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
||||||
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
||||||
|
|
||||||
|
@ -208,7 +209,10 @@ public class DescriptionController {
|
||||||
@Operation(summary = "Create a new or update an existing description")
|
@Operation(summary = "Create a new or update an existing description")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DescriptionPersist.DescriptionPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DescriptionPersist.DescriptionPersistValidator.ValidatorName, argumentName = "model")
|
||||||
public Description persist(@RequestBody DescriptionPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException {
|
public Description persist(
|
||||||
|
@RequestBody DescriptionPersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException {
|
||||||
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
||||||
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
||||||
|
|
||||||
|
@ -226,7 +230,10 @@ public class DescriptionController {
|
||||||
@Operation(summary = "Update the status of an existing description")
|
@Operation(summary = "Update the status of an existing description")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DescriptionStatusPersist.DescriptionStatusPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DescriptionStatusPersist.DescriptionStatusPersistValidator.ValidatorName, argumentName = "model")
|
||||||
public Description persistStatus(@RequestBody DescriptionStatusPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException, InvalidApplicationException {
|
public Description persistStatus(
|
||||||
|
@RequestBody DescriptionStatusPersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException, InvalidApplicationException {
|
||||||
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
||||||
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
||||||
|
|
||||||
|
@ -275,7 +282,9 @@ public class DescriptionController {
|
||||||
@DeleteMapping("{id}")
|
@DeleteMapping("{id}")
|
||||||
@Operation(summary = "Delete a description by id")
|
@Operation(summary = "Delete a description by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
|
public void delete(
|
||||||
|
@Parameter(name = "id", description = "The id of a description to delete", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id
|
||||||
|
) throws MyForbiddenException, InvalidApplicationException, IOException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
this.descriptionService.deleteAndSave(id);
|
this.descriptionService.deleteAndSave(id);
|
||||||
|
@ -285,7 +294,10 @@ public class DescriptionController {
|
||||||
|
|
||||||
@GetMapping("{id}/export/{type}")
|
@GetMapping("{id}/export/{type}")
|
||||||
@Operation(summary = "Export a description in various formats by id")
|
@Operation(summary = "Export a description in various formats by id")
|
||||||
public ResponseEntity<byte[]> export(@PathVariable("id") UUID id, @PathVariable("type") String exportType) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
public ResponseEntity<byte[]> export(
|
||||||
|
@Parameter(name = "id", description = "The id of a description to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
|
@Parameter(name = "type", description = "The type of the export", example = "rda", required = true) @PathVariable("type") String exportType
|
||||||
|
) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||||
logger.debug(new MapLogEntry("exporting description"));
|
logger.debug(new MapLogEntry("exporting description"));
|
||||||
|
|
||||||
return this.descriptionService.export(id, exportType);
|
return this.descriptionService.export(id, exportType);
|
||||||
|
@ -295,7 +307,11 @@ public class DescriptionController {
|
||||||
@Operation(summary = "Upload a file attachment on a field that supports it")
|
@Operation(summary = "Upload a file attachment on a field that supports it")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DescriptionFieldFilePersist.PersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DescriptionFieldFilePersist.PersistValidator.ValidatorName, argumentName = "model")
|
||||||
public StorageFile uploadFieldFiles(@RequestParam("file") MultipartFile file, @RequestParam("model") DescriptionFieldFilePersist model, FieldSet fieldSet) throws IOException {
|
public StorageFile uploadFieldFiles(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam("model") DescriptionFieldFilePersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws IOException {
|
||||||
logger.debug(new MapLogEntry("uploadFieldFiles" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("uploadFieldFiles" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
||||||
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
|
||||||
|
|
||||||
|
@ -311,7 +327,10 @@ public class DescriptionController {
|
||||||
|
|
||||||
@GetMapping("{id}/field-file/{fileId}")
|
@GetMapping("{id}/field-file/{fileId}")
|
||||||
@Operation(summary = "Fetch a field file attachment as byte array")
|
@Operation(summary = "Fetch a field file attachment as byte array")
|
||||||
public ResponseEntity<ByteArrayResource> getFieldFile(@PathVariable("id") UUID id, @PathVariable("fileId") UUID fileId) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
public ResponseEntity<ByteArrayResource> getFieldFile(
|
||||||
|
@Parameter(name = "id", description = "The id of a description", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
|
@Parameter(name = "fileIid", description = "The id of the file we want to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("fileId") UUID fileId
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||||
logger.debug(new MapLogEntry("get Field File" + Description.class.getSimpleName()).And("id", id).And("fileId", fileId));
|
logger.debug(new MapLogEntry("get Field File" + Description.class.getSimpleName()).And("id", id).And("fileId", fileId));
|
||||||
|
|
||||||
StorageFileEntity storageFile = this.descriptionService.getFieldFile(id, fileId);
|
StorageFileEntity storageFile = this.descriptionService.getFieldFile(id, fileId);
|
||||||
|
@ -349,7 +368,9 @@ public class DescriptionController {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml")
|
@RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml")
|
||||||
@Operation(summary = "Export a description in xml format by id")
|
@Operation(summary = "Export a description in xml format by id")
|
||||||
public @ResponseBody ResponseEntity<byte[]> getXml(@PathVariable UUID id) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException {
|
public @ResponseBody ResponseEntity<byte[]> getXml(
|
||||||
|
@Parameter(name = "id", description = "The id of a description to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID id
|
||||||
|
) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException {
|
||||||
logger.debug(new MapLogEntry("export" + DmpBlueprint.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("export" + DmpBlueprint.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
ResponseEntity<byte[]> response = this.descriptionService.exportXml(id);
|
ResponseEntity<byte[]> response = this.descriptionService.exportXml(id);
|
||||||
|
@ -360,181 +381,4 @@ public class DescriptionController {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class SwaggerHelpers {
|
|
||||||
static final String endpoint_query =
|
|
||||||
"""
|
|
||||||
This endpoint is used to fetch all the available descriptions.<br/>
|
|
||||||
It also allows to restrict the results using a query object passed in the request body.<br/>
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_query_request_body =
|
|
||||||
"""
|
|
||||||
Let's explore the options this object gives us.
|
|
||||||
|
|
||||||
### <u>General query parameters:</u>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>page:</b>
|
|
||||||
This is an object controlling the pagination of the results. It contains two properties.
|
|
||||||
</li>
|
|
||||||
<ul>
|
|
||||||
<li><b>offset:</b>
|
|
||||||
How many records to omit.
|
|
||||||
</li>
|
|
||||||
<li><b>size:</b>
|
|
||||||
How many records to include in each page.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
For example, if we want the third page, and our pages to contain 15 elements, we would pass the following object:
|
|
||||||
|
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"offset": 30,
|
|
||||||
"size": 15
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>order:</b>
|
|
||||||
This is an object controlling the ordering of the results.
|
|
||||||
It contains a list of strings called <i>items</i> with the names of the properties to use.
|
|
||||||
<br/>If the name of the property is prefixed with a <b>'-'</b>, the ordering direction is <b>DESC</b>. Otherwise, it is <b>ASC</b>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
For example, if we wanted to order based on the field 'createdAt' in descending order, we would pass the following object:
|
|
||||||
|
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"items": [
|
|
||||||
"-createdAt"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>metadata:</b>
|
|
||||||
This is an object containing metadata for the request. There is only one available option.
|
|
||||||
<ul>
|
|
||||||
<li><b>countAll:</b>
|
|
||||||
If this is set to true, the count property included in the response will account for all the records regardless the pagination,
|
|
||||||
with all the rest of filtering options applied of course.
|
|
||||||
Otherwise, if it is set to false or not present, only the returned results will be counted.
|
|
||||||
<br/>The first option is useful for the UI clients to calculate how many result pages are available.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><b>project:</b>
|
|
||||||
This is an object controlling the data projection of the results.
|
|
||||||
It contains a list of strings called <i>fields</i> with the names of the properties to project.
|
|
||||||
<br/>You can also include properties that are deeper in the object tree by prefixing them with dots.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
### <u>Description specific query parameters:</u>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>like:</b>
|
|
||||||
If there is a like parameter present in the query, only the description entities that include the contents of the parameter either in their labels or the descriptions will be in the response.
|
|
||||||
</li>
|
|
||||||
<li><b>ids:</b>
|
|
||||||
This is a list and contains the ids we want to include in the response. <br/>If empty, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>excludedIds:</b>
|
|
||||||
This is a list and contains the ids we want to exclude from the response. <br/>If empty, no record gets excluded.
|
|
||||||
</li>
|
|
||||||
<li><b>isActive:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on if they are deleted or not.
|
|
||||||
This filter works like this. If we want to view only the active records we pass [1] and for only the deleted records we pass [0].
|
|
||||||
<br/>If not present or if we pass [0,1], every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>statuses:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on their status.
|
|
||||||
The status can be <i>Draft</i>, <i>Finalized</i> or <i>Canceled</i>. We add 0, 1 or 2 to the list respectively.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>createdAfter:</b>
|
|
||||||
This is a date and determines which records we want to include in the response, based on their creation date.
|
|
||||||
Specifically, only the records created after the given date are included.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>createdBefore:</b>
|
|
||||||
This is a date and determines which records we want to include in the response, based on their creation date.
|
|
||||||
Specifically, only the records created before the given date are included.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>finalizedAfter:</b>
|
|
||||||
This is a date and determines which records we want to include in the response, based on their finalization date.
|
|
||||||
Specifically, only the records finalized after the given date are included.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>finalizedBefore:</b>
|
|
||||||
This is a date and determines which records we want to include in the response, based on their finalization date.
|
|
||||||
Specifically, only the records finalized before the given date are included.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_query_request_body_example =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"project":{
|
|
||||||
"fields":[
|
|
||||||
"id",
|
|
||||||
"label",
|
|
||||||
"status",
|
|
||||||
"updatedAt",
|
|
||||||
"belongsToCurrentTenant",
|
|
||||||
"finalizedAt",
|
|
||||||
"descriptionTemplate.id",
|
|
||||||
"descriptionTemplate.label",
|
|
||||||
"descriptionTemplate.groupId",
|
|
||||||
"dmp.id",
|
|
||||||
"dmp.label",
|
|
||||||
"dmp.status",
|
|
||||||
"dmp.accessType",
|
|
||||||
"dmp.blueprint.id",
|
|
||||||
"dmp.blueprint.label",
|
|
||||||
"dmp.blueprint.definition.sections.id",
|
|
||||||
"dmp.blueprint.definition.sections.label",
|
|
||||||
"dmp.blueprint.definition.sections.hasTemplates",
|
|
||||||
"dmp.dmpReferences.id",
|
|
||||||
"dmp.dmpReferences.reference.id",
|
|
||||||
"dmp.dmpReferences.reference.label",
|
|
||||||
"dmp.dmpReferences.reference.type.id",
|
|
||||||
"dmp.dmpReferences.reference.reference",
|
|
||||||
"dmp.dmpReferences.isActive",
|
|
||||||
"dmpDescriptionTemplate.id",
|
|
||||||
"dmpDescriptionTemplate.dmp.id",
|
|
||||||
"dmpDescriptionTemplate.descriptionTemplateGroupId",
|
|
||||||
"dmpDescriptionTemplate.sectionId"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"page":{
|
|
||||||
"size":5,
|
|
||||||
"offset":0
|
|
||||||
},
|
|
||||||
"order":{
|
|
||||||
"items":[
|
|
||||||
"-updatedAt"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"metadata":{
|
|
||||||
"countAll":true
|
|
||||||
},
|
|
||||||
"isActive":[
|
|
||||||
1
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_get =
|
|
||||||
"""
|
|
||||||
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,12 @@ import gr.cite.tools.fieldset.FieldSet;
|
||||||
import gr.cite.tools.logging.LoggerService;
|
import gr.cite.tools.logging.LoggerService;
|
||||||
import gr.cite.tools.logging.MapLogEntry;
|
import gr.cite.tools.logging.MapLogEntry;
|
||||||
import gr.cite.tools.validation.ValidationFilterAnnotation;
|
import gr.cite.tools.validation.ValidationFilterAnnotation;
|
||||||
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.xml.bind.JAXBException;
|
import jakarta.xml.bind.JAXBException;
|
||||||
import org.opencdmp.audit.AuditableAction;
|
import org.opencdmp.audit.AuditableAction;
|
||||||
|
@ -21,6 +24,7 @@ import org.opencdmp.authorization.AuthorizationFlags;
|
||||||
import org.opencdmp.commons.enums.DmpAccessType;
|
import org.opencdmp.commons.enums.DmpAccessType;
|
||||||
import org.opencdmp.commons.enums.DmpStatus;
|
import org.opencdmp.commons.enums.DmpStatus;
|
||||||
import org.opencdmp.commons.enums.IsActive;
|
import org.opencdmp.commons.enums.IsActive;
|
||||||
|
import org.opencdmp.controllers.swagger.SwaggerHelpers;
|
||||||
import org.opencdmp.model.DescriptionsToBeFinalized;
|
import org.opencdmp.model.DescriptionsToBeFinalized;
|
||||||
import org.opencdmp.model.DmpUser;
|
import org.opencdmp.model.DmpUser;
|
||||||
import org.opencdmp.model.DmpValidationResult;
|
import org.opencdmp.model.DmpValidationResult;
|
||||||
|
@ -99,6 +103,7 @@ public class DmpController {
|
||||||
|
|
||||||
@PostMapping("public/query")
|
@PostMapping("public/query")
|
||||||
@Operation(summary = "Query public published plans")
|
@Operation(summary = "Query public published plans")
|
||||||
|
@Hidden
|
||||||
public QueryResult<PublicDmp> publicQuery(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException {
|
public QueryResult<PublicDmp> publicQuery(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException {
|
||||||
logger.debug("querying {}", Dmp.class.getSimpleName());
|
logger.debug("querying {}", Dmp.class.getSimpleName());
|
||||||
|
|
||||||
|
@ -114,6 +119,7 @@ public class DmpController {
|
||||||
|
|
||||||
@GetMapping("public/{id}")
|
@GetMapping("public/{id}")
|
||||||
@Operation(summary = "Fetch a specific public published plan by id")
|
@Operation(summary = "Fetch a specific public published plan by id")
|
||||||
|
@Hidden
|
||||||
public PublicDmp publicGet(@PathVariable("id") UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
public PublicDmp publicGet(@PathVariable("id") UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
||||||
|
|
||||||
|
@ -136,21 +142,38 @@ public class DmpController {
|
||||||
@PostMapping("query")
|
@PostMapping("query")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Query all plans",
|
summary = "Query all plans",
|
||||||
description = SwaggerHelpers.endpoint_query,
|
description = SwaggerHelpers.Dmp.endpoint_query,
|
||||||
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
description = SwaggerHelpers.endpoint_query_request_body,
|
description = SwaggerHelpers.Dmp.endpoint_query_request_body,
|
||||||
content = {
|
content = {
|
||||||
@Content(
|
@Content(
|
||||||
examples = {
|
examples = {
|
||||||
@ExampleObject(
|
@ExampleObject(
|
||||||
name = "Pagination and projection",
|
name = "Pagination and projection",
|
||||||
description = "Simple paginated request using a property projection list and pagination info",
|
description = "Simple paginated request using a property projection list and pagination info",
|
||||||
value = SwaggerHelpers.endpoint_query_request_body_example
|
value = SwaggerHelpers.Dmp.endpoint_query_request_body_example
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "OK",
|
||||||
|
responseCode = "200",
|
||||||
|
content = {
|
||||||
|
@Content(
|
||||||
|
examples = {
|
||||||
|
@ExampleObject(
|
||||||
|
name = "First page",
|
||||||
|
description = "Example with the first page of paginated results",
|
||||||
|
value = SwaggerHelpers.Dmp.endpoint_query_response_example
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
public QueryResult<Dmp> Query(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException {
|
public QueryResult<Dmp> Query(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException {
|
||||||
logger.debug("querying {}", Dmp.class.getSimpleName());
|
logger.debug("querying {}", Dmp.class.getSimpleName());
|
||||||
|
@ -166,7 +189,11 @@ public class DmpController {
|
||||||
|
|
||||||
@GetMapping("{id}")
|
@GetMapping("{id}")
|
||||||
@Operation(summary = "Fetch a specific plan by id")
|
@Operation(summary = "Fetch a specific plan by id")
|
||||||
public Dmp Get(@PathVariable("id") UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
public Dmp Get(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet,
|
||||||
|
Locale locale
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id).And("fields", fieldSet));
|
||||||
|
|
||||||
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
||||||
|
@ -188,7 +215,10 @@ public class DmpController {
|
||||||
@Operation(summary = "Create a new or update an existing plan")
|
@Operation(summary = "Create a new or update an existing plan")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DmpPersist.DmpPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DmpPersist.DmpPersistValidator.ValidatorName, argumentName = "model")
|
||||||
public Dmp Persist(@RequestBody DmpPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException {
|
public Dmp Persist(
|
||||||
|
@RequestBody DmpPersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException {
|
||||||
logger.debug(new MapLogEntry("persisting" + Dmp.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("persisting" + Dmp.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
||||||
|
|
||||||
Dmp persisted = this.dmpService.persist(model, fieldSet);
|
Dmp persisted = this.dmpService.persist(model, fieldSet);
|
||||||
|
@ -204,7 +234,9 @@ public class DmpController {
|
||||||
@DeleteMapping("{id}")
|
@DeleteMapping("{id}")
|
||||||
@Operation(summary = "Delete a plan by id")
|
@Operation(summary = "Delete a plan by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void Delete(@PathVariable("id") UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
|
public void Delete(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to delete", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id
|
||||||
|
) throws MyForbiddenException, InvalidApplicationException, IOException {
|
||||||
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("retrieving" + Dmp.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
this.dmpService.deleteAndSave(id);
|
this.dmpService.deleteAndSave(id);
|
||||||
|
@ -215,7 +247,10 @@ public class DmpController {
|
||||||
@PostMapping("finalize/{id}")
|
@PostMapping("finalize/{id}")
|
||||||
@Operation(summary = "Finalize a plan by id")
|
@Operation(summary = "Finalize a plan by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
public boolean finalize(@PathVariable("id") UUID id, @RequestBody DescriptionsToBeFinalized descriptions) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException {
|
public boolean finalize(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to finalize", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @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()));
|
logger.debug(new MapLogEntry("finalizing" + Dmp.class.getSimpleName()).And("id", id).And("descriptionIds", descriptions.getDescriptionIds()));
|
||||||
|
|
||||||
this.dmpService.finalize(id, descriptions.getDescriptionIds());
|
this.dmpService.finalize(id, descriptions.getDescriptionIds());
|
||||||
|
@ -231,7 +266,10 @@ public class DmpController {
|
||||||
@GetMapping("undo-finalize/{id}")
|
@GetMapping("undo-finalize/{id}")
|
||||||
@Operation(summary = "Undo the finalization of a plan by id (only possible if it is not already deposited)")
|
@Operation(summary = "Undo the finalization of a plan by id (only possible if it is not already deposited)")
|
||||||
@Transactional
|
@Transactional
|
||||||
public boolean undoFinalize(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException {
|
public boolean undoFinalize(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to revert the finalization", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException {
|
||||||
logger.debug(new MapLogEntry("undo-finalizing" + Dmp.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("undo-finalizing" + Dmp.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
||||||
|
@ -247,6 +285,7 @@ public class DmpController {
|
||||||
|
|
||||||
@GetMapping("validate/{id}")
|
@GetMapping("validate/{id}")
|
||||||
@Operation(summary = "Validate if a plan is ready for finalization by id")
|
@Operation(summary = "Validate if a plan is ready for finalization by id")
|
||||||
|
@Hidden
|
||||||
public DmpValidationResult validate(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException {
|
public DmpValidationResult validate(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException {
|
||||||
logger.debug(new MapLogEntry("validating" + Dmp.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("validating" + Dmp.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
|
@ -265,7 +304,10 @@ public class DmpController {
|
||||||
@Operation(summary = "Create a clone of an existing plan")
|
@Operation(summary = "Create a clone of an existing plan")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model")
|
||||||
public Dmp buildClone(@RequestBody CloneDmpPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException, InvalidApplicationException {
|
public Dmp buildClone(
|
||||||
|
@RequestBody CloneDmpPersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException, InvalidApplicationException {
|
||||||
logger.debug(new MapLogEntry("clone" + Dmp.class.getSimpleName()).And("model", model).And("fields", fieldSet));
|
logger.debug(new MapLogEntry("clone" + Dmp.class.getSimpleName()).And("model", model).And("fields", fieldSet));
|
||||||
|
|
||||||
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
this.censorFactory.censor(DmpCensor.class).censor(fieldSet, null);
|
||||||
|
@ -284,7 +326,10 @@ public class DmpController {
|
||||||
@Operation(summary = "Create a new version of an existing plan")
|
@Operation(summary = "Create a new version of an existing plan")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = NewVersionDmpPersist.NewVersionDmpPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = NewVersionDmpPersist.NewVersionDmpPersistValidator.ValidatorName, argumentName = "model")
|
||||||
public Dmp createNewVersion(@RequestBody NewVersionDmpPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, JAXBException, IOException, TransformerException, InvalidApplicationException, ParserConfigurationException {
|
public Dmp createNewVersion(
|
||||||
|
@RequestBody NewVersionDmpPersist model,
|
||||||
|
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||||
|
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, JAXBException, IOException, TransformerException, InvalidApplicationException, ParserConfigurationException {
|
||||||
logger.debug(new MapLogEntry("persisting" + NewVersionDmpPersist.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("persisting" + NewVersionDmpPersist.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
|
||||||
|
|
||||||
Dmp persisted = this.dmpService.createNewVersion(model, fieldSet);
|
Dmp persisted = this.dmpService.createNewVersion(model, fieldSet);
|
||||||
|
@ -301,6 +346,7 @@ public class DmpController {
|
||||||
@Operation(summary = "Assign users to the plan by id")
|
@Operation(summary = "Assign users to the plan by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DmpUserPersist.DmpUserPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DmpUserPersist.DmpUserPersistValidator.ValidatorName, argumentName = "model")
|
||||||
|
@Hidden
|
||||||
public QueryResult<DmpUser> assignUsers(@PathVariable("id") UUID id, @RequestBody List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
|
public QueryResult<DmpUser> assignUsers(@PathVariable("id") UUID id, @RequestBody List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
|
||||||
logger.debug(new MapLogEntry("assigning users to dmp").And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("assigning users to dmp").And("model", model).And("fieldSet", fieldSet));
|
||||||
|
|
||||||
|
@ -318,6 +364,7 @@ public class DmpController {
|
||||||
@Operation(summary = "Remove a user association with the plan")
|
@Operation(summary = "Remove a user association with the plan")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DmpUserRemovePersist.DmpUserRemovePersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DmpUserRemovePersist.DmpUserRemovePersistValidator.ValidatorName, argumentName = "model")
|
||||||
|
@Hidden
|
||||||
public QueryResult<Dmp> removeUser(@RequestBody DmpUserRemovePersist model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
|
public QueryResult<Dmp> removeUser(@RequestBody DmpUserRemovePersist model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
|
||||||
logger.debug(new MapLogEntry("remove user from dmp").And("model", model).And("fieldSet", fieldSet));
|
logger.debug(new MapLogEntry("remove user from dmp").And("model", model).And("fieldSet", fieldSet));
|
||||||
|
|
||||||
|
@ -333,7 +380,11 @@ public class DmpController {
|
||||||
|
|
||||||
@GetMapping("{id}/export/{transformerId}/{type}")
|
@GetMapping("{id}/export/{transformerId}/{type}")
|
||||||
@Operation(summary = "Export a plan in various formats by id")
|
@Operation(summary = "Export a plan in various formats by id")
|
||||||
public ResponseEntity<byte[]> export(@PathVariable("id") UUID id, @PathVariable("transformerId") String transformerId, @PathVariable("type") String exportType) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
public ResponseEntity<byte[]> export(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||||
|
@PathVariable("transformerId") String transformerId,
|
||||||
|
@PathVariable("type") String exportType
|
||||||
|
) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||||
logger.debug(new MapLogEntry("exporting dmp").And("id", id).And("transformerId", transformerId).And("exportType", exportType));
|
logger.debug(new MapLogEntry("exporting dmp").And("id", id).And("transformerId", transformerId).And("exportType", exportType));
|
||||||
|
|
||||||
ResponseEntity<byte[]> bytes = this.dmpService.export(id, transformerId, exportType);
|
ResponseEntity<byte[]> bytes = this.dmpService.export(id, transformerId, exportType);
|
||||||
|
@ -349,6 +400,7 @@ public class DmpController {
|
||||||
@Operation(summary = "Send user invitations for the plan by id")
|
@Operation(summary = "Send user invitations for the plan by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = DmpUserInvitePersist.DmpUserInvitePersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = DmpUserInvitePersist.DmpUserInvitePersistValidator.ValidatorName, argumentName = "model")
|
||||||
|
@Hidden
|
||||||
public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
|
public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
|
||||||
logger.debug(new MapLogEntry("inviting users to dmp").And("model", model));
|
logger.debug(new MapLogEntry("inviting users to dmp").And("model", model));
|
||||||
|
|
||||||
|
@ -364,6 +416,7 @@ public class DmpController {
|
||||||
@GetMapping("{id}/token/{token}/invite-accept")
|
@GetMapping("{id}/token/{token}/invite-accept")
|
||||||
@Operation(summary = "Accept an invitation token for a plan by id")
|
@Operation(summary = "Accept an invitation token for a plan by id")
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@Hidden
|
||||||
public boolean acceptInvitation(@PathVariable("id") UUID id, @PathVariable("token") String token) throws InvalidApplicationException, JAXBException, IOException {
|
public boolean acceptInvitation(@PathVariable("id") UUID id, @PathVariable("token") String token) throws InvalidApplicationException, JAXBException, IOException {
|
||||||
logger.debug(new MapLogEntry("inviting users to dmp").And("id", id));
|
logger.debug(new MapLogEntry("inviting users to dmp").And("id", id));
|
||||||
|
|
||||||
|
@ -378,7 +431,9 @@ public class DmpController {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml")
|
@RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml")
|
||||||
@Operation(summary = "Export a plan in xml format by id")
|
@Operation(summary = "Export a plan in xml format by id")
|
||||||
public @ResponseBody ResponseEntity<byte[]> getXml(@PathVariable UUID id) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException {
|
public @ResponseBody ResponseEntity<byte[]> getXml(
|
||||||
|
@Parameter(name = "id", description = "The id of a plan to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID id
|
||||||
|
) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException {
|
||||||
logger.debug(new MapLogEntry("export" + Dmp.class.getSimpleName()).And("id", id));
|
logger.debug(new MapLogEntry("export" + Dmp.class.getSimpleName()).And("id", id));
|
||||||
|
|
||||||
ResponseEntity<byte[]> response = this.dmpService.exportXml(id);
|
ResponseEntity<byte[]> response = this.dmpService.exportXml(id);
|
||||||
|
@ -392,7 +447,11 @@ public class DmpController {
|
||||||
@RequestMapping(method = RequestMethod.POST, value = "/xml/import")
|
@RequestMapping(method = RequestMethod.POST, value = "/xml/import")
|
||||||
@Operation(summary = "Import a plan from an xml file")
|
@Operation(summary = "Import a plan from an xml file")
|
||||||
@Transactional
|
@Transactional
|
||||||
public Dmp importXml(@RequestParam("file") MultipartFile file, @RequestParam("label") String label, FieldSet fields) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException, TransformerException {
|
public Dmp importXml(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam("label") String label,
|
||||||
|
@Parameter(name = "fields", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fields
|
||||||
|
) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException, TransformerException {
|
||||||
logger.debug(new MapLogEntry("import xml" + Dmp.class.getSimpleName()).And("file", file).And("label", label));
|
logger.debug(new MapLogEntry("import xml" + Dmp.class.getSimpleName()).And("file", file).And("label", label));
|
||||||
|
|
||||||
Dmp model = this.dmpService.importXml(file.getBytes(), label, fields);
|
Dmp model = this.dmpService.importXml(file.getBytes(), label, fields);
|
||||||
|
@ -407,7 +466,13 @@ public class DmpController {
|
||||||
@PostMapping("json/import")
|
@PostMapping("json/import")
|
||||||
@Operation(summary = "Import a plan from an json file")
|
@Operation(summary = "Import a plan from an json file")
|
||||||
@Transactional
|
@Transactional
|
||||||
public Dmp importJson(@RequestParam("file") MultipartFile file, @RequestParam("label") String label, @RequestParam("repositoryId") String repositoryId, @RequestParam("format") String format, FieldSet fields) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
public Dmp importJson(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam("label") String label,
|
||||||
|
@RequestParam("repositoryId") String repositoryId,
|
||||||
|
@RequestParam("format") String format,
|
||||||
|
@Parameter(name = "fields", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fields
|
||||||
|
) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||||
logger.debug(new MapLogEntry("import json" + Dmp.class.getSimpleName()).And("transformerId", repositoryId).And("file", file).And("label", label));
|
logger.debug(new MapLogEntry("import json" + Dmp.class.getSimpleName()).And("transformerId", repositoryId).And("file", file).And("label", label));
|
||||||
|
|
||||||
Dmp model = this.dmpService.importJson(file, label, repositoryId, format, fields);
|
Dmp model = this.dmpService.importJson(file, label, repositoryId, format, fields);
|
||||||
|
@ -421,171 +486,4 @@ public class DmpController {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class SwaggerHelpers {
|
|
||||||
|
|
||||||
static final String endpoint_query =
|
|
||||||
"""
|
|
||||||
This endpoint is used to fetch all the available plans.<br/>
|
|
||||||
It also allows to restrict the results using a query object passed in the request body.<br/>
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_query_request_body =
|
|
||||||
"""
|
|
||||||
Let's explore the options this object gives us.
|
|
||||||
|
|
||||||
### <u>General query parameters:</u>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>page:</b>
|
|
||||||
This is an object controlling the pagination of the results. It contains two properties.
|
|
||||||
</li>
|
|
||||||
<ul>
|
|
||||||
<li><b>offset:</b>
|
|
||||||
How many records to omit.
|
|
||||||
</li>
|
|
||||||
<li><b>size:</b>
|
|
||||||
How many records to include in each page.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
For example, if we want the third page, and our pages to contain 15 elements, we would pass the following object:
|
|
||||||
|
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"offset": 30,
|
|
||||||
"size": 15
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>order:</b>
|
|
||||||
This is an object controlling the ordering of the results.
|
|
||||||
It contains a list of strings called <i>items</i> with the names of the properties to use.
|
|
||||||
<br/>If the name of the property is prefixed with a <b>'-'</b>, the ordering direction is <b>DESC</b>. Otherwise, it is <b>ASC</b>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
For example, if we wanted to order based on the field 'createdAt' in descending order, we would pass the following object:
|
|
||||||
|
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"items": [
|
|
||||||
"-createdAt"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>metadata:</b>
|
|
||||||
This is an object containing metadata for the request. There is only one available option.
|
|
||||||
<ul>
|
|
||||||
<li><b>countAll:</b>
|
|
||||||
If this is set to true, the count property included in the response will account for all the records regardless the pagination,
|
|
||||||
with all the rest of filtering options applied of course.
|
|
||||||
Otherwise, if it is set to false or not present, only the returned results will be counted.
|
|
||||||
<br/>The first option is useful for the UI clients to calculate how many result pages are available.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><b>project:</b>
|
|
||||||
This is an object controlling the data projection of the results.
|
|
||||||
It contains a list of strings called <i>fields</i> with the names of the properties to project.
|
|
||||||
<br/>You can also include properties that are deeper in the object tree by prefixing them with dots.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
### <u>Plan specific query parameters:</u>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><b>like:</b>
|
|
||||||
If there is a like parameter present in the query, only the description entities that include the contents of the parameter either in their labels or the descriptions will be in the response.
|
|
||||||
</li>
|
|
||||||
<li><b>ids:</b>
|
|
||||||
This is a list and contains the ids we want to include in the response. <br/>If empty, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>excludedIds:</b>
|
|
||||||
This is a list and contains the ids we want to exclude from the response. <br/>If empty, no record gets excluded.
|
|
||||||
</li>
|
|
||||||
<li><b>groupIds:</b>
|
|
||||||
This is a list and contains the group ids we want the plans to have. Every plan and all its versions, have the same groupId. <br/>If empty, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>isActive:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on if they are deleted or not.
|
|
||||||
This filter works like this. If we want to view only the active records we pass [1] and for only the deleted records we pass [0].
|
|
||||||
<br/>If not present or if we pass [0,1], every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>statuses:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on their status.
|
|
||||||
The status can be <i>Draft</i> or <i>Finalized</i>. We add 0 or 1 to the list respectively.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>versionStatuses:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on their version status.
|
|
||||||
The status can be <i>Current</i>, <i>Previous</i> or <i>NotFinalized</i>. We add 0, 1 or 2 to the list respectively.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
<li><b>accessTypes:</b>
|
|
||||||
This is a list and determines which records we want to include in the response, based on their access type.
|
|
||||||
The access type can be <i>Public</i> or <i>Restricted</i>. We add 0 or 1 to the list respectively.
|
|
||||||
<br/>If not present, every record is included.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_query_request_body_example =
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"project":{
|
|
||||||
"fields":[
|
|
||||||
"id",
|
|
||||||
"label",
|
|
||||||
"description",
|
|
||||||
"status",
|
|
||||||
"accessType",
|
|
||||||
"version",
|
|
||||||
"versionStatus",
|
|
||||||
"groupId",
|
|
||||||
"updatedAt",
|
|
||||||
"belongsToCurrentTenant",
|
|
||||||
"finalizedAt",
|
|
||||||
"hash",
|
|
||||||
"descriptions.id",
|
|
||||||
"descriptions.label",
|
|
||||||
"descriptions.status",
|
|
||||||
"descriptions.descriptionTemplate.groupId",
|
|
||||||
"descriptions.isActive",
|
|
||||||
"blueprint.id",
|
|
||||||
"blueprint.label",
|
|
||||||
"blueprint.definition.sections.id"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"page":{
|
|
||||||
"size":5,
|
|
||||||
"offset":0
|
|
||||||
},
|
|
||||||
"order":{
|
|
||||||
"items":[
|
|
||||||
"-updatedAt"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"metadata":{
|
|
||||||
"countAll":true
|
|
||||||
},
|
|
||||||
"isActive":[
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"versionStatuses":[
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
static final String endpoint_ =
|
|
||||||
"""
|
|
||||||
|
|
||||||
""";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,14 @@ public class UserController {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("mine/get-permission/token/{token}")
|
||||||
|
@Transactional
|
||||||
|
public Boolean getUserTokenPermission(@PathVariable("token") String token) throws InvalidApplicationException, IOException {
|
||||||
|
logger.debug(new MapLogEntry("confirm merge account to user").And("token", token));
|
||||||
|
|
||||||
|
return this.userTypeService.doesTokenBelongToLoggedInUser(token);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("mine/remove-credential-request")
|
@PostMapping("mine/remove-credential-request")
|
||||||
@Transactional
|
@Transactional
|
||||||
@ValidationFilterAnnotation(validator = RemoveCredentialRequestPersist.RemoveCredentialRequestPersistValidator.ValidatorName, argumentName = "model")
|
@ValidationFilterAnnotation(validator = RemoveCredentialRequestPersist.RemoveCredentialRequestPersistValidator.ValidatorName, argumentName = "model")
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -97,4 +97,7 @@ error-thesaurus:
|
||||||
message: missing contact info for this user
|
message: missing contact info for this user
|
||||||
import-description-without-dmp-description-template:
|
import-description-without-dmp-description-template:
|
||||||
code: 136
|
code: 136
|
||||||
message: Error creating description without dmp description template
|
message: Error creating description without dmp description template
|
||||||
|
duplicate-dmp-user:
|
||||||
|
code: 137
|
||||||
|
message: Duplicate Dmp User not allowed
|
|
@ -958,6 +958,7 @@ permissions:
|
||||||
DeleteLock:
|
DeleteLock:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
- TenantAdmin
|
||||||
dmp:
|
dmp:
|
||||||
roles:
|
roles:
|
||||||
- Owner
|
- Owner
|
||||||
|
|
|
@ -3,12 +3,11 @@ import { RouterModule, Routes } from '@angular/router';
|
||||||
import { AppPermission } from './core/common/enum/permission.enum';
|
import { AppPermission } from './core/common/enum/permission.enum';
|
||||||
import { BreadcrumbService } from './ui/misc/breadcrumb/breadcrumb.service';
|
import { BreadcrumbService } from './ui/misc/breadcrumb/breadcrumb.service';
|
||||||
import { ReloadHelperComponent } from './ui/misc/reload-helper/reload-helper.component';
|
import { ReloadHelperComponent } from './ui/misc/reload-helper/reload-helper.component';
|
||||||
import { DepositOauth2DialogComponent } from './ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component';
|
|
||||||
|
|
||||||
const appRoutes: Routes = [
|
const appRoutes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
redirectTo:'home',
|
redirectTo: 'home',
|
||||||
pathMatch: 'full'
|
pathMatch: 'full'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -35,7 +34,7 @@ const appRoutes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'explore-descriptions',
|
path: 'explore-descriptions',
|
||||||
loadChildren: () => import('./ui/description/description.module').then(m => m.DescriptionModule),
|
loadChildren: () => import('./ui/description/description.module').then(m => m.PublicDescriptionModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -60,7 +59,7 @@ const appRoutes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'explore-plans',
|
path: 'explore-plans',
|
||||||
loadChildren: () => import('./ui/dmp/dmp.module').then(m => m.DmpModule),
|
loadChildren: () => import('./ui/dmp/dmp.module').then(m => m.PublicDmpModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -183,14 +182,6 @@ const appRoutes: Routes = [
|
||||||
title: 'GENERAL.TITLES.COOKIES-POLICY'
|
title: 'GENERAL.TITLES.COOKIES-POLICY'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// path: 'splash',
|
|
||||||
// loadChildren: () => import('./ui/splash/splash.module').then(m => m.SplashModule),
|
|
||||||
// data: {
|
|
||||||
// breadcrumb: true
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
path: 'unauthorized',
|
path: 'unauthorized',
|
||||||
loadChildren: () => import('./ui/misc/unauthorized/unauthorized.module').then(m => m.UnauthorizedModule),
|
loadChildren: () => import('./ui/misc/unauthorized/unauthorized.module').then(m => m.UnauthorizedModule),
|
||||||
|
@ -387,8 +378,22 @@ const appRoutes: Routes = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const tenantEnrichedRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 't/:tenant_code',
|
||||||
|
data: {
|
||||||
|
breadcrumb: true,
|
||||||
|
hideItem: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
...appRoutes
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...appRoutes
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(appRoutes, {})],
|
imports: [RouterModule.forRoot(tenantEnrichedRoutes, {})],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule { }
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { of as observableOf, Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { switchMap, filter, map, takeUntil } from 'rxjs/operators';
|
import { switchMap, filter, map, takeUntil } from 'rxjs/operators';
|
||||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, NavigationStart, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { AuthService, LoginStatus } from './core/services/auth/auth.service';
|
import { AuthService, LoginStatus } from './core/services/auth/auth.service';
|
||||||
import { CultureService } from './core/services/culture/culture-service';
|
import { CultureService } from './core/services/culture/culture-service';
|
||||||
|
@ -23,6 +23,7 @@ import { TenantConfigurationService } from './core/services/tenant-configuration
|
||||||
import { TenantConfigurationType } from './core/common/enum/tenant-configuration-type';
|
import { TenantConfigurationType } from './core/common/enum/tenant-configuration-type';
|
||||||
import { CssColorsTenantConfiguration, TenantConfiguration } from './core/model/tenant-configuaration/tenant-configuration';
|
import { CssColorsTenantConfiguration, TenantConfiguration } from './core/model/tenant-configuaration/tenant-configuration';
|
||||||
import { nameof } from 'ts-simple-nameof';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
import { TenantHandlingService } from './core/services/tenant/tenant-handling.service';
|
||||||
|
|
||||||
|
|
||||||
declare const gapi: any;
|
declare const gapi: any;
|
||||||
|
@ -43,7 +44,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
onlySplash = true;
|
onlySplash = true;
|
||||||
showOnlyRouterOutlet = false;
|
showOnlyRouterOutlet = false;
|
||||||
|
|
||||||
@ViewChild('sidenav') sidenav:MatSidenav;
|
@ViewChild('sidenav') sidenav: MatSidenav;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
@ -61,7 +62,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
private location: Location,
|
private location: Location,
|
||||||
private matomoService: MatomoService,
|
private matomoService: MatomoService,
|
||||||
private tenantConfigurationService: TenantConfigurationService,
|
private tenantConfigurationService: TenantConfigurationService,
|
||||||
private sidenavService: SideNavService
|
private sidenavService: SideNavService,
|
||||||
|
private tenantHandlingService: TenantHandlingService
|
||||||
) {
|
) {
|
||||||
this.initializeServices();
|
this.initializeServices();
|
||||||
this.matomoService.init();
|
this.matomoService.init();
|
||||||
|
@ -69,30 +71,30 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.sideNavSubscription = this.sidenavService.status().subscribe(isopen=>{
|
this.sideNavSubscription = this.sidenavService.status().subscribe(isopen => {
|
||||||
const hamburger = document.getElementById('hamburger');
|
const hamburger = document.getElementById('hamburger');
|
||||||
if(isopen){
|
if (isopen) {
|
||||||
//update value of hamburfer
|
//update value of hamburfer
|
||||||
if(!hamburger){//try later
|
if (!hamburger) {//try later
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const hamburger = document.getElementById('hamburger');
|
const hamburger = document.getElementById('hamburger');
|
||||||
if(hamburger){
|
if (hamburger) {
|
||||||
hamburger.classList.add('change');
|
hamburger.classList.add('change');
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}else{
|
} else {
|
||||||
hamburger.classList.add('change');
|
hamburger.classList.add('change');
|
||||||
}
|
}
|
||||||
this.sidenav.open()
|
this.sidenav.open()
|
||||||
}else{//closed
|
} else {//closed
|
||||||
if(!hamburger){//try later
|
if (!hamburger) {//try later
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const hamburger = document.getElementById('hamburger');
|
const hamburger = document.getElementById('hamburger');
|
||||||
if(hamburger){
|
if (hamburger) {
|
||||||
hamburger.classList.remove('change');
|
hamburger.classList.remove('change');
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}else{
|
} else {
|
||||||
hamburger.classList.remove('change');
|
hamburger.classList.remove('change');
|
||||||
}
|
}
|
||||||
this.sidenav.close();
|
this.sidenav.close();
|
||||||
|
@ -114,7 +116,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
if (this.location.path() === '') {
|
if (this.location.path() === '') {
|
||||||
if (!this.configurationService.useSplash) {
|
if (!this.configurationService.useSplash) {
|
||||||
this.onlySplash = false;
|
this.onlySplash = false;
|
||||||
this.router.navigate(['/reload']).then(() => this.router.navigate(['/home']));
|
this.router.navigate(['/home']);
|
||||||
} else {
|
} else {
|
||||||
this.onlySplash = true;
|
this.onlySplash = true;
|
||||||
this.router.navigate(['/reload']).then(() => this.router.navigate(['/splash']));
|
this.router.navigate(['/reload']).then(() => this.router.navigate(['/splash']));
|
||||||
|
@ -126,7 +128,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
if (!this.cookieService.check("cookiesConsent")) {
|
if (!this.cookieService.check("cookiesConsent")) {
|
||||||
// this.cookieService.set("cookiesConsent", "false", 356);
|
// this.cookieService.set("cookiesConsent", "false", 356);
|
||||||
this.cookieService.set("cookiesConsent", "false", 356,null,null,false, 'Lax');
|
this.cookieService.set("cookiesConsent", "false", 356, null, null, false, 'Lax');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,17 +173,29 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
return { title: child.snapshot.data['title'], usePrefix: usePrefix };
|
return { title: child.snapshot.data['title'], usePrefix: usePrefix };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { title: appTitle, usePrefix: true};
|
return { title: appTitle, usePrefix: true };
|
||||||
})
|
})
|
||||||
).subscribe((titleOptions: { title: string, usePrefix: boolean}) => {
|
).subscribe((titleOptions: { title: string, usePrefix: boolean }) => {
|
||||||
this.translateTitle(titleOptions.title, titleOptions.usePrefix);
|
this.translateTitle(titleOptions.title, titleOptions.usePrefix);
|
||||||
this.translate.onLangChange.subscribe(() => this.translateTitle(titleOptions.title, titleOptions.usePrefix));
|
this.translate.onLangChange.subscribe(() => this.translateTitle(titleOptions.title, titleOptions.usePrefix));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.router
|
||||||
|
.events.pipe(
|
||||||
|
filter(event => event instanceof NavigationEnd)
|
||||||
|
)
|
||||||
|
.subscribe((event: NavigationStart) => {
|
||||||
|
const enrichedUrl = this.tenantHandlingService.getUrlEnrichedWithTenantCode(event.url, this.authentication.selectedTenant() ?? 'default');
|
||||||
|
if (event.url != enrichedUrl) {
|
||||||
|
this.router.navigate([enrichedUrl]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.statusChangeSubscription = this.ccService.statusChange$.subscribe((event: NgcStatusChangeEvent) => {
|
this.statusChangeSubscription = this.ccService.statusChange$.subscribe((event: NgcStatusChangeEvent) => {
|
||||||
if (event.status == "dismiss") {
|
if (event.status == "dismiss") {
|
||||||
// this.cookieService.set("cookiesConsent", "true", 365);
|
// this.cookieService.set("cookiesConsent", "true", 365);
|
||||||
this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax');
|
this.cookieService.set("cookiesConsent", "true", 356, null, null, false, 'Lax');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -203,7 +217,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
}
|
}
|
||||||
this.ccService.destroy();
|
this.ccService.destroy();
|
||||||
this.ccService.init(this.ccService.getConfig());
|
this.ccService.init(this.ccService.getConfig());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
translateTitle(ttl: string, usePrefix: boolean) {
|
translateTitle(ttl: string, usePrefix: boolean) {
|
||||||
|
@ -226,7 +240,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.statusChangeSubscription.unsubscribe();
|
this.statusChangeSubscription.unsubscribe();
|
||||||
if(this.sideNavSubscription){
|
if (this.sideNavSubscription) {
|
||||||
this.sideNavSubscription.unsubscribe();
|
this.sideNavSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,24 +283,24 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
private loadCssColors() {
|
private loadCssColors() {
|
||||||
if (this.authentication.currentAccountIsAuthenticated() && this.authentication.selectedTenant()) {
|
if (this.authentication.currentAccountIsAuthenticated() && this.authentication.selectedTenant()) {
|
||||||
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.CssColors, [
|
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.CssColors, [
|
||||||
nameof<TenantConfiguration>(x => x.type),
|
nameof<TenantConfiguration>(x => x.type),
|
||||||
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor)].join('.'),
|
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor)].join('.'),
|
||||||
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor2)].join('.'),
|
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor2)].join('.'),
|
||||||
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor3)].join('.'),
|
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.primaryColor3)].join('.'),
|
||||||
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.secondaryColor)].join('.'),
|
[nameof<TenantConfiguration>(x => x.cssColors), nameof<CssColorsTenantConfiguration>(x => x.secondaryColor)].join('.'),
|
||||||
])
|
])
|
||||||
.pipe(map(data => data as TenantConfiguration))
|
.pipe(map(data => data as TenantConfiguration))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
data => {
|
data => {
|
||||||
if (data?.cssColors) {
|
if (data?.cssColors) {
|
||||||
if (data.cssColors.primaryColor) document.documentElement.style.setProperty(`--primary-color`, data.cssColors.primaryColor);
|
if (data.cssColors.primaryColor) document.documentElement.style.setProperty(`--primary-color`, data.cssColors.primaryColor);
|
||||||
if (data.cssColors.primaryColor2) document.documentElement.style.setProperty(`--primary-color-2`, data.cssColors.primaryColor2);
|
if (data.cssColors.primaryColor2) document.documentElement.style.setProperty(`--primary-color-2`, data.cssColors.primaryColor2);
|
||||||
if (data.cssColors.primaryColor3) document.documentElement.style.setProperty(`--primary-color-3`, data.cssColors.primaryColor3);
|
if (data.cssColors.primaryColor3) document.documentElement.style.setProperty(`--primary-color-3`, data.cssColors.primaryColor3);
|
||||||
if (data.cssColors.secondaryColor) document.documentElement.style.setProperty(`--secondary-color`, data.cssColors.secondaryColor);
|
if (data.cssColors.secondaryColor) document.documentElement.style.setProperty(`--secondary-color`, data.cssColors.secondaryColor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import { CoreAnnotationServiceModule } from 'annotation-service/services/core-se
|
||||||
import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module';
|
import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module';
|
||||||
import { DepositOauth2DialogModule } from './ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module';
|
import { DepositOauth2DialogModule } from './ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module';
|
||||||
import { AnalyticsService } from './core/services/matomo/analytics-service';
|
import { AnalyticsService } from './core/services/matomo/analytics-service';
|
||||||
|
import { TenantHandlingService } from './core/services/tenant/tenant-handling.service';
|
||||||
|
|
||||||
// AoT requires an exported function for factories
|
// AoT requires an exported function for factories
|
||||||
export function HttpLoaderFactory(languageHttpService: LanguageHttpService) {
|
export function HttpLoaderFactory(languageHttpService: LanguageHttpService) {
|
||||||
|
@ -82,7 +83,7 @@ const appearance: MatFormFieldDefaultOptions = {
|
||||||
// appearance: 'standard'
|
// appearance: 'standard'
|
||||||
};
|
};
|
||||||
|
|
||||||
export function InstallationConfigurationFactory(appConfig: ConfigurationService, keycloak: KeycloakService, authService: AuthService, languageService: LanguageService) {
|
export function InstallationConfigurationFactory(appConfig: ConfigurationService, keycloak: KeycloakService, authService: AuthService, languageService: LanguageService, tenantHandlingService:TenantHandlingService) {
|
||||||
return () => appConfig.loadConfiguration().then(() => {
|
return () => appConfig.loadConfiguration().then(() => {
|
||||||
return languageService.loadAvailableLanguages().toPromise();
|
return languageService.loadAvailableLanguages().toPromise();
|
||||||
}).then(x => keycloak.init({
|
}).then(x => keycloak.init({
|
||||||
|
@ -109,77 +110,81 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService
|
||||||
InterceptorType.UnauthorizedResponse,
|
InterceptorType.UnauthorizedResponse,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tenantCode = tenantHandlingService.extractTenantCodeFromUrlPath(window.location.pathname) ?? authService.selectedTenant() ?? 'default';
|
||||||
const tokenPromise = keycloak.getToken();
|
const tokenPromise = keycloak.getToken();
|
||||||
return authService.prepareAuthRequest(from(tokenPromise), { params }).toPromise().catch(error => authService.onAuthenticateError(error));
|
return authService.prepareAuthRequest(from(tokenPromise), tenantCode, { params }).toPromise().catch(error => authService.onAuthenticateError(error));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@NgModule({ declarations: [
|
@NgModule({
|
||||||
AppComponent,
|
declarations: [
|
||||||
ReloadHelperComponent
|
AppComponent,
|
||||||
],
|
ReloadHelperComponent
|
||||||
bootstrap: [AppComponent], imports: [BrowserModule,
|
],
|
||||||
BrowserAnimationsModule,
|
bootstrap: [AppComponent], imports: [BrowserModule,
|
||||||
KeycloakAngularModule,
|
BrowserAnimationsModule,
|
||||||
CoreServiceModule.forRoot(),
|
KeycloakAngularModule,
|
||||||
CoreAnnotationServiceModule.forRoot(),
|
CoreServiceModule.forRoot(),
|
||||||
CoreNotificationServiceModule.forRoot(),
|
CoreAnnotationServiceModule.forRoot(),
|
||||||
AppRoutingModule,
|
CoreNotificationServiceModule.forRoot(),
|
||||||
CommonUiModule,
|
AppRoutingModule,
|
||||||
TranslateModule.forRoot({
|
CommonUiModule,
|
||||||
compiler: { provide: TranslateCompiler, useClass: OpenDMPCustomTranslationCompiler },
|
TranslateModule.forRoot({
|
||||||
loader: {
|
compiler: { provide: TranslateCompiler, useClass: OpenDMPCustomTranslationCompiler },
|
||||||
provide: TranslateLoader,
|
loader: {
|
||||||
useFactory: HttpLoaderFactory,
|
provide: TranslateLoader,
|
||||||
deps: [LanguageHttpService]
|
useFactory: HttpLoaderFactory,
|
||||||
}
|
deps: [LanguageHttpService]
|
||||||
}),
|
}
|
||||||
OverlayModule,
|
}),
|
||||||
CommonHttpModule,
|
OverlayModule,
|
||||||
MatMomentDateModule,
|
CommonHttpModule,
|
||||||
LoginModule,
|
MatMomentDateModule,
|
||||||
//Ui
|
LoginModule,
|
||||||
NotificationModule,
|
//Ui
|
||||||
// BreadcrumbModule,
|
NotificationModule,
|
||||||
ReactiveFormsModule,
|
// BreadcrumbModule,
|
||||||
FormsModule,
|
ReactiveFormsModule,
|
||||||
NavbarModule,
|
FormsModule,
|
||||||
SidebarModule,
|
NavbarModule,
|
||||||
NgcCookieConsentModule.forRoot(cookieConfig),
|
SidebarModule,
|
||||||
DepositOauth2DialogModule,
|
NgcCookieConsentModule.forRoot(cookieConfig),
|
||||||
GuidedTourModule.forRoot(),
|
DepositOauth2DialogModule,
|
||||||
DragulaModule.forRoot(),
|
GuidedTourModule.forRoot(),
|
||||||
NgxMatomoModule.forRoot({
|
DragulaModule.forRoot(),
|
||||||
mode: MatomoInitializationMode.AUTO_DEFERRED,
|
NgxMatomoModule.forRoot({
|
||||||
})], providers: [
|
mode: MatomoInitializationMode.AUTO_DEFERRED,
|
||||||
ConfigurationService,
|
})], providers: [
|
||||||
{
|
ConfigurationService,
|
||||||
provide: APP_INITIALIZER,
|
{
|
||||||
useFactory: InstallationConfigurationFactory,
|
provide: APP_INITIALIZER,
|
||||||
deps: [ConfigurationService, KeycloakService, AuthService, LanguageService],
|
useFactory: InstallationConfigurationFactory,
|
||||||
multi: true
|
deps: [ConfigurationService, KeycloakService, AuthService, LanguageService, TenantHandlingService],
|
||||||
},
|
multi: true
|
||||||
{
|
},
|
||||||
provide: MAT_DATE_LOCALE,
|
{
|
||||||
deps: [CultureService],
|
provide: MAT_DATE_LOCALE,
|
||||||
useFactory: (cultureService) => cultureService.getCurrentCulture().name
|
deps: [CultureService],
|
||||||
},
|
useFactory: (cultureService) => cultureService.getCurrentCulture().name
|
||||||
{ provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
|
},
|
||||||
{ provide: DateAdapter, useClass: MomentUtcDateAdapter },
|
{ provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
|
||||||
{
|
{ provide: DateAdapter, useClass: MomentUtcDateAdapter },
|
||||||
provide: LOCALE_ID,
|
{
|
||||||
deps: [CultureService],
|
provide: LOCALE_ID,
|
||||||
useFactory: (cultureService) => cultureService.getCurrentCulture().name
|
deps: [CultureService],
|
||||||
},
|
useFactory: (cultureService) => cultureService.getCurrentCulture().name
|
||||||
{
|
},
|
||||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
{
|
||||||
useValue: appearance
|
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||||
},
|
useValue: appearance
|
||||||
Title,
|
},
|
||||||
CookieService,
|
Title,
|
||||||
MatomoService,
|
CookieService,
|
||||||
AnalyticsService,
|
MatomoService,
|
||||||
provideHttpClient(withInterceptorsFromDi())
|
AnalyticsService,
|
||||||
] })
|
provideHttpClient(withInterceptorsFromDi())
|
||||||
|
]
|
||||||
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
|
@ -34,6 +34,7 @@ export enum ResponseErrorCode {
|
||||||
DmpInactiveUser = 134,
|
DmpInactiveUser = 134,
|
||||||
DmpMissingUserContactInfo = 135,
|
DmpMissingUserContactInfo = 135,
|
||||||
ImportDescriptionWithoutDmpDescriptionTemplate = 136,
|
ImportDescriptionWithoutDmpDescriptionTemplate = 136,
|
||||||
|
DuplicateDmpUser = 137,
|
||||||
|
|
||||||
// Notification & Annotation Errors
|
// Notification & Annotation Errors
|
||||||
InvalidApiKey = 200,
|
InvalidApiKey = 200,
|
||||||
|
@ -116,6 +117,8 @@ export class ResponseErrorCodeHelper {
|
||||||
return language.instant("GENERAL.BACKEND-ERRORS.IMPORT-DESCRIPTION-WITHOUT-DMP-DESCRIPTION-TEMPLATE");
|
return language.instant("GENERAL.BACKEND-ERRORS.IMPORT-DESCRIPTION-WITHOUT-DMP-DESCRIPTION-TEMPLATE");
|
||||||
case ResponseErrorCode.InvalidApiKey:
|
case ResponseErrorCode.InvalidApiKey:
|
||||||
return language.instant("GENERAL.BACKEND-ERRORS.INVALID-API-KEY");
|
return language.instant("GENERAL.BACKEND-ERRORS.INVALID-API-KEY");
|
||||||
|
case ResponseErrorCode.DuplicateDmpUser:
|
||||||
|
return language.instant("GENERAL.BACKEND-ERRORS.DUPLICATE-DMP-USER");
|
||||||
case ResponseErrorCode.StaleApiKey:
|
case ResponseErrorCode.StaleApiKey:
|
||||||
return language.instant("GENERAL.BACKEND-ERRORS.STALE-API-KEY");
|
return language.instant("GENERAL.BACKEND-ERRORS.STALE-API-KEY");
|
||||||
case ResponseErrorCode.SensitiveInfo:
|
case ResponseErrorCode.SensitiveInfo:
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { VisibilityRulesService } from '@app/ui/description/editor/description-f
|
||||||
import { StorageFileService } from './services/storage-file/storage-file.service';
|
import { StorageFileService } from './services/storage-file/storage-file.service';
|
||||||
import { TenantConfigurationService } from './services/tenant-configuration/tenant-configuration.service';
|
import { TenantConfigurationService } from './services/tenant-configuration/tenant-configuration.service';
|
||||||
import { DefaultUserLocaleService } from './services/default-user-locale/default-user-locale.service';
|
import { DefaultUserLocaleService } from './services/default-user-locale/default-user-locale.service';
|
||||||
|
import { TenantHandlingService } from './services/tenant/tenant-handling.service';
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
// This is shared module that provides all the services. Its imported only once on the AppModule.
|
||||||
|
@ -109,7 +110,8 @@ export class CoreServiceModule {
|
||||||
PrefillingSourceService,
|
PrefillingSourceService,
|
||||||
VisibilityRulesService,
|
VisibilityRulesService,
|
||||||
TenantConfigurationService,
|
TenantConfigurationService,
|
||||||
StorageFileService
|
StorageFileService,
|
||||||
|
TenantHandlingService
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,11 +154,11 @@ export class AuthService extends BaseService {
|
||||||
public isLoggedIn(): boolean {
|
public isLoggedIn(): boolean {
|
||||||
return this.authState();
|
return this.authState();
|
||||||
}
|
}
|
||||||
public prepareAuthRequest(observable: Observable<string>, httpParams?: Object): Observable<boolean> {
|
public prepareAuthRequest(observable: Observable<string>, tenantCode: string, httpParams?: Object): Observable<boolean> {
|
||||||
return observable.pipe(
|
return observable.pipe(
|
||||||
map((x) => this.currentAuthenticationToken(x)),
|
map((x) => this.currentAuthenticationToken(x)),
|
||||||
exhaustMap(() => forkJoin([
|
exhaustMap(() => forkJoin([
|
||||||
this.accessToken ? this.ensureTenant() : of(false),
|
this.accessToken ? this.ensureTenant(tenantCode ?? this.selectedTenant() ?? 'default') : of(false),
|
||||||
this.accessToken ? this.principalService.me(httpParams) : of(null),
|
this.accessToken ? this.principalService.me(httpParams) : of(null),
|
||||||
])),
|
])),
|
||||||
map((item) => {
|
map((item) => {
|
||||||
|
@ -176,10 +176,7 @@ export class AuthService extends BaseService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ensureTenant(): Observable<string> {
|
private ensureTenant(tenantCode: string): Observable<string> {
|
||||||
if (!this.selectedTenant()) {
|
|
||||||
this.selectedTenant('default');
|
|
||||||
}
|
|
||||||
const params = new BaseHttpParams();
|
const params = new BaseHttpParams();
|
||||||
params.interceptorContext = {
|
params.interceptorContext = {
|
||||||
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]
|
excludedInterceptors: [InterceptorType.TenantHeaderInterceptor]
|
||||||
|
@ -188,10 +185,10 @@ export class AuthService extends BaseService {
|
||||||
map(
|
map(
|
||||||
(myTenants) => {
|
(myTenants) => {
|
||||||
if (myTenants) {
|
if (myTenants) {
|
||||||
if (this.selectedTenant()) {
|
if (myTenants.some(x => x.code.toLocaleLowerCase() == tenantCode.toLocaleLowerCase())) {
|
||||||
if (myTenants.findIndex(x => x.code.toLocaleLowerCase() == this.selectedTenant().toLocaleLowerCase()) < 0) {
|
this.selectedTenant(tenantCode);
|
||||||
this.selectedTenant(null);
|
} else {
|
||||||
}
|
this.selectedTenant(null);
|
||||||
}
|
}
|
||||||
if (!this.selectedTenant()) {
|
if (!this.selectedTenant()) {
|
||||||
if (myTenants.length > 0) {
|
if (myTenants.length > 0) {
|
||||||
|
@ -326,6 +323,7 @@ export class AuthService extends BaseService {
|
||||||
|
|
||||||
return this.prepareAuthRequest(
|
return this.prepareAuthRequest(
|
||||||
from(this.keycloakService.getToken()),
|
from(this.keycloakService.getToken()),
|
||||||
|
this.selectedTenant(),
|
||||||
httpParams
|
httpParams
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this._destroyed))
|
.pipe(takeUntil(this._destroyed))
|
||||||
|
|
|
@ -57,21 +57,34 @@ export class CultureService {
|
||||||
// Set angular locale based on user selection.
|
// Set angular locale based on user selection.
|
||||||
// This is a very hacky way to map cultures with angular cultures, since there is no mapping. We first try to
|
// This is a very hacky way to map cultures with angular cultures, since there is no mapping. We first try to
|
||||||
// use the culture with the specialization (ex en-US), and if not exists we import the base culture (first part).
|
// use the culture with the specialization (ex en-US), and if not exists we import the base culture (first part).
|
||||||
let locale = newCulture.name;
|
// let locale = newCulture.name;
|
||||||
import(`/node_modules/@angular/common/locales/${locale}.mjs`).catch(reason => {
|
// const base = import(
|
||||||
this.logger.error('Could not load locale: ' + locale);
|
// /* webpackExclude: /\.d\.ts$/ */
|
||||||
}).then(selectedLocale => {
|
// /* webpackMode: "lazy-once" */
|
||||||
if (selectedLocale) {
|
// /* webpackChunkName: "i18n-base" */
|
||||||
registerLocaleData(selectedLocale.default);
|
// `@angular/common/locales/${locale}.mjs`)//.then(m => m[basePkg]);
|
||||||
} else {
|
|
||||||
locale = newCulture.name.split('-')[0];
|
// const extra = import(
|
||||||
import(`/node_modules/@angular/common/locales/${locale}.mjs`).catch(reason => {
|
// /* webpackExclude: /\.d\.ts$/ */
|
||||||
this.logger.error('Could not load locale: ' + locale);
|
// /* webpackMode: "lazy-once" */
|
||||||
}).then(selectedDefaultLocale => {
|
// /* webpackChunkName: "i18n-extra" */
|
||||||
registerLocaleData(selectedDefaultLocale.default);
|
// `@angular/common/locales/extra/${locale.split('-')[0]}.mjs`)//.then(m => m[extraPkg]);
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
// import(`/node_modules/ @angular/common/locales/${locale}.mjs`).catch(reason => {
|
||||||
|
// this.logger.error('Could not load locale: ' + locale);
|
||||||
|
// }).then(selectedLocale => {
|
||||||
|
// if (selectedLocale) {
|
||||||
|
// registerLocaleData(selectedLocale.default);
|
||||||
|
// } else {
|
||||||
|
// locale = newCulture.name.split('-')[0];
|
||||||
|
// import(`/node_modules/@angular/common/locales/${locale}.mjs`).catch(reason => {
|
||||||
|
// this.logger.error('Could not load locale: ' + locale);
|
||||||
|
// }).then(selectedDefaultLocale => {
|
||||||
|
// registerLocaleData(selectedDefaultLocale.default);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
getCultureChangeObservable(): Observable<CultureInfo> {
|
getCultureChangeObservable(): Observable<CultureInfo> {
|
||||||
|
|
|
@ -148,9 +148,9 @@ export class DescriptionTemplateService {
|
||||||
//
|
//
|
||||||
// tslint:disable-next-line: member-ordering
|
// tslint:disable-next-line: member-ordering
|
||||||
descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
|
descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
|
||||||
initialItems: (data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup()).pipe(map(x => x.items)),
|
initialItems: (data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)),
|
||||||
filterFn: (searchQuery: string, data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery)).pipe(map(x => x.items)),
|
filterFn: (searchQuery: string, data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], searchQuery)).pipe(map(x => x.items)),
|
||||||
getSelectedItem: (selectedItem: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
getSelectedItem: (selectedItem: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active, IsActive.Inactive], null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
||||||
displayFn: (item: DescriptionTemplate) => item.label,
|
displayFn: (item: DescriptionTemplate) => item.label,
|
||||||
titleFn: (item: DescriptionTemplate) => item.label,
|
titleFn: (item: DescriptionTemplate) => item.label,
|
||||||
subtitleFn: (item: DescriptionTemplate) => item.description,
|
subtitleFn: (item: DescriptionTemplate) => item.description,
|
||||||
|
@ -160,9 +160,9 @@ export class DescriptionTemplateService {
|
||||||
|
|
||||||
// tslint:disable-next-line: member-ordering
|
// tslint:disable-next-line: member-ordering
|
||||||
descriptionTempalteGroupMultipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
descriptionTempalteGroupMultipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
||||||
initialItems: (excludedItems: any[], data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
|
initialItems: (excludedItems: any[], data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
|
||||||
filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)),
|
filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], searchQuery, excludedItems)).pipe(map(x => x.items)),
|
||||||
getSelectedItems: (selectedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)),
|
getSelectedItems: (selectedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active, IsActive.Inactive], null, null, selectedItems)).pipe(map(x => x.items)),
|
||||||
displayFn: (item: DescriptionTemplate) => item.label,
|
displayFn: (item: DescriptionTemplate) => item.label,
|
||||||
titleFn: (item: DescriptionTemplate) => item.label,
|
titleFn: (item: DescriptionTemplate) => item.label,
|
||||||
subtitleFn: (item: DescriptionTemplate) => item.description,
|
subtitleFn: (item: DescriptionTemplate) => item.description,
|
||||||
|
@ -170,14 +170,14 @@ export class DescriptionTemplateService {
|
||||||
popupItemActionIcon: 'visibility'
|
popupItemActionIcon: 'visibility'
|
||||||
};
|
};
|
||||||
|
|
||||||
public buildDescriptionTempalteGroupAutocompleteLookup(like?: string, excludedIds?: Guid[], groupIds?: Guid[], excludedGroupIds?: Guid[]): DescriptionTemplateLookup {
|
public buildDescriptionTempalteGroupAutocompleteLookup(isActive: IsActive[], like?: string, excludedIds?: Guid[], groupIds?: Guid[], excludedGroupIds?: Guid[]): DescriptionTemplateLookup {
|
||||||
const lookup: DescriptionTemplateLookup = new DescriptionTemplateLookup();
|
const lookup: DescriptionTemplateLookup = new DescriptionTemplateLookup();
|
||||||
lookup.page = { size: 100, offset: 0 };
|
lookup.page = { size: 100, offset: 0 };
|
||||||
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
|
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
|
||||||
if (groupIds && groupIds.length > 0) { lookup.groupIds = groupIds; }
|
if (groupIds && groupIds.length > 0) { lookup.groupIds = groupIds; }
|
||||||
if (excludedGroupIds && excludedGroupIds.length > 0) { lookup.excludedGroupIds = excludedGroupIds; }
|
if (excludedGroupIds && excludedGroupIds.length > 0) { lookup.excludedGroupIds = excludedGroupIds; }
|
||||||
|
|
||||||
lookup.isActive = [IsActive.Active];
|
lookup.isActive = isActive;
|
||||||
lookup.versionStatuses = [DescriptionTemplateVersionStatus.Current, DescriptionTemplateVersionStatus.NotFinalized];
|
lookup.versionStatuses = [DescriptionTemplateVersionStatus.Current, DescriptionTemplateVersionStatus.NotFinalized];
|
||||||
lookup.statuses = [DescriptionTemplateStatus.Finalized];
|
lookup.statuses = [DescriptionTemplateStatus.Finalized];
|
||||||
lookup.project = {
|
lookup.project = {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { DOCUMENT, LocationStrategy } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { PRIMARY_OUTLET, Router, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TenantHandlingService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(DOCUMENT) private readonly document: Document,
|
||||||
|
private readonly locationStrategy: LocationStrategy,
|
||||||
|
private readonly router: Router,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
extractTenantCodeFromUrlPath(path: string): string {
|
||||||
|
//Searches for "/t/<tenant_code>/" in a url;
|
||||||
|
const tenantRegex = new RegExp("\/t\/([^\/]+)");
|
||||||
|
const regexResult = tenantRegex.exec(path);
|
||||||
|
let tenantCode = null;
|
||||||
|
if (Array.isArray(regexResult) && regexResult.length > 0) {
|
||||||
|
tenantCode = regexResult[1];
|
||||||
|
}
|
||||||
|
return tenantCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentUrlEnrichedWithTenantCode(tenantCode: string, withOrigin: boolean) {
|
||||||
|
const path = this.getUrlEnrichedWithTenantCode(this.router.routerState.snapshot.url, tenantCode)
|
||||||
|
return withOrigin ? this.getBaseUrl() + path.substring(1) : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrlEnrichedWithTenantCode(url: string, tenantCode: string) {
|
||||||
|
|
||||||
|
const urlTree: UrlTree = this.router.parseUrl(url);
|
||||||
|
const urlSegmentGroup: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
|
||||||
|
|
||||||
|
const tenantParamIndex = urlSegments.findIndex(x => x.path == 't');
|
||||||
|
if (tenantParamIndex >= 0) {
|
||||||
|
|
||||||
|
if (tenantCode == 'default') {
|
||||||
|
urlSegments.splice(tenantParamIndex, 2);
|
||||||
|
} else {
|
||||||
|
const tenantCodeSegment = urlSegments.at(tenantParamIndex + 1);
|
||||||
|
tenantCodeSegment.path = tenantCode;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tenantCode != 'default') {
|
||||||
|
urlTree.root.children[PRIMARY_OUTLET].segments = [new UrlSegment('t', {}), new UrlSegment(tenantCode, {}), ...urlSegments];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlTree.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseUrl(): string {
|
||||||
|
return this.document.location.origin + this.locationStrategy.getBaseHref();
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,6 +117,14 @@ export class UserService {
|
||||||
catchError((error: any) => throwError(error)));
|
catchError((error: any) => throwError(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUserTokenPermission(token: Guid): Observable<boolean> {
|
||||||
|
const url = `${this.apiBase}/mine/get-permission/token/${token}`;
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get<boolean>(url).pipe(
|
||||||
|
catchError((error: any) => throwError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
confirmMergeAccount(token: Guid): Observable<boolean> {
|
confirmMergeAccount(token: Guid): Observable<boolean> {
|
||||||
const url = `${this.apiBase}/mine/confirm-merge-account/token/${token}`;
|
const url = `${this.apiBase}/mine/confirm-merge-account/token/${token}`;
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ export class DescriptionTemplatePreviewDialogComponent extends BaseComponent imp
|
||||||
}
|
}
|
||||||
|
|
||||||
buildForm() {
|
buildForm() {
|
||||||
this.formGroup = this.editorModel.buildForm(null, true);
|
this.formGroup = this.editorModel.buildForm(null, true, this.visibilityRulesService);
|
||||||
this.previewPropertiesFormGroup = this.editorModel.properties.buildForm() as UntypedFormGroup;
|
this.previewPropertiesFormGroup = this.editorModel.properties.buildForm() as UntypedFormGroup;
|
||||||
this.visibilityRulesService.setContext(this.descriptionTemplate.definition, this.previewPropertiesFormGroup);
|
this.visibilityRulesService.setContext(this.descriptionTemplate.definition, this.previewPropertiesFormGroup);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,13 @@
|
||||||
<div style="position: relative;" class="col-12" *ngIf="hasFocus" [@fade-in]>
|
<div style="position: relative;" class="col-12" *ngIf="hasFocus" [@fade-in]>
|
||||||
<div *ngIf="showDescription" class="mb-4">
|
<div *ngIf="showDescription" class="mb-4">
|
||||||
<h5 style="font-weight: bold" class="row">{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.DESCRIPTION' | translate}}</h5>
|
<h5 style="font-weight: bold" class="row">{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.DESCRIPTION' | translate}}</h5>
|
||||||
<rich-text-editor-component [form]="form.get('description')" [id]="'editor1'" [placeholder]="'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.DESCRIPTION'" [wrapperClasses]="'row'" [editable]="!viewOnly">
|
<rich-text-editor-component [form]="form.get('description')" [placeholder]="'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.DESCRIPTION'" [wrapperClasses]="'row'" [editable]="!viewOnly">
|
||||||
</rich-text-editor-component>
|
</rich-text-editor-component>
|
||||||
<mat-error *ngIf="this.form.get('description').hasError('backendError')">{{form.get('description').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="this.form.get('description').hasError('backendError')">{{form.get('description').getError('backendError').message}}</mat-error>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showExtendedDescription" class="mb-4">
|
<div *ngIf="showExtendedDescription" class="mb-4">
|
||||||
<h5 style="font-weight: bold" class="row">{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXTENDED-DESCRIPTION' | translate}}</h5>
|
<h5 style="font-weight: bold" class="row">{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXTENDED-DESCRIPTION' | translate}}</h5>
|
||||||
<rich-text-editor-component [form]="form.get('extendedDescription')" [id]="'editor2'" [placeholder]="'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXTENDED-DESCRIPTION'" [wrapperClasses]="'row'" [editable]="!viewOnly">
|
<rich-text-editor-component [form]="form.get('extendedDescription')" [placeholder]="'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXTENDED-DESCRIPTION'" [wrapperClasses]="'row'" [editable]="!viewOnly">
|
||||||
</rich-text-editor-component>
|
</rich-text-editor-component>
|
||||||
<mat-error *ngIf="this.form.get('extendedDescription').hasError('backendError')">{{form.get('extendedDescription').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="this.form.get('extendedDescription').hasError('backendError')">{{form.get('extendedDescription').getError('backendError').message}}</mat-error>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -72,9 +72,9 @@ export class DmpBlueprintEditorComponent extends BaseEditor<DmpBlueprintEditorMo
|
||||||
public dmpBlueprintExtraFieldDataTypeEnum = this.enumUtils.getEnumValues<DmpBlueprintExtraFieldDataType>(DmpBlueprintExtraFieldDataType);
|
public dmpBlueprintExtraFieldDataTypeEnum = this.enumUtils.getEnumValues<DmpBlueprintExtraFieldDataType>(DmpBlueprintExtraFieldDataType);
|
||||||
public dmpBlueprintFieldCategoryEnum = this.enumUtils.getEnumValues<DmpBlueprintFieldCategory>(DmpBlueprintFieldCategory);
|
public dmpBlueprintFieldCategoryEnum = this.enumUtils.getEnumValues<DmpBlueprintFieldCategory>(DmpBlueprintFieldCategory);
|
||||||
descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
|
descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
|
||||||
initialItems: (data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(null, null, null, this.getUsedDescriptionTemplateGroupIds())).pipe(map(x => x.items)),
|
initialItems: (data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], null, null, null, this.getUsedDescriptionTemplateGroupIds())).pipe(map(x => x.items)),
|
||||||
filterFn: (searchQuery: string, data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery, null, null, this.getUsedDescriptionTemplateGroupIds() ? this.getUsedDescriptionTemplateGroupIds() : null)).pipe(map(x => x.items)),
|
filterFn: (searchQuery: string, data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], searchQuery, null, null, this.getUsedDescriptionTemplateGroupIds() ? this.getUsedDescriptionTemplateGroupIds() : null)).pipe(map(x => x.items)),
|
||||||
getSelectedItem: (selectedItem: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
getSelectedItem: (selectedItem: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active, IsActive.Inactive], null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
||||||
displayFn: (item: DescriptionTemplate) => item.label,
|
displayFn: (item: DescriptionTemplate) => item.label,
|
||||||
titleFn: (item: DescriptionTemplate) => item.label,
|
titleFn: (item: DescriptionTemplate) => item.label,
|
||||||
subtitleFn: (item: DescriptionTemplate) => item.description,
|
subtitleFn: (item: DescriptionTemplate) => item.description,
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
<mat-icon>more_horiz</mat-icon>
|
<mat-icon>more_horiz</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #actionsMenu="matMenu">
|
<mat-menu #actionsMenu="matMenu">
|
||||||
<button mat-menu-item [routerLink]="['/dmp-blueprints/', row.id]">
|
<button *ngIf="(row.status != null && row.status === dmpBlueprintStatuses.Draft)" mat-menu-item [routerLink]="['/dmp-blueprints/', row.id]">
|
||||||
<mat-icon>edit</mat-icon>{{'DMP-BLUEPRINT-LISTING.ACTIONS.EDIT' | translate}}
|
<mat-icon>edit</mat-icon>{{'DMP-BLUEPRINT-LISTING.ACTIONS.EDIT' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="row.belongsToCurrentTenant != false && (row.status === dmpBlueprintStatuses.Finalized || row.status == null)" mat-menu-item [routerLink]="['/dmp-blueprints/new-version' , row.id]">
|
<button *ngIf="row.belongsToCurrentTenant != false && (row.status === dmpBlueprintStatuses.Finalized || row.status == null)" mat-menu-item [routerLink]="['/dmp-blueprints/new-version' , row.id]">
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { Component, Input, NgZone, OnInit } from '@angular/core';
|
import { Component, Input, NgZone, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { AuthService } from '@app/core/services/auth/auth.service';
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
import { PrincipalService } from '@app/core/services/http/principal.service';
|
import { TenantHandlingService } from '@app/core/services/tenant/tenant-handling.service';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { BaseHttpParams } from '@common/http/base-http-params';
|
|
||||||
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
|
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { from } from 'rxjs';
|
import { from } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
@ -27,7 +25,7 @@ export class LoginComponent extends BaseComponent implements OnInit {
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private principalService: PrincipalService,
|
private tenantHandlingService: TenantHandlingService,
|
||||||
private keycloakService: KeycloakService
|
private keycloakService: KeycloakService
|
||||||
) { super(); }
|
) { super(); }
|
||||||
|
|
||||||
|
@ -36,7 +34,8 @@ export class LoginComponent extends BaseComponent implements OnInit {
|
||||||
if (!this.keycloakService.isLoggedIn()) {
|
if (!this.keycloakService.isLoggedIn()) {
|
||||||
this.authService.authenticate(this.returnUrl);
|
this.authService.authenticate(this.returnUrl);
|
||||||
} else {
|
} else {
|
||||||
this.authService.prepareAuthRequest(from(this.keycloakService.getToken())).pipe(takeUntil(this._destroyed)).subscribe(
|
const tenantCode = this.tenantHandlingService.extractTenantCodeFromUrlPath(this.returnUrl) ?? this.authService.selectedTenant() ?? 'default';
|
||||||
|
this.authService.prepareAuthRequest(from(this.keycloakService.getToken()), tenantCode).pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
() => {
|
() => {
|
||||||
let returnUrL = this.returnUrl;
|
let returnUrL = this.returnUrl;
|
||||||
this.zone.run(() => this.router.navigateByUrl(returnUrL));
|
this.zone.run(() => this.router.navigateByUrl(returnUrL));
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="col merge-account-title">{{'MERGE-ACCOUNT.TITLE' | translate}}</div>
|
<div class="col merge-account-title">{{'MERGE-ACCOUNT.TITLE' | translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showForm" class="row merge-account-content">
|
<div *ngIf="showForm" class="row merge-account-content">
|
||||||
<div class="col">
|
<div *ngIf="isTokenValid" class="col">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<span>
|
<span>
|
||||||
|
@ -20,6 +20,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="!isTokenValid" class="col">
|
||||||
|
<span>{{'MERGE-ACCOUNT.MESSAGES.INVALID-TOKEN' | translate}}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #loading>
|
<ng-template #loading>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -16,6 +16,8 @@ import { takeUntil } from "rxjs/operators";
|
||||||
})
|
})
|
||||||
export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
|
isTokenValid: boolean = false;
|
||||||
|
|
||||||
private token: Guid;
|
private token: Guid;
|
||||||
|
|
||||||
get showForm(): boolean {
|
get showForm(): boolean {
|
||||||
|
@ -37,9 +39,19 @@ export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
||||||
.subscribe(params => {
|
.subscribe(params => {
|
||||||
const token = params['token']
|
const token = params['token']
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
this.token = token;
|
this.userService.getUserTokenPermission(token)
|
||||||
|
.subscribe(result => {
|
||||||
|
this.isTokenValid = result
|
||||||
|
this.token = token;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.isTokenValid = false;
|
||||||
|
this.token = Guid.createEmpty();
|
||||||
|
this.onCallbackError(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onConfirm(): void {
|
onConfirm(): void {
|
||||||
|
@ -64,7 +76,7 @@ export class MergeEmailConfirmation extends BaseComponent implements OnInit {
|
||||||
onCallbackError(errorResponse: HttpErrorResponse) {
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
const errorOverrides = new Map<number, string>();
|
const errorOverrides = new Map<number, string>();
|
||||||
errorOverrides.set(302, this.language.instant('EMAIL-CONFIRMATION.EMAIL-FOUND'));
|
errorOverrides.set(302, this.language.instant('EMAIL-CONFIRMATION.EMAIL-FOUND'));
|
||||||
errorOverrides.set(-1, this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'));
|
errorOverrides.set(403, this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'));
|
||||||
this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides, SnackBarNotificationLevel.Error)
|
this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides, SnackBarNotificationLevel.Error)
|
||||||
|
|
||||||
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { AuthService } from '@app/core/services/auth/auth.service';
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
|
||||||
|
@ -11,7 +10,7 @@ export class LogoutComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private keycloak: KeycloakService,
|
private keycloak: KeycloakService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.authService.clear();
|
this.authService.clear();
|
||||||
|
@ -19,9 +18,5 @@ export class LogoutComponent implements OnInit {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
// this.router.navigate(['./'], { replaceUrl: true });
|
// this.router.navigate(['./'], { replaceUrl: true });
|
||||||
});
|
});
|
||||||
// this.tokenService.logout(() => {
|
|
||||||
// localStorage.clear();
|
|
||||||
// this.router.navigate(["./"], { replaceUrl: true });
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormattingModule } from '@app/core/formatting.module';
|
import { FormattingModule } from '@app/core/formatting.module';
|
||||||
import { DescriptionRoutingModule } from '@app/ui/description/description.routing';
|
import { DescriptionRoutingModule, PublicDescriptionRoutingModule } from '@app/ui/description/description.routing';
|
||||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
|
||||||
|
@ -17,3 +17,17 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class DescriptionModule { }
|
export class DescriptionModule { }
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonUiModule,
|
||||||
|
CommonFormsModule,
|
||||||
|
FormattingModule,
|
||||||
|
PublicDescriptionRoutingModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class PublicDescriptionModule { }
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { AuthGuard } from '@app/core/auth-guard.service';
|
||||||
import { BreadcrumbService } from '../misc/breadcrumb/breadcrumb.service';
|
import { BreadcrumbService } from '../misc/breadcrumb/breadcrumb.service';
|
||||||
// import { DescriptionWizardComponent } from './description-wizard/description-wizard.component';
|
|
||||||
// import { DescriptionOverviewComponent } from './overview/description-overview.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'overview',
|
path: 'overview',
|
||||||
loadChildren: () => import('./overview/description-overview.module').then(m => m.DescriptionOverviewModule),
|
loadChildren: () => import('./overview/description-overview.module').then(m => m.DescriptionOverviewModule),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -18,6 +18,28 @@ const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
loadChildren: () => import('./editor/description-editor.module').then(m => m.DescriptionEditorModule),
|
loadChildren: () => import('./editor/description-editor.module').then(m => m.DescriptionEditorModule),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
data: {
|
||||||
|
breadcrumb: true,
|
||||||
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
hideNavigationItem: true
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
loadChildren: () => import('./listing/description-listing.module').then(m => m.DescriptionListingModule),
|
||||||
|
data: {
|
||||||
|
breadcrumb: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const publicRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'overview',
|
||||||
|
loadChildren: () => import('./overview/description-overview.module').then(m => m.DescriptionOverviewModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -40,3 +62,10 @@ const routes: Routes = [
|
||||||
providers: []
|
providers: []
|
||||||
})
|
})
|
||||||
export class DescriptionRoutingModule { }
|
export class DescriptionRoutingModule { }
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(publicRoutes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
export class PublicDescriptionRoutingModule { }
|
||||||
|
|
|
@ -220,7 +220,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
permissionPerSection => {
|
permissionPerSection => {
|
||||||
this.canEdit = permissionPerSection && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()] && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()].some(x => x === AppPermission.EditDescription);
|
this.canEdit = permissionPerSection && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()] && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()].some(x => x === AppPermission.EditDescription);
|
||||||
this.canReview = permissionPerSection && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()] && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()].some(x => x === AppPermission.ReviewDescription);
|
this.canReview = permissionPerSection && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()] && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()].some(x => x === AppPermission.ReviewDescription);
|
||||||
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.canEdit);
|
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.canEdit, this.visibilityRulesService);
|
||||||
if (this.item.descriptionTemplate?.definition) this.visibilityRulesService.setContext(this.item.descriptionTemplate.definition, this.formGroup.get('properties'));
|
if (this.item.descriptionTemplate?.definition) this.visibilityRulesService.setContext(this.item.descriptionTemplate.definition, this.formGroup.get('properties'));
|
||||||
if (this.item.descriptionTemplate?.definition) this.pageToFieldSetMap = this.mapPageToFieldSet(this.item.descriptionTemplate);;
|
if (this.item.descriptionTemplate?.definition) this.pageToFieldSetMap = this.mapPageToFieldSet(this.item.descriptionTemplate);;
|
||||||
|
|
||||||
|
@ -681,7 +681,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
|
|
||||||
this.initialTemplateId = descriptionTemplateId.toString();
|
this.initialTemplateId = descriptionTemplateId.toString();
|
||||||
this.editorModel.properties = new DescriptionPropertyDefinitionEditorModel(this.editorModel.validationErrorModel).fromModel(null, descriptionTemplate, null);
|
this.editorModel.properties = new DescriptionPropertyDefinitionEditorModel(this.editorModel.validationErrorModel).fromModel(null, descriptionTemplate, null);
|
||||||
this.formGroup.setControl('properties', this.editorModel.buildProperties());
|
this.formGroup.setControl('properties', this.editorModel.buildProperties(this.visibilityRulesService));
|
||||||
this.item.descriptionTemplate = descriptionTemplate;
|
this.item.descriptionTemplate = descriptionTemplate;
|
||||||
|
|
||||||
const sectionId = this.item.dmpDescriptionTemplate.sectionId;
|
const sectionId = this.item.dmpDescriptionTemplate.sectionId;
|
||||||
|
@ -755,6 +755,8 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
||||||
finalize() {
|
finalize() {
|
||||||
this.formService.removeAllBackEndErrors(this.formGroup);
|
this.formService.removeAllBackEndErrors(this.formGroup);
|
||||||
this.formService.touchAllFormFields(this.formGroup);
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
|
||||||
this.tocValidationService.validateForm();
|
this.tocValidationService.validateForm();
|
||||||
if (!this.isFormValid()) {
|
if (!this.isFormValid()) {
|
||||||
this.dialog.open(FormValidationErrorsDialogComponent, {
|
this.dialog.open(FormValidationErrorsDialogComponent, {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
import { DescriptionStatus } from "@app/core/common/enum/description-status";
|
import { DescriptionStatus } from "@app/core/common/enum/description-status";
|
||||||
import { DescriptionTemplateFieldType } from "@app/core/common/enum/description-template-field-type";
|
import { DescriptionTemplateFieldType } from "@app/core/common/enum/description-template-field-type";
|
||||||
|
import { DescriptionTemplateFieldValidationType } from "@app/core/common/enum/description-template-field-validation-type";
|
||||||
import { IsActive } from "@app/core/common/enum/is-active.enum";
|
import { IsActive } from "@app/core/common/enum/is-active.enum";
|
||||||
import { DescriptionTemplate, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateSection } from "@app/core/model/description-template/description-template";
|
import { DescriptionTemplate, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateSection } from "@app/core/model/description-template/description-template";
|
||||||
import { Description, DescriptionExternalIdentifier, DescriptionExternalIdentifierPersist, DescriptionField, DescriptionFieldPersist, DescriptionPersist, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionPropertyDefinitionFieldSetItemPersist, DescriptionPropertyDefinitionFieldSetPersist, DescriptionPropertyDefinitionPersist, DescriptionReference, DescriptionReferencePersist } from "@app/core/model/description/description";
|
import { Description, DescriptionExternalIdentifier, DescriptionExternalIdentifierPersist, DescriptionField, DescriptionFieldPersist, DescriptionPersist, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionPropertyDefinitionFieldSetItemPersist, DescriptionPropertyDefinitionFieldSetPersist, DescriptionPropertyDefinitionPersist, DescriptionReference, DescriptionReferencePersist } from "@app/core/model/description/description";
|
||||||
import { ReferencePersist } from "@app/core/model/reference/reference";
|
import { ReferencePersist } from "@app/core/model/reference/reference";
|
||||||
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
||||||
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
|
import { BackendErrorValidator, MinMaxValidator, RequiredWithVisibilityRulesValidator, UrlValidator } from '@common/forms/validation/custom-validator';
|
||||||
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
||||||
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
|
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
|
||||||
import { Guid } from "@common/types/guid";
|
import { Guid } from "@common/types/guid";
|
||||||
|
import { VisibilityRulesService } from "./description-form/visibility-rules/visibility-rules.service";
|
||||||
|
|
||||||
export class DescriptionEditorModel extends BaseEditorModel implements DescriptionPersist {
|
export class DescriptionEditorModel extends BaseEditorModel implements DescriptionPersist {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -42,7 +44,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
buildForm(context: ValidationContext = null, disabled: boolean = false, visibilityRulesService: VisibilityRulesService): UntypedFormGroup {
|
||||||
if (context == null) { context = this.createValidationContext(); }
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
return this.formBuilder.group({
|
return this.formBuilder.group({
|
||||||
|
@ -54,14 +56,15 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
|
||||||
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
|
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
|
||||||
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
|
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
|
||||||
tags: [{ value: this.tags, disabled: disabled }, context.getValidation('tags').validators],
|
tags: [{ value: this.tags, disabled: disabled }, context.getValidation('tags').validators],
|
||||||
properties: this.buildProperties(),
|
properties: this.buildProperties(visibilityRulesService),
|
||||||
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
|
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
buildProperties() {
|
buildProperties(visibilityRulesService: VisibilityRulesService) {
|
||||||
return this.properties.buildForm({
|
return this.properties.buildForm({
|
||||||
rootPath: `properties.`
|
rootPath: `properties.`,
|
||||||
|
visibilityRulesService: visibilityRulesService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,18 +85,37 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
|
||||||
return baseContext;
|
return baseContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
static reApplyPropertiesValidators(params: {
|
static getFieldValueControlName(fieldType: DescriptionTemplateFieldType, multipleSelect: boolean): string {
|
||||||
formGroup: UntypedFormGroup,
|
switch (fieldType) {
|
||||||
validationErrorModel: ValidationErrorModel,
|
case DescriptionTemplateFieldType.FREE_TEXT:
|
||||||
}): void {
|
case DescriptionTemplateFieldType.TEXT_AREA:
|
||||||
|
case DescriptionTemplateFieldType.UPLOAD:
|
||||||
const { formGroup, validationErrorModel } = params;
|
case DescriptionTemplateFieldType.RICH_TEXT_AREA:
|
||||||
const control = formGroup?.get('properties');
|
case DescriptionTemplateFieldType.RADIO_BOX:
|
||||||
DescriptionPropertyDefinitionEditorModel.reapplyValidators({
|
return 'textValue';
|
||||||
formGroup: control.get('fieldSets') as UntypedFormGroup,
|
case DescriptionTemplateFieldType.DATASET_IDENTIFIER:
|
||||||
rootPath: `properties.`,
|
case DescriptionTemplateFieldType.VALIDATION:
|
||||||
validationErrorModel: validationErrorModel
|
return 'externalIdentifier';
|
||||||
});
|
case DescriptionTemplateFieldType.DATE_PICKER:
|
||||||
|
return 'dateValue';
|
||||||
|
case DescriptionTemplateFieldType.CHECK_BOX:
|
||||||
|
case DescriptionTemplateFieldType.BOOLEAN_DECISION:
|
||||||
|
return 'booleanValue';
|
||||||
|
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DESCRIPTIONS:
|
||||||
|
if (multipleSelect) return 'textListValue';
|
||||||
|
else return 'textValue';
|
||||||
|
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DMPS:
|
||||||
|
if (multipleSelect) return 'textListValue';
|
||||||
|
else return 'textValue';
|
||||||
|
case DescriptionTemplateFieldType.REFERENCE_TYPES:
|
||||||
|
if (multipleSelect) return 'references';
|
||||||
|
else return 'reference';
|
||||||
|
case DescriptionTemplateFieldType.SELECT:
|
||||||
|
if (multipleSelect) return 'textListValue';
|
||||||
|
else return 'textValue';
|
||||||
|
case DescriptionTemplateFieldType.TAGS:
|
||||||
|
return 'tags';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +135,8 @@ export class DescriptionPropertyDefinitionEditorModel implements DescriptionProp
|
||||||
buildForm(params?: {
|
buildForm(params?: {
|
||||||
context?: ValidationContext,
|
context?: ValidationContext,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
rootPath?: string
|
rootPath?: string,
|
||||||
|
visibilityRulesService: VisibilityRulesService
|
||||||
}): UntypedFormGroup {
|
}): UntypedFormGroup {
|
||||||
let { context = null, disabled = false, rootPath } = params ?? {}
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
|
@ -128,7 +151,8 @@ export class DescriptionPropertyDefinitionEditorModel implements DescriptionProp
|
||||||
const fieldSetsFormGroup = this.formBuilder.group({});
|
const fieldSetsFormGroup = this.formBuilder.group({});
|
||||||
if (this.fieldSets.size > 0) {
|
if (this.fieldSets.size > 0) {
|
||||||
this.fieldSets.forEach((value, key) => fieldSetsFormGroup.addControl(key.toString(), value.buildForm({
|
this.fieldSets.forEach((value, key) => fieldSetsFormGroup.addControl(key.toString(), value.buildForm({
|
||||||
rootPath: `${rootPath}fieldSets[${key}].`
|
rootPath: `${rootPath}fieldSets[${key}].`,
|
||||||
|
visibilityRulesService: params.visibilityRulesService
|
||||||
})), context.getValidation('fieldSets'));
|
})), context.getValidation('fieldSets'));
|
||||||
formGroup.addControl('fieldSets', fieldSetsFormGroup);
|
formGroup.addControl('fieldSets', fieldSetsFormGroup);
|
||||||
}
|
}
|
||||||
|
@ -149,25 +173,6 @@ export class DescriptionPropertyDefinitionEditorModel implements DescriptionProp
|
||||||
return baseContext;
|
return baseContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
static reapplyValidators(params: {
|
|
||||||
formGroup: UntypedFormGroup,
|
|
||||||
validationErrorModel: ValidationErrorModel,
|
|
||||||
rootPath: string
|
|
||||||
}): void {
|
|
||||||
|
|
||||||
const { formGroup, rootPath, validationErrorModel } = params;
|
|
||||||
|
|
||||||
const keys = Object.keys(formGroup.value as Object);
|
|
||||||
keys.forEach((key) => {
|
|
||||||
const formArray = formGroup?.get(key);
|
|
||||||
DescriptionPropertyDefinitionFieldSetEditorModel.reapplyValidators({
|
|
||||||
formArray: formArray.get('items') as UntypedFormArray,
|
|
||||||
rootPath: `${rootPath}fieldSets[${key}].`,
|
|
||||||
validationErrorModel: validationErrorModel
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateProperties(item: DescriptionPropertyDefinition, descriptionTemplate: DescriptionTemplate, descriptionReferences: DescriptionReference[]): Map<string, DescriptionPropertyDefinitionFieldSetEditorModel> {
|
private calculateProperties(item: DescriptionPropertyDefinition, descriptionTemplate: DescriptionTemplate, descriptionReferences: DescriptionReference[]): Map<string, DescriptionPropertyDefinitionFieldSetEditorModel> {
|
||||||
let result: Map<string, DescriptionPropertyDefinitionFieldSetEditorModel> = new Map<string, DescriptionPropertyDefinitionFieldSetEditorModel>();
|
let result: Map<string, DescriptionPropertyDefinitionFieldSetEditorModel> = new Map<string, DescriptionPropertyDefinitionFieldSetEditorModel>();
|
||||||
if (descriptionTemplate) (
|
if (descriptionTemplate) (
|
||||||
|
@ -265,11 +270,14 @@ export class DescriptionPropertyDefinitionFieldSetEditorModel implements Descrip
|
||||||
items?: DescriptionPropertyDefinitionFieldSetItemEditorModel[] = [];
|
items?: DescriptionPropertyDefinitionFieldSetItemEditorModel[] = [];
|
||||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
fieldSetDefinition: DescriptionTemplateFieldSet;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public fromModel(item: DescriptionPropertyDefinitionFieldSet, descriptionReferences: DescriptionReference[], definitionFieldSet: DescriptionTemplateFieldSet): DescriptionPropertyDefinitionFieldSetEditorModel {
|
public fromModel(item: DescriptionPropertyDefinitionFieldSet, descriptionReferences: DescriptionReference[], definitionFieldSet: DescriptionTemplateFieldSet): DescriptionPropertyDefinitionFieldSetEditorModel {
|
||||||
|
this.fieldSetDefinition = definitionFieldSet;
|
||||||
if (item) {
|
if (item) {
|
||||||
if (item.items) { item.items.map(x => this.items.push(new DescriptionPropertyDefinitionFieldSetItemEditorModel(this.validationErrorModel).fromModel(x, descriptionReferences, definitionFieldSet))); }
|
if (item.items) { item.items.map(x => this.items.push(new DescriptionPropertyDefinitionFieldSetItemEditorModel(this.validationErrorModel).fromModel(x, descriptionReferences, definitionFieldSet))); }
|
||||||
}
|
}
|
||||||
|
@ -279,13 +287,15 @@ export class DescriptionPropertyDefinitionFieldSetEditorModel implements Descrip
|
||||||
buildForm(params?: {
|
buildForm(params?: {
|
||||||
context?: ValidationContext,
|
context?: ValidationContext,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
rootPath?: string
|
rootPath?: string,
|
||||||
|
visibilityRulesService: VisibilityRulesService
|
||||||
}): UntypedFormGroup {
|
}): UntypedFormGroup {
|
||||||
let { context = null, disabled = false, rootPath } = params ?? {}
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = DescriptionPropertyDefinitionFieldSetEditorModel.createValidationContext({
|
context = DescriptionPropertyDefinitionFieldSetEditorModel.createValidationContext({
|
||||||
validationErrorModel: this.validationErrorModel,
|
validationErrorModel: this.validationErrorModel,
|
||||||
rootPath
|
rootPath,
|
||||||
|
fieldSetDefinition: this.fieldSetDefinition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +303,8 @@ export class DescriptionPropertyDefinitionFieldSetEditorModel implements Descrip
|
||||||
items: this.formBuilder.array(
|
items: this.formBuilder.array(
|
||||||
(this.items ?? []).map(
|
(this.items ?? []).map(
|
||||||
(item, index) => item.buildForm({
|
(item, index) => item.buildForm({
|
||||||
rootPath: `${rootPath}items[${index}].`
|
rootPath: `${rootPath}items[${index}].`,
|
||||||
|
visibilityRulesService: params.visibilityRulesService
|
||||||
})
|
})
|
||||||
), context.getValidation('items').validators
|
), context.getValidation('items').validators
|
||||||
)
|
)
|
||||||
|
@ -302,14 +313,25 @@ export class DescriptionPropertyDefinitionFieldSetEditorModel implements Descrip
|
||||||
|
|
||||||
static createValidationContext(params: {
|
static createValidationContext(params: {
|
||||||
rootPath?: string,
|
rootPath?: string,
|
||||||
validationErrorModel: ValidationErrorModel
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
fieldSetDefinition: DescriptionTemplateFieldSet
|
||||||
}): ValidationContext {
|
}): ValidationContext {
|
||||||
const { rootPath = '', validationErrorModel } = params;
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
const baseContext: ValidationContext = new ValidationContext();
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
baseValidationArray.push({ key: 'items', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}items`)] });
|
const validators = [];
|
||||||
|
validators.push(BackendErrorValidator(validationErrorModel, `${rootPath}items`));
|
||||||
|
|
||||||
|
if (params.fieldSetDefinition?.multiplicity?.min >= 0 && params.fieldSetDefinition?.multiplicity?.max >= 0) {
|
||||||
|
validators.push(MinMaxValidator(params.fieldSetDefinition.multiplicity.min, params.fieldSetDefinition.multiplicity.min));
|
||||||
|
} else if (params.fieldSetDefinition?.multiplicity?.min >= 0) {
|
||||||
|
validators.push(Validators.min(params.fieldSetDefinition.multiplicity.min));
|
||||||
|
}
|
||||||
|
else if (params.fieldSetDefinition?.multiplicity?.max >= 0) {
|
||||||
|
validators.push(Validators.max(params.fieldSetDefinition.multiplicity.max));
|
||||||
|
}
|
||||||
|
baseValidationArray.push({ key: 'items', validators: validators });
|
||||||
baseContext.validation = baseValidationArray;
|
baseContext.validation = baseValidationArray;
|
||||||
return baseContext;
|
return baseContext;
|
||||||
}
|
}
|
||||||
|
@ -317,14 +339,18 @@ export class DescriptionPropertyDefinitionFieldSetEditorModel implements Descrip
|
||||||
static reapplyValidators(params: {
|
static reapplyValidators(params: {
|
||||||
formArray: UntypedFormArray,
|
formArray: UntypedFormArray,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
rootPath: string
|
rootPath: string,
|
||||||
|
fieldSetDefinition: DescriptionTemplateFieldSet,
|
||||||
|
visibilityRulesService: VisibilityRulesService
|
||||||
}): void {
|
}): void {
|
||||||
const { validationErrorModel, rootPath, formArray } = params;
|
const { validationErrorModel, rootPath, formArray, fieldSetDefinition } = params;
|
||||||
formArray?.controls?.forEach(
|
formArray?.controls?.forEach(
|
||||||
(control, index) => DescriptionPropertyDefinitionFieldSetItemEditorModel.reapplyValidators({
|
(control, index) => DescriptionPropertyDefinitionFieldSetItemEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
rootPath: `${rootPath}items[${index}].`,
|
rootPath: `${rootPath}items[${index}].`,
|
||||||
validationErrorModel: validationErrorModel
|
validationErrorModel: validationErrorModel,
|
||||||
|
fieldSetDefinition: fieldSetDefinition,
|
||||||
|
visibilityRulesService: params.visibilityRulesService
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -359,7 +385,8 @@ export class DescriptionPropertyDefinitionFieldSetItemEditorModel implements Des
|
||||||
buildForm(params?: {
|
buildForm(params?: {
|
||||||
context?: ValidationContext,
|
context?: ValidationContext,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
rootPath?: string
|
rootPath?: string,
|
||||||
|
visibilityRulesService: VisibilityRulesService
|
||||||
}): UntypedFormGroup {
|
}): UntypedFormGroup {
|
||||||
let { context = null, disabled = false, rootPath } = params ?? {}
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
|
@ -376,7 +403,9 @@ export class DescriptionPropertyDefinitionFieldSetItemEditorModel implements Des
|
||||||
|
|
||||||
const fieldsFormGroup = this.formBuilder.group({});
|
const fieldsFormGroup = this.formBuilder.group({});
|
||||||
this.fields.forEach((value, key) => fieldsFormGroup.addControl(key.toString(), value.buildForm({
|
this.fields.forEach((value, key) => fieldsFormGroup.addControl(key.toString(), value.buildForm({
|
||||||
rootPath: `${rootPath}fields[${key}].`
|
rootPath: `${rootPath}fields[${key}].`,
|
||||||
|
visibilityRulesService: params.visibilityRulesService,
|
||||||
|
visibilityRulesKey: key + '_' + formGroup.get('ordinal').value
|
||||||
})), context.getValidation('fields')
|
})), context.getValidation('fields')
|
||||||
)
|
)
|
||||||
formGroup.addControl('fields', fieldsFormGroup);
|
formGroup.addControl('fields', fieldsFormGroup);
|
||||||
|
@ -403,10 +432,12 @@ export class DescriptionPropertyDefinitionFieldSetItemEditorModel implements Des
|
||||||
static reapplyValidators(params: {
|
static reapplyValidators(params: {
|
||||||
formGroup: UntypedFormGroup,
|
formGroup: UntypedFormGroup,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
rootPath: string
|
rootPath: string,
|
||||||
|
fieldSetDefinition: DescriptionTemplateFieldSet,
|
||||||
|
visibilityRulesService: VisibilityRulesService
|
||||||
}): void {
|
}): void {
|
||||||
|
|
||||||
const { formGroup, rootPath, validationErrorModel } = params;
|
const { formGroup, rootPath, validationErrorModel, fieldSetDefinition } = params;
|
||||||
const context = DescriptionPropertyDefinitionFieldSetItemEditorModel.createValidationContext({
|
const context = DescriptionPropertyDefinitionFieldSetItemEditorModel.createValidationContext({
|
||||||
rootPath,
|
rootPath,
|
||||||
validationErrorModel
|
validationErrorModel
|
||||||
|
@ -419,7 +450,10 @@ export class DescriptionPropertyDefinitionFieldSetItemEditorModel implements Des
|
||||||
DescriptionFieldEditorModel.reapplyValidators({
|
DescriptionFieldEditorModel.reapplyValidators({
|
||||||
formGroup: control as UntypedFormGroup,
|
formGroup: control as UntypedFormGroup,
|
||||||
rootPath: `${rootPath}fields[${key}].`,
|
rootPath: `${rootPath}fields[${key}].`,
|
||||||
validationErrorModel: validationErrorModel
|
validationErrorModel: validationErrorModel,
|
||||||
|
fieldDefinition: fieldSetDefinition.fields.find(x => x.id == key),
|
||||||
|
visibilityRulesService: params.visibilityRulesService,
|
||||||
|
visibilityRulesKey: key + '_' + formGroup.get('ordinal').value
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -442,6 +476,8 @@ export class DescriptionFieldEditorModel implements DescriptionFieldPersist {
|
||||||
tags: string[] = [];
|
tags: string[] = [];
|
||||||
externalIdentifier?: DescriptionExternalIdentifierEditorModel = new DescriptionExternalIdentifierEditorModel(this.validationErrorModel);
|
externalIdentifier?: DescriptionExternalIdentifierEditorModel = new DescriptionExternalIdentifierEditorModel(this.validationErrorModel);
|
||||||
|
|
||||||
|
fieldDefinition: DescriptionTemplateField;
|
||||||
|
|
||||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -449,6 +485,7 @@ export class DescriptionFieldEditorModel implements DescriptionFieldPersist {
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public fromModel(item: DescriptionField, descriptionTemplateField: DescriptionTemplateField, descriptionReferences: DescriptionReference[]): DescriptionFieldEditorModel {
|
public fromModel(item: DescriptionField, descriptionTemplateField: DescriptionTemplateField, descriptionReferences: DescriptionReference[]): DescriptionFieldEditorModel {
|
||||||
|
this.fieldDefinition = descriptionTemplateField;
|
||||||
if (item) {
|
if (item) {
|
||||||
this.textValue = item.textValue;
|
this.textValue = item.textValue;
|
||||||
this.textListValue = item.textListValue;
|
this.textListValue = item.textListValue;
|
||||||
|
@ -488,46 +525,89 @@ export class DescriptionFieldEditorModel implements DescriptionFieldPersist {
|
||||||
buildForm(params?: {
|
buildForm(params?: {
|
||||||
context?: ValidationContext,
|
context?: ValidationContext,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
rootPath?: string
|
rootPath?: string,
|
||||||
|
visibilityRulesService: VisibilityRulesService,
|
||||||
|
visibilityRulesKey: string
|
||||||
}): UntypedFormGroup {
|
}): UntypedFormGroup {
|
||||||
let { context = null, disabled = false, rootPath } = params ?? {}
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = DescriptionFieldEditorModel.createValidationContext({
|
context = DescriptionFieldEditorModel.createValidationContext({
|
||||||
validationErrorModel: this.validationErrorModel,
|
validationErrorModel: this.validationErrorModel,
|
||||||
rootPath
|
rootPath,
|
||||||
|
fieldDefinition: this.fieldDefinition,
|
||||||
|
visibilityRulesService: params.visibilityRulesService,
|
||||||
|
visibilityRulesKey: params.visibilityRulesKey
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.formBuilder.group({
|
const fieldType = this.fieldDefinition.data.fieldType;
|
||||||
textValue: [{ value: this.textValue, disabled: disabled }, context.getValidation('textValue').validators],
|
const multipleSelect = this.fieldDefinition.data.multipleSelect;
|
||||||
textListValue: [{ value: this.textListValue, disabled: disabled }, context.getValidation('textListValue').validators],
|
const fieldValueControlName = DescriptionEditorModel.getFieldValueControlName(fieldType, multipleSelect);
|
||||||
dateValue: [{ value: this.dateValue, disabled: disabled }, context.getValidation('dateValue').validators],
|
const formGroup = this.formBuilder.group({});
|
||||||
booleanValue: [{ value: this.booleanValue, disabled: disabled }, context.getValidation('booleanValue').validators],
|
switch (fieldType) {
|
||||||
references: [{ value: this.references, disabled: disabled }, context.getValidation('references').validators],
|
case DescriptionTemplateFieldType.FREE_TEXT:
|
||||||
reference: [{ value: this.reference, disabled: disabled }, context.getValidation('reference').validators],
|
case DescriptionTemplateFieldType.TEXT_AREA:
|
||||||
tags: [{ value: this.tags, disabled: disabled }, context.getValidation('tags').validators],
|
case DescriptionTemplateFieldType.UPLOAD:
|
||||||
externalIdentifier: this.externalIdentifier.buildForm({
|
case DescriptionTemplateFieldType.RICH_TEXT_AREA:
|
||||||
rootPath: `${rootPath}externalIdentifier.`
|
case DescriptionTemplateFieldType.RADIO_BOX:
|
||||||
}),
|
formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
});
|
case DescriptionTemplateFieldType.DATASET_IDENTIFIER:
|
||||||
|
case DescriptionTemplateFieldType.VALIDATION:
|
||||||
|
formGroup.addControl(fieldValueControlName, this.externalIdentifier.buildForm({
|
||||||
|
rootPath: `${rootPath}externalIdentifier.`
|
||||||
|
}));
|
||||||
|
case DescriptionTemplateFieldType.DATE_PICKER:
|
||||||
|
formGroup.addControl(fieldValueControlName, new FormControl({ value: this.dateValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.CHECK_BOX:
|
||||||
|
case DescriptionTemplateFieldType.BOOLEAN_DECISION:
|
||||||
|
formGroup.addControl(fieldValueControlName, new FormControl({ value: this.booleanValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DESCRIPTIONS:
|
||||||
|
if (multipleSelect) formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textListValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
else formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DMPS:
|
||||||
|
if (multipleSelect) formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textListValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
else formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.REFERENCE_TYPES:
|
||||||
|
if (multipleSelect) formGroup.addControl(fieldValueControlName, new FormControl({ value: this.references, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
else formGroup.addControl(fieldValueControlName, new FormControl({ value: this.reference, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.SELECT:
|
||||||
|
if (multipleSelect) formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textListValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
else formGroup.addControl(fieldValueControlName, new FormControl({ value: this.textValue, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
case DescriptionTemplateFieldType.TAGS:
|
||||||
|
formGroup.addControl(fieldValueControlName, new FormControl({ value: this.tags, disabled: disabled }, context.getValidation(fieldValueControlName).validators));
|
||||||
|
}
|
||||||
|
|
||||||
|
return formGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createValidationContext(params: {
|
static createValidationContext(params: {
|
||||||
rootPath?: string,
|
rootPath?: string,
|
||||||
validationErrorModel: ValidationErrorModel
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
fieldDefinition: DescriptionTemplateField,
|
||||||
|
visibilityRulesService: VisibilityRulesService,
|
||||||
|
visibilityRulesKey: string
|
||||||
}): ValidationContext {
|
}): ValidationContext {
|
||||||
const { rootPath = '', validationErrorModel } = params;
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
const baseContext: ValidationContext = new ValidationContext();
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
baseValidationArray.push({ key: 'textValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}textValue`)] });
|
|
||||||
baseValidationArray.push({ key: 'textListValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}textListValue`)] });
|
const fieldValueControlName = DescriptionEditorModel.getFieldValueControlName(params.fieldDefinition.data.fieldType, params.fieldDefinition.data.multipleSelect);
|
||||||
baseValidationArray.push({ key: 'dateValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}dateValue`)] });
|
const validators = [];
|
||||||
baseValidationArray.push({ key: 'booleanValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}booleanValue`)] });
|
validators.push(BackendErrorValidator(validationErrorModel, `${rootPath}${fieldValueControlName}`));
|
||||||
baseValidationArray.push({ key: 'references', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
|
||||||
baseValidationArray.push({ key: 'reference', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
params.fieldDefinition.validations.forEach(validation => {
|
||||||
baseValidationArray.push({ key: 'tags', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}tags`)] });
|
switch (validation) {
|
||||||
baseValidationArray.push({ key: 'externalIdentifier', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}externalIdentifier`)] });
|
case DescriptionTemplateFieldValidationType.Required:
|
||||||
|
validators.push(RequiredWithVisibilityRulesValidator(params.visibilityRulesService, params.visibilityRulesKey));
|
||||||
|
break;
|
||||||
|
case DescriptionTemplateFieldValidationType.Url:
|
||||||
|
validators.push(UrlValidator());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
baseValidationArray.push({ key: fieldValueControlName, validators: validators });
|
||||||
|
|
||||||
baseContext.validation = baseValidationArray;
|
baseContext.validation = baseValidationArray;
|
||||||
return baseContext;
|
return baseContext;
|
||||||
}
|
}
|
||||||
|
@ -535,13 +615,20 @@ export class DescriptionFieldEditorModel implements DescriptionFieldPersist {
|
||||||
static reapplyValidators(params: {
|
static reapplyValidators(params: {
|
||||||
formGroup: UntypedFormGroup,
|
formGroup: UntypedFormGroup,
|
||||||
validationErrorModel: ValidationErrorModel,
|
validationErrorModel: ValidationErrorModel,
|
||||||
rootPath: string
|
rootPath: string,
|
||||||
|
fieldDefinition: DescriptionTemplateField,
|
||||||
|
visibilityRulesService: VisibilityRulesService,
|
||||||
|
visibilityRulesKey: string
|
||||||
}): void {
|
}): void {
|
||||||
|
|
||||||
const { formGroup, rootPath, validationErrorModel } = params;
|
const { formGroup, rootPath, validationErrorModel, fieldDefinition } = params;
|
||||||
const context = DescriptionFieldEditorModel.createValidationContext({
|
const context = DescriptionFieldEditorModel.createValidationContext({
|
||||||
rootPath,
|
rootPath,
|
||||||
validationErrorModel
|
validationErrorModel,
|
||||||
|
fieldDefinition: fieldDefinition,
|
||||||
|
visibilityRulesService: params.visibilityRulesService,
|
||||||
|
visibilityRulesKey: params.visibilityRulesKey
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
['textValue', 'textListValue', 'dateValue', 'booleanValue'].forEach(keyField => {
|
['textValue', 'textListValue', 'dateValue', 'booleanValue'].forEach(keyField => {
|
||||||
|
@ -717,45 +804,6 @@ export class DescriptionFieldIndicator {
|
||||||
this.sectionIds = sectionIds;
|
this.sectionIds = sectionIds;
|
||||||
this.fieldSetId = fieldSetId;
|
this.fieldSetId = fieldSetId;
|
||||||
this.fieldId = fieldId;
|
this.fieldId = fieldId;
|
||||||
|
this.type = DescriptionEditorModel.getFieldValueControlName(type, multipleSelect);
|
||||||
switch (type) {
|
|
||||||
case DescriptionTemplateFieldType.FREE_TEXT:
|
|
||||||
case DescriptionTemplateFieldType.TEXT_AREA:
|
|
||||||
case DescriptionTemplateFieldType.UPLOAD:
|
|
||||||
case DescriptionTemplateFieldType.RICH_TEXT_AREA:
|
|
||||||
case DescriptionTemplateFieldType.RADIO_BOX:
|
|
||||||
this.type = "textValue";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.DATASET_IDENTIFIER:
|
|
||||||
case DescriptionTemplateFieldType.VALIDATION:
|
|
||||||
this.type = "externalIdentifier";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.DATE_PICKER:
|
|
||||||
this.type = "dateValue";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.CHECK_BOX:
|
|
||||||
case DescriptionTemplateFieldType.BOOLEAN_DECISION:
|
|
||||||
this.type = "booleanValue";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DESCRIPTIONS:
|
|
||||||
if (multipleSelect) this.type = "textListValue";
|
|
||||||
else this.type = "textValue"
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DMPS:
|
|
||||||
if (multipleSelect) this.type = "textListValue";
|
|
||||||
else this.type = "textValue";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.REFERENCE_TYPES:
|
|
||||||
if (multipleSelect) this.type = "references";
|
|
||||||
else this.type = "reference";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.SELECT:
|
|
||||||
if (multipleSelect) this.type = "textListValue";
|
|
||||||
else this.type = "textValue";
|
|
||||||
break;
|
|
||||||
case DescriptionTemplateFieldType.TAGS:
|
|
||||||
this.type = "tags";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,11 +82,11 @@ export class DescriptionFormFieldSetComponent extends BaseComponent {
|
||||||
}
|
}
|
||||||
const properties: DescriptionPropertyDefinitionFieldSet = this.propertiesFormGroup.value;
|
const properties: DescriptionPropertyDefinitionFieldSet = this.propertiesFormGroup.value;
|
||||||
let ordinal = 0;
|
let ordinal = 0;
|
||||||
if (properties?.items && properties.items.map(x => x.ordinal).filter(val => !isNaN(val)).length > 0) {
|
if (properties?.items && properties.items.map(x => x.ordinal).filter(val => !isNaN(val)).length > 0) {
|
||||||
ordinal = Math.max(...properties.items.map(x => x.ordinal).filter(val => !isNaN(val))) + 1;
|
ordinal = Math.max(...properties.items.map(x => x.ordinal).filter(val => !isNaN(val))) + 1;
|
||||||
}
|
}
|
||||||
const item: DescriptionPropertyDefinitionFieldSetEditorModel = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel).calculateFieldSetProperties(this.fieldSet, ordinal, null, null);
|
const item: DescriptionPropertyDefinitionFieldSetEditorModel = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel).calculateFieldSetProperties(this.fieldSet, ordinal, null, null);
|
||||||
formArray.push((item.buildForm({ rootPath: `properties.fieldSets[${this.fieldSet.id}].` }).get('items') as UntypedFormArray).at(0));
|
formArray.push((item.buildForm({ rootPath: `properties.fieldSets[${this.fieldSet.id}].`, visibilityRulesService: this.visibilityRulesService }).get('items') as UntypedFormArray).at(0));
|
||||||
this.visibilityRulesService.reloadVisibility();
|
this.visibilityRulesService.reloadVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,9 @@ export class DescriptionFormFieldSetComponent extends BaseComponent {
|
||||||
{
|
{
|
||||||
formArray: formArray,
|
formArray: formArray,
|
||||||
validationErrorModel: this.validationErrorModel,
|
validationErrorModel: this.validationErrorModel,
|
||||||
rootPath: `properties.fieldSets[${this.fieldSet.id}].`
|
rootPath: `properties.fieldSets[${this.fieldSet.id}].`,
|
||||||
|
fieldSetDefinition: this.fieldSet,
|
||||||
|
visibilityRulesService: this.visibilityRulesService
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
formArray.markAsDirty();
|
formArray.markAsDirty();
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-form-field class="w-100">
|
<mat-form-field class="w-100">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<input matInput [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}" [required]="isRequired">
|
<input matInput [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}">
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('pattern')">{{'GENERAL.VALIDATION.URL.MESSAGE' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('pattern')">{{'GENERAL.VALIDATION.URL.MESSAGE' | translate}}</mat-error>
|
||||||
|
@ -31,14 +31,14 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<ng-container *ngIf="field.data.multipleSelect">
|
<ng-container *ngIf="field.data.multipleSelect">
|
||||||
<mat-select [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [required]="isRequired" [multiple]="field.data.multipleSelect">
|
<mat-select [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [multiple]="field.data.multipleSelect">
|
||||||
<mat-option *ngFor="let opt of field.data.options" [value]="opt.value">{{opt.label}}</mat-option>
|
<mat-option *ngFor="let opt of field.data.options" [value]="opt.value">{{opt.label}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!(field.data.multipleSelect)">
|
<ng-container *ngIf="!(field.data.multipleSelect)">
|
||||||
<mat-select [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [required]="isRequired" [multiple]="field.data.multipleSelect">
|
<mat-select [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [multiple]="field.data.multipleSelect">
|
||||||
<mat-option *ngFor="let opt of field.data.options" [value]="opt.value">{{opt.label}}
|
<mat-option *ngFor="let opt of field.data.options" [value]="opt.value">{{opt.label}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
@ -51,25 +51,25 @@
|
||||||
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DESCRIPTIONS" class="col-12">
|
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DESCRIPTIONS" class="col-12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ng-container *ngIf="field.data.multipleSelect">
|
<ng-container *ngIf="field.data.multipleSelect">
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="descriptionService.multipleAutocompleteConfiguration">
|
<app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="descriptionService.multipleAutocompleteConfiguration">
|
||||||
</app-multiple-auto-complete>
|
</app-multiple-auto-complete>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
<mat-hint>{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.EXTERNAL-SOURCE-HINT" | translate }}</mat-hint>
|
<mat-hint>{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.EXTERNAL-SOURCE-HINT" | translate }}</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!(field.data.multipleSelect)">
|
<ng-container *ngIf="!(field.data.multipleSelect)">
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="descriptionService.singleAutocompleteConfiguration" [required]="isRequired">
|
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="descriptionService.singleAutocompleteConfiguration">
|
||||||
</app-single-auto-complete>
|
</app-single-auto-complete>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
<mat-hint>{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.EXTERNAL-SOURCE-HINT" | translate }}</mat-hint>
|
<mat-hint>{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.EXTERNAL-SOURCE-HINT" | translate }}</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DMPS" class="col-12">
|
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DMPS" class="col-12">
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
<ng-container *ngIf="!(field.data.multipleSelect)">
|
<ng-container *ngIf="!(field.data.multipleSelect)">
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="dmpService.singleAutocompleteConfiguration" [required]="isRequired">
|
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="dmpService.singleAutocompleteConfiguration">
|
||||||
</app-single-auto-complete>
|
</app-single-auto-complete>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-form-field *ngSwitchCase="descriptionTemplateFieldTypeEnum.TEXT_AREA" class="w-100">
|
<mat-form-field *ngSwitchCase="descriptionTemplateFieldTypeEnum.TEXT_AREA" class="w-100">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<textarea matInput class="text-area" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" matTextareaAutosize matAutosizeMinRows="3" matAutosizeMaxRows="15" [required]="isRequired" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}"></textarea>
|
<textarea matInput class="text-area" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" matTextareaAutosize matAutosizeMinRows="3" matAutosizeMaxRows="15" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}"></textarea>
|
||||||
<button mat-icon-button type="button" *ngIf="!propertiesFormGroup?.get(field.id).get('textValue').disabled && propertiesFormGroup?.get(field.id).get('textValue').value" matSuffix aria-label="Clear" (click)="this.propertiesFormGroup?.get(field.id).get('textValue').patchValue('')">
|
<button mat-icon-button type="button" *ngIf="!propertiesFormGroup?.get(field.id).get('textValue').disabled && propertiesFormGroup?.get(field.id).get('textValue').value" matSuffix aria-label="Clear" (click)="this.propertiesFormGroup?.get(field.id).get('textValue').patchValue('')">
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="descriptionTemplateFieldTypeEnum.RICH_TEXT_AREA">
|
<ng-container *ngSwitchCase="descriptionTemplateFieldTypeEnum.RICH_TEXT_AREA">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<rich-text-editor-component [form]="propertiesFormGroup?.get(field.id).get('textValue')" [placeholder]="field.data.label" [required]="isRequired" [wrapperClasses]="'full-width editor ' +
|
<rich-text-editor-component [form]="propertiesFormGroup?.get(field.id).get('textValue')" [placeholder]="field.data.label" [wrapperClasses]="'full-width editor ' +
|
||||||
((isRequired && propertiesFormGroup?.get(field.id).get('textValue').touched && propertiesFormGroup?.get(field.id).get('textValue').hasError('required')) ? 'required' : '')" [editable]="!propertiesFormGroup?.get(field.id).get('textValue').disabled">
|
((isRequired && propertiesFormGroup?.get(field.id).get('textValue').touched && propertiesFormGroup?.get(field.id).get('textValue').hasError('required')) ? 'required' : '')" [editable]="!propertiesFormGroup?.get(field.id).get('textValue').disabled">
|
||||||
</rich-text-editor-component>
|
</rich-text-editor-component>
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.BOOLEAN_DECISION" class="col-12">
|
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.BOOLEAN_DECISION" class="col-12">
|
||||||
<mat-radio-group [formControl]="propertiesFormGroup?.get(field.id).get('booleanValue')" [required]="isRequired">
|
<mat-radio-group [formControl]="propertiesFormGroup?.get(field.id).get('booleanValue')">
|
||||||
<mat-radio-button class="radio-button-item" [value]="true">{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.ACTIONS.YES" | translate }}</mat-radio-button>
|
<mat-radio-button class="radio-button-item" [value]="true">{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.ACTIONS.YES" | translate }}</mat-radio-button>
|
||||||
<mat-radio-button class="radio-button-item" [value]="false">{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.ACTIONS.NO" | translate }}</mat-radio-button>
|
<mat-radio-button class="radio-button-item" [value]="false">{{ "TYPES.DATASET-PROFILE-COMBO-BOX-TYPE.ACTIONS.NO" | translate }}</mat-radio-button>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('booleanValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('booleanValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('booleanValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('booleanValue').getError('backendError').message}}</mat-error>
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.RADIO_BOX" class="col-12">
|
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.RADIO_BOX" class="col-12">
|
||||||
<mat-radio-group [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [required]="isRequired">
|
<mat-radio-group [formControl]="propertiesFormGroup?.get(field.id).get('textValue')">
|
||||||
<mat-radio-button *ngFor="let option of field.data.options let index = index" class="radio-button-item" [value]="option.value">{{option.label}}</mat-radio-button>
|
<mat-radio-button *ngFor="let option of field.data.options let index = index" class="radio-button-item" [value]="option.value">{{option.label}}</mat-radio-button>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
|
@ -170,7 +170,7 @@
|
||||||
|
|
||||||
<mat-form-field *ngSwitchCase="descriptionTemplateFieldTypeEnum.DATE_PICKER" class="col-12">
|
<mat-form-field *ngSwitchCase="descriptionTemplateFieldTypeEnum.DATE_PICKER" class="col-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<input matInput placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" class="table-input" [matDatepicker]="date" [required]="isRequired" [formControl]="propertiesFormGroup?.get(field.id).get('dateValue')">
|
<input matInput placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" class="table-input" [matDatepicker]="date" [formControl]="propertiesFormGroup?.get(field.id).get('dateValue')">
|
||||||
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
|
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
|
||||||
<mat-datepicker #date></mat-datepicker>
|
<mat-datepicker #date></mat-datepicker>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('dateValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('dateValue').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('dateValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('dateValue').getError('backendError').message}}</mat-error>
|
||||||
|
@ -185,13 +185,13 @@
|
||||||
<div class="row" *ngIf="datasetIdInitialized">
|
<div class="row" *ngIf="datasetIdInitialized">
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<input matInput class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}" [required]="isRequired" [disabled]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').disabled">
|
<input matInput class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}" [disabled]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').disabled">
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="col-md-12">
|
<mat-form-field class="col-md-12">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<mat-select class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('type')" [placeholder]="('TYPES.DATASET-PROFILE-IDENTIFIER.IDENTIFIER-TYPE' | translate) + (isRequired? ' *': '')" [required]="isRequired" [disabled]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('type').disabled">
|
<mat-select class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('type')" [placeholder]="('TYPES.DATASET-PROFILE-IDENTIFIER.IDENTIFIER-TYPE' | translate) + (isRequired? ' *': '')" [disabled]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('type').disabled">
|
||||||
<mat-option *ngFor="let type of datasetIdTypes" [value]="type.value">
|
<mat-option *ngFor="let type of datasetIdTypes" [value]="type.value">
|
||||||
{{ type.name }}
|
{{ type.name }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
@ -206,7 +206,7 @@
|
||||||
<div class="row align-items-baseline">
|
<div class="row align-items-baseline">
|
||||||
<mat-form-field class="col-md-4">
|
<mat-form-field class="col-md-4">
|
||||||
<mat-label>{{ field.data.label }}</mat-label>
|
<mat-label>{{ field.data.label }}</mat-label>
|
||||||
<input matInput class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}" [required]="isRequired">
|
<input matInput class="col-md-12" [formControl]="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier')" placeholder="{{(field.data.label) + (isRequired? ' *': '')}}">
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('externalIdentifier')?.get('identifier').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@ -231,4 +231,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -80,11 +80,12 @@ export class FormProgressIndicationComponent extends BaseComponent implements On
|
||||||
|
|
||||||
countRequiredFieldsByFieldset(ordinal: number, fieldsFormGroup: UntypedFormGroup, filterValid: boolean = false): number {
|
countRequiredFieldsByFieldset(ordinal: number, fieldsFormGroup: UntypedFormGroup, filterValid: boolean = false): number {
|
||||||
let fieldsCount: number = 0;
|
let fieldsCount: number = 0;
|
||||||
const fieldNames = Object.keys(fieldsFormGroup.controls);
|
const fieldSetNames = Object.keys(fieldsFormGroup.controls);
|
||||||
for(let item of fieldNames) {
|
for(let item of fieldSetNames) {
|
||||||
if (!this.checkVisibility || this.visibilityRulesService.isVisible(item, ordinal)) {
|
if (!this.checkVisibility || this.visibilityRulesService.isVisible(item, ordinal)) {
|
||||||
const fieldControl = fieldsFormGroup.get(item);
|
const fieldControl = fieldsFormGroup.get(item);
|
||||||
for (let fieldType of this.fieldTypes) {
|
const fieldNames = Object.keys((fieldControl as UntypedFormGroup).controls);
|
||||||
|
for (let fieldType of fieldNames) {
|
||||||
const typedControl = fieldControl.get(fieldType);
|
const typedControl = fieldControl.get(fieldType);
|
||||||
let controlFilter: boolean = this.controlRequired(typedControl) && this.controlEnabled(typedControl);
|
let controlFilter: boolean = this.controlRequired(typedControl) && this.controlEnabled(typedControl);
|
||||||
if (filterValid) controlFilter = controlFilter && typedControl.valid;
|
if (filterValid) controlFilter = controlFilter && typedControl.valid;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="main-content pl-5 pr-5">
|
<div class="main-content pl-5 pr-5">
|
||||||
<div class="container-fluid pl-0 pr-0">
|
<div class="container-fluid pl-0 pr-0">
|
||||||
<div *ngIf="description">
|
<div *ngIf="description && userName">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 pl-2 mb-3">
|
<div class="col-12 pl-2 mb-3">
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
|
@ -197,12 +197,20 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col pl-0" style="min-width: 0;">
|
<div class="col pl-0" style="min-width: 0;">
|
||||||
<p class="authors-label">{{ dmpUser.user?.name }} <span *ngIf="isUserAuthor(dmpUser.user?.id)">({{ 'DESCRIPTION-OVERVIEW.YOU' | translate }})</span></p>
|
<ng-container *ngIf="!isUserAuthor(dmpUser.user?.id); else you">
|
||||||
<p class="authors-role">
|
<p class="authors-label">{{ dmpUser.user?.name }}</p>
|
||||||
<span>{{ enumUtils.toDmpUserRoleString(dmpUser.role) }} - </span>
|
</ng-container>
|
||||||
<span *ngIf="!dmpUser.sectionId">{{ 'DESCRIPTION-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
|
<ng-template #you>
|
||||||
<span *ngIf="dmpUser.sectionId">{{ getSectionNameById(dmpUser.sectionId) }}</span>
|
<p class="authors-label">{{ userName }}
|
||||||
|
<span>({{ 'DESCRIPTION-OVERVIEW.YOU' | translate }})</span>
|
||||||
</p>
|
</p>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<p class="authors-role">
|
||||||
|
<span>{{ enumUtils.toDmpUserRoleString(dmpUser.role) }} - </span>
|
||||||
|
<span *ngIf="!dmpUser.sectionId">{{ 'DESCRIPTION-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
|
||||||
|
<span *ngIf="dmpUser.sectionId">{{ getSectionNameById(dmpUser.sectionId) }}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto" *ngIf="canInviteDmpUsers && description.dmp?.status === dmpStatusEnum.Draft && dmpUser.role != dmpUserRoleEnum.Owner">
|
<div class="col-auto" *ngIf="canInviteDmpUsers && description.dmp?.status === dmpStatusEnum.Draft && dmpUser.role != dmpUserRoleEnum.Owner">
|
||||||
<button (click)="removeUserFromDmp(dmpUser)" mat-mini-fab matTooltip="{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}" matTooltipPosition="above">
|
<button (click)="removeUserFromDmp(dmpUser)" mat-mini-fab matTooltip="{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}" matTooltipPosition="above">
|
||||||
|
|
|
@ -36,11 +36,14 @@ import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dialog/dmp-
|
||||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
import { nameof } from 'ts-simple-nameof';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
|
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
|
||||||
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
|
import { User } from '@app/core/model/user/user';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -72,6 +75,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
||||||
canInviteDmpUsers = false;
|
canInviteDmpUsers = false;
|
||||||
|
|
||||||
authorFocus: string;
|
authorFocus: string;
|
||||||
|
userName: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -94,7 +98,8 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
||||||
private lockService: LockService,
|
private lockService: LockService,
|
||||||
private analyticsService: AnalyticsService,
|
private analyticsService: AnalyticsService,
|
||||||
private breadcrumbService: BreadcrumbService,
|
private breadcrumbService: BreadcrumbService,
|
||||||
private httpErrorHandlingService: HttpErrorHandlingService
|
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
|
private userService: UserService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -183,6 +188,15 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.isAuthenticated()) {
|
||||||
|
this.userService.getSingle(this.authentication.userId(), [
|
||||||
|
nameof<User>(x => x.id),
|
||||||
|
nameof<User>(x => x.name)])
|
||||||
|
.pipe(map(u => u.name)).subscribe(name => this.userName = name);
|
||||||
|
} else {
|
||||||
|
this.userName = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get unauthorizedTootipText(): string {
|
get unauthorizedTootipText(): string {
|
||||||
|
@ -229,7 +243,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
||||||
isUserAuthor(userId: Guid): boolean {
|
isUserAuthor(userId: Guid): boolean {
|
||||||
if (this.isAuthenticated()) {
|
if (this.isAuthenticated()) {
|
||||||
const principalId: Guid = this.authentication.userId();
|
const principalId: Guid = this.authentication.userId();
|
||||||
return userId === principalId;
|
return this.userName && (userId === principalId);
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,9 +96,9 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
|
|
||||||
getDescriptionTemplateMultipleAutoCompleteConfiguration(sectionId: Guid): MultipleAutoCompleteConfiguration {
|
getDescriptionTemplateMultipleAutoCompleteConfiguration(sectionId: Guid): MultipleAutoCompleteConfiguration {
|
||||||
return {
|
return {
|
||||||
initialItems: (excludedItems: any[], data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
|
initialItems: (excludedItems: any[], data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
|
||||||
filterFn: (searchQuery: string, excludedItems: any[]) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)),
|
filterFn: (searchQuery: string, excludedItems: any[]) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], searchQuery, excludedItems)).pipe(map(x => x.items)),
|
||||||
getSelectedItems: (selectedItems: any[]) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)),
|
getSelectedItems: (selectedItems: any[]) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active, IsActive.Inactive], null, null, selectedItems)).pipe(map(x => x.items)),
|
||||||
displayFn: (item: DescriptionTemplate) => item.label,
|
displayFn: (item: DescriptionTemplate) => item.label,
|
||||||
titleFn: (item: DescriptionTemplate) => item.label,
|
titleFn: (item: DescriptionTemplate) => item.label,
|
||||||
subtitleFn: (item: DescriptionTemplate) => item.description,
|
subtitleFn: (item: DescriptionTemplate) => item.description,
|
||||||
|
@ -358,7 +358,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
setTimeout(x => {
|
setTimeout(x => {
|
||||||
this.step = this.step > 0 ? this.step - 1 : 0;
|
if (this.isNew) this.step = 0;
|
||||||
|
else this.step = this.step > 0 ? this.step - 1 : 0;
|
||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormattingModule } from '@app/core/formatting.module';
|
import { FormattingModule } from '@app/core/formatting.module';
|
||||||
import { DmpRoutingModule } from '@app/ui/dmp/dmp.routing';
|
import { DmpRoutingModule, PublicDmpRoutingModule } from '@app/ui/dmp/dmp.routing';
|
||||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
|
||||||
|
@ -17,3 +17,17 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class DmpModule { }
|
export class DmpModule { }
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonUiModule,
|
||||||
|
CommonFormsModule,
|
||||||
|
FormattingModule,
|
||||||
|
PublicDmpRoutingModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class PublicDmpModule { }
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { BreadcrumbService } from '../misc/breadcrumb/breadcrumb.service';
|
import { BreadcrumbService } from '../misc/breadcrumb/breadcrumb.service';
|
||||||
|
import { AuthGuard } from '@app/core/auth-guard.service';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'overview',
|
path: 'overview',
|
||||||
loadChildren: () => import('./overview/dmp-overview.module').then(m => m.DmpOverviewModule),
|
loadChildren: () => import('./overview/dmp-overview.module').then(m => m.DmpOverviewModule),
|
||||||
|
canActivate:[AuthGuard],
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -16,6 +18,7 @@ const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new',
|
||||||
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
|
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
|
||||||
|
canActivate:[AuthGuard],
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -27,6 +30,7 @@ const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
|
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
|
||||||
|
canActivate:[AuthGuard],
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
|
@ -37,80 +41,32 @@ const routes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
canActivate:[AuthGuard],
|
||||||
loadChildren: () => import('./listing/dmp-listing.module').then(m => m.DmpListingModule),
|
loadChildren: () => import('./listing/dmp-listing.module').then(m => m.DmpListingModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumb: true
|
breadcrumb: true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const publicRoutes: Routes = [
|
||||||
|
{
|
||||||
// {
|
path: 'overview',
|
||||||
// path: 'publicEdit/:publicId',
|
loadChildren: () => import('./overview/dmp-overview.module').then(m => m.DmpOverviewModule),
|
||||||
// component: DmpEditorComponent,
|
data: {
|
||||||
// data: {
|
breadcrumb: true,
|
||||||
// breadcrumb: true,
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
// title: 'GENERAL.TITLES.DMP-PUBLIC-EDIT'
|
hideNavigationItem: true
|
||||||
// },
|
}),
|
||||||
// canDeactivate: [CanDeactivateGuard]
|
}
|
||||||
// },
|
},
|
||||||
|
{
|
||||||
// {
|
path: '',
|
||||||
// path: 'publicOverview/:publicId',
|
loadChildren: () => import('./listing/dmp-listing.module').then(m => m.DmpListingModule),
|
||||||
// component: DmpOverviewComponent,
|
data: {
|
||||||
// data: {
|
breadcrumb: true
|
||||||
// breadcrumb: true,
|
},
|
||||||
// title: 'GENERAL.TITLES.DMP-OVERVIEW'
|
},
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// {
|
|
||||||
// path: 'new/dataset',
|
|
||||||
// component: DmpEditorComponent,
|
|
||||||
// canActivate: [AuthGuard],
|
|
||||||
// data: {
|
|
||||||
// breadcrumbs: 'new/dataset',
|
|
||||||
// title: 'GENERAL.TITLES.DATASET-NEW'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'new/dataset/:dmpId',
|
|
||||||
// component: DmpEditorComponent,
|
|
||||||
// canActivate: [AuthGuard],
|
|
||||||
// data: {
|
|
||||||
// breadcrumbs: 'new/dataset',
|
|
||||||
// title: 'GENERAL.TITLES.DATASET-NEW'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'new_version/:id',
|
|
||||||
// // component: DmpWizardComponent,
|
|
||||||
// component: DmpCloneComponent,
|
|
||||||
// data: {
|
|
||||||
// clone: false,
|
|
||||||
// breadcrumb: true,
|
|
||||||
// title: 'GENERAL.TITLES.DMP-NEW-VERSION'
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'clone/:id',
|
|
||||||
// component: DmpCloneComponent,
|
|
||||||
// data: {
|
|
||||||
// clone: false,
|
|
||||||
// breadcrumb: true,
|
|
||||||
// title: 'GENERAL.TITLES.DMP-CLONE'
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'invitation/:id',
|
|
||||||
// component: InvitationAcceptedComponent,
|
|
||||||
// data: {
|
|
||||||
// breadcrumb: true
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -118,3 +74,9 @@ const routes: Routes = [
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class DmpRoutingModule { }
|
export class DmpRoutingModule { }
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(publicRoutes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class PublicDmpRoutingModule { }
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<app-dmp-user-field-component [form]="formGroup" [validationErrorModel]="editorModel.validationErrorModel" [sections]="selectedBlueprint.definition.sections" [viewOnly]="false" [initializeUsers]="true" [enableSorting]="false"></app-dmp-user-field-component>
|
<app-dmp-user-field-component [form]="formGroup" [validationErrorModel]="editorModel.validationErrorModel" [sections]="selectedBlueprint.definition.sections" [viewOnly]="false" [initializeUsers]="true" [enableSorting]="false"></app-dmp-user-field-component>
|
||||||
</div>
|
</div>
|
||||||
<div class="col mt-2">
|
<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>
|
<button mat-raised-button *ngIf="hasValue()" [disabled]="inProgressSendButton" (click)="send()" type="button" class="invite-btn">{{'DMP-USER-INVITATION-DIALOG.ACTIONS.INVITE' | translate}}</button>
|
||||||
<mat-error *ngIf="formGroup.get('users').hasError('backendError')">{{formGroup.get('users').getError('backendError').message}}</mat-error>
|
<mat-error *ngIf="formGroup.get('users').hasError('backendError')">{{formGroup.get('users').getError('backendError').message}}</mat-error>
|
||||||
<mat-error *ngIf="formGroup.get('users').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="formGroup.get('users').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
|
|
||||||
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
||||||
|
import { DmpBlueprint } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
||||||
import { DmpUserPersist } from '@app/core/model/dmp/dmp';
|
import { DmpUserPersist } from '@app/core/model/dmp/dmp';
|
||||||
import { DmpService } from '@app/core/services/dmp/dmp.service';
|
import { DmpService } from '@app/core/services/dmp/dmp.service';
|
||||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { FilterService } from '@common/modules/text-filter/filter-service';
|
import { FormService } from '@common/forms/form-service';
|
||||||
|
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { DmpEditorModel } from '../../dmp-editor-blueprint/dmp-editor.model';
|
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { DmpBlueprint } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
import { DmpEditorModel } from '../../dmp-editor-blueprint/dmp-editor.model';
|
||||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
import { DmpEditorService } from '../../dmp-editor-blueprint/dmp-editor.service';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-invitation-dialog-component',
|
selector: 'app-invitation-dialog-component',
|
||||||
templateUrl: 'dmp-invitation-dialog.component.html',
|
templateUrl: 'dmp-invitation-dialog.component.html',
|
||||||
styleUrls: ['./dmp-invitation-dialog.component.scss'],
|
styleUrls: ['./dmp-invitation-dialog.component.scss'],
|
||||||
|
providers: [DmpEditorService]
|
||||||
})
|
})
|
||||||
export class DmpInvitationDialogComponent extends BaseComponent implements OnInit {
|
export class DmpInvitationDialogComponent extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
|
@ -31,7 +33,8 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
editorModel: DmpEditorModel;
|
editorModel: DmpEditorModel;
|
||||||
formGroup: UntypedFormGroup;
|
formGroup: UntypedFormGroup;
|
||||||
dmpUserRoleEnum = DmpUserRole;
|
dmpUserRoleEnum = DmpUserRole;
|
||||||
selectedBlueprint: DmpBlueprint;
|
selectedBlueprint: DmpBlueprint;
|
||||||
|
inProgressSendButton = false;
|
||||||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -43,8 +46,8 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
private uiNotificationService: UiNotificationService,
|
private uiNotificationService: UiNotificationService,
|
||||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
private dmpService: DmpService,
|
private dmpService: DmpService,
|
||||||
private userService: UserService,
|
private formService: FormService,
|
||||||
private filterService: FilterService,
|
private dmpEditorService: DmpEditorService,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -58,10 +61,14 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
}
|
}
|
||||||
|
|
||||||
send() {
|
send() {
|
||||||
|
this.formService.removeAllBackEndErrors(this.formGroup.get("users"));
|
||||||
|
this.formService.touchAllFormFields(this.formGroup.get("users"));
|
||||||
|
|
||||||
if (!this.formGroup.get("users").valid) { return; }
|
if (!this.formGroup.get("users").valid) { return; }
|
||||||
|
this.inProgressSendButton = true;
|
||||||
const userFormData = this.formGroup.get("users").value as DmpUserPersist[];
|
const userFormData = this.formGroup.get("users").value as DmpUserPersist[];
|
||||||
|
|
||||||
this.dmpService.inviteUsers(this.dmpId, {users: userFormData})
|
this.dmpService.inviteUsers(this.dmpId, { users: userFormData })
|
||||||
.pipe(takeUntil(this._destroyed))
|
.pipe(takeUntil(this._destroyed))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
complete => {
|
complete => {
|
||||||
|
@ -84,9 +91,21 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-USER-INVITATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
|
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-USER-INVITATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
|
// this.inProgressSendButton = false;
|
||||||
|
// let errorOverrides = new Map<number, string>();
|
||||||
|
// errorOverrides.set(-1, this.language.instant('DMP-USER-INVITATION-DIALOG.ERROR'));
|
||||||
|
// this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides, SnackBarNotificationLevel.Error);
|
||||||
|
// }
|
||||||
|
|
||||||
onCallbackError(errorResponse: HttpErrorResponse) {
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
let errorOverrides = new Map<number, string>();
|
this.inProgressSendButton = false;
|
||||||
errorOverrides.set(-1, this.language.instant('DMP-USER-INVITATION-DIALOG.ERROR'));
|
this.httpErrorHandlingService.handleBackedRequestError(errorResponse);
|
||||||
this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides, SnackBarNotificationLevel.Error);
|
|
||||||
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
|
if (error.statusCode === 400) {
|
||||||
|
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="main-content dmp-overview pl-5 pr-5">
|
<div class="main-content dmp-overview pl-5 pr-5">
|
||||||
<div class="container-fluid pl-0 pr-0">
|
<div class="container-fluid pl-0 pr-0">
|
||||||
<div *ngIf="dmp">
|
<div *ngIf="dmp && userName">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 pl-2 mb-3">
|
<div class="col-12 pl-2 mb-3">
|
||||||
<app-navigation-breadcrumb />
|
<app-navigation-breadcrumb />
|
||||||
|
@ -258,11 +258,15 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col pl-0" style="min-width: 0;">
|
<div class="col pl-0" style="min-width: 0;">
|
||||||
<!-- <div class="mytext">{{ dmpUser.user?.name }}</div> -->
|
<ng-container *ngIf="!isUserAuthor(dmpUser.user?.id); else you">
|
||||||
<p class="authors-label">{{ dmpUser.user?.name }}
|
<p class="authors-label">{{ dmpUser.user?.name }}</p>
|
||||||
<span *ngIf="isUserAuthor(dmpUser.user?.id)">
|
</ng-container>
|
||||||
({{ 'DMP-OVERVIEW.YOU' | translate }})</span>
|
<ng-template #you>
|
||||||
</p>
|
<p class="authors-label"> {{ userName }}
|
||||||
|
<span >
|
||||||
|
({{ 'DMP-OVERVIEW.YOU' | translate }})</span>
|
||||||
|
</p>
|
||||||
|
</ng-template>
|
||||||
<p class="authors-role">
|
<p class="authors-role">
|
||||||
<span>{{ enumUtils.toDmpUserRoleString(dmpUser.role) }} - </span>
|
<span>{{ enumUtils.toDmpUserRoleString(dmpUser.role) }} - </span>
|
||||||
<span *ngIf="!dmpUser.sectionId">{{ 'DMP-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
|
<span *ngIf="!dmpUser.sectionId">{{ 'DMP-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
|
||||||
|
|
|
@ -41,7 +41,7 @@ import { PopupNotificationDialogComponent } from '@app/library/notification/popu
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
import { nameof } from 'ts-simple-nameof';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
import { CloneDmpDialogComponent } from '../clone-dialog/dmp-clone-dialog.component';
|
import { CloneDmpDialogComponent } from '../clone-dialog/dmp-clone-dialog.component';
|
||||||
import { DmpDeleteDialogComponent } from '../dmp-delete-dialog/dmp-delete-dialog.component';
|
import { DmpDeleteDialogComponent } from '../dmp-delete-dialog/dmp-delete-dialog.component';
|
||||||
|
@ -51,6 +51,9 @@ import { DmpInvitationDialogComponent } from '../invitation/dialog/dmp-invitatio
|
||||||
import { NewVersionDmpDialogComponent } from '../new-version-dialog/dmp-new-version-dialog.component';
|
import { NewVersionDmpDialogComponent } from '../new-version-dialog/dmp-new-version-dialog.component';
|
||||||
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { User } from '@app/core/model/user/user';
|
||||||
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dmp-overview',
|
selector: 'app-dmp-overview',
|
||||||
|
@ -85,6 +88,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
dmpUserRoleEnum = DmpUserRole;
|
dmpUserRoleEnum = DmpUserRole;
|
||||||
|
|
||||||
authorFocus: string;
|
authorFocus: string;
|
||||||
|
userName: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -108,6 +112,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
private analyticsService: AnalyticsService,
|
private analyticsService: AnalyticsService,
|
||||||
private breadcrumbService: BreadcrumbService,
|
private breadcrumbService: BreadcrumbService,
|
||||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
|
private userService: UserService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -205,6 +210,13 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
this.depositRepos = repos;
|
this.depositRepos = repos;
|
||||||
},
|
},
|
||||||
error => this.depositRepos = []);
|
error => this.depositRepos = []);
|
||||||
|
|
||||||
|
this.userService.getSingle(this.authentication.userId(), [
|
||||||
|
nameof<User>(x => x.id),
|
||||||
|
nameof<User>(x => x.name)])
|
||||||
|
.pipe(map(u => u.name)).subscribe(name => this.userName = name);
|
||||||
|
} else {
|
||||||
|
this.userName = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +235,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
isUserAuthor(userId: Guid): boolean {
|
isUserAuthor(userId: Guid): boolean {
|
||||||
if (this.isAuthenticated()) {
|
if (this.isAuthenticated()) {
|
||||||
const principalId: Guid = this.authentication.userId();
|
const principalId: Guid = this.authentication.userId();
|
||||||
return userId === principalId;
|
return this.userName && userId === principalId;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,14 +90,20 @@ export class NavbarComponent extends BaseComponent implements OnInit {
|
||||||
this.authentication.getAuthenticationStateObservable().subscribe(authenticationState => {
|
this.authentication.getAuthenticationStateObservable().subscribe(authenticationState => {
|
||||||
if (authenticationState.loginStatus === LoginStatus.LoggedIn) {
|
if (authenticationState.loginStatus === LoginStatus.LoggedIn) {
|
||||||
this.loadLogo();
|
this.loadLogo();
|
||||||
|
this.loadUser();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.loadLogo();
|
this.loadLogo();
|
||||||
|
this.loadUser();
|
||||||
|
}
|
||||||
|
|
||||||
this.userService.getSingle(this.authentication.userId(), [
|
private loadUser() {
|
||||||
nameof<User>(x => x.id),
|
if (this.authentication.currentAccountIsAuthenticated() && this.authentication.userId()) {
|
||||||
nameof<User>(x => x.name)
|
this.userService.getSingle(this.authentication.userId(), [
|
||||||
]).subscribe(u => this.userName = u.name); //TODO HANDLE-ERRORS
|
nameof<User>(x => x.id),
|
||||||
|
nameof<User>(x => x.name)
|
||||||
|
]).subscribe(u => this.userName = u.name); //TODO HANDLE-ERRORS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadLogo() {
|
private loadLogo() {
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<div *ngIf="showItem(groupMenuItem);">
|
<div *ngIf="showItem(groupMenuItem);">
|
||||||
<hr *ngIf="!firstGroup">
|
<hr *ngIf="!firstGroup">
|
||||||
<mat-list-item routerLinkActive="active" [isActiveMatchOptions]="{ paths: 'exact', queryParams: 'ignored' }" *ngFor="let groupMenuRoute of groupMenuItem.routes; let first = first" class="nav-item" [ngClass]="{'mt-4': first && firstGroup}">
|
<mat-list-item routerLinkActive="active" [isActiveMatchOptions]="{ paths: 'exact', queryParams: 'ignored' }" *ngFor="let groupMenuRoute of groupMenuItem.routes; let first = first" class="nav-item" [ngClass]="{'mt-4': first && firstGroup}">
|
||||||
<!-- {{ groupMenuRoute |json }} -->
|
|
||||||
<a class="new-dmp nav-link nav-row" *ngIf="groupMenuRoute.path !== '/contact-support' && groupMenuRoute.path !== '/co-branding' && groupMenuRoute.path !== '/feedback' && groupMenuRoute.path !== '/descriptions'" [routerLink]="[groupMenuRoute.path]" [ngClass]="{'dmp-tour': groupMenuRoute.path == '/plans'}">
|
<a class="new-dmp nav-link nav-row" *ngIf="groupMenuRoute.path !== '/contact-support' && groupMenuRoute.path !== '/co-branding' && groupMenuRoute.path !== '/feedback' && groupMenuRoute.path !== '/descriptions'" [routerLink]="[groupMenuRoute.path]" [ngClass]="{'dmp-tour': groupMenuRoute.path == '/plans'}">
|
||||||
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
||||||
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>
|
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import { Component, EventEmitter, OnInit, Output } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { MatButtonToggleChange } from "@angular/material/button-toggle";
|
import { MatButtonToggleChange } from "@angular/material/button-toggle";
|
||||||
import { Router } from "@angular/router";
|
|
||||||
import { Tenant } from "@app/core/model/tenant/tenant";
|
import { Tenant } from "@app/core/model/tenant/tenant";
|
||||||
import { AuthService } from "@app/core/services/auth/auth.service";
|
import { AuthService } from "@app/core/services/auth/auth.service";
|
||||||
import { PrincipalService } from "@app/core/services/http/principal.service";
|
import { PrincipalService } from "@app/core/services/http/principal.service";
|
||||||
|
import { TenantHandlingService } from "@app/core/services/tenant/tenant-handling.service";
|
||||||
import { BaseComponent } from "@common/base/base.component";
|
import { BaseComponent } from "@common/base/base.component";
|
||||||
import { BaseHttpParams } from "@common/http/base-http-params";
|
import { BaseHttpParams } from "@common/http/base-http-params";
|
||||||
import { InterceptorType } from "@common/http/interceptors/interceptor-type";
|
import { InterceptorType } from "@common/http/interceptors/interceptor-type";
|
||||||
import { KeycloakService } from "keycloak-angular";
|
import { Observable } from "rxjs";
|
||||||
import { Observable, from } from "rxjs";
|
|
||||||
import { takeUntil } from "rxjs/operators";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tenant-switch',
|
selector: 'app-tenant-switch',
|
||||||
|
@ -20,10 +18,9 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit {
|
||||||
tenants: Observable<Array<Tenant>>;
|
tenants: Observable<Array<Tenant>>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
|
||||||
private keycloakService: KeycloakService,
|
|
||||||
private principalService: PrincipalService,
|
private principalService: PrincipalService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
private tenantHandlingService: TenantHandlingService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +31,6 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.tenants = this.loadUserTenants(); //TODO
|
this.tenants = this.loadUserTenants(); //TODO
|
||||||
//this.tenantChange.emit(this.getCurrentLanguage())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUserTenants(): Observable<Array<Tenant>> {
|
loadUserTenants(): Observable<Array<Tenant>> {
|
||||||
|
@ -48,22 +44,7 @@ export class TenantSwitchComponent extends BaseComponent implements OnInit {
|
||||||
onTenantSelected(selectedTenant: MatButtonToggleChange) {
|
onTenantSelected(selectedTenant: MatButtonToggleChange) {
|
||||||
if (selectedTenant.value === undefined || selectedTenant.value === '') return;
|
if (selectedTenant.value === undefined || selectedTenant.value === '') return;
|
||||||
|
|
||||||
this.formSubmit(selectedTenant.value);
|
this.authService.selectedTenant(selectedTenant.value);
|
||||||
this.loadUser();
|
window.location.href = this.tenantHandlingService.getCurrentUrlEnrichedWithTenantCode(selectedTenant.value, true);
|
||||||
}
|
|
||||||
|
|
||||||
formSubmit(selectedTenant: string): void {
|
|
||||||
this.authService.selectedTenant(selectedTenant);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadUser(): void {
|
|
||||||
this.authService.prepareAuthRequest(from(this.keycloakService.getToken()), {})
|
|
||||||
.pipe(takeUntil(this._destroyed))
|
|
||||||
.subscribe(
|
|
||||||
() => {
|
|
||||||
this.authService.onAuthenticateSuccessReload();
|
|
||||||
},
|
|
||||||
(error) => this.authService.onAuthenticateError(error)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
<!-- {{ userCredentials | async | json }} -->
|
|
||||||
|
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div *ngIf="user | async as userProfile; else loading" class="user-profile">
|
<div *ngIf="user | async as userProfile; else loading" class="user-profile">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
@ -18,6 +17,7 @@ import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
|
||||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
|
||||||
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
||||||
|
import { TenantHandlingService } from '@app/core/services/tenant/tenant-handling.service';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
|
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
|
||||||
|
@ -28,16 +28,15 @@ import { FormValidationErrorsDialogComponent } from '@common/forms/form-validati
|
||||||
import { BaseHttpParams } from '@common/http/base-http-params';
|
import { BaseHttpParams } from '@common/http/base-http-params';
|
||||||
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
|
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
|
||||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
|
||||||
import * as moment from 'moment-timezone';
|
import * as moment from 'moment-timezone';
|
||||||
import { Observable, from, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { map, takeUntil } from 'rxjs/operators';
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
import { nameof } from 'ts-simple-nameof';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
import { AddAccountDialogComponent } from './add-account/add-account-dialog.component';
|
import { AddAccountDialogComponent } from './add-account/add-account-dialog.component';
|
||||||
import { UserProfileEditorModel } from './user-profile-editor.model';
|
import { UserProfileEditorModel } from './user-profile-editor.model';
|
||||||
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-profile',
|
selector: 'app-user-profile',
|
||||||
|
@ -81,7 +80,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public enumUtils: EnumUtils,
|
public enumUtils: EnumUtils,
|
||||||
private formBuilder: UntypedFormBuilder,
|
private formBuilder: UntypedFormBuilder,
|
||||||
private keycloakService: KeycloakService,
|
private tenantHandlingService: TenantHandlingService,
|
||||||
private principalService: PrincipalService,
|
private principalService: PrincipalService,
|
||||||
private formService: FormService,
|
private formService: FormService,
|
||||||
private referenceService: ReferenceService,
|
private referenceService: ReferenceService,
|
||||||
|
@ -281,7 +280,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
||||||
}, maxWidth: '30em'
|
}, maxWidth: '30em'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error => this.httpErrorHandlingService.handleBackedRequestError(error));
|
error => this.httpErrorHandlingService.handleBackedRequestError(error));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -309,7 +308,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => this.httpErrorHandlingService.handleBackedRequestError(error)); //TODO how to handle this
|
error => this.httpErrorHandlingService.handleBackedRequestError(error)); //TODO how to handle this
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -367,17 +366,9 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
|
||||||
if (this.tenantFormGroup.valid === false) return;
|
if (this.tenantFormGroup.valid === false) return;
|
||||||
|
|
||||||
const selectedTenant = this.tenantFormGroup.get('tenantCode').value;
|
const selectedTenant = this.tenantFormGroup.get('tenantCode').value;
|
||||||
this.formSubmit(selectedTenant);
|
|
||||||
this.loadUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
formSubmit(selectedTenant: string): void {
|
this.authService.selectedTenant(selectedTenant.value);
|
||||||
this.authService.selectedTenant(selectedTenant);
|
window.location.href = this.tenantHandlingService.getCurrentUrlEnrichedWithTenantCode(selectedTenant.value, true);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Preferences
|
//Preferences
|
||||||
|
|
|
@ -69,7 +69,8 @@
|
||||||
"DESCRIPTION-TEMPLATE-INACTIVE-USER": "This description template contains users that are not exist",
|
"DESCRIPTION-TEMPLATE-INACTIVE-USER": "This description template contains users that are not exist",
|
||||||
"DESCRIPTION-TEMPLATE-MISSING-USER-CONTACT-INFO": "This description template contains users that don't have contact info",
|
"DESCRIPTION-TEMPLATE-MISSING-USER-CONTACT-INFO": "This description template contains users that don't have contact info",
|
||||||
"DMP-INACTIVE-USER": "This plan contains users that are not exist",
|
"DMP-INACTIVE-USER": "This plan contains users that are not exist",
|
||||||
"DMP-MISSING-USER-CONTACT-INFO": "This plan contains users that don't have contact info"
|
"DMP-MISSING-USER-CONTACT-INFO": "This plan contains users that don't have contact info",
|
||||||
|
"DUPLICATE-DMP-USER": "You can't invite authors with same role and plan section more than once"
|
||||||
},
|
},
|
||||||
"FORM-VALIDATION-DISPLAY-DIALOG": {
|
"FORM-VALIDATION-DISPLAY-DIALOG": {
|
||||||
"WARNING": "Warning!",
|
"WARNING": "Warning!",
|
||||||
|
@ -257,7 +258,7 @@
|
||||||
"POLICY": "Cookies Policy"
|
"POLICY": "Cookies Policy"
|
||||||
},
|
},
|
||||||
"EMAIL-CONFIRMATION": {
|
"EMAIL-CONFIRMATION": {
|
||||||
"EXPIRED-EMAIL": "Mail invitation expired",
|
"EXPIRED-EMAIL": "Your mail invitation has expired, or you are not logged in with the correct account.",
|
||||||
"EMAIL-FOUND": "Email is already confirmed"
|
"EMAIL-FOUND": "Email is already confirmed"
|
||||||
},
|
},
|
||||||
"HOME": {
|
"HOME": {
|
||||||
|
@ -2221,7 +2222,8 @@
|
||||||
"MERGE-ACCOUNT": {
|
"MERGE-ACCOUNT": {
|
||||||
"TITLE": "Merge Your Account",
|
"TITLE": "Merge Your Account",
|
||||||
"MESSAGES": {
|
"MESSAGES": {
|
||||||
"CONFIRMATION": "Are you sure that you want to merge this account?"
|
"CONFIRMATION": "Are you sure that you want to merge this account?",
|
||||||
|
"INVALID-TOKEN": "Looks like your mail invitation has expired, or you are not logged in with the correct account. Please try logging in again or have the invitation re-sent."
|
||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"CONFIRM": "Confirm"
|
"CONFIRM": "Confirm"
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { AbstractControl, UntypedFormArray, UntypedFormGroup, ValidatorFn, Valid
|
||||||
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
||||||
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
|
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
|
||||||
import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type';
|
import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type';
|
||||||
|
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||||
|
|
||||||
export function BackendErrorValidator(errorModel: ValidationErrorModel, propertyName: string): ValidatorFn {
|
export function BackendErrorValidator(errorModel: ValidationErrorModel, propertyName: string): ValidatorFn {
|
||||||
return (control: AbstractControl): { [key: string]: any } => {
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
|
@ -29,6 +30,34 @@ export function CustomErrorValidator(errorModel: ValidationErrorModel, propertyN
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RequiredWithVisibilityRulesValidator(visibilityRulesService: VisibilityRulesService, visibilityRulesKey: string) {
|
||||||
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
|
|
||||||
|
if (visibilityRulesService.isVisibleMap[visibilityRulesKey] ?? true) {
|
||||||
|
return Validators.required(control);
|
||||||
|
}
|
||||||
|
control.setErrors(null);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UrlValidator() {
|
||||||
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
|
const urlRegex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/;
|
||||||
|
return Validators.pattern(urlRegex);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MinMaxValidator(min: number, max: number) {
|
||||||
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function DateValidator(): ValidatorFn {
|
export function DateValidator(): ValidatorFn {
|
||||||
return (control: AbstractControl): { [key: string]: any } => {
|
return (control: AbstractControl): { [key: string]: any } => {
|
||||||
if (control.value) {
|
if (control.value) {
|
||||||
|
@ -105,28 +134,28 @@ export function DmpBlueprintSystemFieldRequiredValidator(): ValidatorFn {
|
||||||
let foundTitle = false;
|
let foundTitle = false;
|
||||||
let foundDescription = false;
|
let foundDescription = false;
|
||||||
let foundLanguage = false;
|
let foundLanguage = false;
|
||||||
let foundAccess = false;
|
let foundAccess = false;
|
||||||
|
|
||||||
const sectionsFormArray = (control as UntypedFormArray);
|
const sectionsFormArray = (control as UntypedFormArray);
|
||||||
if (sectionsFormArray.controls != null && sectionsFormArray.controls.length > 0 ){
|
if (sectionsFormArray.controls != null && sectionsFormArray.controls.length > 0) {
|
||||||
sectionsFormArray.controls.forEach((section, index) => {
|
sectionsFormArray.controls.forEach((section, index) => {
|
||||||
const fieldsFormArray = section.get('fields') as UntypedFormArray;
|
const fieldsFormArray = section.get('fields') as UntypedFormArray;
|
||||||
if (fieldsFormArray && fieldsFormArray.length > 0){
|
if (fieldsFormArray && fieldsFormArray.length > 0) {
|
||||||
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Title)){
|
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Title)) {
|
||||||
foundTitle = true;
|
foundTitle = true;
|
||||||
}
|
}
|
||||||
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Description)){
|
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Description)) {
|
||||||
foundDescription = true;
|
foundDescription = true;
|
||||||
}
|
}
|
||||||
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Language)){
|
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.Language)) {
|
||||||
foundLanguage = true;
|
foundLanguage = true;
|
||||||
}
|
}
|
||||||
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.AccessRights)){
|
if (fieldsFormArray.controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === DmpBlueprintSystemFieldType.AccessRights)) {
|
||||||
foundAccess = true;
|
foundAccess = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return foundTitle && foundDescription && foundAccess && foundLanguage ? null : { 'dmpBlueprintSystemFieldRequired': true };
|
return foundTitle && foundDescription && foundAccess && foundLanguage ? null : { 'dmpBlueprintSystemFieldRequired': true };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,13 +43,13 @@ export class UnauthorizedResponseInterceptor extends BaseInterceptor {
|
||||||
this.authService.refreshToken().then((isRefreshed) => {
|
this.authService.refreshToken().then((isRefreshed) => {
|
||||||
this.accountRefresh$ = null;
|
this.accountRefresh$ = null;
|
||||||
if (!isRefreshed) {
|
if (!isRefreshed) {
|
||||||
this.logoutUser();
|
this.handleUnauthorized();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}).catch(x => {
|
}).catch(x => {
|
||||||
this.logoutUser();
|
this.handleUnauthorized();
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
).pipe(filter((x) => x));
|
).pipe(filter((x) => x));
|
||||||
|
@ -67,10 +67,10 @@ export class UnauthorizedResponseInterceptor extends BaseInterceptor {
|
||||||
return next.handle(newRequest);
|
return next.handle(newRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private logoutUser() {
|
private handleUnauthorized() {
|
||||||
if (!this.isLoginRoute() && !this.isSignupRoute()) {
|
if (!this.isLoginRoute() && !this.isSignupRoute()) {
|
||||||
this.authService.clear();
|
this.authService.clear();
|
||||||
this.router.navigate(['/unauthorized']);
|
this.router.navigate(['/unauthorized', { queryParams: { returnUrl: this.router.url } }]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
{description}
|
{description}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
<p>Dear {recipient},</p>
|
<p>Dear {recipient},</p>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
{description}
|
{description}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Simple Transactional Email</title>
|
<title>OpenCDMP Notification</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
GLOBAL RESETS
|
GLOBAL RESETS
|
||||||
|
|
Loading…
Reference in New Issue