Commits

Anonymous committed 628d708

- pure client side validation for xhtml theme
- ajax client side validation for ajax theme (using DWR)
- Validator interface now has setter/getter for the type (requiredstring, int, etc),
which is used by the pure client side implementation

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@1796573baa09-0c28-0410-bef9-dab3c582ae83

  • Participants
  • Parent commits 5efb1f8

Comments (0)

Files changed (33)

File src/java/com/opensymphony/webwork/components/Form.java

 import com.opensymphony.xwork.util.OgnlValueStack;
 import com.opensymphony.xwork.ActionContext;
 import com.opensymphony.xwork.ActionInvocation;
+import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.validator.ActionValidatorManager;
+import com.opensymphony.xwork.validator.Validator;
+import com.opensymphony.xwork.validator.FieldValidator;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import java.util.Iterator;
 
 /**
  * <!-- START SNIPPET: javadoc -->
         }
 
         final ActionConfig actionConfig = ConfigurationManager.getConfiguration().getRuntimeConfiguration().getActionConfig(namespace, action);
+        String actionName = action;
         if (actionConfig != null) {
             if (isPortlet & !isAjax) {
                 addParameter("action", actionURL);
                 ActionMapping mapping = new ActionMapping(action, namespace, actionMethod, parameters);
                 String result = UrlHelper.buildUrl(ActionMapperFactory.getMapper().getUriFromActionMapping(mapping), request, response, null);
                 addParameter("action", result);
+
+                // let's try to get the actual action class and name
+                // this can be used for getting the list of validators
+                addParameter("actionName", actionName);
+                try {
+                    Class clazz = ObjectFactory.getObjectFactory().getClassInstance(actionConfig.getClassName());
+                    addParameter("actionClass", clazz);
+                } catch (ClassNotFoundException e) {
+                    // this is OK, we'll just move on
+                }
             }
 
             addParameter("namespace", namespace);
         if (validate != null) {
             addParameter("validate", findValue(validate, Boolean.class));
         }
+
+        // keep a collection of the tag names for anything special the templates might want to do (such as pure client
+        // side validation)
+        if (!parameters.containsKey("tagNames")) {
+            // we have this if check so we don't do this twice (on open and close of the template)
+            addParameter("tagNames", new ArrayList());
+        }
     }
 
+    public List getValidators(String name) {
+        Class actionClass = (Class) getParameters().get("actionClass");
+        if (actionClass == null) {
+            return Collections.EMPTY_LIST;
+        }
+
+        List all = ActionValidatorManager.getValidators(actionClass, (String) getParameters().get("actionName"));
+        List validators = new ArrayList();
+        for (Iterator iterator = all.iterator(); iterator.hasNext();) {
+            Validator validator = (Validator) iterator.next();
+            if (validator instanceof FieldValidator) {
+                FieldValidator fieldValidator = (FieldValidator) validator;
+                if (fieldValidator.getFieldName().equals(name)) {
+                    validators.add(fieldValidator);
+                }
+            }
+        }
+
+        return validators;
+    }
+
+
     /**
      * @ww.tagattribute required="false"
      * description="HTML onsubmit attribute"

File src/java/com/opensymphony/webwork/components/UIBean.java

 import com.opensymphony.webwork.WebWorkConstants;
 import com.opensymphony.xwork.config.ConfigurationException;
 import com.opensymphony.xwork.util.OgnlValueStack;
+import com.opensymphony.xwork.validator.ActionValidatorManager;
+import com.opensymphony.xwork.validator.Validator;
+import com.opensymphony.xwork.validator.FieldValidator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.Writer;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Iterator;
 
 /**
  * UIBean is the standard superclass of all webwork UI componentns.
             addParameter("id", form.getParameters().get("id") + "_" + escape(name));
         }
 
-        if (form != null) {
+        if (form != null && name != null) {
             addParameter("form", form.getParameters());
+
+            List tags = (List) form.getParameters().get("tagNames");
+            tags.add(name);
         }
 
         evaluateExtraParams();

File src/java/com/opensymphony/webwork/static/template/ajax/validation.js

+var webworkValidator = new ValidationClient("$!base/validation");
+webworkValidator.onErrors = function(input, errors) {
+
+	var table = input.parentNode.parentNode.parentNode;
+	var form = input.form;
+
+	clearErrorRows(table);
+	clearErrorLabels(form);
+
+    if (errors.fieldErrors) {
+        for (var fieldName in errors.fieldErrors) {
+            if (form.elements[fieldName].touched) {
+                for (var i = 0; i < errors.fieldErrors[fieldName].length; i++) {
+                    addError(form.elements[fieldName], errors.fieldErrors[fieldName][i]);
+                }
+            }
+        }
+    }
+}
+
+function validate(element) {
+    // mark the element as touch
+    element.touched = true;
+    var namespace = element.form.attributes['namespace'].nodeValue;
+    var actionName = element.form.attributes['name'].nodeValue;
+	webworkValidator.validate(element, namespace, actionName);
+}

File src/java/com/opensymphony/webwork/static/template/xhtml/validation.js

-var webworkValidator = new ValidationClient("$!base/validation");
-webworkValidator.onErrors = function(input, errors) {
-
-	var table = input.parentNode.parentNode.parentNode;
-	var form = input.form;
-
-	clearErrorRows(table);
-	clearErrorLabels(form);
-
-    if (errors.fieldErrors) {
-        for (var fieldName in errors.fieldErrors) {
-            if (form.elements[fieldName].touched) {
-                for (var i = 0; i < errors.fieldErrors[fieldName].length; i++) {
-                    addError(form.elements[fieldName], errors.fieldErrors[fieldName][i]);
-                }
-            }
-        }
-    }
-}
-
-function validate(element) {
-    // mark the element as touch
-    element.touched = true;
-    var namespace = element.form.attributes['namespace'].nodeValue;
-    var actionName = element.form.attributes['name'].nodeValue;
-	webworkValidator.validate(element, namespace, actionName);
-}
-
 function clearErrorRows(table) {
     // clear out any rows with an "errorFor" attribute
     var rows = table.rows;
 
 }
 
-
 function addError(e, errorText) {
     try {
         // clear out any rows with an "errorFor" of e.id

File src/java/com/opensymphony/webwork/views/freemarker/ScopesHashModel.java

 
         return null;
     }
+
+    public void put(String string, boolean b) {
+        super.put(string, b);
+    }
+
+    public void put(String string, Object object) {
+        super.put(string, object);
+    }
 }

File src/java/template/ajax/controlheader.ftl

+<#include "/${parameters.templateDir}/xhtml/controlheader-core.ftl" />
+<#if parameters.form?exists && parameters.form.validate?default(false) == true>
+	<#-- can't mutate the data model in freemarker -->
+    <#if parameters.onblur?exists>
+        ${tag.addParameter('onblur', "validate(this);${parameters.onblur}")}
+    <#else>
+        ${tag.addParameter('onblur', "validate(this);")}
+    </#if>
+</#if>
+    <td>

File src/java/template/ajax/form.ftl

 <script src="${base}/webwork/validationClient.js"></script>
 <script src="${base}/dwr/interface/validator.js"></script>
 <script src="${base}/dwr/engine.js"></script>
+<script src="${base}/webwork/template/ajax/validation.js"></script>
 <script src="${base}/webwork/template/xhtml/validation.js"></script>
 </#if>
 <form<#rt/>

File src/java/template/xhtml/checkboxlist.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 
 <#include "/${parameters.templateDir}/simple/checkboxlist.ftl" />
     <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" /><#nt/>

File src/java/template/xhtml/combobox.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 
 <#include "/${parameters.templateDir}/simple/combobox.ftl" />
     <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" /><#nt/>

File src/java/template/xhtml/controlheader-core.ftl

+<#--
+	Only show message if errors are available.
+	This will be done if ActionSupport is used.
+-->
+<#assign hasFieldErrors = fieldErrors?exists && fieldErrors[parameters.name]?exists/>
+<#if hasFieldErrors>
+<#list fieldErrors[parameters.name] as error>
+<tr errorFor="${parameters.id}">
+<#if parameters.labelposition?default("") == 'top'>
+    <td align="left" valign="top" colspan="2"><#rt/>
+<#else>
+    <td align="center" valign="top" colspan="2"><#rt/>
+</#if>
+        <span class="errorMessage">${error?html}</span><#t/>
+    </td><#lt/>
+</tr>
+</#list>
+</#if>
+<#--
+	if the label position is top,
+	then give the label it's own row in the table
+-->
+<tr>
+<#if parameters.labelposition?default("") == 'top'>
+    <td align="left" valign="top" colspan="2"><#rt/>
+<#else>
+    <td align="right" valign="top"><#rt/>
+</#if>
+<#if parameters.label?exists>
+    <label <#t/>
+<#if parameters.id?exists>
+        for="${parameters.id?html}" <#t/>
+</#if>
+<#if hasFieldErrors>
+        class="errorLabel"<#t/>
+<#else>
+        class="label"<#t/>
+</#if>
+    ><#t/>
+<#if parameters.required?default(false)>
+        <span class="required">*</span><#t/>
+</#if>
+        ${parameters.label?html}:</label><#t/>
+</#if>
+    </td><#lt/>
+<#-- add the extra row -->
+<#if parameters.labelposition?default("") == 'top'>
+</tr>
+<tr>
+</#if>

File src/java/template/xhtml/controlheader.ftl

-<#--
-	Only show message if errors are available.
-	This will be done if ActionSupport is used.
--->
-<#assign hasFieldErrors = fieldErrors?exists && fieldErrors[parameters.name]?exists/>
-<#if hasFieldErrors>
-<#list fieldErrors[parameters.name] as error>
-<tr errorFor="${parameters.id}">
-<#if parameters.labelposition?default("") == 'top'>
-    <td align="left" valign="top" colspan="2"><#rt/>
-<#else>
-    <td align="center" valign="top" colspan="2"><#rt/>
-</#if>
-        <span class="errorMessage">${error?html}</span><#t/>
-    </td><#lt/>
-</tr>
-</#list>
-</#if>
-<#--
-	if the label position is top,
-	then give the label it's own row in the table
--->
-<tr>
-<#if parameters.labelposition?default("") == 'top'>
-    <td align="left" valign="top" colspan="2"><#rt/>
-<#else>
-    <td align="right" valign="top"><#rt/>
-</#if>
-<#if parameters.label?exists>
-    <label <#t/>
-<#if parameters.id?exists>
-        for="${parameters.id?html}" <#t/>
-</#if>
-<#if hasFieldErrors>
-        class="errorLabel"<#t/>
-<#else>
-        class="label"<#t/>
-</#if>
-    ><#t/>
-<#if parameters.required?default(false)>
-        <span class="required">*</span><#t/>
-</#if>
-        ${parameters.label?html}:</label><#t/>
-</#if>
-    </td><#lt/>
-<#-- add the extra row -->
-<#if parameters.labelposition?default("") == 'top'>
-</tr>
-<tr>
-</#if>
-<#if parameters.form?exists && parameters.form.validate?default(false) == true>
-	<#-- can't mutate the data model in freemarker -->
-    <#if parameters.onblur?exists>
-        ${tag.addParameter('onblur', "validate(this);${parameters.onblur}")}
-    <#else>
-        ${tag.addParameter('onblur', "validate(this);")}
-    </#if>
-</#if>
+<#include "/${parameters.templateDir}/xhtml/controlheader-core.ftl" />
     <td>

File src/java/template/xhtml/datepicker.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/datepicker.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/debug.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/debug.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/doubleselect.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 
 <#include "/${parameters.templateDir}/simple/doubleselect.ftl" />
     <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" /><#nt/>

File src/java/template/xhtml/file.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/file.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/form-close.ftl

 </table>
-<#include "/${parameters.templateDir}/simple/form-close.ftl" />
+<#include "/${parameters.templateDir}/simple/form-close.ftl" />
+<#if parameters.validate?exists>
+<script>
+    function validateForm_${parameters.id}() {
+        form = document.getElementById("${parameters.id}");
+        clearErrorRows(form.childNodes[1]);
+        clearErrorLabels(form);
+
+        var errors = false;
+    <#list parameters.tagNames as tagName>
+        <#list tag.getValidators("${tagName}") as validator>
+        // field name: ${validator.fieldName}
+        // validator name: ${validator.validatorType}
+        if (form.elements['${validator.fieldName}']) {
+            field = form.elements['${validator.fieldName}'];
+
+            <#if validator.validatorType = "requiredstring">
+            if (field.value == null || field.value == "" || field.value.match("\W+")) {
+                addError(field, "${validator.defaultMessage}");
+                errors = true;
+            }
+            </#if>
+        }
+        </#list>
+    </#list>
+
+        return !errors;
+    }
+</script>
+</#if>

File src/java/template/xhtml/form.ftl

-<#if parameters.validate?exists>
-<script src="${base}/webwork/validationClient.js"></script>
-<script src="${base}/dwr/interface/validator.js"></script>
-<script src="${base}/dwr/engine.js"></script>
+<#if parameters.validate?default(false) == true>
 <script src="${base}/webwork/template/xhtml/validation.js"></script>
+    <#if parameters.onsubmit?exists>
+        ${tag.addParameter('onsubmit', "return validateForm_${parameters.id}();${parameters.onblur}")}
+    <#else>
+        ${tag.addParameter('onsubmit', "return validateForm_${parameters.id}();")}
+    </#if>
 </#if>
 <#include "/${parameters.templateDir}/simple/form.ftl" />
 <table class="wwFormTable">

File src/java/template/xhtml/label.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/label.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/password.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/password.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/radiomap.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 
 <#include "/${parameters.templateDir}/simple/radiomap.ftl" />
     <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" /><#nt/>

File src/java/template/xhtml/select.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/select.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/text.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/text.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File src/java/template/xhtml/textarea.ftl

-<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
+<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
 <#include "/${parameters.templateDir}/simple/textarea.ftl" />
 <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

File webapps/showcase/src/java/com/opensymphony/webwork/showcase/validation/QuizAction-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
+<validators>
+    <field name="name">
+        <field-validator type="requiredstring">
+            <message>You must enter a name</message>
+        </field-validator>
+    </field>
+    <field name="age">
+        <field-validator type="int">
+            <param name="min">13</param>
+            <param name="max">19</param>
+            <message>Only people ages 13 to 19 may take this quiz</message>
+        </field-validator>
+    </field>
+</validators>

File webapps/showcase/src/java/com/opensymphony/webwork/showcase/validation/QuizAction.java

+package com.opensymphony.webwork.showcase.validation;
+
+import com.opensymphony.xwork.ActionSupport;
+
+/**
+ * @author Patrick Lightbody (plightbo at gmail dot com)
+ */
+public class QuizAction extends ActionSupport {
+    String name;
+    int age;
+    String answer;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+}

