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