Great!

Xdoc is very easy.  You can find examples by looking at the xdoc directory
contents in one of the turbine source trees.  I included the
intake-service.xml xdoc, which has pretty much all the common elements for
you to follow.  I guess that you can email the document to me when you are
ready.  I sent a message to the dev list asking what the committers want me
to do with documentation once we have it hefty enough to post.

Thanks,

Chris

> -----Original Message-----
> From: Paul Smith [mailto:Paul.Smith@;lawlex.com.au]
> Sent: Tuesday, November 12, 2002 3:39 PM
> To: 'Turbine Users List'
> Subject: RE: Docs: more stuff on..
>
>
> Absolutely on VerifyError, but I'm not totally familiar with the Upload
> issue (never had the problem myself, but have seen it  pop up).
> I could do
> some research and describe the issue/resolutions from a search of the
> archive?
>
> I'll mock up some text for the VerifyError issue.  BTW, I am very very
> unfamiliar with xdocs, could someone point me to some reference
> material on
> the use of it? Sounds like I'll need to format any content in
> that format...
>
> cheers,
>
> Paul Smith
>
> > -----Original Message-----
> > From: Chris K Chew [mailto:chris@;fenetics.com]
> > Sent: Wednesday, 13 November 2002 9:35 AM
> > To: Turbine Users List
> > Subject: RE: Docs: more stuff on..
> >
> >
> > Hi Paul.
> >
> > I included a FAQ section with your suggested bullet points,
> > and added one
> > about Intake and another about the versions.  You can find it
> > at the online
> > outline whiteboard: http://www.fenetics.com/turbine/index.html.
> >
> > Would you be willing to provide some answers to the
> > VerifyError and Upload
> > Issues in 2.1?
> >
> > Thanks,
> >
> > Chris
> >
> >
> > > -----Original Message-----
> > > From: Paul Smith [mailto:Paul.Smith@;lawlex.com.au]
> > > Sent: Tuesday, November 12, 2002 2:56 PM
> > > To: 'Turbine Users List'
> > > Subject: Docs: more stuff on..
> > >
> > >
> > > While I'm waiting for 2.2 final to describe the 2.1/2.2
> > differences, I can
> > > help out as a proof reader/additional copywriter for stuff.
> > >
> > > I'm catching up on reading all the threads from yesterday, so
> > > I've skimmed a
> > > fair bit, but I think an _*easily accessbile*_ FAQ would be
> > a bonus for
> > > people, there's at least a Top 10 we should compile for 2.1
> > as a helping
> > > hand to anyone who has to stay in 2.1 land for a while.
> > (Chris/Quiton,
> > > perhaps this should be added to the Top level @
> > > http://www.fenetics.com/turbine/index.html ?)
> > >
> > > If this would be useful, perhaps we should gather the list
> > of items (as in
> > > single sentence description) we'd like to include in the
> > FAQ, and then I
> > > could canvas appropriate detailed response content from the
> > > experts, and/or
> > > build some links to the appropriate place in the rest of the docs.
> > >
> > > Some examples I am thinking of here are:
> > >
> > > * java.lang.VerifyError using TDK (this seems to crop up once
> > > every days or
> > > so)
> > > * Extending TurbineUser (this would just link directly to
> > appropriate docs
> > > area)
> > > * Upload issues (i.e. a couple of specific bugs/issues that
> > people often
> > > request, particularly in 2.1)
> > >
> > >
> > > > -----Original Message-----
> > > > From: Rodney Schneider [mailto:rodney.schneider@;actf.com.au]
> > > > Sent: Tuesday, 12 November 2002 11:49 AM
> > > > To: Turbine Users List
> > > > Subject: Re: Documentation (was: Re: Thanks)
> > > >
> > > >
> > > > On Tue, 12 Nov 2002 11:18, you wrote:
> > > >
> > > > > I have thought of three guides to write, but am not sure if
> > > > they would be
> > > > > helpful, or which to do first.  Any suggestions?
> > > > >
> > > > > * Multiple Site Howto -- How to organize classes and
> > > > actions so that a
> > > > > single turbine instance can serve multiple sites that may
> > > > need to share
> > > > > common functionality
> > > >
> > > > The above Howto might be useful to some people, but IMHO
> > > > there are higher
> > > > priorities; see below.
> > > >
> > > > If you look at the Torque documentation, they currently have
> > > > a Tutorial, a
> > > > User Guide and a Developer Guide as well as various Howtos
> > > > Guides.  I think
> > > > this would work well for Turbine.  I personally think that
> > > > the current
> > > > Turbine 2.x documentation is spread out across too many Howto
> > > > documents and
> > > > that many of these could be compiled into a single
> > > > comprehensive User Guide
> > > > separated into appropriate chapters.
> > > >
> > > > > * Service Howto/Introduction -- What services are, why they
> > > > are nice, and
> > > > > how to write them
> > > >
> > > > I think it would be good if Turbine Services took up a
> > > > chapter in the Turbine
> > > > 2 User Guide.  See the existing service documentation for a
> > > > place to start:
> > > > http://jakarta.apache.org/turbine/turbine-2/apidocs/org/apache
> > > /turbine/services/package-summary.html
> > > http://jakarta.apache.org/turbine/turbine-2/services.html
> > >
> > > > * Non TDK Howto -- I found the TDK confusing because it
> > did a bunch of
> > > > stuff that I didn't necessarily want.  So I learned about
> > Turbine by
> > > > removing everything, and putting it back in as I needed it.
> > > This document
> > > > would be a "tour" in the form of setting up an example
> > app without using
> > > > the TDK.
> > >
> > > This would be the Turbine 2 Tutorial and would come with a sample
> > > application, similar in structure to the Torque Tutorial.
> > I think this
> > > would
> > > probably be the most important document to write.  If we had a
> > > Tutorial, we
> > > would no longer need a TDK.
> > >
> > > I was in the process of migrating my Turbine 2.1 application over
> > > to Turbine
> > >
> > > 2.2-RC1 a couple of weeks ago, and I began writing a
> > Turbine 2.1 to 2.2
> > > Migration Howto.  Unfortunately, work has become very busy so I
> > > haven't had
> > > a
> > > chance to complete it yet.  Paul Smith had also volunteered to
> > > rewrite/update
> > > the TDK Howto to reflect the differences between TDK-2.1 and
> > > TDK-2.2, but he
> > >
> > > is waiting for the final release of TDK-2.2 before he starts.
> > >
> > > Regards,
> > >
> > > -- Rodney
> > >
> > > --
> > > To unsubscribe, e-mail:
> > > <mailto:turbine-user-unsubscribe@;jakarta.apache.org>
> > > For additional commands, e-mail:
> > > <mailto:turbine-user-help@;jakarta.apache.org>
> > >
> > > --
> > > To unsubscribe, e-mail:
> > > <mailto:turbine-user-unsubscribe@;jakarta.apache.org>
> > > For additional commands, e-mail:
> > > <mailto:turbine-user-help@;jakarta.apache.org>
> >
> >
> > --
> > To unsubscribe, e-mail:
> <mailto:turbine-user-unsubscribe@;jakarta.apache.org>
> For additional commands, e-mail:
> <mailto:turbine-user-help@;jakarta.apache.org>
>
> --
> To unsubscribe, e-mail:
> <mailto:turbine-user-unsubscribe@;jakarta.apache.org>
> For additional commands, e-mail:
> <mailto:turbine-user-help@;jakarta.apache.org>
<?xml version="1.0"?>

<document>

 <properties>
  <title>Turbine Services - Intake Service</title>
  <author email="[EMAIL PROTECTED]">John McNally</author>
  <author email="[EMAIL PROTECTED]">Jon S. Stevens</author>
 </properties>

 <body>

<section name="Intake Service">

<p>
Intake uses an xml specification to perform web form input validation
and mapping input data to a bean's properties.  In other words, Intake
will allow a web application to take web form input, validate it and
then map the data to an object. Tools like the <a
href="../torque.html">Torque</a> help provide mapping of Objects to the
database and Intake helps map web form data to Objects.
</p>

<p>
The visual picture of where Intake fits into a Turbine web application
looks something like this:
</p>

<source><![CDATA[
------------------
    HTML Form
------------------
     Intake
------------------
 Business Objects  <- Torque Generated
------------------
      Peers        <- Torque Generated
------------------
      RDBMS
------------------
]]></source>

<p>
There are several advantages to using Intake. First off, it provides for
a centralized management system for dealing with form data. All of the
configuration of Intake is done through a single XML file. Next, Intake
is good for validating the form data. Intake provides the ability to do
regular expression matching in order to make sure that form fields
contain the data that they should contain. For example, if someone
should only enter a number into a form field, it can be validated with a
regular expression. Lastly, Intake can provides a centralized source for
error messages. If the validation fails, the error message defined in
the XML file can be shown to the user.
</p>

</section>

<section name="Configuration">

<source><![CDATA[
# -------------------------------------------------------------------
# 
#  S E R V I C E S
#
# -------------------------------------------------------------------
# Classes for Turbine Services should be defined here.
# Format: services.[name].classname=[implementing class]
#
# To specify properties of a service use the following syntax:
# service.[name].[property]=[value]

services.IntakeService.classname= \
org.apache.turbine.services.intake.TurbineIntakeService

# -------------------------------------------------------------------
# 
#  I N T A K E   S E R V I C E
#
# -------------------------------------------------------------------

# The location of the xml file specifying valid inputs
services.IntakeService.xml.path=WEB-INF/conf/intake.xml

tool.request.intake=org.apache.turbine.services.intake.IntakeTool
]]></source>

</section>

<section name="Usage">
<p>
Intake is implemented as a Turbine Service and a Pull Tool.  An XML
specification is parsed during the service initialization and used by
the pull tool while processing request data and generating the response.
</p>

<p>
Intake is made available in the Velocity context with the default value
of $intake. The name of the variable that is used is what is configured 
for the tool. For example, the current configuration is "tool.request.intake". 
To change the name of the variable to "foo", it would be "tool.request.foo".
</p>

<p>
Intake is made available in Java code by adding the following imports to
the top of a .java file:
</p>

<source><![CDATA[
import org.apache.turbine.services.intake.IntakeTool;
import org.apache.turbine.services.intake.model.*;
]]></source>

</section>

<section name="Xml File">

<p>
The following example come from <a
href="http://scarab.tigris.org/";>Scarab</a>. These are a couple of
groups from Scarab's intake.xml:
</p>

<source><![CDATA[
<input-data basePackage="org.tigris.scarab.">

<group name="AttributeValue" key="attv" 
            mapToObject="om.AttributeValue">
    <field name="Value" key="val" type="String">
        <rule name="maxLength" value="255">Value length cannot be &gt; 255</rule>
        <required-message>This module requires data for this 
                          attribute.
        </required-message>
    </field>
    <field name="Url" key="url" type="String" mapToProperty="Value">
        <rule name="maxLength" value="255">Url length cannot be &gt; 255</rule>
        <rule name="mask" value="^$|http.+">Please enter an url starting with "http"</rule>
        <required-message>This module requires a valid url.</required-message>
    </field>
    <field name="OptionId" key="optionid" type="NumberKey">
        <rule name="mask" value="^$|[0-9]+">Please select a valid choice</rule>
        <required-message>This module requires that you select an option 
                          for this attribute.
        </required-message>
    </field>
</group>

<group name="Login" key="login">
    <field name="Username" key="u" type="String">
        <rule name="minLength" value="1">Please enter an email address</rule>
        <rule name="mask" value=".+@.+\..+">Please enter a valid email address</rule>
    </field> 
    <field name="Password" key="p" type="String">
        <rule name="minLength" value="1">Please enter a password</rule>
    </field>
</group>

</input-data>
]]></source>

<p>
A group is a set of fields that have been aligned so that they form a
logical unit. The first group includes some of the properties from a
om.AttributeValue business object (BO). This object is a Java Bean
object with get/set methods for each of the properties in the object. In
this case, the object has been auto-generated by Torque from Scarab's
SQL schema.
</p>

<p>
The group tag has a class attribute which is the name that will be used
within templates and java code to refer to the group.  It also contains a 
key attribute which will be used in the query parameters.  The key is
not referenced in any code, so it can be a short name (even 1 character) as 
long as it is uniquely identifies the group from the rest.
</p>
<p>
An object that the group's fields will map can also be specified.  This will
be a default; the individual fields within a group can map to different
objects.
</p>
<p>
Fields have attributes: name and key which serve similar function to the
group's class and key attributes.  It has mapToObject and mapToProperty fields
that can be used to associate a business object to the field for prepopulating
the field as well as assigning the field data to the bean after successful
validation.  The field must have a type, which can be simple types such as
String and Integer.  It is planned that more complex types, such as
Email, Url, or Date, will be added that will add functionality that is 
difficult or not possible with a simple regex mask.
</p>
<p>
The field can define rule elements.  The basic fields include rules for
minimum and maximum, lengths and values, as well as a regex mask.
</p>

</section>

<section name="Login Example">

<source><![CDATA[
<group name="Login" key="login">
]]></source>

<p>
The name="Login" is a descriptive name for the group. The key="login" is
the value that is used in the web forms to identify the group. The key=
value is not directly referenced. In other words, you do not need to
know it exists unless you are debugging your application. Both of these
attribute values must be unique across all groups in the XML file. Now,
lets look at the fields in the group.
</p>

<source><![CDATA[
<field name="Username" key="u" type="String">
    <rule name="minLength" value="1">Please enter an email address</rule>
    <rule name="mask" value=".+@.+\..+">Please enter a valid email address</rule>
</field> 
<field name="Password" key="p" type="String">
    <rule name="minLength" value="1">Please enter a password</rule>
</field>
]]></source>

<p>
The name="Username" is the descriptive name for the field. The key="u"
is the value that is used in the web forms to identify the field. Both
of these attributes must be unique across the fields within the group.
The type="String" specifies what the system expects the input for that
field to be (please see the intake.dtd for the allowed values). Within
the field, it is possible to specify one or more rules. These rules
define how Intake should validate web form data. There are minLength,
maxLength and mask attributes to the rule tag. The message inside the
rule tag is a text message which can be used to display an error within
the template.
</p>

<p>
At this point, it is best to show an example form of how to use Intake
within a Velocity template:
</p>

<source><![CDATA[
(1) <form action="$link.setPage("Login.vm")" method="POST" name="login" >
(2)    <input type="hidden" name="action" value="Login">
(3)    #if ($data.Parameters.nextTemplate)
(4)    <input type="hidden" name="nextTemplate" 
              value="$data.Parameters.nextTemplate">
       #else
(5)    <input type="hidden" name="nextTemplate" value="Start.vm">    
       #end

<p>
Email Address:

(6)    #set ( $loginGroup = $intake.Login.Default )

(7)    #if ( !$loginGroup.Username.isValid() )
(8)        $loginGroup.Username.Message<br>
       #end
(9)    <input name= "$loginGroup.Username.Key"
              value="$!loginGroup.Username" size="25" type="text">
</p>

<p>
Password:

(10)    #if ( !$loginGroup.Password.isValid() )
(11)        $loginGroup.Password.Message<br>
        #end
(12)   <input name= "$loginGroup.Password.Key"
              value="" size="25" type="text" 
              onChange="document.login.submit();">
</p>

(13) <input type="submit" name="eventSubmit_doLogin" value="Login">
(14) $intake.declareGroups()
</form>
]]></source>

<p>
The example above shows quite a few different concepts with regards to
web application design, so lets break them down a bit, starting from the
top. Each of the important lines have been numbered for easy reference.
</p>

<p>
<ol>
    <li>
    Create the &lt;form&gt; tag. Within it, we use the $link object to
    create a URI for the template "Login.vm". In other words, when the
    button is clicked, the page will submit upon itself.
    </li>
    <li>
    Set the Action to execute to be "Login". This can either be a hidden
    input field or be defined with the
    $link.setPage().setAction("Login") method
    </li>
    <li>
    Check to see if there is a "nextTemplate" defined in the
    GET/POST/PATH_INFO information. On success, the Action can use the
    nextTemplate field to decide what page to show next.
    </li>
    <li>
    If (3), then create a hidden input tag that holds the value for
    nextTemplate.
    </li>
    <li>
    If not (3), then set the nextTemplate to be the "Start.vm" page.
    </li>
    <li>
    This retrieves the default Login Group object from Intake. What this
    means is that the group "Login" as defined in Scarab's intake.xml is
    represented as an object.
    </li>
    <li>
    It is then possible to query the object to confirm if the
    information within it is valid.
    </li>
    <li>
    This will display the invalid error message as defined in the
    intake.xml &lt;rule&gt; definitions.
    </li>
    <li>
    Here we define a form input text field. The $loginGroup.Username.Key
    specifies an Intake system generated key. The value attribute
    $!loginGroup.Username will tell Intake to either display an empty
    String or display the previous form submission.
    </li>
    <li>
    Repeat the same procedure as for the Username field.
    </li>
    <li>
    Repeat the same procedure as for the Username field.
    </li>
    <li>
    A bit of JavaScript will cause the form to submit itself if one hits
    tab after entering the password.
    </li>
    <li>
    eventSubmit_doLogin is special. It tells Turbine to execute the
    doLogin method in the Action. This is based on the Action Events
    system.
    </li>
    <li>
    $intake.declareGroups() tells Intake to add a couple hidden input
    fields to the page output. These fields represent the Groups that
    were used in the template.
    </li>
</ol>
</p>

<p>
Below is an example of the HTML that is sent to the browser after the 
page has been requested:
</p>

<source><![CDATA[
<form action="http://foo:8080/scarab/servlet/scarab/template/Login.vm"; 
      method="POST" name="login" >
    <input type="hidden" name="action" value="Login">
        <input type="hidden" name="nextTemplate" value="Start.vm">    
    
<p>
Email Address:
    
       <input name= "login_0u"
        value="" size="25" type="text">
</p>

<p>
Password:

       <input name= "login_0p"
        value="" size="25" type="text" onchange="document.login.submit();">
</p>

<input type="submit" name="eventSubmit_doLogin" value="Login">

<input type="hidden" name="intake-grp" value="login"></input>
<input type="hidden" name="login" value="_0"></input>

</form>
]]></source>

<p>
Some notes to consider:
</p>

<ol>
    <li>
    The _0 signifies the "default" group.
    </li>
    <li>
    The login_0u signifies the login group combined with the _0 and the
    "u" is from the intake.xml file for the field "Username".
    </li>
    <li>
    The two hidden input fields are what is generated from the
    $intake.declareGroups()
    </li>
</ol>

<p>
The Java Action code which handles the submission of the form looks like this:
</p>

<source><![CDATA[
public void doLogin( RunData data, Context context ) throws Exception
{
    IntakeTool intake = (IntakeTool) context.get("intake");

    if ( intake.isAllValid() &amp;&amp; checkUser(data, context) ) 
    {
        String template = data.getParameters()
            .getString(ScarabConstants.NEXT_TEMPLATE, 
            TurbineResources.getString("template.homepage", "Start.vm") );
        setTemplate(data, template);
    }
    else 
    {
        // Retrieve an anonymous user
        data.setUser (TurbineSecurity.getAnonymousUser());
        setTemplate(data, 
            data.getParameters()
                .getString(ScarabConstants.TEMPLATE, "Login.vm"));
    }
}

/**
    Checks to make sure that the user exists, has been confirmed.
*/
public boolean checkUser(RunData data, Context context)
    throws Exception
{
    User user = null;
    IntakeTool intake = (IntakeTool)context
        .get(ScarabConstants.INTAKE_TOOL);

    try
    {
        String username = null;
        String password = null;
        try
        {
            Group login = intake.get("Login", IntakeTool.DEFAULT_KEY);
            username = login.get("Username").toString();
            password = login.get("Password").toString();
        }
        catch ( Exception e )
        {
            throw new TurbineSecurityException(
                "Login information was not supplied.");
        }

        // Authenticate the user and get the object.
        user = TurbineSecurity.getAuthenticatedUser( username, password );

        ...
    }
}
]]></source>

<p>
Intake is retrieved from the context and asked whether all the inputs
that it knows about were valid. If not the login form will be quickly
reshown and error messages will be given. If the data is valid, the
field data is extracted manually in this case, as the Intake fields do
not map directly to a bean object. The next example will use the
group.setProperties() method to directly assign Intake's field data to
the matching beans.
</p>

</section>

<section name="Attribute Value example">

<source><![CDATA[
<group name="AttributeValue" key="attv" 
            mapToObject="om.AttributeValue">
]]></source>

<p>
The name="AttributeValue" is simply a descriptive name for the group.
The key="attv" is the value that is used in the web forms to identify
the group. Both of these attributes must be unique across all groups in
the XML file. The mapToObject="om.AttributeValue" is an optional
attribute. This specifies what Java Bean object that this group maps to.
If a mapToObject is not specified, then it is possible to use Intake to
retrieve the values of the data directly instead of getting it from a
populated object. This will be covered in detail further on.
</p>


<source><![CDATA[
<field name="Value" key="val" type="String">
    <rule name="maxLength" value="255">Value length cannot be &gt; 255</rule>
    <required-message>This module requires data for this 
                      attribute.
    </required-message>
</field>
<field name="Url" key="url" type="String" mapToProperty="Value">
    <rule name="maxLength" value="255">Url length cannot be &gt; 255</rule>
    <rule name="mask" value="^$|http.+">Please enter an url starting with "http"</rule>
    <required-message>This module requires a valid url.</required-message>
</field>
]]></source>

<p>
The fields within a group relate to the form fields on a web page. At
this point, it is probably best to show an example rather than
explaining in detail what each part of the field tag is. Therefore,
using the fields above in a simple example, one might have a form with a
text entry box that allows you to edit a Url. The filename is:
"EditUrl.vm".
</p>

<source><![CDATA[
#set ( $action = $link.setPage("EditUrl.vm").setAction("SaveUrl") )
<form action="$action"
      method="post">
 
#set ( $attributeValue = $issue.AttributeValue("URL") )
#set ( $group = $intake.AttributeValue.mapTo($attributeValue) )

Enter Url:
<input type="text" name="$group.Url.Key" value="$!group.Url.Value">

<input type="submit" name="eventSubmit_doSave" value="Submit>

$intake.declareGroups()
</form>
]]></source>

<p>
To explain the template above, the first #set is done simply for
convenience. The second #set is part of Scarab. It uses the $issue
object to retrieve a "URL" AttributeValue object for a particular issue.
</p>

<p>
The next #set tells Intake to map that object to the AttributeValue
group. What it does is tell Intake to create an AttributeValue Group
object which has been mapped to the AttributeValue retrieved from the
$issue object. This Group object represents the XML file &lt;group&gt;
as a Java object.
</p>

<p>
Moving down further into the example, there is the &lt;input&gt; field
which has a name and value attributes. The $group.Url.Key tells Intake
to retrieve the key information for the field. This would evaluate to
"attv_0url". What this is a combination of the group key (attv), a "_0"
is the result of retrieving the "$intake.AttributeValue.Default" and the
"url" is the field key. The value attribute would evaluate to just an 
empty String the first time around. The $intake.declareGroups() is a 
special method that will create a hidden input field that declares which
groups have been added to the page. We will discuss that in more detail
further down.
</p>

<p>
View source on the HTML page after executing the template and this is
what the form above would look like:
</p>

<source><![CDATA[
<form action="http://server/s/servlet/s/template/EnterUrl.vm/action/EnterUrlAction"; 
      method="post">

Enter Url:
<input type="text" name="attv_0url" value="">

<input type="submit" name="eventSubmit_doEnter" value="Submit>

<input type="hidden" name="attv" value="_0">
<input type="hidden" name="intake-grp" value="attv">
</form>
]]></source>

<p>
When the form is submitted to the server (the user clicks the submit
button), the following code in the EnterUrlAction.java class is executed.
</p>

<source><![CDATA[
public void doEnter( RunData data, Context context ) throws Exception
{
    IntakeTool intake = (IntakeTool)context
        .get(ScarabConstants.INTAKE_TOOL);
    
    // check to see if the fields are valid
    if ( intake.isAllValid() ) 
    {
        // get the "AttributeValue" Group
        AttributeValue av = new AttributeValue();
        Group group = intake.get("AttributeValue", IntakeTool.DEFAULT_KEY);
        group.setProperties (av);
        // now av is properly populated with the form data
    }
}
]]></source>

<p>
If the form fields are invalid as a result of not matching one of the
rules that are defined in the fields in the XML file, then the action
does nothing and the page is displayed again.
</p>

<p>
Back to explaining the fields, lets look at the example again:
</p>

<source><![CDATA[
    <field name="Value" key="val" type="String">
        <rule name="maxLength" value="255">Value length cannot be &gt; 255</rule>
        <required-message>This module requires data for this 
                          attribute.
        </required-message>
    </field>
    <field name="Url" key="url" type="String" mapToProperty="Value">
        <rule name="maxLength" value="255">Url length cannot be &gt; 255</rule>
        <rule name="mask" value="^$|http.+">Please enter an url starting with "http"</rule>
        <required-message>This module requires a valid url.</required-message>
    </field>
]]></source>

  </section>

  <section name="Multiple groups of the same class in one form">
  
<p>
This example uses a form from Scarab that assigns values to various
attribute's that are relevant for an issue (bug).  Attributes include
summary, operating system, platform, assigned to, etc.  Some of the 
attributes are required, but not all.  
</p>

<p>
The template:
</p>

<source><![CDATA[
#set ( $action = $link.setPage("entry,Wizard3.vm").setAction("ReportIssue")
.addPathInfo("nextTemplate", "entry,Wizard4.vm") )
#set ($user = $scarabR.User)
#set ($module = $user.CurrentModule)
#set ($issue = $user.ReportingIssue)


<form method="get" action="$action">

<hr><br>Please fill in the following:<br><br>

#foreach ( $attVal in $issue.OrderedModuleAttributeValues )
  #set ( $attrInput = $intake.AttributeValue.mapTo($attVal) ) 

  #if ( $attVal.Attribute.AttributeType.ValidationKey )
        #set ( $field = $attVal.Attribute.AttributeType.ValidationKey )
  #elseif ($attVal.Attribute.AttributeType.Name == "combo-box" )
        #set ( $field = "OptionId" )
  #else
        #set ( $field = "Value" )  
  #end

    #if ( $attVal.isRequired() )
        $attrInput.get($field).setRequired(true)
        <b>*</b>
    #end

    $attVal.Attribute.Name:

    #if ($attVal.Attribute.AttributeType.Name == "combo-box" )
          <font color="red">
            #attrValueErrorMsg ( $attVal $field )
          </font>
          <br>
          #attrValueSelect ($attVal $field "")
    #else

          <font color="red">
            #attrValueErrorMsg ( $attVal $field )
          </font>
          <br>
        #if ($attVal.Attribute.AttributeType.Name == "long-string" )        

            <textarea name= "$attrInput.Value.Key" cols="40" 
               rows="5">$!attrInput.Value</textarea>
        #else

           <input name= "$attrInput.Value.Key"
                value="$!attrInput.Value" size="20" type="text">

        #end
<br><br>
  #end
#end

<p>
            <input type="submit" 
                name="eventSubmit_doEnterissue" value="Submit Issue">

$intake.declareGroups()
</form>
]]></source>

