Anonymous avatar Anonymous committed 1dc3df5

resolves issues WW-279 and WW-280

It appeared as if the code to handle the Velocity portion of the FormTag and the TokenTag were never implemented. The FormTag was no problem to implement, however, the TokenTag because of its usage of pageContext attributes was a little messier. Ultimately, I ended up not using the pageContext attributes. (Need to talk to Jason to see what the impact of this is).

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

Comments (0)

Files changed (9)

src/etc/example/xwork.xml

             </result>
         </action>
 
+        <action name="showVelocityForm" class="com.opensymphony.webwork.example.FormAction">
+            <result name="success" type="velocity">
+                <param name="location">/form.vm</param>
+            </result>
+        </action>
+
         <action name="formTest" class="com.opensymphony.webwork.example.FormAction" method="processForm" >
             <result name="success" type="dispatcher">
                 <param name="location">/form.jsp</param>

src/etc/taglib.tld

         <bodycontent>empty</bodycontent>
         <info>An HTML Hidden UI widget</info>
         <attribute>
+            <name>label</name>
+            <required>false</required>
+            <rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
             <name>theme</name>
             <required>false</required>
             <rtexprvalue>true</rtexprvalue>

src/java/com/opensymphony/webwork/views/jsp/ui/AbstractUITag.java

     private static final Log LOG = LogFactory.getLog(AbstractUITag.class);
 
     /**
-    * The name of the default theme used by WW2.
-    */
+     * The name of the default theme used by WW2.
+     */
     public static String THEME;
     protected static VelocityEngine velocityEngine = VelocityManager.getVelocityEngine();
 
     }
 
     /**
-    * com.opensymphony.webwork.views.jsp.ParameterizedTag implementation
-    * @return a Map of user specified Map parameters
-    * @see ParameterizedTag
-    */
+     * com.opensymphony.webwork.views.jsp.ParameterizedTag implementation
+     * @return a Map of user specified Map parameters
+     * @see ParameterizedTag
+     */
     public Map getParams() {
         if (params == null) {
             params = new HashMap();
     }
 
     /**
-    * for Velocity
-    * @return
-    * @throws JspException
-    */
+     * for Velocity
+     * @return
+     * @throws JspException
+     */
     public String getShow() throws JspException {
         int result = doStartTag();
         doEndTag();
     }
 
     /**
-    * simple setter method for Velocity
-    * @param id
-    */
+     * simple setter method for Velocity
+     * @param id
+     */
     public Object Id(String id) {
         setId(id);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param label
-    */
+     * simple setter method for Velocity
+     * @param label
+     */
     public Object Label(String label) {
         setLabel(label);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param name
-    */
+     * simple setter method for Velocity
+     * @param name
+     */
     public Object Name(Object name) {
         setName(name);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param onchange
-    */
+     * simple setter method for Velocity
+     * @param onchange
+     */
     public Object Onchange(String onchange) {
         setOnchange(onchange);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param label
-    */
+     * simple setter method for Velocity
+     * @param label
+     */
     public Object Required(boolean required) {
         setRequired(required);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param template
-    */
+     * simple setter method for Velocity
+     * @param template
+     */
     public Object Template(String template) {
         setTemplate(template);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param theme
-    */
+     * simple setter method for Velocity
+     * @param theme
+     */
     public Object Theme(String theme) {
         setTheme(theme);
 
     }
 
     /**
-    * simple setter method for Velocity
-    * @param value
-    */
+     * simple setter method for Velocity
+     * @param value
+     */
     public Object Value(Object value) {
         setValue(value);
 
     }
 
     /**
-    * com.opensymphony.webwork.views.jsp.ParameterizedTag implementation
-    * @param key the String name of a parameter to add
-    * @param value the value associated with that parameter
-    * @see ParameterizedTag
-    */
+     * com.opensymphony.webwork.views.jsp.ParameterizedTag implementation
+     * @param key the String name of a parameter to add
+     * @param value the value associated with that parameter
+     * @see ParameterizedTag
+     */
     public void addParam(String key, Object value) {
         this.getParams().put(key, value);
     }
 
     public int doEndTag() throws JspException {
-        OgnlValueStack stack = ActionContext.getContext().getValueStack();
-
-        if (stack != null) {
-            if (value != null) {
-                actualValue = stack.findValue(value.toString());
-            } else if (name != null) {
-                actualValue = stack.findValue(name.toString());
-            }
-        }
+        evaluateActualValue();
 
         try {
             mergeTemplate(this.getTemplateName());
 
     public int doStartTag() throws JspException {
         /**
-        * Migrated instantiation of the params HashMap to here from the constructor to facilitate implementation of the
-        * release() method.
-        */
+         * Migrated instantiation of the params HashMap to here from the constructor to facilitate implementation of the
+         * release() method.
+         */
         this.params = new HashMap();
 
         return EVAL_BODY_INCLUDE;
     }
 
     /**
-    * Clears all the instance variables to allow this instance to be reused.
-    */
+     * Clears all the instance variables to allow this instance to be reused.
+     */
     public void release() {
         super.release();
         this.params = null;
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @return
+     * @throws JspException
+     */
     public Object set(String label) throws JspException {
         return this.set(label, null, null, null, null, null, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name) throws JspException {
         return this.set(label, name, null, null, null, null, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @param value
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @param value
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name, Object value) throws JspException {
         return this.set(label, name, value, null, null, null, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @param value
-    * @param id
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @param value
+     * @param id
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name, Object value, String id) throws JspException {
         return this.set(label, name, value, id, null, null, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @param value
-    * @param id
-    * @param onchange
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @param value
+     * @param id
+     * @param onchange
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name, Object value, String id, String onchange) throws JspException {
         return this.set(label, name, value, id, onchange, null, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @param value
-    * @param id
-    * @param onchange
-    * @param template
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @param value
+     * @param id
+     * @param onchange
+     * @param template
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name, Object value, String id, String onchange, String template) throws JspException {
         return this.set(label, name, value, id, onchange, template, null);
     }
 
     /**
-    * For Velocity
-    * @param label
-    * @param name
-    * @param value
-    * @param id
-    * @param onchange
-    * @param template
-    * @param theme
-    * @return
-    * @throws JspException
-    */
+     * For Velocity
+     * @param label
+     * @param name
+     * @param value
+     * @param id
+     * @param onchange
+     * @param template
+     * @param theme
+     * @return
+     * @throws JspException
+     */
     public Object set(String label, Object name, Object value, String id, String onchange, String template, String theme) throws JspException {
         this.setLabel(label);
         this.setName(name);
     }
 
     /**
-    * A contract that requires each concrete UI Tag to specify which template should be used as a default.  For
-    * example, the CheckboxTab might return "checkbox.vm" while the RadioTag might return "radio.vm".  This value
-    * <strong>not</strong> begin with a '/' unless you intend to make the path absolute rather than relative to the
-    * current theme.
-    * @return The name of the template to be used as the default.
-    */
+     * A contract that requires each concrete UI Tag to specify which template should be used as a default.  For
+     * example, the CheckboxTab might return "checkbox.vm" while the RadioTag might return "radio.vm".  This value
+     * <strong>not</strong> begin with a '/' unless you intend to make the path absolute rather than relative to the
+     * current theme.
+     * @return The name of the template to be used as the default.
+     */
     protected abstract String getDefaultTemplate();
 
     /**
-    * Find the name of the Velocity template that we should use.
-    * @return The name of the Velocity template that we should use. This value should begin with a '/'
-    */
+     * Find the name of the Velocity template that we should use.
+     * @return The name of the Velocity template that we should use. This value should begin with a '/'
+     */
     protected String getTemplateName() {
         return buildTemplateName(getTemplate(), getDefaultTemplate());
     }
 
     /**
-    *
-    * @param myTemplate
-    * @param myDefaultTemplate
-    * @return
-    */
+     *
+     * @param myTemplate
+     * @param myDefaultTemplate
+     * @return
+     */
     protected String buildTemplateName(String myTemplate, String myDefaultTemplate) {
         /**
-        * If no used defined template has been speccified, apply the appropriate theme to the default template
-        */
+         * If no used defined template has been speccified, apply the appropriate theme to the default template
+         */
         if (myTemplate == null) {
             if (this.theme == null) {
                 return THEME + myDefaultTemplate;
             }
 
             /**
-            * If a theme has been specified and it begins with a '/', allow this to override any theme value provided.
-            */
+             * If a theme has been specified and it begins with a '/', allow this to override any theme value provided.
+             */
         } else if (myTemplate.startsWith("/")) {
             return myTemplate;
 
             /**
-            * Otherwise, apply the appropriate theme to the user specified template
-            */
+             * Otherwise, apply the appropriate theme to the user specified template
+             */
         } else {
             if (this.theme == null) {
                 return THEME + myTemplate;
         }
     }
 
+    /**
+     * this method derives the value of this.actualValue from the value stack if this.actualValue has not
+     * already been set
+     */
+    protected void evaluateActualValue() {
+        if (this.actualValue != null) {
+            return;
+        }
+
+        OgnlValueStack stack = ActionContext.getContext().getValueStack();
+
+        if (stack != null) {
+            if (value != null) {
+                actualValue = stack.findValue(value.toString());
+            } else if (name != null) {
+                actualValue = stack.findValue(name.toString());
+            }
+        }
+    }
+
     protected void mergeTemplate(String templateName) throws Exception {
         Template t = velocityEngine.getTemplate(templateName);
         Context context = VelocityManager.createContext(pageContext.getServletConfig(), pageContext.getRequest(), pageContext.getResponse());
         }
 
         /**
-        * Make the OGNL stack available to the velocityEngine templates.
-        * @todo Consider putting all the VelocityServlet Context values in - after all, if we're already sending
-        * the request, it might also make sense for consistency to send the page and res and any others.
-        */
+         * Make the OGNL stack available to the velocityEngine templates.
+         * @todo Consider putting all the VelocityServlet Context values in - after all, if we're already sending
+         * the request, it might also make sense for consistency to send the page and res and any others.
+         */
         context.put("tag", this);
 
         t.merge(context, writer);
     }
 
     /**
-    * initialize the
-    * @throws JspException
-    */
+     * initialize the
+     * @throws JspException
+     */
     protected VelocityEngine newVelocityEngine() throws Exception {
         Properties p = new Properties();
 
             }
 
             /**
-            * Refactored the Velocity templates for the WebWork taglib into the classpath from the web path.  This will
-            * enable WebWork projects to have access to the templates by simply including the WebWork jar file.
-            * Unfortunately, there does not appear to be a macro for the class loader keywords
-            * Matt Ho - Mon Mar 17 00:21:46 PST 2003
-            */
+             * Refactored the Velocity templates for the WebWork taglib into the classpath from the web path.  This will
+             * enable WebWork projects to have access to the templates by simply including the WebWork jar file.
+             * Unfortunately, there does not appear to be a macro for the class loader keywords
+             * Matt Ho - Mon Mar 17 00:21:46 PST 2003
+             */
             p.setProperty("class.resource.loader.description", "Velocity Classpath Resource Loader");
             p.setProperty("class.resource.loader.class", "com.opensymphony.webwork.views.velocity.WebWorkResourceLoader");
         }

src/java/com/opensymphony/webwork/views/jsp/ui/FormTag.java

  */
 package com.opensymphony.webwork.views.jsp.ui;
 
+import com.opensymphony.webwork.ServletActionContext;
 import com.opensymphony.webwork.views.util.UrlHelper;
+import com.opensymphony.webwork.views.velocity.IterationRenderer;
+
+import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
  * @author Jason Carreira
  * Created Apr 1, 2003 8:19:47 PM
  */
-public class FormTag extends AbstractClosingUITag {
+public class FormTag extends AbstractClosingUITag implements IterationRenderer {
     //~ Static fields/initializers /////////////////////////////////////////////
 
     final public static String OPEN_TEMPLATE = "form.vm";
     //~ Methods ////////////////////////////////////////////////////////////////
 
     public void setAction(String action) {
-        HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
-        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
-        String result = UrlHelper.buildUrl(action, request, response, null);
+        /**
+         * If called from a JSP, pageContext will not be null.  otherwise, we'll get request and response from the
+         * ServletActionContext.
+         *
+         * todo - determine if there's any reason we can't just always use ServletActionContext
+         */
+        HttpServletResponse response = null;
+        HttpServletRequest request = null;
+
+        if (pageContext != null) {
+            response = (HttpServletResponse) pageContext.getResponse();
+            request = (HttpServletRequest) pageContext.getRequest();
+        } else {
+            request = ServletActionContext.getRequest();
+            response = ServletActionContext.getResponse();
+        }
 
+        String result = UrlHelper.buildUrl(action, request, response, null);
         this.action = result;
     }
 
         return method;
     }
 
+    public int doAfterRender(Context context, Writer writer) {
+        return IterationRenderer.RENDER_DONE;
+    }
+
+    public void doBeforeRender(Context context, Writer writer) {
+        this.evaluateActualValue();
+
+        try {
+            String openTemplate = this.getOpenTemplate();
+
+            if (openTemplate == null) {
+                openTemplate = OPEN_TEMPLATE;
+            }
+
+            String templateName = buildTemplateName(getTemplate(), openTemplate);
+
+            Template template = velocityEngine.getTemplate(templateName);
+            template.merge(context, writer);
+        } catch (Exception e) {
+            try {
+                writer.write("<pre>");
+                e.printStackTrace(new PrintWriter(writer));
+                writer.write("</pre>");
+            } catch (IOException e1) {
+                // ok
+            }
+        }
+    }
+
     /**
-    * Clears all the instance variables to allow this instance to be reused.
-    */
+     * Clears all the instance variables to allow this instance to be reused.
+     */
     public void release() {
         super.release();
         method = null;

src/java/com/opensymphony/webwork/views/jsp/ui/TokenTag.java

 import com.opensymphony.webwork.dispatcher.SessionMap;
 import com.opensymphony.webwork.util.TokenHelper;
 
+import org.apache.velocity.context.Context;
+
+import java.io.Writer;
+
 import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
     }
 
     /**
-    * First looks for the token in the PageContext using the supplied name (or {@link TokenHelper#DEFAULT_TOKEN_NAME}
-    * if no name is provided) so that the same token can be re-used for the scope of a request for the same name. If
-    * the token is not in the PageContext, a new Token is created and set into the Session and the PageContext with
-    * the name.
-    * @return
-    * @throws JspException
-    */
+ * First looks for the token in the PageContext using the supplied name (or {@link TokenHelper#DEFAULT_TOKEN_NAME}
+ * if no name is provided) so that the same token can be re-used for the scope of a request for the same name. If
+ * the token is not in the PageContext, a new Token is created and set into the Session and the PageContext with
+ * the name.
+ * @return
+ * @throws JspException
+ */
     public int doEndTag() throws JspException {
         if (name == null) {
             name = TokenHelper.DEFAULT_TOKEN_NAME;
     }
 
     /**
-    * Clears all the instance variables to allow this instance to be reused.
-    */
+ * Clears all the instance variables to allow this instance to be reused.
+ */
     public void release() {
         super.release();
         token = null;
         name = null;
     }
 
+    public void render(Context context, Writer writer) throws Exception {
+        /**
+ * todo - look for a better way of implementing this.  this smells bad.
+ */
+        verifySession();
+        this.token = TokenHelper.setToken(name.toString());
+
+        super.render(context, writer);
+    }
+
     protected String getDefaultTemplate() {
         return TEMPLATE;
     }
     }
 
     /**
-    * This method checks to see if a HttpSession object exists in the context. If a session
-    * doesn't exist, it creates a new one and adds it to the context.
-    */
+ * This method checks to see if a HttpSession object exists in the context. If a session
+ * doesn't exist, it creates a new one and adds it to the context.
+ */
     private void verifySession() {
         Map session = ServletActionContext.getContext().getSession();
 
         // if the session is null, add a new HttpSession to the context
         if (session == null) {
-            HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
+            HttpServletRequest request = ServletActionContext.getRequest();
+
+            if (pageContext != null) {
+                request = (HttpServletRequest) pageContext.getRequest();
+            } else {
+                request = ServletActionContext.getRequest();
+            }
+
             ServletActionContext.getContext().setSession(new SessionMap(request.getSession()));
         }
     }

src/java/com/opensymphony/webwork/views/velocity/IterationRenderer.java

      */
     int RENDER_AGAIN = 2;
 
+    /**
+     * value to be returned by doAfterRender if we're done rendering
+     */
+    int RENDER_DONE = 0;
+
     //~ Methods ////////////////////////////////////////////////////////////////
 
     /**

src/webapp/WEB-INF/classes/webwork.properties

 webwork.multipart.saveDir=\temp
 webwork.multipart.maxSize=12345
 
+### Load custom property files (does not override webwork.properties!)
+webwork.custom.properties=test,com/opensymphony/webwork/othertest
+
 # added the MockTag to the path of Tags that the TagDirective will search through
-webwork.velocity.tag.path = com.opensymphony.webwork.views.velocity.ui
+webwork.velocity.tag.path = com.opensymphony.webwork.views.velocity.ui

src/webapp/default.jsp

 Click <a href="action.jsp">here</a> for an example of the stupid little counter being called via the ww:action tag<br>
 Click <a href="bean.jsp">here</a> for an example of the stupid little counter being called via the ww:bean tag<br>
 Click <a href="TagTest.action">here</a> to see sample usages of the UI tags<br>
-Click <a href="VelocityTagTest.action">here</a> to see sample usages of the UI tags using Velocity as the view<br>
+Click <a href="VelocityTagTest.action">here</a> to see sample usages of the UI tags using <strong>Velocity as the view</strong><br>
 Click <a href="tags.vm">here</a> to see sample usages of the UI tags using directly accessed Velocity templates<br>
 Click <a href="CountryTest.action">here</a> for a performance benchmark of a 200+ element array<br>
 Click <a href="IteratorTest.action">here</a> for an example of the Iterator Tag<br>
 Click <a href="showForm.action">here</a> for an example of the Form Tag and the Token Tag for hidden transaction tokens<br>
+Click <a href="showVelocityForm.action">here</a> for an example of the Form Tag and the Token Tag for hidden transaction tokens <strong>using Velocity as the view</strong><br>
 Click <a href="form2.jsp">here</a> for an example of the Form Tag and the Token Tag for hidden transaction tokens. This example shows you the original result rather than an error (TokenSessionInterceptor).<br>
 Click <a href="exception.action">here</a> for an example of what happens when exceptions are thrown

src/webapp/form.vm

+<html>
+<head><title>Webwork Form Example</title></head>
+<body>
+#if( $errorMessages )
+<p>
+<font color="red">
+<b>ERRORS:</b><br>
+<ul>
+#foreach( $message in $errorMessages )
+<li>$message</li>
+#end
+</ul>
+</font>
+</p>
+#end
+Status: $status<br><p>
+<b>Test Form with valid token</b><br>
+#bodytag( Form "name=myForm" "action=formTest.action" "method=POST" )
+#tag( Token "name=myToken" )<br>
+<table>
+#tag( TextField "label=Foo" "name='foo'" "value=foo" )<br>
+</table>
+<input type="submit" value="Test With Token"/>
+#end
+<b>Test Form without valid token</b><br>
+#bodytag( Form "name=myForm" "action=formTest.action" "method=POST" )
+<table>
+#*
+    @todo something weird happens on resin where if this textfield has the same label as the previous textfield,
+    the the label for this textfield comes up as empty.
+*#
+#tag( TextField "label=No Foo" "name='foo'" "value=foo" )<br>
+</table>
+<input type="submit" value="Test Without Token"/>
+#end ## Form
+
+</body>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.