File webapps/showcase/src/webapp/WEB-INF/classes/xwork-validation.xml

-<!DOCTYPE xwork PUBLIC 
-	"-//OpenSymphony Group//XWork 1.0//EN" 
-	"http://www.opensymphony.com/xwork/xwork-1.1.dtd">
+<!DOCTYPE xwork PUBLIC  "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.1.dtd">
 	
 <xwork>
 	<include file="webwork-default.xml" />
 
-	<package name="validation" extends="webwork-default">
-	
+	<package name="validation" extends="webwork-default" namespace="/validation">
+	    <action name="quizBasic" class="com.opensymphony.webwork.showcase.validation.QuizAction">
+            <result name="input">quiz-basic.jsp</result>
+            <result>quiz-success.jsp</result>
+        </action>
+
+        <action name="quizClient" class="com.opensymphony.webwork.showcase.validation.QuizAction">
+            <result name="input">quiz-client.jsp</result>
+            <result>quiz-success.jsp</result>
+        </action>
+
+        <action name="quizAjax" class="com.opensymphony.webwork.showcase.validation.QuizAction">
+            <result name="input">quiz-ajax.jsp</result>
+            <result>quiz-success.jsp</result>
+        </action>
 	</package>
 </xwork>
 	

File webapps/showcase/src/webapp/WEB-INF/dwr.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
+
+<dwr>
+    <allow>
+        <create creator="new" javascript="validator">
+            <param name="class" value="com.opensymphony.webwork.validators.DWRValidator"/>
+        </create>
+        <convert converter="bean" match="com.opensymphony.xwork.ValidationAwareSupport"/>
+    </allow>
+
+    <signatures>
+        <![CDATA[
+        com.opensymphony.webwork.validators.DWRValidator.doPost(String, String, Map<String, String>);
+        ]]>
+    </signatures>
+</dwr>

