Create a Custom Validator - BloomReach Experience - Open Source CMS
10-07-2019

Create a Custom Validator

This page applies to Bloomreach Experience Manager 13.3 and later. For version 13.2 and older, please refer to the 12.x documentation.

Introduction

Goal

Develop a custom validator to use in your document types.

Background

When defining a document type, validators can be attached to its primitive fields, compound fields, and the document type itself to make sure their values satisfy certain requirements. Bloomreach Experience Manager provides a number of validators out-of-the-box. If the out-of-the-box validators are insufficient, developers can create their own validators as explained on this page.

Validation only works for document types with workflow defined, i.e. of type hippostdpubwf:document

Choose a Scope

Validators can have field scope, compound scope, or document scope. The table below summarized for each scope what is validated, where violations are reported, and at what level the validator can be configured:

Validator Scope

Validates

Reports

Configured

field

value of one field

below the field

per field

compound

values of multiple fields in a compound

above the compound

per field or compound type

document

values of multiple fields in a document

above the document (field highlighting not supported)

per document type

Create a Validator Class

Create a validator class and make it extend org.onehippo.cms.services.validation.api.Validator.

As an example, see the out-of-the-box field-scoped RegExpValidator class:

public class RegExpValidator implements Validator<String> {

    private static final String PATTERN_KEY = "regexp.pattern";
    private final Pattern pattern;

    public RegExpValidator(final Node config) {
        try {
            pattern = Pattern.compile(config.getProperty(PATTERN_KEY).getString());
        } catch (RepositoryException e) {
            throw new ValidationContextException("Cannot read required property '" 
                    + PATTERN_KEY + "'", e);
        }
    }

    public Optional<Violation> validate(final ValidationContext context, final String value) {
        if (pattern.matcher(value).find()) {
            return Optional.empty();
        }

        return Optional.of(context.createViolation());
    }
}

The API requires to implement the following methods:

  • constructor: must be public, so the system can instantiate the class via reflection. The constructor can either accept no parameters, or a single parameter of type javax.jcr.Node that holds validator-specific configuration.

  • validate: performs the validation and returns a violation when errors are found. The type of the validated value should match with the JCR-level type (StringDate, Long, etc.). Compound fields and documents get a value of type javax.jcr.Node. You can retrieve all kinds of information from the context parameter, for example:
    • JCR name (e.g. "myproject:title")
    • JCR Type (e.g. "String" or "myproject:mycompound")
    • Type (e.g. "Text", which is a special sub-type of "String")
    • User locale (e.g. "en")
    • User time zone (e.g. "Europe/Amsterdam")
    • Parent node of the value (e.g. the node for the compound it's part of)
    • Document node
The validate method must be thread-safe.

Configure a Validator

To register a validator, add a new hipposys:moduleconfig node to the validation service configuration, located at /hippo:configuration/hippo:modules/validation/hippo:moduleconfig.

Best practice: use the project namespace in the name of the validator. This will make it easier for you to distinguish between default product validators and project validators.

Example of the email validator:

/hippo:configuration/hippo:modules/validation/hippo:moduleconfig/myproject:email:
  jcr:primaryType: hipposys:moduleconfig
  hipposys:className: org.onehippo.cms.services.validation.validator.RegExpValidator
  regex.pattern: ^[a-zA-Z0-9._%+-][email protected][a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$

Add a Validator to (a Field in) a Document Type

To add a validator at field level (either primitive or compound), find the document type and the field you'd like to validate in the relevant namespace definition at /hippo:namespaces/[namespace]/[documenttype]/hipposysedit:nodetype/hipposysedit:nodetype/[fieldname].

Add the node name of the validator configuration as a value to the multi-valued hipposysedit:validators property.

The example below is for an 'Email' field of an 'author' document type:

/hippo:namespaces/myproject/author/hipposysedit:nodetype/hipposysedit:nodetype/email:
  jcr:primaryType: hipposysedit:field
  hipposysedit:mandatory: false
  hipposysedit:multiple: false
  hipposysedit:ordered: false
  hipposysedit:path: myproject:email
  hipposysedit:primary: false
  hipposysedit:type: String
  hipposysedit:validators: [myproject:email]

To add a validator at document type level, add the node name of the validator configuration as a value to the multi-valued hipposysedit:validators property to /hippo:namespaces/[namespace]/[documenttype]/hipposysedit:nodetype/hipposysedit:nodetype.

Configure Validation Messages

The last step is to create a validation message. Validator messages are stored as repository resource bundles at /hippo:configuration/hippo:translations/hippo:cms/validators/.

For each of the languages in your project, add a translation property with the same name as the validator configuration with the appropriate violation message to the resource bundle of the target language. For example:

/en:
  jcr:primaryType: hipposys:resourcebundle
  myproject:email: Enter a valid email address
  escaped: 'Do not use these characters: < > & '' "'
  image-references: Select an image
  non-empty: Enter a value

As can be seen in the example above, validation messages generally have the form of an imperative sentence such as "Enter a valid email address". For consistency, it is recommended you do the same in your custom validators.

Note that it is possible to define multiple violation messages for a validator, in which case the property name is composed as <validator-name>#<subKey> such as in the example below:

/en:
  jcr:primaryType: hipposys:resourcebundle
  myproject:customvalidator: Custom validation message
  myproject:customvalidator#alternate: Alternate validation message

Note that within a validator class, it's possible to access translated violations through the validation context using the context.createViolation() method and access alternate violations using the context.createViolation(String subKey) method.

Verify That the Custom Validator Works Correctly

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?