Skip to content

Commit

Permalink
WW-5260 Introduces a constant to set submitUnchecked attribute of che…
Browse files Browse the repository at this point in the history
…ckbox tag globally
  • Loading branch information
lukaszlenart committed Nov 11, 2022
1 parent 1af3e31 commit 279cef1
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 36 deletions.
5 changes: 4 additions & 1 deletion core/src/main/java/org/apache/struts2/StrutsConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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";
}
37 changes: 24 additions & 13 deletions core/src/main/java/org/apache/struts2/components/Checkbox.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
Expand All @@ -46,16 +47,17 @@
*
* <!-- 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 String submitUncheckedGlobal;

protected String fieldValue;
protected String submitUnchecked;

Expand All @@ -77,27 +79,36 @@ protected void evaluateExtraParams() {
if (submitUnchecked != null) {
Object parsedValue = findValue(submitUnchecked, Boolean.class);
addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
} else if (submitUncheckedGlobal != null) {
addParameter("submitUnchecked", Boolean.parseBoolean(submitUncheckedGlobal));
} else {
addParameter("submitUnchecked", false);
}
}

protected Class getValueClassType() {
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);
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/resources/template/simple/checkbox.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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>
2 changes: 1 addition & 1 deletion core/src/site/resources/tags/checkbox-attributes.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

/**
Expand All @@ -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() {
Expand Down Expand Up @@ -73,7 +83,7 @@ public void testChecked() throws Exception {
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 {
Expand Down Expand Up @@ -102,7 +112,7 @@ public void testChecked_clearTagStateSet() throws Exception {
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 {
Expand All @@ -129,10 +139,10 @@ public void testCheckedWithTopLabelPosition() throws Exception {
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");

Expand All @@ -159,7 +169,7 @@ public void testCheckedWithTopLabelPosition_clearTagStateSet() throws Exception
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 {
Expand All @@ -186,7 +196,7 @@ public void testCheckedWithLeftLabelPosition() throws Exception {
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 {
Expand Down Expand Up @@ -216,7 +226,7 @@ public void testCheckedWithLeftLabelPosition_clearTagStateSet() throws Exception
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 {
Expand Down Expand Up @@ -245,7 +255,7 @@ public void testCheckedWithError() throws Exception {
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 {
Expand Down Expand Up @@ -277,7 +287,7 @@ public void testCheckedWithError_clearTagStateSet() throws Exception {
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 {
Expand Down Expand Up @@ -306,7 +316,7 @@ public void testCheckedWithErrorStyle() throws Exception {
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 {
Expand Down Expand Up @@ -338,7 +348,7 @@ public void testCheckedWithErrorStyle_clearTagStateSet() throws Exception {
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 {
Expand All @@ -362,7 +372,7 @@ public void testUnchecked() throws Exception {
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 {
Expand All @@ -389,7 +399,7 @@ public void testUnchecked_clearTagStateSet() throws Exception {
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 {
Expand All @@ -414,7 +424,7 @@ public void testDisabled() throws Exception {
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 {
Expand Down Expand Up @@ -442,7 +452,7 @@ public void testDisabled_clearTagStateSet() throws Exception {
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 {
Expand Down Expand Up @@ -487,4 +497,29 @@ public void testSubmitUncheckedAsTrue() throws Exception {
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"));
}

}
31 changes: 31 additions & 0 deletions core/src/test/resources/struts-checkbox-submit-unchecked.xml
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 279cef1

Please sign in to comment.