<p>
The main new thing added here is that the $intake group is mapped to a business
object.  A business object that is to be used in this way is expected to
implement the Retrievable interface, which provides a method to get a
String key which uniquely identifies the object.  
</p>
<p>
The action:
</p>

<source>
public void doEnterissue( RunData data, Context context )
    throws Exception
{
    IntakeTool intake = (IntakeTool)context
        .get(ScarabConstants.INTAKE_TOOL);

    // Summary is always required.
    ScarabUser user = (ScarabUser)data.getUser();
    Issue issue = user.getReportingIssue();
    AttributeValue aval = (AttributeValue)issue
        .getModuleAttributeValuesMap().get("SUMMARY");
    Group group = intake.get("AttributeValue", aval.getQueryKey());
    Field summary = group.get("Value");
    summary.setRequired(true);
    issue.setVocabulary(new Vocabulary(summary.toString()));

    if ( intake.isAllValid() ) 
    {
        Iterator i = issue.getModuleAttributeValuesMap()
            .values().iterator();
        while (i.hasNext()) 
        {
            aval = (AttributeValue)i.next();
            group = intake.get("AttributeValue", aval.getQueryKey());
            if ( group != null ) 
            {
                group.setProperties(aval);
            }                
        }
        
        if ( issue.containsMinimumAttributeValues() ) 
        {
            issue.save();

            String template = data.getParameters()
                .getString(ScarabConstants.NEXT_TEMPLATE, 
                           "entry,Wizard3.vm");
            setTemplate(data, template);            
        }
    }
}
</source>

