This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new e8c81e9f2 WW-5702 fix(core): close the scope gaps in HTML5 constraint 
derivation (#1934)
e8c81e9f2 is described below

commit e8c81e9f2de9344b25d9d7e7a230e11c7e37bc36
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Sep 14 06:56:40 2026 +0200

    WW-5702 fix(core): close the scope gaps in HTML5 constraint derivation 
(#1934)
    
    * WW-5702 fix(core): close the scope gaps in HTML5 constraint derivation
    
    Items 1, 3, 4, 7 and 13 of the review follow-up to WW-5695.
    
    - Gate derivation on the resolved theme's ancestry including html5, so
      xhtml/simple/css_xhtml forms no longer resolve validators and call
      getMessage() per field to build a map nothing renders. A custom theme
      with `parent = html5` keeps deriving (the walk reuses
      Template.getPossibleTemplates and the cached theme.properties).
    - Form.getFieldValidators unwraps FieldVisitorValidatorWrapper, so a
      textfield behind a visitor validator gets its concrete constraint and
      data-msg-<type> instead of data-msg-field-visitor and nothing else.
    - Visitor validators are resolved once per form, not once per field.
    - Checkbox and Hidden report their real HtmlControlType; inert with the
      default provider, which admits neither, but honest for replacements.
    
    Item 3 (action-less forms resolving under an empty context) turned out
    to be a false finding: Component.findValue(null, String.class) returns
    null because containsExpression(null) is false, and
    DefaultActionMapper.parseActionName returns null for a null name, so the
    attributes.actionName fallback is reachable. The new test proves alias-
    scoped validators are found for a form without an action attribute; no
    production change was needed.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    
    * WW-5702 refactor(core): address Sonar findings on the constraint 
scope-gap fix
    
    S1117: the local in themeRendersConstraints() hid the template field.
    S8924: Mockito's BDD `then(mock).should()` replaces the fully-qualified
    `Mockito.verify`, which was qualified only because AbstractUITagTest
    inherits verify(URL) and an inherited method shadows a static import.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../org/apache/struts2/components/Checkbox.java    |  5 ++
 .../java/org/apache/struts2/components/Form.java   | 10 ++-
 .../java/org/apache/struts2/components/Hidden.java |  5 ++
 .../java/org/apache/struts2/components/UIBean.java | 24 ++++++-
 .../struts2/components/ConstraintAction.java       | 20 ++++++
 .../components/ConstraintAttributesTest.java       | 40 +++++++++++-
 .../{ConstraintAction.java => ConstraintUser.java} | 36 ++---------
 .../apache/struts2/components/ControlTypeTest.java | 18 ++++--
 .../components/FormFieldValidatorsTest.java        | 75 ++++++++++++++++++++--
 ...nstraintAction-constraintAction-validation.xml} | 18 +-----
 .../components/ConstraintAction-validation.xml     |  5 ++
 ...alidation.xml => ConstraintUser-validation.xml} | 18 +-----
 .../resources/template/html5child/theme.properties | 19 ++++++
 13 files changed, 218 insertions(+), 75 deletions(-)

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 2720377dc..b6159bf4e 100644
--- a/core/src/main/java/org/apache/struts2/components/Checkbox.java
+++ b/core/src/main/java/org/apache/struts2/components/Checkbox.java
@@ -74,6 +74,11 @@ public class Checkbox extends UIBean {
         return TEMPLATE;
     }
 
