Intent

"Write once, use many" paradigm applied to forms. Create form parts once, that you can aggregate and reuse across many forms, in many applications without any neither change nor redundancy.

Motivation

Forms usually contains some identical information (System information, Author, Date and time concepts, ...) and differ only a little from mode to mode (create, update, search, detail). If we use forms framework in a quick approach, we usually define form (and configuration files) for each mode, and redundancy and complexity increase a lot, while maintenability decreases a lot too... The infernal spiral (direct french translation :-).

We can have a more elaborate approach to avoid all these drawbacks. I will try to explain you this in the following lines.

Architecture

http://www.bluexml.org/static/images/form-generation-dp.gif

Advanced concepts

To avoid redundancy, increase maintenance and reduce complexity, it may be a good idea :

Just before starting, here is the directory structure. Under the crud/feature/cud/docs directory, you have :

http://www.bluexml.org/static/images/class-form-generation-dp.gif http://www.bluexml.org/static/images/doctype-form-generation-dp.gif http://www.bluexml.org/static/images/metawidget-form-generation-dp.gif

Class concept implementation

First, we have a form repository that lists the form classes we need. Form repositories are stored in cud/doctypes directory. Above, we create a calendar doctype, composed of a few classes :

<fd:form
  xmlns:fd="http://apache.org/cocoon/forms/1.0#definition"
  xmlns:fb="http://apache.org/cocoon/forms/1.0#binding"
  xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
  xmlns:filter="http://bluexml.org/filter/1.0">

  <fd:widgets id="calendar">
        <fd:new id="Base"/>
        <fd:new id="Authoring"/>
        <fd:new id="Classification"/>
        <fd:new id="Calendar"/>
        <fd:new id="Date"/>
        <fd:new id="Time"/>     
        <fd:new id="Alarm"/>
        <fd:new id="Security"/> 
        <fd:new id="NewVersion"/>
        <fd:new id="Versions"/>
  </fd:widgets>

</fd:form>

Classes are in reality form definition. You will find above the form definition for the Calendar class. You may notice dynamic list.

<fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" 
        xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" 
        xmlns:i18n="http://apache.org/cocoon/i18n/2.1" 
        xmlns:filter="http://bluexml.org/filter/1.0">
        
        <fd:widgets>
                <fd:class id="Calendar">
                        <fd:widgets>
                                <fd:field id="title" required="true">
                                        <fd:datatype base="string"/>
                                </fd:field>
                                <fd:field id="description">
                                        <fd:datatype base="string"/>
                                </fd:field>
                                <fd:field id="type">
                                        <fd:datatype base="string"/>
                                        <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/>
                                </fd:field>
                                <fd:field id="location">
                                        <fd:datatype base="string"/>
                                        <fd:selection-list src="cocoon:/process-selection-list-specific?xpath=/document[(meta/doctype='ae_entity')and(meta/type='lieu')]&amp;order=meta/name&amp;value=$doc/meta/id&amp;label=fn:string(fn:concat($doc/meta/name,%20'%20-%20',%20$doc/meta/city))" dynamic="true"/>
                                </fd:field>
                                <fd:field id="link">
                                        <fd:datatype base="string"/>
                                </fd:field>
                                <fd:field id="visibility">
                                        <fd:datatype base="string"/>
                                        <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/>
                                </fd:field>
                                <fd:field id="calendar">
                                        <fd:datatype base="string"/>
                                        <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/>
                                </fd:field>
                        </fd:widgets>
                </fd:class>
        </fd:widgets>
</fd:form>

With this first file, we'll have this final form definition.

Form binding and template

Data structure and structure of the presentation are often (but not always) related, when editing or searching data. So I decided to use only one file. It reduces complexity and increases maintenability. The use of meta widgets and filter tags help a lot to achieve this goal too. This way, one file only is necessary.

Metawidgets

They are only a group of binding, template and filter tags.

For example, you maybe use often Date-Time widgets, that displays date and time. To avoid redundancy, you can define the following metawidget :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- BX CPT -->
<fd:form
    xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" 
        xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" 
        xmlns:i18n="http://apache.org/cocoon/i18n/2.1" 
        xmlns:filter="http://bluexml.org/filter/1.0"
        xmlns:meta="http://bluexml.org/meta/1.0">
        
    <meta:widget id="Time">
        <meta:group label="Début" styling-style="width: 300px;" styling-layout="rowWithLabel">
            <fb:value id="dtstart" path="dtstart">
                <fd:convertor datatype="date" type="formatting"/>
            </fb:value>
            <meta:group label="à" styling-layout="row">
                <fb:value id="tstart_hh" path="tstart_hh"/>h
                <fb:value id="tstart_mm" path="tstart_mm"/>m
                <fb:value id="tstart_ss" path="tstart_ss"/>s
            </meta:group>
         </meta:group>
        <meta:group label="Fin" styling-style="width: 300px;" styling-layout="rowWithLabel">
            <fb:value id="dtend" path="dtend">
                <fd:convertor datatype="date" type="formatting"/>
            </fb:value>
            <meta:group label="à" styling-layout="row">
                <fb:value id="tend_hh" path="tend_hh"/>h
                <fb:value id="tend_mm" path="tend_mm"/>m
                <fb:value id="tend_ss" path="tend_ss"/>s
            </meta:group>
        </meta:group>
    </meta:widget>

</fd:form>

To include this set of definition (31 lines), you just have to put the following line in your metabind file :

    <meta:new id="DateTime"/>

Metabind file

In the example below, a calendar doctype is defined. Usual bindings for Date-Time (among others) are reused through the Date-Time metawidget.

<fb:context
        xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" 
        xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" 
        xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
        xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
        xmlns:filter="http://bluexml.org/filter/1.0"
        xmlns:meta="http://bluexml.org/meta/1.0"
        path="document">

        <fb:context path="meta">
        <meta:group 
            styling-type="categories"
            label="General"
            state="internal-tab-state">

            <meta:group styling-layout="columns" label="General">
                <meta:new id="General"/>
                <meta:new id="DateTime"/>
            </meta:group>
            <meta:group styling-type="fieldset" styling-layout="columns" label="Compl.">
                <fb:value id="visibility" path="visibility"/>
                <fb:value id="location" path="location"/>
                <fb:value id="link" path="link"/>
            </meta:group>
            <meta:new id="Alarm"/>            
            <meta:new id="Classification"/>
            <meta:new id="NewVersion"/>
            <meta:new id="System"/>
        </meta:group>
        </fb:context>
    <meta:new id="Versions"/>
    <ft:widget id="action.ok"/>
</fb:context>

Form binding

From this metabind file, we create the following form binding (by just changing the last parameter) :

From this same metabind file, we may have different form binding for user's role (but it is not configured here).

Form template

From this metabind file, we create this form template (create, update, search).

Code

Here is the global idea. All the code is part of the BlueXML project, located in http://www.bluexml.org. Just download the code in the download section.

You may have a look through the cvs browser at :

Conclusion

Here is the global idea. Maybe we could implement this at the Forms framework core ?

DesignPattern/FormGeneration (last edited 2009-09-20 23:43:06 by localhost)