mirror of
https://github.com/apache/struts.git
synced 2026-08-06 15:17:00 +00:00
WW-600 Enables Client-side validation for visitor validations
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1485978 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -31,16 +31,22 @@ import com.opensymphony.xwork2.interceptor.MethodFilterInterceptorUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.validator.ActionValidatorManager;
|
||||
import com.opensymphony.xwork2.validator.FieldValidator;
|
||||
import com.opensymphony.xwork2.validator.ValidationException;
|
||||
import com.opensymphony.xwork2.validator.ValidationInterceptor;
|
||||
import com.opensymphony.xwork2.validator.Validator;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import com.opensymphony.xwork2.validator.ValidatorContext;
|
||||
import com.opensymphony.xwork2.validator.validators.VisitorFieldValidator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.views.annotations.StrutsTag;
|
||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -116,14 +122,17 @@ public class Form extends ClosingUIBean {
|
||||
super(stack, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean evaluateNameValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultOpenTemplate() {
|
||||
return OPEN_TEMPLATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultTemplate() {
|
||||
return TEMPLATE;
|
||||
}
|
||||
@@ -153,6 +162,7 @@ public class Form extends ClosingUIBean {
|
||||
* Revised for Portlet actionURL as form action, and add wwAction as hidden
|
||||
* field. Refer to template.simple/form.vm
|
||||
*/
|
||||
@Override
|
||||
protected void evaluateExtraParams() {
|
||||
super.evaluateExtraParams();
|
||||
if (validate != null) {
|
||||
@@ -210,6 +220,7 @@ public class Form extends ClosingUIBean {
|
||||
* <li>if an 'action' attribute is specified, it will be used as the id.</li>
|
||||
* </ol>
|
||||
*/
|
||||
@Override
|
||||
protected void populateComponentHtmlId(Form form) {
|
||||
if (id != null) {
|
||||
addParameter("id", escape(id));
|
||||
@@ -262,20 +273,144 @@ public class Form extends ClosingUIBean {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
List<Validator> all = actionValidatorManager.getValidators(actionClass, (String) getParameters().get("actionName"));
|
||||
String formActionValue = findString(action);
|
||||
ActionMapping mapping = actionMapper.getMappingFromActionName(formActionValue);
|
||||
String actionName = mapping.getName();
|
||||
String methodName = mapping.getMethod();
|
||||
|
||||
List<Validator> actionValidators = actionValidatorManager.getValidators(actionClass, actionName, methodName);
|
||||
List<Validator> validators = new ArrayList<Validator>();
|
||||
for (Validator validator : all) {
|
||||
if (validator instanceof FieldValidator) {
|
||||
FieldValidator fieldValidator = (FieldValidator) validator;
|
||||
if (fieldValidator.getFieldName().equals(name)) {
|
||||
validators.add(fieldValidator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findFieldValidators(name, actionClass, actionName, actionValidators, validators, "");
|
||||
|
||||
return validators;
|
||||
}
|
||||
|
||||
private void findFieldValidators(String name, Class actionClass, String actionName,
|
||||
List<Validator> validatorList, List<Validator> retultValidators, String prefix) {
|
||||
|
||||
for (Validator validator : validatorList) {
|
||||
if (validator instanceof FieldValidator) {
|
||||
FieldValidator fieldValidator = (FieldValidator) validator;
|
||||
|
||||
if (validator instanceof VisitorFieldValidator) {
|
||||
VisitorFieldValidator vfValidator = (VisitorFieldValidator) fieldValidator;
|
||||
Class clazz = getVisitorReturnType(actionClass, vfValidator.getFieldName());
|
||||
if (clazz == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Validator> visitorValidators = actionValidatorManager.getValidators(clazz, actionName);
|
||||
String vPrefix = prefix + (vfValidator.isAppendPrefix() ? vfValidator.getFieldName() + "." : "");
|
||||
findFieldValidators(name, clazz, actionName, visitorValidators, retultValidators, vPrefix);
|
||||
} else if ((prefix + fieldValidator.getFieldName()).equals(name)) {
|
||||
if (StringUtils.isNotBlank(prefix)) {
|
||||
//fixing field name for js side
|
||||
FieldVisitorValidatorWrapper wrap = new FieldVisitorValidatorWrapper(fieldValidator, prefix);
|
||||
retultValidators.add(wrap);
|
||||
} else {
|
||||
retultValidators.add(fieldValidator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap field validator, add visitor's field prefix to the field name.
|
||||
* Javascript side is not aware of the visitor validators
|
||||
* and does not know how to prefix the fields.
|
||||
*/
|
||||
/*
|
||||
* Class is public because Freemarker has problems accessing properties.
|
||||
*/
|
||||
public static class FieldVisitorValidatorWrapper implements FieldValidator {
|
||||
private FieldValidator fieldValidator;
|
||||
private String namePrefix;
|
||||
public FieldVisitorValidatorWrapper(FieldValidator fv, String namePrefix) {
|
||||
this.fieldValidator = fv;
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
public String getValidatorType() {
|
||||
return "field-visitor";
|
||||
}
|
||||
public String getFieldName() {
|
||||
return namePrefix + fieldValidator.getFieldName();
|
||||
}
|
||||
public FieldValidator getFieldValidator() {
|
||||
return fieldValidator;
|
||||
}
|
||||
public void setFieldValidator(FieldValidator fieldValidator) {
|
||||
this.fieldValidator = fieldValidator;
|
||||
}
|
||||
public String getDefaultMessage() {
|
||||
return fieldValidator.getDefaultMessage();
|
||||
}
|
||||
public String getMessage(Object object) {
|
||||
return fieldValidator.getMessage(object);
|
||||
}
|
||||
public String getMessageKey() {
|
||||
return fieldValidator.getMessageKey();
|
||||
}
|
||||
public String[] getMessageParameters() {
|
||||
return fieldValidator.getMessageParameters();
|
||||
}
|
||||
public ValidatorContext getValidatorContext() {
|
||||
return fieldValidator.getValidatorContext();
|
||||
}
|
||||
public void setDefaultMessage(String message) {
|
||||
fieldValidator.setDefaultMessage(message);
|
||||
}
|
||||
public void setFieldName(String fieldName) {
|
||||
fieldValidator.setFieldName(fieldName);
|
||||
}
|
||||
public void setMessageKey(String key) {
|
||||
fieldValidator.setMessageKey(key);
|
||||
}
|
||||
public void setMessageParameters(String[] messageParameters) {
|
||||
fieldValidator.setMessageParameters(messageParameters);
|
||||
}
|
||||
public void setValidatorContext(ValidatorContext validatorContext) {
|
||||
fieldValidator.setValidatorContext(validatorContext);
|
||||
}
|
||||
public void setValidatorType(String type) {
|
||||
fieldValidator.setValidatorType(type);
|
||||
}
|
||||
public void setValueStack(ValueStack stack) {
|
||||
fieldValidator.setValueStack(stack);
|
||||
}
|
||||
public void validate(Object object) throws ValidationException {
|
||||
fieldValidator.validate(object);
|
||||
}
|
||||
public String getNamePrefix() {
|
||||
return namePrefix;
|
||||
}
|
||||
public void setNamePrefix(String namePrefix) {
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type of visited object.
|
||||
* @param actionClass
|
||||
* @param visitorFieldName
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class getVisitorReturnType(Class actionClass, String visitorFieldName) {
|
||||
if (visitorFieldName == null) {
|
||||
return null;
|
||||
}
|
||||
String methodName = "get" + org.apache.commons.lang.StringUtils.capitalize(visitorFieldName);
|
||||
try {
|
||||
Method method = actionClass.getMethod(methodName, new Class[0]);
|
||||
return method.getReturnType();
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a incrementing sequence unique to this <code>Form</code> component.
|
||||
* It is used by <code>Form</code> component's child that might need a
|
||||
|
||||
@@ -36,6 +36,26 @@ END SNIPPET: supported-validators
|
||||
<#if ((parameters.validate!false == true) && (parameters.performValidation!false == true))>
|
||||
<script type="text/javascript">
|
||||
function validateForm_${parameters.id?replace('[^a-zA-Z0-9_]', '_', 'r')}() {
|
||||
<#--
|
||||
In case of multiselect fields return only the first value.
|
||||
-->
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
if (type == 'select-one' || type == 'select-multiple') {
|
||||
return (field.selectedIndex == -1 ? "" : field.options[field.selectedIndex].value);
|
||||
} else if (type == 'checkbox' || type == 'radio') {
|
||||
if (!field.length) {
|
||||
field = [field];
|
||||
}
|
||||
for (var i = 0; i < field.length; i++) {
|
||||
if (field[i].checked) {
|
||||
return field[i].value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
form = document.getElementById("${parameters.id}");
|
||||
clearErrorMessages(form);
|
||||
clearErrorLabels(form);
|
||||
@@ -43,27 +63,36 @@ END SNIPPET: supported-validators
|
||||
var errors = false;
|
||||
var continueValidation = true;
|
||||
<#list parameters.tagNames as tagName>
|
||||
<#list tag.getValidators("${tagName}") as validator>
|
||||
// field name: ${validator.fieldName}
|
||||
// validator name: ${validator.validatorType}
|
||||
if (form.elements['${validator.fieldName}']) {
|
||||
field = form.elements['${validator.fieldName}'];
|
||||
<#list tag.getValidators("${tagName}") as aValidator>
|
||||
// field name: ${aValidator.fieldName}
|
||||
// validator name: ${aValidator.validatorType}
|
||||
if (form.elements['${aValidator.fieldName}']) {
|
||||
field = form.elements['${aValidator.fieldName}'];
|
||||
<#if aValidator.validatorType = "field-visitor">
|
||||
<#assign validator = aValidator.fieldValidator >
|
||||
//visitor validator switched to: ${validator.validatorType}
|
||||
<#else>
|
||||
<#assign validator = aValidator >
|
||||
</#if>
|
||||
|
||||
var error = "${validator.getMessage(action)?js_string}";
|
||||
var fieldValue = getFieldValue(field);
|
||||
|
||||
<#if validator.validatorType = "required">
|
||||
if (field.value == "") {
|
||||
if (fieldValue == "") {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
<#if validator.shortCircuit>continueValidation = false;</#if>
|
||||
}
|
||||
<#elseif validator.validatorType = "requiredstring">
|
||||
if (continueValidation && field.value != null && (field.value == "" || field.value.replace(/^\s+|\s+$/g,"").length == 0)) {
|
||||
if (continueValidation && fieldValue != null && (fieldValue == "" || fieldValue.replace(/^\s+|\s+$/g,"").length == 0)) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
<#if validator.shortCircuit>continueValidation = false;</#if>
|
||||
}
|
||||
<#elseif validator.validatorType = "stringlength">
|
||||
if (continueValidation && field.value != null) {
|
||||
var value = field.value;
|
||||
if (continueValidation && fieldValue != null) {
|
||||
var value = fieldValue;
|
||||
<#if validator.trim>
|
||||
//trim field value
|
||||
while (value.substring(0,1) == ' ')
|
||||
@@ -79,28 +108,28 @@ END SNIPPET: supported-validators
|
||||
}
|
||||
}
|
||||
<#elseif validator.validatorType = "regex">
|
||||
if (continueValidation && field.value != null && !field.value.match("${validator.regex?js_string}")) {
|
||||
if (continueValidation && fieldValue != null && !fieldValue.match("${validator.regex?js_string}")) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
<#if validator.shortCircuit>continueValidation = false;</#if>
|
||||
}
|
||||
<#elseif validator.validatorType = "email">
|
||||
if (continueValidation && field.value != null && field.value.length > 0 && field.value.match("${validator.regex?js_string}")==null) {
|
||||
if (continueValidation && fieldValue != null && fieldValue.length > 0 && fieldValue.match("${validator.regex?js_string}")==null) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
<#if validator.shortCircuit>continueValidation = false;</#if>
|
||||
}
|
||||
<#elseif validator.validatorType = "url">
|
||||
if (continueValidation && field.value != null && field.value.length > 0 && field.value.match(/^(ftp|http|https):\/\/((%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:])+@)?((%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=])+)(:[0-9]+)?((\/(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@])*)*)(\?(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@/?])*)?(#(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@/?])*)?$/gi)==null) {
|
||||
if (continueValidation && fieldValue != null && fieldValue.length > 0 && fieldValue.match("${validator.regex?js_string}")==null) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
<#if validator.shortCircuit>continueValidation = false;</#if>
|
||||
}
|
||||
<#elseif validator.validatorType = "int" || validator.validatorType = "short">
|
||||
if (continueValidation && field.value != null) {
|
||||
if (<#if validator.min??>parseInt(field.value) <
|
||||
if (continueValidation && fieldValue != null) {
|
||||
if (<#if validator.min??>parseInt(fieldValue) <
|
||||
${validator.min?c}<#else>false</#if> ||
|
||||
<#if validator.max??>parseInt(field.value) >
|
||||
<#if validator.max??>parseInt(fieldValue) >
|
||||
${validator.max?c}<#else>false</#if>) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
@@ -108,8 +137,8 @@ END SNIPPET: supported-validators
|
||||
}
|
||||
}
|
||||
<#elseif validator.validatorType = "double">
|
||||
if (continueValidation && field.value != null) {
|
||||
var value = parseFloat(field.value);
|
||||
if (continueValidation && fieldValue != null) {
|
||||
var value = parseFloat(fieldValue);
|
||||
if (<#if validator.minInclusive??>value < ${validator.minInclusive?c}<#else>false</#if> ||
|
||||
<#if validator.maxInclusive??>value > ${validator.maxInclusive?c}<#else>false</#if> ||
|
||||
<#if validator.minExclusive??>value <= ${validator.minExclusive?c}<#else>false</#if> ||
|
||||
@@ -126,4 +155,4 @@ END SNIPPET: supported-validators
|
||||
return !errors;
|
||||
}
|
||||
</script>
|
||||
</#if>
|
||||
</#if>
|
||||
@@ -21,18 +21,17 @@
|
||||
|
||||
package org.apache.struts2.components;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.validator.validators.RequiredFieldValidator;
|
||||
import org.apache.struts2.TestAction;
|
||||
import org.apache.struts2.views.jsp.AbstractUITagTest;
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
import com.opensymphony.xwork2.validator.validators.RequiredFieldValidator;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <code>FormTest</code>
|
||||
@@ -45,6 +44,7 @@ public class FormTest extends AbstractUITagTest {
|
||||
Form form = new Form(stack, request, response);
|
||||
container.inject(form);
|
||||
form.getParameters().put("actionClass", TestAction.class);
|
||||
form.setAction("actionName");
|
||||
List v = form.getValidators("foo");
|
||||
assertEquals(1, v.size());
|
||||
assertEquals(RequiredFieldValidator.class, v.get(0).getClass());
|
||||
|
||||
@@ -32,12 +32,32 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function validateForm_myAction() {
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
if(type == 'select-one' || type == 'select-multiple') {
|
||||
return (field.selectedIndex == -1 ? "" : field.options[field.selectedIndex].value);
|
||||
} else if(type=='checkbox'||type=='radio') {
|
||||
if(!field.length){
|
||||
field=[field];
|
||||
}
|
||||
for(var i=0; i < field.length; i++){
|
||||
if(field[i].checked) {
|
||||
return field[i].value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
form = document.getElementById("myAction");
|
||||
clearErrorMessages(form);
|
||||
clearErrorLabels(form);
|
||||
var errors = false;
|
||||
var continueValidation=true;
|
||||
return !errors; }
|
||||
return !errors;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -33,23 +33,41 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateForm_myAction() {
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
if(type == 'select-one' || type == 'select-multiple') {
|
||||
return (field.selectedIndex == -1 ? "" : field.options[field.selectedIndex].value);
|
||||
} else if(type == 'checkbox' || type == 'radio') {
|
||||
if(!field.length) {
|
||||
field = [field];
|
||||
}
|
||||
for(var i = 0; i < field.length; i++) {
|
||||
if(field[i].checked) {
|
||||
return field[i].value;
|
||||
}
|
||||
}
|
||||
return"";
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
form = document.getElementById("myAction");
|
||||
clearErrorMessages(form);
|
||||
clearErrorLabels(form);
|
||||
var errors = false;
|
||||
var continueValidation = true;
|
||||
//fieldname:myUpDownSelectTag
|
||||
//validatorname:int
|
||||
if(form.elements['myUpDownSelectTag']){
|
||||
field=form.elements['myUpDownSelectTag'];
|
||||
var error="bar must be between 6000 and 10000.";
|
||||
if(continueValidation && field.value!=null){
|
||||
if(parseInt(field.value)<6000||parseInt(field.value)>10000){
|
||||
addError(field,error);
|
||||
errors=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return!errors;
|
||||
}</script>
|
||||
|
||||
//fieldname:myUpDownSelectTag
|
||||
//validatorname:int
|
||||
if(form.elements['myUpDownSelectTag']) {
|
||||
field = form.elements['myUpDownSelectTag'];
|
||||
var error = "bar must be between 6000 and 10000.";
|
||||
var fieldValue = getFieldValue(field);
|
||||
if(continueValidation && fieldValue != null) {
|
||||
if(parseInt(fieldValue) < 6000 || parseInt(fieldValue) > 10000) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !errors;
|
||||
}
|
||||
</script>
|
||||
@@ -33,24 +33,43 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateForm_myAction() {
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
if(type == 'select-one' || type == 'select-multiple') {
|
||||
return (field.selectedIndex == -1 ? "" : field.options[field.selectedIndex].value);
|
||||
} else if(type == 'checkbox' || type == 'radio') {
|
||||
if(!field.length) {
|
||||
field=[field];
|
||||
}
|
||||
for(var i = 0; i < field.length; i++) {
|
||||
if(field[i].checked) {
|
||||
return field[i].value;
|
||||
}
|
||||
}
|
||||
return"";
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
form = document.getElementById("myAction");
|
||||
clearErrorMessages(form);
|
||||
clearErrorLabels(form);
|
||||
var errors = false;
|
||||
var continueValidation = true;
|
||||
//fieldname:myUpDownSelectTag
|
||||
//validatorname:double
|
||||
if(form.elements['myUpDownSelectTag']){
|
||||
field=form.elements['myUpDownSelectTag'];
|
||||
var error="bar must be between 6000.1 and 10000.1.";
|
||||
if(continueValidation && field.value!=null){
|
||||
var value = parseFloat(field.value);
|
||||
if(value<6000.1||value>10000.1||false||false){
|
||||
addError(field,error);
|
||||
errors=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return!errors;
|
||||
}</script>
|
||||
//fieldname:myUpDownSelectTag
|
||||
//validatorname:double
|
||||
if(form.elements['myUpDownSelectTag']) {
|
||||
field = form.elements['myUpDownSelectTag'];
|
||||
var error = "bar must be between 6000.1 and 10000.1.";
|
||||
var fieldValue = getFieldValue(field);
|
||||
if(continueValidation && fieldValue != null) {
|
||||
var value = parseFloat(fieldValue);
|
||||
if(value < 6000.1 || value > 10000.1 || false || false) {
|
||||
addError(field, error);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !errors;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user