1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.components;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Set;
25
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.struts2.StrutsConstants;
31 import org.apache.struts2.config.Settings;
32 import org.apache.struts2.dispatcher.Dispatcher;
33 import org.apache.struts2.dispatcher.mapper.ActionMapperFactory;
34 import org.apache.struts2.dispatcher.mapper.ActionMapping;
35 import org.apache.struts2.portlet.context.PortletActionContext;
36 import org.apache.struts2.portlet.util.PortletUrlHelper;
37 import org.apache.struts2.views.util.UrlHelper;
38
39 import com.opensymphony.xwork2.ActionContext;
40 import com.opensymphony.xwork2.ActionInvocation;
41 import com.opensymphony.xwork2.ObjectFactory;
42 import com.opensymphony.xwork2.config.Configuration;
43 import com.opensymphony.xwork2.config.RuntimeConfiguration;
44 import com.opensymphony.xwork2.config.entities.ActionConfig;
45 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
46 import com.opensymphony.xwork2.interceptor.MethodFilterInterceptorUtil;
47 import com.opensymphony.xwork2.util.ValueStack;
48 import com.opensymphony.xwork2.validator.ActionValidatorManagerFactory;
49 import com.opensymphony.xwork2.validator.FieldValidator;
50 import com.opensymphony.xwork2.validator.ValidationInterceptor;
51 import com.opensymphony.xwork2.validator.Validator;
52
53 /***
54 * <!-- START SNIPPET: javadoc -->
55 * <p/>
56 * Renders HTML an input form.<p/>
57 * <p/>
58 * The remote form allows the form to be submitted without the page being refreshed. The results from the form
59 * can be inserted into any HTML element on the page.<p/>
60 * <p/>
61 * NOTE:<p/>
62 * The order / logic in determining the posting url of the generated HTML form is as follows:-
63 * <ol>
64 * <li>
65 * If the action attribute is not specified, then the current request will be used to
66 * determine the posting url
67 * </li>
68 * <li>
69 * If the action is given, Struts will try to obtain an ActionConfig. This will be
70 * successfull if the action attribute is a valid action alias defined struts.xml.
71 * </li>
72 * <li>
73 * If the action is given and is not an action alias defined in struts.xml, Struts
74 * will used the action attribute as if it is the posting url, separting the namespace
75 * from it and using UrlHelper to generate the final url.
76 * </li>
77 * </ol>
78 * <p/>
79 * <!-- END SNIPPET: javadoc -->
80 * <p/>
81 * <p/> <b>Examples</b>
82 * <p/>
83 * <pre>
84 * <!-- START SNIPPET: example -->
85 * <p/>
86 * <s:form ... />
87 * <p/>
88 * <!-- END SNIPPET: example -->
89 * </pre>
90 *
91 * @s.tag name="form" tld-body-content="JSP" tld-tag-class="org.apache.struts2.views.jsp.ui.FormTag"
92 * description="Renders an input form"
93 */
94 public class Form extends ClosingUIBean {
95 public static final String OPEN_TEMPLATE = "form";
96 public static final String TEMPLATE = "form-close";
97
98 private int sequence = 0;
99
100 protected String onsubmit;
101 protected String action;
102 protected String target;
103 protected String enctype;
104 protected String method;
105 protected String namespace;
106 protected String validate;
107 protected String portletMode;
108 protected String windowState;
109 protected String acceptcharset;
110
111 public Form(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
112 super(stack, request, response);
113 }
114
115 protected boolean evaluateNameValue() {
116 return false;
117 }
118
119 public String getDefaultOpenTemplate() {
120 return OPEN_TEMPLATE;
121 }
122
123 protected String getDefaultTemplate() {
124 return TEMPLATE;
125 }
126
127
128
129
130
131
132 protected void evaluateExtraParams() {
133 super.evaluateExtraParams();
134
135
136
137 if (validate != null) {
138 addParameter("validate", findValue(validate, Boolean.class));
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 if (onsubmit != null) {
157 addParameter("onsubmit", findString(onsubmit));
158 }
159
160 if (target != null) {
161 addParameter("target", findString(target));
162 }
163
164 if (enctype != null) {
165 addParameter("enctype", findString(enctype));
166 }
167
168 if (method != null) {
169 addParameter("method", findString(method));
170 }
171
172 if (acceptcharset != null) {
173 addParameter("acceptcharset", findString(acceptcharset));
174 }
175
176
177
178 if (!parameters.containsKey("tagNames")) {
179
180 addParameter("tagNames", new ArrayList());
181 }
182 }
183
184 /***
185 * Form component determine the its HTML element id as follows:-
186 * <ol>
187 * <li>if an 'id' attribute is specified.</li>
188 * <li>if an 'action' attribute is specified, it will be used as the id.</li>
189 * </ol>
190 */
191 protected void populateComponentHtmlId(Form form) {
192 boolean isAjax = "ajax".equalsIgnoreCase(this.theme);
193
194 String action = null;
195 if (this.action != null) {
196
197 action = findString(this.action);
198 }
199
200 if (id != null) {
201 addParameter("id", escape(id));
202 }
203 if (Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) {
204 evaluateExtraParamsPortletRequest(namespace, action);
205 } else {
206 String namespace = determineNamespace(this.namespace, getStack(),
207 request);
208 evaluateExtraParamsServletRequest(action, namespace, isAjax);
209 }
210 }
211
212 /***
213 * @param isAjax
214 * @param namespace
215 * @param action
216 */
217 private void evaluateExtraParamsServletRequest(String action, String namespace, boolean isAjax) {
218 if (action == null) {
219
220 ActionInvocation ai = (ActionInvocation) getStack().getContext().get(ActionContext.ACTION_INVOCATION);
221 if (ai != null) {
222 action = ai.getProxy().getActionName();
223 namespace = ai.getProxy().getNamespace();
224 } else {
225
226 String uri = request.getRequestURI();
227 action = uri.substring(uri.lastIndexOf('/'));
228 }
229 }
230
231 String actionMethod = "";
232
233 boolean allowDynamicMethodCalls = "true".equals(Settings.get(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION));
234
235
236 if (allowDynamicMethodCalls) {
237 if (action.indexOf("!") != -1) {
238 int endIdx = action.lastIndexOf("!");
239 actionMethod = action.substring(endIdx + 1, action.length());
240 action = action.substring(0, endIdx);
241 }
242 }
243
244 Configuration config = Dispatcher.getInstance().getConfigurationManager().getConfiguration();
245 final ActionConfig actionConfig = config.getRuntimeConfiguration().getActionConfig(namespace, action);
246 String actionName = action;
247 if (actionConfig != null) {
248
249 ActionMapping mapping = new ActionMapping(action, namespace, actionMethod, parameters);
250 String result = UrlHelper.buildUrl(ActionMapperFactory.getMapper().getUriFromActionMapping(mapping), request, response, null);
251 addParameter("action", result);
252
253
254
255 addParameter("actionName", actionName);
256 try {
257 Class clazz = ObjectFactory.getObjectFactory().getClassInstance(actionConfig.getClassName());
258 addParameter("actionClass", clazz);
259 } catch (ClassNotFoundException e) {
260
261 }
262
263 addParameter("namespace", namespace);
264
265
266 if (name == null) {
267 addParameter("name", action);
268 }
269
270
271 if (id == null) {
272 addParameter("id", action);
273 }
274 } else if (action != null) {
275
276
277
278
279 String result = UrlHelper.buildUrl(action, request, response, null);
280 addParameter("action", result);
281
282
283 int slash = result.lastIndexOf('/');
284 if (slash != -1) {
285 addParameter("namespace", result.substring(0, slash));
286 } else {
287 addParameter("namespace", "");
288 }
289
290
291 if (id == null) {
292 slash = result.lastIndexOf('/');
293 int dot = result.indexOf('.', slash);
294 if (dot != -1) {
295 id = result.substring(slash + 1, dot);
296 } else {
297 id = result.substring(slash + 1);
298 }
299 addParameter("id", escape(id));
300 }
301 }
302
303
304
305
306 evaluateClientSideJsEnablement(actionName, namespace, actionMethod);
307 }
308
309 private void evaluateClientSideJsEnablement(String actionName, String namespace, String actionMethod) {
310
311
312 Boolean validate = (Boolean) getParameters().get("validate");
313 if (validate != null && validate.booleanValue()) {
314
315 addParameter("performValidation", Boolean.FALSE);
316
317 RuntimeConfiguration runtimeConfiguration = Dispatcher.getInstance().getConfigurationManager().getConfiguration().getRuntimeConfiguration();
318 ActionConfig actionConfig = runtimeConfiguration.getActionConfig(namespace, actionName);
319
320 if (actionConfig != null) {
321 List interceptors = actionConfig.getInterceptors();
322 for (Iterator i = interceptors.iterator(); i.hasNext();) {
323 InterceptorMapping interceptorMapping = (InterceptorMapping) i.next();
324 if (ValidationInterceptor.class.isInstance(interceptorMapping.getInterceptor())) {
325 ValidationInterceptor validationInterceptor = (ValidationInterceptor) interceptorMapping.getInterceptor();
326
327 Set excludeMethods = validationInterceptor.getExcludeMethodsSet();
328 Set includeMethods = validationInterceptor.getIncludeMethodsSet();
329
330 if (MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, actionMethod)) {
331 addParameter("performValidation", Boolean.TRUE);
332 }
333 return;
334 }
335 }
336 }
337 }
338 }
339
340 /***
341 * Constructs the action url adapted to a portal environment.
342 *
343 * @param action The action to create the URL for.
344 */
345 private void evaluateExtraParamsPortletRequest(String namespace, String action) {
346
347 if (this.action != null) {
348
349 action = findString(this.action);
350 }
351
352 String type = "action";
353 if (StringUtils.isNotEmpty(method)) {
354 if ("GET".equalsIgnoreCase(method.trim())) {
355 type = "render";
356 }
357 }
358 if (action != null) {
359 String result = PortletUrlHelper.buildUrl(action, namespace,
360 getParameters(), type, portletMode, windowState);
361 addParameter("action", result);
362
363
364 int slash = result.lastIndexOf('/');
365 if (slash != -1) {
366 addParameter("namespace", result.substring(0, slash));
367 } else {
368 addParameter("namespace", "");
369 }
370
371
372
373 if (id == null) {
374 slash = action.lastIndexOf('/');
375 int dot = action.indexOf('.', slash);
376 if (dot != -1) {
377 id = action.substring(slash + 1, dot);
378 } else {
379 id = action.substring(slash + 1);
380 }
381 addParameter("id", escape(id));
382 }
383 }
384
385 }
386
387 public List getValidators(String name) {
388 Class actionClass = (Class) getParameters().get("actionClass");
389 if (actionClass == null) {
390 return Collections.EMPTY_LIST;
391 }
392
393 List all = ActionValidatorManagerFactory.getInstance().getValidators(actionClass, (String) getParameters().get("actionName"));
394 List validators = new ArrayList();
395 for (Iterator iterator = all.iterator(); iterator.hasNext();) {
396 Validator validator = (Validator) iterator.next();
397 if (validator instanceof FieldValidator) {
398 FieldValidator fieldValidator = (FieldValidator) validator;
399 if (fieldValidator.getFieldName().equals(name)) {
400 validators.add(fieldValidator);
401 }
402 }
403 }
404
405 return validators;
406 }
407
408 /***
409 * Get a incrementing sequence unique to this <code>Form</code> component.
410 * It is used by <code>Form</code> component's child that might need a
411 * sequence to make them unique.
412 *
413 * @return int
414 */
415 protected int getSequence() {
416 return sequence++;
417 }
418
419
420 /***
421 * HTML onsubmit attribute
422 *
423 * @s.tagattribute required="false"
424 */
425 public void setOnsubmit(String onsubmit) {
426 this.onsubmit = onsubmit;
427 }
428
429 /***
430 * Set action nane to submit to, without .action suffix
431 *
432 * @s.tagattribute required="false" default="current action"
433 */
434 public void setAction(String action) {
435 this.action = action;
436 }
437
438 /***
439 * HTML form target attribute
440 *
441 * @s.tagattribute required="false"
442 */
443 public void setTarget(String target) {
444 this.target = target;
445 }
446
447 /***
448 * HTML form enctype attribute
449 *
450 * @s.tagattribute required="false"
451 */
452 public void setEnctype(String enctype) {
453 this.enctype = enctype;
454 }
455
456 /***
457 * HTML form method attribute
458 *
459 * @s.tagattribute required="false"
460 */
461 public void setMethod(String method) {
462 this.method = method;
463 }
464
465 /***
466 * namespace for action to submit to
467 *
468 * @s.tagattribute required="false" default="current namespace"
469 */
470 public void setNamespace(String namespace) {
471 this.namespace = namespace;
472 }
473
474 /***
475 * Whether client side/remote validation should be performed. Only useful with theme xhtml/ajax
476 *
477 * @s.tagattribute required="false" type="Boolean" default="false"
478 */
479 public void setValidate(String validate) {
480 this.validate = validate;
481 }
482
483 /***
484 * The portlet mode to display after the form submit
485 *
486 * @s.tagattribute required="false"
487 */
488 public void setPortletMode(String portletMode) {
489 this.portletMode = portletMode;
490 }
491
492 /***
493 * The window state to display after the form submit
494 *
495 * @s.tagattribute required="false"
496 */
497 public void setWindowState(String windowState) {
498 this.windowState = windowState;
499 }
500
501 /***
502 * The accepted charsets for this form. The values may be comma or blank delimited.
503 *
504 * @s.tagattribute required="false"
505 */
506 public void setAcceptcharset(String acceptcharset) {
507 this.acceptcharset = acceptcharset;
508 }
509 }