View Javadoc

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 }