This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch docs/struts-7.2.0-documentation-updates in repository https://gitbox.apache.org/repos/asf/struts-site.git
commit b3626f1334dd1f318481ed97a5f3da7304203ba8 Author: Lukasz Lenart <[email protected]> AuthorDate: Tue Feb 17 07:36:12 2026 +0100 docs: add documentation for Struts 7.2.0 new features and changes - Checkbox hidden field prefix constant (WW-3429) - Spring autowire alwaysRespect default change to true (WW-3647) - Spring bean names support in type converters (WW-4291) - Preparable.prepare() default method (WW-5588) - Dynamic file upload validation parameters (WW-5585) - @Validations annotation fix for doubleRange/shortRange (WW-5579) - JSP direct access security warning (WW-5294) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --- .claude/settings.json | 5 +- CLAUDE.md | 141 ++++++++------------- .../action-file-upload-interceptor.md | 19 ++- source/core-developers/prepare-interceptor.md | 6 +- source/core-developers/type-conversion.md | 11 +- source/plugins/spring/index.md | 15 ++- source/security/index.md | 9 +- source/tag-developers/checkbox-tag.md | 18 +++ 8 files changed, 128 insertions(+), 96 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index a22e52465..d78853e27 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -7,12 +7,15 @@ "WebFetch(domain:github.com)", "WebFetch(domain:struts.apache.org)", "WebFetch(domain:raw.githubusercontent.com)", + "WebFetch(domain:cwiki.apache.org)", "Bash(git checkout:*)", "Bash(git add:*)", "Bash(git commit:*)", "Bash(git push:*)", "Bash(gh pr create:*)", - "Bash(gh pr view:*)" + "Bash(gh pr view:*)", + "Bash(gh pr diff:*)", + "Bash(ls:*)" ], "deny": [], "ask": [] diff --git a/CLAUDE.md b/CLAUDE.md index de11465d0..16811823d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,122 +4,89 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is the Apache Struts website repository, which powers https://struts.apache.org/. The site is built with Jekyll and uses a mix of Markdown and HTML files. +This is the Apache Struts website repository, which powers https://struts.apache.org/. The site is built with Jekyll 4.2 and uses a mix of Markdown and HTML files. ## Development Commands ### Running the Site Locally -The site can be run locally using Jekyll or Docker: - -**Using Jekyll directly:** -```bash -bundle exec jekyll serve -w --trace --host 0.0.0.0 -``` - -**Using Docker (recommended):** ```bash -# Build the Docker image (only needed when Dockerfile changes) -./docker-build.sh +# Using Docker (recommended, Apple Silicon) +./docker-build.sh # Build image (only when Dockerfile changes) +./docker-arm64-serve.sh # Serve site at http://localhost:4000 -# Run the site in Docker -./docker-arm64-serve.sh # For ARM64 architecture (Apple Silicon) -``` - -The site will be available at http://localhost:4000 - -### Installing Dependencies - -```bash -bundle install +# Using Jekyll directly +bundle install # Install dependencies +bundle exec jekyll serve -w --trace --host 0.0.0.0 ``` -## Directory Structure - -- `source/` - All source content files (Markdown and HTML) - - `source/_layouts/` - Jekyll layout templates (main-page, default, core-developers, maven-archetypes) - - `source/_includes/` - Reusable HTML partials (header, footer) - - `source/.htaccess` - Redirect rules -- `content/` - Generated output (not committed to git) -- `_config.yml` - Jekyll configuration and site variables - -## Site Configuration +## Architecture -The `_config.yml` file contains important site-wide variables: +### Directory Structure -- `current_version` / `prev_version` - Struts version tracking -- `release_date` / `prev_release_date` - Release date tracking -- `archetype_version` - Maven archetype version -- `repository_url` - GitHub repository for edit links -- `apidocs` - Path to API documentation -- `wiki_url` - Confluence wiki URL -- `archive_url` - Archive distribution URL -- `jira_url` - JIRA issue tracking URL +- `source/` - All source content (Markdown/HTML), Jekyll source directory + - `_layouts/` - Page templates: `default`, `main-page`, `core-developers`, `maven-archetypes` + - `_includes/` - Shared partials: `header.html`, `footer.html` + - `_plugins/` - Custom Liquid tags (see below) + - `.htaccess` - Apache redirect rules (update when adding yearly announcement pages) +- `_config.yml` - Jekyll config and site-wide version/release variables +- `.asf.yaml` - ASF infrastructure config (deployment, notifications, branch protection) -These variables are used throughout the site for dynamic content. +### Content Sections -## Content Guidelines +The `source/` directory contains these main content areas: +- `docs/` - Framework documentation +- `getting-started/` - Getting started guides +- `plugins/` - Plugin documentation +- `core-developers/` - Core developer guide (uses `core-developers` layout) +- `security/` - Security bulletins +- `tag-developers/` - Tag developer documentation +- `maven-archetypes/` - Maven archetype docs (uses `maven-archetypes` layout) +- `announce-YYYY.md/html` - Yearly announcement pages -### Page Front Matter +### Site Variables in `_config.yml` -All Markdown pages should start with YAML front matter: - -```yaml ---- -layout: default -title: Page Title ---- -``` +Version and release date variables (`current_version`, `prev_version`, `release_date`, etc.) are referenced throughout `source/index.html`, `source/download.md`, and `source/releases.md` using `{{ site.variable_name }}`. When preparing a new release, update these in `_config.yml` rather than editing individual pages. -Available layouts: `default`, `main-page`, `core-developers`, `maven-archetypes` +### Custom Jekyll Plugins -### Styling Classes +Two custom Liquid tags in `source/_plugins/`: -Use these Kramdown-style classes for formatting: +- **`{% snippet %}`** - Fetches and embeds code snippets from remote sources (Apache Git repos). Supports parameters: `url=`, `id=` (for snippet markers), `lang=` (syntax highlighting), `javadoc=` (strip Javadoc formatting). Caches fetched content in `target/snippet_*.cache`. +- **`{% remote_file_content %}`** - Fetches and inlines content from a remote URL. -**Text alignment:** `{:.text-left}`, `{:.text-center}`, `{:.text-right}`, `{:.text-justify}` +### Layouts -**Text colors:** `{:.text-primary}`, `{:.text-info}`, `{:.text-success}`, `{:.text-warning}`, `{:.text-danger}` +- `default` - Standard content page with "Edit on GitHub" link and optional parent breadcrumb +- `main-page` - Homepage layout (no sidebar, no edit link) +- `core-developers` - Like default but with hardcoded "back to Core Developers Guide" link +- `maven-archetypes` - Similar to core-developers -**Backgrounds:** `{:.bg-primary}`, `{:.bg-info}`, `{:.bg-success}`, `{:.bg-warning}`, `{:.bg-danger}` +### Styling -**Labels:** `{:.label .label-primary}`, etc. +Kramdown attribute syntax for Bootstrap classes: +- Alerts: `{:.alert .alert-warning}`, `{:.alert .alert-info}`, etc. +- Text: `{:.text-primary}`, `{:.text-danger}`, etc. +- See `source/updating-website.md` for complete examples. -**Alert panels:** `{:.alert .alert-info}`, `{:.alert .alert-success}`, `{:.alert .alert-warning}`, `{:.alert .alert-danger}` +## Deployment -See `source/updating-website.md` for complete styling examples. +### Branching -## Deployment Process - -### Branching Strategy - -- `main` - Source branch for all changes and PRs -- `asf-site` - Production site (auto-deployed, DO NOT MODIFY MANUALLY) -- `asf-staging` - Staging site (auto-deployed, DO NOT MODIFY MANUALLY) +- `main` - Source branch for all changes/PRs +- `asf-site` - Production (auto-deployed, DO NOT MODIFY) +- `asf-staging` - Staging (auto-deployed, DO NOT MODIFY) ### Workflow -1. Make changes to the `main` branch -2. Small changes: Push directly to `main` (for contributors) -3. Larger changes: Open a Pull Request against `main` -4. PRs are automatically built and deployed to the staging site: https://struts.staged.apache.org/ -5. After PR merge, changes are automatically built and deployed to production via buildbot - -### Build Infrastructure - -- `.asf.yaml` - Controls ASF infrastructure integration (notifications, JIRA linking, Jekyll deployment) -- Buildbot job builds and deploys the main site -- Jenkins job builds and deploys the staging site and JavaDocs +1. Push changes to `main` or open a PR against `main` +2. PRs auto-deploy to staging: https://struts.staged.apache.org/ +3. After merge, auto-deployed to production via buildbot -### JavaDocs Deployment +### Common Tasks -JavaDocs are deployed via a dedicated Jenkins job. Provide the Git tag (e.g., `STRUTS_2_5_22`) to generate and deploy JavaDocs for a specific release. +**New release**: Update version variables in `_config.yml`, add announcement entry to the current year's `announce-YYYY.md`. -## Jekyll Configuration +**New announcement year**: Create `source/announce-YYYY.md`, update the redirect in `source/.htaccess` (`RedirectMatch \/announce.html` line) to point to the new year. -- Uses Kramdown markdown processor with GFM (GitHub Flavored Markdown) input -- Syntax highlighting via Rouge -- Auto-generates IDs for headings -- Table of contents levels: 1-3 -- Source directory: `source/` -- Encoding: UTF-8 \ No newline at end of file +**New security bulletin**: Add to `source/security/` directory. \ No newline at end of file diff --git a/source/core-developers/action-file-upload-interceptor.md b/source/core-developers/action-file-upload-interceptor.md index 06a67c55d..52e489e8b 100644 --- a/source/core-developers/action-file-upload-interceptor.md +++ b/source/core-developers/action-file-upload-interceptor.md @@ -37,13 +37,30 @@ You can override the text of these messages by providing text for the following ## Parameters - `maximumSize` (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set - on the action. Note, this is <b>not</b> related to the various properties found in struts.properties. + on the action. Note, this is <b>not</b> related to the various properties found in struts.properties. Default to approximately 2MB. - `allowedTypes` (optional) - a comma separated list of content types (ie: `text/html`) that the interceptor will allow a file reference to be set on the action. If none is specified allow all types to be uploaded. - `allowedExtensions` (optional) - a comma separated list of file extensions (ie: `.html`) that the interceptor will allow a file reference to be set on the action. If none is specified allow all extensions to be uploaded. +### Dynamic Parameter Evaluation + +> Since Struts 7.2.0 + +The `allowedTypes`, `allowedExtensions`, and `maximumSize` parameters support `${...}` expression evaluation, +enabling per-request dynamic validation. This is available when used with `WithLazyParams`. + +```xml +<interceptor-ref name="actionFileUpload"> + <param name="allowedTypes">${allowedContentTypes}</param> + <param name="maximumSize">${maxFileSize}</param> +</interceptor-ref> +``` + +The expressions are evaluated against the ValueStack at the time of the upload, allowing your action to provide +dynamic values based on the current request context. + ## Extending the Interceptor You can extend this interceptor and override the acceptFile method to provide more control over which files are supported diff --git a/source/core-developers/prepare-interceptor.md b/source/core-developers/prepare-interceptor.md index c2e97c94e..40fcde2fb 100644 --- a/source/core-developers/prepare-interceptor.md +++ b/source/core-developers/prepare-interceptor.md @@ -29,9 +29,13 @@ on the actual object loaded from the database. See the example for more info. In `PrepareInterceptor` applies only when action implements `Preparable` 1. if the action class have `prepare<MethodName>()`, it will be invoked 2. else if the action class have `prepareDo<MethodName>()`, it will be invoked - 3. no matter if 1] or 2] is performed, if `alwaysInvokePrepare` property of the interceptor is `true` (which is by + 3. no matter if 1) or 2) is performed, if `alwaysInvokePrepare` property of the interceptor is `true` (which is by default `true`), `prepare()` will be invoked. +> Note: since Struts 7.2.0, the `Preparable.prepare()` method is now a `default` method with an empty implementation. +> Actions that only use per-method variants (e.g., `prepareEdit()`, `prepareSave()`) no longer need to provide +> an empty `prepare()` override. + ## Parameters - `alwaysInvokePrepare` - Default to true. If true, prepare will always be invoked, otherwise it will not. diff --git a/source/core-developers/type-conversion.md b/source/core-developers/type-conversion.md index 85b16bac2..f52d61ef6 100644 --- a/source/core-developers/type-conversion.md +++ b/source/core-developers/type-conversion.md @@ -114,14 +114,21 @@ amount=com.acme.converters.MyCustomBigDecimalConverter ## Applying a Type Converter for an application -Application-wide converters can be specified in a file called `struts-conversion.properties` or `xwork-conversion.properties` (deprecated) +Application-wide converters can be specified in a file called `struts-conversion.properties` or `xwork-conversion.properties` (deprecated) located in the root of the classpath. ``` # syntax: <type> = <converterClassName> -java.math.BigDecimal = com.acme.MyBigDecimalConverter +java.math.BigDecimal = com.acme.MyBigDecimalConverter ``` +> NOTE: since Struts 7.2.0, when the Spring plugin is active, you can use Spring bean names in addition to fully qualified +> class names as converter values in `struts-conversion.properties`. For example, if you have a Spring bean named +> `myBigDecimalConverter`, you can reference it directly: +> ``` +> java.math.BigDecimal = myBigDecimalConverter +> ``` + ## A Simple Example Type conversion is great for situations where you need to turn a String in to a more complex object. Because the web diff --git a/source/plugins/spring/index.md b/source/plugins/spring/index.md index 3203a681d..e35f76934 100644 --- a/source/plugins/spring/index.md +++ b/source/plugins/spring/index.md @@ -228,6 +228,19 @@ so only actions are handled by it. This constant supports a comma separated list > This feature is experimental, and **should never** be used in production > systems. +## Migration Note: autowire alwaysRespect default change (7.2.0) + +Starting with Struts 7.2.0, the `struts.objectFactory.spring.autoWire.alwaysRespect` constant defaults to `true` +(previously `false`). This means the configured autowire strategy is now always applied consistently, which fixes issues +such as broken redirect URLs when Spring String beans are involved. +{:.alert .alert-warning} + +If you experience unexpected behavior after upgrading to 7.2.0, you can restore the previous behavior by setting: + +```xml +<constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="false" /> +``` + ## Settings The following settings can be customized. See the [developer guide](/core-developers/configuration-files). @@ -235,7 +248,7 @@ The following settings can be customized. See the [developer guide](/core-develo |Setting|Description|Default|Possible Values| |-------|-----------|-------|---------------| |struts.objectFactory.spring.autoWire|The autowire strategy|name|name,type,auto, or constructor| -|struts.objectFactory.spring.autoWire.alwaysRespect|Whether the autowire strategy should always be used, or if the framework should try to guess the best strategy based on the situation|false for backwards-compatibility|true or false| +|struts.objectFactory.spring.autoWire.alwaysRespect|Whether the autowire strategy should always be used, or if the framework should try to guess the best strategy based on the situation|true (changed from false in 7.2.0)|true or false| |struts.objectFactory.spring.useClassCache|Whether to have Spring use its class cache or not|true|true or false| |struts.class.reloading.watchList|List of jar files or directories to watch for changes|null|Comma separated list of absolute or relative paths to jars or directories| |struts.class.reloading.acceptClasses|List of regular expressions of accepted class names|null|Comma separated list of regular expressions of classes that will be loaded by the reloading class loader(we suggest to add regular expressions so only action classes are handled by the reloading class loader)| diff --git a/source/security/index.md b/source/security/index.md index 19eff51bd..79ddd46e7 100644 --- a/source/security/index.md +++ b/source/security/index.md @@ -40,11 +40,14 @@ by security level. ### Never expose JSP files directly -You must always hide JSP file behind an action, you cannot allow for direct access to the JSP files as this can leads -to unpredictable security vulnerabilities. You can achieve this by putting all your JSP files under the `WEB-INF` folder -- most of the JEE containers restrict access to files placed under the `WEB-INF` folder. Second option is to add security +You must always hide JSP file behind an action, you cannot allow for direct access to the JSP files as this can leads +to unpredictable security vulnerabilities. You can achieve this by putting all your JSP files under the `WEB-INF` folder +- most of the JEE containers restrict access to files placed under the `WEB-INF` folder. Second option is to add security constraint to the `web.xml` file: +> Note: since Struts 7.2.0, the framework now logs a security warning when JSP tags are accessed directly outside of +> an action scope. This helps identify JSP files that are inadvertently exposed without action protection. + ```xml <!-- Restricts access to pure JSP files - access available only via Struts action --> <security-constraint> diff --git a/source/tag-developers/checkbox-tag.md b/source/tag-developers/checkbox-tag.md index 38426f42d..58f0b6ffa 100644 --- a/source/tag-developers/checkbox-tag.md +++ b/source/tag-developers/checkbox-tag.md @@ -39,3 +39,21 @@ Renders an HTML input element of type checkbox, populated by the specified prope ```html <input type="checkbox" name="checkboxField1" value="true" checked="checked" /> ``` + +## Hidden Field Prefix + +> Since Struts 7.2.0 + +The checkbox tag generates a companion hidden field to ensure that unchecked values are still submitted with the form. +By default, this hidden field uses the prefix `__checkbox_` (e.g., `__checkbox_checkboxField1`). + +For HTML5 compliance, you can change the prefix to `struts_checkbox_` using the `struts.ui.checkbox.hiddenPrefix` constant: + +```xml +<constant name="struts.ui.checkbox.hiddenPrefix" value="struts_checkbox_" /> +``` + +| Prefix | Example Hidden Field Name | Notes | +|--------|--------------------------|-------| +| `__checkbox_` | `__checkbox_checkboxField1` | Default, backward-compatible | +| `struts_checkbox_` | `struts_checkbox_checkboxField1` | HTML5-compliant alternative |
