Author: jholmes Date: Tue Jul 24 10:33:38 2007 New Revision: 559127 URL: http://svn.apache.org/viewvc?view=rev&rev=559127 Log: WW-1938 Bug with multiple s:param tags inside s:url tag
Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Component.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/URL.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java struts/struts2/trunk/core/src/site/resources/tags/url.html struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Component.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Component.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Component.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Component.java Tue Jul 24 10:33:38 2007 @@ -333,16 +333,19 @@ * @param scheme http or https * @param includeContext should the context path be included or not * @param encodeResult should the url be encoded + * @param forceAddSchemeHostAndPort should the scheme host and port be forced + * @param escapeAmp should ampersand (&) be escaped to & * @return the action url. */ protected String determineActionURL(String action, String namespace, String method, HttpServletRequest req, HttpServletResponse res, Map parameters, String scheme, - boolean includeContext, boolean encodeResult) { + boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, + boolean escapeAmp) { String finalAction = findString(action); String finalNamespace = determineNamespace(namespace, getStack(), req); ActionMapping mapping = new ActionMapping(finalAction, finalNamespace, method, parameters); String uri = actionMapper.getUriFromActionMapping(mapping); - return UrlHelper.buildUrl(uri, req, res, parameters, scheme, includeContext, encodeResult); + return UrlHelper.buildUrl(uri, req, res, parameters, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, escapeAmp); } /** Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java Tue Jul 24 10:33:38 2007 @@ -1,3 +1,23 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package org.apache.struts2.components; import java.io.IOException; @@ -29,7 +49,7 @@ String result; if (urlComponent.value == null && urlComponent.action != null) { - result = urlComponent.determineActionURL(urlComponent.action, urlComponent.namespace, urlComponent.method, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode); + result = urlComponent.determineActionURL(urlComponent.action, urlComponent.namespace, urlComponent.method, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode, false, urlComponent.escapeAmp); } else { String _value = urlComponent.value; @@ -38,7 +58,7 @@ if (_value != null && _value.indexOf("?") > 0) { _value = _value.substring(0, _value.indexOf("?")); } - result = UrlHelper.buildUrl(_value, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode); + result = UrlHelper.buildUrl(_value, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode, false, urlComponent.escapeAmp); } if ( urlComponent.anchor != null && urlComponent.anchor.length() > 0 ) { result += '#' + urlComponent.anchor; Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/URL.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/URL.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/URL.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/URL.java Tue Jul 24 10:33:38 2007 @@ -20,7 +20,9 @@ */ package org.apache.struts2.components; -import java.io.IOException; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.ValueStack; + import java.io.Writer; import java.util.Collections; import java.util.Iterator; @@ -32,16 +34,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; -import org.apache.struts2.StrutsException; -import org.apache.struts2.StrutsConstants; -import org.apache.struts2.dispatcher.Dispatcher; import org.apache.struts2.views.util.UrlHelper; -import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.util.ValueStack; - /** * <!-- START SNIPPET: javadoc --> * @@ -52,6 +49,12 @@ * an Iterable all the values will be added to the URL.</p> * * <b>NOTE:</b> + * <p>By default request parameters will be separated using escaped ampersands (i.e., &amp;). + * This is necessary for XHTML compliance, however, when using the URL generated by this tag + * with the <s:property> tag, the <b>escapeAmp</b> attribute should be used to disable + * ampersand escaping.</p> + * + * <b>NOTE:</b> * <p>When includeParams is 'all' or 'get', the parameter defined in <param> tag will take * precedence and will not be overriden if they exists in the parameter submitted. For * example, in Example 3 below, if there is a id parameter in the url where the page this @@ -67,16 +70,20 @@ * <ul> * <li>action (String) - (value or action choose either one, if both exist value takes precedence) action's name (alias) <li> * <li>value (String) - (value or action choose either one, if both exist value takes precedence) the url itself</li> - * <li>scheme (String) - http scheme (http, https) default to the scheme this request is in</li> + * <li>scheme (String) - http scheme (http, https) defaults to the scheme this request is in</li> * <li>namespace - action's namespace</li> - * <li>method (String) - action's method, default to execute() </li> - * <li>encode (Boolean) - url encode the generated url. Default is true</li> - * <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Default is 'get'. + * <li>method (String) - action's method name, defaults to 'execute'</li> + * <li>encode (Boolean) - url encode the generated url. Defaults to 'true'.</li> + * <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Defaults to 'get'. * none - include no parameters in the URL * get - include only GET parameters in the URL (default) * all - include both GET and POST parameters in the URL * </li> - * <li>includeContext (Boolean) - determine wheather to include the web app context path. Default is true.</li> + * <li>includeContext (Boolean) - Specifies whether to include the web app context path. Defaults to 'true'.</li> + * <li>escapeAmp (Boolean) - Specifies whether to escape ampersand (&) to (&amp;) or not. Defaults to 'true'.</li> + * <li>portletMode (String) - The resulting portlet mode.</li> + * <li>windowState (String) - The resulting portlet window state.</li> + * <li>portletUrlType (String) - Specifies if this should be a portlet render or action URL.</li> * </ul> * * <!-- END SNIPPET: params --> @@ -96,7 +103,7 @@ * </s:url> * * <-- Example 3--> - * <s:url includeParams="get" > + * <s:url includeParams="get"> * <s:param name="id" value="%{'22'}" /> * </s:url> * @@ -134,6 +141,7 @@ protected String method; protected boolean encode = true; protected boolean includeContext = true; + protected boolean escapeAmp = true; protected String portletMode; protected String windowState; protected String portletUrlType; @@ -293,6 +301,11 @@ @StrutsTagAttribute(description="The anchor for this URL") public void setAnchor(String anchor) { this.anchor = anchor; + } + + @StrutsTagAttribute(description="Specifies whether to escape ampersand (&) to (&amp;) or not", type="Boolean", defaultValue="true") + public void setEscapeAmp(boolean escapeAmp) { + this.escapeAmp = escapeAmp; } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/jsp/URLTag.java Tue Jul 24 10:33:38 2007 @@ -44,6 +44,7 @@ protected String method; protected String encode; protected String includeContext; + protected String escapeAmp; protected String portletMode; protected String windowState; protected String portletUrlType; @@ -74,6 +75,9 @@ if (includeContext != null) { url.setIncludeContext(Boolean.valueOf(includeContext).booleanValue()); } + if (escapeAmp != null) { + url.setEscapeAmp(Boolean.valueOf(escapeAmp).booleanValue()); + } } public void setEncode(String encode) { @@ -82,6 +86,10 @@ public void setIncludeContext(String includeContext) { this.includeContext = includeContext; + } + + public void setEscapeAmp(String escapeAmp) { + this.escapeAmp = escapeAmp; } public void setIncludeParams(String name) { Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java Tue Jul 24 10:33:38 2007 @@ -18,7 +18,6 @@ * specific language governing permissions and limitations * under the License. */ - package org.apache.struts2.views.util; import java.io.UnsupportedEncodingException; @@ -91,6 +90,10 @@ } public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) { + return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true); + } + + public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) { StringBuffer link = new StringBuffer(); boolean changedScheme = false; @@ -169,7 +172,11 @@ } //if the action was not explicitly set grab the params from the request - buildParametersString(params, link); + if (escapeAmp) { + buildParametersString(params, link); + } else { + buildParametersString(params, link, "&"); + } String result; Modified: struts/struts2/trunk/core/src/site/resources/tags/url.html URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/site/resources/tags/url.html?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/site/resources/tags/url.html (original) +++ struts/struts2/trunk/core/src/site/resources/tags/url.html Tue Jul 24 10:33:38 2007 @@ -33,7 +33,7 @@ <td align="left" valign="top"></td> <td align="left" valign="top">true</td> <td align="left" valign="top">String</td> - <td align="left" valign="top">he action generate url for, if not using value</td> + <td align="left" valign="top">The action to generate the URL for, if not using value</td> </tr> <tr> <td align="left" valign="top">anchor</td> @@ -52,6 +52,14 @@ <td align="left" valign="top">Whether to encode parameters</td> </tr> <tr> + <td align="left" valign="top">escapeAmp</td> + <td align="left" valign="top">false</td> + <td align="left" valign="top">true</td> + <td align="left" valign="top">true</td> + <td align="left" valign="top">Boolean</td> + <td align="left" valign="top">Specifies whether to escape ampersand (&) to (&amp;) or not</td> + </tr> + <tr> <td align="left" valign="top">id</td> <td align="left" valign="top">false</td> <td align="left" valign="top"></td> @@ -65,7 +73,7 @@ <td align="left" valign="top">true</td> <td align="left" valign="top">true</td> <td align="left" valign="top">Boolean</td> - <td align="left" valign="top">Whether actual context should be included in url</td> + <td align="left" valign="top">Whether actual context should be included in URL</td> </tr> <tr> <td align="left" valign="top">includeParams</td> @@ -105,7 +113,7 @@ <td align="left" valign="top"></td> <td align="left" valign="top">true</td> <td align="left" valign="top">String</td> - <td align="left" valign="top">Specifies if this should be a portlet render or action url</td> + <td align="left" valign="top">Specifies if this should be a portlet render or action URL</td> </tr> <tr> <td align="left" valign="top">scheme</td> Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java (original) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/jsp/URLTagTest.java Tue Jul 24 10:33:38 2007 @@ -374,6 +374,19 @@ assertEquals("/team.action?section=team&company=acme+inc", writer.toString()); } + public void testRequestURIActionIncludeGetDoNotEscapeAmp() throws Exception { + request.setRequestURI("/public/about"); + request.setQueryString("section=team&company=acme inc"); + + tag.setAction("team"); + tag.setIncludeParams("get"); + tag.setEscapeAmp("false"); + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("/team.action?section=team&company=acme+inc", writer.toString()); + } + public void testRequestURINoActionIncludeNone() throws Exception { request.setRequestURI("/public/about"); request.setQueryString("section=team&company=acme inc"); Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java?view=diff&rev=559127&r1=559126&r2=559127 ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java (original) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java Tue Jul 24 10:33:38 2007 @@ -20,6 +20,8 @@ */ package org.apache.struts2.views.util; +import com.mockobjects.dynamic.Mock; + import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -28,11 +30,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsTestCase; -import com.mockobjects.dynamic.Mock; - /** * Test case for UrlHelper. @@ -40,8 +39,6 @@ */ public class UrlHelperTest extends StrutsTestCase { - - public void testForceAddSchemeHostAndPort() throws Exception { String expectedUrl = "http://localhost/contextPath/path1/path2/myAction.action"; @@ -142,6 +139,25 @@ params.put("foo", "bar"); String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params); + assertEquals(expectedString, urlString); + } + + /** + * just one &, not & + */ + public void testBuildUrlCorrectlyAddsDoNotEscapeAmp() { + String expectedString = "my.actionName?foo=bar&hello=world"; + Mock mockHttpServletRequest = new Mock(HttpServletRequest.class); + mockHttpServletRequest.expectAndReturn("getScheme", "http"); + Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); + mockHttpServletResponse.expectAndReturn("encodeURL", expectedString, expectedString); + + String actionName = "my.actionName"; + TreeMap params = new TreeMap(); + params.put("hello", "world"); + params.put("foo", "bar"); + + String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, null, true, true, false, false); assertEquals(expectedString, urlString); }