File webapps/showcase/src/webapp/WEB-INF/web.xml

         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
 
+    <servlet>
+        <servlet-name>dwr</servlet-name>
+        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>dwr</servlet-name>
+        <url-pattern>/dwr/*</url-pattern>
+    </servlet-mapping>
+
     <welcome-file-list>
         <welcome-file>index.jsp</welcome-file>
         <welcome-file>default.jsp</welcome-file>

File webapps/showcase/src/webapp/showcase.jsp

     <ul>
         <li><ww:url id="url" namespace="/continuations" action="guess"/><ww:a href="%{url}">Continuations example</ww:a></li>
         <li><ww:url id="url" namespace="/tags/ui" action="example" method="input"/><ww:a href="%{url}">UI Tags example</ww:a></li>
+        <li><ww:url id="url" namespace="/validation" action="quizBasic" method="input"/><ww:a href="%{url}">Validation (basic)</ww:a></li>
+        <li><ww:url id="url" namespace="/validation" action="quizClient" method="input"/><ww:a href="%{url}">Validation (client)</ww:a></li>
+        <li><ww:url id="url" namespace="/validation" action="quizAjax" method="input"/><ww:a href="%{url}">Validation (ajax)</ww:a></li>
+        <li><ww:url id="url" namespace="/tags/ui" action="example" method="input"/><ww:a href="%{url}">UI Tags example</ww:a></li>
         <li><ww:url id="url" namespace="/skill" action="list"/><ww:a href="%{url}">List available Skills</ww:a></li>
         <li><ww:url id="url" namespace="/skill" action="edit"/><ww:a href="%{url}">Create/Edit Skill</ww:a></li>
         <li><ww:url id="url" namespace="/employee" action="list"/><ww:a href="%{url}">List available Employees</ww:a></li>

File webapps/showcase/src/webapp/validation/quiz-ajax.jsp

+<%@ taglib prefix="ww" uri="/webwork" %>
+<html>
+<head>
+    <title>Validation - Basic</title>
+    <link rel="stylesheet" href="<ww:url value="/webwork/xhtml/styles.css"/>" type="text/css"/>
+</head>
+
+<body>
+
+<ww:form method="post" validate="true" theme="ajax">
+    <ww:textfield label="Name" name="name"/>
+    <ww:textfield label="Age" name="age"/>
+    <ww:textfield label="Favorite color" name="answer"/>
+    <ww:submit/>
+</ww:form>
+
+</body>
+</html>

File webapps/showcase/src/webapp/validation/quiz-basic.jsp

+<%@ taglib prefix="ww" uri="/webwork" %>
+<html>
+<head>
+    <title>Validation - Basic</title>
+    <link rel="stylesheet" href="<ww:url value="/webwork/xhtml/styles.css"/>" type="text/css"/>
+</head>
+
+<body>
+
+<b>What is your favorite color?</b>
+<p/>
+
+<ww:form method="post">
+    <ww:textfield label="Name" name="name"/>
+    <ww:textfield label="Age" name="age"/>
+    <ww:textfield label="Favorite color" name="answer"/>
+    <ww:submit/>
+</ww:form>
+
+</body>
+</html>

File webapps/showcase/src/webapp/validation/quiz-client.jsp

+<%@ taglib prefix="ww" uri="/webwork" %>
+<html>
+<head>
+    <title>Validation - Basic</title>
+    <link rel="stylesheet" href="<ww:url value="/webwork/xhtml/styles.css"/>" type="text/css"/>
+</head>
+
+<body>
+
+<ww:form method="post" validate="true">
+    <ww:textfield label="Name" name="name"/>
+    <ww:textfield label="Age" name="age"/>
+    <ww:textfield label="Favorite color" name="answer"/>
+    <ww:submit/>
+</ww:form>
+
+</body>
+</html>

File webapps/showcase/src/webapp/validation/quiz-success.jsp

+<%@ taglib prefix="ww" uri="/webwork" %>
+<html>
+<head>
+    <title>Quiz submitted!</title>
+</head>
+
+<body>
+
+Thank you, <b><ww:property value="name"/></b>. Your answer has been submitted as:
+
+<b><ww:property value="answer"/></b>
+
+</body>
+</html>