| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.struts2.components; |
| |
| import com.opensymphony.xwork2.ObjectFactory; |
| import com.opensymphony.xwork2.config.Configuration; |
| import com.opensymphony.xwork2.config.RuntimeConfiguration; |
| import com.opensymphony.xwork2.config.entities.ActionConfig; |
| import com.opensymphony.xwork2.config.entities.InterceptorMapping; |
| import com.opensymphony.xwork2.inject.Inject; |
| import com.opensymphony.xwork2.interceptor.MethodFilterInterceptorUtil; |
| import com.opensymphony.xwork2.util.ValueStack; |
| import com.opensymphony.xwork2.validator.*; |
| 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.struts2.views.jsp.TagUtils; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * <!-- START SNIPPET: javadoc --> |
| * <p> |
| * Renders HTML an input form. |
| * </p> |
| * |
| * <p> |
| * The remote form allows the form to be submitted without the page being refreshed. The results from the form |
| * can be inserted into any HTML element on the page. |
| * </p> |
| * <p> |
| * NOTE:<br> |
| * The order / logic in determining the posting url of the generated HTML form is as follows: |
| * </p> |
| * |
| * <ol> |
| * <li> |
| * If the action attribute is not specified, then the current request will be used to |
| * determine the posting url |
| * </li> |
| * <li> |
| * If the action is given, Struts will try to obtain an ActionConfig. This will be |
| * successful if the action attribute is a valid action alias defined struts.xml. |
| * </li> |
| * <li> |
| * If the action is given and is not an action alias defined in struts.xml, Struts |
| * will used the action attribute as if it is the posting url, separting the namespace |
| * from it and using UrlHelper to generate the final url. |
| * </li> |
| * </ol> |
| * |
| * <!-- END SNIPPET: javadoc --> |
| * |
| * <p><b>Examples</b></p> |
| * |
| * <pre> |
| * <!-- START SNIPPET: example --> |
| * |
| * <s:form ... /> |
| * |
| * <!-- END SNIPPET: example --> |
| * </pre> |
| * |
| */ |
| @StrutsTag( |
| name="form", |
| tldTagClass="org.apache.struts2.views.jsp.ui.FormTag", |
| description="Renders an input form", |
| allowDynamicAttributes=true) |
| public class Form extends ClosingUIBean { |
| public static final String OPEN_TEMPLATE = "form"; |
| public static final String TEMPLATE = "form-close"; |
| |
| private int sequence = 0; |
| |
| protected String onsubmit; |
| protected String onreset; |
| protected String action; |
| protected String target; |
| protected String enctype; |
| protected String method; |
| protected String namespace; |
| protected String validate; |
| protected String portletMode; |
| protected String windowState; |
| protected String acceptcharset; |
| protected boolean includeContext = true; |
| |
| protected String focusElement; |
| protected Configuration configuration; |
| protected ObjectFactory objectFactory; |
| protected UrlRenderer urlRenderer; |
| protected ActionValidatorManager actionValidatorManager; |
| |
| public Form(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { |
| super(stack, request, response); |
| } |
| |
| @Override |
| protected boolean evaluateNameValue() { |
| return false; |
| } |
| |
| @Override |
| public String getDefaultOpenTemplate() { |
| return OPEN_TEMPLATE; |
| } |
| |
| @Override |
| protected String getDefaultTemplate() { |
| return TEMPLATE; |
| } |
| |
| @Inject |
| public void setConfiguration(Configuration configuration) { |
| this.configuration = configuration; |
| } |
| |
| @Inject |
| public void setObjectFactory(ObjectFactory objectFactory) { |
| this.objectFactory = objectFactory; |
| } |
| |
| @Inject |
| public void setUrlRenderer(UrlRenderer urlRenderer) { |
| this.urlRenderer = urlRenderer; |
| } |
| |
| @Inject |
| public void setActionValidatorManager(ActionValidatorManager mgr) { |
| this.actionValidatorManager = mgr; |
| } |
| |
| |
| /* |
| * 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) { |
| addParameter("validate", findValue(validate, Boolean.class)); |
| } |
| |
| if (name == null) { |
| //make the name the same as the id |
| String id = (String) getParameters().get("id"); |
| if (StringUtils.isNotEmpty(id)) { |
| addParameter("name", id); |
| } |
| } |
| |
| if (onsubmit != null) { |
| addParameter("onsubmit", findString(onsubmit)); |
| } |
| |
| if (onreset != null) { |
| addParameter("onreset", findString(onreset)); |
| } |
| |
| if (target != null) { |
| addParameter("target", findString(target)); |
| } |
| |
| if (enctype != null) { |
| addParameter("enctype", findString(enctype)); |
| } |
| |
| if (method != null) { |
| addParameter("method", findString(method)); |
| } |
| |
| if (acceptcharset != null) { |
| addParameter("acceptcharset", findString(acceptcharset)); |
| } |
| |
| // keep a collection of the tag names for anything special the templates might want to do (such as pure client |
| // side validation) |
| if (!parameters.containsKey("tagNames")) { |
| // we have this if check so we don't do this twice (on open and close of the template) |
| addParameter("tagNames", new ArrayList()); |
| } |
| |
| if (focusElement != null) { |
| addParameter("focusElement", findString(focusElement)); |
| } |
| } |
| |
| /** |
| * Form component determine the its HTML element id as follows:- |
| * <ol> |
| * <li>if an 'id' attribute is specified.</li> |
| * <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) { |
| super.populateComponentHtmlId(null); |
| } |
| |
| // if no id given, it will be tried to generate it from the action attribute |
| // by the urlRenderer implementation |
| urlRenderer.renderFormUrl(this); |
| } |
| |
| /** |
| * Evaluate client side JavaScript Enablement. |
| * @param actionName the actioName to check for |
| * @param namespace the namespace to check for |
| * @param actionMethod the method to ckeck for |
| */ |
| protected void evaluateClientSideJsEnablement(String actionName, String namespace, String actionMethod) { |
| |
| // Only evaluate if Client-Side js is to be enable when validate=true |
| Boolean validate = (Boolean) getParameters().get("validate"); |
| if (validate != null && validate) { |
| |
| addParameter("performValidation", Boolean.FALSE); |
| |
| RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration(); |
| ActionConfig actionConfig = runtimeConfiguration.getActionConfig(namespace, actionName); |
| |
| if (actionConfig != null) { |
| List<InterceptorMapping> interceptors = actionConfig.getInterceptors(); |
| for (InterceptorMapping interceptorMapping : interceptors) { |
| if (ValidationInterceptor.class.isInstance(interceptorMapping.getInterceptor())) { |
| ValidationInterceptor validationInterceptor = (ValidationInterceptor) interceptorMapping.getInterceptor(); |
| |
| Set excludeMethods = validationInterceptor.getExcludeMethodsSet(); |
| Set includeMethods = validationInterceptor.getIncludeMethodsSet(); |
| |
| if (MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, actionMethod)) { |
| addParameter("performValidation", Boolean.TRUE); |
| } |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| public List getValidators(String name) { |
| Class actionClass = (Class) getParameters().get("actionClass"); |
| if (actionClass == null) { |
| return Collections.EMPTY_LIST; |
| } |
| |
| String formActionValue = findString(action); |
| ActionMapping mapping = actionMapper.getMappingFromActionName(formActionValue); |
| |
| if (mapping == null) { |
| mapping = actionMapper.getMappingFromActionName((String) getParameters().get("actionName")); |
| } |
| |
| if (mapping == null) { |
| return Collections.EMPTY_LIST; |
| } |
| |
| String actionName = mapping.getName(); |
| |
| String methodName = null; |
| if (isValidateAnnotatedMethodOnly(actionName)) { |
| methodName = mapping.getMethod(); |
| } |
| |
| List<Validator> actionValidators = actionValidatorManager.getValidators(actionClass, actionName, methodName); |
| List<Validator> validators = new ArrayList<>(); |
| |
| findFieldValidators(name, actionClass, actionName, actionValidators, validators, ""); |
| |
| return validators; |
| } |
| |
| private boolean isValidateAnnotatedMethodOnly(String actionName) { |
| RuntimeConfiguration runtimeConfiguration = configuration.getRuntimeConfiguration(); |
| String actionNamespace = TagUtils.buildNamespace(actionMapper, stack, request); |
| ActionConfig actionConfig = runtimeConfiguration.getActionConfig(actionNamespace, actionName); |
| |
| if (actionConfig != null) { |
| List<InterceptorMapping> interceptors = actionConfig.getInterceptors(); |
| for (InterceptorMapping interceptorMapping : interceptors) { |
| if (ValidationInterceptor.class.isInstance(interceptorMapping.getInterceptor())) { |
| ValidationInterceptor validationInterceptor = (ValidationInterceptor) interceptorMapping.getInterceptor(); |
| return validationInterceptor.isValidateAnnotatedMethodOnly(); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private void findFieldValidators(String name, Class actionClass, String actionName, |
| List<Validator> validatorList, List<Validator> resultValidators, 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, resultValidators, vPrefix); |
| } else if ((prefix + fieldValidator.getFieldName()).equals(name)) { |
| if (StringUtils.isNotBlank(prefix)) { |
| //fixing field name for js side |
| FieldVisitorValidatorWrapper wrap = new FieldVisitorValidatorWrapper(fieldValidator, prefix); |
| resultValidators.add(wrap); |
| } else { |
| resultValidators.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 action class |
| * @param visitorFieldName field name |
| * @return type of visited object |
| */ |
| @SuppressWarnings("unchecked") |
| protected Class getVisitorReturnType(Class actionClass, String visitorFieldName) { |
| if (visitorFieldName == null) { |
| return null; |
| } |
| String methodName = "get" + 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 |
| * sequence to make them unique. |
| * |
| * @return int |
| */ |
| protected int getSequence() { |
| return sequence++; |
| } |
| |
| @StrutsTagAttribute(description="HTML onsubmit attribute") |
| public void setOnsubmit(String onsubmit) { |
| this.onsubmit = onsubmit; |
| } |
| |
| @StrutsTagAttribute(description="HTML onreset attribute") |
| public void setOnreset(String onreset) { |
| this.onreset = onreset; |
| } |
| |
| @StrutsTagAttribute(description="Set action name to submit to, without .action suffix", defaultValue="current action") |
| public void setAction(String action) { |
| this.action = action; |
| } |
| |
| @StrutsTagAttribute(description="HTML form target attribute") |
| public void setTarget(String target) { |
| this.target = target; |
| } |
| |
| @StrutsTagAttribute(description="HTML form enctype attribute") |
| public void setEnctype(String enctype) { |
| this.enctype = enctype; |
| } |
| |
| @StrutsTagAttribute(description="HTML form method attribute") |
| public void setMethod(String method) { |
| this.method = method; |
| } |
| |
| @StrutsTagAttribute(description="Namespace for action to submit to", defaultValue="current namespace") |
| public void setNamespace(String namespace) { |
| this.namespace = namespace; |
| } |
| |
| @StrutsTagAttribute(description="Whether client side/remote validation should be performed. Only" + |
| " useful with theme xhtml/ajax", type="Boolean", defaultValue="false") |
| public void setValidate(String validate) { |
| this.validate = validate; |
| } |
| |
| @StrutsTagAttribute(description="The portlet mode to display after the form submit") |
| public void setPortletMode(String portletMode) { |
| this.portletMode = portletMode; |
| } |
| |
| @StrutsTagAttribute(description="The window state to display after the form submit") |
| public void setWindowState(String windowState) { |
| this.windowState = windowState; |
| } |
| |
| @StrutsTagAttribute(description="The accepted charsets for this form. The values may be comma or blank delimited.") |
| public void setAcceptcharset(String acceptcharset) { |
| this.acceptcharset = acceptcharset; |
| } |
| |
| @StrutsTagAttribute(description="Id of element that will receive the focus when page loads.") |
| public void setFocusElement(String focusElement) { |
| this.focusElement = focusElement; |
| } |
| |
| @StrutsTagAttribute(description="Whether actual context should be included in URL", type="Boolean", defaultValue="true") |
| public void setIncludeContext(boolean includeContext) { |
| this.includeContext = includeContext; |
| } |
| } |