You are here: Home Blog An Image Dimension Validator for Plone 4+
Personal tools

An Image Dimension Validator for Plone 4+

Posted by daniel at Nov 26, 2013 01:20 PM |
An Image Dimension Validator for Plone 4+ that works with (the Diazo based) plone.app.contenttypes. This also generally demonstrates how to add validators to behaviours from plone.app.contenttypes

Update: Grok has fallen out of favour, so an alternative registration of the validator using ZCML is also included below

The Context for the problem

I had a need to add fixed size banner images through the CMS, and whilst weighing up how to do this, I discovered plone.app.contenttypes which is a rewrite of core plone content types in Dexterity.  This includes a "lead image" behaviour, which I figured I could tweak to suit my purposes (though I may end up doing this differently), so long as I could enforce the lead image size.  In addition, plone.app.contenttypes enables you to quickly add fields and behaviours to core content types (or duplicates thereof), so adding the "lead image" to Pages & Folders took a matter of minutes on the through the web interface; though of course you will want to export these settings into your product (probably your project policy product) too, which takes a wee bit longer.

I much prefer dexterity to archetypes, because of the cleaner, faster, more maintainable code - so as long as I could validate the image site, this was a "go" for me.

So how to set up a validator on existing fields?

I found the basic answer on Daniel Widerin's website, where he describes a Validator based on filesize.  This was a very helpful foundation for what I wanted to do.

My adaption to the above:

  • validates (very strictly) based on image size,
  • is encapsulated in one file because it wires in the validator using Grok rather than ZCML (though I hear that Grok is less favoured than I thought amongst Plonistas)
  • Validates on all objects having a Lead Image behaviour, except News Items - though there is a slight problem with this (described below)

 

 # -*- coding: utf-8 -*-
from plone.namedfile.interfaces import INamedBlobImageField
from plone.app.contenttypes.behaviors.leadimage import ILeadImage
from plone.app.contenttypes.interfaces import INewsItem

from zope.interface import Invalid
from z3c.form import validator
from five import grok

# Strict Dimensions check
REQUIRED_HEIGHT = 320
REQUIRED_WIDTH = 2000

# Filesize limit
MAXSIZE_KB = 200


class ImageDimensionsValidator(validator.FileUploadValidator):

    def validate(self, value):
        super(ImageDimensionsValidator, self).validate(value)

        if INewsItem.providedBy(self.context):
            return None

        if value:
            # See: plone.namedfile.file.NamedBlobImage
            width, height = value.getImageSize()
            
            if REQUIRED_HEIGHT and REQUIRED_WIDTH:
                if (width != REQUIRED_WIDTH or
                    height != REQUIRED_HEIGHT):
                    raise Invalid("Image has wrong dimensions - it should be %d x %d pixels (h x w)"
                                  ", but is %d x %d" %
                                  (REQUIRED_HEIGHT, REQUIRED_WIDTH, height, width))
            elif REQUIRED_HEIGHT:
                if (height != REQUIRED_HEIGHT):
                    raise Invalid("Image is the wrong height - it should be %d pixels" %
                                  (REQUIRED_HEIGHT))
            elif REQUIRED_WIDTH:
                if (width != REQUIRED_WIDTH):
                    raise Invalid("Image is the wrong width - it should be %d pixels" %
                                  (REQUIRED_WIDTH))

            # Lastly check filesize
            if value.getSize() > (MAXSIZE_KB * 1024):
                raise Invalid("Image filesize is too large. Maximum permitted: %dKB " % MAXSIZE_KB)
            


validator.WidgetValidatorDiscriminators(ImageDimensionsValidator,
                                        context=ILeadImage,
                                        field=ILeadImage['image'])
grok.global_adapter(ImageDimensionsValidator)

 

Alternative registration with ZCML

Grok has fallen out of fashion since I wrote this, so if you want to use ZCML to register the validator (and you probably should), then adding the following to the configure.zcml in the same directory should do enable you to delete the line containing grok.global_adapter (assuming you name the above file validator.py):

 <adapter 
    factory=".validator.ImageDimensionsValidator"  
    provides="z3c.form.interfaces.IValidator" />

 

Here's a link to the Gist

Issues

The single issue is that the test for News Items doesn't work when News Item's are being created, because the INewsItem interface isn't applied to the object at that stage. Let me know if you know how to fix it!

My solution to the above is likely to be to create my own behaviour to use instead of ILeadImage, which I did sort of misuse I guess.  Nonetheless I thought this was worth documenting before I move on.