How to create a constraint for image size upload
Question
How to add a constraint on a specific content type for an image?
Answer
You can define a custom validator. E.g. a validator for the image size:
In a custom module you can define a custom validator for any field:
When you have a custom definition of a type with an image field like:
[jnt:imageValTest] > jnt:content, jmix:editorialContent, jmix:structuredContent
- header (string)
- myImage (weakreference, picker[type='image'])
And you want to limit the upload size (selectable image size) for the myImage field, you have to define a constraint (in case of FileSize no default constraint exists):
package org.foo.modules.validators;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = FileSizeValidator.class)
@Documented
public @interface FileSize {
Class[] groups() default {};
long max();
String message() default "{javax.validation.constraints.file.size}";
long min();
Class[] payload() default {};
}
Note: Inside this Interface you have to define the error message, in the example it has the key
javax.validation.constraints.file.size.
Also define needed methods, in this case a min and max file size.
In the header you have to define a Validator class, where you can add the correct checks:
package org.foo.modules.validators;
import javax.jcr.RepositoryException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.jahia.services.content.JCRNodeWrapper;
public class FileSizeValidator implements ConstraintValidator {
private long max;
private long min;
@Override
public void initialize(FileSize constraintAnnotation) {
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
}
@Override
public boolean isValid(Object val, ConstraintValidatorContext arg1) {
MyImageValidator validator = null;
if(val != null && val instanceof MyImageValidator) {
validator = (MyImageValidator)val;
} else {
return false;
}
try {
JCRNodeWrapper imageNode = validator.getImageNode();
if (imageNode == null) { //if imageNode is null return false because no file is selected (return true if you allow empty field)
return false;
}
long contentLength = imageNode.getFileContent().getContentLength();
return (min <= 0 || contentLength >= min) && (max <= 0 || contentLength <= max);
} catch (RepositoryException e) {
// ...
}
return false;
}
}
In the next step, you need a JCRNodeValidator, for that define a class where you return the correct field which is used by the validator above:
package org.foo.modules.validators;
import javax.jcr.ItemNotFoundException;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFormatException;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.decorator.validation.JCRNodeValidator;
public class MyImageValidator implements JCRNodeValidator {
private JCRNodeWrapper node;
private JCRNodeWrapper imageNode;
public MyImageValidator(JCRNodeWrapper node) {
super();
this.node = node;
}
JCRNodeWrapper getImageNode()
throws ItemNotFoundException, ValueFormatException, PathNotFoundException, RepositoryException {
if (imageNode == null && node.getProperty("myImage") != null) {
imageNode = (JCRNodeWrapper) node.getProperty("myImage").getNode();
}
return imageNode;
}
@FileSize(min = 0, max = 1048576L)
public MyImageValidator getMyImage() {
return this;
}
}
Here you can use the defined constraint above @FileSize(min = 0, max = 1048576L) to define the correct limits.
And least you have to put the Validator in place for the correct content type. To do it, you have to define a JCRNodeValidatorDefinition bean (can be registered with OSGI):
package org.foo.modules.validators;
import java.util.Collections;
import java.util.Map;
import org.jahia.services.content.decorator.validation.JCRNodeValidatorDefinition;
import org.osgi.service.component.annotations.Component;
@Component(service = JCRNodeValidatorDefinition.class)
public class MyCustomValidators extends JCRNodeValidatorDefinition {
/**
* Register custom validator for generic content nodetype
*
* @return validators list
* @see org.foo.modules.validators.MyImageValidator
*/
@Override
public Map getValidators() {
return Collections.singletonMap("jnt:imageValTest", MyImageValidator.class);
}
}
Now, the module can be compiled. And the Validator will be in place for the
node type.
Another full example can be shown on GitHub issue, see the attached file