Apache » Cocoon »

The Apache Cocoon Project

# Cocoon Forms 1.0

- Cocoon 2.2
- Core 2.2
- Blocks 2.2
- Maven Plugins 2.2
- Subprojects

# A Simple Example

# A simple CForms example

In this example we will show how to create a simple registration form using CForms and flowscript. We will follow to following steps:

1. Create a block for this sample
2. Create a form definition file
3. Create a template file
4. Write a bit of flowscript
5. Add some pipelines to the sitemap

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

# Create a block

This has been covered in the tutorial, but it won't hurt to recall this Maven goal:

    mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create
    -DarchetypeGroupId=org.apache.cocoon
    -DarchetypeArtifactId=cocoon-22-archetype-block
    -DarchetypeVersion=1.0.0-RC2 -DgroupId=com.mycompany
    -DartifactId=simple-sample

Note: As of the writing of this page, the Maven archetype plugin version has to be specified, due to a bug in the current release of it.
Edit the `simple-sample/pom.xml` file and add the support for Cocoon Forms:

    <dependency>
          <groupId>org.apache.cocoon</groupId>
          <artifactId>cocoon-forms-impl</artifactId>
          <version>1.0.0-RC1</version>
    </dependency>

Move in the `simple-sample` directory and build the block to ensure the playing field is set appropriately.

    mvn install

To access resources hold in the fomrs-impla and ajax-impl blocks, the Servlet Service Framework has to be instructed to fine them. Hence, open `src/main/resources/META-INF/cocoon/spring/servlet-service.xml` and add the following in the`<servlet:context>` element:

    <servlet:connections>
      <entry key="ajax" value-ref="org.apache.cocoon.ajax.impl.servlet"/>
      <entry key="forms" value-ref="org.apache.cocoon.forms.impl.servlet"/>
    </servlet:connections>

# Create a form definition file

Below the form definition file is displayed, `registration_definition.xml` (to be put under `src/main/resources/COB-INF/resource/internal` directory). This lists all the widgets in the form, together with their configuration information:

    <?xml version="1.0" encoding="UTF-8"?>
    <fd:form
      xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">

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

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

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

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

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

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

    </fd:form>

All elements are in the Forms Definition namespace: fd.

Every definition file has a `<fd:form>` element as the root element.

The child widgets of the form are defined inside the `<fd:widgets>` element. As you can see, most of the widgets are field widgets. The field widget is the most important widget in CForms. It is very flexible because it can be associated with different datatypes and with a selection list. See the reference docs for more information on this and other widgets.

A nice feature is that the `fd: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.

# Create a template file

Here is the template for our registration form example, `registration_template.xml` (to be put under `src/main/resources/COB-INF/resource/internal` directory):

    <?xml version="1.0" encoding="UTF-8"?>
    <html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
      xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
      xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">

      <jx:import uri="servlet:forms:/resource/internal/generation/jx-macros.xml"/>

      <head>
        <title>Registration form</title>
      </head>
      <body>
        <h1>Registration</h1>
        <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST">
          <ft:widget-label id="name"/>
          <ft:widget id="name"/>
          <br/>
          <ft:widget-label id="email"/>
          <ft:widget id="email"/>
          <br/>
          <ft:widget-label id="age"/>
          <ft:widget id="age"/>
          <br/>
          <ft:widget-label id="password"/>
          <ft:widget id="password">
            <fi:styling type="password"/>
          </ft:widget>
          <br/>
          <ft:widget-label id="confirmPassword"/>
          <ft:widget id="confirmPassword">
            <fi:styling type="password"/>
          </ft:widget>
          <br/>
          <ft:widget id="spam"/>
          <ft:widget-label id="spam"/>
          <br/>

          <input type="submit"/>
        </ft:form-template>
      </body>
    </html>

The CForms-specific elements here are in the "Forms Template" namespace: ft.

First, jx:import is used to import the CForms macros which execute the elements in the ft namespace.

The `<ft:widget-label>` tag will cause the label of a widget to be inserted at the location of the tag. The `<ft: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 "Forms Instance" namespace: fi.

The XML representation of the widget will then be translated to HTML by an XSLT stylesheet (forms-samples-styling.xsl in our case -- see sitemap snippets below). 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 a fi:styling element inside the ft:widget element. This element is in the fi namespace because it will be copied literally. The attributes and/or content of the fi:styling element depend on what is supported by the particular stylesheet used.

As an alternative to the template approach, you could also use the FormsGenerator, which will generate an XML representation of the whole form, and style that with a custom-written XSLT. For most users we recommend the template approach though.

# Write a bit of flowscript

Flowscript is Cocoon's solution to handling the flow of a web interaction. It is based on the concept of continuations. If you don't know yet about continuations and flowscript, learn about it here.

Here's the flowscript for our example, `registration.js` (put it under `src/main/resources/COB-INF/flow`):

    cocoon.load("servlet:forms:/resource/internal/flow/javascript/Form.js");

    function registration() {
        var form = new Form("cocoon://resource/internal/registration_definition.xml");

        form.showForm("registration-display-pipeline");

        var viewData = { "username" : form.getChild("name").getValue() }
        cocoon.sendPage("registration-success-pipeline", viewData);
    }

