blob: 9e69183aeaffb4946150b40c7b72c4ecf2b216e0 [file] [log] [blame]
<!DOCTYPE html>
<!--
| Generated by Apache Maven Doxia Site Renderer 1.9.2 from xdocs/howto.xml at 21 September 2021
| Rendered using Apache Maven Fluido Skin 1.9
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="generator" content="Apache Maven Doxia Site Renderer 1.9.2" />
<meta name="author" content="Jeffery Painter" />
<meta name="author" content="John McNally" />
<meta name="author" content="Jon S. Stevens" />
<meta name="author" content="Eric Pugh" />
<title>Fulcrum Intake &#x2013; Fulcrum Intake Service</title>
<link rel="stylesheet" href="./css/apache-maven-fluido-1.9.min.css" />
<link rel="stylesheet" href="./css/site.css" />
<link rel="stylesheet" href="./css/print.css" media="print" />
<script src="./js/apache-maven-fluido-1.9.min.js"></script>
</head>
<body class="topBarDisabled">
<div class="container-fluid">
<header>
<div id="banner">
<div class="pull-left"><a href="../../" id="bannerLeft" title="Apache Turbine"><img src="../../images/turbine-project.png" alt="Apache Turbine"/></a></div>
<div class="pull-right"></div>
<div class="clear"><hr/></div>
</div>
<div id="breadcrumbs">
<ul class="breadcrumb">
<li id="publishDate">Last Published: 21 September 2021<span class="divider">|</span>
</li>
<li id="projectVersion">Version: 2.0.1-SNAPSHOT</li>
<li class="pull-right"><span class="divider">|</span>
<a href="../" title="Fulcrum">Fulcrum</a></li>
<li class="pull-right"><span class="divider">|</span>
<a href="../../" title="Turbine">Turbine</a></li>
<li class="pull-right"><a href="https://www.apache.org" class="externalLink" title="Apache">Apache</a></li>
</ul>
</div>
</header>
<div class="row-fluid">
<header id="leftColumn" class="span2">
<nav class="well sidebar-nav">
<ul class="nav nav-list">
<li class="nav-header">Overview</li>
<li><a href="index.html" title="Main"><span class="none"></span>Main</a></li>
<li class="active"><a href="#"><span class="none"></span>Howto</a></li>
<li class="nav-header">Project Documentation</li>
<li><a href="project-info.html" title="Project Information"><span class="icon-chevron-right"></span>Project Information</a></li>
<li><a href="project-reports.html" title="Project Reports"><span class="icon-chevron-right"></span>Project Reports</a></li>
<li class="nav-header">Apache</li>
<li><a href="https://www.apache.org/" class="externalLink" title="Apache Website"><span class="none"></span>Apache Website</a></li>
<li><a href="https://www.apache.org/licenses/" class="externalLink" title="License"><span class="none"></span>License</a></li>
<li><a href="https://www.apache.org/foundation/how-it-works.html" class="externalLink" title="How the ASF works"><span class="none"></span>How the ASF works</a></li>
<li><a href="https://www.apache.org/foundation/sponsorship.html" class="externalLink" title="Sponsorship"><span class="none"></span>Sponsorship</a></li>
<li><a href="https://www.apache.org/foundation/thanks.html" class="externalLink" title="Thanks"><span class="none"></span>Thanks</a></li>
<li><a href="https://www.apache.org/security/" class="externalLink" title="Security"><span class="none"></span>Security</a></li>
</ul>
</nav>
<div class="well sidebar-nav">
<hr />
<div id="poweredBy">
<div class="clear"></div>
<div class="clear"></div>
<div class="clear"></div>
<a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
</div>
</div>
</header>
<main id="bodyColumn" class="span10" >
<section>
<h2><a name="Intake_Service"></a>Intake Service</h2>
<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 class="externalLink" href="http://db.apache.org/torque/">Torque</a> help provide mapping of Objects to the
database and Intake helps map web form data to Objects. Intake can be used
standalone by any application that supports Avalon components. However it
has extensive integration with the <a class="externalLink" href="http://turbine.apache.org/">Turbine</a>
application framework. This HowTo focuses on using it with Turbine.
</p>
<p>
The visual picture of where Intake fits into a Turbine web application
looks something like this:
</p>
<div class="source"><pre class="prettyprint">
------------------
HTML Form
------------------
Intake
------------------
Business Objects &lt;- e.g. Torque Generated
------------------
Peers &lt;- e.g. Torque Generated
------------------
RDBMS
------------------
</pre></div>
<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. Intake also supports the input of localized values and
does the right thing based on the Locale the user requested. Lastly, Intake can
provide 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>
<h2><a name="Usage"></a>Usage</h2>
<p>
Intake is implemented as a Avalon component. Access to it is provided by
a Turbine Pull Tool. One or more XML specifications are 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 &quot;tool.request.intake&quot;.
To change the name of the variable to &quot;foo&quot;, it would be &quot;tool.request.foo&quot;.
</p>
<p>
Intake is made available in Java code by adding the following imports to
the top of a .java file:
</p>
<div class="source"><pre class="prettyprint">
import org.apache.fulcrum.intake.model.*;
import org.apache.turbine.services.intake.IntakeTool;
</pre></div>
</section>
<section>
<h2><a name="Xml_File"></a>Xml File</h2>
<p>
The following example comes from <a class="externalLink" href="http://scarab.tigris.org/">Scarab</a>. These are a couple of
groups from Scarab's intake.xml:
</p>
<div class="source"><pre class="prettyprint">
&lt;input-data xmlns=&quot;http://turbine.apache.org/schema/intake/4.0&quot; basePackage=&quot;org.tigris.scarab.&quot;&gt;
&lt;group name=&quot;AttributeValue&quot; key=&quot;attv&quot;
mapToObject=&quot;om.AttributeValue&quot;&gt;
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Value length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires data for
this attribute.
&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;Url&quot; key=&quot;url&quot; type=&quot;String&quot; mapToProperty=&quot;Value&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Url length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;mask&quot; value=&quot;^$|http.+&quot;&gt;Please enter an url starting with &quot;http&quot;&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires a valid url.&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;OptionId&quot; key=&quot;optionid&quot; type=&quot;NumberKey&quot;&gt;
&lt;rule name=&quot;mask&quot; value=&quot;^$|[0-9]+&quot;&gt;Please select a valid choice&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires that you select
an option for this attribute.
&lt;/rule&gt;
&lt;/field&gt;
&lt;/group&gt;
&lt;group name=&quot;Login&quot; key=&quot;login&quot;&gt;
&lt;field name=&quot;Username&quot; key=&quot;u&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;minLength&quot; value=&quot;1&quot;&gt;Please enter an email address&lt;/rule&gt;
&lt;rule name=&quot;mask&quot; value=&quot;.+@.+\..+&quot;&gt;Please enter a valid email address&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;Password&quot; key=&quot;p&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;minLength&quot; value=&quot;1&quot;&gt;Please enter a password&lt;/rule&gt;
&lt;/field&gt;
&lt;/group&gt;
&lt;/input-data&gt;
</pre></div>
<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 <code>name</code> attribute which is the name that will be used
within templates and java code to refer to the group. It also contains a
<code>key</code> 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: <code>name</code> and <code>key</code> which serve
similar function to the group's <code>name</code> and <code>key</code>
attributes. It has <code>mapToObject</code> and <code>mapToProperty</code> 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 <code>type</code> out of the following
supported types:
</p>
<ul>
<li><code>boolean</code></li>
<li><code>BigDecimal</code> This field type supports localization.</li>
<li><code>int</code></li>
<li><code>float</code> This field type supports localization.</li>
<li><code>FileItem</code></li>
<li><code>String</code> (default)</li>
<li><code>DateString</code></li>
<li><code>double</code> This field type supports localization.</li>
<li><code>short</code></li>
<li><code>long</code></li>
<li><code>custom</code> Custom field types can be defined by using this type.
The class name of the customized field must be given in the <code>fieldClass</code>
attribute then. The custom class must extend the class
<code>org.apache.fulcrum.intake.model.Field</code>. This way, more
complex types, such as Email, Url, or Date, can be added that add functionality
that is difficult or not possible with a simple regex mask.</li>
</ul>
The field types supporting localization will parse the given strings from the
request using the locale from the parser to allow the entry of floating point numbers with
localized decimal separators.
<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>
<h2><a name="Login_Example"></a>Login Example</h2>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;Login&quot; key=&quot;login&quot;&gt;
</pre></div>
<p>
The name=&quot;Login&quot; is a descriptive name for the group. The key=&quot;login&quot; 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>
<div class="source"><pre class="prettyprint">
&lt;field name=&quot;Username&quot; key=&quot;u&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;minLength&quot; value=&quot;1&quot;&gt;Please enter an email address&lt;/rule&gt;
&lt;rule name=&quot;mask&quot; value=&quot;.+@.+\..+&quot;&gt;Please enter a valid email address&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;Password&quot; key=&quot;p&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;minLength&quot; value=&quot;1&quot;&gt;Please enter a password&lt;/rule&gt;
&lt;/field&gt;
</pre></div>
<p>
The name=&quot;Username&quot; is the descriptive name for the field. The key=&quot;u&quot;
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=&quot;String&quot; 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>
<div class="source"><pre class="prettyprint">
(1) &lt;form action=&quot;$link.setPage(&quot;Login.vm&quot;)&quot; method=&quot;POST&quot; name=&quot;login&quot; &gt;
(2) &lt;input type=&quot;hidden&quot; name=&quot;action&quot; value=&quot;Login&quot;&gt;
(3) #if ($data.Parameters.nextTemplate)
(4) &lt;input type=&quot;hidden&quot; name=&quot;nextTemplate&quot;
value=&quot;$data.Parameters.nextTemplate&quot;&gt;
#else
(5) &lt;input type=&quot;hidden&quot; name=&quot;nextTemplate&quot; value=&quot;Start.vm&quot;&gt;
#end
&lt;p&gt;
Email Address:
(6) #set ( $loginGroup = $intake.Login.Default )
(7) #if ( !$loginGroup.Username.isValid() )
(8) $loginGroup.Username.Message&lt;br&gt;
#end
(9) &lt;input name= &quot;$loginGroup.Username.Key&quot;
value=&quot;$!loginGroup.Username&quot; size=&quot;25&quot; type=&quot;text&quot;&gt;
&lt;/p&gt;
&lt;p&gt;
Password:
(10) #if ( !$loginGroup.Password.isValid() )
(11) $loginGroup.Password.Message&lt;br&gt;
#end
(12) &lt;input name= &quot;$loginGroup.Password.Key&quot;
value=&quot;&quot; size=&quot;25&quot; type=&quot;text&quot;
onChange=&quot;document.login.submit();&quot;&gt;
&lt;/p&gt;
(13) &lt;input type=&quot;submit&quot; name=&quot;eventSubmit_doLogin&quot; value=&quot;Login&quot;&gt;
(14) $intake.declareGroups()
&lt;/form&gt;
</pre></div>
<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>
</p>
<ol style="list-style-type: decimal">
<li>
Create the &lt;form&gt; tag. Within it, we use the $link object to
create a URI for the template &quot;Login.vm&quot;. In other words, when the
button is clicked, the page will submit upon itself.
</li>
<li>
Set the Action to execute to be &quot;Login&quot;. This can either be a hidden
input field or be defined with the
$link.setPage().setAction(&quot;Login&quot;) method
</li>
<li>
Check to see if there is a &quot;nextTemplate&quot; 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 &quot;Start.vm&quot; page.
</li>
<li>
This retrieves the default Login Group object from Intake. What this
means is that the group &quot;Login&quot; 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>
Below is an example of the HTML that is sent to the browser after the
page has been requested:
</p>
<div class="source"><pre class="prettyprint">
&lt;form action=&quot;http://foo:8080/scarab/servlet/scarab/template/Login.vm&quot;
method=&quot;POST&quot; name=&quot;login&quot; &gt;
&lt;input type=&quot;hidden&quot; name=&quot;action&quot; value=&quot;Login&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;nextTemplate&quot; value=&quot;Start.vm&quot;&gt;
&lt;p&gt;
Email Address:
&lt;input name= &quot;login_0u&quot;
value=&quot;&quot; size=&quot;25&quot; type=&quot;text&quot;&gt;
&lt;/p&gt;
&lt;p&gt;
Password:
&lt;input name= &quot;login_0p&quot;
value=&quot;&quot; size=&quot;25&quot; type=&quot;text&quot; onchange=&quot;document.login.submit();&quot;&gt;
&lt;/p&gt;
&lt;input type=&quot;submit&quot; name=&quot;eventSubmit_doLogin&quot; value=&quot;Login&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;intake-grp&quot; value=&quot;login&quot;&gt;&lt;/input&gt;
&lt;input type=&quot;hidden&quot; name=&quot;login&quot; value=&quot;_0&quot;&gt;&lt;/input&gt;
&lt;/form&gt;
</pre></div>
<p>
Some notes to consider:
</p>
<ol style="list-style-type: decimal">
<li>
The _0 signifies the &quot;default&quot; group.
</li>
<li>
The login_0u signifies the login group combined with the _0 and the
&quot;u&quot; is from the intake.xml file for the field &quot;Username&quot;.
</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>
<div class="source"><pre class="prettyprint">
public void doLogin( PipelineData pipelineData, Context context ) throws Exception
{
// get a reference to the run data object
RunData data = (RunData) pipelineData;
IntakeTool intake = (IntakeTool) context.get(&quot;intake&quot;);
if ( intake.isAllValid() &amp;amp;&amp;amp; checkUser(data, context) )
{
String template = data.getParameters()
.getString(ScarabConstants.NEXT_TEMPLATE,
TurbineResources.getString(&quot;template.homepage&quot;, &quot;Start.vm&quot;) );
setTemplate(data, template);
}
else
{
// Retrieve an anonymous user
data.setUser (TurbineSecurity.getAnonymousUser());
setTemplate(data,
data.getParameters()
.getString(ScarabConstants.TEMPLATE, &quot;Login.vm&quot;));
}
}
/**
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(&quot;Login&quot;, IntakeTool.DEFAULT_KEY);
username = login.get(&quot;Username&quot;).toString();
password = login.get(&quot;Password&quot;).toString();
}
catch ( Exception e )
{
throw new TurbineSecurityException(
&quot;Login information was not supplied.&quot;);
}
// Authenticate the user and get the object.
user = TurbineSecurity.getAuthenticatedUser( username, password );
...
}
}
</pre></div>
<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>
<h2><a name="Attribute_Value_example"></a>Attribute Value example</h2>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;AttributeValue&quot; key=&quot;attv&quot;
mapToObject=&quot;om.AttributeValue&quot;&gt;
</pre></div>
<p>
The name=&quot;AttributeValue&quot; is simply a descriptive name for the group.
The key=&quot;attv&quot; 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=&quot;om.AttributeValue&quot; 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>
<div class="source"><pre class="prettyprint">
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Value length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires data for
this attribute.
&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;Url&quot; key=&quot;url&quot; type=&quot;String&quot; mapToProperty=&quot;Value&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Url length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;mask&quot; value=&quot;^$|http.+&quot;&gt;Please enter an url starting with &quot;http&quot;&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires a valid url.&lt;/rule&gt;
&lt;/field&gt;
</pre></div>
<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:
&quot;EditUrl.vm&quot;.
</p>
<div class="source"><pre class="prettyprint">
#set ( $action = $link.setPage(&quot;EditUrl.vm&quot;).setAction(&quot;SaveUrl&quot;) )
&lt;form action=&quot;$action&quot;
method=&quot;post&quot;&gt;
#set ( $attributeValue = $issue.AttributeValue(&quot;URL&quot;) )
#set ( $group = $intake.AttributeValue.mapTo($attributeValue) )
Enter Url:
&lt;input type=&quot;text&quot; name=&quot;$group.Url.Key&quot; value=&quot;$!group.Url.Value&quot;&gt;
&lt;input type=&quot;submit&quot; name=&quot;eventSubmit_doSave&quot; value=&quot;Submit&gt;
$intake.declareGroups()
&lt;/form&gt;
</pre></div>
<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 &quot;URL&quot; 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
&quot;attv_0url&quot;. What this is a combination of the group key (attv), a &quot;_0&quot;
is the result of retrieving the &quot;$intake.AttributeValue.Default&quot; and the
&quot;url&quot; 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>
<div class="source"><pre class="prettyprint">
&lt;form action=&quot;http://server/s/servlet/s/template/EnterUrl.vm/action/EnterUrlAction&quot;
method=&quot;post&quot;&gt;
Enter Url:
&lt;input type=&quot;text&quot; name=&quot;attv_0url&quot; value=&quot;&quot;&gt;
&lt;input type=&quot;submit&quot; name=&quot;eventSubmit_doEnter&quot; value=&quot;Submit&gt;
&lt;input type=&quot;hidden&quot; name=&quot;attv&quot; value=&quot;_0&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;intake-grp&quot; value=&quot;attv&quot;&gt;
&lt;/form&gt;
</pre></div>
<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>
<div class="source"><pre class="prettyprint">
public void doEnter( PipelineData pipelineData, 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 &quot;AttributeValue&quot; Group
AttributeValue av = new AttributeValue();
Group group = intake.get(&quot;AttributeValue&quot;, IntakeTool.DEFAULT_KEY);
group.setProperties (av);
// now av is properly populated with the form data
}
}
</pre></div>
<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>
<div class="source"><pre class="prettyprint">
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Value length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires data for
this attribute.
&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;Url&quot; key=&quot;url&quot; type=&quot;String&quot; mapToProperty=&quot;Value&quot;&gt;
&lt;rule name=&quot;maxLength&quot; value=&quot;255&quot;&gt;Url length cannot be &amp;gt; 255&lt;/rule&gt;
&lt;rule name=&quot;mask&quot; value=&quot;^$|http.+&quot;&gt;Please enter an url starting with &quot;http&quot;&lt;/rule&gt;
&lt;rule name=&quot;required&quot; value=&quot;true&quot;&gt;This module requires a valid url.&lt;/rule&gt;
&lt;/field&gt;
</pre></div>
</section>
<section>
<h2><a name="Multiple_groups_of_the_same_class_in_one_form"></a>Multiple groups of the same class in one form</h2>
<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>
<div class="source"><pre class="prettyprint">
#set ( $action = $link.setPage(&quot;entry,Wizard3.vm&quot;).setAction(&quot;ReportIssue&quot;)
.addPathInfo(&quot;nextTemplate&quot;, &quot;entry,Wizard4.vm&quot;) )
#set ($user = $scarabR.User)
#set ($module = $user.CurrentModule)
#set ($issue = $user.ReportingIssue)
&lt;form method=&quot;get&quot; action=&quot;$action&quot;&gt;
&lt;hr&gt;&lt;br&gt;Please fill in the following:&lt;br&gt;&lt;br&gt;
#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 == &quot;combo-box&quot; )
#set ( $field = &quot;OptionId&quot; )
#else
#set ( $field = &quot;Value&quot; )
#end
#if ( $attVal.isRequired() )
$attrInput.get($field).setRequired(true)
&lt;b&gt;*&lt;/b&gt;
#end
$attVal.Attribute.Name:
#if ($attVal.Attribute.AttributeType.Name == &quot;combo-box&quot; )
&lt;font color=&quot;red&quot;&gt;
#attrValueErrorMsg ( $attVal $field )
&lt;/font&gt;
&lt;br&gt;
#attrValueSelect ($attVal $field &quot;&quot;)
#else
&lt;font color=&quot;red&quot;&gt;
#attrValueErrorMsg ( $attVal $field )
&lt;/font&gt;
&lt;br&gt;
#if ($attVal.Attribute.AttributeType.Name == &quot;long-string&quot; )
&lt;textarea name= &quot;$attrInput.Value.Key&quot; cols=&quot;40&quot;
rows=&quot;5&quot;&gt;$!attrInput.Value&lt;/textarea&gt;
#else
&lt;input name= &quot;$attrInput.Value.Key&quot;
value=&quot;$!attrInput.Value&quot; size=&quot;20&quot; type=&quot;text&quot;&gt;
#end
&lt;br&gt;&lt;br&gt;
#end
#end
&lt;p&gt;
&lt;input type=&quot;submit&quot;
name=&quot;eventSubmit_doEnterissue&quot; value=&quot;Submit Issue&quot;&gt;
$intake.declareGroups()
&lt;/form&gt;
</pre></div>
<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>
<div class="source"><pre class="prettyprint">
public void doEnterissue( PipelineData pipelineData, Context context )
throws Exception
{
// get a reference to the run data object
RunData data = (RunData) pipelineData;
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(&quot;SUMMARY&quot;);
Group group = intake.get(&quot;AttributeValue&quot;, aval.getQueryKey());
Field summary = group.get(&quot;Value&quot;);
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(&quot;AttributeValue&quot;, aval.getQueryKey());
if ( group != null )
{
group.setProperties(aval);
}
}
if ( issue.containsMinimumAttributeValues() )
{
issue.save();
String template = data.getParameters()
.getString(ScarabConstants.NEXT_TEMPLATE,
&quot;entry,Wizard3.vm&quot;);
setTemplate(data, template);
}
}
}
</pre></div>
<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>
<div class="source"><pre class="prettyprint">
Dan Diephouse wrote:
&gt;
&gt; I want to validate a form that updates my business object properties
&gt; with intake. I got the latest cvs of turbine-2 repository and built a
&gt; new distribution of the turbine jar. I can easily use intake to add
&gt; validate items I add to my databse, but I'm running in to a snag when
&gt; trying to modify them. I've defined by business object in the intake
&gt; validation file. I use the following code to update my item:
&gt;
&gt; IntakeTool intake = (IntakeTool) context.get(&quot;intake&quot;);
&gt;
&gt; ParameterParser pp = data.getParameters();
&gt;
&gt; if ( intake.isAllValid() ) {
&gt; Job j = new Job();
&gt; j.setNew(false);
&gt; group.setProperties(j);
&gt;
&gt; JobPeer.doUpdate(j);
&gt;
&gt; Error--&gt; data.getParameters().add(&quot;jobid&quot;, j.getJobId().toString());
&gt; data.setMessage(&quot;Job updated.&quot;);
&gt; } else {
&gt; data.setMessage(&quot;There was an error updating the job.
&gt; Check below for further information.&quot;);
&gt; }
&gt;
&gt; It runs OK, until I try and retrieve the JobId. I get a Null Pointer
&gt; exception. Is there something I'm doing wrong here? Or is this a bug?
&gt; Do I need to specify that this is not a new object to intake? I would
&gt; include my template, but there are lots of fields so its really long.
&gt; But here's a little summary with most fields gone:
&gt;
&gt; #set ( $job = $basecamp.getJob() ) - This gets a job from a pull tool
&gt; #set ( $jobGroup = $intake.Job.mapTo($job) )
&gt; &lt;input type=hidden name=&quot;$jobGroup.JobId.Key&quot;
&gt; value=&quot;$jobGroup.JobId.Value&quot;&gt;
&gt;
&gt; #if ( !$jobGroup.Title.isValid() )
&gt; $jobGroup.Title.Message&lt;br&gt;
&gt; #end
&gt; &lt;input type=text name=&quot;$jobGroup.Title.Key&quot;
&gt; value=&quot;$!jobGroup.Title.Value&quot; size=&quot;50&quot;&gt;
&gt; .
&gt; .
&gt; .
&gt; .
&gt; $intake.declareGroups()
&gt;
&gt; Thanks,
&gt;
&gt; 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 &quot;_0&quot;, 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(&quot;Job&quot;, 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
</pre></div>
</section>
<section>
<h2><a name="Default_values_for_fields"></a>Default values for fields</h2>
<p>
If you want to use input fields which should default to non-empty values for,
new objects, you can use the defaultValue field:
</p>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;test&quot; key=&quot;test&quot;&gt;
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot; defaultValue=&quot;preset&quot;&gt;
&lt;/field&gt;
&lt;/group&gt;
</pre></div>
<p>If you set up a form like this:</p>
<div class="source"><pre class="prettyprint">
#if($!dz)
#set ($frm = $intake.test.mapTo($t))
$frm.Mode.
#else
#set ($frm = $intake.test.default )
#end
&lt;FORM NAME=&quot;entryForm&quot;&gt;
&lt;INPUT type=&quot;text&quot; name=&quot;$frm.get(&quot;val&quot;).Key&quot; value=&quot;$!frm.get(&quot;val&quot;)&quot;&gt;
&lt;/FORM&gt;
</pre></div>
<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>
<section>
<h2><a name="Empty_fields"></a>Empty fields</h2>
<p>
Sometimes you have form fields which are not required (can be left
empty) but should be mapped onto a non null value in your bean. This
is especially true if you use string fields in conjunction with
&quot;required&quot; columns in a database. For this case, you can preset an
&quot;empty&quot; value for each field which is used if a field is missing or
empty in the form parameters returned by the browser. If you don't set
this, the default is &quot;null&quot;.
</p>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;test&quot; key=&quot;test&quot;&gt;
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot; emptyValue=&quot;&quot;/&gt;
&lt;/group&gt;
</pre></div>
<p>This would return the empty String (not a null value) for the &quot;val&quot; field
if the user does not enter anything. Without the emptyValue parameter, intake
would assign the null value to the bean field.</p>
</section>
<section>
<h2><a name="Display_attributes_for_fields"></a>Display attributes for fields</h2>
<p>
The Intake DTD allows additional attributes that can help you in your
template. The <code>displayName</code> can be used to provide a label
for the given field. The <code>displaySize</code> can be used to define
the displayed size of the field (either numerical or as a CSS-style).
The Intake tool $intake provides these values as
<code>$field.DisplayName</code> and <code>$field.DisplaySize</code>,
respectively.
</p>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;test&quot; key=&quot;test&quot;&gt;
&lt;field name=&quot;Value&quot; key=&quot;val&quot; type=&quot;String&quot;
displayName=&quot;Test Value&quot;
displaySize=&quot;150px&quot;
/&gt;
&lt;/group&gt;
</pre></div>
<p>This could be used in the template like this:</p>
<div class="source"><pre class="prettyprint">
#set ($tg = $intake.test.Default)
$tg.Value.DisplayName:
&lt;input type=&quot;text&quot; name=&quot;$tg.Value.Key&quot; value=&quot;$!tg.Value.HTMLString&quot;
style=&quot;width:${tg.Value.DisplaySize};&quot; /&gt;
</pre></div>
</section>
<section>
<h2><a name="Custom_validators"></a>Custom validators</h2>
<p>
Fields can have custom validators through the help of the
<code>validator</code> attribute. If the validator class is not
qualified (i.e. has no package) the package
<code>org.apache.turbine.services.intake.validator</code> is
assumed. Validator classes must implement the
<code>org.apache.turbine.services.intake.validator.Validator</code>
interface. If the validator needs initialization from the list
of constraints (based on the <code>rule</code> elements in the
definition file), it also must implement
<code>org.apache.turbine.services.intake.validator.InitableByConstraintMap</code>.
It is recommended to extend one of the existing validator classes
which live in the <code>org.apache.turbine.services.intake.validator</code>
package. In this validator class, override <code>assertValidity(String)</code>,
don't forget to call <code>super.assertValidity(testValue)</code> and add
your own checks to the validator.
</p>
<p>
For validator classes extending <code>DefaultValidator</code>
the most important method to implement is <code>assertValidity()</code>.
There are two signatures of this method. If you just need to compare
the value entered to a list of database entries, for example, it
is sufficient to implement <code>assertValidity(String)</code>. The
default code handles multi-valued fields and other stuff for you. If
you want to do advanced checks and comparisons, you need to implement
<code>assertValidity(Field)</code>. Here you can check for locale,
associated group and values of other fields in the same group. See
the following section for examples.
</p>
</section>
<section>
<h2><a name="Validating_dependent_fields"></a>Validating dependent fields</h2>
<p>
Sometimes it is necessary to validate fields not only against a set of
constant rules but also against the values of other fields in the same
form or group. For these cases, Intake provides the helper class
<code>FieldReference</code>. It provides additional rules to compare
fields to other fields:
</p>
<table border="0" class="table table-striped">
<tr class="a">
<th>Rule Name</th>
<th>Comparison</th>
<th>Rule Value</th>
</tr>
<tr class="b">
<td>less-than</td>
<td align="center">&lt;</td>
<td>&lt;name of other field&gt;</td>
</tr>
<tr class="a">
<td>greater-than</td>
<td align="center">&gt;</td>
<td>&lt;name of other field&gt;</td>
</tr>
<tr class="b">
<td>less-than-or-equal</td>
<td align="center">&lt;=</td>
<td>&lt;name of other field&gt;</td>
</tr>
<tr class="a">
<td>greater-than-or-equal</td>
<td align="center">&gt;=</td>
<td>&lt;name of other field&gt;</td>
</tr>
</table>
<p>
Two examples of implementations of these comparison rules are
available as <code>DateRangeValidator</code> and
<code>IntegerRangeValidator</code>. An example on how to use these
is shown below.
</p>
<div class="source"><pre class="prettyprint">
&lt;group name=&quot;DateRange&quot; key=&quot;dr&quot;&gt;
&lt;field name=&quot;DateFrom&quot; key=&quot;dmin&quot; type=&quot;DateString&quot;&gt;
&lt;rule value=&quot;MM/dd/yyyy&quot; name=&quot;format&quot;&gt;Invalid format&lt;/rule&gt;
&lt;/field&gt;
&lt;field name=&quot;DateTo&quot; key=&quot;dmax&quot; type=&quot;DateString&quot;
validator=&quot;DateRangeValidator&quot;&gt;
&lt;rule value=&quot;MM/dd/yyyy&quot; name=&quot;format&quot;&gt;Invalid format&lt;/rule&gt;
&lt;rule value=&quot;DateFrom&quot; name=&quot;greater-than&quot;&gt;
To-Date must be greater than From-Date&lt;/rule&gt;
&lt;/field&gt;
&lt;/group&gt;
</pre></div>
</section>
</main>
</div>
</div>
<hr/>
<footer>
<div class="container-fluid">
<div class="row-fluid">
<p>&#169; 2005&#x2013;2021
<a href="https://www.apache.org/">The Apache Software Foundation</a>
</p>
</div>
</div>
</footer>
</body>
</html>