<p>
The action shows how the business object or action can let intake know if
a field is required. It also shows how multiple groups of the same class
can be added to a template and then the information is easily passed on
to an associated bean.
</p>


<source><![CDATA[
Dan Diephouse wrote:
> 
> I want to validate a form that updates my business object properties
> with intake.  I got the latest cvs of turbine-2 repository and built a
> new distribution of the turbine jar.  I can easily use intake to add
> validate items I add to my databse, but I'm running in to a snag when
> trying to modify them.  I've defined by business object in the intake
> validation file.  I use the following code to update my item:
> 
>          IntakeTool intake = (IntakeTool) context.get("intake");
> 
>          ParameterParser pp = data.getParameters();
> 
>          if ( intake.isAllValid() ) {
>              Job j = new Job();
>              j.setNew(false);
>              group.setProperties(j);
> 
>              JobPeer.doUpdate(j);
> 
> Error-->    data.getParameters().add("jobid", j.getJobId().toString());
>              data.setMessage("Job updated.");
>          } else {
>              data.setMessage("There was an error updating the job.
> Check below for further information.");
>          }
> 
> It runs OK, until I try and retrieve the JobId.  I get a Null Pointer
> exception.  Is there something I'm doing wrong here?  Or is this a bug?
>   Do I need to specify that this is not a new object to intake?  I would
> include my template, but there are lots of fields so its really long.
> But here's a little summary with most fields gone:
> 
> #set ( $job = $basecamp.getJob() )   - This gets a job from a pull tool
> #set ( $jobGroup = $intake.Job.mapTo($job) )
> <input type=hidden name="$jobGroup.JobId.Key" 
> value="$jobGroup.JobId.Value">
> 
> #if ( !$jobGroup.Title.isValid() )
> $jobGroup.Title.Message<br>
> #end
> <input type=text name="$jobGroup.Title.Key"
> value="$!jobGroup.Title.Value" size="50">
> .
> .
> .
> .
> $intake.declareGroups()
> 
> Thanks,
> 
> Dan Diephouse

This is not how i usually do things, but I guess it could work.  One
thing that is wrong is that you are using mapTo(job) in the template and
then in the action you are using IntakeTool.DEFAULT_KEY.  Unless
job.getQueryKey() returns "_0", this combination is not going to work.
Print out data.getParameters().toString() in the action to see what the
parameters look like.

Here is what I consider a normal course of events:

1.  In the template:  

$job = $foo.Job
#set ( $jobGroup = $intake.Job.mapTo($job) )

(This job is can be a new Job or one that is already saved.)

2. In the action:

// get the same (not necessarilary the same java Object, but has the
exact same attribute values) job
Job job = foo.getJob()
Group group = intake.get("Job", job.getQueryKey());
group.setProperties(job);

It appears as though you are trying to map the parameters given for one
job to another job.

john mcnally
]]></source>

  </section>

  <section name="Default Values for fields">

<p>
If you want to use input fields which should default to non-empty values,
you can use the defaultValue field:
</p>

<source><![CDATA[
<group name="test" key="test">
    <field name="Value" key="val" type="String" defaultValue="preset">
    </field>
</group>
]]></source>

If you set up a form like this:

<source><![CDATA[
#if($!dz)
#set ($frm = $intake.test.mapTo($t))
$frm.Mode.
#else
#set ($frm = $intake.test.default )
#end

<FORM NAME="entryForm">
<INPUT type="text" name="$frm.get("val").Key" value="$!frm.get("val")">
</FORM>
]]></source>

<p>
then you can either map your form to the fields of the $t object or to the default values, which, for
the val Field is the string 'preset'.
</p>

</section>

 </body>

</document>

--
To unsubscribe, e-mail:   <mailto:turbine-user-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:turbine-user-help@;jakarta.apache.org>

Reply via email to