[cvs] expresso/expresso-web/expresso/doc/edg validation.xml

JCorporate Ltd jcorp at jcorp2.servlets.net
Mon Aug 9 09:30:29 PDT 2004


Update of /home/javacorp/.cvs/expresso/expresso/expresso-web/expresso/doc/edg
In directory jcorp2.servlets.net:/tmp/cvs-serv16617

Added Files:
	validation.xml 
Log Message:
validator chapter for EDG


--- NEW FILE: validation.xml ---
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="validation" xreflabel="Validating user input">
  <title>Validating user input</title>

  <para><note>
      <para>If you find this EDG documentation helpful please consider <link
      linkend="donate_validation">DONATING</link>! to keep the doc alive and
      current.</para>
    </note></para>

  <para><informaltable colsep="0" frame="none" pgwide="1" rowsep="0">
      <tgroup cols="2" colsep="0" rowsep="0">
        <colspec align="left" colsep="0" colwidth="50%" rowsep="0" />

        <colspec align="right" colsep="0" colwidth="50%" rowsep="0" />

        <tbody>
          <row>
            <entry><emphasis role="bold">Version:</emphasis> <emphasis
            role="version">Expresso 5.6</emphasis></entry>

            <entry><emphasis role="bold">Maintainer:</emphasis><ulink
            url="mailto:r.davidovich at dmci.eu.com?Subject=EDG"><emphasis
            role="maintainer">Raul Davidovich</emphasis></ulink></entry>
          </row>
        </tbody>
      </tgroup>
    </informaltable></para>

  <para>Since 5.6, Expresso has integrated Struts Validator. This framework
  makes input validation very simple and flexible. Struts Validator can be
  called automatically when submitting the form, or manually from the handle
  State. It can also automatically generate client-side Javascript form
  validation. As all Struts components, Validator is configured by XML files,
  and it’s designed to be modular. In Expresso, Validator has been modified to
  use the handle state name instead of the form name for identifying the forms
  to validate, simplifying the use of multi-page forms.</para>

  <sect1 id="validatorSetUp">
    <title>Setting up the Validator plugin</title>

    <para>To set up the plugin, one must add it to the struts-config.xml file,
    as follows: <programlisting>  &lt;struts-config&gt;
    &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
      &lt;set-property property="pathnames" value="/WEB-INF/config/validator-rules.xml,/WEB-INF/config/validation.xml" /&gt;
    &lt;/plug-in&gt;
  &lt;/struts-config&gt;</programlisting> The pathnames property tells the
    plugin where to find the configuration files. You can set it to wherever
    you like. The validation-rules.xml file describes each rule that can be
    applied, saying which class will actually do the job, and the javascript
    code used in client-side validation. The validation.xml file sets which
    rules to apply to each field of each form to validate, and what response
    to give if the validation fails. Configuring the rules to apply for
    validating the forms The form validation is configured in the
    validation.xml file as follows: <programlisting>  &lt;form-validation&gt;
    &lt;global /&gt;
    &lt;formset&gt;
      &lt;form name="myHandleState"&gt;
        &lt;field depends="required,minlength" property="myProp"&gt;
          &lt;arg0 key="myProp is required" resource="false" /&gt;
          &lt;var&gt;
            &lt;var-name&gt;minlength&lt;/var-name&gt;
            &lt;var-value&gt;5&lt;/var-value&gt;
          &lt;/var&gt;
          &lt;msg key="myapp.errormsgs.minlengthmsg" name="minlength" /&gt;
        &lt;/field&gt;
      &lt;/form&gt;
    &lt;/formset&gt;
    &lt;formset language="fr"&gt;
      &lt;form name="myHandleState"&gt;
        &lt;field depends="required,minlength" property="myProp"&gt;
          &lt;arg0 key="myProp est requis" resource="false" /&gt;
          &lt;var&gt;
            &lt;var-name&gt;minlength&lt;/var-name&gt;
            &lt;var-value&gt;5&lt;/var-value&gt;
          &lt;/var&gt;
          &lt;msg key="myapp.errormsgs.minlengthmsg" name="minlength" /&gt;
        &lt;/field&gt;
      &lt;/form&gt;
    &lt;/formset&gt;
  &lt;/form-validation&gt;</programlisting> Each &lt;formset&gt; tag contains
    &lt;form&gt; tags.Each form to validate should have it’s &lt;form&gt; tag.
    The “name” property must be the same as the handle state for this form.
    The &lt;form&gt; tag contains one &lt;field&gt; tag for each field to
    check when validating the form. The “name” property must be the same as
    the field name (the Input) in the web page. The “depends” property defines
    which rules to apply to this field. Inside the &lt;field&gt; tag can be
    nested:<itemizedlist>
        <listitem>
          <para>&lt;argn&gt; tags defining replacement parameters for any
          messages returned to the user. N is the order of the message to
          replace.</para>
        </listitem>

        <listitem>
          <para>&lt;var&gt; tags with variable properties to pass to the
          validation functions (the maximum length of the field, a mask to
          apply, etc)</para>
        </listitem>

        <listitem>
          <para>&lt;msg&gt; tags specifying custom message keys for
          validators</para>
        </listitem>
      </itemizedlist> &lt;argn&gt; tags defining replacement parameters for
    any messages returned to the user. N is the order of the message to
    replace. &lt;var&gt; tags with variable properties to pass to the
    validation functions (the maximum length of the field, a mask to apply,
    etc) &lt;msg&gt; tags specifying custom message keys for validators If no
    replacement messages are defined, the default messages from
    validator-rules.xml will be used. Global constants can be inside the
    &lt;global tags&gt; and FormSet/Locale constants can be created in the
    &lt;formset&gt; tags. Constants are currently only replaced in the Field's
    property attribute, the Field's &lt;var&gt; element value attribute, the
    Field's &lt;msg&gt; element key attribute, and Field's &lt;arg0&gt; -
    &lt;arg3&gt; element's key attribute. The order of replacement is
    FormSet/Locale constants are replaced first, Global constants second, and
    for the &lt;arg&gt; elements variables are replaced last: <programlisting>  &lt;global&gt;
    &lt;constant&gt;
      &lt;constant-name&gt;zip&lt;/constant-name&gt;
      &lt;constant-value&gt;^\d{5}(-\d{4})?$&lt;/constant-value&gt;
    &lt;/constant&gt;
  &lt;/global&gt;
  &lt;field depends="required,mask" property="zip"&gt;
    &lt;arg0 key="registrationForm.zippostal.displayname" /&gt;
    &lt;var&gt;
      &lt;var-name&gt;mask&lt;/var-name&gt;
      &lt;var-value&gt;${zip}&lt;/var-value&gt;
    &lt;/var&gt;
  &lt;/field&gt;</programlisting> The var element under a field can be used to
    store variables for use by a pluggable validator. These variables are
    available through the Field's getVar(String key) method: <programlisting>  &lt;field depends="required,integer,intRange" property="integer"&gt;
    &lt;arg0 key="typeForm.integer.displayname" /&gt;
    &lt;arg1 key="${var:min}" name="intRange" resource="false" /&gt;
    &lt;arg2 key="${var:max}" name="intRange" resource="false" /&gt;
    &lt;var&gt;
      &lt;var-name&gt;min&lt;/var-name&gt;
      &lt;var-value&gt;10&lt;/var-value&gt;
    &lt;/var&gt;
    &lt;var&gt;
      &lt;var-name&gt;max&lt;/var-name&gt;
      &lt;var-value&gt;20&lt;/var-value&gt;
    &lt;/var&gt;
  &lt;/field&gt;</programlisting></para>
  </sect1>

  <sect1 id="builtInValidators">
    <title>Standard Built In Validations</title>

    <indexterm>
      <primary>Validator</primary>

      <secondary>Built In</secondary>
    </indexterm>

    <para>Validator is shipped with the following set of pre-defined
    validation rules. required - mandatory field validation. Has no variables.
    <programlisting>    &lt;field property="name" depends="required"&gt;
        &lt;arg0 key="customer.name"/&gt;
    &lt;/field&gt;</programlisting> requiredif - field dependant validator
    Deprecated, use validwhen. validwhen - validator for checking one field
    against another. see later section titled Designing "Complex Validations
    with validwhen". minlength - validate input data isn't less than a
    specified minimum length. Requires a minlength variable. <programlisting>    &lt;field property="name" depends="required,minlength"&gt;
        &lt;arg0 key="customer.name"/&gt;
        &lt;arg1 name="minlength" key="${var:minlength}" resource="false"/&gt;
        &lt;var&gt;&lt;var-name&gt;minlength&lt;/var-name&gt;&lt;var-value&gt;3&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> maxlength - validate input data doesn't
    exceed a specified maximum length. Requires a maxlength variable.
    <programlisting>    &lt;field property="name" depends="required,maxlength"&gt;
        &lt;arg0 key="customer.name"/&gt;
        &lt;arg1 name="maxlength" key="${var:maxlength}" resource="false"/&gt;
        &lt;var&gt;&lt;var-name&gt;maxlength&lt;/var-name&gt;&lt;var-value&gt;30&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> mask - validate format according to a
    regular expression. Requires a mask variable to specify the regular
    expression. Since version 1.1, the regular expression must start with a ^
    and end with a $ (see example below). <programlisting>    &lt;field property="name" depends="required,mask"&gt;
        &lt;msg name="mask" key="registrationForm.lastname.maskmsg"/&gt;
        &lt;arg0 key="registration.name"/&gt;
        &lt;var&gt;&lt;var-name&gt;mask&lt;/var-name&gt;&lt;var-value&gt;^[a-zA-Z]*$&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> byte - validates that a field can be
    converted to a Byte. <programlisting>    &lt;field property="age" depends="byte"&gt;
        &lt;arg0 key="employee.age"/&gt;
    &lt;/field&gt;</programlisting> short - validates that a field can be
    converted to a Short. <programlisting>    &lt;field property="productnumber" depends="short"&gt;
        &lt;arg0 key="order.prodno"/&gt;
    &lt;/field&gt;</programlisting> integer - validates that a field can be
    converted to an Integer. <programlisting>    &lt;field property="ordernumber" depends="integer"&gt;
        &lt;arg0 key="order.number"/&gt;
    &lt;/field&gt;</programlisting> long - validates that a field can be
    converted to a Long. <programlisting>    &lt;field property="ordernumber" depends="long"&gt;
        &lt;arg0 key="order.number"/&gt;
    &lt;/field&gt;</programlisting> float - validates that a field can be
    converted to a Float. <programlisting>    &lt;field property="amount" depends="float"&gt;
        &lt;arg0 key="sale.amount"/&gt;
    &lt;/field&gt;</programlisting> double - validates that a field can be
    converted to a Double. <programlisting>    &lt;field property="amount" depends="double"&gt;
        &lt;arg0 key="sale.amount"/&gt;
    &lt;/field&gt;</programlisting> date - validates that a field can be
    converted to a Date. This validation rule uses java.text.SimpleDateFormat
    to parse the date and optionally either a datePattern or datePatternStrict
    variable can be used. If no pattern is specified the default short date
    format is assumed. The difference between using the datePatternStrict and
    datePattern variables is that datePatternStrict checks additionally that
    the input data is the same length as the pattern specified (so for example
    1/1/2004 would fail with a pattern of MM/dd/yyyy). <programlisting>    &lt;field property="saledate" depends="required,date"&gt;
        &lt;arg0 key="myForm.saledate"/&gt;
        &lt;var&gt;&lt;var-name&gt;datePattern&lt;/var-name&gt;&lt;var-value&gt;MM/dd/yyyy&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;
    

    &lt;field property="saledate" depends="required,date"&gt;
        &lt;arg0 key="sale.orderdate"/&gt;
        &lt;var&gt;&lt;var-name&gt;datePatternStrict&lt;/var-name&gt;&lt;var-value&gt;MM/dd/yyyy&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> range - validate number range. Deprecated,
    use intRange, floatRange or doubleRange. intRange - validates that an
    integer field is within a specified range. Requires min and max variables
    to specify the range. This validator depends on the integer validator
    which must also be in the field's depends attribute. <programlisting>    &lt;field property="age" depends="required,integer,intRange"&gt;
        &lt;arg0 key="employee.age"/&gt;
        &lt;arg1 name="intRange" key="${var:min}" resource="false"/&gt;
        &lt;arg2 name="intRange" key="${var:max}" resource="false"/&gt;
        &lt;var&gt;&lt;var-name&gt;min&lt;/var-name&gt;&lt;var-value&gt;18&lt;/var-value&gt;&lt;/var&gt;
        &lt;var&gt;&lt;var-name&gt;max&lt;/var-name&gt;&lt;var-value&gt;65&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> floatRange - validates that a float field
    is within a specified range Requires min and max variables to specify the
    range. This validator depends on the float validator which must also be in
    the field's depends attribute. <programlisting>    &lt;field property="ordervalue" depends="required,float,floatRange"&gt;
        &lt;arg0 key="order.value"/&gt;
        &lt;arg1 name="floatRange" key="${var:min}" resource="false"/&gt;
        &lt;arg2 name="floatRange" key="${var:max}" resource="false"/&gt;
        &lt;var&gt;&lt;var-name&gt;min&lt;/var-name&gt;&lt;var-value&gt;100&lt;/var-value&gt;&lt;/var&gt;
        &lt;var&gt;&lt;var-name&gt;max&lt;/var-name&gt;&lt;var-value&gt;4.99&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> doubleRange - validates that a double
    field is within a specified range Requires min and max variables to
    specify the range. This validator depends on the double validator which
    must also be in the field's depends attribute. <programlisting>    &lt;field property="ordervalue" depends="required,double,doubleRange"&gt;
        &lt;arg0 key="employee.age"/&gt;
        &lt;arg1 name="doubleRange" key="${var:min}" resource="false"/&gt;
        &lt;arg2 name="doubleRange" key="${var:max}" resource="false"/&gt;
        &lt;var&gt;&lt;var-name&gt;min&lt;/var-name&gt;&lt;var-value&gt;100&lt;/var-value&gt;&lt;/var&gt;
        &lt;var&gt;&lt;var-name&gt;max&lt;/var-name&gt;&lt;var-value&gt;4.99&lt;/var-value&gt;&lt;/var&gt;
    &lt;/field&gt;</programlisting> creditCard - validate credit card number
    format <programlisting>    &lt;field property="name" depends="required, creditCard"&gt;
        &lt;arg0 key="customer.cardnumber"/&gt;
    &lt;/field&gt;</programlisting> email - validate email address format
    <programlisting>    &lt;field property="customeremail" depends="email"&gt;
        &lt;arg0 key="customer.email"/&gt;
    &lt;/field&gt;</programlisting> url - validates url format. Has four
    optional variables (allowallschemes, allow2slashes, nofragments and
    schemes) which can be used to configure this validator. allowallschemes
    specifies whether all schemes are allowed. Valid values are true or false
    (default is false). If this is set to true then the schemes variable is
    ignored. allow2slashes specifies whether double '/' characters are
    allowed. Valid values are true or false (default is false). nofragments
    specifies whether fragements are allowed. Valid values are true or false
    (default is false - i.e. fragments are allowed). schemes - use to specify
    a comma separated list of valid schemes. If not specified then the
    defaults are used which are http, https and ftp. <programlisting>    &lt;field property="custUrl" depends="url"&gt;
        &lt;arg0 key="customer.url"/&gt;
    &lt;/field&gt;

    &lt;field property="custUrl" depends="url"&gt;
        &lt;arg0 key="customer.url"/&gt;
        &lt;var&gt;
           &lt;var-name&gt;nofragments&lt;/var-name&gt;
           &lt;var-value&gt;true&lt;/var-value&gt;
        &lt;/var&gt;
        &lt;var&gt;
           &lt;var-name&gt;schemes&lt;/var-name&gt;
           &lt;var-value&gt;http,https,telnet,file&lt;/var-value&gt;
        &lt;/var&gt;
    &lt;/field&gt;</programlisting> </para>
  </sect1>

  <sect1 id="defineableAndDBMaint">
    <title>Manually validating a form</title>

    <para><emphasis>NOTE: for manual validation to work as illustrated here,
    you should either not use custom forms, or have your custom forms to
    extend DefaultForm. </emphasis> Manual validation is very simple. If
    you’re using external states, you simply call
    myStateForm.validate(ControllerRequest request) in your handle state, and
    the method will return an ErrorCollection with the failed validation
    messages (or empty if the form passed all the validations):
    <programlisting>public class MyHandleState
    extends State {

  public MyHandleState() {
    super();
  }

  public MyHandleState(String newName, String newDescrip) {
    super(newName, newDescrip);
  }

  public void perform(StateForm stateForm, ControllerRequest params,
                      ControllerResponse response) throws
      NonHandleableException, ControllerException {

    ErrorCollection errors = stateForm.validate(params);

    if (errors.getErrorCount() != 0) {
      // handle an invalid form
      // in this case go back to the prompt state
      response.saveErrors(errors);
      try {
        response.setFormCache();
        transition("myPromptState", params, response);
      }
      catch (NonHandleableException nhe) {
        errors.addError("problem after transition");
      }
    }
    else {
      // handle a valid form
      // for example update a DBObject with the form data
    }
  }
</programlisting> If you’re using internal states, it’s a bit trickier, since
    internal states are not aware of ActionForms. <programlisting>…
protected void runMyHandleState(ControllerRequest params, ControllerResponse response){
…
//cast your request to a form-aware request
ServletControllerRequest cparams = (ServletControllerRequest)params;
//retrieve the form from the request…
DefaultForm myForm = (DefaultForm)cparams.getForm();
…
//the rest of the process is exactly as in the external states
}   response.saveErrors(errors);

  setResponse(response);

  return;

}</programlisting></para>
  </sect1>

  <sect1>
    <title>Automatically validating a form</title>

    <para>Automatic form validation is configured in the action mappings of
    your app’s struts-config.xml, and will be enabled or disabled for all the
    states in the controller referenced by the mapping. To enable it, you
    simply set the validate=”true” property for the action, and that’s it :D
    <programlisting>&lt;struts-config&gt;
