From 0bda63ed416813bdfc45cc0df7f44e1c26fff963 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 25 Aug 2026 00:13:48 +0200 Subject: [PATCH 1/5] docs: document html5 constraint validation, deprecate the JS validator Rewrites client-side-validation.md around the html5 theme's new HTML5 constraint-validation attributes (struts.ui.html5.constraints), including the never-false-reject rule, the full validator-to-attribute mapping, the trim="false"/caseSensitive="true" conditions on stringlength/regex, the never-change-the-type rule, data-msg-* hooks, the requiredLabel/required distinction, and the swappable HtmlConstraintProvider extension point. Deprecates the older generated-JavaScript client-side validator (xhtml/ css_xhtml themes, ) per WW-5694, removed in 8.0.0 per WW-5696: deletes pure-java-script-client-side-validation.md, repoints its inbound links (xhtml-theme.md, css-xhtml-theme.md, ajax-client-side-validation.md) to the deprecated section of the rewritten page, adds deprecation banners to client-validation-example.md and form-tag.md's validate attribute. Drops the deleted page's claim that client-side messages are not internationalized: ValidatorSupport.getMessage resolves through DelegatingValidatorContext and textProviderFactory, so they always were. Co-Authored-By: Claude Opus 5 --- .../ajax-client-side-validation.md | 4 +- .../core-developers/client-side-validation.md | 171 ++++++++++++++---- .../client-validation-example.md | 6 + ...pure-java-script-client-side-validation.md | 50 ----- source/tag-developers/css-xhtml-theme.md | 21 ++- source/tag-developers/form-tag.md | 7 + source/tag-developers/xhtml-theme.md | 11 +- 7 files changed, 168 insertions(+), 102 deletions(-) delete mode 100644 source/core-developers/pure-java-script-client-side-validation.md diff --git a/source/core-developers/ajax-client-side-validation.md b/source/core-developers/ajax-client-side-validation.md index b9e47ed69f..45012487ed 100644 --- a/source/core-developers/ajax-client-side-validation.md +++ b/source/core-developers/ajax-client-side-validation.md @@ -14,8 +14,8 @@ parent: ## Description -AJAX-based client side validation improves upon [Pure JavaScript Client Side Validation](pure-java-script-client-side-validation) -by using a combination of JavaScript, DOM manipulation, and remote server communication. Unlike the pure client side +AJAX-based client side validation improves upon [Pure JavaScript Client Side Validation](client-side-validation#pure-javascript-client-side-validation-deprecated) +(deprecated since Struts 7.4.0, removed in 8.0.0) by using a combination of JavaScript, DOM manipulation, and remote server communication. Unlike the pure client side implementation, AJAX-based validation communicates with the server. This means all your validation rules that worked when submitting a form will still work within the browser. diff --git a/source/core-developers/client-side-validation.md b/source/core-developers/client-side-validation.md index 20a8dabade..e281ba7eab 100644 --- a/source/core-developers/client-side-validation.md +++ b/source/core-developers/client-side-validation.md @@ -14,57 +14,156 @@ parent: ## Basics -The framework adds support for client-side validation on top of the standard validation framework. +Since Struts 7.4.0, the [html5 theme](../tag-developers/html5-theme) can derive HTML5 constraint-validation +attributes (`required`, `minlength`, `maxlength`, `pattern`, `min`, `max`) directly from a field's +server-side [validators](validation), so the browser rejects obviously-invalid input before the form is +even submitted. This replaces the older, generated-JavaScript validator used by the _xhtml theme_ and +_css_xhtml theme_, which is now deprecated — see [Pure JavaScript Client Side Validation +(deprecated)](#pure-javascript-client-side-validation-deprecated) below. -Client-side validation can be enabled on a per-form basis by specifying `validate="true"` in the _form_ tag. +There is also [AJAX Client Side Validation](ajax-client-side-validation), which runs the full server-side +validation stack (including visitor validators and `validate()`) over AJAX and is unaffected by any of this. -```jsp - - ... - -``` +## HTML5 Constraint Validation -If a `name` for the form is not given, the action mapping name will be used as the form name. Otherwise, a correct -`action` and `namespace` attributes must be provided to the `` tag. +### Enabling it -## Referencing "submitProfile" in the "/user" namespace +HTML5 constraint validation is off by default. Turn it on with the `struts.ui.html5.constraints` constant: -```jsp - - ... - +```properties +struts.ui.html5.constraints=true +``` + +It only has an effect on fields rendered with the `html5` theme (see [Using the HTML5 +theme](../tag-developers/html5-theme#using-the-html5-theme)). There is no per-form opt-in attribute — +unlike the deprecated JavaScript validator, this feature does not use ``. Once the +constant is on and a field's theme is `html5`, its validators are consulted automatically. + +> The constant defaults to `false` in 7.4.0 so existing `html5`-theme forms keep rendering unchanged. The +> default is expected to flip to `true` in Struts 8.0.0. + +### The governing rule: never false-reject + +The mapping is deliberately conservative. A constraint is emitted only when the browser cannot reject +input the server would have accepted. If the browser rejected something the server allows, the user would +be stuck with a form that will not submit and no explanation why. Being conservative simply costs a field +its client-side check — that's a harmless, quiet failure mode, so the mapping always chooses it over the +alternative. + +The clearest consequence of this rule: **Struts never sets or changes an input's `type`.** A field stays +whatever `type` the developer gave it. In particular: + +- Switching a field to `type="number"` would reject a value like `1234,50`, which the framework's + locale-aware numeric conversion happily accepts in a comma-decimal locale. +- The browsers' `email` and `url` input grammars don't match `EmailValidator` and `UrlValidator`. + +So `min`/`max` range constraints are only ever added to a control the developer *already* made numeric +(`type="number"` or `type="range"`) — Struts will never promote a plain text field into one just because +an `int` or `double` validator is attached to it. + +### Mapping table + +| Validator | Emits | Condition | +|---|---|---| +| `requiredstring` | `required` | on text-entry controls (`text`, `search`, `tel`, `password`, `email`, `url`) and `textarea` | +| `required` | `required` | only on `radio` and `file` | +| `stringlength` | `minlength` / `maxlength` | on text-entry or `textarea`, and only if the validator has `trim="false"`; each attribute is added only if actually configured | +| `regex` | `pattern` | on text-entry controls only, and only if `caseSensitive="true"`, `trim="false"`, and the regex is ECMAScript-safe (see below) | +| `int`, `short`, `long` | `min` / `max` | only when the control is already `type="number"` or `type="range"` | +| `double` | `min` / `max` | same as above; only inclusive bounds are emitted — exclusive bounds have no HTML equivalent and are omitted | +| `date` | — | nothing yet; temporal `min`/`max` is deferred to a future release | +| `email`, `url`, `creditcard` | — | never emitted | +| `fieldexpression`, `expression`, `conversion`, visitor validators | — | never emitted | +| any validator carrying a message | `data-msg-` | always added, including for validators that emit no constraint attribute at all | + +Two of these conditions are easy to miss and sharply limit how often `required` and `pattern` actually show up: + +**`required` is split across two validators, and they don't behave alike.** `requiredstring` fails on +null, empty, and (by default) blank values, so it is strictly stricter than the browser's `required` — safe +to emit on any text-entry control. Plain `required`, however, only fails on a null value, an empty array, +or an empty collection. That means an empty text input (which submits `""`, not nothing), a `select` with +an empty-valued option, and an **unticked checkbox** (`CheckboxInterceptor` substitutes the parameter +`"false"` for it) all pass server-side validation while a browser `required` attribute would block them. +Only `radio` and `file` controls omit their parameter entirely when left empty, so those are the only two +control types where plain `required` agrees with the server — which is why the table above emits `required` +for the `required` validator on those two types alone. + +**`pattern` needs `trim="false"`, which is not the default.** `RegexFieldValidator.trim` defaults to +`true`, so the server matches the field's *trimmed* value while an HTML `pattern` attribute matches the +*raw* one. A regex like `[a-z]+` would accept `"abc "` server-side while the browser blocks it. Because of +this, `pattern` is only ever emitted for validators explicitly configured with `trim="false"` — which most +existing `regex` validators are not. In practice, expect `pattern` to show up rarely until applications +start setting `trim="false"` deliberately for fields where it's safe. + +**ECMAScript-safe** means the regex uses only constructs that mean the same thing in Java's regex engine +and in the browser's: literals, `\d`/`\w` and their negations, character classes without POSIX or Unicode +property syntax, grouping, alternation, anchors, and bounded quantifiers. Notably, **`\s` and `\S` are +excluded** — Java's `\s` is ASCII-only by default while ECMAScript's `\s` covers the wider Unicode +whitespace set, so a pattern like `^\S+$` would accept a value containing a non-breaking space server-side +and reject it in the browser. Any regex using a construct outside this allowlist simply gets no `pattern` +attribute at all — it is never rejected loudly, it just quietly doesn't get a client-side check. + +### `data-msg-*` attributes + +Every validator carrying a message — even one that emits no HTML constraint attribute at all — adds a +`data-msg-` attribute (for example `data-msg-email`, `data-msg-regex`) holding the +validator's fully resolved, internationalized message. **Struts ships no JavaScript that reads these.** +They exist purely as a hook: an application can write its own script to read `data-msg-*` and show +whichever messages it wants, in whatever way it wants, including for validators (like `email` or +`creditcard`) that never get a native browser check. + +### `requiredLabel` is unrelated to the `required` attribute + +This is a common point of confusion: the `requiredLabel` tag attribute only controls whether a visual +marker (usually `*`) is drawn next to a field's label. It has no connection to the HTML `required` +attribute described above, and setting `requiredLabel="true"` does not make a field required in the +browser — the two are decided completely independently. + +### Extension point: `HtmlConstraintProvider` + +The mapping above is implemented by `StrutsHtmlConstraintProvider`, the default implementation of the +`HtmlConstraintProvider` interface, registered under the `struts.htmlConstraintProvider` constant: + +```properties +struts.htmlConstraintProvider=struts ``` -Technically, the form's action attribute can refer to a "path" that includes the namespace and action as a URI. -But, client-side validation **requires** that the action name and namespeact to be set separately. +An application that wants a less conservative mapping — for example, treating an `email` validator as +`type="email"`, or emitting `pattern` for case-insensitive regexes by rewriting them — can register its own +`HtmlConstraintProvider` implementation under this constant instead of the default. This is the escape +hatch for every limitation described above: the framework's own mapping stays deliberately conservative, +but nothing stops an application from replacing it with one that fits its own validators and locales. + +## Pure JavaScript Client Side Validation (deprecated) + +> **Deprecated since Struts 7.4.0 ([WW-5694](https://issues.apache.org/jira/browse/WW-5694)), removed in +> Struts 8.0.0 ([WW-5696](https://issues.apache.org/jira/browse/WW-5696)).** New applications should use +> the [html5 theme's constraint validation](#html5-constraint-validation) described above instead. -## Won't work with client-side validation! +The `` attribute enables an older client-side validation mechanism, used by the +_xhtml theme_ and _css_xhtml theme_. It uses 100% client-side JavaScript, generated from the same +validation configuration used server-side, to try to reject bad input before the form is submitted: ```jsp - + ... - ``` -All the usual [validation configuration](validation) steps apply to client-side validation. Client-side validation -uses the same validation rules as server-side validation. If server-side validation doesn't work, then client-side -validation won't work either. +If a `name` for the form is not given, the action mapping name is used as the form name. Otherwise, a +correct `action` and `namespace` attribute must be provided to the `` tag — client-side validation +requires the action name and namespace to be resolvable separately, so a form whose `action` is given as a +full URI (for example ``) will not get +client-side validation, even though the form still works. -## The left hand doesn't know ... - -> The required attribute on many _Struts Tags_ is not integrated with client-side validation! The tag attribute is used -> by certain themes (like xhtml) to put a visual marker (usually '*') next to the field. The tag doesn't know -> if the validation system actually "requires" the field or not. +Because the validation logic is repeated in generated JavaScript, only a subset of validators is +supported (`required`, `requiredstring`, `stringlength`, `regex`, `email`, `url`, `int`, `double`), it is +not available for visitor validators at all, and — being a separate implementation of each validator's +logic — some values the JavaScript accepts may still be rejected server-side, or vice versa. This is one of +the reasons it is being replaced: the html5 theme's constraint validation above is derived directly from +the real validators, rather than reimplementing them in JavaScript. ## Example -See [Client Validation example](client-validation-example) for a complete example of client-side validation. - -## Client Side Validation Types - -There are two styles of client side validation. - -|[Pure JavaScript Client Side Validation](pure-java-script-client-side-validation)|Used by the _xhtml theme_ and _css_xhtml theme_| -|-----------------------------------------------------|--------------------------------------------------| -|[AJAX Client Side Validation](ajax-client-side-validation)|Use to used by the _ajax theme_ | +See [Client Validation example](client-validation-example) for a complete, though now-deprecated, example +of the JavaScript-based client-side validation described above. diff --git a/source/core-developers/client-validation-example.md b/source/core-developers/client-validation-example.md index 751e081215..3e3d5573a1 100644 --- a/source/core-developers/client-validation-example.md +++ b/source/core-developers/client-validation-example.md @@ -8,6 +8,12 @@ parent: # Client Validation Example +> **Deprecated since Struts 7.4.0 ([WW-5694](https://issues.apache.org/jira/browse/WW-5694)), removed in +> Struts 8.0.0 ([WW-5696](https://issues.apache.org/jira/browse/WW-5696)).** This example walks through the +> generated-JavaScript client-side validator (``) used by the _xhtml theme_ and +> _css_xhtml theme_. New applications should use the [html5 theme's constraint +> validation](client-side-validation#html5-constraint-validation) instead. + Let's create a Client-Side validation workflow, step by step. The `validate` attribute is set to `true`. > Note: Some themes do not support client-side validation. diff --git a/source/core-developers/pure-java-script-client-side-validation.md b/source/core-developers/pure-java-script-client-side-validation.md deleted file mode 100644 index 43fde3e842..0000000000 --- a/source/core-developers/pure-java-script-client-side-validation.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -layout: default -title: Pure JavaScript Client Side Validation -parent: - title: Client Side Validation - url: client-side-validation ---- - -# Pure JavaScript Client Side Validation -{:.no_toc} - -* Will be replaced with the ToC, excluding a header -{:toc} - -## Description - -Pure JavaScript client side validation is the simplest but least feature-rich type of [Client Side Validation](client-side-validation). -This type of validation uses 100% client-side JavaScript code to try to validate the values entered by the user. -Because the validation logic is actually repeated in the JavaScript code, it is important to understand that -some values will be considered acceptable by the JavaScript code but will be marked as unacceptable by the server-side [Validation](validation). - -Only the following validators are supported: - -* required validator -* requiredstring validator -* stringlength validator -* regex validator -* email validator -* url validator -* int validator -* double validator - -> JavaScript client validation is not available for visitor validations. - -## Error reporting - -Because client side validation does not talk to the server, the theme (_xhtml theme_ or _css_xhtml theme_ ) is responsible -for properly manipulating the HTML DOM to display the error message inline. The JavaScript that is responsible for doing -this logic is `validation.js` and can be found in each theme. - -> Errors are reported using the default validation message, not the internationalized version that the server-side might -> be aware of. This is a known issue. You may want to try the [AJAX Client Side Validation](ajax-client-side-validation) -> for messages that are fully internationalized. - -## Additional Validator Support - -If you wish to add additional validator support beyond those listed, you may override the _xhtml theme_ template -`form-close-validate.ftl`. This file contains the JavaScript that tries to validate each user-entered value from within -the browser. The _css_xhtml theme_ extends the _xhtml theme_ and therefore doesn't have its own `form-close-validate.ftl` -template. diff --git a/source/tag-developers/css-xhtml-theme.md b/source/tag-developers/css-xhtml-theme.md index a6d3e73e08..44176e72ea 100644 --- a/source/tag-developers/css-xhtml-theme.md +++ b/source/tag-developers/css-xhtml-theme.md @@ -14,8 +14,9 @@ The _css_xhtml theme_ provides all the basics that the [simple theme](simple-the [textfield](textfield-tag), [select](select-tag), etc) - Labels for each of the HTML [Struts Tags](struts-tags), placed according to the CSS stylesheet - [Validation](../core-developers/validation) and error reporting -- [Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation) using 100% - JavaScript on the browser +- [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) + using 100% JavaScript on the browser — **deprecated since Struts 7.4.0, removed in 8.0.0**; use the + [html5 theme](html5-theme) instead ## Wrapping the Simple Theme @@ -69,15 +70,15 @@ The head includes a style sheet. The contents of **styles.css** are: ### Form template The css_xhtml [form](form-tag) template is almost exactly like the _xhtml form template_ , including support for -[Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation). The difference -is that instead of printing out an opening and closing `` element, there are no elements. Instead, the CSS rules -for the individual HTML tags are assumed to handle all display logic. However, as noted, client-side validation is still -supported. +[Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) +(deprecated since Struts 7.4.0, removed in 8.0.0). The difference is that instead of printing out an opening and closing +`
` element, there are no elements. Instead, the CSS rules for the individual HTML tags are assumed to handle all +display logic. However, as noted, client-side validation is still supported. ### css_xhtml form template The css_xhtml [form](form-tag) template is almost exactly like the _xhtml form template_ , including support for -[Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation). The only -difference is that instead of printing out an opening and closing `
` element, there are no elements. Instead, -the CSS rules for the individual HTML tags are assumed to handle all display logic. However, as noted, client side -validation is still supported. +[Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) +(deprecated since Struts 7.4.0, removed in 8.0.0). The only difference is that instead of printing out an opening and +closing `
` element, there are no elements. Instead, the CSS rules for the individual HTML tags are assumed to +handle all display logic. However, as noted, client side validation is still supported. diff --git a/source/tag-developers/form-tag.md b/source/tag-developers/form-tag.md index 52684722e8..c516e37302 100644 --- a/source/tag-developers/form-tag.md +++ b/source/tag-developers/form-tag.md @@ -41,3 +41,10 @@ There are two flavours [Client Side Validation](../core-developers/client-side-v are using (xhtml, ajax, etc). If you are using the [xhtml theme](xhtml-theme) or [css_xhtml theme](css-xhtml-theme), pure client side validation will be used. Read the [Client Side Validation](../core-developers/client-side-validation) docs for more information. + +> **`validate` is deprecated since Struts 7.4.0 and will be removed in 8.0.0** +> ([WW-5694](https://issues.apache.org/jira/browse/WW-5694), +> [WW-5696](https://issues.apache.org/jira/browse/WW-5696)). It only controls the older, generated-JavaScript +> client-side validator used by the xhtml and css_xhtml themes. It has no effect on the [html5 theme's constraint +> validation](../core-developers/client-side-validation#html5-constraint-validation), which applications should +> use instead. diff --git a/source/tag-developers/xhtml-theme.md b/source/tag-developers/xhtml-theme.md index feb0214897..abed1e85f2 100644 --- a/source/tag-developers/xhtml-theme.md +++ b/source/tag-developers/xhtml-theme.md @@ -19,8 +19,9 @@ The xhtml provides all the basics that the [simple theme](simple-theme) provides - Labels for each of the HTML [Struts Tags](struts-tags) on the left hand side (or top, depending on the `labelposition` attribute) - [Validation](../core-developers/validation) and error reporting -- [Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation) using - 100% JavaScript on the browser +- [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) + using 100% JavaScript on the browser — **deprecated since Struts 7.4.0, removed in 8.0.0**; use the + [html5 theme](html5-theme) instead ## Wrapping the Simple Theme @@ -94,7 +95,8 @@ The head template imports a style sheet. The contents of **styles.css** are: The xhtml form template sets up the wrapping table around all the other form elements. In addition to creating this wrapping table, the opening and closing templates also, if the `validate` parameter is set to true, enable -[Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation). +[Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) +(deprecated since Struts 7.4.0, removed in 8.0.0). {% highlight freemarker %} {% remote_file_content https://raw.githubusercontent.com/apache/struts/main/core/src/main/resources/template/xhtml/form.ftl %} @@ -110,7 +112,8 @@ The closing template, `form-close.ftl`: The xhtml form template sets up the wrapping table around all the other [xhtml theme](xhtml-theme) form elements. In addition to creating this wrapping table, the opening and closing templates also, if the `validate` parameter is set -to `true`, enable [Pure JavaScript Client Side Validation](../core-developers/pure-java-script-client-side-validation.htmk). +to `true`, enable [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) +(deprecated since Struts 7.4.0, removed in 8.0.0). See the **form.ftl** contents: From 2e9203d342c9bf92649dec93292438207134b0f3 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 25 Aug 2026 00:17:20 +0200 Subject: [PATCH 2/5] docs: stop asserting a specific version for the html5.constraints default flip The shipped javadoc and default.properties deliberately say the struts.ui.html5.constraints default is "expected to flip in a future major release" rather than naming 8.0.0, since the release version is chosen at release time from the accumulated semver impact. The docs page asserted "in Struts 8.0.0" instead, which is a firmer commitment than the code itself makes. Softened to match the code's wording, with WW-5696 linked so the claim stays traceable. Also makes every other "removed in 8.0.0" claim (the JS validator's removal) cite WW-5694/WW-5696 inline rather than floating as a bare assertion. Naming 8.0.0 for that removal is kept as-is: it's a major-version removal, already committed to in Form.java's own @Deprecated(since = "7.4.0", forRemoval = true) javadoc, and WW-5696 is filed against it. Co-Authored-By: Claude Opus 5 --- source/core-developers/ajax-client-side-validation.md | 3 ++- source/core-developers/client-side-validation.md | 5 +++-- source/tag-developers/css-xhtml-theme.md | 11 +++++++---- source/tag-developers/xhtml-theme.md | 11 +++++++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/source/core-developers/ajax-client-side-validation.md b/source/core-developers/ajax-client-side-validation.md index 45012487ed..2f52ccff57 100644 --- a/source/core-developers/ajax-client-side-validation.md +++ b/source/core-developers/ajax-client-side-validation.md @@ -15,7 +15,8 @@ parent: ## Description AJAX-based client side validation improves upon [Pure JavaScript Client Side Validation](client-side-validation#pure-javascript-client-side-validation-deprecated) -(deprecated since Struts 7.4.0, removed in 8.0.0) by using a combination of JavaScript, DOM manipulation, and remote server communication. Unlike the pure client side +(deprecated since 7.4.0 [WW-5694](https://issues.apache.org/jira/browse/WW-5694), removed in 8.0.0 +[WW-5696](https://issues.apache.org/jira/browse/WW-5696)) by using a combination of JavaScript, DOM manipulation, and remote server communication. Unlike the pure client side implementation, AJAX-based validation communicates with the server. This means all your validation rules that worked when submitting a form will still work within the browser. diff --git a/source/core-developers/client-side-validation.md b/source/core-developers/client-side-validation.md index e281ba7eab..5b4836b458 100644 --- a/source/core-developers/client-side-validation.md +++ b/source/core-developers/client-side-validation.md @@ -39,8 +39,9 @@ theme](../tag-developers/html5-theme#using-the-html5-theme)). There is no per-fo unlike the deprecated JavaScript validator, this feature does not use ``. Once the constant is on and a field's theme is `html5`, its validators are consulted automatically. -> The constant defaults to `false` in 7.4.0 so existing `html5`-theme forms keep rendering unchanged. The -> default is expected to flip to `true` in Struts 8.0.0. +> The constant defaults to `false` so existing `html5`-theme forms keep rendering unchanged. The default is +> expected to flip to `true` in a future major release, tracked by +> [WW-5696](https://issues.apache.org/jira/browse/WW-5696). ### The governing rule: never false-reject diff --git a/source/tag-developers/css-xhtml-theme.md b/source/tag-developers/css-xhtml-theme.md index 44176e72ea..0cd207e23e 100644 --- a/source/tag-developers/css-xhtml-theme.md +++ b/source/tag-developers/css-xhtml-theme.md @@ -15,8 +15,9 @@ The _css_xhtml theme_ provides all the basics that the [simple theme](simple-the - Labels for each of the HTML [Struts Tags](struts-tags), placed according to the CSS stylesheet - [Validation](../core-developers/validation) and error reporting - [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) - using 100% JavaScript on the browser — **deprecated since Struts 7.4.0, removed in 8.0.0**; use the - [html5 theme](html5-theme) instead + using 100% JavaScript on the browser — deprecated since 7.4.0 + ([WW-5694](https://issues.apache.org/jira/browse/WW-5694)), removed in 8.0.0 + ([WW-5696](https://issues.apache.org/jira/browse/WW-5696)); use the [html5 theme](html5-theme) instead ## Wrapping the Simple Theme @@ -71,7 +72,8 @@ The head includes a style sheet. The contents of **styles.css** are: The css_xhtml [form](form-tag) template is almost exactly like the _xhtml form template_ , including support for [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) -(deprecated since Struts 7.4.0, removed in 8.0.0). The difference is that instead of printing out an opening and closing +(deprecated since 7.4.0 [WW-5694](https://issues.apache.org/jira/browse/WW-5694), removed in 8.0.0 +[WW-5696](https://issues.apache.org/jira/browse/WW-5696)). The difference is that instead of printing out an opening and closing `
` element, there are no elements. Instead, the CSS rules for the individual HTML tags are assumed to handle all display logic. However, as noted, client-side validation is still supported. @@ -79,6 +81,7 @@ display logic. However, as noted, client-side validation is still supported. The css_xhtml [form](form-tag) template is almost exactly like the _xhtml form template_ , including support for [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) -(deprecated since Struts 7.4.0, removed in 8.0.0). The only difference is that instead of printing out an opening and +(deprecated since 7.4.0 [WW-5694](https://issues.apache.org/jira/browse/WW-5694), removed in 8.0.0 +[WW-5696](https://issues.apache.org/jira/browse/WW-5696)). The only difference is that instead of printing out an opening and closing `
` element, there are no elements. Instead, the CSS rules for the individual HTML tags are assumed to handle all display logic. However, as noted, client side validation is still supported. diff --git a/source/tag-developers/xhtml-theme.md b/source/tag-developers/xhtml-theme.md index abed1e85f2..50297eea93 100644 --- a/source/tag-developers/xhtml-theme.md +++ b/source/tag-developers/xhtml-theme.md @@ -20,8 +20,9 @@ The xhtml provides all the basics that the [simple theme](simple-theme) provides the `labelposition` attribute) - [Validation](../core-developers/validation) and error reporting - [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) - using 100% JavaScript on the browser — **deprecated since Struts 7.4.0, removed in 8.0.0**; use the - [html5 theme](html5-theme) instead + using 100% JavaScript on the browser — deprecated since 7.4.0 + ([WW-5694](https://issues.apache.org/jira/browse/WW-5694)), removed in 8.0.0 + ([WW-5696](https://issues.apache.org/jira/browse/WW-5696)); use the [html5 theme](html5-theme) instead ## Wrapping the Simple Theme @@ -96,7 +97,8 @@ The head template imports a style sheet. The contents of **styles.css** are: The xhtml form template sets up the wrapping table around all the other form elements. In addition to creating this wrapping table, the opening and closing templates also, if the `validate` parameter is set to true, enable [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) -(deprecated since Struts 7.4.0, removed in 8.0.0). +(deprecated since 7.4.0 [WW-5694](https://issues.apache.org/jira/browse/WW-5694), removed in 8.0.0 +[WW-5696](https://issues.apache.org/jira/browse/WW-5696)). {% highlight freemarker %} {% remote_file_content https://raw.githubusercontent.com/apache/struts/main/core/src/main/resources/template/xhtml/form.ftl %} @@ -113,7 +115,8 @@ The closing template, `form-close.ftl`: The xhtml form template sets up the wrapping table around all the other [xhtml theme](xhtml-theme) form elements. In addition to creating this wrapping table, the opening and closing templates also, if the `validate` parameter is set to `true`, enable [Pure JavaScript Client Side Validation](../core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated) -(deprecated since Struts 7.4.0, removed in 8.0.0). +(deprecated since 7.4.0 [WW-5694](https://issues.apache.org/jira/browse/WW-5694), removed in 8.0.0 +[WW-5696](https://issues.apache.org/jira/browse/WW-5696)). See the **form.ftl** contents: From 32e721803f0eb6cebadef6662eb311c536102d44 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 25 Aug 2026 00:24:03 +0200 Subject: [PATCH 3/5] docs: add the regex validator's fourth pattern gate, generalize the trim note The regex row promised pattern whenever text-entry, caseSensitive="true", trim="false" and ECMAScript-safe held, but StrutsHtmlConstraintProvider has a fourth gate: email and creditcard validators are excluded even then, since both extend RegexFieldValidator but carry grammars the browser doesn't share. Also generalizes the trim="false" explanation: StringLengthFieldValidator.trim defaults to true just like RegexFieldValidator.trim, so minlength/maxlength are emitted just as rarely as pattern is, for the same reason (server measures/ matches the trimmed value, browser sees the raw one). The page previously warned about this only for pattern, which understated stringlength's own reach. Reworked into one shared paragraph covering both. Co-Authored-By: Claude Opus 5 --- .../core-developers/client-side-validation.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/source/core-developers/client-side-validation.md b/source/core-developers/client-side-validation.md index 5b4836b458..d81b9c3ea6 100644 --- a/source/core-developers/client-side-validation.md +++ b/source/core-developers/client-side-validation.md @@ -69,7 +69,7 @@ an `int` or `double` validator is attached to it. | `requiredstring` | `required` | on text-entry controls (`text`, `search`, `tel`, `password`, `email`, `url`) and `textarea` | | `required` | `required` | only on `radio` and `file` | | `stringlength` | `minlength` / `maxlength` | on text-entry or `textarea`, and only if the validator has `trim="false"`; each attribute is added only if actually configured | -| `regex` | `pattern` | on text-entry controls only, and only if `caseSensitive="true"`, `trim="false"`, and the regex is ECMAScript-safe (see below) | +| `regex` | `pattern` | on text-entry controls only, and only if `caseSensitive="true"`, `trim="false"`, the regex is ECMAScript-safe (see below), and the validator is not an `email` or `creditcard` validator (both extend `RegexFieldValidator` but carry grammars the browser does not share) | | `int`, `short`, `long` | `min` / `max` | only when the control is already `type="number"` or `type="range"` | | `double` | `min` / `max` | same as above; only inclusive bounds are emitted — exclusive bounds have no HTML equivalent and are omitted | | `date` | — | nothing yet; temporal `min`/`max` is deferred to a future release | @@ -77,7 +77,8 @@ an `int` or `double` validator is attached to it. | `fieldexpression`, `expression`, `conversion`, visitor validators | — | never emitted | | any validator carrying a message | `data-msg-` | always added, including for validators that emit no constraint attribute at all | -Two of these conditions are easy to miss and sharply limit how often `required` and `pattern` actually show up: +Two of these conditions are easy to miss and sharply limit how often `required`, `minlength`/`maxlength`, +and `pattern` actually show up: **`required` is split across two validators, and they don't behave alike.** `requiredstring` fails on null, empty, and (by default) blank values, so it is strictly stricter than the browser's `required` — safe @@ -89,12 +90,15 @@ Only `radio` and `file` controls omit their parameter entirely when left empty, control types where plain `required` agrees with the server — which is why the table above emits `required` for the `required` validator on those two types alone. -**`pattern` needs `trim="false"`, which is not the default.** `RegexFieldValidator.trim` defaults to -`true`, so the server matches the field's *trimmed* value while an HTML `pattern` attribute matches the -*raw* one. A regex like `[a-z]+` would accept `"abc "` server-side while the browser blocks it. Because of -this, `pattern` is only ever emitted for validators explicitly configured with `trim="false"` — which most -existing `regex` validators are not. In practice, expect `pattern` to show up rarely until applications -start setting `trim="false"` deliberately for fields where it's safe. +**Both `minlength`/`maxlength` and `pattern` need `trim="false"`, which is not the default.** Both +`StringLengthFieldValidator.trim` and `RegexFieldValidator.trim` default to `true`, so the server measures +or matches the field's *trimmed* value while the HTML attribute constrains the *raw* one. A `stringlength` +validator with `maxLength="4"` would reject `"abcd "` server-side after trimming while the browser, seeing +five raw characters, would block it first — and a `regex` of `[a-z]+` would accept `"abc "` server-side +while the browser blocks it. Because of this, `minlength`/`maxlength` and `pattern` are only ever emitted +for validators explicitly configured with `trim="false"` — which most existing `stringlength` and `regex` +validators are not. In practice, expect both to show up rarely until applications start setting +`trim="false"` deliberately for fields where it's safe. **ECMAScript-safe** means the regex uses only constructs that mean the same thing in Java's regex engine and in the browser's: literals, `\d`/`\w` and their negations, character classes without POSIX or Unicode From acbface9d4c8e99b72998fadc8b1d5fdb9d197ed Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 25 Aug 2026 00:27:39 +0200 Subject: [PATCH 4/5] docs: fix the stringlength example's direction (server accepts, browser blocks) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous sentence said a stringlength validator with maxLength="4" would reject "abcd " server-side after trimming — backwards. Trimmed to "abcd" (4 chars), StringLengthFieldValidator.validateValue only rejects when trimmedLength > maxLengthToUse, and 4 > 4 is false, so the server accepts it. The paragraph exists to show the server-accepts/browser-blocks asymmetry that makes emitting the constraint attribute risky if trim isn't false; an example asserting the opposite direction illustrated nothing. Corrected to state the server accepts "abcd " while a browser maxlength="4" would stop the fifth character from ever being typed, matching the direction of the retained [a-z]+/"abc " regex example alongside it. Co-Authored-By: Claude Opus 5 --- source/core-developers/client-side-validation.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/core-developers/client-side-validation.md b/source/core-developers/client-side-validation.md index d81b9c3ea6..1bf76e2b4b 100644 --- a/source/core-developers/client-side-validation.md +++ b/source/core-developers/client-side-validation.md @@ -93,9 +93,10 @@ for the `required` validator on those two types alone. **Both `minlength`/`maxlength` and `pattern` need `trim="false"`, which is not the default.** Both `StringLengthFieldValidator.trim` and `RegexFieldValidator.trim` default to `true`, so the server measures or matches the field's *trimmed* value while the HTML attribute constrains the *raw* one. A `stringlength` -validator with `maxLength="4"` would reject `"abcd "` server-side after trimming while the browser, seeing -five raw characters, would block it first — and a `regex` of `[a-z]+` would accept `"abc "` server-side -while the browser blocks it. Because of this, `minlength`/`maxlength` and `pattern` are only ever emitted +validator with `maxLength="4"` accepts `"abcd "` — it trims to four characters, which is within the limit — +but a browser enforcing `maxlength="4"` would stop the user typing the fifth character at all. Likewise, a +`regex` of `[a-z]+` accepts `"abc "` server-side (it trims to `"abc"` first) while the browser, matching the +raw value, blocks it. Because of this, `minlength`/`maxlength` and `pattern` are only ever emitted for validators explicitly configured with `trim="false"` — which most existing `stringlength` and `regex` validators are not. In practice, expect both to show up rarely until applications start setting `trim="false"` deliberately for fields where it's safe. From 9f79caba720e745563c2b251e062c0a8ac3f07f9 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 25 Aug 2026 11:14:22 +0200 Subject: [PATCH 5/5] docs: redirect the removed pure-javascript page to its new section The page was folded into client-side-validation.md as a deprecated section, which frees the URL but 404s external inbound links to a page that has been live for years. Internal links were already updated; this covers the ones we do not control. Uses .htaccess rather than leaving a stub page behind, following the existing "downloads was renamed to releases" precedent in the same file: no orphan page in the nav tree, and the reader lands on the section rather than on a pointer to it. Matches both the extensionless and .html forms. Issued as a permanent (301) redirect. The fold-in is not going to be undone, and 301 is what consolidates the old URL's link equity onto the new section; the surrounding rules only default to 302 because none of them ever specified. Pins the target heading's anchor explicitly with {#...} instead of relying on kramdown's auto_ids. The generated id is identical today, but eight internal links and the redirect now depend on it, so it should not be a side effect of the heading's wording. Co-Authored-By: Claude Opus 5 --- source/.htaccess | 4 ++++ source/core-developers/client-side-validation.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/source/.htaccess b/source/.htaccess index 41562793bf..28cf19561e 100644 --- a/source/.htaccess +++ b/source/.htaccess @@ -28,6 +28,10 @@ RedirectMatch \/2.*\/(.*)? http://struts.apache.org/$1 # page downloads.html was renamed to releases.html RedirectMatch \/downloads /releases +# page pure-java-script-client-side-validation was folded into client-side-validation as a +# deprecated section, see WW-5694 +RedirectMatch permanent \/core\-developers\/pure\-java\-script\-client\-side\-validation(\.html)?$ /core-developers/client-side-validation#pure-javascript-client-side-validation-deprecated + # always points to the most recent announce-YYYY page, no manual update needed {% assign announcements = site.pages | where_exp: "page", "page.path contains 'announce-'" | sort: "path" -%} RedirectMatch \/announce.html(#a[0-9]+)? {{ announcements.last.url }}$1 diff --git a/source/core-developers/client-side-validation.md b/source/core-developers/client-side-validation.md index 1bf76e2b4b..e3a2949a57 100644 --- a/source/core-developers/client-side-validation.md +++ b/source/core-developers/client-side-validation.md @@ -140,7 +140,7 @@ An application that wants a less conservative mapping — for example, treating hatch for every limitation described above: the framework's own mapping stays deliberately conservative, but nothing stops an application from replacing it with one that fits its own validators and locales. -## Pure JavaScript Client Side Validation (deprecated) +## Pure JavaScript Client Side Validation (deprecated) {#pure-javascript-client-side-validation-deprecated} > **Deprecated since Struts 7.4.0 ([WW-5694](https://issues.apache.org/jira/browse/WW-5694)), removed in > Struts 8.0.0 ([WW-5696](https://issues.apache.org/jira/browse/WW-5696)).** New applications should use