diff --git a/custom-definitions/README.md b/custom-definitions/README.md index c441874..7e06bf7 100644 --- a/custom-definitions/README.md +++ b/custom-definitions/README.md @@ -11,6 +11,7 @@ - MimeType - NodeType - Template + - [DateDependency](src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/README.md) ## Configuration diff --git a/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidator.java b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidator.java new file mode 100644 index 0000000..4a865af --- /dev/null +++ b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidator.java @@ -0,0 +1,38 @@ +package com.merkle.oss.magnolia.definition.custom.validator.datedependency; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.Temporal; + +import com.vaadin.data.ValidationResult; +import com.vaadin.data.ValueContext; +import com.vaadin.data.validator.AbstractValidator; + +public class DateDependencyValidator extends AbstractValidator { + private final DateDependencyValidatorDefinition definition; + + public DateDependencyValidator( + final DateDependencyValidatorDefinition definition, + final String errorMessage + ) { + super(errorMessage); + this.definition = definition; + } + + @Override + public ValidationResult apply(final Temporal value, final ValueContext context) { + return toResult(value, isValid(value)); + } + + private boolean isValid(final Temporal value) { + definition.getState().update(definition.getPropertyName(), convert(value)); + return definition.getValidator().test(definition.getState()); + } + + private LocalDateTime convert(final Temporal value) { + if(value instanceof LocalDate) { + return ((LocalDate)value).atStartOfDay(); + } + return LocalDateTime.from(value); + } +} \ No newline at end of file diff --git a/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinition.java b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinition.java new file mode 100644 index 0000000..b1d531c --- /dev/null +++ b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinition.java @@ -0,0 +1,66 @@ +package com.merkle.oss.magnolia.definition.custom.validator.datedependency; + +import info.magnolia.i18nsystem.I18nable; +import info.magnolia.ui.field.ConfiguredFieldValidatorDefinition; +import info.magnolia.ui.field.FieldValidatorFactory; +import info.magnolia.ui.field.ValidatorType; + +import java.time.LocalDateTime; +import java.time.temporal.Temporal; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +import javax.annotation.Nullable; + +import com.merkle.oss.magnolia.definition.key.generator.FieldValidatorDefinitionKeyGenerator; + +@I18nable(keyGenerator = FieldValidatorDefinitionKeyGenerator.class) +@ValidatorType("dateDependency") +public class DateDependencyValidatorDefinition extends ConfiguredFieldValidatorDefinition { + private final State state; + private final String propertyName; + private final Predicate validator; + + DateDependencyValidatorDefinition(final State state, final String propertyName, final boolean i18n, final Predicate validator) { + this.state = state; + this.propertyName = propertyName; + this.validator = validator; + state.propertyNameI18nMapping.put(propertyName, i18n); + } + + @Override + public Class> getFactoryClass() { + return DateDependencyValidatorFactory.class; + } + + public State getState() { + return state; + } + + public String getPropertyName() { + return propertyName; + } + + public Predicate getValidator() { + return validator; + } + + public static class State { + private final Map propertyNameI18nMapping = new HashMap<>(); + private final Map dates = new HashMap<>(); + + void update(final String propertyName, @Nullable final LocalDateTime LocalDateTime) { + dates.put(propertyName, LocalDateTime); + } + + Map getPropertyNameI18nMapping() { + return propertyNameI18nMapping; + } + + public Optional get(final String propertyName) { + return Optional.ofNullable(dates.get(propertyName)); + } + } +} \ No newline at end of file diff --git a/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinitionBuilder.java b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinitionBuilder.java new file mode 100644 index 0000000..d2315e5 --- /dev/null +++ b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorDefinitionBuilder.java @@ -0,0 +1,38 @@ +package com.merkle.oss.magnolia.definition.custom.validator.datedependency; + +import info.magnolia.ui.field.DateFieldDefinition; + +import java.util.function.Predicate; + +import com.merkle.oss.magnolia.definition.builder.validator.AbstractConfiguredFieldValidatorDefinitionBuilder; +import com.merkle.oss.magnolia.definition.custom.validator.datedependency.DateDependencyValidatorDefinition.State; + +public class DateDependencyValidatorDefinitionBuilder extends AbstractConfiguredFieldValidatorDefinitionBuilder { + private final Predicate validator; + private final State state = new State(); + + public DateDependencyValidatorDefinitionBuilder(final Predicate validator) { + this.validator = validator; + } + + public DateDependencyValidatorDefinition build(final String propertyName) { + return build(propertyName, false); + } + public DateDependencyValidatorDefinition build(final String propertyName, final boolean i18n) { + return build("dateDependencyValidator", propertyName, i18n); + } + public DateDependencyValidatorDefinition build(final String name, final String propertyName) { + return build(name, propertyName, false); + } + public DateDependencyValidatorDefinition build(final String name, final String propertyName, final boolean i18n) { + final DateDependencyValidatorDefinition definition = new DateDependencyValidatorDefinition(state, propertyName, i18n, validator); + super.populate(definition, name); + return definition; + } + + public DateFieldDefinition buildAndAdd(final DateFieldDefinition dateField) { + final DateDependencyValidatorDefinition validator = build(dateField.getName(), dateField.isI18n()); + dateField.getValidators().add(validator); + return dateField; + } +} diff --git a/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorFactory.java b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorFactory.java new file mode 100644 index 0000000..063439d --- /dev/null +++ b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/DateDependencyValidatorFactory.java @@ -0,0 +1,60 @@ +package com.merkle.oss.magnolia.definition.custom.validator.datedependency; + +import info.magnolia.ui.ValueContext; +import info.magnolia.ui.editor.LocaleContext; +import info.magnolia.ui.field.AbstractFieldValidatorFactory; + +import java.time.LocalDateTime; +import java.time.temporal.Temporal; +import java.util.Optional; + +import javax.inject.Inject; +import javax.jcr.Item; +import javax.jcr.Node; + +import com.merkle.oss.magnolia.powernode.PowerNode; +import com.merkle.oss.magnolia.powernode.PowerNodeService; +import com.merkle.oss.magnolia.powernode.ValueConverter; +import com.vaadin.data.Validator; + +public class DateDependencyValidatorFactory extends AbstractFieldValidatorFactory { + private final PowerNodeService powerNodeService; + private final ValueContext valueContext; + private final LocaleContext localeContext; + + @Inject + public DateDependencyValidatorFactory( + final PowerNodeService powerNodeService, + final ValueContext valueContext, + final LocaleContext localeContext, + final DateDependencyValidatorDefinition definition + ) { + super(definition); + this.powerNodeService = powerNodeService; + this.valueContext = valueContext; + this.localeContext = localeContext; + } + + @Override + public Validator createValidator() { + valueContext.getSingle() + .filter(Item::isNode) + .map(Node.class::cast) + .map(powerNodeService::convertToPowerNode) + .ifPresent(this::updateState); + return new DateDependencyValidator(definition, getI18nErrorMessage()); + } + + private void updateState(final PowerNode item) { + definition.getState().getPropertyNameI18nMapping().forEach((propertyName, i18n) -> + definition.getState().update(propertyName, get(item, propertyName, i18n).orElse(null)) + ); + } + + private Optional get(final PowerNode item, final String propertyName, final boolean i18n) { + if(i18n) { + return item.getProperty(propertyName, localeContext.getCurrent(), ValueConverter::getLocalDateTime); + } + return item.getProperty(propertyName, ValueConverter::getLocalDateTime); + } +} diff --git a/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/README.md b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/README.md new file mode 100644 index 0000000..68eecfe --- /dev/null +++ b/custom-definitions/src/main/java/com/merkle/oss/magnolia/definition/custom/validator/datedependency/README.md @@ -0,0 +1,29 @@ +# DateDependency Validator +Validator to define dependencies between multiple date fields. E.g. endDate must be after startDate. + +## Usage +### Dialog +```java +import info.magnolia.ui.field.EditorPropertyDefinition; +import info.magnolia.module.blossom.annotation.TabFactory; +import com.merkle.oss.magnolia.definition.builder.simple.DateFieldDefinitionBuilder; +import com.merkle.oss.magnolia.definition.custom.validator.datedependency.DateDependencyValidatorDefinitionBuilder; + +@TabFactory("someTab") +public List someTab() { + final DateDependencyValidatorDefinitionBuilder dateDependencyValidatorBuilder = new DateDependencyValidatorDefinitionBuilder(state -> + state.get("startDate").flatMap(startDate -> + state.get("endDate").map(startDate::isBefore) + ).orElse(true) + ); + return List.of( + //Option1 specifying dateField name & i18n + new DateFieldDefinitionBuilder().validator(dateDependencyValidatorBuilder.build("startDate")).build("startDate"), + new DateFieldDefinitionBuilder().i18n().validator(dateDependencyValidatorBuilder.build("endDate", true)).build("endDate") + + //Option2 Taking name & i18n from DateFieldDefinition +// dateDependencyValidatorBuilder.buildAndAdd(new DateFieldDefinitionBuilder().build("startDate")), +// dateDependencyValidatorBuilder.buildAndAdd(new DateFieldDefinitionBuilder().i18n().build("endDate")) + ); +} +``` \ No newline at end of file