The flowscript works as follows:

First we create a `Form` object, specifying the form definition file to be used. The Form object is actually a javascript wrapper around the "real" Java form instance object.

Then the `showForm` function is called on the form object. This will (re)display the form to the user until validation of the form succeeded. As parameter to the showForm function, we pass the sitemap pipeline to be used to display the form.

Finally we get some data from the form (the entered name), and call a sitemap pipeline to display this data. This pipeline is based on the JXTemplate generator.

# Add some pipelines to the sitemap

The sitemap.xmap has to be modified as well:

First, an i18n dictionary has to be definied:

      <map:components>
        <map:transformers default="xslt">
          <map:transformer name="i18n"
            src="org.apache.cocoon.transformation.I18nTransformer">
            <catalogues default="forms">
              <catalogue id="forms" name="messages"
                location="servlet:forms:/resource/internal/i18n"/>
            </catalogues>
            <cache-at-startup>true</cache-at-startup>
          </map:transformer>
        </map:transformers>
      </map:components>

Then some pipelines to process the form:

        <!--
          Registration form pipeline
        -->
        <map:pipeline>

          <map:match pattern="registration">
            <map:call function="registration"/>
          </map:match>

          <map:match pattern="*.continue">
            <map:call continuation="{1}"/>
          </map:match>

          <map:match pattern="registration-display-pipeline">
            <map:generate type="jx" src="resource/internal/registration_template.xml"/>
            <map:transform type="i18n">
              <map:parameter name="locale" value="en"/>
            </map:transform>
            <map:transform src="resource/internal/forms-samples-styling.xsl">
              <map:parameter name="forms-resources" value="{servlet:forms:/resource/external/forms}"/>
              <map:parameter name="dojo-resources" value="{servlet:ajax:/resource/external/dojo}"/>
            </map:transform>
            <map:serialize/>
          </map:match>

          <map:match pattern="registration-success-pipeline">
            <map:generate type="jx" src="resource/internal/registration_success.jx"/>
            <map:serialize/>
          </map:match>
        </map:pipeline

Note the following things:

- The i18n transformer is configured so it knows about the forms messages. The forms catalogue does not have to be the default one, but here it is the only one and thus the default.
- In the `map:flow` tag our flowscript file is NOT declared, since Cocoon loads automatically what's in the `flow` directory.
- Then we have the pipelines:

- The first two are for managing the flowscript: when someone hits the registration URL, we call the registration function in our flowscript
- When a form is submitted, it will be matched by the second matcher, *.continue, which will continue the execution of the flowscript.
- The third matcher is for displaying the form. Note the JXTemplate generator is used so that the form template tags get interpreted.
- The fourth pipeline is for showing the "success" page, again using the JXTemplate generator, the contents of the registration_succcess.jx file is given below.
- The last one is for making default CForms resources available, such as javascript libraries, CSS files and images.
- Resources (XSL files, CSS, images, etc.) defined in the forms-impl and ajax-impl blocks are provided to this block by the Servlet Service Framework (look at the paramters given to `forms-samples-styling.xsl`).

As promised, here is the content of the `registration_success.jx` file (add it to ``src/main/resources/COB-INF/resource/internal)``:

    <?xml version="1.0"?>
    <html>
      <head>
        <title>Registration successful</title>
      </head>
      <body>
        Registration was successful for ${username}!
      </body>
    </html>

One last thing you need to do is to add an XSLT file with this content (add it to ``src/main/resources/COB-INF/resource/internal`` and name it `forms-samples-styling.xsl`):

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:include href="servlet:forms:/resource/internal/xsl/forms-page-styling.xsl"/>
      <xsl:include href="servlet:forms:/resource/internal/xsl/forms-field-styling.xsl"/>

      <!-- Location of the resources directories, where JS libs and icons are stored -->
      <xsl:param name="dojo-resources"/>
      <xsl:param name="forms-resources"/>

      <xsl:template match="head">
        <head>
          <xsl:apply-templates select="." mode="forms-page"/>
          <xsl:apply-templates select="." mode="forms-field"/>
          <xsl:apply-templates/>
        </head>
      </xsl:template>

      <xsl:template match="body">
        <body>
          <xsl:apply-templates select="." mode="forms-page"/>
          <xsl:apply-templates select="." mode="forms-field"/>
          <xsl:apply-templates/>
        </body>
      </xsl:template>

    </xsl:stylesheet>

# Try it out

If you have created all the files mentioned above, you can now try out this sample.Build the thing and fire up jetty:

    mvn jetty:run

Direct your browser to:

    http://localhost:8888/simple-sample/registration

Try entering incorrect data and see what it does.

# Next steps

The example we have studied here is quite simple. It might seem elaborate for a simple form (though notice you didn't have to write any Java for all of this, nor to restart Cocoon), but adding more complexity to the form is now simply a matter of adding more widgets, custom validation logic, event handlers, etc. To have a feel for the power of CForms, take a look at the examples included included in the Forms block.
Errors and Improvements? If you see any errors or potential improvements in this document please help us: View, Edit or comment on the latest development version (registration required).