diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index b7dec53b2..c75ff3d54 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -379,7 +379,7 @@ public final class StrutsConstants { public static final String STRUTS_CONVERTER_FILE_PROCESSOR = "struts.converter.file.processor"; public static final String STRUTS_CONVERTER_ANNOTATION_PROCESSOR = "struts.converter.annotation.processor"; public static final String STRUTS_CONVERTER_CREATOR = "struts.converter.creator"; - public static final String STRUTS_CONVERTER_HOLDER = "struts..converter.holder"; + public static final String STRUTS_CONVERTER_HOLDER = "struts.converter.holder"; public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser"; @@ -462,4 +462,7 @@ public final class StrutsConstants { public static final String STRUTS_URL_QUERY_STRING_PARSER = "struts.url.queryStringParser"; public static final String STRUTS_URL_ENCODER = "struts.url.encoder"; public static final String STRUTS_URL_DECODER = "struts.url.decoder"; + + /** A global flag to set property {@link org.apache.struts2.components.Checkbox#setSubmitUnchecked(String)} */ + public static final String STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED = "struts.ui.checkbox.submitUnchecked"; } diff --git a/core/src/main/java/org/apache/struts2/components/Checkbox.java b/core/src/main/java/org/apache/struts2/components/Checkbox.java index 81ea73fa7..49e054eb1 100644 --- a/core/src/main/java/org/apache/struts2/components/Checkbox.java +++ b/core/src/main/java/org/apache/struts2/components/Checkbox.java @@ -18,13 +18,14 @@ */ package org.apache.struts2.components; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.ValueStack; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; -import com.opensymphony.xwork2.util.ValueStack; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * @@ -46,15 +47,19 @@ import com.opensymphony.xwork2.util.ValueStack; * * * - * */ @StrutsTag( - name="checkbox", - tldTagClass="org.apache.struts2.views.jsp.ui.CheckboxTag", - description="Render a checkbox input field", - allowDynamicAttributes=true) + name = "checkbox", + tldTagClass = "org.apache.struts2.views.jsp.ui.CheckboxTag", + description = "Render a checkbox input field", + allowDynamicAttributes = true) public class Checkbox extends UIBean { - final public static String TEMPLATE = "checkbox"; + + private static final String ATTR_SUBMIT_UNCHECKED = "submitUnchecked"; + + public static final String TEMPLATE = "checkbox"; + + private String submitUncheckedGlobal; protected String fieldValue; protected String submitUnchecked; @@ -69,35 +74,45 @@ public class Checkbox extends UIBean { protected void evaluateExtraParams() { if (fieldValue != null) { - addParameter("fieldValue", findString(fieldValue)); + addParameter(ATTR_FIELD_VALUE, findString(fieldValue)); } else { - addParameter("fieldValue", "true"); + addParameter(ATTR_FIELD_VALUE, "true"); } if (submitUnchecked != null) { Object parsedValue = findValue(submitUnchecked, Boolean.class); - addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue); + addParameter(ATTR_SUBMIT_UNCHECKED, parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue); + } else if (submitUncheckedGlobal != null) { + addParameter(ATTR_SUBMIT_UNCHECKED, Boolean.parseBoolean(submitUncheckedGlobal)); } else { - addParameter("submitUnchecked", false); + addParameter(ATTR_SUBMIT_UNCHECKED, false); } } - protected Class getValueClassType() { + @Override + protected Class getValueClassType() { return Boolean.class; // for checkboxes, everything needs to end up as a Boolean } - @StrutsTagAttribute(description="The actual HTML value attribute of the checkbox.", defaultValue="true") + @Inject(value = StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED, required = false) + public void setSubmitUncheckedGlobal(String submitUncheckedGlobal) { + this.submitUncheckedGlobal = submitUncheckedGlobal; + } + + @StrutsTagAttribute(description = "The actual HTML value attribute of the checkbox.", defaultValue = "true") public void setFieldValue(String fieldValue) { this.fieldValue = fieldValue; } - @StrutsTagAttribute(description="If set to true, unchecked elements will be submitted with the form.", type="Boolean", defaultValue="false") + @StrutsTagAttribute(description = "If set to true, unchecked elements will be submitted with the form. " + + "Since Struts 6.1.1 you can use a constant \"" + StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED + "\" to set this attribute globally", + type = "Boolean", defaultValue = "false") public void setSubmitUnchecked(String submitUnchecked) { this.submitUnchecked = submitUnchecked; } @Override - @StrutsTagAttribute(description="Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme") + @StrutsTagAttribute(description = "Define label position of form element (top/left), also 'right' is supported when using 'xhtml' theme") public void setLabelPosition(String labelPosition) { super.setLabelPosition(labelPosition); } diff --git a/core/src/main/java/org/apache/struts2/components/UIBean.java b/core/src/main/java/org/apache/struts2/components/UIBean.java index dc7213f36..c28e54c66 100644 --- a/core/src/main/java/org/apache/struts2/components/UIBean.java +++ b/core/src/main/java/org/apache/struts2/components/UIBean.java @@ -435,8 +435,11 @@ import java.util.Map; * */ public abstract class UIBean extends Component { + private static final Logger LOG = LogManager.getLogger(UIBean.class); + protected static final String ATTR_FIELD_VALUE = "fieldValue"; + protected HttpServletRequest request; protected HttpServletResponse response; diff --git a/core/src/main/resources/template/simple/checkbox.ftl b/core/src/main/resources/template/simple/checkbox.ftl index a344e6d30..af563f4b5 100644 --- a/core/src/main/resources/template/simple/checkbox.ftl +++ b/core/src/main/resources/template/simple/checkbox.ftl @@ -38,11 +38,11 @@ <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> -/> +/><#rt/> <#if parameters.submitUnchecked!false> <#if parameters.disabled!false> disabled="disabled"<#rt/> - /> -<#rt/> + /><#rt/> + diff --git a/core/src/site/resources/tags/checkbox-attributes.html b/core/src/site/resources/tags/checkbox-attributes.html index de448af92..ffc6ab1f8 100644 --- a/core/src/site/resources/tags/checkbox-attributes.html +++ b/core/src/site/resources/tags/checkbox-attributes.html @@ -283,7 +283,7 @@ false false Boolean - If set to true, unchecked elements will be submitted with the form. + If set to true, unchecked elements will be submitted with the form. Since Struts 6.1.1 you can use a constant "struts.ui.checkbox.submitUnchecked" to set this property globally tabindex diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java index 5f35b8acd..137b3db21 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java @@ -18,11 +18,21 @@ */ package org.apache.struts2.views.jsp.ui; -import java.util.Map; - +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.config.ConfigurationProvider; +import com.opensymphony.xwork2.inject.ContainerBuilder; +import com.opensymphony.xwork2.test.StubConfigurationProvider; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.location.LocatableProperties; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.TestAction; import org.apache.struts2.views.jsp.AbstractUITagTest; +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + public class CheckboxTest extends AbstractUITagTest { /** @@ -31,7 +41,7 @@ public class CheckboxTest extends AbstractUITagTest { * String, String[])} as properties to verify.
This implementation extends testdata from AbstractUITag. * * @return A Map of PropertyHolders values bound to {@link org.apache.struts2.views.jsp.AbstractUITagTest.PropertyHolder#getName()} - * as key. + * as key. */ @Override protected Map initializedGenericTagTestProperties() { @@ -73,7 +83,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testChecked_clearTagStateSet() throws Exception { @@ -102,7 +112,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithTopLabelPosition() throws Exception { @@ -129,10 +139,10 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } - public void testCheckedWithTopLabelPosition_clearTagStateSet() throws Exception { + public void testCheckedWithTopLabelPosition_clearTagStateSet() throws Exception { TestAction testAction = (TestAction) action; testAction.setFoo("true"); @@ -159,7 +169,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithLeftLabelPosition() throws Exception { @@ -186,7 +196,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithLeftLabelPosition_clearTagStateSet() throws Exception { @@ -216,7 +226,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithError() throws Exception { @@ -245,7 +255,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithError_clearTagStateSet() throws Exception { @@ -277,7 +287,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithErrorStyle() throws Exception { @@ -306,7 +316,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testCheckedWithErrorStyle_clearTagStateSet() throws Exception { @@ -338,7 +348,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testUnchecked() throws Exception { @@ -362,7 +372,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testUnchecked_clearTagStateSet() throws Exception { @@ -389,7 +399,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testDisabled() throws Exception { @@ -414,7 +424,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testDisabled_clearTagStateSet() throws Exception { @@ -442,7 +452,7 @@ public class CheckboxTest extends AbstractUITagTest { freshTag.setPageContext(pageContext); assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " + "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.", - strutsBodyTagsAreReflectionEqual(tag, freshTag)); + strutsBodyTagsAreReflectionEqual(tag, freshTag)); } public void testSubmitUncheckedAsFalse() throws Exception { @@ -487,4 +497,29 @@ public class CheckboxTest extends AbstractUITagTest { verify(CheckboxTag.class.getResource("Checkbox-8.txt")); } + public void testSubmitUncheckedGlobalAsTrue() throws Exception { + initDispatcherWithConfigs("struts-default.xml, struts-checkbox-submit-unchecked.xml"); + String submitUnchecked = container.getInstance(String.class, StrutsConstants.STRUTS_UI_CHECKBOX_SUBMIT_UNCHECKED); + assertEquals("true", submitUnchecked); + + createMocks(); + + TestAction testAction = (TestAction) action; + testAction.setFoo("true"); + + CheckboxTag tag = new CheckboxTag(); + tag.setPageContext(pageContext); + tag.setLabel("mylabel"); + tag.setName("foo"); + tag.setFieldValue("baz"); + // tag.setSubmitUnchecked("true"); - value should be injected by container + tag.setTitle("mytitle"); + tag.setDisabled("true"); + + tag.doStartTag(); + tag.doEndTag(); + + verify(CheckboxTag.class.getResource("Checkbox-8.txt")); + } + } diff --git a/core/src/test/resources/struts-checkbox-submit-unchecked.xml b/core/src/test/resources/struts-checkbox-submit-unchecked.xml new file mode 100644 index 000000000..d94dde62f --- /dev/null +++ b/core/src/test/resources/struts-checkbox-submit-unchecked.xml @@ -0,0 +1,31 @@ + + + + + + + + + +