+    @Override
+    protected HtmlControlType getControlType() {
+        return HtmlControlType.CHECKBOX;
+    }
+
     protected void evaluateExtraParams() {
         if (fieldValue != null) {
             addParameter(ATTR_FIELD_VALUE, findString(fieldValue));
diff --git a/core/src/main/java/org/apache/struts2/components/Form.java 
b/core/src/main/java/org/apache/struts2/components/Form.java
index cb7a4f0ab..d8daf841b 100644
--- a/core/src/main/java/org/apache/struts2/components/Form.java
+++ b/core/src/main/java/org/apache/struts2/components/Form.java
@@ -43,7 +43,9 @@ import 
org.apache.struts2.views.annotations.StrutsTagAttribute;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -127,6 +129,7 @@ public class Form extends ClosingUIBean {
     private List<Validator> cachedActionValidators;
     private String cachedActionName;
     private boolean actionValidatorsResolved;
+    private final Map<Class<?>, List<Validator>> cachedVisitorValidators = new 
HashMap<>();
 
     public Form(ValueStack stack, HttpServletRequest request, 
HttpServletResponse response) {
         super(stack, request, response);
@@ -337,6 +340,10 @@ public class Form extends ClosingUIBean {
         Class actionClass = (Class) getAttributes().get(ATTR_ACTION_CLASS);
         List<Validator> validators = new ArrayList<>();
         findFieldValidators(name, actionClass, cachedActionName, 
cachedActionValidators, validators, "");
+        // the wrapper only exists to prefix the field name for the deprecated 
JS validator; callers of
+        // this method dispatch on the concrete validator type
+        validators.replaceAll(validator -> validator instanceof 
FieldVisitorValidatorWrapper wrapper
+            ? wrapper.getFieldValidator() : validator);
         return validators;
     }
 
@@ -393,7 +400,8 @@ public class Form extends ClosingUIBean {
                         continue;
                     }
 
-                    List<Validator> visitorValidators = 
actionValidatorManager.getValidators(clazz, actionName);
+                    List<Validator> visitorValidators = 
cachedVisitorValidators.computeIfAbsent(clazz,
+                        visited -> 
actionValidatorManager.getValidators(visited, actionName));
                     String vPrefix = prefix + (vfValidator.isAppendPrefix() ? 
vfValidator.getFieldName() + "." : "");
                     findFieldValidators(name, clazz, actionName, 
visitorValidators, resultValidators, vPrefix);
                 } else if ((prefix + 
fieldValidator.getFieldName()).equals(name)) {
diff --git a/core/src/main/java/org/apache/struts2/components/Hidden.java 
b/core/src/main/java/org/apache/struts2/components/Hidden.java
index b8c16237e..46302c422 100644
--- a/core/src/main/java/org/apache/struts2/components/Hidden.java
+++ b/core/src/main/java/org/apache/struts2/components/Hidden.java
@@ -63,4 +63,9 @@ public class Hidden extends UIBean {
         return TEMPLATE;
     }
 
+    @Override
+    protected HtmlControlType getControlType() {
+        return HtmlControlType.HIDDEN;
+    }
+
 }
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 42d7cbbcd..af466d386 100644
--- a/core/src/main/java/org/apache/struts2/components/UIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/UIBean.java
@@ -450,6 +450,7 @@ public abstract class UIBean extends Component {
 
     static final String TEMPLATE_DIR = "templateDir";
     static final String THEME = "theme";
+    private static final String CONSTRAINT_THEME = "html5";
 
     protected static final String ATTR_FIELD_VALUE = "fieldValue";
     protected static final String ATTR_NAME_VALUE = "nameValue";
@@ -947,7 +948,7 @@ public abstract class UIBean extends Component {
             return;
         }
         String fieldName = (String) getAttributes().get("name");
-        if (fieldName == null) {
+        if (fieldName == null || !themeRendersConstraints()) {
             return;
         }
         int stackDepth = stack.getRoot().size();
@@ -969,6 +970,27 @@ public abstract class UIBean extends Component {
         }
     }
 
+    /**
+     * Only {@code html5/common-attributes.ftl} renders the derived map, so 
deriving it under any other
+     * theme is wasted work for every field. A custom theme inherits that 
template through
+     * {@code parent = html5} in its theme.properties, hence the walk up the 
ancestry rather than a
+     * name match. The gate is still by name: a theme that renders {@code 
attributes.constraints} itself
+     * without descending from html5 never receives the map.
+     */
+    private boolean themeRendersConstraints() {
+        Template resolved = buildTemplateName(template, getDefaultTemplate());
+        TemplateEngine engine = 
templateEngineManager.getTemplateEngine(resolved, templateSuffix);
+        if (engine == null) {
+            return false;
+        }
+        for (Template candidate : resolved.getPossibleTemplates(engine)) {
+            if (CONSTRAINT_THEME.equals(candidate.getTheme())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * The object server-side validation runs against, which is what the 
derived {@code data-msg-*}
      * messages must be resolved against too: {@code 
ValidatorSupport.getMessage} builds a
diff --git 
a/core/src/test/java/org/apache/struts2/components/ConstraintAction.java 
b/core/src/test/java/org/apache/struts2/components/ConstraintAction.java
index 2a1f9b4f3..606d163da 100644
--- a/core/src/test/java/org/apache/struts2/components/ConstraintAction.java
+++ b/core/src/test/java/org/apache/struts2/components/ConstraintAction.java
@@ -26,6 +26,8 @@ public class ConstraintAction extends ActionSupport {
     private String username;
     private String comment;
     private String bio;
+    private String nickname;
+    private ConstraintUser user;
 
     public String getUsername() {
         return username;
@@ -53,4 +55,22 @@ public class ConstraintAction extends ActionSupport {
     public void setBio(String bio) {
         this.bio = bio;
     }
+
+    public String getNickname() {
+        return nickname;
+    }
+
+    @StrutsParameter
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    @StrutsParameter(depth = 1)
+    public ConstraintUser getUser() {
+        return user;
+    }
+
+    public void setUser(ConstraintUser user) {
+        this.user = user;
+    }
 }
diff --git 
a/core/src/test/java/org/apache/struts2/components/ConstraintAttributesTest.java
 
b/core/src/test/java/org/apache/struts2/components/ConstraintAttributesTest.java
index 7a76f1d7b..6d70cd20e 100644
--- 
a/core/src/test/java/org/apache/struts2/components/ConstraintAttributesTest.java
+++ 
b/core/src/test/java/org/apache/struts2/components/ConstraintAttributesTest.java
@@ -34,6 +34,8 @@ import java.util.Map;
 public class ConstraintAttributesTest extends AbstractUITagTest {
 
     private FormTag form;
+    private String theme = "html5";
+    private String fieldName = "username";
 
     public void testNoConstraintsWhenTheConstantIsOff() throws Exception {
         initDispatcherWith("false");
@@ -49,6 +51,41 @@ public class ConstraintAttributesTest extends 
AbstractUITagTest {
         assertEquals("3", constraints.get("minlength"));
     }
 
+    /**
+     * Only the html5 theme renders the derived map, so under any other theme 
the derivation is
+     * wasted work per field — validator resolution plus a {@code 
getMessage()} call each.
+     */
+    public void testNoConstraintsUnderAThemeThatDoesNotRenderThem() throws 
Exception {
+        initDispatcherWith("true");
+        theme = "xhtml";
+
+        assertNull(renderFieldAndReturnConstraints(null));
+    }
+
+    /**
+     * A custom theme declares {@code parent = html5} in its theme.properties 
and inherits the
+     * templates that render the map, so the gate has to walk the ancestry, 
not match the name.
+     */
+    public void testConstraintsUnderAThemeInheritingFromHtml5() throws 
Exception {
+        initDispatcherWith("true");
+        theme = "html5child";
+
+        Map<String, String> constraints = 
renderFieldAndReturnConstraints(null);
+        assertNotNull("expected constraints under a child of html5", 
constraints);
+        assertEquals("3", constraints.get("minlength"));
+    }
+
+    public void testVisitorValidatedNestedFieldGetsTheConcreteConstraint() 
throws Exception {
+        initDispatcherWith("true");
+        fieldName = "user.name";
+
+        Map<String, String> constraints = 
renderFieldAndReturnConstraints(null);
+        assertNotNull("expected constraints for a visitor-validated field", 
constraints);
+        assertEquals("required", constraints.get("required"));
+        assertEquals("name is required", 
constraints.get("data-msg-requiredstring"));
+        assertFalse(constraints.containsKey("data-msg-field-visitor"));
+    }
+
     /**
      * Pins the hook to running after {@code evaluateExtraParams()}. A {@code 
stringlength} validator on
      * a control the browser treats as numeric must not emit {@code minlength} 
at all — that attribute
@@ -139,11 +176,12 @@ public class ConstraintAttributesTest extends 
AbstractUITagTest {
         form.setPageContext(pageContext);
         form.setAction("constraintAction");
         form.setNamespace("");
+        form.setTheme(theme);
         form.doStartTag();
 
         TextFieldTag field = new TextFieldTag();
         field.setPageContext(pageContext);
-        field.setName("username");
+        field.setName(fieldName);
         if (type != null) {
             field.setType(type);
         }
diff --git 
a/core/src/test/java/org/apache/struts2/components/ConstraintAction.java 
b/core/src/test/java/org/apache/struts2/components/ConstraintUser.java
similarity index 54%
copy from core/src/test/java/org/apache/struts2/components/ConstraintAction.java
copy to core/src/test/java/org/apache/struts2/components/ConstraintUser.java
index 2a1f9b4f3..0acfc461b 100644
--- a/core/src/test/java/org/apache/struts2/components/ConstraintAction.java
+++ b/core/src/test/java/org/apache/struts2/components/ConstraintUser.java
@@ -18,39 +18,15 @@
  */
 package org.apache.struts2.components;
 
-import org.apache.struts2.ActionSupport;
-import org.apache.struts2.interceptor.parameter.StrutsParameter;
+public class ConstraintUser {
 
-public class ConstraintAction extends ActionSupport {
+    private String name;
 
-    private String username;
-    private String comment;
-    private String bio;
-
-    public String getUsername() {
-        return username;
-    }
-
-    @StrutsParameter
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    public String getComment() {
-        return comment;
-    }
-
-    @StrutsParameter
-    public void setComment(String comment) {
-        this.comment = comment;
-    }
-
-    public String getBio() {
-        return bio;
+    public String getName() {
+        return name;
     }
 
-    @StrutsParameter
-    public void setBio(String bio) {
-        this.bio = bio;
+    public void setName(String name) {
+        this.name = name;
     }
 }
diff --git 
a/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java 
b/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java
index c43cfe6b0..9c819b059 100644
--- a/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java
+++ b/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java
@@ -64,11 +64,17 @@ public class ControlTypeTest extends AbstractUITagTest {
         assertEquals(HtmlControlType.FILE, file.getControlType());
     }
 
-    public void testControlsWithoutAnOverrideAreUnknown() {
-        // CheckboxInterceptor substitutes "false" for an unticked box, so the 
server accepts what
-        // a browser "required" would block — that is a real false reject, and 
the reason Checkbox
-        // and Hidden deliberately have no getControlType() override.
-        assertEquals(HtmlControlType.OTHER, new Checkbox(stack, request, 
response).getControlType());
-        assertEquals(HtmlControlType.OTHER, new Hidden(stack, request, 
response).getControlType());
+    /**
+     * Neither type supports a constraint and the default provider never emits 
{@code required} for
+     * them (CheckboxInterceptor substitutes "false" for an unticked box, so 
the server accepts what
+     * the browser would block). The honest type is for replacement providers, 
which otherwise cannot
+     * tell a checkbox or hidden input from an unknown control.
+     */
+    public void testCheckboxIsCheckbox() {
+        assertEquals(HtmlControlType.CHECKBOX, new Checkbox(stack, request, 
response).getControlType());
+    }
+
+    public void testHiddenIsHidden() {
+        assertEquals(HtmlControlType.HIDDEN, new Hidden(stack, request, 
response).getControlType());
     }
 }
diff --git 
a/core/src/test/java/org/apache/struts2/components/FormFieldValidatorsTest.java 
b/core/src/test/java/org/apache/struts2/components/FormFieldValidatorsTest.java
index cdc2fbfc2..0b09f608e 100644
--- 
a/core/src/test/java/org/apache/struts2/components/FormFieldValidatorsTest.java
+++ 
b/core/src/test/java/org/apache/struts2/components/FormFieldValidatorsTest.java
@@ -22,6 +22,7 @@ import org.apache.struts2.TestConfigurationProvider;
 import org.apache.struts2.mock.MockActionProxy;
 import org.apache.struts2.validator.ActionValidatorManager;
 import org.apache.struts2.validator.Validator;
+import org.apache.struts2.validator.validators.RequiredStringValidator;
 import org.apache.struts2.views.jsp.AbstractUITagTest;
 import org.apache.struts2.views.jsp.ui.FormTag;
 
@@ -31,8 +32,11 @@ import java.util.List;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.BDDMockito.then;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
@@ -64,16 +68,79 @@ public class FormFieldValidatorsTest extends 
AbstractUITagTest {
         form.getFieldValidators("myUpDownSelectTag");
         form.getFieldValidators("someOtherField");
 
-        // fully qualified: AbstractUITagTest inherits verify(URL), which 
would shadow a static import
-        org.mockito.Mockito.verify(manager, times(1))
-            .getValidators(any(Class.class), anyString(), 
nullable(String.class));
+        then(manager).should(times(1)).getValidators(any(Class.class), 
anyString(), nullable(String.class));
+    }
+
+    /**
+     * The common {@code <s:form>} carries no {@code action} attribute; {@code 
ServletUrlRenderer}
+     * then resolves the name from the current invocation into {@code 
attributes.actionName} only.
+     * Validators scoped to that alias ({@code 
ConstraintAction-constraintAction-validation.xml})
+     * must still be found, not silently skipped under an empty context.
+     */
+    public void 
testFindsAliasScopedValidatorsForAFormWithoutAnActionAttribute() throws 
Exception {
+        currentActionIs("constraintAction");
+        FormTag tag = new FormTag();
+        tag.setPageContext(pageContext);
+        tag.doStartTag();
+        Form form = (Form) tag.getComponent();
+
+        List<Validator> validators = form.getFieldValidators("nickname");
+
+        assertEquals(1, validators.size());
+        assertEquals("requiredstring", validators.get(0).getValidatorType());
+    }
+
+    /**
+     * A {@code visitor} on {@code user} reaches {@code user.name} through 
{@code
+     * FieldVisitorValidatorWrapper}, which exists to prefix the field name 
for the deprecated JS
+     * validator and implements only {@code FieldValidator}. The constraint 
provider dispatches on the
+     * concrete validator type, so the wrapper has to be unwrapped or the 
field gets a
+     * {@code data-msg-field-visitor} and never a constraint.
+     */
+    public void testUnwrapsVisitorValidatedFieldValidators() throws Exception {
+        currentActionIs("constraintAction");
+        Form form = formFor("constraintAction");
+
+        List<Validator> validators = form.getFieldValidators("user.name");
+
+        assertEquals(1, validators.size());
+        assertTrue("expected the concrete validator, got " + 
validators.get(0).getClass(),
+            validators.get(0) instanceof RequiredStringValidator);
+    }
+
+    /**
+     * The manager caches only validator configs and builds fresh instances on 
every call, so the
+     * visitor branch must be resolved once per form like the top-level list, 
not once per field.
+     */
+    public void testResolvesAVisitorsValidatorsOnlyOnceAcrossFields() throws 
Exception {
+        currentActionIs("constraintAction");
+        Form form = formFor("constraintAction");
+        ActionValidatorManager manager = 
spy(container.getInstance(ActionValidatorManager.class));
+        form.setActionValidatorManager(manager);
+
+        form.getFieldValidators("user.name");
+        form.getFieldValidators("username");
+        form.getFieldValidators("bio");
+
+        then(manager).should(times(1)).getValidators(eq(ConstraintUser.class), 
anyString());
+    }
+
+    private void currentActionIs(String actionName) {
+        MockActionProxy proxy = (MockActionProxy) actionProxy;
+        proxy.setActionName(actionName);
+        proxy.setNamespace("");
+        
proxy.setConfig(configuration.getRuntimeConfiguration().getActionConfig("", 
actionName));
     }
 
     private Form formForDoubleValidationAction() throws Exception {
+        return formFor("doubleValidationAction");
+    }
+
+    private Form formFor(String actionName) throws Exception {
         FormTag tag = new FormTag();
         tag.setPageContext(pageContext);
         tag.setName("myForm");
-        tag.setAction("doubleValidationAction");
+        tag.setAction(actionName);
         tag.setNamespace("");
         tag.doStartTag();
         return (Form) tag.getComponent();
diff --git 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
 
b/core/src/test/resources/org/apache/struts2/components/ConstraintAction-constraintAction-validation.xml
similarity index 62%
copy from 
core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
copy to 
core/src/test/resources/org/apache/struts2/components/ConstraintAction-constraintAction-validation.xml
index 30bf5dd43..c6d7c5870 100644
--- 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
+++ 
b/core/src/test/resources/org/apache/struts2/components/ConstraintAction-constraintAction-validation.xml
@@ -21,23 +21,9 @@
 -->
 <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" 
"https://struts.apache.org/dtds/xwork-validator-1.0.dtd";>
 <validators>
-    <field name="username">
-        <field-validator type="stringlength">
-            <param name="trim">false</param>
-            <param name="minLength">3</param>
-            <message>username must be at least ${minLength} 
characters</message>
-        </field-validator>
-    </field>
-    <field name="comment">
+    <field name="nickname">
         <field-validator type="requiredstring">
-            <message>Contains "quotes" and &lt;brackets&gt;</message>
-        </field-validator>
-    </field>
-    <field name="bio">
-        <field-validator type="stringlength">
-            <param name="trim">false</param>
-            <param name="maxLength">10</param>
-            <message>bio must be at most ${maxLength} characters</message>
+            <message>nickname is required</message>
         </field-validator>
     </field>
 </validators>
diff --git 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
 
b/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
index 30bf5dd43..cadbb050a 100644
--- 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
+++ 
b/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
@@ -40,4 +40,9 @@
             <message>bio must be at most ${maxLength} characters</message>
         </field-validator>
     </field>
+    <field name="user">
+        <field-validator type="visitor">
+            <message/>
+        </field-validator>
+    </field>
 </validators>
diff --git 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
 
b/core/src/test/resources/org/apache/struts2/components/ConstraintUser-validation.xml
similarity index 62%
copy from 
core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
copy to 
core/src/test/resources/org/apache/struts2/components/ConstraintUser-validation.xml
index 30bf5dd43..b9611d705 100644
--- 
a/core/src/test/resources/org/apache/struts2/components/ConstraintAction-validation.xml
+++ 
b/core/src/test/resources/org/apache/struts2/components/ConstraintUser-validation.xml
@@ -21,23 +21,9 @@
 -->
 <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" 
"https://struts.apache.org/dtds/xwork-validator-1.0.dtd";>
 <validators>
-    <field name="username">
-        <field-validator type="stringlength">
-            <param name="trim">false</param>
-            <param name="minLength">3</param>
-            <message>username must be at least ${minLength} 
characters</message>
-        </field-validator>
-    </field>
-    <field name="comment">
+    <field name="name">
         <field-validator type="requiredstring">
-            <message>Contains "quotes" and &lt;brackets&gt;</message>
-        </field-validator>
-    </field>
-    <field name="bio">
-        <field-validator type="stringlength">
-            <param name="trim">false</param>
-            <param name="maxLength">10</param>
-            <message>bio must be at most ${maxLength} characters</message>
+            <message>name is required</message>
         </field-validator>
     </field>
 </validators>
diff --git a/core/src/test/resources/template/html5child/theme.properties 
b/core/src/test/resources/template/html5child/theme.properties
new file mode 100644
index 000000000..e79317a26
--- /dev/null
+++ b/core/src/test/resources/template/html5child/theme.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+parent = html5

Reply via email to