Merge pull request #636 from apache/WW-5260-global-submit-unchecked

[WW-5260] Introduces a constant to set submitUnchecked attribute of checkbox tag globally
This commit is contained in:
Lukasz Lenart
2022-11-13 09:42:40 +01:00
committed by GitHub
7 changed files with 128 additions and 41 deletions
@@ -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";
}
@@ -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;
/**
* <!-- START SNIPPET: javadoc -->
@@ -46,15 +47,19 @@ import com.opensymphony.xwork2.util.ValueStack;
*
* <!-- END SNIPPET: example -->
* </pre>
*
*/
@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);
}
@@ -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;
@@ -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>
<input type="hidden" id="__checkbox_${parameters.id}" name="__checkbox_${parameters.name}" value="${parameters.fieldValue}"<#rt/>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
</#if>
/>
</#if><#rt/>
/><#rt/>
</#if>
@@ -283,7 +283,7 @@
<td class="tag-attribute">false</td>
<td class="tag-attribute">false</td>
<td class="tag-attribute">Boolean</td>
<td class="tag-attribute">If set to true, unchecked elements will be submitted with the form.</td>
<td class="tag-attribute">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</td>
</tr>
<tr>
<td class="tag-attribute">tabindex</td>
@@ -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.<br> 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<String, PropertyHolder> 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"));
}
}
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* 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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.ui.checkbox.submitUnchecked" value="true"/>
<package name="default" extends="struts-default">
</package>
</struts>