Apache Forrest WikiRenderer
 
   

WoodySample

PDF
PDF

A simple Woody example

Here we will show how to create a form by showing a "registration form" example.

Here is a screenshot of the form we're going to create:

http://outerthought.net/~bruno/images/woody_registrationform_initial.png

This example is included in the Woody block, you may want to play with it first before reading the explanation below.

The form definition file

Below the form definition file is displayed. This lists all the widgets in the form, together with their configuration information.

<wd:form
  xmlns:wd="http://cocoon.apache.org/woody/definition/1.0"
  xmlns:i18n="http://apache.org/cocoon/i18n/2.1">

  <wd:field id="name" required="true">
    <wd:label>Name:</wd:label>
    <wd:datatype base="string">
      <wd:validation>
        <wd:length min="2"/>
      </wd:validation>
    </wd:datatype>
  </wd:field>

  <wd:field id="email" required="true">
    <wd:label>Email address:</wd:label>
    <wd:datatype base="string">
      <wd:validation>
        <wd:email/>
      </wd:validation>
    </wd:datatype>
  </wd:field>

  <wd:field id="age">
    <wd:label>Your age:</wd:label>
    <wd:datatype base="long">
      <wd:validation>
        <wd:range min="0" max="150"/>
      </wd:validation>
    </wd:datatype>
  </wd:field>

  <wd:field id="password" required="true">
    <wd:label>Password:</wd:label>
    <wd:datatype base="string">
      <wd:validation>
        <wd:length min="5" max="20"/>
      </wd:validation>
    </wd:datatype>
  </wd:field>

  <wd:field id="confirmPassword" required="true">
    <wd:label>Re-enter password:</wd:label>
    <wd:datatype base="string">
      <wd:validation>
        <wd:assert test="password = confirmPassword">
          <wd:failmessage>The two passwords are not equal.</wd:failmessage>
        </wd:assert>
      </wd:validation>
    </wd:datatype>
  </wd:field>

  <wd:booleanfield id="spam">
    <wd:label>Send me spam</wd:label>
  </wd:booleanfield>

</wd:form>

All elements are in the Woody Definition (wd) namespace.

The children of the wd:form element identify widgets. As you can see, most of the widgets are "field" widgets. The "field" widget is the most important widget in Woody, so lets look at it in a bit more detail.

Field widget

A field widget can be associated with a datatype. The function of the datatype is to convert the string value entered by the user to a more specific type like a number or a date (and vice versa, convert them back to strings). The datatype will also perform the validation. (This split-up between "widget" and "datatype" is specific for the field widget -- it is perfectly possible to make widgets that have nothing to do with datatypes). In this way, a field widget contains strongly-typed data. For example, if you associated a "long" datatype with a field widget, then you can be sure that when you retrieve the widget's value, you will get a Long object (that is, if the form was validated successfully).

The reasoning behind the "base" attribute on the wd:datatype element is that you are actually defining a new datatype, based on the built-in "string" or "long" datatype, and customise it with validation rules (and possibly other parameters).

A datatype can furthermore be associated with a selection list. This makes that the field widget could be rendered either as a textbox or a list, depending on whether its datatype has a selection list. For an example of selection lists, see the "Form1" example provided with Woody.

http://outerthought.net/~bruno/images/field_datatype_relation.png

If we wouldn't make this datatype and selection list associations, we would need to create specific widgets for each possible combination: StringField, LongField, DateField, StringSelectionList, LongSelectionList, ...

Labels

A nice feature is that the wd:label tags can contain mixed content. On the one hand, this can be used to provide rich formatting in the label. But it also enables you to put i18n-elements in there, to be interpreted by the I18nTransformer. This way, internationalisation is done using standard Cocoon techniques.

The sitemap

Here is the relevant sitemap fragment for handling the registration form:

<map:match pattern="registration">
  <map:select type="request-method">
    <map:when test="GET">
      <map:act type="woody-make-form">
        <map:parameter name="form-definition" value="forms/registration.xml"/>
        <map:parameter name="attribute-name" value="registrationform"/>
      </map:act>
    </map:when>
    <map:otherwise>
      <map:act type="woody-handle-form-submit">
        <map:parameter name="form-definition" value="forms/registration.xml"/>
        <map:parameter name="attribute-name" value="registrationform"/>

        <map:generate type="serverpages" src="forms/registration_success.xsp"/>
        <map:serialize/>
      </map:act>
    </map:otherwise>
  </map:select>

  <map:generate src="forms/registration_template.xml"/>
  <map:transform type="woody">
    <map:parameter name="attribute-name" value="registrationform"/>
  </map:transform>
  <map:transform type="i18n">
    <map:parameter name="locale" value="en-US"/>
  </map:transform>
  <map:transform src="xsl/html/woody-default.xsl"/>
  <map:transform type="i18n">
    <map:parameter name="locale" value="en-US"/>
  </map:transform>
  <map:serialize/>
</map:match>

It works as follows:

  • based on the request method (GET or POST), we decide whether this is a first-time request of the form or whether this is a form submit
  • in case of a first-time request, the "woody-make-form" action is used, which will simply create a form instance and put it in a request attribute (this is a storage area that lasts for the duration of the request)
  • in case of a form submit, we use the "woody-handle-form-submit" action. This action will also create a form instance, and let it process the request and validate it. If validation was successful, it returns non-null (so that the contents of the map:act element will be executed), otherwise it returns null (so that the pipeline below the map:act will be executed).
  • in case of successful validation, we simply execute an XSP page. In "real-life" situations, this may also call an action, do a redirect, or whathever.
  • in case validation failed, or in case the form is displayed for the first time, the pipeline below the selector is executed. How this works is described in the next section.

The WoodyTemplateTransformer

The publishing pipeline for the form starts from a template file. Here's the template for our registration form example:

<html xmlns:wt="http://cocoon.apache.org/woody/template/1.0">
  <head>
    <title>Registration form</title>
  </head>
  <body>
    <h1>Registration</h1>
    <form action="registration" method="POST">
      <wt:widget-label id="name"/>
      <wt:widget id="name"/>
      <br/>
      <wt:widget-label id="email"/>
      <wt:widget id="email"/>
      <br/>
      <wt:widget-label id="age"/>
      <wt:widget id="age"/>
      <br/>
      <wt:widget-label id="password"/>
      <wt:widget id="password"/>
      <br/>
      <wt:widget-label id="confirmPassword"/>
      <wt:widget id="confirmPassword"/>
      <br/>
      <wt:widget id="spam"/>
      <wt:widget-label id="spam"/>
      <br/>

      <input type="submit"/>
    </form>
  </body>
</html>

The Woody-specific elements here are in the "Woody Template" (wt) namespace.

The wt:widget-label tag will cause the label of a widget to be inserted at the location of the tag. The wt:widget tag will cause the XML representation of a widget to be inserted at the location of that tag. The inserted XML will be in the "Woody Instance" (wi) namespace.

The XML representation of the widget will then be translated to HTML by an XSLT stylesheet (woody-default.xsl in our case). This XSLT only has to handle individual widgets, and not the page as a whole, and is thus not specific for one form but can be reused across forms.

For certain widgets it may be necessary to provide extra presentation hints, such as the width of a text box, the style of a selection list (drop down, radio buttons, ...) or class and style attribute values. This can be done by putting extra elements inside the wt:widget element. Currently there's not much implemented yet, but what's available will be documented in a reference section.

As an alternative to the template approach, you could also use the WoodyGenerator, which will generate an XML representation of the whole form, and style that with a custom-written XSLT.