Exercise 7: Using a pattern in a z3c form widget

Warning

This exercise requires a working buildout using a fork of the collective.jstraining package.

This exercise will go through adding a widget that checks the minimum size of an image before it is uploaded.

We will be working in the exercise7 directory of the collective.jstraining package.

Add your JavaScript file

First off, in your exercise7/static directory, add a file named script.js. Use this file to do anything you would like to the page:

/* global require, FileReader, Image */

require([
  'jquery',
  'mockup-patterns-base',
], function($, Base) {
  'use strict';

  Base.extend({
    name: 'exercise7',
    trigger: '.pat-exercise7',
    parser: 'mockup',
    defaults: {
      minHeight: 200,
      minWidth: 200
    },
    init: function() {
      var that = this;

      that.$el.on('change', function(){
        if(this.files.length === 0){
          return;
        }

        var fr = new FileReader();
        fr.onload = function() {
          var img = new Image();
          img.onload = function() {
            if(img.width < that.options.minWidth ||
               img.height < that.options.minHeight){
              alert('Invalid image size. The image must be at least ' +
                    that.options.minWidth + 'x' + that.options.minHeight + '.');
              that.$el[0].value = '';
            }
          };
          img.src = fr.result;
        };
        fr.readAsDataURL(this.files[0]);
      });
    }
  });

});

This pattern simply has minWidth and minHeight options and when a file is selected for upload, will check to make sure it is a valid size.

Register static resource directory

Next, let’s register the static directory we just placed our script into. To register, you need to add ZCML registration for the static directory your script is in. Add this to the exercise7/configure.zcml file:

<plone:static
     directory="static"
     type="plone"
     name="exercise7"
     />

Register JavaScript resource

Register our script as a JavaScript resource with Plone. In the exercise7/profiles/default/registry.xml file, add configuration to register your script:

<records prefix="plone.resources/exercise7"
         interface='Products.CMFPlone.interfaces.IResourceRegistry'>
    <value key="js">++plone++exercise7/script.js</value>
</records>

Create a custom widget

Our custom widget will apply to all lead images. Add a file widget.py to your exercise7 directory with the follow contents:

from .interfaces import IExercise7Layer
from .interfaces import IMinSizeImageWidget
from plone.app.contenttypes.behaviors.leadimage import ILeadImage
from plone.formwidget.namedfile.widget import NamedImageWidget
from Products.CMFPlone.resources import add_resource_on_request
from z3c.form.interfaces import IFieldWidget
from z3c.form.util import getSpecification
from zope.component import adapter
from zope.interface import implementer
from zope.interface import implements

import json
import z3c.form.widget


class MinSizeImageWidget(NamedImageWidget):
    """A widget for a named file object
    """
    implements(IMinSizeImageWidget)

    def pattern_options(self):
        # provide the pattern options
        return json.dumps({
            'minHeight': 300,
            'minWidth': 300
        })

    def render(self):
        # add the registered resource
        add_resource_on_request(self.request, 'exercise7')
        return super(MinSizeImageWidget, self).render()


@adapter(getSpecification(ILeadImage['image']), IExercise7Layer)
@implementer(IFieldWidget)
def LeadImageMinSizeImageFieldWidget(field, request):
    widget = z3c.form.widget.FieldWidget(field, MinSizeImageWidget(request))
    return widget

Notice in the render method we utilize the add_resource_on_request function to load our pattern.

The code for image_widget.pt is already provided for this example since it is quite long. Review the file and notice where we are passing the value from the pattern_options method into our widget.

Register widget customization

Next, we need to register our custom widget so it is used. In your configure.zcml file, add the following:

<adapter factory=".widget.LeadImageMinSizeImageFieldWidget" />
<z3c:widgetTemplate
  mode="input"
  widget=".interfaces.IMinSizeImageWidget"
  layer=".interfaces.IExercise7Layer"
  template="image_widget.pt"
  />

Installation

  1. Start up your Plone instance
  2. Install the Exercise 7 add-on

Now, try to add/edit a lead image to content on the site.

Warning

To make sure your resource registry configuration changes apply, you’ll need to be in development mode. You can also toggle development mode on and off, click save, to force configuration to be re-built after changes instead of keeping development mode on.