001: package com.opensymphony.webwork.components;
002:
003: import com.opensymphony.webwork.dispatcher.mapper.ActionMapperFactory;
004: import com.opensymphony.webwork.dispatcher.mapper.ActionMapping;
005: import com.opensymphony.webwork.dispatcher.DispatcherUtils;
006: import com.opensymphony.webwork.portlet.context.PortletActionContext;
007: import com.opensymphony.webwork.portlet.util.PortletUrlHelper;
008: import com.opensymphony.webwork.views.util.UrlHelper;
009: import com.opensymphony.xwork.config.ConfigurationManager;
010: import com.opensymphony.xwork.config.RuntimeConfiguration;
011: import com.opensymphony.xwork.config.entities.ActionConfig;
012: import com.opensymphony.xwork.config.entities.InterceptorMapping;
013: import com.opensymphony.xwork.util.OgnlValueStack;
014: import com.opensymphony.xwork.ActionContext;
015: import com.opensymphony.xwork.ActionInvocation;
016: import com.opensymphony.xwork.ObjectFactory;
017: import com.opensymphony.xwork.interceptor.MethodFilterInterceptorUtil;
018: import com.opensymphony.xwork.validator.Validator;
019: import com.opensymphony.xwork.validator.FieldValidator;
020: import com.opensymphony.xwork.validator.ActionValidatorManagerFactory;
021: import com.opensymphony.xwork.validator.ValidationInterceptor;
022:
023: import javax.servlet.http.HttpServletRequest;
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.commons.lang.StringUtils;
027:
028: import java.util.ArrayList;
029: import java.util.List;
030: import java.util.Collections;
031: import java.util.Iterator;
032: import java.util.Set;
033:
034: /**
035: * <!-- START SNIPPET: javadoc -->
036: *
037: * Renders HTML an input form.<p/>
038: *
039: * The remote form allows the form to be submitted without the page being refreshed. The results from the form
040: * can be inserted into any HTML element on the page.<p/>
041: *
042: * NOTE:<p/>
043: * The order / logic in determining the posting url of the generated HTML form is as follows:-
044: * <ol>
045: * <li>
046: * If the action attribute is not specified, then the current request will be used to
047: * determine the posting url
048: * </li>
049: * <li>
050: * If the action is given, WebWork will try to obtain an ActionConfig. This will be
051: * successfull if the action attribute is a valid action alias defined xwork.xml.
052: * </li>
053: * <li>
054: * If the action is given and is not an action alias defined in xwork.xml WebWork
055: * will used the action attribute as if it is the posting url, separting the namespace
056: * from it and using UrlHelper to generate the final url.
057: * </li>
058: * </ol>
059: *
060: * <!-- END SNIPPET: javadoc -->
061: *
062: * <p/> <b>Examples</b>
063: *
064: * <pre>
065: * <!-- START SNIPPET: example -->
066: *
067: * <ww:form ... />
068: *
069: * <!-- END SNIPPET: example -->
070: * </pre>
071: *
072: * @author Patrick Lightbody
073: * @author Ian Roughley
074: * @author Rene Gielen
075: * @author Rainer Hermanns
076: * @version $Date: 2007-01-14 10:33:47 +0100 (Sun, 14 Jan 2007) $ $Id: Form.java 2805 2007-01-14 09:33:47Z tmjee $
077: * @since 2.2
078: *
079: * @ww.tag name="form" tld-body-content="JSP" tld-tag-class="com.opensymphony.webwork.views.jsp.ui.FormTag"
080: * description="Renders an input form"
081: */
082: public class Form extends ClosingUIBean {
083: public static final String OPEN_TEMPLATE = "form";
084: public static final String TEMPLATE = "form-close";
085:
086: private int sequence = 0;
087:
088: protected String onsubmit;
089: protected String action;
090: protected String target;
091: protected String enctype;
092: protected String method;
093: protected String namespace;
094: protected String validate;
095: protected String portletMode;
096: protected String windowState;
097: protected String acceptcharset;
098:
099: public Form(OgnlValueStack stack, HttpServletRequest request,
100: HttpServletResponse response) {
101: super (stack, request, response);
102: }
103:
104: protected boolean evaluateNameValue() {
105: return false;
106: }
107:
108: public String getDefaultOpenTemplate() {
109: return OPEN_TEMPLATE;
110: }
111:
112: protected String getDefaultTemplate() {
113: return TEMPLATE;
114: }
115:
116: /*
117: * Revised for Portlet actionURL as form action, and add wwAction as hidden
118: * field. Refer to template.simple/form.vm
119: */
120: protected void evaluateExtraParams() {
121: super .evaluateExtraParams();
122:
123: //boolean isAjax = "ajax".equalsIgnoreCase(this.theme);
124:
125: if (validate != null) {
126: addParameter("validate", findValue(validate, Boolean.class));
127: }
128:
129: // calculate the action and namespace
130: /*String action = null;
131: if (this.action != null) {
132: // if it isn't specified, we'll make somethig up
133: action = findString(this.action);
134: }
135:
136: if (DispatcherUtils.isPortletSupportActive() && PortletActionContext.isPortletRequest()) {
137: evaluateExtraParamsPortletRequest(namespace, action);
138: } else {
139: String namespace = determineNamespace(this.namespace, getStack(),
140: request);
141: evaluateExtraParamsServletRequest(action, namespace, isAjax);
142: }*/
143:
144: if (onsubmit != null) {
145: addParameter("onsubmit", findString(onsubmit));
146: }
147:
148: if (target != null) {
149: addParameter("target", findString(target));
150: }
151:
152: if (enctype != null) {
153: addParameter("enctype", findString(enctype));
154: }
155:
156: if (method != null) {
157: addParameter("method", findString(method));
158: }
159:
160: if (acceptcharset != null) {
161: addParameter("acceptcharset", findString(acceptcharset));
162: }
163:
164: // keep a collection of the tag names for anything special the templates might want to do (such as pure client
165: // side validation)
166: if (!parameters.containsKey("tagNames")) {
167: // we have this if check so we don't do this twice (on open and close of the template)
168: addParameter("tagNames", new ArrayList());
169: }
170: }
171:
172: /**
173: * Form component determine the its HTML element id as follows:-
174: * <ol>
175: * <li>if an 'id' attribute is specified.</li>
176: * <li>if an 'action' attribute is specified, it will be used as the id.</li>
177: * </ol>
178: */
179: protected void populateComponentHtmlId(Form form) {
180: boolean isAjax = "ajax".equalsIgnoreCase(this .theme);
181:
182: String action = null;
183: if (this .action != null) {
184: // if it isn't specified, we'll make somethig up
185: action = findString(this .action);
186: }
187:
188: if (id != null) {
189: addParameter("id", escape(id));
190: }
191: if (DispatcherUtils.isPortletSupportActive()
192: && PortletActionContext.isPortletRequest()) {
193: evaluateExtraParamsPortletRequest(namespace, action);
194: } else {
195: String namespace = determineNamespace(this .namespace,
196: getStack(), request);
197: evaluateExtraParamsServletRequest(action, namespace, isAjax);
198: }
199: }
200:
201: /**
202: * @param isAjax
203: * @param namespace
204: * @param action
205: */
206: private void evaluateExtraParamsServletRequest(String action,
207: String namespace, boolean isAjax) {
208: if (action == null) {
209: // no action supplied? ok, then default to the current request (action or general URL)
210: ActionInvocation ai = (ActionInvocation) getStack()
211: .getContext().get(ActionContext.ACTION_INVOCATION);
212: if (ai != null) {
213: action = ai.getProxy().getActionName();
214: namespace = ai.getProxy().getNamespace();
215: } else {
216: // hmm, ok, we need to just assume the current URL cut down
217: String uri = request.getRequestURI();
218: action = uri.substring(uri.lastIndexOf('/'));
219: }
220: }
221:
222: String actionMethod = "";
223: if (action.indexOf("!") != -1) {
224: int endIdx = action.lastIndexOf("!");
225: actionMethod = action
226: .substring(endIdx + 1, action.length());
227: action = action.substring(0, endIdx);
228: }
229:
230: final ActionConfig actionConfig = ConfigurationManager
231: .getConfiguration().getRuntimeConfiguration()
232: .getActionConfig(namespace, action);
233: String actionName = action;
234: if (actionConfig != null) {
235:
236: ActionMapping mapping = new ActionMapping(action,
237: namespace, actionMethod, parameters);
238: String result = UrlHelper.buildUrl(ActionMapperFactory
239: .getMapper().getUriFromActionMapping(mapping),
240: request, response, null);
241: addParameter("action", result);
242:
243: // let's try to get the actual action class and name
244: // this can be used for getting the list of validators
245: addParameter("actionName", actionName);
246: try {
247: Class clazz = ObjectFactory.getObjectFactory()
248: .getClassInstance(actionConfig.getClassName());
249: addParameter("actionClass", clazz);
250: } catch (ClassNotFoundException e) {
251: // this is OK, we'll just move on
252: }
253:
254: addParameter("namespace", namespace);
255:
256: // if the name isn't specified, use the action name
257: if (name == null) {
258: addParameter("name", action);
259: }
260:
261: // if the id isn't specified, use the action name
262: if (id == null) {
263: addParameter("id", action);
264: }
265: } else if (action != null) {
266: String result = UrlHelper.buildUrl(action, request,
267: response, null);
268: addParameter("action", result);
269:
270: // namespace: cut out anything between the start and the last /
271: int slash = result.lastIndexOf('/');
272: if (slash != -1) {
273: addParameter("namespace", result.substring(0, slash));
274: } else {
275: addParameter("namespace", "");
276: }
277:
278: // name/id: cut out anything between / and . should be the id and name
279: if (id == null) {
280: slash = result.lastIndexOf('/');
281: int dot = result.indexOf('.', slash);
282: if (dot != -1) {
283: id = result.substring(slash + 1, dot);
284: } else {
285: id = result.substring(slash + 1);
286: }
287: addParameter("id", escape(id));
288: }
289: }
290:
291: // WW-1284
292: // evaluate if client-side js is to be enabled. (if validation interceptor
293: // does allow validation eg. method is not filtered out)
294: evaluateClientSideJsEnablement(actionName, namespace,
295: actionMethod);
296: }
297:
298: private void evaluateClientSideJsEnablement(String actionName,
299: String namespace, String actionMethod) {
300:
301: // Only evaluate if Client-Side js is to be enable when validate=true
302: Boolean validate = (Boolean) getParameters().get("validate");
303: if (validate != null && validate.booleanValue()) {
304:
305: addParameter("performValidation", Boolean.FALSE);
306:
307: RuntimeConfiguration runtimeConfiguration = ConfigurationManager
308: .getConfiguration().getRuntimeConfiguration();
309: ActionConfig actionConfig = runtimeConfiguration
310: .getActionConfig(namespace, actionName);
311:
312: if (actionConfig != null) {
313: List interceptors = actionConfig.getInterceptors();
314: for (Iterator i = interceptors.iterator(); i.hasNext();) {
315: InterceptorMapping interceptorMapping = (InterceptorMapping) i
316: .next();
317: if (ValidationInterceptor.class
318: .isInstance(interceptorMapping
319: .getInterceptor())) {
320: ValidationInterceptor validationInterceptor = (ValidationInterceptor) interceptorMapping
321: .getInterceptor();
322:
323: Set excludeMethods = validationInterceptor
324: .getExcludeMethodsSet();
325: Set includeMethods = validationInterceptor
326: .getIncludeMethodsSet();
327:
328: if (MethodFilterInterceptorUtil.applyMethod(
329: excludeMethods, includeMethods,
330: actionMethod)) {
331: addParameter("performValidation",
332: Boolean.TRUE);
333: }
334: return;
335: }
336: }
337: }
338: }
339: }
340:
341: /**
342: * Constructs the action url adapted to a portal environment.
343: * @param action The action to create the URL for.
344: */
345: private void evaluateExtraParamsPortletRequest(String namespace,
346: String action) {
347:
348: if (this .action != null) {
349: // if it isn't specified, we'll make somethig up
350: action = findString(this .action);
351: }
352:
353: String type = "action";
354: if (StringUtils.isNotEmpty(method)) {
355: if ("GET".equalsIgnoreCase(method.trim())) {
356: type = "render";
357: }
358: }
359: if (action != null) {
360: String result = PortletUrlHelper.buildUrl(action,
361: namespace, getParameters(), type, portletMode,
362: windowState);
363: addParameter("action", result);
364:
365: // namespace: cut out anything between the start and the last /
366: int slash = result.lastIndexOf('/');
367: if (slash != -1) {
368: addParameter("namespace", result.substring(0, slash));
369: } else {
370: addParameter("namespace", "");
371: }
372:
373: // name/id: cut out anything between / and . should be the id and
374: // name
375: if (id == null) {
376: slash = action.lastIndexOf('/');
377: int dot = action.indexOf('.', slash);
378: if (dot != -1) {
379: id = action.substring(slash + 1, dot);
380: } else {
381: id = action.substring(slash + 1);
382: }
383: addParameter("id", escape(id));
384: }
385: }
386:
387: }
388:
389: public List getValidators(String name) {
390: Class actionClass = (Class) getParameters().get("actionClass");
391: if (actionClass == null) {
392: return Collections.EMPTY_LIST;
393: }
394:
395: List all = ActionValidatorManagerFactory.getInstance()
396: .getValidators(actionClass,
397: (String) getParameters().get("actionName"));
398: List validators = new ArrayList();
399: for (Iterator iterator = all.iterator(); iterator.hasNext();) {
400: Validator validator = (Validator) iterator.next();
401: if (validator instanceof FieldValidator) {
402: FieldValidator fieldValidator = (FieldValidator) validator;
403: if (fieldValidator.getFieldName().equals(name)) {
404: validators.add(fieldValidator);
405: }
406: }
407: }
408:
409: return validators;
410: }
411:
412: /**
413: * Get a incrementing sequence unique to this <code>Form</code> component.
414: * It is used by <code>Form</code> component's child that might need a
415: * sequence to make them unique.
416: *
417: * @return int
418: */
419: protected int getSequence() {
420: return sequence++;
421: }
422:
423: /**
424: * HTML onsubmit attribute
425: * @ww.tagattribute required="false"
426: */
427: public void setOnsubmit(String onsubmit) {
428: this .onsubmit = onsubmit;
429: }
430:
431: /**
432: * Set action nane to submit to, without .action suffix
433: * @ww.tagattribute required="false" default="current action"
434: */
435: public void setAction(String action) {
436: this .action = action;
437: }
438:
439: /**
440: * HTML form target attribute
441: * @ww.tagattribute required="false"
442: */
443: public void setTarget(String target) {
444: this .target = target;
445: }
446:
447: /**
448: * HTML form enctype attribute
449: * @ww.tagattribute required="false"
450: */
451: public void setEnctype(String enctype) {
452: this .enctype = enctype;
453: }
454:
455: /**
456: * HTML form method attribute
457: * @ww.tagattribute required="false"
458: */
459: public void setMethod(String method) {
460: this .method = method;
461: }
462:
463: /**
464: * namespace for action to submit to
465: * @ww.tagattribute required="false" default="current namespace"
466: */
467: public void setNamespace(String namespace) {
468: this .namespace = namespace;
469: }
470:
471: /**
472: * Whether client side/remote validation should be performed. Only useful with theme xhtml/ajax
473: * @ww.tagattribute required="false" type="Boolean" default="false"
474: */
475: public void setValidate(String validate) {
476: this .validate = validate;
477: }
478:
479: /**
480: * The portlet mode to display after the form submit
481: * @ww.tagattribute required="false"
482: */
483: public void setPortletMode(String portletMode) {
484: this .portletMode = portletMode;
485: }
486:
487: /**
488: * The window state to display after the form submit
489: * @ww.tagattribute required="false"
490: */
491: public void setWindowState(String windowState) {
492: this .windowState = windowState;
493: }
494:
495: /**
496: * The accepted charsets for this form. The values may be comma or blank delimited.
497: * @ww.tagattribute required="false"
498: */
499: public void setAcceptcharset(String acceptcharset) {
500: this.acceptcharset = acceptcharset;
501: }
502: }
|