1 /*
2 * Copyright 2007 The Kuali Foundation Licensed under the Educational Community
3 * License, Version 1.0 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
6 * or agreed to in writing, software distributed under the License is
7 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8 * KIND, either express or implied. See the License for the specific language
9 * governing permissions and limitations under the License.
10 */
11 package org.kuali.rice.krad.uif.field;
12
13 import org.apache.commons.lang.StringUtils;
14 import org.kuali.rice.krad.uif.UifConstants;
15 import org.kuali.rice.krad.uif.UifParameters;
16 import org.kuali.rice.krad.uif.UifPropertyPaths;
17 import org.kuali.rice.krad.uif.view.FormView;
18 import org.kuali.rice.krad.uif.view.View;
19 import org.kuali.rice.krad.uif.component.Component;
20 import org.kuali.rice.krad.uif.widget.LightBox;
21
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 /**
27 * Field that presents an action that can be taken on the UI such as submitting
28 * the form or invoking a script
29 *
30 * @author Kuali Rice Team (rice.collab@kuali.org)
31 */
32 public class ActionField extends FieldBase {
33 private static final long serialVersionUID = 1025672792657238829L;
34
35 private String methodToCall;
36 private String navigateToPageId;
37
38 private String clientSideJs;
39
40 private String jumpToIdAfterSubmit;
41 private String jumpToNameAfterSubmit;
42 private String focusOnAfterSubmit;
43
44 private String actionLabel;
45 private ImageField actionImageField;
46 private String actionImageLocation = "LEFT";
47
48 private Map<String, String> actionParameters;
49
50 private LightBox lightBoxLookup;
51
52 private LightBox lightBoxDirectInquiry;
53
54 private boolean blockValidateDirty;
55
56 public ActionField() {
57 super();
58 actionParameters = new HashMap<String, String>();
59 }
60
61 /**
62 * The following initialization is performed:
63 * <ul>
64 * <li>Set the actionLabel if blank to the Field label</li>
65 * </ul>
66 *
67 * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View)
68 */
69 @Override
70 public void performInitialization(View view) {
71 super.performInitialization(view);
72
73 if (StringUtils.isBlank(actionLabel)) {
74 actionLabel = this.getLabel();
75 }
76 }
77
78 /**
79 * The following finalization is performed:
80 * <ul>
81 * <li>Add methodToCall action parameter if set and setup event code for
82 * setting action parameters</li>
83 * </ul>
84 *
85 * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
86 * java.lang.Object, org.kuali.rice.krad.uif.component.Component)
87 */
88 @Override
89 public void performFinalize(View view, Object model, Component parent) {
90 super.performFinalize(view, model, parent);
91 //clear alt text to avoid screen reader confusion when using image in button with text
92 if(actionImageField != null && StringUtils.isNotBlank(actionImageLocation) && StringUtils.isNotBlank(actionLabel)){
93 actionImageField.setAltText("");
94 }
95
96 actionParameters.put(UifConstants.UrlParams.SHOW_HOME, "false");
97 actionParameters.put(UifConstants.UrlParams.SHOW_HISTORY, "false");
98
99 if (StringUtils.isNotBlank(navigateToPageId)) {
100 actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
101 if (StringUtils.isBlank(methodToCall)) {
102 actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME,
103 UifConstants.MethodToCallNames.NAVIGATE);
104 }
105 }
106
107 if (!actionParameters.containsKey(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)
108 && StringUtils.isNotBlank(methodToCall)) {
109 actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME, methodToCall);
110 }
111
112 // If there is no lightBox then create the on click script
113 if (lightBoxLookup == null) {
114 String prefixScript = this.getOnClickScript();
115 if (prefixScript == null) {
116 prefixScript = "";
117 }
118
119 boolean validateFormDirty = false;
120 if (view instanceof FormView && !isBlockValidateDirty()) {
121 validateFormDirty = ((FormView) view).isValidateDirty();
122 }
123
124 boolean includeDirtyCheckScript = false;
125 String writeParamsScript = "";
126 if (!actionParameters.isEmpty()) {
127 for (String key : actionParameters.keySet()) {
128 String parameterPath = key;
129 if (!key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
130 parameterPath = UifPropertyPaths.ACTION_PARAMETERS + "[" + key + "]";
131 }
132
133 writeParamsScript = writeParamsScript + "writeHiddenToForm('" + parameterPath + "' , '"
134 + actionParameters.get(key) + "'); ";
135
136 // Include dirtycheck js function call if the method to call
137 // is refresh, navigate, cancel or close
138 if (validateFormDirty && !includeDirtyCheckScript
139 && key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
140 String keyValue = (String) actionParameters.get(key);
141 if (StringUtils.equals(keyValue, UifConstants.MethodToCallNames.REFRESH)
142 || StringUtils.equals(keyValue, UifConstants.MethodToCallNames.NAVIGATE)
143 || StringUtils.equals(keyValue, UifConstants.MethodToCallNames.CANCEL)
144 || StringUtils.equals(keyValue, UifConstants.MethodToCallNames.CLOSE)) {
145 includeDirtyCheckScript = true;
146 }
147 }
148 }
149 }
150
151 // TODO possibly fix some other way - this is a workaround, prevents
152 // showing history and showing home again on actions which submit
153 // the form
154 writeParamsScript = writeParamsScript + "writeHiddenToForm('" + UifConstants.UrlParams.SHOW_HISTORY
155 + "', '" + "false" + "'); ";
156 writeParamsScript = writeParamsScript + "writeHiddenToForm('" + UifConstants.UrlParams.SHOW_HOME + "' , '"
157 + "false" + "'); ";
158
159 if (StringUtils.isBlank(focusOnAfterSubmit)) {
160 // if this is blank focus this actionField by default
161 focusOnAfterSubmit = this.getId();
162 writeParamsScript = writeParamsScript + "writeHiddenToForm('focusId' , '" + this.getId() + "'); ";
163 } else if (!focusOnAfterSubmit.equalsIgnoreCase(UifConstants.Order.FIRST)) {
164 // Use the id passed in
165 writeParamsScript = writeParamsScript + "writeHiddenToForm('focusId' , '" + focusOnAfterSubmit + "'); ";
166 } else {
167 // First input will be focused, must be first field set to empty
168 // string
169 writeParamsScript = writeParamsScript + "writeHiddenToForm('focusId' , ''); ";
170 }
171
172 if (StringUtils.isBlank(jumpToIdAfterSubmit) && StringUtils.isBlank(jumpToNameAfterSubmit)) {
173 jumpToIdAfterSubmit = this.getId();
174 writeParamsScript = writeParamsScript + "writeHiddenToForm('jumpToId' , '" + this.getId() + "'); ";
175 } else if (StringUtils.isNotBlank(jumpToIdAfterSubmit)) {
176 writeParamsScript = writeParamsScript + "writeHiddenToForm('jumpToId' , '" + jumpToIdAfterSubmit
177 + "'); ";
178 } else {
179 writeParamsScript = writeParamsScript + "writeHiddenToForm('jumpToName' , '" + jumpToNameAfterSubmit
180 + "'); ";
181 }
182
183 String postScript = "";
184 if (StringUtils.isNotBlank(clientSideJs)) {
185 postScript = clientSideJs;
186 } else {
187 postScript = "writeHiddenToForm('renderFullView' , 'true'); jq('#kualiForm').submit();";
188 }
189
190 if (includeDirtyCheckScript) {
191 this.setOnClickScript("e.preventDefault(); if (checkDirty(e) == false) { " + prefixScript
192 + writeParamsScript + postScript + " ; } ");
193 } else {
194 this.setOnClickScript("e.preventDefault();" + prefixScript + writeParamsScript + postScript);
195 }
196
197 } else {
198 // When there is a light box - don't add the on click script as it
199 // will be prevented from executing
200 // Create a script map object which will be written to the form on
201 // click event
202 StringBuffer sb = new StringBuffer();
203 sb.append("{");
204 for (String key : actionParameters.keySet()) {
205 String optionValue = actionParameters.get(key);
206 if (sb.length() > 1) {
207 sb.append(",");
208 }
209 if (!key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
210 sb.append("\"" + UifPropertyPaths.ACTION_PARAMETERS + "[" + key + "]" + "\"");
211 } else {
212 sb.append("\"" + key + "\"");
213 }
214 sb.append(":");
215 sb.append("\"" + optionValue + "\"");
216 }
217 sb.append("}");
218 lightBoxLookup.setActionParameterMapString(sb.toString());
219 }
220 }
221
222 /**
223 * @see org.kuali.rice.krad.uif.component.ComponentBase#getNestedComponents()
224 */
225 @Override
226 public List<Component> getNestedComponents() {
227 List<Component> components = super.getNestedComponents();
228
229 components.add(actionImageField);
230 components.add(lightBoxLookup);
231 components.add(lightBoxDirectInquiry);
232
233 return components;
234 }
235
236 /**
237 * Name of the method that should be called when the action is selected
238 * <p>
239 * For a server side call (clientSideCall is false), gives the name of the
240 * method in the mapped controller that should be invoked when the action is
241 * selected. For client side calls gives the name of the script function
242 * that should be invoked when the action is selected
243 * </p>
244 *
245 * @return String name of method to call
246 */
247 public String getMethodToCall() {
248 return this.methodToCall;
249 }
250
251 /**
252 * Setter for the actions method to call
253 *
254 * @param methodToCall
255 */
256 public void setMethodToCall(String methodToCall) {
257 this.methodToCall = methodToCall;
258 }
259
260 /**
261 * Label text for the action
262 * <p>
263 * The label text is used by the template renderers to give a human readable
264 * label for the action. For buttons this generally is the button text,
265 * while for an action link it would be the links displayed text
266 * </p>
267 *
268 * @return String label for action
269 */
270 public String getActionLabel() {
271 return this.actionLabel;
272 }
273
274 /**
275 * Setter for the actions label
276 *
277 * @param actionLabel
278 */
279 public void setActionLabel(String actionLabel) {
280 this.actionLabel = actionLabel;
281 }
282
283 /**
284 * Image to use for the action
285 * <p>
286 * When the action image field is set (and render is true) the image will be
287 * used to present the action as opposed to the default (input submit). For
288 * action link templates the image is used for the link instead of the
289 * action link text
290 * </p>
291 *
292 * @return ImageField action image
293 */
294 public ImageField getActionImageField() {
295 return this.actionImageField;
296 }
297
298 /**
299 * Setter for the action image field
300 *
301 * @param actionImageField
302 */
303 public void setActionImageField(ImageField actionImageField) {
304 this.actionImageField = actionImageField;
305 }
306
307 /**
308 * For an <code>ActionField</code> that is part of a
309 * <code>NavigationGroup</code, the navigate to page id can be set to
310 * configure the page that should be navigated to when the action is
311 * selected
312 * <p>
313 * Support exists in the <code>UifControllerBase</code> for handling
314 * navigation between pages
315 * </p>
316 *
317 * @return String id of page that should be rendered when the action item is
318 * selected
319 */
320 public String getNavigateToPageId() {
321 return this.navigateToPageId;
322 }
323
324 /**
325 * Setter for the navigate to page id
326 *
327 * @param navigateToPageId
328 */
329 public void setNavigateToPageId(String navigateToPageId) {
330 this.navigateToPageId = navigateToPageId;
331 actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
332 this.methodToCall = UifConstants.MethodToCallNames.NAVIGATE;
333 }
334
335 /**
336 * Parameters that should be sent when the action is invoked
337 * <p>
338 * Action renderer will decide how the parameters are sent for the action
339 * (via script generated hiddens, or script parameters, ...)
340 * </p>
341 * <p>
342 * Can be set by other components such as the <code>CollectionGroup</code>
343 * to provide the context the action is in (such as the collection name and
344 * line the action applies to)
345 * </p>
346 *
347 * @return Map<String, String> action parameters
348 */
349 public Map<String, String> getActionParameters() {
350 return this.actionParameters;
351 }
352
353 /**
354 * Setter for the action parameters
355 *
356 * @param actionParameters
357 */
358 public void setActionParameters(Map<String, String> actionParameters) {
359 this.actionParameters = actionParameters;
360 }
361
362 /**
363 * Convenience method to add a parameter to the action parameters Map
364 *
365 * @param parameterName
366 * - name of parameter to add
367 * @param parameterValue
368 * - value of parameter to add
369 */
370 public void addActionParameter(String parameterName, String parameterValue) {
371 if (actionParameters == null) {
372 this.actionParameters = new HashMap<String, String>();
373 }
374
375 this.actionParameters.put(parameterName, parameterValue);
376 }
377
378 /**
379 * Get an actionParameter by name
380 */
381 public String getActionParameter(String parameterName) {
382
383 return this.actionParameters.get(parameterName);
384 }
385
386 /**
387 * @see org.kuali.rice.krad.uif.component.ComponentBase#getSupportsOnClick()
388 */
389 @Override
390 public boolean getSupportsOnClick() {
391 return true;
392 }
393
394 /**
395 * Setter for the light box lookup widget
396 *
397 * @param lightBoxLookup
398 * <code>LightBoxLookup</code> widget to set
399 */
400 public void setLightBoxLookup(LightBox lightBoxLookup) {
401 this.lightBoxLookup = lightBoxLookup;
402 }
403
404 /**
405 * LightBoxLookup widget for the field
406 * <p>
407 * The light box lookup widget will change the lookup behaviour to open the
408 * lookup in a light box.
409 * </p>
410 *
411 * @return the <code>DirectInquiry</code> field DirectInquiry
412 */
413 public LightBox getLightBoxLookup() {
414 return lightBoxLookup;
415 }
416
417 /**
418 * @return the jumpToIdAfterSubmit
419 */
420 public String getJumpToIdAfterSubmit() {
421 return this.jumpToIdAfterSubmit;
422 }
423
424 /**
425 * The id to jump to in the next page, the element with this id will be
426 * jumped to automatically when the new page is retrieved after a submit.
427 * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the
428 * resulting page. Passing in nothing for both jumpToIdAfterSubmit and
429 * jumpToNameAfterSubmit will result in this ActionField being jumped to by
430 * default if it is present on the new page. WARNING: jumpToIdAfterSubmit
431 * always takes precedence over jumpToNameAfterSubmit, if set.
432 *
433 * @param jumpToIdAfterSubmit
434 * the jumpToIdAfterSubmit to set
435 */
436 public void setJumpToIdAfterSubmit(String jumpToIdAfterSubmit) {
437 this.jumpToIdAfterSubmit = jumpToIdAfterSubmit;
438 }
439
440 /**
441 * The name to jump to in the next page, the element with this name will be
442 * jumped to automatically when the new page is retrieved after a submit.
443 * Passing in nothing for both jumpToIdAfterSubmit and jumpToNameAfterSubmit
444 * will result in this ActionField being jumped to by default if it is
445 * present on the new page. WARNING: jumpToIdAfterSubmit always takes
446 * precedence over jumpToNameAfterSubmit, if set.
447 *
448 * @return the jumpToNameAfterSubmit
449 */
450 public String getJumpToNameAfterSubmit() {
451 return this.jumpToNameAfterSubmit;
452 }
453
454 /**
455 * @param jumpToNameAfterSubmit
456 * the jumpToNameAfterSubmit to set
457 */
458 public void setJumpToNameAfterSubmit(String jumpToNameAfterSubmit) {
459 this.jumpToNameAfterSubmit = jumpToNameAfterSubmit;
460 }
461
462 /**
463 * The id of the field to place focus on in the new page after the new page
464 * is retrieved. Passing in "FIRST" will focus on the first visible input
465 * element on the form. Passing in the empty string will result in this
466 * ActionField being focused.
467 *
468 * @return the focusOnAfterSubmit
469 */
470 public String getFocusOnAfterSubmit() {
471 return this.focusOnAfterSubmit;
472 }
473
474 /**
475 * @param focusOnAfterSubmit
476 * the focusOnAfterSubmit to set
477 */
478 public void setFocusOnAfterSubmit(String focusOnAfterSubmit) {
479 this.focusOnAfterSubmit = focusOnAfterSubmit;
480 }
481
482 /**
483 * Client side javascript to be executed when this actionField is clicked.
484 * This overrides the default action for this ActionField so the method
485 * called must explicitly submit, navigate, etc. through js, if necessary.
486 * In addition, this js occurs AFTER onClickScripts set on this field, it
487 * will be the last script executed by the click event. Sidenote: This js is
488 * always called after hidden actionParameters and methodToCall methods are
489 * written by the js to the html form.
490 *
491 * @return the clientSideJs
492 */
493 public String getClientSideJs() {
494 return this.clientSideJs;
495 }
496
497 /**
498 * @param clientSideJs
499 * the clientSideJs to set
500 */
501 public void setClientSideJs(String clientSideJs) {
502 if (!StringUtils.endsWith(clientSideJs, ";")) {
503 clientSideJs = clientSideJs + ";";
504 }
505 this.clientSideJs = clientSideJs;
506 }
507
508 /**
509 * Setter for the light box direct inquiry widget
510 *
511 * @param lightBoxDirectInquiry
512 * <code>LightBox</code> widget to set
513 */
514 public void setLightBoxDirectInquiry(LightBox lightBoxDirectInquiry) {
515 this.lightBoxDirectInquiry = lightBoxDirectInquiry;
516 }
517
518 /**
519 * LightBox widget for the field
520 * <p>
521 * The light box widget will change the direct inquiry behaviour to open up
522 * in a light box.
523 * </p>
524 *
525 * @return the <code>LightBox</code> field LightBox
526 */
527 public LightBox getLightBoxDirectInquiry() {
528 return lightBoxDirectInquiry;
529 }
530
531 /**
532 * @param blockValidateDirty
533 * the blockValidateDirty to set
534 */
535 public void setBlockValidateDirty(boolean blockValidateDirty) {
536 this.blockValidateDirty = blockValidateDirty;
537 }
538
539 /**
540 * @return the blockValidateDirty
541 */
542 public boolean isBlockValidateDirty() {
543 return blockValidateDirty;
544 }
545
546 public String getActionImageLocation() {
547 return actionImageLocation;
548 }
549
550 /**
551 * Set to TOP, BOTTOM, LEFT, RIGHT to position image at that location within the button.
552 * For the subclass ActionLinkField only LEFT and RIGHT are allowed. When set to blank/null, the image
553 * itself will be the ActionField, if no value is set the default is ALWAYS LEFT, you must explicitly set
554 * blank/null to use ONLY the image as the ActionField.
555 * @return
556 */
557 public void setActionImageLocation(String actionImageLocation) {
558 this.actionImageLocation = actionImageLocation;
559 }
560 }