This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch feature/WW-5444-html5 in repository https://gitbox.apache.org/repos/asf/struts.git
commit a5e6374b48a7f813806963dbd1994e1184c7ea50 Author: Lukasz Lenart <[email protected]> AuthorDate: Sun Nov 23 18:49:47 2025 +0100 test(html5-theme): add comprehensive unit tests and fix template variables - Add testGenericHtml5() methods to 13 UI tag test classes - Create theme.properties with parent=simple configuration - Migrate all 32 HTML5 templates from deprecated 'parameters' to 'attributes' variable - Exclude DoubleSelect and UpDownSelect (not supported in HTML5 theme) - All 26 HTML5 theme tests passing (100% success rate) Components tested: Textfield, Textarea, Password, Checkbox, CheckboxList, Radio, Select, File, Hidden, Label, ComboBox, Reset, Submit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --- core/src/main/resources/template/html5/a-close.ftl | 38 +- .../main/resources/template/html5/actionerror.ftl | 14 +- .../resources/template/html5/actionmessage.ftl | 16 +- .../src/main/resources/template/html5/checkbox.ftl | 36 +- .../main/resources/template/html5/checkboxlist.ftl | 82 ++-- .../src/main/resources/template/html5/combobox.ftl | 60 +-- .../resources/template/html5/common-attributes.ftl | 4 +- core/src/main/resources/template/html5/css.ftl | 22 +- core/src/main/resources/template/html5/debug.ftl | 6 +- .../template/html5/dynamic-attributes.ftl | 6 +- .../main/resources/template/html5/fielderror.ftl | 24 +- core/src/main/resources/template/html5/file.ftl | 36 +- .../main/resources/template/html5/form-close.ftl | 2 +- core/src/main/resources/template/html5/form.ftl | 62 +-- core/src/main/resources/template/html5/head.ftl | 2 +- core/src/main/resources/template/html5/hidden.ftl | 26 +- core/src/main/resources/template/html5/label.ftl | 26 +- core/src/main/resources/template/html5/link.ftl | 60 +-- core/src/main/resources/template/html5/nonce.ftl | 4 +- .../src/main/resources/template/html5/optgroup.ftl | 32 +- .../src/main/resources/template/html5/password.ftl | 42 +- .../template/html5/prefixed-dynamic-attributes.ftl | 6 +- .../src/main/resources/template/html5/radiomap.ftl | 68 +-- core/src/main/resources/template/html5/reset.ftl | 78 ++-- core/src/main/resources/template/html5/script.ftl | 36 +- .../resources/template/html5/scripting-events.ftl | 56 +-- core/src/main/resources/template/html5/select.ftl | 106 ++--- .../main/resources/template/html5/submit-close.ftl | 8 +- core/src/main/resources/template/html5/submit.ftl | 86 ++-- core/src/main/resources/template/html5/text.ftl | 44 +- .../src/main/resources/template/html5/textarea.ftl | 54 +-- .../main/resources/template/html5/theme.properties | 3 + core/src/main/resources/template/html5/token.ftl | 4 +- .../struts2/views/jsp/ui/CheckboxListTest.java | 13 + .../apache/struts2/views/jsp/ui/CheckboxTest.java | 11 + .../apache/struts2/views/jsp/ui/ComboBoxTest.java | 13 + .../org/apache/struts2/views/jsp/ui/FileTest.java | 11 + .../apache/struts2/views/jsp/ui/HiddenTest.java | 11 + .../org/apache/struts2/views/jsp/ui/LabelTest.java | 11 + .../apache/struts2/views/jsp/ui/PasswordTest.java | 11 + .../org/apache/struts2/views/jsp/ui/RadioTest.java | 13 + .../org/apache/struts2/views/jsp/ui/ResetTest.java | 11 + .../apache/struts2/views/jsp/ui/SelectTest.java | 13 + .../apache/struts2/views/jsp/ui/SubmitTest.java | 11 + .../apache/struts2/views/jsp/ui/TextareaTest.java | 11 + .../apache/struts2/views/jsp/ui/TextfieldTest.java | 11 + .../2025-11-23-WW-5444-html5-theme-testing.md | 518 +++++++++++++++++++++ 47 files changed, 1245 insertions(+), 573 deletions(-) diff --git a/core/src/main/resources/template/html5/a-close.ftl b/core/src/main/resources/template/html5/a-close.ftl index 15465dec8..f2d2d9333 100644 --- a/core/src/main/resources/template/html5/a-close.ftl +++ b/core/src/main/resources/template/html5/a-close.ftl @@ -18,33 +18,33 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <@compress single_line=true> <a -<#if parameters.id??> - id="${parameters.id}" +<#if attributes.id??> + id="${attributes.id}" </#if> -<#if parameters.href??> - href="${parameters.href?no_esc}" +<#if attributes.href??> + href="${attributes.href?no_esc}" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#if parameters.tabindex??> - tabindex="${parameters.tabindex}" +<#if attributes.tabindex??> + tabindex="${attributes.tabindex}" </#if> -<#if parameters.cssClass??> - class="${parameters.cssClass}" +<#if attributes.cssClass??> + class="${attributes.cssClass}" </#if> -<#if parameters.cssStyle??> - style="${parameters.cssStyle}" +<#if attributes.cssStyle??> + style="${attributes.cssStyle}" </#if> -<#if parameters.title??> - title="${parameters.title}" +<#if attributes.title??> + title="${attributes.title}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> ->${tag.escapeHtmlBody()?then(parameters.body, parameters.body?no_esc)}</a> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> +>${tag.escapeHtmlBody()?then(attributes.body, attributes.body?no_esc)}</a> </@compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/actionerror.ftl b/core/src/main/resources/template/html5/actionerror.ftl index e6586adef..0e195e8d5 100644 --- a/core/src/main/resources/template/html5/actionerror.ftl +++ b/core/src/main/resources/template/html5/actionerror.ftl @@ -22,21 +22,21 @@ <#if (actionErrors?? && actionErrors?size > 0)> <@compress single_line=true> <ul - <#if parameters.id??> - id="${parameters.id}" + <#if attributes.id??> + id="${attributes.id}" </#if> - <#if parameters.cssClass??> - class="${parameters.cssClass}" + <#if attributes.cssClass??> + class="${attributes.cssClass}" <#else> class="errorMessage" </#if> - <#if parameters.cssStyle??> - style="${parameters.cssStyle}" + <#if attributes.cssStyle??> + style="${attributes.cssStyle}" </#if> > <#list actionErrors as error> <#if error??><#t/> - <li><span><#if parameters.escape>${error!}<#else>${error!?no_esc}</#if></span></li> + <li><span><#if attributes.escape>${error!}<#else>${error!?no_esc}</#if></span></li> </#if> </#list> </ul> diff --git a/core/src/main/resources/template/html5/actionmessage.ftl b/core/src/main/resources/template/html5/actionmessage.ftl index 606c460e3..1e7950be0 100644 --- a/core/src/main/resources/template/html5/actionmessage.ftl +++ b/core/src/main/resources/template/html5/actionmessage.ftl @@ -19,24 +19,24 @@ * under the License. */ --> -<#if (actionMessages?? && actionMessages?size > 0 && !parameters.isEmptyList)> +<#if (actionMessages?? && actionMessages?size > 0 && !attributes.isEmptyList)> <@compress single_line=true> <ul - <#if parameters.id??> - id="${parameters.id}" + <#if attributes.id??> + id="${attributes.id}" </#if> - <#if parameters.cssClass??> - class="${parameters.cssClass}" + <#if attributes.cssClass??> + class="${attributes.cssClass}" <#else> class="actionMessage" </#if> - <#if parameters.cssStyle??> - style="${parameters.cssStyle}" + <#if attributes.cssStyle??> + style="${attributes.cssStyle}" </#if> > <#list actionMessages as message> <#if message??><#t/> - <li><span><#if parameters.escape>${message!}<#else>${message!?no_esc}</#if></span></li> + <li><span><#if attributes.escape>${message!}<#else>${message!?no_esc}</#if></span></li> </#if> </#list> </ul> diff --git a/core/src/main/resources/template/html5/checkbox.ftl b/core/src/main/resources/template/html5/checkbox.ftl index 134f5d280..10f5300dd 100644 --- a/core/src/main/resources/template/html5/checkbox.ftl +++ b/core/src/main/resources/template/html5/checkbox.ftl @@ -18,35 +18,35 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> -<input type="checkbox" name="${parameters.name}" value="${parameters.fieldValue}" -<#if parameters.nameValue?? && parameters.nameValue> +<input type="checkbox" name="${attributes.name}" value="${attributes.fieldValue}" +<#if attributes.nameValue?? && attributes.nameValue> checked="checked" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" +<#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#if parameters.title?has_content> - title="${parameters.title}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> -<#if parameters.submitUnchecked!false> -<input type="hidden" id="__checkbox_${parameters.id}" name="__checkbox_${parameters.name}" value="${parameters.fieldValue}" -<#if parameters.disabled!false> +<#if attributes.submitUnchecked!false> +<input type="hidden" id="__checkbox_${attributes.id}" name="__checkbox_${attributes.name}" value="${attributes.fieldValue}" +<#if attributes.disabled!false> disabled="disabled" </#if> /> </#if> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/checkboxlist.ftl b/core/src/main/resources/template/html5/checkboxlist.ftl index 17745976c..a55732a82 100644 --- a/core/src/main/resources/template/html5/checkboxlist.ftl +++ b/core/src/main/resources/template/html5/checkboxlist.ftl @@ -18,103 +18,103 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <#assign itemCount = 0/> -<#if parameters.list??> -<@s.iterator value="parameters.list"> +<#if attributes.list??> +<@s.iterator value="attributes.list"> <#assign itemCount = itemCount + 1/> - <#if parameters.listKey??> - <#assign itemKey = stack.findValue(parameters.listKey)/> - <#assign itemKeyStr = stack.findString(parameters.listKey)/> + <#if attributes.listKey??> + <#assign itemKey = stack.findValue(attributes.listKey)/> + <#assign itemKeyStr = stack.findString(attributes.listKey)/> <#else> <#assign itemKey = stack.findValue('top')/> <#assign itemKeyStr = stack.findString('top')> </#if> - <#if parameters.listLabelKey??> + <#if attributes.listLabelKey??> <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale file for it's localized value. This is then used as a label --> - <#assign itemValue = struts.getText(stack.findString(parameters.listLabelKey))/> - <#elseif parameters.listValue??> - <#assign itemValue = stack.findString(parameters.listValue)!""/> + <#assign itemValue = struts.getText(stack.findString(attributes.listLabelKey))/> + <#elseif attributes.listValue??> + <#assign itemValue = stack.findString(attributes.listValue)!""/> <#else> <#assign itemValue = stack.findString('top')/> </#if> - <#if parameters.listCssClass??> - <#if stack.findString(parameters.listCssClass)??> - <#assign itemCssClass= stack.findString(parameters.listCssClass)/> + <#if attributes.listCssClass??> + <#if stack.findString(attributes.listCssClass)??> + <#assign itemCssClass= stack.findString(attributes.listCssClass)/> <#else> <#assign itemCssClass = ''/> </#if> </#if> - <#if parameters.listCssStyle??> - <#if stack.findString(parameters.listCssStyle)??> - <#assign itemCssStyle= stack.findString(parameters.listCssStyle)/> + <#if attributes.listCssStyle??> + <#if stack.findString(attributes.listCssStyle)??> + <#assign itemCssStyle= stack.findString(attributes.listCssStyle)/> <#else> <#assign itemCssStyle = ''/> </#if> </#if> - <#if parameters.listTitle??> - <#if stack.findString(parameters.listTitle)??> - <#assign itemTitle= stack.findString(parameters.listTitle)/> + <#if attributes.listTitle??> + <#if stack.findString(attributes.listTitle)??> + <#assign itemTitle= stack.findString(attributes.listTitle)/> <#else> <#assign itemTitle = ''/> </#if> </#if> -<input type="checkbox" name="${parameters.name}" value="${itemKeyStr}" -<#if parameters.id?has_content> - id="${parameters.id}-${itemCount}" +<input type="checkbox" name="${attributes.name}" value="${itemKeyStr}" +<#if attributes.id?has_content> + id="${attributes.id}-${itemCount}" <#else> - id="${parameters.name}-${itemCount}" + id="${attributes.name}-${itemCount}" </#if> -<#if tag.contains(parameters.nameValue, itemKey)> +<#if tag.contains(attributes.nameValue, itemKey)> checked="checked" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> <#if itemCssClass??> class="${itemCssClass}" <#else> -<#if parameters.cssClass?has_content> - class="${parameters.cssClass}" +<#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> </#if> <#if itemCssStyle??> style="${itemCssStyle}" <#else> -<#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" +<#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> </#if> <#if itemTitle??> title="${itemTitle}" <#else> -<#if parameters.title?has_content> - title="${parameters.title}" +<#if attributes.title?has_content> + title="${attributes.title}" </#if> </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> <#global evaluate_dynamic_attributes = true/> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> <label -<#if parameters.id?has_content> - for="${parameters.id}-${itemCount}" +<#if attributes.id?has_content> + for="${attributes.id}-${itemCount}" <#else> - for="${parameters.name}-${itemCount}" + for="${attributes.name}-${itemCount}" </#if> class="checkboxLabel">${itemValue}</label> </@s.iterator> <#else> </#if> -<input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.name}" +<input type="hidden" id="__multiselect_${attributes.id}" name="__multiselect_${attributes.name}" value="" -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> /> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/combobox.ftl b/core/src/main/resources/template/html5/combobox.ftl index 3056e0935..2e950c49c 100644 --- a/core/src/main/resources/template/html5/combobox.ftl +++ b/core/src/main/resources/template/html5/combobox.ftl @@ -19,70 +19,70 @@ */ --> <@s.script> - function autoPopulate_${parameters.escapedId}(targetElement) { - <#if parameters.headerKey?? && parameters.headerValue??> - if (targetElement.options[targetElement.selectedIndex].value == '${parameters.headerKey?js_string}') { + function autoPopulate_${attributes.escapedId}(targetElement) { + <#if attributes.headerKey?? && attributes.headerValue??> + if (targetElement.options[targetElement.selectedIndex].value == '${attributes.headerKey?js_string}') { return; } </#if> - <#if parameters.emptyOption!false> + <#if attributes.emptyOption!false> if (targetElement.options[targetElement.selectedIndex].value == '') { return; } </#if> - targetElement.form.elements['${parameters.name?js_string}'].value=targetElement.options[targetElement.selectedIndex].value; + targetElement.form.elements['${attributes.name?js_string}'].value=targetElement.options[targetElement.selectedIndex].value; } </@s.script> -<#include "/${parameters.templateDir}/html5/text.ftl" /> +<#include "/${attributes.templateDir}/html5/text.ftl" /> <#compress> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> - <#if parameters.list??> - <select onChange="autoPopulate_${parameters.escapedId}(this);" - <#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> - <#if parameters.disabled!false> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> + <#if attributes.list??> + <select onChange="autoPopulate_${attributes.escapedId}(this);" + <#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> + <#if attributes.disabled!false> disabled="disabled" </#if> > - <#if (parameters.headerKey?? && parameters.headerValue??)> - <option value="${parameters.headerKey}">${parameters.headerValue}</option> + <#if (attributes.headerKey?? && attributes.headerValue??)> + <option value="${attributes.headerKey}">${attributes.headerValue}</option> </#if> - <#if parameters.emptyOption!false> + <#if attributes.emptyOption!false> <option value=""></option> </#if> - <@s.iterator value="parameters.list"> - <#if parameters.listKey??> - <#assign tmpListKey = stack.findString(parameters.listKey) /> + <@s.iterator value="attributes.list"> + <#if attributes.listKey??> + <#assign tmpListKey = stack.findString(attributes.listKey) /> <#else> <#assign tmpListKey = stack.findString('top') /> </#if> - <#if parameters.listValue??> - <#assign tmpListValue = stack.findString(parameters.listValue) /> + <#if attributes.listValue??> + <#assign tmpListValue = stack.findString(attributes.listValue) /> <#else> <#assign tmpListValue = stack.findString('top') /> </#if> - <#if parameters.listCssClass??> - <#if stack.findString(parameters.listCssClass)??> - <#assign itemCssClass= stack.findString(parameters.listCssClass)/> + <#if attributes.listCssClass??> + <#if stack.findString(attributes.listCssClass)??> + <#assign itemCssClass= stack.findString(attributes.listCssClass)/> <#else> <#assign itemCssClass = ''/> </#if> </#if> - <#if parameters.listCssStyle??> - <#if stack.findString(parameters.listCssStyle)??> - <#assign itemCssStyle= stack.findString(parameters.listCssStyle)/> + <#if attributes.listCssStyle??> + <#if stack.findString(attributes.listCssStyle)??> + <#assign itemCssStyle= stack.findString(attributes.listCssStyle)/> <#else> <#assign itemCssStyle = ''/> </#if> </#if> - <#if parameters.listTitle??> - <#if stack.findString(parameters.listTitle)??> - <#assign itemTitle= stack.findString(parameters.listTitle)/> + <#if attributes.listTitle??> + <#if stack.findString(attributes.listTitle)??> + <#assign itemTitle= stack.findString(attributes.listTitle)/> <#else> <#assign itemTitle = ''/> </#if> </#if> <option value="${tmpListKey}" - <#if (parameters.nameValue == tmpListKey)> + <#if (attributes.nameValue == tmpListKey)> selected="selected" </#if> <#if itemCssClass??> @@ -99,4 +99,4 @@ </select> </#if> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/common-attributes.ftl b/core/src/main/resources/template/html5/common-attributes.ftl index 0bfe73fc5..bd81fcb15 100644 --- a/core/src/main/resources/template/html5/common-attributes.ftl +++ b/core/src/main/resources/template/html5/common-attributes.ftl @@ -19,7 +19,7 @@ */ --> <#compress> -<#if parameters.accesskey?has_content> - accesskey="${parameters.accesskey}" +<#if attributes.accesskey?has_content> + accesskey="${attributes.accesskey}" </#if> </#compress> diff --git a/core/src/main/resources/template/html5/css.ftl b/core/src/main/resources/template/html5/css.ftl index 07220ac9f..00ea4b9fc 100644 --- a/core/src/main/resources/template/html5/css.ftl +++ b/core/src/main/resources/template/html5/css.ftl @@ -19,17 +19,17 @@ */ --> <#compress> -<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors.get(parameters.name)??/> -<#if parameters.cssClass?has_content && !(hasFieldErrors && parameters.cssErrorClass??)> - class="${parameters.cssClass}" -<#elseif parameters.cssClass?has_content && (hasFieldErrors && parameters.cssErrorClass??)> - class="${parameters.cssClass} ${parameters.cssErrorClass}" -<#elseif !(parameters.cssClass?has_content) && (hasFieldErrors && parameters.cssErrorClass??)> - class="${parameters.cssErrorClass}" +<#assign hasFieldErrors = attributes.name?? && fieldErrors?? && fieldErrors.get(attributes.name)??/> +<#if attributes.cssClass?has_content && !(hasFieldErrors && attributes.cssErrorClass??)> + class="${attributes.cssClass}" +<#elseif attributes.cssClass?has_content && (hasFieldErrors && attributes.cssErrorClass??)> + class="${attributes.cssClass} ${attributes.cssErrorClass}" +<#elseif !(attributes.cssClass?has_content) && (hasFieldErrors && attributes.cssErrorClass??)> + class="${attributes.cssErrorClass}" </#if> -<#if parameters.cssStyle?has_content && !(hasFieldErrors && (parameters.cssErrorStyle?? || parameters.cssErrorClass??))> - style="${parameters.cssStyle}" -<#elseif hasFieldErrors && parameters.cssErrorStyle??> - style="${parameters.cssErrorStyle}" +<#if attributes.cssStyle?has_content && !(hasFieldErrors && (attributes.cssErrorStyle?? || attributes.cssErrorClass??))> + style="${attributes.cssStyle}" +<#elseif hasFieldErrors && attributes.cssErrorStyle??> + style="${attributes.cssErrorStyle}" </#if> </#compress> diff --git a/core/src/main/resources/template/html5/debug.ftl b/core/src/main/resources/template/html5/debug.ftl index 889702213..fef4f6fa5 100644 --- a/core/src/main/resources/template/html5/debug.ftl +++ b/core/src/main/resources/template/html5/debug.ftl @@ -41,7 +41,7 @@ <br> <a href="#" id="toggle-button">[Debug]</a> -<div style="display:none" id="<#if parameters.id??>${parameters.id}<#else>debug</#if>"> +<div style="display:none" id="<#if attributes.id??>${attributes.id}<#else>debug</#if>"> <h2>Struts ValueStack Debug</h2> <br> @@ -50,7 +50,7 @@ <tr><th>Object</th><th>Property Name</th><th>Property Value</th><th>Property Class</th></tr> <#assign index=1> - <#list parameters.stackValues as stackObject> + <#list attributes.stackValues as stackObject> <tr> <td rowspan="${stackObject.value.size()}">${stackObject.key}</td> @@ -86,7 +86,7 @@ </div> <@s.script> document.getElementById('toggle-button').onclick = function() { - toggleDebug('<#if parameters.id??>${parameters.id}<#else>debug</#if>'); + toggleDebug('<#if attributes.id??>${attributes.id}<#else>debug</#if>'); return false; } </@s.script> diff --git a/core/src/main/resources/template/html5/dynamic-attributes.ftl b/core/src/main/resources/template/html5/dynamic-attributes.ftl index 246ec466f..64ea3dfa6 100644 --- a/core/src/main/resources/template/html5/dynamic-attributes.ftl +++ b/core/src/main/resources/template/html5/dynamic-attributes.ftl @@ -26,10 +26,10 @@ <#return true> </#if> </#function> -<#if (parameters.dynamicAttributes?? && parameters.dynamicAttributes?size > 0)> -<#assign aKeys = parameters.dynamicAttributes.keySet()> +<#if (attributes.dynamicAttributes?? && attributes.dynamicAttributes?size > 0)> +<#assign aKeys = attributes.dynamicAttributes.keySet()> <#list aKeys?filter(acceptKey) as aKey> -<#assign keyValue = parameters.dynamicAttributes.get(aKey)/> +<#assign keyValue = attributes.dynamicAttributes.get(aKey)/> <#if keyValue?is_string> <#if evaluate_dynamic_attributes!false == true> <#assign value = struts.translateVariables(keyValue)!keyValue/> diff --git a/core/src/main/resources/template/html5/fielderror.ftl b/core/src/main/resources/template/html5/fielderror.ftl index 4381fdcd5..ef16199ff 100644 --- a/core/src/main/resources/template/html5/fielderror.ftl +++ b/core/src/main/resources/template/html5/fielderror.ftl @@ -33,22 +33,22 @@ <#assign eValue = fieldErrors.get(fieldErrorFieldName)> <#if (haveMatchedErrorField && (!doneStartUlTag))> <ul - <#if parameters.id?has_content> - id="${parameters.id}" + <#if attributes.id?has_content> + id="${attributes.id}" </#if> - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" <#else> class="errorMessage" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> > <#assign doneStartUlTag=true> </#if> <#list eValue as eEachValue> - <li><span><#if parameters.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span> + <li><span><#if attributes.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span> </li> </#list> </#if> @@ -61,19 +61,19 @@ <#else> <#if (eKeysSize > 0)> <ul - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" <#else> class="errorMessage" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> > <#list eKeys as eKey> <#assign eValue = fieldErrors.get(eKey)> <#list eValue as eEachValue> - <li><span><#if parameters.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span> + <li><span><#if attributes.escape>${eEachValue!}<#else>${eEachValue!?no_esc}</#if></span> </li> </#list> </#list> diff --git a/core/src/main/resources/template/html5/file.ftl b/core/src/main/resources/template/html5/file.ftl index 60a486f50..386ae0fa2 100644 --- a/core/src/main/resources/template/html5/file.ftl +++ b/core/src/main/resources/template/html5/file.ftl @@ -18,32 +18,32 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <input type="file" - name="${(parameters.name!"")}" -<#if parameters.get("size")?has_content> - size="${parameters.get("size")}" + name="${(attributes.name!"")}" +<#if attributes.get("size")?has_content> + size="${attributes.get("size")}" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#if parameters.accept?has_content> - accept="${parameters.accept}" +<#if attributes.accept?has_content> + accept="${attributes.accept}" </#if> -<#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" +<#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#if parameters.title?has_content> - title="${parameters.title}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/form-close.ftl b/core/src/main/resources/template/html5/form-close.ftl index f3aaa7491..b170ba294 100644 --- a/core/src/main/resources/template/html5/form-close.ftl +++ b/core/src/main/resources/template/html5/form-close.ftl @@ -19,4 +19,4 @@ */ --> </form> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/form.ftl b/core/src/main/resources/template/html5/form.ftl index f0eaa4342..b7c74c5d3 100644 --- a/core/src/main/resources/template/html5/form.ftl +++ b/core/src/main/resources/template/html5/form.ftl @@ -18,55 +18,55 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> -<#if (parameters.validate!false == false)> - <#if parameters.onsubmit?has_content> - ${tag.addParameter('onsubmit', "${parameters.onsubmit}") } +<#if (attributes.validate!false == false)> + <#if attributes.onsubmit?has_content> + ${tag.addParameter('onsubmit', "${attributes.onsubmit}") } </#if> </#if> <form -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#if parameters.name?has_content> - name="${parameters.name}" +<#if attributes.name?has_content> + name="${attributes.name}" </#if> -<#if parameters.onsubmit?has_content> - onsubmit="<#outputformat 'JavaScript'>${parameters.onsubmit}</#outputformat>" +<#if attributes.onsubmit?has_content> + onsubmit="<#outputformat 'JavaScript'>${attributes.onsubmit}</#outputformat>" </#if> -<#if parameters.onreset?has_content> - onreset="<#outputformat 'JavaScript'>${parameters.onreset}</#outputformat>" +<#if attributes.onreset?has_content> + onreset="<#outputformat 'JavaScript'>${attributes.onreset}</#outputformat>" </#if> -<#if parameters.action?has_content> - action="${parameters.action}" +<#if attributes.action?has_content> + action="${attributes.action}" </#if> -<#if parameters.target?has_content> - target="${parameters.target}" +<#if attributes.target?has_content> + target="${attributes.target}" </#if> -<#if parameters.method?has_content> - method="${parameters.method}" +<#if attributes.method?has_content> + method="${attributes.method}" <#else> method="post" </#if> -<#if parameters.enctype?has_content> - enctype="${parameters.enctype}" +<#if attributes.enctype?has_content> + enctype="${attributes.enctype}" </#if> -<#if parameters.cssClass?has_content> - class="${parameters.cssClass}" +<#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> -<#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" +<#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> -<#if parameters.title?has_content> - title="${parameters.title}" +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#if parameters.acceptcharset?has_content> - accept-charset="${parameters.acceptcharset}" +<#if attributes.acceptcharset?has_content> + accept-charset="${attributes.acceptcharset}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> -<#if parameters.onreset?has_content> - onreset="${parameters.onreset}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> +<#if attributes.onreset?has_content> + onreset="${attributes.onreset}" </#if> > </#compress> diff --git a/core/src/main/resources/template/html5/head.ftl b/core/src/main/resources/template/html5/head.ftl index 01c5141a9..502d9a30d 100644 --- a/core/src/main/resources/template/html5/head.ftl +++ b/core/src/main/resources/template/html5/head.ftl @@ -19,5 +19,5 @@ */ --> <@compress single_line=true> -<@s.script src="${base}${parameters.staticContentPath}/utils.js" /> +<@s.script src="${base}${attributes.staticContentPath}/utils.js" /> </@compress> diff --git a/core/src/main/resources/template/html5/hidden.ftl b/core/src/main/resources/template/html5/hidden.ftl index 7e7184953..afcc6b20b 100644 --- a/core/src/main/resources/template/html5/hidden.ftl +++ b/core/src/main/resources/template/html5/hidden.ftl @@ -18,26 +18,26 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <input type="hidden" - name="${(parameters.name!"")}" -<#if parameters.nameValue??> - value="<@s.property value="parameters.nameValue"/>" + name="${(attributes.name!"")}" +<#if attributes.nameValue??> + value="<@s.property value="attributes.nameValue"/>" </#if> -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#if parameters.cssClass?has_content> - class="${parameters.cssClass}" +<#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> -<#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" +<#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/label.ftl b/core/src/main/resources/template/html5/label.ftl index 730efb048..7a5641a99 100644 --- a/core/src/main/resources/template/html5/label.ftl +++ b/core/src/main/resources/template/html5/label.ftl @@ -18,25 +18,25 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <label -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#if parameters.title?has_content> - title="${parameters.title}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#if parameters.for?has_content> - for="${parameters.for}" +<#if attributes.for?has_content> + for="${attributes.for}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > -<#if parameters.nameValue??> -<@s.property value="parameters.nameValue"/> +<#if attributes.nameValue??> +<@s.property value="attributes.nameValue"/> </#if> </label> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/link.ftl b/core/src/main/resources/template/html5/link.ftl index 891d23763..9bd742b55 100644 --- a/core/src/main/resources/template/html5/link.ftl +++ b/core/src/main/resources/template/html5/link.ftl @@ -18,57 +18,57 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <@compress single_line=true> <link - <#if parameters.rel?has_content> - rel="${parameters.rel}" + <#if attributes.rel?has_content> + rel="${attributes.rel}" <#else> rel="stylesheet" </#if> - <#if parameters.type?has_content> - type="${parameters.type}" + <#if attributes.type?has_content> + type="${attributes.type}" <#else> type="text/css" </#if> - <#if parameters.href?has_content> - href="${parameters.href}" + <#if attributes.href?has_content> + href="${attributes.href}" </#if> - <#if parameters.hreflang?has_content> - hreflang="${parameters.hreflang}" + <#if attributes.hreflang?has_content> + hreflang="${attributes.hreflang}" </#if> - <#if parameters.disabled?has_content && parameters.disabled == "true"> + <#if attributes.disabled?has_content && attributes.disabled == "true"> disabled </#if> - <#if parameters.media?has_content> - media="${parameters.media}" + <#if attributes.media?has_content> + media="${attributes.media}" <#else> media="all" </#if> - <#if parameters.title?has_content> - title="${parameters.title}" + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.as?has_content> - as="${parameters.as}" + <#if attributes.as?has_content> + as="${attributes.as}" </#if> - <#if parameters.referrerpolicy?has_content> - referrerpolicy="${parameters.referrerpolicy}" + <#if attributes.referrerpolicy?has_content> + referrerpolicy="${attributes.referrerpolicy}" </#if> - <#if parameters.sizes?has_content> - sizes="${parameters.sizes}" + <#if attributes.sizes?has_content> + sizes="${attributes.sizes}" </#if> - <#if parameters.crossorigin?has_content> - crossorigin="${parameters.crossorigin}" + <#if attributes.crossorigin?has_content> + crossorigin="${attributes.crossorigin}" </#if> - <#if parameters.integrity?has_content> - integrity="${parameters.integrity}" + <#if attributes.integrity?has_content> + integrity="${attributes.integrity}" </#if> - <#if parameters.importance?has_content> - importance="${parameters.importance}" + <#if attributes.importance?has_content> + importance="${attributes.importance}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/nonce.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/nonce.ftl" /> /> </@compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/nonce.ftl b/core/src/main/resources/template/html5/nonce.ftl index 4e1efe179..8570d9786 100644 --- a/core/src/main/resources/template/html5/nonce.ftl +++ b/core/src/main/resources/template/html5/nonce.ftl @@ -19,7 +19,7 @@ */ --> <#compress> -<#if parameters.nonce?has_content> - nonce="${parameters.nonce}" +<#if attributes.nonce?has_content> + nonce="${attributes.nonce}" </#if> </#compress> diff --git a/core/src/main/resources/template/html5/optgroup.ftl b/core/src/main/resources/template/html5/optgroup.ftl index 8273a5d00..c1d4681c3 100644 --- a/core/src/main/resources/template/html5/optgroup.ftl +++ b/core/src/main/resources/template/html5/optgroup.ftl @@ -18,38 +18,38 @@ * under the License. */ --> -<#if parameters.optGroupInternalListUiBeanList??> +<#if attributes.optGroupInternalListUiBeanList??> <#compress> - <#assign optGroupInternalListUiBeans=parameters.optGroupInternalListUiBeanList /> + <#assign optGroupInternalListUiBeans=attributes.optGroupInternalListUiBeanList /> <#list optGroupInternalListUiBeans as optGroupInternalListUiBean> <optgroup<#rt> - <#if optGroupInternalListUiBean.parameters.label?has_content> - label="${optGroupInternalListUiBean.parameters.label}"<#rt> + <#if optGroupInternalListUiBean.attributes.label?has_content> + label="${optGroupInternalListUiBean.attributes.label}"<#rt> </#if> - <#if optGroupInternalListUiBean.parameters.disabled!false> + <#if optGroupInternalListUiBean.attributes.disabled!false> disabled="disabled"<#rt> </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > - <#list optGroupInternalListUiBean.parameters.list as optGroupBean> + <#list optGroupInternalListUiBean.attributes.list as optGroupBean> <#assign trash=stack.push(optGroupBean) /> - <#assign tmpKey=stack.findValue(optGroupInternalListUiBean.parameters.listKey) /> - <#assign tmpValue=stack.findValue(optGroupInternalListUiBean.parameters.listValue) /> + <#assign tmpKey=stack.findValue(optGroupInternalListUiBean.attributes.listKey) /> + <#assign tmpValue=stack.findValue(optGroupInternalListUiBean.attributes.listValue) /> <#assign tmpKeyStr = tmpKey.toString() /> <#assign optGroupItemCssClass = ''/> - <#if optGroupInternalListUiBean.parameters.listCssClass??> - <#assign optGroupItemCssClass= stack.findString(optGroupInternalListUiBean.parameters.listCssClass)!''/> + <#if optGroupInternalListUiBean.attributes.listCssClass??> + <#assign optGroupItemCssClass= stack.findString(optGroupInternalListUiBean.attributes.listCssClass)!''/> </#if> <#assign optGroupItemCssStyle = ''/> - <#if optGroupInternalListUiBean.parameters.listCssStyle??> - <#assign optGroupItemCssStyle= stack.findString(optGroupInternalListUiBean.parameters.listCssStyle)!''/> + <#if optGroupInternalListUiBean.attributes.listCssStyle??> + <#assign optGroupItemCssStyle= stack.findString(optGroupInternalListUiBean.attributes.listCssStyle)!''/> </#if> <#assign optGroupItemTitle = ''/> - <#if optGroupInternalListUiBean.parameters.listTitle??> - <#assign optGroupItemTitle= stack.findString(optGroupInternalListUiBean.parameters.listTitle)!''/> + <#if optGroupInternalListUiBean.attributes.listTitle??> + <#assign optGroupItemTitle= stack.findString(optGroupInternalListUiBean.attributes.listTitle)!''/> </#if> <option value="${tmpKeyStr}"<#rt> - <#if tag.contains(parameters.nameValue, tmpKey) == true> + <#if tag.contains(attributes.nameValue, tmpKey) == true> selected="selected"<#rt> </#if> <#if optGroupItemCssClass?has_content> diff --git a/core/src/main/resources/template/html5/password.ftl b/core/src/main/resources/template/html5/password.ftl index 222aa5947..63d807969 100644 --- a/core/src/main/resources/template/html5/password.ftl +++ b/core/src/main/resources/template/html5/password.ftl @@ -18,38 +18,38 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <input type="password" - name="${(parameters.name!"")}" -<#if parameters.get("size")?has_content> - size="${parameters.get("size")}" + name="${(attributes.name!"")}" +<#if attributes.get("size")?has_content> + size="${attributes.get("size")}" </#if> -<#if parameters.maxlength?has_content> - maxlength="${parameters.maxlength}" +<#if attributes.maxlength?has_content> + maxlength="${attributes.maxlength}" </#if> -<#if parameters.nameValue?? && parameters.showPassword!false> - value="<@s.property value="parameters.nameValue"/>" +<#if attributes.nameValue?? && attributes.showPassword!false> + value="<@s.property value="attributes.nameValue"/>" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#if parameters.readonly!false> +<#if attributes.readonly!false> readonly="readonly" </#if> -<#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" +<#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#if parameters.title?has_content> - title="${parameters.title}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl b/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl index ad1dd1cd5..0d94cd74e 100644 --- a/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl +++ b/core/src/main/resources/template/html5/prefixed-dynamic-attributes.ftl @@ -20,11 +20,11 @@ */ --> <#macro prefixedDynamicAttributes prefix> -<#if (parameters.dynamicAttributes?? && parameters.dynamicAttributes?size > 0)> -<#assign aKeys = parameters.dynamicAttributes.keySet()> +<#if (attributes.dynamicAttributes?? && attributes.dynamicAttributes?size > 0)> +<#assign aKeys = attributes.dynamicAttributes.keySet()> <#list aKeys as aKey> <#if aKey?starts_with(prefix)> -<#assign keyValue = parameters.dynamicAttributes.get(aKey)/> +<#assign keyValue = attributes.dynamicAttributes.get(aKey)/> <#if keyValue?is_string> <#assign value = struts.translateVariables(keyValue)!keyValue/> <#else> diff --git a/core/src/main/resources/template/html5/radiomap.ftl b/core/src/main/resources/template/html5/radiomap.ftl index 208f45f1d..13a597f70 100644 --- a/core/src/main/resources/template/html5/radiomap.ftl +++ b/core/src/main/resources/template/html5/radiomap.ftl @@ -18,67 +18,67 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> - <@s.iterator value="parameters.list"> - <#if parameters.listKey??> - <#assign itemKey = stack.findValue(parameters.listKey)/> - <#assign itemKeyStr = stack.findString(parameters.listKey)/> + <@s.iterator value="attributes.list"> + <#if attributes.listKey??> + <#assign itemKey = stack.findValue(attributes.listKey)/> + <#assign itemKeyStr = stack.findString(attributes.listKey)/> <#else> <#assign itemKey = stack.findValue('top')/> <#assign itemKeyStr = stack.findString('top')> </#if> - <#if parameters.listValueKey??> + <#if attributes.listValueKey??> <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale file for it's localized value. This is then used as a label --> - <#assign valueKey = stack.findString(parameters.listValueKey)!''/> + <#assign valueKey = stack.findString(attributes.listValueKey)!''/> <#if valueKey?has_content> <#assign itemValue = struts.getText(valueKey) /> <#else> - <#assign itemValue = parameters.listValueKey /> + <#assign itemValue = attributes.listValueKey /> </#if> - <#elseif parameters.listValue??> - <#assign itemValue = stack.findString(parameters.listValue)/> + <#elseif attributes.listValue??> + <#assign itemValue = stack.findString(attributes.listValue)/> <#else> <#assign itemValue = stack.findString('top')/> </#if> - <#if parameters.listCssClass??> - <#if stack.findString(parameters.listCssClass)??> - <#assign itemCssClass= stack.findString(parameters.listCssClass)/> + <#if attributes.listCssClass??> + <#if stack.findString(attributes.listCssClass)??> + <#assign itemCssClass= stack.findString(attributes.listCssClass)/> <#else> <#assign itemCssClass = ''/> </#if> </#if> - <#if parameters.listCssStyle??> - <#if stack.findString(parameters.listCssStyle)??> - <#assign itemCssStyle= stack.findString(parameters.listCssStyle)/> + <#if attributes.listCssStyle??> + <#if stack.findString(attributes.listCssStyle)??> + <#assign itemCssStyle= stack.findString(attributes.listCssStyle)/> <#else> <#assign itemCssStyle = ''/> </#if> </#if> - <#if parameters.listTitle??> - <#if stack.findString(parameters.listTitle)??> - <#assign itemTitle= stack.findString(parameters.listTitle)/> + <#if attributes.listTitle??> + <#if stack.findString(attributes.listTitle)??> + <#assign itemTitle= stack.findString(attributes.listTitle)/> <#else> <#assign itemTitle = ''/> </#if> </#if> <input type="radio" - <#if parameters.name?has_content> - name="${parameters.name?no_esc}" + <#if attributes.name?has_content> + name="${attributes.name?no_esc}" </#if> - id="${parameters.id}${itemKeyStr?replace(".", "_")}" - <#if tag.contains(parameters.nameValue!'', itemKey)> + id="${attributes.id}${itemKeyStr?replace(".", "_")}" + <#if tag.contains(attributes.nameValue!'', itemKey)> checked="checked" </#if> <#if itemKey??> value="${itemKeyStr}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> <#if itemCssClass?has_content> class="${itemCssClass}" @@ -89,19 +89,19 @@ <#if itemTitle?has_content> title="${itemTitle}" <#else> - <#if parameters.title?has_content> - title="${parameters.title}" + <#if attributes.title?has_content> + title="${attributes.title}" </#if> </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> <#global evaluate_dynamic_attributes = true/> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> - <label for="${parameters.id}${itemKeyStr?replace(".", "_")}"<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl"/>> + <label for="${attributes.id}${itemKeyStr?replace(".", "_")}"<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl"/>> ${itemValue} </label> </@s.iterator> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/reset.ftl b/core/src/main/resources/template/html5/reset.ftl index 56dc96aa7..02d36e9e5 100644 --- a/core/src/main/resources/template/html5/reset.ftl +++ b/core/src/main/resources/template/html5/reset.ftl @@ -18,67 +18,67 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> - <#if parameters.type?? && parameters.type=="button"> + <#if attributes.type?? && attributes.type=="button"> <button type="reset" - <#if parameters.name?has_content> - name="${parameters.name}" + <#if attributes.name?has_content> + name="${attributes.name}" </#if> - <#if parameters.nameValue??> - value="<@s.property value="parameters.nameValue"/>" + <#if attributes.nameValue??> + value="<@s.property value="attributes.nameValue"/>" </#if> - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl"/> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> - ><#if parameters.src?has_content> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl"/> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> + ><#if attributes.src?has_content> <img - <#if parameters.label?has_content> - alt="${parameters.label}" + <#if attributes.label?has_content> + alt="${attributes.label}" </#if> - <#if parameters.src?has_content> - src="${parameters.src}" + <#if attributes.src?has_content> + src="${attributes.src}" </#if> - /><#else><#if parameters.label?has_content><@s.property value="parameters.label"/></#if></#if></button> + /><#else><#if attributes.label?has_content><@s.property value="attributes.label"/></#if></#if></button> <#else> <input type="reset" - <#if parameters.name?has_content> - name="${parameters.name}" + <#if attributes.name?has_content> + name="${attributes.name}" </#if> - <#if parameters.nameValue??> - value="<@s.property value="parameters.nameValue"/>" + <#if attributes.nameValue??> + value="<@s.property value="attributes.nameValue"/>" </#if> - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> - <#if parameters.title?has_content> - title="${parameters.title}" + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> </#if> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/script.ftl b/core/src/main/resources/template/html5/script.ftl index b37b0b7c2..c5d8b7d35 100644 --- a/core/src/main/resources/template/html5/script.ftl +++ b/core/src/main/resources/template/html5/script.ftl @@ -20,34 +20,34 @@ --> <@compress single_line=true> <script - <#if parameters.async?has_content && parameters.async == "true"> + <#if attributes.async?has_content && attributes.async == "true"> async </#if> - <#if parameters.charset?has_content> - charset="${parameters.charset}" + <#if attributes.charset?has_content> + charset="${attributes.charset}" </#if> - <#if parameters.defer?has_content && parameters.defer=="true"> + <#if attributes.defer?has_content && attributes.defer=="true"> defer </#if> - <#if parameters.type?has_content> - type="${parameters.type}" + <#if attributes.type?has_content> + type="${attributes.type}" </#if> - <#if parameters.src?has_content> - src="${parameters.src}" + <#if attributes.src?has_content> + src="${attributes.src}" </#if> - <#if parameters.referrerpolicy?has_content> - referrerpolicy="${parameters.referrerpolicy}" + <#if attributes.referrerpolicy?has_content> + referrerpolicy="${attributes.referrerpolicy}" </#if> - <#if parameters.nomodule?has_content && parameters.nomodule=="true"> + <#if attributes.nomodule?has_content && attributes.nomodule=="true"> nomodule </#if> - <#if parameters.integrity?has_content> - integrity="${parameters.integrity}" + <#if attributes.integrity?has_content> + integrity="${attributes.integrity}" </#if> - <#if parameters.crossorigin?has_content> - crossorigin="${parameters.crossorigin}" + <#if attributes.crossorigin?has_content> + crossorigin="${attributes.crossorigin}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/nonce.ftl" />> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/nonce.ftl" />> </@compress> diff --git a/core/src/main/resources/template/html5/scripting-events.ftl b/core/src/main/resources/template/html5/scripting-events.ftl index 714ae9d56..9dc06bc7c 100644 --- a/core/src/main/resources/template/html5/scripting-events.ftl +++ b/core/src/main/resources/template/html5/scripting-events.ftl @@ -19,46 +19,46 @@ */ --> <#compress> -<#if parameters.onclick??> - onclick="<#outputformat 'JavaScript'>${parameters.onclick}</#outputformat>" +<#if attributes.onclick??> + onclick="<#outputformat 'JavaScript'>${attributes.onclick}</#outputformat>" </#if> -<#if parameters.ondblclick??> - ondblclick="<#outputformat 'JavaScript'>${parameters.ondblclick}</#outputformat>" +<#if attributes.ondblclick??> + ondblclick="<#outputformat 'JavaScript'>${attributes.ondblclick}</#outputformat>" </#if> -<#if parameters.onmousedown??> - onmousedown="<#outputformat 'JavaScript'>${parameters.onmousedown}</#outputformat>" +<#if attributes.onmousedown??> + onmousedown="<#outputformat 'JavaScript'>${attributes.onmousedown}</#outputformat>" </#if> -<#if parameters.onmouseup??> - onmouseup="<#outputformat 'JavaScript'>${parameters.onmouseup}</#outputformat>" +<#if attributes.onmouseup??> + onmouseup="<#outputformat 'JavaScript'>${attributes.onmouseup}</#outputformat>" </#if> -<#if parameters.onmouseover??> - onmouseover="<#outputformat 'JavaScript'>${parameters.onmouseover}</#outputformat>" +<#if attributes.onmouseover??> + onmouseover="<#outputformat 'JavaScript'>${attributes.onmouseover}</#outputformat>" </#if> -<#if parameters.onmousemove??> - onmousemove="<#outputformat 'JavaScript'>${parameters.onmousemove}</#outputformat>" +<#if attributes.onmousemove??> + onmousemove="<#outputformat 'JavaScript'>${attributes.onmousemove}</#outputformat>" </#if> -<#if parameters.onmouseout??> - onmouseout="<#outputformat 'JavaScript'>${parameters.onmouseout}</#outputformat>" +<#if attributes.onmouseout??> + onmouseout="<#outputformat 'JavaScript'>${attributes.onmouseout}</#outputformat>" </#if> -<#if parameters.onfocus??> - onfocus="<#outputformat 'JavaScript'>${parameters.onfocus}</#outputformat>" +<#if attributes.onfocus??> + onfocus="<#outputformat 'JavaScript'>${attributes.onfocus}</#outputformat>" </#if> -<#if parameters.onblur??> - onblur="<#outputformat 'JavaScript'>${parameters.onblur}</#outputformat>" +<#if attributes.onblur??> + onblur="<#outputformat 'JavaScript'>${attributes.onblur}</#outputformat>" </#if> -<#if parameters.onkeypress??> - onkeypress="<#outputformat 'JavaScript'>${parameters.onkeypress}</#outputformat>" +<#if attributes.onkeypress??> + onkeypress="<#outputformat 'JavaScript'>${attributes.onkeypress}</#outputformat>" </#if> -<#if parameters.onkeydown??> - onkeydown="<#outputformat 'JavaScript'>${parameters.onkeydown}</#outputformat>" +<#if attributes.onkeydown??> + onkeydown="<#outputformat 'JavaScript'>${attributes.onkeydown}</#outputformat>" </#if> -<#if parameters.onkeyup??> - onkeyup="<#outputformat 'JavaScript'>${parameters.onkeyup}</#outputformat>" +<#if attributes.onkeyup??> + onkeyup="<#outputformat 'JavaScript'>${attributes.onkeyup}</#outputformat>" </#if> -<#if parameters.onselect??> - onselect="<#outputformat 'JavaScript'>${parameters.onselect}</#outputformat>" +<#if attributes.onselect??> + onselect="<#outputformat 'JavaScript'>${attributes.onselect}</#outputformat>" </#if> -<#if parameters.onchange??> - onchange="<#outputformat 'JavaScript'>${parameters.onchange}</#outputformat>" +<#if attributes.onchange??> + onchange="<#outputformat 'JavaScript'>${attributes.onchange}</#outputformat>" </#if> </#compress> diff --git a/core/src/main/resources/template/html5/select.ftl b/core/src/main/resources/template/html5/select.ftl index d30b3e1ae..324ce2abf 100644 --- a/core/src/main/resources/template/html5/select.ftl +++ b/core/src/main/resources/template/html5/select.ftl @@ -18,49 +18,49 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <#setting number_format="#.#####"> <select - name="${(parameters.name!"")}" - <#if parameters.get("size")?has_content> - size="${parameters.get("size")}" + name="${(attributes.name!"")}" + <#if attributes.get("size")?has_content> + size="${attributes.get("size")}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#if parameters.id?has_content> - id="${parameters.id}" + <#if attributes.id?has_content> + id="${attributes.id}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> - <#if parameters.title?has_content> - title="${parameters.title}" + <#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.multiple!false> + <#if attributes.multiple!false> multiple="multiple" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > - <#if parameters.headerKey?? && parameters.headerValue??> - <option value="${parameters.headerKey}" - <#if tag.contains(parameters.nameValue, parameters.headerKey) == true> + <#if attributes.headerKey?? && attributes.headerValue??> + <option value="${attributes.headerKey}" + <#if tag.contains(attributes.nameValue, attributes.headerKey) == true> selected="selected" </#if> - >${parameters.headerValue}</option> + >${attributes.headerValue}</option> </#if> - <#if parameters.emptyOption!false> + <#if attributes.emptyOption!false> <option value=""></option> </#if> - <@s.iterator value="parameters.list"> - <#if parameters.listKey??> - <#if stack.findValue(parameters.listKey)??> - <#assign itemKey = stack.findValue(parameters.listKey)/> - <#assign itemKeyStr = stack.findString(parameters.listKey)/> + <@s.iterator value="attributes.list"> + <#if attributes.listKey??> + <#if stack.findValue(attributes.listKey)??> + <#assign itemKey = stack.findValue(attributes.listKey)/> + <#assign itemKeyStr = stack.findString(attributes.listKey)/> <#else> <#assign itemKey = ''/> <#assign itemKeyStr = ''/> @@ -69,47 +69,47 @@ <#assign itemKey = stack.findValue('top')/> <#assign itemKeyStr = stack.findString('top')> </#if> - <#if parameters.listValueKey??> + <#if attributes.listValueKey??> <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale file for it's localized value. This is then used as a label --> - <#assign valueKey = stack.findString(parameters.listValueKey)!'' /> + <#assign valueKey = stack.findString(attributes.listValueKey)!'' /> <#if valueKey?has_content> <#assign itemValue = struts.getText(valueKey) /> <#else> - <#assign itemValue = parameters.listValueKey /> + <#assign itemValue = attributes.listValueKey /> </#if> - <#elseif parameters.listValue??> - <#if stack.findString(parameters.listValue)??> - <#assign itemValue = stack.findString(parameters.listValue)/> + <#elseif attributes.listValue??> + <#if stack.findString(attributes.listValue)??> + <#assign itemValue = stack.findString(attributes.listValue)/> <#else> <#assign itemValue = ''/> </#if> <#else> <#assign itemValue = stack.findString('top')/> </#if> - <#if parameters.listCssClass??> - <#if stack.findString(parameters.listCssClass)??> - <#assign itemCssClass= stack.findString(parameters.listCssClass)/> + <#if attributes.listCssClass??> + <#if stack.findString(attributes.listCssClass)??> + <#assign itemCssClass= stack.findString(attributes.listCssClass)/> <#else> <#assign itemCssClass = ''/> </#if> </#if> - <#if parameters.listCssStyle??> - <#if stack.findString(parameters.listCssStyle)??> - <#assign itemCssStyle= stack.findString(parameters.listCssStyle)/> + <#if attributes.listCssStyle??> + <#if stack.findString(attributes.listCssStyle)??> + <#assign itemCssStyle= stack.findString(attributes.listCssStyle)/> <#else> <#assign itemCssStyle = ''/> </#if> </#if> - <#if parameters.listTitle??> - <#if stack.findString(parameters.listTitle)??> - <#assign itemTitle= stack.findString(parameters.listTitle)/> + <#if attributes.listTitle??> + <#if stack.findString(attributes.listTitle)??> + <#assign itemTitle= stack.findString(attributes.listTitle)/> <#else> <#assign itemTitle = ''/> </#if> </#if> <option value="${itemKeyStr}" - <#if tag.contains(parameters.nameValue, itemKey) == true> + <#if tag.contains(attributes.nameValue, itemKey) == true> selected="selected" </#if> <#if itemCssClass?has_content> @@ -123,27 +123,27 @@ </#if> >${itemValue}</option> </@s.iterator> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/optgroup.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/optgroup.ftl" /> </select> - <#if parameters.multiple!false> - <#if (parameters.id?? && parameters.name??)> - <input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.name}" value="" + <#if attributes.multiple!false> + <#if (attributes.id?? && attributes.name??)> + <input type="hidden" id="__multiselect_${attributes.id}" name="__multiselect_${attributes.name}" value="" </#if> - <#if (parameters.id?? && !parameters.name??)> - <input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.id}" value="" + <#if (attributes.id?? && !attributes.name??)> + <input type="hidden" id="__multiselect_${attributes.id}" name="__multiselect_${attributes.id}" value="" </#if> - <#if ( !parameters.id?? && parameters.name??)> - <input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.id}" value="" + <#if ( !attributes.id?? && attributes.name??)> + <input type="hidden" id="__multiselect_${attributes.id}" name="__multiselect_${attributes.id}" value="" </#if> - <#if ( !parameters.id?? && !parameters.name??)> + <#if ( !attributes.id?? && !attributes.name??)> <input type="hidden" id="" name="" value="" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> /> </#if> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/submit-close.ftl b/core/src/main/resources/template/html5/submit-close.ftl index 1211a060c..3b243d2c9 100644 --- a/core/src/main/resources/template/html5/submit-close.ftl +++ b/core/src/main/resources/template/html5/submit-close.ftl @@ -19,11 +19,11 @@ */ --> <#compress> -<#if parameters.type?? && parameters.type=="button"> -<#if parameters.body?length gt 0>${tag.escapeHtmlBody()?then(parameters.body, parameters.body?no_esc)}<#elseif parameters.label??><@s.property value="parameters.label"/></#if> +<#if attributes.type?? && attributes.type=="button"> +<#if attributes.body?length gt 0>${tag.escapeHtmlBody()?then(attributes.body, attributes.body?no_esc)}<#elseif attributes.label??><@s.property value="attributes.label"/></#if> </button> <#else> -${tag.escapeHtmlBody()?then(parameters.body, parameters.body?no_esc)} +${tag.escapeHtmlBody()?then(attributes.body, attributes.body?no_esc)} </#if> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/submit.ftl b/core/src/main/resources/template/html5/submit.ftl index e3d60452c..d691c090c 100644 --- a/core/src/main/resources/template/html5/submit.ftl +++ b/core/src/main/resources/template/html5/submit.ftl @@ -18,77 +18,77 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> -<#if parameters.type?? && parameters.type=="button"> +<#if attributes.type?? && attributes.type=="button"> <button type="submit" - <#if parameters.id?has_content> - id="${parameters.id}" + <#if attributes.id?has_content> + id="${attributes.id}" </#if> - <#if parameters.name?has_content> - name="${parameters.name}" + <#if attributes.name?has_content> + name="${attributes.name}" </#if> - <#if parameters.nameValue??> - value="<@s.property value="parameters.nameValue"/>" + <#if attributes.nameValue??> + value="<@s.property value="attributes.nameValue"/>" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> - <#if parameters.title?has_content> - title="${parameters.title}" + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl"/> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl"/> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > <#else> -<#if parameters.type?? && parameters.type=="image"> +<#if attributes.type?? && attributes.type=="image"> <input type="image" - <#if parameters.label?has_content> - alt="${parameters.label}" + <#if attributes.label?has_content> + alt="${attributes.label}" </#if> - <#if parameters.src?has_content> - src="${parameters.src}" + <#if attributes.src?has_content> + src="${attributes.src}" </#if> <#else> <input type="submit" - <#if parameters.nameValue?has_content> - value="<@s.property value="parameters.nameValue"/>" + <#if attributes.nameValue?has_content> + value="<@s.property value="attributes.nameValue"/>" </#if> </#if> - <#if parameters.id?has_content> - id="${parameters.id}" + <#if attributes.id?has_content> + id="${attributes.id}" </#if> - <#if parameters.name?has_content> - name="${parameters.name}" + <#if attributes.name?has_content> + name="${attributes.name}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.cssClass?has_content> - class="${parameters.cssClass}" + <#if attributes.cssClass?has_content> + class="${attributes.cssClass}" </#if> - <#if parameters.cssStyle?has_content> - style="${parameters.cssStyle}" + <#if attributes.cssStyle?has_content> + style="${attributes.cssStyle}" </#if> - <#if parameters.title?has_content> - title="${parameters.title}" + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > </#if> </#compress> diff --git a/core/src/main/resources/template/html5/text.ftl b/core/src/main/resources/template/html5/text.ftl index 53b0535bb..7ac956deb 100644 --- a/core/src/main/resources/template/html5/text.ftl +++ b/core/src/main/resources/template/html5/text.ftl @@ -18,39 +18,39 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <input - type="${(parameters.type!"text")}" - name="${(parameters.name!"")}" -<#if parameters.get("size")?has_content> - size="${parameters.get("size")}" + type="${(attributes.type!"text")}" + name="${(attributes.name!"")}" +<#if attributes.get("size")?has_content> + size="${attributes.get("size")}" </#if> -<#if parameters.maxlength?has_content> - maxlength="${parameters.maxlength}" +<#if attributes.maxlength?has_content> + maxlength="${attributes.maxlength}" </#if> -<#if parameters.nameValue??> - value="${parameters.nameValue}" +<#if attributes.nameValue??> + value="${attributes.nameValue}" </#if> -<#if parameters.disabled!false> +<#if attributes.disabled!false> disabled="disabled" </#if> -<#if parameters.readonly!false> +<#if attributes.readonly!false> readonly="readonly" </#if> -<#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" +<#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> -<#if parameters.id?has_content> - id="${parameters.id}" +<#if attributes.id?has_content> + id="${attributes.id}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> -<#if parameters.title?has_content> - title="${parameters.title}" +<#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> +<#if attributes.title?has_content> + title="${attributes.title}" </#if> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> /> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/textarea.ftl b/core/src/main/resources/template/html5/textarea.ftl index 4fc24f322..9866b72c8 100644 --- a/core/src/main/resources/template/html5/textarea.ftl +++ b/core/src/main/resources/template/html5/textarea.ftl @@ -18,48 +18,48 @@ * under the License. */ --> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlheader.ftl" /> <#compress> <textarea - name="${(parameters.name!"")}" -<#if parameters.cols?has_content> - cols="${(parameters.cols!"")}" + name="${(attributes.name!"")}" +<#if attributes.cols?has_content> + cols="${(attributes.cols!"")}" </#if> - <#if parameters.rows?has_content> - rows="${(parameters.rows!"")}" + <#if attributes.rows?has_content> + rows="${(attributes.rows!"")}" </#if> - <#if parameters.wrap?has_content> - wrap="${parameters.wrap}" + <#if attributes.wrap?has_content> + wrap="${attributes.wrap}" </#if> - <#if parameters.disabled!false> + <#if attributes.disabled!false> disabled="disabled" </#if> - <#if parameters.readonly!false> + <#if attributes.readonly!false> readonly="readonly" </#if> - <#if parameters.tabindex?has_content> - tabindex="${parameters.tabindex}" + <#if attributes.tabindex?has_content> + tabindex="${attributes.tabindex}" </#if> - <#if parameters.id?has_content> - id="${parameters.id}" + <#if attributes.id?has_content> + id="${attributes.id}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" /> - <#if parameters.title?has_content> - title="${parameters.title}" + <#include "/${attributes.templateDir}/${attributes.expandTheme}/css.ftl" /> + <#if attributes.title?has_content> + title="${attributes.title}" </#if> - <#if parameters.maxlength?has_content> - maxlength="${parameters.maxlength}" + <#if attributes.maxlength?has_content> + maxlength="${attributes.maxlength}" </#if> - <#if parameters.minlength?has_content> - minlength="${parameters.minlength}" + <#if attributes.minlength?has_content> + minlength="${attributes.minlength}" </#if> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" /> - <#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/scripting-events.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/common-attributes.ftl" /> + <#include "/${attributes.templateDir}/${attributes.expandTheme}/dynamic-attributes.ftl" /> > -<#if parameters.nameValue??> - <@s.property value="parameters.nameValue"/> +<#if attributes.nameValue??> + <@s.property value="attributes.nameValue"/> </#if> </textarea> </#compress> -<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> +<#include "/${attributes.templateDir}/${attributes.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/main/resources/template/html5/theme.properties b/core/src/main/resources/template/html5/theme.properties new file mode 100644 index 000000000..6625ab56f --- /dev/null +++ b/core/src/main/resources/template/html5/theme.properties @@ -0,0 +1,3 @@ +# HTML5 Theme Configuration +# Inherits from the simple theme for base functionality +parent = simple diff --git a/core/src/main/resources/template/html5/token.ftl b/core/src/main/resources/template/html5/token.ftl index d063bbbb5..1ecc66ec3 100644 --- a/core/src/main/resources/template/html5/token.ftl +++ b/core/src/main/resources/template/html5/token.ftl @@ -18,5 +18,5 @@ * under the License. */ --> -<input type="hidden" name="${parameters.tokenNameField!""}" value="${parameters.name!""}" /> -<input type="hidden" name="${parameters.name!""}" value="${parameters.token!""}" /> \ No newline at end of file +<input type="hidden" name="${attributes.tokenNameField!""}" value="${attributes.name!""}" /> +<input type="hidden" name="${attributes.name!""}" value="${attributes.token!""}" /> \ No newline at end of file diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java index 0ea404aa1..050dbdbb1 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxListTest.java @@ -59,6 +59,19 @@ public class CheckboxListTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", new String[]{"tabindex","cssClass","cssStyle","id"}); } + public void testGenericHtml5() throws Exception { + CheckboxListTag tag = new CheckboxListTag(); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"tabindex","cssClass","cssStyle","id"}); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + CheckboxListTag tag = new CheckboxListTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"tabindex","cssClass","cssStyle","id"}); + } + private void prepareTagGeneric(CheckboxListTag tag) { TestAction testAction = (TestAction) action; Collection<String> collection = new ArrayList<String>(2); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java index e9375ae42..e7da044ca 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java @@ -53,6 +53,17 @@ public class CheckboxTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", null); } + public void testGenericHtml5() throws Exception { + CheckboxTag tag = new CheckboxTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + CheckboxTag tag = new CheckboxTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } + public void testChecked() throws Exception { TestAction testAction = (TestAction) action; testAction.setFoo("true"); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java index e21ca40bc..03721c9d0 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java @@ -44,6 +44,19 @@ public class ComboBoxTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", null); } + public void testGenericHtml5() throws Exception { + ComboBoxTag tag = new ComboBoxTag(); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + ComboBoxTag tag = new ComboBoxTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", null); + } + private void prepareTagGeneric(ComboBoxTag tag) { TestAction testAction = (TestAction) action; ArrayList collection = new ArrayList(); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java index d5a296c7f..301d6c5c5 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java @@ -113,4 +113,15 @@ public class FileTest extends AbstractUITagTest { FileTag tag = new FileTag(); verifyGenericProperties(tag, "xhtml", null); } + + public void testGenericHtml5() throws Exception { + FileTag tag = new FileTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + FileTag tag = new FileTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java index 7b8044c31..5363ed145 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/HiddenTest.java @@ -280,4 +280,15 @@ public class HiddenTest extends AbstractUITagTest { HiddenTag tag = new HiddenTag(); verifyGenericProperties(tag, "xhtml", null); } + + public void testGenericHtml5() throws Exception { + HiddenTag tag = new HiddenTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + HiddenTag tag = new HiddenTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java index 7bc5a7c54..0409695bc 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/LabelTest.java @@ -209,6 +209,17 @@ public class LabelTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", null); } + public void testGenericHtml5() throws Exception { + LabelTag tag = new LabelTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + LabelTag tag = new LabelTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } + public void testWithKeyNoValueFromStack() throws Exception { TestAction testAction = (TestAction) action; final String key = "labelKey"; diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java index a4d5cf236..098bb8ca8 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java @@ -84,4 +84,15 @@ public class PasswordTest extends AbstractUITagTest { PasswordTag tag = new PasswordTag(); verifyGenericProperties(tag, "xhtml", new String[]{"value"}); } + + public void testGenericHtml5() throws Exception { + PasswordTag tag = new PasswordTag(); + verifyGenericProperties(tag, "html5", new String[]{"value"}); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + PasswordTag tag = new PasswordTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", new String[]{"value"}); + } } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java index 47a5caad7..bc86a9909 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java @@ -553,6 +553,19 @@ public class RadioTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", new String[]{"id", "value"}); } + public void testGenericHtml5() throws Exception { + RadioTag tag = new RadioTag(); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"id", "value"}); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + RadioTag tag = new RadioTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"id", "value"}); + } + public void testDynamicAttributes() throws Exception { TestAction testAction = (TestAction) action; testAction.setFoo("bar"); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java index c297b86cc..c5b2091e7 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/ResetTest.java @@ -584,4 +584,15 @@ public class ResetTest extends AbstractUITagTest { ResetTag tag = new ResetTag(); verifyGenericProperties(tag, "xhtml", null); } + + public void testGenericHtml5() throws Exception { + ResetTag tag = new ResetTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + ResetTag tag = new ResetTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java index a087da478..91575a3cf 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java @@ -1059,6 +1059,19 @@ public class SelectTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", new String[]{"value"}); } + public void testGenericHtml5() throws Exception { + SelectTag tag = new SelectTag(); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"value"}); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + SelectTag tag = new SelectTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + prepareTagGeneric(tag); + verifyGenericProperties(tag, "html5", new String[]{"value"}); + } + public void testMultipleOn() throws Exception { SelectTag tag = new SelectTag(); tag.setPageContext(pageContext); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java index e5c8299d2..1f44ffc6b 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java @@ -649,6 +649,17 @@ public class SubmitTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", null); } + public void testGenericHtml5() throws Exception { + SubmitTag tag = new SubmitTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + SubmitTag tag = new SubmitTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } + /** * Test that by default submit tag body is HTML-escaped. */ diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java index c83086255..5fa79095f 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java @@ -152,4 +152,15 @@ public class TextareaTest extends AbstractUITagTest { TextareaTag tag = new TextareaTag(); verifyGenericProperties(tag, "xhtml", new String[] {"value"}); } + + public void testGenericHtml5() throws Exception { + TextareaTag tag = new TextareaTag(); + verifyGenericProperties(tag, "html5", new String[] {"value"}); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + TextareaTag tag = new TextareaTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", new String[] {"value"}); + } } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java index ad28ee4bd..fc2dfa8a7 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java @@ -59,6 +59,17 @@ public class TextfieldTest extends AbstractUITagTest { verifyGenericProperties(tag, "xhtml", null); } + public void testGenericHtml5() throws Exception { + TextFieldTag tag = new TextFieldTag(); + verifyGenericProperties(tag, "html5", null); + } + + public void testGenericHtml5_clearTagStateSet() throws Exception { + TextFieldTag tag = new TextFieldTag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); + } + public void testErrors() throws Exception { TestAction testAction = (TestAction) action; testAction.setFoo("bar"); diff --git a/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md b/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md new file mode 100644 index 000000000..9c2408d4e --- /dev/null +++ b/thoughts/shared/research/2025-11-23-WW-5444-html5-theme-testing.md @@ -0,0 +1,518 @@ +--- +date: 2025-11-23T17:48:30+01:00 +topic: "HTML5 Theme Implementation and Testing Approach" +tags: [research, html5-theme, testing, ui-tags, themes, WW-5444] +status: complete +git_commit: 461c06d0c5afde260fdb0f32038a32f2af5bb42e +git_branch: feature/WW-5444-html5 +--- + +# Research: HTML5 Theme Implementation and Testing Approach + +**Date**: 2025-11-23T17:48:30+01:00 +**Branch**: feature/WW-5444-html5 +**Jira**: [WW-5444](https://issues.apache.org/jira/browse/WW-5444) + +## Research Question + +What testing is needed for the HTML5 theme implementation in Apache Struts, and how should it be approached based on existing theme testing patterns? + +## Summary + +The HTML5 theme has been implemented with 45 FreeMarker templates in the showcase application for easier manual testing. Analysis of existing theme testing patterns reveals a well-established infrastructure in `AbstractUITagTest` that should be replicated for html5 theme validation. The theme currently lives in `apps/showcase` but should eventually move to `core/` for production distribution. + +**Key Findings:** +- HTML5 theme has 45 templates but NO unit tests yet +- Existing themes (simple, xhtml, css_xhtml) use `verifyGenericProperties()` for testing +- Test pattern is straightforward: add `testGenericHtml5()` to each UI tag test class +- Production themes belong in `core/src/main/resources/template/`, not `apps/showcase` +- No historical documentation exists in thoughts/ directory about theme testing + +## Detailed Findings + +### Current HTML5 Theme Implementation + +**Location**: `apps/showcase/src/main/resources/template/html5/` + +**45 FreeMarker Templates:** +- Form elements: `form.ftl`, `form-close.ftl`, `text.ftl`, `textarea.ftl`, `password.ftl`, `checkbox.ftl`, `checkboxlist.ftl`, `radiomap.ftl`, `select.ftl`, `file.ftl`, `hidden.ftl`, `token.ftl` +- Complex components: `combobox.ftl`, `doubleselect.ftl`, `inputtransferselect.ftl`, `optiontransferselect.ftl`, `updownselect.ftl`, `optgroup.ftl` +- Display components: `actionerror.ftl`, `actionmessage.ftl`, `fielderror.ftl`, `label.ftl` +- Navigation: `a.ftl`, `a-close.ftl`, `link.ftl`, `submit.ftl`, `submit-close.ftl`, `reset.ftl` +- Utilities: `head.ftl`, `script.ftl`, `script-close.ftl`, `debug.ftl`, `css.ftl`, `nonce.ftl`, `datetextfield.ftl` +- Structural: `controlheader.ftl`, `controlfooter.ftl`, `empty.ftl` +- Attribute handling: `common-attributes.ftl`, `dynamic-attributes.ftl`, `prefixed-dynamic-attributes.ftl`, `scripting-events.ftl` + +**Supporting Files:** +- Java: `apps/showcase/src/main/java/org/apache/struts2/showcase/action/Html5Action.java` +- JSP: `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp` +- Config: `apps/showcase/src/main/resources/struts.xml` (lines 163-167) + +**Key Features:** +- Clean HTML5 markup without table-based layouts +- Empty control headers/footers (no wrapping divs) +- Support for HTML5 input types via TextField component +- Bootstrap CSS integration +- CSP nonce attribute support + +### Theme Testing Infrastructure + +**Core Testing Class**: `core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java` + +This class provides the foundation for all UI tag testing with two primary testing approaches: + +#### 1. Generic Property Testing + +**Method**: `verifyGenericProperties(AbstractUITag tag, String theme, String[] exclude)` + +Tests that all standard HTML attributes render correctly for a given theme: +- Sets 15+ generic properties (id, name, cssClass, cssStyle, title, disabled, tabindex, value, onclick, ondblclick, etc.) +- Adds dynamic attribute test (`data-id="id-random"`) +- Renders tag with specified theme +- Verifies each property appears correctly in HTML output +- Checks for FreeMarker template errors + +**Property Test Coverage** (Lines 110-133): +```java +protected Map<String, PropertyHolder> initializedGenericTagTestProperties() { + Map<String, PropertyHolder> result = new HashMap<>(); + new PropertyHolder("name", "someName").addToMap(result); + new PropertyHolder("id", "someId").addToMap(result); + new PropertyHolder("cssClass", "cssClass1", "class=\"cssClass1\"").addToMap(result); + new PropertyHolder("cssStyle", "cssStyle1", "style=\"cssStyle1\"").addToMap(result); + new PropertyHolder("title", "someTitle").addToMap(result); + new PropertyHolder("disabled", "true", "disabled=\"disabled\"").addToMap(result); + new PropertyHolder("tabindex", "99").addToMap(result); + new PropertyHolder("value", "someValue").addToMap(result); + // ... event handlers (onclick, ondblclick, onmousedown, etc.) + return result; +} +``` + +#### 2. Specific Output Testing + +**Method**: `verify(URL url)` + +Compares rendered output against expected `.txt` files: +- Loads expected HTML from resource file +- Normalizes both actual and expected output (removes whitespace differences) +- Performs exact string comparison +- Provides detailed error messages on mismatch + +**Expected File Location**: `core/src/test/resources/org/apache/struts2/views/jsp/ui/{TagName}-{N}.txt` + +### Existing Theme Test Patterns + +**Simple Theme Tests** (from TextfieldTest.java): +```java +public void testGenericSimple() throws Exception { + TextFieldTag tag = new TextFieldTag(); + verifyGenericProperties(tag, "simple", null); +} +``` + +**XHTML Theme Tests**: +```java +public void testGenericXhtml() throws Exception { + TextFieldTag tag = new TextFieldTag(); + verifyGenericProperties(tag, "xhtml", null); +} +``` + +**With Property Exclusions** (RadioTest.java): +```java +public void testGenericSimple() throws Exception { + RadioTag tag = new RadioTag(); + verifyGenericProperties(tag, "simple", new String[]{"id", "value"}); + // Excludes "id" and "value" as radio buttons handle them differently +} +``` + +### Theme Template Location Patterns + +**Production Themes** → `core/src/main/resources/template/` +- **simple**: 47 templates (base theme, no parent) +- **xhtml**: 30 templates (parent = simple) +- **css_xhtml**: 13 templates (parent = xhtml) + +**Demo/Showcase Themes** → `apps/showcase/src/main/resources/template/` +- **html5**: 45 templates (currently here) +- **ajaxErrorContainers**: 4 templates + +**Theme Inheritance Pattern**: +``` +simple (root) + ↓ +xhtml (parent = simple) + ↓ +css_xhtml (parent = xhtml) +``` + +Defined via `theme.properties` with `parent = <parent-theme>` property. + +**Key Pattern Rule**: Production-ready themes MUST be in `core/` to be packaged with struts2-core.jar and available to all applications. + +### Integration Testing Pattern + +**Example from Showcase**: `apps/showcase/src/test/java/it/org/apache/struts2/showcase/UITagExampleTest.java` + +Uses HtmlUnit to test complete form workflows: +```java +@Test +public void testInputForm() throws Exception { + try (final WebClient webClient = new WebClient()) { + final HtmlPage page = webClient.getPage( + ParameterUtils.getBaseUrl() + "/tags/ui/example!input.action" + ); + + final HtmlForm form = page.getFormByName("exampleSubmit"); + final HtmlTextInput textField = form.getInputByName("name"); + // ... interact with form elements + + final HtmlSubmitInput button = form.getInputByValue("Submit"); + final HtmlPage page2 = button.click(); + + // Verify results + Assert.assertEquals("name", page2.getElementById("name").asNormalizedText()); + } +} +``` + +## Code References + +### HTML5 Theme Implementation +- `apps/showcase/src/main/resources/template/html5/*.ftl` - All 45 FreeMarker templates +- `apps/showcase/src/main/java/org/apache/struts2/showcase/action/Html5Action.java` - Demo action +- `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp` - Demo page +- `apps/showcase/src/main/resources/struts.xml:163-167` - Configuration + +### Testing Infrastructure +- `core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:155-174` - verifyGenericProperties() method +- `core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:110-133` - Generic property definitions +- `core/src/test/java/org/apache/struts2/views/jsp/AbstractUITagTest.java:201-230` - verify() method for output comparison +- `core/src/test/resources/org/apache/struts2/views/jsp/ui/` - Expected output files + +### Example Test Classes +- `core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java` - Comprehensive test examples +- `core/src/test/java/org/apache/struts2/views/jsp/ui/PasswordTest.java:78-86` - Theme test pattern +- `core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java` - Checkbox-specific tests +- `core/src/test/java/org/apache/struts2/views/jsp/ui/SelectTest.java` - Select/dropdown tests + +### Integration Tests +- `apps/showcase/src/test/java/it/org/apache/struts2/showcase/UITagExampleTest.java` - HtmlUnit integration test example + +## Testing Recommendations + +### Priority 1: Unit Tests for Generic Properties (High Priority) + +Add `testGenericHtml5()` method to each UI tag test class: + +**Files to modify** (in `core/src/test/java/org/apache/struts2/views/jsp/ui/`): +1. `TextfieldTest.java` +2. `TextareaTest.java` +3. `PasswordTest.java` +4. `CheckboxTest.java` +5. `CheckboxListTest.java` +6. `RadioTest.java` +7. `SelectTest.java` +8. `FileTest.java` +9. `HiddenTest.java` +10. `LabelTest.java` +11. `FormTagTest.java` +12. `SubmitTest.java` +13. `ResetTest.java` +14. `AnchorTagTest.java` +15. `ActionErrorTagTest.java` +16. `ActionMessageTagTest.java` +17. `FieldErrorTagTest.java` +18. `DoubleSelectTest.java` +19. `ComboBoxTest.java` +20. `UpDownSelectTagTest.java` +21. `InputTransferSelectTagTest.java` +22. `OptionTransferSelectTagTest.java` + +**Pattern to add**: +```java +public void testGenericHtml5() throws Exception { + <TagName>Tag tag = new <TagName>Tag(); + verifyGenericProperties(tag, "html5", null); +} + +public void testGenericHtml5_clearTagStateSet() throws Exception { + <TagName>Tag tag = new <TagName>Tag(); + tag.setPerformClearTagStateForTagPoolingServers(true); + verifyGenericProperties(tag, "html5", null); +} +``` + +**Exclusions to consider**: +- Radio/Checkbox: Exclude "id" and "value" (handled differently) +- Hidden: Exclude properties that don't apply to hidden fields +- Token: Minimal properties apply + +**Effort**: ~2-3 lines per test class × 22 classes = **minimal implementation effort** + +**Benefit**: Ensures all 45 templates render correctly with standard HTML attributes + +### Priority 2: Integration Tests in Showcase (Medium Priority) + +**Create**: `apps/showcase/src/test/java/it/org/apache/struts2/showcase/Html5TagExampleTest.java` + +**Test Coverage**: +1. Basic form rendering with html5 theme +2. All input types render correctly +3. Error messages display properly (actionerror, actionmessage, fielderror) +4. Dynamic attributes work (data-*, aria-*) +5. Submit workflow completes successfully +6. No table-based layout in output + +**Example structure**: +```java +public class Html5TagExampleTest { + @Test + public void testHtml5FormRendering() throws Exception { + try (final WebClient webClient = new WebClient()) { + final HtmlPage page = webClient.getPage( + ParameterUtils.getBaseUrl() + "/html5/index.action" + ); + + // Verify clean HTML5 markup + assertFalse(page.asXml().contains("<table")); + + // Test error display + assertNotNull(page.getElementById("actionErrors")); + + // Verify Bootstrap compatibility + assertTrue(page.asXml().contains("class=\"")); + } + } +} +``` + +**Effort**: 1-2 integration test methods + +**Benefit**: End-to-end validation that theme works in real application context + +### Priority 3: Expand Showcase Demo (Medium Priority) + +**Enhance**: `apps/showcase/src/main/webapp/WEB-INF/html5/index.jsp` + +**Add demonstrations for**: +1. All form input types (text, password, checkbox, radio, select, textarea, file, hidden) +2. Complex components (doubleselect, combobox, updownselect, etc.) +3. Error handling scenarios (fielderror, actionerror, actionmessage) +4. Dynamic attributes (data-*, aria-*) +5. HTML5 input types (email, url, number, date, etc.) +6. Submit/reset buttons +7. Links and anchors +8. Label positioning + +**Benefit**: Visual validation during development, documentation for users + +### Priority 4: Template Relocation Decision (High Priority) + +**Question**: Should html5 theme move from `apps/showcase` to `core`? + +**Options**: + +**Option A: Move to core/** (Recommended for production theme) +- **Location**: `core/src/main/resources/template/html5/` +- **Pros**: + - Available to all Struts applications + - Shipped with struts2-core.jar + - Consistent with simple/xhtml/css_xhtml +- **Cons**: + - Commits to maintaining it + - Increases core artifact size slightly + +**Option B: Keep in showcase** (Recommended for experimental theme) +- **Location**: `apps/showcase/src/main/resources/template/html5/` +- **Pros**: + - Easy to experiment and iterate + - Doesn't affect core framework + - Users can copy templates to their apps +- **Cons**: + - Not available in struts2-core.jar + - Users must manually add templates + - Inconsistent with other themes + +**Recommendation**: If html5 theme is meant to be production-ready and recommended for new applications, move to `core/`. If it's experimental or an example, keep in showcase. + +**Implementation**: If moving to core, also move: +- Templates: `apps/showcase/src/main/resources/template/html5/` → `core/src/main/resources/template/html5/` +- Tests: Create unit tests in `core/src/test/java/org/apache/struts2/views/jsp/ui/*Test.java` +- Update showcase to use core's html5 theme instead of local copy + +### Priority 5: HTML5 Validation Testing (Low-Medium Priority) + +**Tools to use**: +- W3C HTML5 Validator (via API or library) +- HtmlUnit for semantic validation +- Accessibility checkers (ARIA attributes, semantic tags) + +**What to validate**: +1. Valid HTML5 markup (no deprecated elements) +2. Proper attribute handling (data-*, aria-*, etc.) +3. Semantic HTML structure where appropriate +4. No table-based layouts (unless data tables) +5. Accessibility compliance (labels, ARIA) + +**Example test**: +```java +@Test +public void testHtml5ValidMarkup() throws Exception { + // Render form with html5 theme + FormTag form = new FormTag(); + form.setTheme("html5"); + // ... configure + form.doStartTag(); + form.doEndTag(); + + String output = writer.toString(); + + // Verify no table layout + assertFalse(output.contains("<table")); + assertFalse(output.contains("class=\"tdLabel\"")); + + // Verify HTML5 elements + assertTrue(output.matches(".*<form[^>]*>.*")); + + // No deprecated attributes + assertFalse(output.contains("align=")); + assertFalse(output.contains("border=")); +} +``` + +### Priority 6: Documentation (Medium Priority) + +**Create**: +1. **User Guide**: How to use html5 theme in applications + - Migration guide from xhtml/simple to html5 + - Bootstrap CSS integration examples + - HTML5 input type usage + +2. **Developer Guide**: How html5 theme works + - Template structure + - Differences from xhtml theme + - Customization points + +3. **Testing Guide**: How to test themes + - Unit test pattern + - Integration test approach + - Expected output file creation + +**Location**: Consider adding to `core/src/site/` for Maven site generation + +## Architecture Insights + +### Theme Testing Architecture + +The Struts theme testing architecture demonstrates several key design patterns: + +1. **Template Method Pattern**: `AbstractUITagTest` provides template methods like `verifyGenericProperties()` that subclasses call with theme-specific parameters + +2. **Resource-Based Verification**: Expected output files as resources allow platform-independent testing without hardcoded strings in test code + +3. **Normalization Strategy**: Whitespace normalization makes tests resilient to formatting differences while still catching real HTML changes + +4. **Property Holder Pattern**: Encapsulates test data (name, value, expectation) for declarative property testing + +5. **Theme Inheritance**: FreeMarker template resolution with fallback supports theme hierarchies (css_xhtml → xhtml → simple) + +### HTML5 Theme Design Decisions + +1. **No Control Wrappers**: Empty `controlheader.ftl` and `controlfooter.ftl` provide clean HTML without extra divs, unlike xhtml theme's table-based layout + +2. **Bootstrap Compatibility**: Clean markup designed to work with Bootstrap CSS classes without framework-specific markup + +3. **HTML5 Input Types**: TextField component supports HTML5 input types (email, url, number, etc.) via type attribute + +4. **CSP Support**: Includes `nonce.ftl` for Content Security Policy nonce attribute handling + +5. **Dynamic Attributes**: Full support for data-* and aria-* attributes via `dynamic-attributes.ftl` + +### Testing Infrastructure Design + +1. **Separation of Concerns**: Generic property tests separate from specific behavior tests + +2. **Reusable Test Infrastructure**: `AbstractUITagTest` provides shared testing utilities for all UI tag tests + +3. **Theme Parameterization**: Same test can validate multiple themes by changing theme parameter + +4. **Exclusion Mechanism**: Allows tags to exclude properties that don't apply (e.g., hidden field doesn't render visible properties) + +5. **Integration with Maven**: Test resources use classpath resolution for portability + +## Open Questions + +1. **Production Readiness**: Is html5 theme ready for production (core/) or still experimental (showcase/)? + +2. **Theme Properties**: Should html5 theme have a `theme.properties` file defining inheritance (e.g., `parent = simple`)? + +3. **Default Theme**: Should html5 become the default theme for new Struts applications (replacing xhtml)? + +4. **Backward Compatibility**: How to handle applications migrating from xhtml to html5 theme? + +5. **CSS Framework Integration**: Should html5 theme include optional CSS for Bootstrap, Tailwind, or remain unopinionated? + +6. **Component Coverage**: Are all 45 templates necessary, or should some inherit from simple theme? + +7. **Form Layout**: Should html5 theme include layout options (inline, horizontal, vertical) like Bootstrap forms? + +8. **Error Positioning**: How should field errors be positioned in html5 theme compared to xhtml theme's table-based approach? + +## Next Steps + +### Immediate Actions (Before PR) + +1. **Add unit tests**: Implement `testGenericHtml5()` in all UI tag test classes +2. **Run test suite**: Execute `mvn test -DskipAssembly` to verify all tests pass +3. **Decide location**: Determine if html5 theme should move to core/ or stay in showcase/ +4. **Create integration test**: Add `Html5TagExampleTest.java` for end-to-end validation + +### Short-term Actions (Before Release) + +1. **Expand showcase demo**: Add comprehensive examples to index.jsp +2. **HTML5 validation**: Verify all templates produce valid HTML5 markup +3. **Documentation**: Create user guide for html5 theme usage +4. **Performance testing**: Compare rendering performance vs xhtml theme + +### Long-term Considerations + +1. **Default theme discussion**: Consider making html5 the recommended/default theme +2. **Migration tooling**: Create utilities to help migrate from xhtml to html5 +3. **CSS framework integration**: Evaluate official integrations with popular CSS frameworks +4. **Accessibility audit**: Ensure WCAG compliance for all components + +## Testing Effort Estimation + +**Unit Tests**: +- 22 test classes × 2 methods each = 44 test methods +- ~2-3 lines per method +- **Effort**: 2-3 hours + +**Integration Tests**: +- 1 test class with 3-5 test methods +- **Effort**: 2-4 hours + +**Expected Output Files** (if needed for specific tests): +- 0 files needed for generic property tests +- Optional for specific behavior tests +- **Effort**: Variable, only if specific behavior needs validation + +**Total Estimated Effort**: **4-7 hours** for comprehensive test coverage + +## References + +### Jira Issues +- [WW-5444](https://issues.apache.org/jira/browse/WW-5444) - Implement a new html5 theme + +### Related Code +- Theme rendering: `core/src/main/java/org/apache/struts2/components/UIBean.java` +- Template engine: `core/src/main/java/org/apache/struts2/components/template/` +- FreeMarker templates: `core/src/main/resources/template/` + +### Maven Commands +- Run tests: `mvn test -DskipAssembly` +- Build showcase: `mvn clean install` (from apps/showcase/) +- Full build: `mvn clean install` (from project root)
