Compare commits

..

69 Commits

Author SHA1 Message Date
Lukasz Lenart 631ce98d17 [maven-release-plugin] prepare release STRUTS_2_3_33 2017-07-07 14:47:12 +02:00
Yasser Zamani 086b637355 Adds constant to control proxy member access 2017-07-07 13:35:10 +04:30
Lukasz Lenart ae56301979 WW-4805 Ports proxy detection to 2.3 2017-06-29 09:41:53 +02:00
Lukasz Lenart 73da12e723 Uses resource message key instead of passing message directly 2017-06-23 09:22:43 +02:00
Yasser Zamani 583da3d5df WW-4805 Blocks ognl access to class members of Spring proxy 2017-06-21 16:40:29 +04:30
Stefaan Dutry 9e8627ca66 WW-4770 Fixes problem with dojo.version 2017-03-24 21:34:31 +01:00
Lukasz Lenart c349dfa5ab WW-4735 Ports more relax domain suffix validation 2017-03-24 11:45:51 +01:00
Lukasz Lenart 4d888a5ab6 [maven-release-plugin] prepare for next development iteration 2017-03-06 12:03:18 +01:00
Lukasz Lenart 1ed29d508f [maven-release-plugin] prepare release STRUTS_2_3_32 2017-03-06 12:03:06 +01:00
Lukasz Lenart 3523064939 Uses default error key if specified key doesn't exist 2017-03-06 11:56:39 +01:00
Lukasz Lenart 54dff362f5 Adds additional example URLs to check if validation works properly 2016-11-25 19:35:15 +01:00
Lukasz Lenart b12864dd38 [maven-release-plugin] prepare for next development iteration 2016-10-05 08:53:34 +02:00
Lukasz Lenart de90290354 [maven-release-plugin] prepare release STRUTS_2_3_31 2016-10-05 08:53:19 +02:00
Lukasz Lenart ba0563183b Fixes issues in config-browse plugin 2016-09-28 11:42:24 +02:00
Lukasz Lenart b348fc552c WW-4674 Uses the same logic as in Dispatcher to control handling exception 2016-09-16 16:37:29 +02:00
Lukasz Lenart 81371e09c5 WW-4685 Adjusts cherry pick to 2.3.x 2016-09-16 16:07:59 +02:00
Lukasz Lenart 431e50e922 WW-4685 Uses dedicated prefixes to access I18N and Struts internals 2016-09-16 16:00:45 +02:00
Lukasz Lenart 156db8e5df WW-4685 Supports evaluating expressions from tiles definitions as a Struts values 2016-09-16 16:00:27 +02:00
Lukasz Lenart 6f5ddca471 WW-4667 Applies params to all instances of interceptor defined in stack 2016-09-06 09:07:02 +02:00
Lukasz Lenart cc0d52f216 WW-4663 Checks if expression is null to avoid NPE 2016-09-02 13:57:15 +02:00
Lukasz Lenart 23e0181328 WW-4669 Returns default action/method instead of throwing exception 2016-08-30 09:20:41 +02:00
Lukasz Lenart 030ffa3354 Disables params evaluation when creating results by convention 2016-08-17 15:36:20 +02:00
Lukasz Lenart 8e67b9144a Adds double check if resource exists 2016-08-17 15:36:20 +02:00
Lukasz Lenart 77017cd677 Allows modify params on coping 2016-08-17 15:36:20 +02:00
cnenning 7d8c3598ef merged fix for WW-4628 (proper url decoding of query-string) 2016-08-03 13:28:00 +02:00
cnenning 83bb64f4c2 WW-4628: new issues were introduced by last change, restoring old behavior and ignoring new tests 2016-08-01 15:42:36 +02:00
cnenning ae2840f183 merged fix for WW-4628 (avoid double encoding of url parameters) 2016-08-01 13:50:23 +02:00
Lukasz Lenart e8b48f8bb4 WW-4601 Hides webconsole when not in devMode 2016-07-08 06:44:12 +02:00
Lukasz Lenart 3243448084 [maven-release-plugin] prepare for next development iteration 2016-07-07 08:32:14 +02:00
Lukasz Lenart 5c61c9a575 [maven-release-plugin] prepare release STRUTS_2_3_30 2016-07-07 08:31:52 +02:00
Lukasz Lenart e40dcaa3f4 Uses the latest OGNL version 2016-06-29 20:24:57 +02:00
Lukasz Lenart ab6acd2382 Cleans up test 2016-06-26 22:04:23 +02:00
Lukasz Lenart bf0a01d390 Upgrades OGNL to latest version 2016-06-26 22:02:32 +02:00
Lukasz Lenart 00c47b7ce6 WW-4648 Upgrades commons-fileupload to the latest version 2016-06-22 10:56:31 +02:00
Lukasz Lenart c84810ff82 Adds test to guard pre-evaulation of name attribute 2016-06-22 10:46:09 +02:00
Lukasz Lenart 2ea59b31b4 Drops commented out test 2016-06-22 10:38:39 +02:00
Lukasz Lenart fae1e0e799 Uses LocaleProvider and default TextProvider instead of evaluating expression 2016-06-22 10:38:12 +02:00
Lukasz Lenart e4ce3ce839 Reverts dropping name pre-evaluation 2016-06-22 08:55:42 +02:00
Lukasz Lenart 730441b7a7 [maven-release-plugin] prepare for next development iteration 2016-06-14 07:11:10 +02:00
Lukasz Lenart bb22c585b5 [maven-release-plugin] prepare release STRUTS_2_3_29 2016-06-14 07:10:57 +02:00
Lukasz Lenart eccc31ebce Improves validation RegEx 2016-06-14 06:53:36 +02:00
Lukasz Lenart 1c92a8b2c9 Reverts previous RegEx 2016-06-03 13:12:02 +02:00
Lukasz Lenart b28b78c062 Introduces new callMethod() function to be used to execute actions 2016-06-03 13:10:58 +02:00
Lukasz Lenart 8d172b8e9b Upgrades to latest OGNL version 2016-06-03 13:08:51 +02:00
Lukasz Lenart 88b885339a Drops pre-evaluation of name 2016-06-02 16:57:44 +02:00
Lukasz Lenart 3f526d630a Drops unneeded method 2016-06-02 16:46:40 +02:00
Lukasz Lenart 34025eafec Allows empty string as a action name 2016-05-23 17:04:38 +02:00
Lukasz Lenart 54e8bf1f75 Disallows dot in action name 2016-05-23 16:02:25 +02:00
Lukasz Lenart 9f2cae2f1e WW-4627 Adds unit test to confirm the issue 2016-05-13 08:00:04 +02:00
Lukasz Lenart 9bcd1bc9f6 WW-4626 Adds support for .cat domain 2016-05-13 07:40:25 +02:00
Lukasz Lenart f052398cd2 WW-4618 Supports custom redirect types which base on ServletRedirectResult 2016-05-13 07:04:50 +02:00
Lukasz Lenart 237432512d Throws away methods that doesn't match pattern 2016-05-12 15:40:04 +02:00
Lukasz Lenart ca25476120 Upgrades commons-collections 2016-05-05 10:46:56 +02:00
Aleksandr Mashchenko 731fde544c WW-4630 - <s:submit> generates a value attribute for type=image which violates W3C (cherry picked from commit c4d2b687be8bdb454a11c5376bb475a8fa7f9c67​) 2016-05-04 23:00:33 +03:00
Lukasz Lenart 76eb8f38a3 Cleans up method name 2016-04-29 08:40:03 +02:00
Lukasz Lenart deefeffd11 Cleans up method name 2016-04-20 08:01:29 +02:00
Lukasz Lenart 98d2692e43 Drops defining location via request 2016-04-20 08:01:22 +02:00
Lukasz Lenart 46517afb14 Adds additional blocked classes 2016-04-20 08:01:12 +02:00
Lukasz Lenart 5190b53673 Uses isSequence flag to block chained expressions 2016-04-20 08:01:02 +02:00
Lukasz Lenart d36f31b3e1 Upgrades OGNL 2016-04-20 08:00:53 +02:00
Lukasz Lenart cbae13a6a8 Reverts 098ee502b4 2016-04-11 16:30:38 +02:00
Lukasz Lenart 865f1f2784 Adds support for deprecated param name 2016-04-08 10:26:21 +02:00
Aleksandr Mashchenko 5ce807aaea WW-4622 - Adds fallback to static config definitions to allow work in restricted envs (cherry picked from 6770b35), getRealPath NPE check; WW-4623, WW-4624 - Adds support for multiple tiles defintions (cherry picked from c12cd61) 2016-04-06 19:36:13 +03:00
Lukasz Lenart 17dab897d9 Uses proper doctype version 2016-04-04 11:21:54 +02:00
Lukasz Lenart 45fa8d0320 Adds missing closing tags 2016-04-04 11:21:37 +02:00
Lukasz Lenart 6fee2eb1a4 [maven-release-plugin] prepare for next development iteration 2016-03-18 20:50:53 +01:00
Lukasz Lenart 0ac8932aa3 [maven-release-plugin] prepare release STRUTS_2_3_28 2016-03-18 20:50:41 +01:00
Lukasz Lenart 6b497ef8f7 WW-4605 Reverts to previous flow when result is created just before executing it 2016-03-18 20:41:06 +01:00
Lukasz Lenart 519c767114 [maven-release-plugin] prepare for next development iteration 2016-03-18 08:54:43 +01:00
127 changed files with 1094 additions and 267 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-blank</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-jboss-blank</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-mailreader</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-portlet</artifactId>
@@ -5,11 +5,11 @@ Here you'll find examples of what is possible with the Struts Portlet integratio
<ul>
<li><a href="<s:url action="formExample"/>">A simple form</a></li>
<li><a href="<s:url action="formExamplePrg" method="input"/>">Form example with proper PRG</a></li>
<li><a href="<s:url action="formExampleModelDriven" method="input"/>">Model driven example</li>
<li><a href="<s:url action="formExampleModelDriven" method="input"/>">Model driven example</a>/li>
<li><a href="<s:url action="validationExample"/>">Validation</a></li>
<li><a href="<s:url action="tokenExample"/>">Token</a></li>
<li><a href="<s:url action="springExample"/>">Spring integration</a></li>
<li><a href="<s:url action="fileUpload" method="input"/>">File upload</li>
<li><a href="<s:url action="fileUpload" method="input"/>">File upload</a></li>
<li><a href="<s:url action="freeMarkerExample"/>">FreeMarker</a></li>
<li><a href="<s:url action="velocityHelloWorld"/>">Velocity</a></li>
<li><a href="<s:url action="index" namespace="/tiles"/>">Form Example Validation with Tiles</a></li>
+2 -2
View File
@@ -26,12 +26,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>2.3.27</version>
<version>2.3.33</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -36,7 +36,8 @@ public class SaveGangsterAction extends Action {
// Some code to save the gangster to the db as necessary
GangsterForm gform = (GangsterForm) form;
ActionMessages messages = new ActionMessages();
messages.add("msg", new ActionMessage("Gangster " + gform.getName() + " added successfully"));
System.out.println(gform.getName());
messages.add("msg", new ActionMessage("struts1.gangsterAdded", gform.getName()));
addMessages(request, messages);
return mapping.findForward("success");
@@ -7,3 +7,5 @@ item.create=Create {0}
item.list={0} List
token.transfer.time=The bank transfer was executed at {0,date,HH:mm:ss MM-dd-yyyy}
struts1.gangsterAdded=Gangster {0} added successfully
@@ -5,3 +5,5 @@ item.create={0} neu anlegen
item.list={0}-Liste
token.transfer.time=Die \u00dcberweisung wurde am {0,date,HH:mm:ss MM-dd-yyyy} durchgef\u00fchrt
struts1.gangsterAdded=Gangster {0} wurde erfolgreich hinzugefugt
@@ -0,0 +1 @@
struts1.gangsterAdded=Gangster {0} anadido con exito
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-archetypes</artifactId>
@@ -2,7 +2,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -2,7 +2,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -2,7 +2,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -2,7 +2,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -2,7 +2,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-archetypes</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+3 -3
View File
@@ -10,7 +10,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -25,7 +25,7 @@
</licenses>
<properties>
<struts-version.version>2.3.27</struts-version.version>
<struts-version.version>2.3.33</struts-version.version>
</properties>
<build>
@@ -197,6 +197,6 @@
</dependencyManagement>
<scm>
<tag>STRUTS_2_3_27</tag>
<tag>STRUTS_2_3_33</tag>
</scm>
</project>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -272,6 +272,13 @@ public final class StrutsConstants {
/** actions names' whitelist **/
public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
/** default action name to use when action didn't match the whitelist **/
public static final String STRUTS_DEFAULT_ACTION_NAME = "struts.default.action.name";
/** methods names' whitelist **/
public static final String STRUTS_ALLOWED_METHOD_NAMES = "struts.allowed.method.names";
/** default method name to use when method didn't match the whitelist **/
public static final String STRUTS_DEFAULT_METHOD_NAME = "struts.default.method.name";
/** enables action: prefix **/
public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled";
@@ -22,14 +22,12 @@
package org.apache.struts2.components;
import java.io.Writer;
import java.util.Locale;
import java.util.ResourceBundle;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.StrutsException;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
@@ -95,6 +93,8 @@ public class I18n extends Component {
protected String name;
protected Container container;
private TextProvider textProvider;
private TextProvider defaultTextProvider;
private LocaleProvider localeProvider;
public I18n(ValueStack stack) {
super(stack);
@@ -105,26 +105,31 @@ public class I18n extends Component {
this.container = container;
}
@Inject
public void setTextProvider(TextProvider textProvider) {
this.defaultTextProvider = textProvider;
}
@Inject
public void setLocaleProvider(LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
public boolean start(Writer writer) {
boolean result = super.start(writer);
try {
String name = this.findString(this.name, "name", "Resource bundle name is required. Example: foo or foo_en");
ResourceBundle bundle = (ResourceBundle) findValue("getTexts('" + name + "')");
ResourceBundle bundle = defaultTextProvider.getTexts(name);
if (bundle == null) {
bundle = LocalizedTextUtil.findResourceBundle(name, (Locale) getStack().getContext().get(ActionContext.LOCALE));
bundle = LocalizedTextUtil.findResourceBundle(name, localeProvider.getLocale());
}
if (bundle != null) {
final Locale locale = (Locale) getStack().getContext().get(ActionContext.LOCALE);
TextProviderFactory tpf = new TextProviderFactory();
container.inject(tpf);
textProvider = tpf.createInstance(bundle, new LocaleProvider() {
public Locale getLocale() {
return locale;
}
});
textProvider = tpf.createInstance(bundle, localeProvider);
getStack().push(textProvider);
pushed = true;
}
@@ -22,6 +22,8 @@ package org.apache.struts2.dispatcher;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.struts2.StrutsConstants;
@@ -39,6 +41,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
import java.util.StringTokenizer;
/**
@@ -93,6 +96,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
*/
protected String encoding;
protected boolean devMode;
/**
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting.
@@ -101,29 +105,34 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
* New setting
*/
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT)
public void setServeStaticContent(String val) {
serveStatic = "true".equals(val);
public void setServeStaticContent(String serveStaticContent) {
this.serveStatic = BooleanUtils.toBoolean(serveStaticContent);
}
/**
* Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE
* setting.
*
* @param val
* @param serveStaticBrowserCache
* New setting
*/
@Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE)
public void setServeStaticBrowserCache(String val) {
serveStaticBrowserCache = "true".equals(val);
public void setServeStaticBrowserCache(String serveStaticBrowserCache) {
this.serveStaticBrowserCache = BooleanUtils.toBoolean(serveStaticBrowserCache);
}
/**
* Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
* @param val New setting
* @param encoding New setting
*/
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setEncoding(String val) {
encoding = val;
public void setEncoding(String encoding) {
this.encoding = encoding;
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String devMode) {
this.devMode = Boolean.parseBoolean(devMode);
}
/*
@@ -141,7 +150,16 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
}
protected String getAdditionalPackages() {
return "org.apache.struts2.static template org.apache.struts2.interceptor.debugging static";
List<String> packages = new LinkedList<String>();
packages.add("org.apache.struts2.static");
packages.add("template");
packages.add("static");
if (devMode) {
packages.add("org.apache.struts2.interceptor.debugging");
}
return StringUtils.join(packages.iterator(), ' ');
}
/**
@@ -273,7 +291,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
* @param name resource name
* @param packagePrefix The package prefix to use to locate the resource
* @return full path
* @throws UnsupportedEncodingException
* @throws UnsupportedEncodingException If there is a encoding problem
*/
protected String buildPath(String name, String packagePrefix) throws UnsupportedEncodingException {
String resourcePath;
@@ -211,7 +211,11 @@ public class Dispatcher {
*/
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String mode) {
devMode = "true".equals(mode);
devMode = Boolean.parseBoolean(mode);
}
public boolean isDevMode() {
return devMode;
}
/**
@@ -265,6 +269,10 @@ public class Dispatcher {
this.handleException = Boolean.parseBoolean(handleException);
}
public boolean isHandleException() {
return handleException;
}
@Inject
public void setDispatcherErrorHandler(DispatcherErrorHandler errorHandler) {
this.errorHandler = errorHandler;
@@ -33,6 +33,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsException;
import org.apache.struts2.util.PrefixTrie;
import javax.servlet.http.HttpServletRequest;
@@ -120,6 +121,11 @@ public class DefaultActionMapper implements ActionMapper {
protected boolean alwaysSelectFullNamespace = false;
protected PrefixTrie prefixTrie = null;
protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
protected String defaultActionName = "index";
protected Pattern allowedMethodNames = Pattern.compile("[a-zA-Z_]*[0-9]*");
protected String defaultMethodName = "execute";
private boolean allowActionPrefix = false;
private boolean allowActionCrossNamespaceAccess = false;
@@ -136,7 +142,7 @@ public class DefaultActionMapper implements ActionMapper {
put(METHOD_PREFIX, new ParameterAction() {
public void execute(String key, ActionMapping mapping) {
if (allowDynamicMethodCalls) {
mapping.setMethod(key.substring(METHOD_PREFIX.length()));
mapping.setMethod(cleanupMethodName(key.substring(METHOD_PREFIX.length())));
}
}
});
@@ -148,7 +154,7 @@ public class DefaultActionMapper implements ActionMapper {
if (allowDynamicMethodCalls) {
int bang = name.indexOf('!');
if (bang != -1) {
String method = name.substring(bang + 1);
String method = cleanupMethodName(name.substring(bang + 1));
mapping.setMethod(method);
name = name.substring(0, bang);
}
@@ -204,6 +210,21 @@ public class DefaultActionMapper implements ActionMapper {
this.allowedActionNames = Pattern.compile(allowedActionNames);
}
@Inject(value = StrutsConstants.STRUTS_DEFAULT_ACTION_NAME, required = false)
public void setDefaultActionName(String defaultActionName) {
this.defaultActionName = defaultActionName;
}
@Inject(value = StrutsConstants.STRUTS_ALLOWED_METHOD_NAMES, required = false)
public void setAllowedMethodNames(String allowedMethodNames) {
this.allowedMethodNames = Pattern.compile(allowedMethodNames);
}
@Inject(value = StrutsConstants.STRUTS_DEFAULT_METHOD_NAME, required = false)
public void setDefaultMethodName(String defaultMethodName) {
this.defaultMethodName = defaultMethodName;
}
@Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_ENABLED)
public void setAllowActionPrefix(String allowActionPrefix) {
this.allowActionPrefix = "true".equalsIgnoreCase(allowActionPrefix);
@@ -375,7 +396,7 @@ public class DefaultActionMapper implements ActionMapper {
}
/**
* Cleans up action name from suspicious characters
* Checks action name against allowed pattern if not matched returns default action name
*
* @param rawActionName action name extracted from URI
* @return safe action name
@@ -384,18 +405,23 @@ public class DefaultActionMapper implements ActionMapper {
if (allowedActionNames.matcher(rawActionName).matches()) {
return rawActionName;
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Action [#0] does not match allowed action names pattern [#1], cleaning it up!",
rawActionName, allowedActionNames);
}
String cleanActionName = rawActionName;
for (String chunk : allowedActionNames.split(rawActionName)) {
cleanActionName = cleanActionName.replace(chunk, "");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Cleaned action name [#0]", cleanActionName);
}
return cleanActionName;
LOG.warn("{} did not match allowed action names {} - default action {} will be used!", rawActionName, allowedActionNames, defaultActionName);
return defaultActionName;
}
}
/**
* Checks method name (when DMI is enabled) against allowed pattern if not matched returns default action name
*
* @param rawMethodName method name extracted from URI
* @return safe method name
*/
protected String cleanupMethodName(final String rawMethodName) {
if (allowedMethodNames.matcher(rawMethodName).matches()) {
return rawMethodName;
} else {
LOG.warn("{} did not match allowed method names {} - default method {} will be used!", rawMethodName, allowedMethodNames, defaultMethodName);
return defaultMethodName;
}
}
@@ -120,7 +120,11 @@ public class JakartaMultiPartRequest implements MultiPartRequest {
if (LOG.isDebugEnabled()) {
LOG.debug("Preparing error message for key: [#0]", errorKey);
}
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
if (LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, new Object[0]) == null) {
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
} else {
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
}
}
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
@@ -539,7 +539,11 @@ public class JakartaStreamMultiPartRequest implements MultiPartRequest {
String errorKey = "struts.message.upload.error." + e.getClass().getSimpleName();
if (LOG.isDebugEnabled())
LOG.debug("Preparing error message for key: [#0]", errorKey);
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
if (LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, new Object[0]) == null) {
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
} else {
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
}
}
/**
@@ -108,7 +108,11 @@ public class MultiPartRequestWrapper extends StrutsRequestWrapper {
if (LOG.isDebugEnabled()) {
LOG.debug("Preparing error message for key: [#0]", errorKey);
}
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
if (LocalizedTextUtil.findText(this.getClass(), errorKey, getLocale(), null, new Object[0]) == null) {
return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
} else {
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
}
}
/**
@@ -168,7 +168,9 @@ public class PrepareOperations {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}
}
@@ -21,10 +21,12 @@ package org.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
import org.apache.struts2.dispatcher.ServletRedirectResult;
import java.util.Map;
@@ -68,7 +70,10 @@ class MessageStorePreResultListener implements PreResultListener {
boolean isRedirect = false;
try {
isRedirect = invocation.getResult() instanceof ServletRedirectResult;
ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode);
if (resultConfig != null) {
isRedirect = ServletRedirectResult.class.isAssignableFrom(Class.forName(resultConfig.getClassName()));
}
} catch (Exception e) {
LOG.warn("Cannot read result!", e);
}
@@ -44,11 +44,11 @@ public class ComponentUtils {
* @return true if it is an expression
*/
public static boolean isExpression(String expr) {
return expr.startsWith("%{") && expr.endsWith("}");
return expr != null && expr.startsWith("%{") && expr.endsWith("}");
}
public static boolean containsExpression(String expr) {
return expr.contains("%{") && expr.contains("}");
return expr != null && expr.contains("%{") && expr.contains("}");
}
}
@@ -19,4 +19,16 @@ public class URLDecoderUtil {
return UDecoder.URLDecode(sequence, charset);
}
/**
* Decodes a <code>x-www-form-urlencoded</code> string.
* @param sequence the String to decode
* @param charset The name of a supported character encoding.
* @param isQueryString whether input is a query string. If <code>true</code> other decoding rules apply.
* @return the newly decoded <code>String</code>
* @exception IllegalArgumentException If the encoding is not valid
*/
public static String decode(String sequence, String charset, boolean isQueryString) {
return UDecoder.URLDecode(sequence, charset, isQueryString);
}
}
@@ -24,6 +24,18 @@ package org.apache.struts2.views.util;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.StrutsConstants;
@@ -179,9 +191,9 @@ public class DefaultUrlHelper implements UrlHelper {
//if the action was not explicitly set grab the params from the request
if (escapeAmp) {
buildParametersString(params, link, AMP);
buildParametersString(params, link, AMP, true);
} else {
buildParametersString(params, link, "&");
buildParametersString(params, link, "&", true);
}
String result = link.toString();
@@ -202,6 +214,10 @@ public class DefaultUrlHelper implements UrlHelper {
}
public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator) {
buildParametersString(params, link, paramSeparator, true);
}
public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator, boolean encode) {
if ((params != null) && (params.size() > 0)) {
if (!link.toString().contains("?")) {
link.append("?");
@@ -219,7 +235,7 @@ public class DefaultUrlHelper implements UrlHelper {
if (value instanceof Iterable) {
for (Iterator iterator = ((Iterable) value).iterator(); iterator.hasNext();) {
Object paramValue = iterator.next();
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY));
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY, encode));
if (iterator.hasNext()) {
link.append(paramSeparator);
@@ -229,14 +245,14 @@ public class DefaultUrlHelper implements UrlHelper {
Object[] array = (Object[]) value;
for (int i = 0; i < array.length; i++) {
Object paramValue = array[i];
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY));
link.append(buildParameterSubstring(name, paramValue != null ? paramValue.toString() : StringUtils.EMPTY, encode));
if (i < array.length - 1) {
link.append(paramSeparator);
}
}
} else {
link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY));
link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY, encode));
}
if (iter.hasNext()) {
@@ -250,11 +266,11 @@ public class DefaultUrlHelper implements UrlHelper {
return HTTP_PROTOCOL.equals(scheme) || HTTPS_PROTOCOL.equals(scheme);
}
private String buildParameterSubstring(String name, String value) {
private String buildParameterSubstring(String name, String value, boolean encode) {
StringBuilder builder = new StringBuilder();
builder.append(encode(name));
builder.append(encode ? encode(name) : name);
builder.append('=');
builder.append(encode(value));
builder.append(encode ? encode(value) : value);
return builder.toString();
}
@@ -283,7 +299,7 @@ public class DefaultUrlHelper implements UrlHelper {
*/
public String decode( String input ) {
try {
return URLDecoderUtil.decode(input, encoding);
return URLDecoderUtil.decode(input, encoding, false);
} catch (Exception e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Could not decode URL parameter '#0', returning value un-decoded", input);
@@ -292,6 +308,22 @@ public class DefaultUrlHelper implements UrlHelper {
}
}
/**
* Decodes the URL using {@link URLDecoderUtil#decode(String, String, boolean)} with the encoding specified in the configuration.
*
* @param input the input to decode
* @param isQueryString whether input is a query string. If <code>true</code> other decoding rules apply.
* @return the encoded string
*/
public String decode( String input, boolean isQueryString ) {
try {
return URLDecoderUtil.decode(input, encoding, isQueryString);
} catch (Exception e) {
LOG.warn("Could not decode URL parameter '{}', returning value un-decoded", input);
return input;
}
}
public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) {
Map<String, Object> queryParams = new LinkedHashMap<String, Object>();
if (queryString != null) {
@@ -308,8 +340,8 @@ public class DefaultUrlHelper implements UrlHelper {
paramValue = tmpParams[1];
}
if (paramName != null) {
paramName = decode(paramName);
String translatedParamValue = decode(paramValue);
paramName = decode(paramName, true);
String translatedParamValue = decode(paramValue, true);
if (queryParams.containsKey(paramName) || forceValueArray) {
// WW-1619 append new param value to existing value(s)
@@ -448,12 +448,7 @@ public class XSLTResult implements Result {
ServletActionContext.getServletContext());
}
protected Templates getTemplates(String path) throws TransformerException, IOException {
String pathFromRequest = ServletActionContext.getRequest().getParameter("xslt.location");
if (pathFromRequest != null)
path = pathFromRequest;
protected Templates getTemplates(final String path) throws TransformerException, IOException {
if (path == null)
throw new TransformerException("Stylesheet path is null");
@@ -50,6 +50,8 @@
ognl.OgnlContext,
ognl.ClassResolver,
ognl.TypeConverter,
ognl.MemberAccess,
ognl.DefaultMemberAccess,
com.opensymphony.xwork2.ognl.SecurityMemberAccess,
com.opensymphony.xwork2.ActionContext" />
@@ -61,6 +61,9 @@
</#if>
<#else>
<input type="submit"<#rt/>
<#if parameters.nameValue?has_content>
value="<@s.property value="parameters.nameValue"/>"<#rt/>
</#if>
</#if>
<#if parameters.id?has_content>
id="${parameters.id?html}"<#rt/>
@@ -68,9 +71,6 @@
<#if parameters.name?has_content>
name="${parameters.name?html}"<#rt/>
</#if>
<#if parameters.nameValue?has_content>
value="<@s.property value="parameters.nameValue"/>"<#rt/>
</#if>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
</#if>
@@ -164,21 +164,4 @@ public class UIBeanTest extends StrutsInternalTestCase {
assertEquals("12", txtFld.getTheme());
}
// I couldn't figure out how to make this test work. Bailing for now.
// public void testEscapeLabel() throws Exception {
// ValueStack stack = ActionContext.getContext().getValueStack();
// MockHttpServletRequest req = new MockHttpServletRequest();
// MockHttpServletResponse res = new MockHttpServletResponse();
// stack.push(this);
//
// TextField txtFld = new TextField(stack, req, res);
// txtFld.setKey("test['foo']");
// txtFld.evaluateParams();
// assertEquals("test_label", txtFld.getParameters().get("label"));
// }
//
// public String getText(String key) {
// assertEquals("test[\\'foo\\']", key);
// return "test_label";
// }
}
@@ -30,6 +30,7 @@ import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsException;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest;
@@ -162,8 +163,8 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
public void testGetMappingWithNamespaceSlash() throws Exception {
req.setupGetRequestURI("/my.hh/abc.action");
req.setupGetServletPath("/my.hh/abc.action");
req.setupGetRequestURI("/my-hh/abc.action");
req.setupGetServletPath("/my-hh/abc.action");
req.setupGetAttribute(null);
req.addExpectedGetAttributeName("javax.servlet.include.servlet_path");
@@ -180,7 +181,7 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
mapping = mapper.getMapping(req, configManager);
assertEquals("", mapping.getNamespace());
assertEquals("my.hh/abc", mapping.getName());
assertEquals("my-hh/abc", mapping.getName());
}
public void testGetMappingWithUnknownNamespace() throws Exception {
@@ -845,13 +846,13 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
assertEquals(actionName, mapper.cleanupActionName(actionName));
actionName = "${action}";
assertEquals("action", mapper.cleanupActionName(actionName));
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
actionName = "${${%{action}}}";
assertEquals("action", mapper.cleanupActionName(actionName));
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
actionName = "${#foo='action',#foo}";
assertEquals("fooactionfoo", mapper.cleanupActionName(actionName));
assertEquals(mapper.defaultActionName, mapper.cleanupActionName(actionName));
actionName = "test-action";
assertEquals("test-action", mapper.cleanupActionName(actionName));
@@ -863,4 +864,19 @@ public class DefaultActionMapperTest extends StrutsInternalTestCase {
assertEquals("test!bar.action", mapper.cleanupActionName(actionName));
}
public void testAllowedMethodNames() throws Exception {
DefaultActionMapper mapper = new DefaultActionMapper();
assertEquals("", mapper.cleanupMethodName(""));
assertEquals("test", mapper.cleanupMethodName("test"));
assertEquals("test_method", mapper.cleanupMethodName("test_method"));
assertEquals("_test", mapper.cleanupMethodName("_test"));
assertEquals("test1", mapper.cleanupMethodName("test1"));
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("2test"));
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("%{exp}"));
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("${%{foo}}"));
assertEquals(mapper.defaultMethodName, mapper.cleanupMethodName("${#foo='method',#foo}"));
}
}
@@ -4,9 +4,13 @@ import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.mock.MockActionProxy;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
import org.apache.struts2.dispatcher.ServletRedirectResult;
import org.easymock.EasyMock;
import javax.servlet.http.HttpServletRequest;
@@ -138,8 +142,13 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
EasyMock.expectLastCall().andReturn(action);
EasyMock.expectLastCall().anyTimes();
mockActionInvocation.getResult();
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
mockActionInvocation.getProxy();
MockActionProxy actionProxy = new MockActionProxy();
ResultConfig resultConfig = new ResultConfig.Builder(Action.SUCCESS, ServletRedirectResult.class.getName()).build();
ActionConfig actionConfig = new ActionConfig.Builder("", "test", action.getClass().getName()).addResultConfig(resultConfig).build();
actionProxy.setConfig(actionConfig);
EasyMock.expectLastCall().andReturn(actionProxy);
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(mockActionInvocation);
@@ -213,8 +222,13 @@ public class MessageStorePreResultListenerTest extends StrutsInternalTestCase {
mockActionInvocation.getAction();
EasyMock.expectLastCall().andReturn(action);
mockActionInvocation.getResult();
EasyMock.expectLastCall().andReturn(new ServletActionRedirectResult());
mockActionInvocation.getProxy();
MockActionProxy actionProxy = new MockActionProxy();
ResultConfig resultConfig = new ResultConfig.Builder(Action.SUCCESS, ServletRedirectResult.class.getName()).build();
ActionConfig actionConfig = new ActionConfig.Builder("", "test", action.getClass().getName()).addResultConfig(resultConfig).build();
actionProxy.setConfig(actionConfig);
EasyMock.expectLastCall().andReturn(actionProxy);
EasyMock.expectLastCall().anyTimes();
EasyMock.replay(mockActionInvocation);
@@ -93,6 +93,10 @@ public class ComponentUtilsTest extends StrutsInternalTestCase {
assertFalse(actual);
}
public void testIsExpressionIsFalseWhenNull() throws Exception {
assertFalse(ComponentUtils.isExpression(null));
}
public void testContainsExpressionIsTrue() throws Exception {
// given
String anExpression = "%{foo}";
@@ -125,6 +129,10 @@ public class ComponentUtilsTest extends StrutsInternalTestCase {
// then
assertFalse(actual);
}
public void testContainsExpressionIsFalseWhenNull() throws Exception {
assertFalse(ComponentUtils.containsExpression(null));
}
}
class MockConfigurationProvider implements ConfigurationProvider {
@@ -68,4 +68,11 @@ public class URLDecoderUtilTest {
assertEquals("xxxx\u00ea", result);
}
@Test
public void testURLDecodePlusCharAsSpace() {
String result = URLDecoderUtil.decode("a+b", "UTF-8", true);
assertEquals("a b", result);
}
}
@@ -179,8 +179,7 @@ public class ActionTagTest extends AbstractTagTest {
assertTrue(stack.getContext().containsKey(ServletActionContext.PAGE_CONTEXT));
assertTrue(stack.getContext().get(ServletActionContext.PAGE_CONTEXT)instanceof PageContext);
assertNotNull(result);
assertFalse(result.isExecuted());
assertNull(result); // result is never executed, hence never set into invocation
}
public void testExecuteButResetReturnSameInvocation() throws Exception {
@@ -237,7 +237,7 @@ public class SubmitTest extends AbstractUITagTest {
tag.doStartTag();
tag.doEndTag();
assertEquals("<input type=\"submit\" id=\"myname\" name=\"myname\" value=\"Submit\"/>", writer.toString().trim());
assertEquals("<input type=\"submit\" value=\"Submit\" id=\"myname\" name=\"myname\"/>", writer.toString().trim());
}
/**
@@ -281,4 +281,20 @@ public class TextfieldTest extends AbstractUITagTest {
verify(TextFieldTag.class.getResource("Textfield-11.txt"));
}
public void testNameEvaluation() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setArray(new String[]{"test", "bar"});
testAction.setFooInt(1);
TextFieldTag tag = new TextFieldTag();
tag.setPageContext(pageContext);
tag.setName("array[%{fooInt}]");
tag.doStartTag();
tag.doEndTag();
verify(TextFieldTag.class.getResource("Textfield-12.txt"));
}
}
@@ -21,21 +21,23 @@
package org.apache.struts2.views.util;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Scope.Strategy;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsInternalTestCase;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsInternalTestCase;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Scope.Strategy;
/**
* Test case for DefaultUrlHelper.
@@ -393,7 +395,15 @@ public class DefaultUrlHelperTest extends StrutsInternalTestCase {
assertEquals(result, expectedResult);
}
public void testDecodeSpacesInQueryString() throws Exception {
Map<String, Object> queryParameters = urlHelper.parseQueryString("name=value+with+space", false);
assertTrue(queryParameters.containsKey("name"));
assertEquals("value with space", queryParameters.get("name"));
}
public void setUp() throws Exception {
super.setUp();
stubContainer = new StubContainer(container);
@@ -1,3 +1,3 @@
<tr>
<td colspan="2"><div align="left"><input type="submit" id="myname" name="myname" value="bar" disabled="disabled" tabindex="1"/></div></td>
<td colspan="2"><div align="left"><input type="submit" value="bar" id="myname" name="myname" disabled="disabled" tabindex="1"/></div></td>
</tr>
@@ -1,3 +1,3 @@
<tr>
<td colspan="2"><div align="right"><input type="submit" id="myname" name="myname" value="Submit" title="mytitle"/></div></td>
<td colspan="2"><div align="right"><input type="submit" value="Submit" id="myname" name="myname" title="mytitle"/></div></td>
</tr>
@@ -1,3 +1,3 @@
<tr>
<td colspan="2"><div align="right"><input type="image" alt="bar" id="myname" name="myname" value="bar" disabled="disabled"/></div></td>
<td colspan="2"><div align="right"><input type="image" alt="bar" id="myname" name="myname" disabled="disabled"/></div></td>
</tr>
@@ -1,3 +1,3 @@
<tr>
<td colspan="2"><div align="right"><input type="image" alt="mylabel" src="some.gif" id="myname" name="myname" value="bar"/></div></td>
<td colspan="2"><div align="right"><input type="image" alt="mylabel" src="some.gif" id="myname" name="myname"/></div></td>
</tr>
@@ -0,0 +1,4 @@
<tr>
<td class="tdLabel"></td>
<td><input type="text" name="array[1]" value="bar" id="array_1_"/></td>
</tr>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-cdi-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-codebehind-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-config-browser-plugin</artifactId>
@@ -50,6 +50,7 @@ public class ShowBeansAction extends ActionNamesAction {
@Inject
public void setContainer(Container container) {
super.setContainer(container);
bindings = new TreeMap<String, Set<Binding>>();
bindings.put(ObjectFactory.class.getName(), addBindings(container, ObjectFactory.class, StrutsConstants.STRUTS_OBJECTFACTORY));
bindings.put(XWorkConverter.class.getName(), addBindings(container, XWorkConverter.class, StrutsConstants.STRUTS_XWORKCONVERTER));
@@ -36,6 +36,7 @@ public class ShowConstantsAction extends ActionNamesAction {
@Inject
public void setContainer(Container container) {
super.setContainer(container);
constants = new HashMap<String, String>();
for (String key : container.getInstanceNames(String.class)) {
constants.put(key, container.getInstance(String.class, key));
@@ -22,7 +22,7 @@
-->
<#include "tigris-macros.ftl">
<@startPage pageTitle="Actions in namespace"/>
<h3>Actions in <#if namespace == ""> default namespace <#else> ${namespace} </#if></h3>
<h3>Actions in <#if namespace == ""> default namespace <#else> ${namespace?html} </#if></h3>
<table>
<tr>
<td>
@@ -29,7 +29,7 @@
<tr><td>Action name:</td><td>${actionName}</td></tr>
<tr><td>Namespace:</td><td> ${namespace}</td></tr>
<tr><td>Action class:</td><td> ${config.className}</td></tr>
<tr><td>Action method:</td><td> <#if config.methodName?exists>${config.methodName}</#if></td></tr>
<tr><td>Action method:</td><td> <#if config.methodName??>${config.methodName}</#if></td></tr>
<tr><td>Parameters:</td><td> <#list config.params?keys as p>
${p}
</#list></td></tr>
@@ -41,9 +41,9 @@
</table>
<!-- URLTag is faulty -->
<@s.url id="url" action="showConfig" includeParams="none">
<@s.param name="namespace">${namespace}</@s.param>
<@s.param name="actionName">${actionName}</@s.param>
<@s.url var="url" action="showConfig" includeParams="none">
<@s.param name="namespace">${namespace?html}</@s.param>
<@s.param name="actionName">${actionName?html}</@s.param>
</@s.url>
<#assign url = url + "&amp;detailView=">
<!-- Set all to false -->
@@ -25,7 +25,7 @@
<#call startPage pageTitle="Validator Details"/>
<table>
<tr><td>Validated Class:</td><td>${action.stripPackage(clazz)}</td></tr>
<tr><td>Context:</td><td>${context}</td></tr>
<tr><td>Context:</td><td>${context?html}</td></tr>
<tr><td>Validator Number:</td><td>${selected}</td></tr>
<tr><td>Validator Type:</td><td>${action.stripPackage(selectedValidator.class)}</td></tr>
</table>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
@@ -43,6 +43,7 @@ import com.opensymphony.xwork2.util.logging.LoggerFactory;
import javax.servlet.ServletContext;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -332,27 +333,34 @@ public class ConventionUnknownHandler implements UnknownHandler {
Map<String, ResultTypeConfig> resultsByExtension) {
try {
boolean traceEnabled = LOG.isTraceEnabled();
if (traceEnabled)
if (traceEnabled) {
LOG.trace("Checking ServletContext for [#0]", path);
}
if (servletContext.getResource(path) != null) {
if (traceEnabled)
LOG.trace("Found");
URL resource = servletContext.getResource(path);
if (resource != null && resource.getPath().endsWith(path)) {
if (traceEnabled) {
LOG.trace("Found resource #0", resource);
}
return buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
}
if (traceEnabled)
LOG.trace("Checking ClasLoader for #0", path);
if (traceEnabled) {
LOG.trace("Checking ClassLoader for #0", path);
}
String classLoaderPath = path.startsWith("/") ? path.substring(1, path.length()) : path;
if (ClassLoaderUtil.getResource(classLoaderPath, getClass()) != null) {
if (traceEnabled)
LOG.trace("Found");
resource = ClassLoaderUtil.getResource(classLoaderPath, getClass());
if (resource != null && resource.getPath().endsWith(classLoaderPath)) {
if (traceEnabled) {
LOG.trace("Found resource #0", resource);
}
return buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
}
} catch (MalformedURLException e) {
if (LOG.isErrorEnabled())
if (LOG.isErrorEnabled()) {
LOG.error("Unable to parse template path: [#0] skipping...", path);
}
}
return null;
@@ -107,14 +107,26 @@ public class ConventionsServiceImpl implements ConventionsService {
public Map<String, ResultTypeConfig> getResultTypesByExtension(PackageConfig packageConfig) {
Map<String, ResultTypeConfig> results = packageConfig.getAllResultTypeConfigs();
ResultTypeConfig dispatcher = disableParse(results.get("dispatcher"));
ResultTypeConfig velocity = disableParse(results.get("velocity"));
ResultTypeConfig freemarker = disableParse(results.get("freemarker"));
Map<String, ResultTypeConfig> resultsByExtension = new HashMap<String, ResultTypeConfig>();
resultsByExtension.put("jsp", results.get("dispatcher"));
resultsByExtension.put("jspf", results.get("dispatcher"));
resultsByExtension.put("jspx", results.get("dispatcher"));
resultsByExtension.put("vm", results.get("velocity"));
resultsByExtension.put("ftl", results.get("freemarker"));
resultsByExtension.put("html", results.get("dispatcher"));
resultsByExtension.put("htm", results.get("dispatcher"));
resultsByExtension.put("jsp", dispatcher);
resultsByExtension.put("jspf", dispatcher);
resultsByExtension.put("jspx", dispatcher);
resultsByExtension.put("vm", velocity);
resultsByExtension.put("ftl", freemarker);
resultsByExtension.put("html", dispatcher);
resultsByExtension.put("htm", dispatcher);
return resultsByExtension;
}
private ResultTypeConfig disableParse(ResultTypeConfig resultConfig) {
if (resultConfig != null) {
return new ResultTypeConfig.Builder(resultConfig).addParam("parse", "false").build();
}
return null;
}
}
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<artifactId>struts2-plugins</artifactId>
<groupId>org.apache.struts</groupId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -33,7 +33,7 @@ dojo.global=function(){
return dj_currentContext;
};
dojo.locale=djConfig.locale;
dojo.version={major:0,minor:4,patch:3,flag:"",revision:Number("$Rev$".match(/[0-9]+/)[0]),toString:function(){
dojo.version={major:0,minor:4,patch:3,flag:"",revision:Number("$Rev: 670371 $".match(/[0-9]+/)[0]),toString:function(){
with(dojo.version){
return major+"."+minor+"."+patch+flag+" ("+revision+")";
}
@@ -100,7 +100,7 @@ dojo.locale = djConfig.locale;
dojo.version = {
// summary: version number of this instance of dojo.
major: 0, minor: 4, patch: 3, flag: "",
revision: Number("$Rev$".match(/[0-9]+/)[0]),
revision: Number("$Rev: 670371 $".match(/[0-9]+/)[0]),
toString: function(){
with(dojo.version){
return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String
@@ -90,7 +90,7 @@ dojo.locale = djConfig.locale;
dojo.version = {
// summary: version number of this instance of dojo.
major: 0, minor: 4, patch: 3, flag: "",
revision: Number("$Rev$".match(/[0-9]+/)[0]),
revision: Number("$Rev: 670371 $".match(/[0-9]+/)[0]),
toString: function(){
with(dojo.version){
return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-java8-support-plugin</artifactId>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-javatemplates-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-jfreechart-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-jsf-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-json-plugin</artifactId>
@@ -7,7 +7,9 @@ import org.junit.Test;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@@ -90,6 +92,41 @@ public class JSONWriterTest extends StrutsTestCase{
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-02.txt"), json);
}
@Test
public void testWriteBeanWithList() throws Exception {
BeanWithList bean1 = new BeanWithList();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.Two);
List<String> errors = new ArrayList<String>();
errors.add("Field is required");
bean1.setErrors(errors);
JSONWriter jsonWriter = new JSONWriter();
jsonWriter.setEnumAsBean(false);
jsonWriter.setIgnoreHierarchy(false);
String json = jsonWriter.write(bean1);
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-04.txt"), json);
}
private class BeanWithList extends Bean {
private List<String> errors;
public List<String> getErrors() {
return errors;
}
public void setErrors(List<String> errors) {
this.errors = errors;
}
}
private class AnnotatedBean extends Bean{
private URL url;
@@ -0,0 +1,16 @@
{
"bigDecimal":null,
"bigInteger":null,
"booleanField":true,
"byteField":0,
"charField":"s",
"doubleField":10.1,
"enumBean":"Two",
"enumField":"ValueA",
"errors":["Field is required"],
"floatField":1.5,
"intField":10,
"longField":100,
"objectField":null,
"stringField":"str"
}
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-rest-plugin</artifactId>
@@ -112,6 +112,7 @@ public class RestActionMapper extends DefaultActionMapper {
private boolean allowDynamicMethodCalls = false;
public RestActionMapper() {
this.defaultMethodName = indexMethodName;
}
public String getIdParameterName() {
@@ -290,7 +291,7 @@ public class RestActionMapper extends DefaultActionMapper {
fullName = fullName.substring(0, lastSlashPos);
}
mapping.setName(fullName);
mapping.setName(cleanupActionName(fullName));
}
return mapping;
}
@@ -311,7 +312,7 @@ public class RestActionMapper extends DefaultActionMapper {
mapping.setName(actionName);
if (allowDynamicMethodCalls) {
mapping.setMethod(actionMethod);
mapping.setMethod(cleanupMethodName(actionMethod));
} else {
mapping.setMethod(null);
}
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-sitegraph-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-sitemesh-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-spring-plugin</artifactId>
@@ -34,6 +34,7 @@
<constant name="struts.class.reloading.watchList" value="" />
<constant name="struts.class.reloading.acceptClasses" value="" />
<constant name="struts.class.reloading.reloadConfig" value="false" />
<constant name="xwork.disallowProxyMemberAccess" value="true" />
<package name="spring-default">
<interceptors>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-struts1-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-testng-plugin</artifactId>
+1 -1
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.3.27</version>
<version>2.3.33</version>
</parent>
<artifactId>struts2-tiles-plugin</artifactId>
@@ -0,0 +1,67 @@
/*
* 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.tiles;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.evaluator.AbstractAttributeEvaluator;
import org.apache.tiles.servlet.context.ServletUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class I18NAttributeEvaluator extends AbstractAttributeEvaluator {
private static final Logger LOG = LoggerFactory.getLogger(I18NAttributeEvaluator.class);
public Object evaluate(String expression, TilesRequestContext request) {
Object result = expression;
HttpServletRequest httpRequest = ServletUtil.getServletRequest(request).getRequest();
ActionContext ctx = ServletActionContext.getActionContext(httpRequest);
if (ctx == null) {
LOG.error("Cannot obtain HttpServletRequest from [{}]", request.getClass().getName());
throw new ConfigurationException("There is no ActionContext for current request!");
}
TextProviderFactory tpf = new TextProviderFactory();
ctx.getContainer().inject(tpf);
LocaleProvider localeProvider = ctx.getContainer().getInstance(LocaleProvider.class);
TextProvider textProvider = tpf.createInstance(ctx.getActionInvocation().getAction().getClass(), localeProvider);
if (textProvider != null) {
LOG.debug("Trying find text [{}] using TextProvider {}", expression, textProvider);
result = textProvider.getText(expression);
}
return result;
}
public void init(Map<String, String> initParameters) {
}
}

Some files were not shown because too many files have changed in this diff Show More