&lt;action path="/myAction" type="com.mycompany.myapp.controller.MyController" name="default" scope="request" validate="true"&gt;
&lt;forward name="whatever" path="/pages/whatever.jsp"/&gt;
&lt;/action&gt;
&lt;/action-mappings&gt;
&lt;/struts-config&gt;</programlisting> <emphasis>I repeat, that’s it..
    Expresso will handle the rest for you</emphasis></para>
  </sect1>

  <sect1>
    <title>Using client-side Javascript validation</title>

    <para>Struts Validator can automatically generate Javascript code for
    validating your forms at the client-side (server-side validation will also
    be performed no matter if client-side validation is enabled or not). For
    using it, there are two steps to do in your JSP. 1)load the Javascript
    code corresponding to your form <programlisting>&lt;html:javascript formName=”myHandleState” /&gt;</programlisting>
    2)tell the form to call the JS code on submit <programlisting>&lt;html:form action="/myAction" method="POST" onsubmit="return validateMyHandleState(this)"&gt;</programlisting>
    Now you’ll form will be validated following the rules you defined in
    validation.xml both, client-side and server-side</para>
  </sect1>

  <sect1>
    <title>Designing Complex Validations with valid when</title>

    <para>[Since Struts 1.2.0] A frequent requirement in validation design is
    to validate one field against another (for example, if you have asked the
    user to type in a password twice for confirmation, to make sure that the
    values match.) In addition, there are fields in a form that may only be
    required if other fields have certain values. The validwhen validation
    rule is designed to handle these cases. The validwhen rule takes a single
    var field, called test. The value of this var is a boolean expression
    which must be true in order for the validation to success. The values
    which are allowed in the expression are:<itemizedlist>
        <listitem>
          <para>Single or double-quoted string literals. </para>
        </listitem>

        <listitem>
          <para>Integer literals in decimal, hex or octal format </para>
        </listitem>

        <listitem>
          <para>The value null which will match against either null or an
          empty string </para>
        </listitem>

        <listitem>
          <para>Other fields in the form referenced by field name, such as
          customerAge</para>
        </listitem>

        <listitem>
          <para>Indexed fields in the form referenced by an explicit integer,
          such as childLastName[2] </para>
        </listitem>

        <listitem>
          <para>Indexed fields in the form referenced by an implicit integer,
          such as childLastName[], which will use the same index into the
          array as the index of the field being tested. </para>
        </listitem>

        <listitem>
          <para>Properties of an indexed fields in the form referenced by an
          explicit or implicit integer, such as child[].lastName, which will
          use the same index into the array as the index of the field being
          tested. </para>
        </listitem>

        <listitem>
          <para>The literal *this, which contains the value of the field
          currently being tested</para>
        </listitem>
      </itemizedlist></para>

    <para>As an example of how this would work, consider a form with fields
    sendNewsletter and emailAddress. The emailAddress field is only required
    if the sendNewsletter field is not null. You could code this using the
    validwhen rule as: <programlisting>  &lt;field depends="validwhen" property="emailAddress"&gt;
    &lt;arg0 key="userinfo.emailAddress.label" /&gt;
    &lt;var&gt;
      &lt;var-name&gt;test&lt;/var-name&gt;
      &lt;var-value&gt;((sendNewsletter == null) or (*this* != null))&lt;/var-value&gt;
    &lt;/var&gt;
  &lt;/field&gt;</programlisting>Which reads as: this field is valid if
    sendNewsletter is null or the field value is not null. </para>

    <para></para>

    <para>Here's a slightly more complicated example using indexed fields.
    Assume a form with a number of lines to allow the user to enter part
    numbers and quantities they wish to order. An array of beans of class
    orderLine is used to hold the entries in a property called orderLines. If
    you wished to verify that every line with part number also had a quantity
    entered, you could do it with:<programlisting>  &lt;field depends="validwhen" indexedListProperty="orderLines" property="quantity"&gt;
    &lt;arg0 key="orderform.quantity.label" /&gt;
    &lt;var&gt;
      &lt;var-name&gt;test&lt;/var-name&gt;
      &lt;var-value&gt;((orderLines[].partNumber == null) or (*this* != null))&lt;/var-value&gt;
    &lt;/var&gt;
  &lt;/field&gt;</programlisting>Which reads as: This field is value if the
    corresponding partNumber field is null, or this field is not null. </para>

    <para> As a final example, imagine a form where the user must enter their
    height in inches, and if they are under 60 inches in height, it is an
    error to have checked off nbaPointGuard as a career. <programlisting>  &lt;field depends="validwhen" property="nbaPointGuard"&gt;
    &lt;arg0 key="careers.nbaPointGuard.label" /&gt;
    &lt;var&gt;
      &lt;var-name&gt;test&lt;/var-name&gt;
      &lt;var-value&gt;((heightInInches &amp;gt;= 60) or (*this* == null))&lt;/var-value&gt;
    &lt;/var&gt;
  &lt;/field&gt;</programlisting> <emphasis>A few quick notes on the grammer.
    All comparisons must be enclosed in parens. Only two items may be joined
    with and or or If both items to be compared are convertable to ints, a
    numeric comparison is done, otherwise a string comparison is done.
    </emphasis></para>
  </sect1>

  <sect1>
    <title>Writing your own pluggable validators</title>

    <para>Please refer to the Struts Validator Developer guide. I couldn’t
    explain better how to do this…
    http://struts.apache.org/userGuide/dev_validator.html</para>
  </sect1>

  <sect1>
    <title>Conclusion</title>

    <para>The Validator integration in Expresso gives us an unmatched power
    and flexibility for validating user input. It also brings less painful the
    port from Struts applications where validation rules already exist.</para>

    <para>As this feature will be adopted by the community, new plugins and
    other improvements will help making this tool even more powerful.</para>

    <sect2><title>Contributors</title> <para> The following persons have
    contributed their time to this chapter: <itemizedlist mark="bullet">
    <listitem> <para> Raul Davidovich <link linkend="jgroup">(JGroup
    Expert)</link> </para> </listitem> </itemizedlist> </para></sect2>

    <para><note> <para id="donate_dbmaint"> Was this EDG documentation
    helpful? Do you wish to express your appreciation for the time expended
    over years developing the EDG doc? We now accept and appreciate monetary
    donations. Your support will keep the EDG doc alive and current. Please
    click the Donate button and enter ANY amount you think the EDG doc is
    worth. In appreciation of a $35+ donation, we'll give you a subscription
    service by emailing you notifications of doc updates; and donations $75+
    will also receive an Expresso T-shirt. All online donation forms are SSL
    secured and payment can be made via Credit Card or your Paypal account.
    Thank you in advance. </para> <para> <ulink
    url="http://www.jcorporate.com/edgdoc.html"> <inlinemediaobject>
    <imageobject> <imagedata fileref="../images/edg/paypal.bmp"
    format="BMP"></imagedata> </imageobject> </inlinemediaobject> </ulink>
    </para> </note></para>

    <para>Copyright © 2001-2004 Jcorporate Ltd. All rights reserved.</para>
  </sect1>
</chapter>


More information about the cvs mailing list