View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.element;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.exception.RiceRuntimeException;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22  import org.kuali.rice.krad.datadictionary.validator.ErrorReport;
23  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
24  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
25  import org.kuali.rice.krad.uif.UifConstants;
26  import org.kuali.rice.krad.uif.UifParameters;
27  import org.kuali.rice.krad.uif.UifPropertyPaths;
28  import org.kuali.rice.krad.uif.component.Component;
29  import org.kuali.rice.krad.uif.component.ComponentSecurity;
30  import org.kuali.rice.krad.uif.field.DataField;
31  import org.kuali.rice.krad.uif.service.ExpressionEvaluatorService;
32  import org.kuali.rice.krad.uif.util.ExpressionUtils;
33  import org.kuali.rice.krad.uif.util.ScriptUtils;
34  import org.kuali.rice.krad.uif.view.FormView;
35  import org.kuali.rice.krad.uif.view.View;
36  
37  import java.util.ArrayList;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  
42  /**
43   * Field that presents an action that can be taken on the UI such as submitting
44   * the form or invoking a script
45   *
46   * @author Kuali Rice Team (rice.collab@kuali.org)
47   */
48  @BeanTag(name="action")
49  public class Action extends ContentElementBase {
50      private static final long serialVersionUID = 1025672792657238829L;
51  
52      private String methodToCall;
53      private String actionEvent;
54      private String navigateToPageId;
55  
56      private String actionScript;
57  
58      private String actionLabel;
59      private Image actionImage;
60      private String actionImagePlacement;
61  
62      private String jumpToIdAfterSubmit;
63      private String jumpToNameAfterSubmit;
64      private String focusOnIdAfterSubmit;
65  
66      private boolean performClientSideValidation;
67      private boolean performDirtyValidation;
68  
69      private String preSubmitCall;
70      private boolean ajaxSubmit;
71  
72      private String ajaxReturnType;
73      private String refreshId;
74      private String refreshPropertyName;
75  
76      private String successCallback;
77      private String errorCallback;
78  
79      private String loadingMessageText;
80      private boolean disableBlocking;
81  
82      private Map<String, String> additionalSubmitData;
83      private Map<String, String> actionParameters;
84  
85      private boolean evaluateDisabledOnKeyUp;
86  
87      private boolean disabled;
88      private String disabledReason;
89      private String disabledExpression;
90      private String disabledConditionJs;
91      private List<String> disabledConditionControlNames;
92  
93      private List<String> disabledWhenChangedPropertyNames;
94      private List<String> enabledWhenChangedPropertyNames;
95  
96      public Action() {
97          super();
98  
99          actionImagePlacement = UifConstants.Position.LEFT.name();
100 
101         ajaxSubmit = true;
102 
103         successCallback = "";
104         errorCallback = "";
105         preSubmitCall = "";
106 
107         additionalSubmitData = new HashMap<String, String>();
108         actionParameters = new HashMap<String, String>();
109 
110         disabled = false;
111         disabledWhenChangedPropertyNames = new ArrayList<String>();
112         enabledWhenChangedPropertyNames = new ArrayList<String>();
113     }
114 
115     /**
116      * Sets the disabledExpression, if any, evaluates it and sets the disabled property
117      *
118      * @param view - view instance to which the component belongs
119      * @param model - Top level object containing the data (could be the form or a
120      * @param parent
121      */
122     public void performApplyModel(View view, Object model, Component parent) {
123         super.performApplyModel(view, model, parent);
124         disabledExpression = this.getPropertyExpression("disabled");
125         if (disabledExpression != null) {
126             ExpressionEvaluatorService expressionEvaluatorService =
127                     KRADServiceLocatorWeb.getExpressionEvaluatorService();
128             disabledExpression = expressionEvaluatorService.replaceBindingPrefixes(view, this, disabledExpression);
129             disabled = (Boolean) expressionEvaluatorService.evaluateExpression(model, this.getContext(),
130                     disabledExpression);
131         }
132     }
133 
134     /**
135      * The following finalization is performed:
136      *
137      * <ul>
138      * <li>Add methodToCall action parameter if set and setup event code for
139      * setting action parameters</li>
140      * <li>Invoke method to build the data attributes and submit data for the action</li>
141      * <li>Compose the final onclick script for the action</li>
142      * <li>Parses the disabled expressions, if any, to equivalent javascript and evaluates the disable/enable when
143      * changed property names</li>
144      * </ul>
145      *
146      * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
147      *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
148      */
149     @Override
150     public void performFinalize(View view, Object model, Component parent) {
151         super.performFinalize(view, model, parent);
152 
153         if (StringUtils.isNotEmpty(disabledExpression)
154                 && !disabledExpression.equalsIgnoreCase("true")
155                 && !disabledExpression.equalsIgnoreCase("false")) {
156             disabledConditionControlNames = new ArrayList<String>();
157             disabledConditionJs = ExpressionUtils.parseExpression(disabledExpression, disabledConditionControlNames);
158         }
159 
160         List<String> adjustedDisablePropertyNames = new ArrayList<String>();
161         for (String propertyName : disabledWhenChangedPropertyNames) {
162             adjustedDisablePropertyNames.add(
163                     KRADServiceLocatorWeb.getExpressionEvaluatorService().replaceBindingPrefixes(view, this,
164                             propertyName));
165         }
166         disabledWhenChangedPropertyNames = adjustedDisablePropertyNames;
167 
168         List<String> adjustedEnablePropertyNames = new ArrayList<String>();
169         for (String propertyName : enabledWhenChangedPropertyNames) {
170             adjustedEnablePropertyNames.add(
171                     KRADServiceLocatorWeb.getExpressionEvaluatorService().replaceBindingPrefixes(view, this,
172                             propertyName));
173         }
174         enabledWhenChangedPropertyNames = adjustedEnablePropertyNames;
175 
176         // clear alt text to avoid screen reader confusion when using image in button with text
177         if (actionImage != null && StringUtils.isNotBlank(actionImagePlacement) && StringUtils.isNotBlank(
178                 actionLabel)) {
179             actionImage.setAltText("");
180         }
181 
182         if (!actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT) && StringUtils.isNotBlank(actionEvent)) {
183             actionParameters.put(UifConstants.UrlParams.ACTION_EVENT, actionEvent);
184         }
185 
186         if (StringUtils.isNotBlank(navigateToPageId)) {
187             actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
188             if (StringUtils.isBlank(methodToCall)) {
189                 actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME,
190                         UifConstants.MethodToCallNames.NAVIGATE);
191             }
192         }
193 
194         if (!actionParameters.containsKey(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME) && StringUtils
195                 .isNotBlank(methodToCall)) {
196             actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME, methodToCall);
197         }
198 
199         setupRefreshAction(view);
200 
201         buildActionData(view, model, parent);
202 
203         // build final onclick script
204         String onClickScript = this.getOnClickScript();
205         if (StringUtils.isNotBlank(actionScript)) {
206             onClickScript = ScriptUtils.appendScript(onClickScript, actionScript);
207         } else {
208             onClickScript = ScriptUtils.appendScript(onClickScript, "actionInvokeHandler(this);");
209         }
210 
211         // add dirty check if it is enabled for the view and the action requires it
212         if (view instanceof FormView) {
213             if (((FormView) view).isApplyDirtyCheck() && performDirtyValidation) {
214                 onClickScript = "if (checkDirty(e) == false) { " + onClickScript + " ; } ";
215             }
216         }
217 
218         //stop action if the action is disabled
219         if (disabled) {
220             this.addStyleClass("disabled");
221             this.setSkipInTabOrder(true);
222         }
223         onClickScript = "if(jQuery(this).hasClass('disabled')){ return false; }" + onClickScript;
224 
225         setOnClickScript("e.preventDefault();" + onClickScript);
226     }
227 
228     /**
229      * When the action is updating a component sets up the refresh script for the component (found by the
230      * given refresh id or refresh property name)
231      *
232      * @param view - view instance the action belongs to
233      */
234     protected void setupRefreshAction(View view) {
235         // if refresh property or id is given, make return type update component
236         if (StringUtils.isNotBlank(refreshPropertyName) || StringUtils.isNotBlank(refreshId)) {
237             ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATECOMPONENT.getKey();
238         }
239 
240         // if refresh property name is given, adjust the binding and then attempt to find the
241         // component in the view index
242         Component refreshComponent = null;
243         if (StringUtils.isNotBlank(refreshPropertyName)) {
244             // TODO: does this support all binding prefixes?
245             if (refreshPropertyName.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
246                 refreshPropertyName = StringUtils.removeStart(refreshPropertyName, UifConstants.NO_BIND_ADJUST_PREFIX);
247             } else if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
248                 refreshPropertyName = view.getDefaultBindingObjectPath() + "." + refreshPropertyName;
249             }
250 
251             DataField dataField = view.getViewIndex().getDataFieldByPath(refreshPropertyName);
252             if (dataField != null) {
253                 refreshComponent = dataField;
254                 refreshId = refreshComponent.getId();
255             }
256         } else if (StringUtils.isNotBlank(refreshId)) {
257             Component component = view.getViewIndex().getComponentById(refreshId);
258             if (component != null) {
259                 refreshComponent = component;
260             }
261         }
262 
263         if (refreshComponent != null) {
264             refreshComponent.setRefreshedByAction(true);
265 
266             // update initial state
267             Component initialComponent = view.getViewIndex().getInitialComponentStates().get(
268                     refreshComponent.getBaseId());
269             if (initialComponent != null) {
270                 initialComponent.setRefreshedByAction(true);
271                 view.getViewIndex().getInitialComponentStates().put(refreshComponent.getBaseId(), initialComponent);
272             }
273         }
274     }
275 
276     /**
277      * Builds the data attributes that will be read client side to determine how to
278      * handle the action and the additional data that should be submitted with the action
279      *
280      * <p>
281      * Note these data attributes will be exposed as a data map client side. The simple attributes (non object
282      * value) are also written out as attributes on the action element.
283      * </p>
284      *
285      * @param view - view instance the action belongs to
286      * @param model - model object containing the view data
287      * @param parent - component the holds the action
288      */
289     protected void buildActionData(View view, Object model, Component parent) {
290         // map properties to data attributes
291         addDataAttribute("ajaxsubmit", Boolean.toString(ajaxSubmit));
292         addDataAttributeIfNonEmpty("successcallback", this.successCallback);
293         addDataAttributeIfNonEmpty("errorcallback", this.errorCallback);
294         addDataAttributeIfNonEmpty("presubmitcall", this.preSubmitCall);
295         addDataAttributeIfNonEmpty("loadingmessage", this.loadingMessageText);
296         addDataAttributeIfNonEmpty("disableblocking", Boolean.toString(this.disableBlocking));
297         addDataAttributeIfNonEmpty("ajaxreturntype", this.ajaxReturnType);
298         addDataAttributeIfNonEmpty("refreshid", this.refreshId);
299         addDataAttribute("validate", Boolean.toString(this.performClientSideValidation));
300 
301         // all action parameters should be submitted with action
302         Map<String, String> submitData = new HashMap<String, String>();
303         for (String key : actionParameters.keySet()) {
304             String parameterPath = key;
305             if (!key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
306                 parameterPath = UifPropertyPaths.ACTION_PARAMETERS + "[" + key + "]";
307             }
308             submitData.put(parameterPath, actionParameters.get(key));
309         }
310 
311         for (String key : additionalSubmitData.keySet()) {
312             submitData.put(key, additionalSubmitData.get(key));
313         }
314 
315         // TODO possibly fix some other way - this is a workaround, prevents
316         // showing history and showing home again on actions which submit the form
317         submitData.put(UifConstants.UrlParams.SHOW_HISTORY, "false");
318         submitData.put(UifConstants.UrlParams.SHOW_HOME, "false");
319 
320         // if focus id not set default to focus on action
321         if (focusOnIdAfterSubmit.equalsIgnoreCase(UifConstants.Order.SELF.toString())) {
322             focusOnIdAfterSubmit = this.getId();
323             submitData.put("focusId", focusOnIdAfterSubmit);
324         }
325         else if(focusOnIdAfterSubmit.equalsIgnoreCase(UifConstants.Order.NEXT_INPUT.toString())){
326             focusOnIdAfterSubmit = UifConstants.Order.NEXT_INPUT.toString() + ":" + this.getId();
327             submitData.put("focusId", focusOnIdAfterSubmit);
328         }
329         else {
330             // Use the id passed in
331             submitData.put("focusId", focusOnIdAfterSubmit);
332         }
333 
334         // if jump to not set default to jump to location of the action
335         if (StringUtils.isBlank(jumpToIdAfterSubmit) && StringUtils.isBlank(jumpToNameAfterSubmit)) {
336             jumpToIdAfterSubmit = this.getId();
337             submitData.put("jumpToId", jumpToIdAfterSubmit);
338         } else if (StringUtils.isNotBlank(jumpToIdAfterSubmit)) {
339             submitData.put("jumpToId", jumpToIdAfterSubmit);
340         } else {
341             submitData.put("jumpToName", jumpToNameAfterSubmit);
342         }
343 
344         addDataAttribute("submitData", ScriptUtils.toJSON(submitData));
345     }
346 
347     /**
348      * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
349      */
350     @Override
351     public List<Component> getComponentsForLifecycle() {
352         List<Component> components = super.getComponentsForLifecycle();
353 
354         components.add(actionImage);
355 
356         return components;
357     }
358 
359     /**
360      * Name of the method that should be called when the action is selected
361      *
362      * <p>
363      * For a server side call (clientSideCall is false), gives the name of the
364      * method in the mapped controller that should be invoked when the action is
365      * selected. For client side calls gives the name of the script function
366      * that should be invoked when the action is selected
367      * </p>
368      *
369      * @return String name of method to call
370      */
371     @BeanTagAttribute(name="methodToCall")
372     public String getMethodToCall() {
373         return this.methodToCall;
374     }
375 
376     /**
377      * Setter for the actions method to call
378      *
379      * @param methodToCall
380      */
381     public void setMethodToCall(String methodToCall) {
382         this.methodToCall = methodToCall;
383     }
384 
385     /**
386      * Label text for the action
387      *
388      * <p>
389      * The label text is used by the template renderers to give a human readable
390      * label for the action. For buttons this generally is the button text,
391      * while for an action link it would be the links displayed text
392      * </p>
393      *
394      * @return String label for action
395      */
396     @BeanTagAttribute(name="actionLabel")
397     public String getActionLabel() {
398         return this.actionLabel;
399     }
400 
401     /**
402      * Setter for the actions label
403      *
404      * @param actionLabel
405      */
406     public void setActionLabel(String actionLabel) {
407         this.actionLabel = actionLabel;
408     }
409 
410     /**
411      * Image to use for the action
412      *
413      * <p>
414      * When the action image component is set (and render is true) the image will be
415      * used to present the action as opposed to the default (input submit). For
416      * action link templates the image is used for the link instead of the
417      * action link text
418      * </p>
419      *
420      * @return Image action image
421      */
422     @BeanTagAttribute(name="actionImage",type=BeanTagAttribute.AttributeType.SINGLEBEAN)
423     public Image getActionImage() {
424         return this.actionImage;
425     }
426 
427     /**
428      * Setter for the action image field
429      *
430      * @param actionImage
431      */
432     public void setActionImage(Image actionImage) {
433         this.actionImage = actionImage;
434     }
435 
436     /**
437      * For an <code>Action</code> that is part of a
438      * <code>NavigationGroup</code, the navigate to page id can be set to
439      * configure the page that should be navigated to when the action is
440      * selected
441      *
442      * <p>
443      * Support exists in the <code>UifControllerBase</code> for handling
444      * navigation between pages
445      * </p>
446      *
447      * @return String id of page that should be rendered when the action item is
448      *         selected
449      */
450     @BeanTagAttribute(name="navigateToPageId")
451     public String getNavigateToPageId() {
452         return this.navigateToPageId;
453     }
454 
455     /**
456      * Setter for the navigate to page id
457      *
458      * @param navigateToPageId
459      */
460     public void setNavigateToPageId(String navigateToPageId) {
461         this.navigateToPageId = navigateToPageId;
462         actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
463         this.methodToCall = UifConstants.MethodToCallNames.NAVIGATE;
464     }
465 
466     /**
467      * Name of the event that will be set when the action is invoked
468      *
469      * <p>
470      * Action events can be looked at by the view or components in order to render differently depending on
471      * the action requested.
472      * </p>
473      *
474      * @return String action event name
475      * @see org.kuali.rice.krad.uif.UifConstants.ActionEvents
476      */
477     @BeanTagAttribute(name="actionEvent")
478     public String getActionEvent() {
479         return actionEvent;
480     }
481 
482     /**
483      * Setter for the action event
484      *
485      * @param actionEvent
486      */
487     public void setActionEvent(String actionEvent) {
488         this.actionEvent = actionEvent;
489     }
490 
491     /**
492      * Map of additional data that will be posted when the action is invoked
493      *
494      * <p>
495      * Each entry in this map will be sent as a request parameter when the action is chosen. Note this in
496      * addition to the form data that is sent. For example, suppose the model contained a property named
497      * number and a boolean named showActive, we can send values for this properties by adding the following
498      * entries to this map:
499      * {'number':'a13', 'showActive', 'true'}
500      * </p>
501      *
502      * <p>
503      * The additionalSubmitData map is different from the actionParameters map. All name/value pairs given as
504      * actionParameters populated the form map actionParameters. While name/value pair given in additionalSubmitData
505      * populate different form (model) properties
506      * </p>
507      *
508      * @return Map<String, String> additional key/value pairs to submit
509      */
510     @BeanTagAttribute(name="additionalSubmitData", type = BeanTagAttribute.AttributeType.MAPVALUE)
511     public Map<String, String> getAdditionalSubmitData() {
512         return additionalSubmitData;
513     }
514 
515     /**
516      * Setter for map holding additional data to post
517      *
518      * @param additionalSubmitData
519      */
520     public void setAdditionalSubmitData(Map<String, String> additionalSubmitData) {
521         this.additionalSubmitData = additionalSubmitData;
522     }
523 
524     /**
525      * Parameters that should be sent when the action is invoked
526      *
527      * <p>
528      * Action renderer will decide how the parameters are sent for the action
529      * (via script generated hiddens, or script parameters, ...)
530      * </p>
531      *
532      * <p>
533      * Can be set by other components such as the <code>CollectionGroup</code>
534      * to provide the context the action is in (such as the collection name and
535      * line the action applies to)
536      * </p>
537      *
538      * @return Map<String, String> action parameters
539      */
540     @BeanTagAttribute(name="actionParameters", type= BeanTagAttribute.AttributeType.MAPVALUE)
541     public Map<String, String> getActionParameters() {
542         return this.actionParameters;
543     }
544 
545     /**
546      * Setter for the action parameters
547      *
548      * @param actionParameters
549      */
550     public void setActionParameters(Map<String, String> actionParameters) {
551         this.actionParameters = actionParameters;
552     }
553 
554     /**
555      * Convenience method to add a parameter to the action parameters Map
556      *
557      * @param parameterName - name of parameter to add
558      * @param parameterValue - value of parameter to add
559      */
560     public void addActionParameter(String parameterName, String parameterValue) {
561         if (actionParameters == null) {
562             this.actionParameters = new HashMap<String, String>();
563         }
564 
565         this.actionParameters.put(parameterName, parameterValue);
566     }
567 
568     /**
569      * Get an actionParameter by name
570      */
571     public String getActionParameter(String parameterName) {
572         return this.actionParameters.get(parameterName);
573     }
574 
575     /**
576      * Override to assert a {@link ActionSecurity} instance is set
577      *
578      * @param componentSecurity - instance of ActionSecurity
579      */
580     @Override
581     public void setComponentSecurity(ComponentSecurity componentSecurity) {
582         if (!(componentSecurity instanceof ActionSecurity)) {
583             throw new RiceRuntimeException("Component security for Action should be instance of ActionSecurity");
584         }
585 
586         super.setComponentSecurity(componentSecurity);
587     }
588 
589     @Override
590     protected Class<? extends ComponentSecurity> getComponentSecurityClass() {
591         return ActionSecurity.class;
592     }
593 
594     /**
595      * @return the jumpToIdAfterSubmit
596      */
597     @BeanTagAttribute(name="jumpToIdAfterSubmit")
598     public String getJumpToIdAfterSubmit() {
599         return this.jumpToIdAfterSubmit;
600     }
601 
602     /**
603      * The id to jump to in the next page, the element with this id will be
604      * jumped to automatically when the new page is retrieved after a submit.
605      * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the
606      * resulting page. Passing in nothing for both jumpToIdAfterSubmit and
607      * jumpToNameAfterSubmit will result in this Action being jumped to by
608      * default if it is present on the new page. WARNING: jumpToIdAfterSubmit
609      * always takes precedence over jumpToNameAfterSubmit, if set.
610      *
611      * @param jumpToIdAfterSubmit the jumpToIdAfterSubmit to set
612      */
613     public void setJumpToIdAfterSubmit(String jumpToIdAfterSubmit) {
614         this.jumpToIdAfterSubmit = jumpToIdAfterSubmit;
615     }
616 
617     /**
618      * The name to jump to in the next page, the element with this name will be
619      * jumped to automatically when the new page is retrieved after a submit.
620      * Passing in nothing for both jumpToIdAfterSubmit and jumpToNameAfterSubmit
621      * will result in this Action being jumped to by default if it is
622      * present on the new page. WARNING: jumpToIdAfterSubmit always takes
623      * precedence over jumpToNameAfterSubmit, if set.
624      *
625      * @return the jumpToNameAfterSubmit
626      */
627     @BeanTagAttribute(name="jumpToNameAfterSubmit")
628     public String getJumpToNameAfterSubmit() {
629         return this.jumpToNameAfterSubmit;
630     }
631 
632     /**
633      * @param jumpToNameAfterSubmit the jumpToNameAfterSubmit to set
634      */
635     public void setJumpToNameAfterSubmit(String jumpToNameAfterSubmit) {
636         this.jumpToNameAfterSubmit = jumpToNameAfterSubmit;
637     }
638 
639     /**
640      * The element to place focus on in the new page after the new page
641      * is retrieved.
642      *
643      * <p>The following are allowed:
644      * <ul>
645      * <li>A valid element id</li>
646      * <li>"FIRST" will focus on the first visible input element on the form</li>
647      * <li>"SELF" will result in this Action being focused (action bean defaults to "SELF")</li>
648      * <li>"LINE_FIRST" will result in the first input of the collection line to be focused (if available)</li>
649      * <li>"NEXT_INPUT" will result in the next available input that exists after this Action to be focused
650      * (only if this action still exists on the page)</li>
651      * </ul>
652      * </p>
653      *
654      * @return the focusOnAfterSubmit
655      */
656     @BeanTagAttribute(name="focusOnIdAfterSubmit")
657     public String getFocusOnIdAfterSubmit() {
658         return this.focusOnIdAfterSubmit;
659     }
660 
661     /**
662      * @param focusOnIdAfterSubmit the focusOnAfterSubmit to set
663      */
664     public void setFocusOnIdAfterSubmit(String focusOnIdAfterSubmit) {
665         this.focusOnIdAfterSubmit = focusOnIdAfterSubmit;
666     }
667 
668     /**
669      * Indicates whether the form data should be validated on the client side
670      *
671      * return true if validation should occur, false otherwise
672      */
673     @BeanTagAttribute(name="performClientSideValidation")
674     public boolean isPerformClientSideValidation() {
675         return this.performClientSideValidation;
676     }
677 
678     /**
679      * Setter for the client side validation flag
680      *
681      * @param performClientSideValidation
682      */
683     public void setPerformClientSideValidation(boolean performClientSideValidation) {
684         this.performClientSideValidation = performClientSideValidation;
685     }
686 
687     /**
688      * Client side javascript to be executed when this actionField is clicked
689      *
690      * <p>
691      * This overrides the default action for this Action so the method
692      * called must explicitly submit, navigate, etc. through js, if necessary.
693      * In addition, this js occurs AFTER onClickScripts set on this field, it
694      * will be the last script executed by the click event. Sidenote: This js is
695      * always called after hidden actionParameters and methodToCall methods are
696      * written by the js to the html form.
697      * </p>
698      *
699      * @return the actionScript
700      */
701     @BeanTagAttribute(name="actionScript")
702     public String getActionScript() {
703         return this.actionScript;
704     }
705 
706     /**
707      * @param actionScript the actionScript to set
708      */
709     public void setActionScript(String actionScript) {
710         if (!StringUtils.endsWith(actionScript, ";")) {
711             actionScript = actionScript + ";";
712         }
713         this.actionScript = actionScript;
714     }
715 
716     /**
717      * @param performDirtyValidation the blockValidateDirty to set
718      */
719     public void setPerformDirtyValidation(boolean performDirtyValidation) {
720         this.performDirtyValidation = performDirtyValidation;
721     }
722 
723     /**
724      * @return the blockValidateDirty
725      */
726     @BeanTagAttribute(name="performDirtyValidation")
727     public boolean isPerformDirtyValidation() {
728         return performDirtyValidation;
729     }
730 
731     /**
732      * Indicates whether the action (input or button) is disabled (doesn't allow interaction)
733      *
734      * @return boolean true if the action field is disabled, false if not
735      */
736     @BeanTagAttribute(name="disabled")
737     public boolean isDisabled() {
738         return disabled;
739     }
740 
741     /**
742      * Setter for the disabled indicator
743      *
744      * @param disabled
745      */
746     public void setDisabled(boolean disabled) {
747         this.disabled = disabled;
748     }
749 
750     /**
751      * If the action field is disabled, gives a reason for why which will be displayed as a tooltip
752      * on the action field (button)
753      *
754      * @return String disabled reason text
755      * @see {@link #isDisabled()}
756      */
757     @BeanTagAttribute(name="disabledReason")
758     public String getDisabledReason() {
759         return disabledReason;
760     }
761 
762     /**
763      * Setter for the disabled reason text
764      *
765      * @param disabledReason
766      */
767     public void setDisabledReason(String disabledReason) {
768         this.disabledReason = disabledReason;
769     }
770     @BeanTagAttribute(name="actionImagePlacement")
771     public String getActionImagePlacement() {
772         return actionImagePlacement;
773     }
774 
775     /**
776      * Set to TOP, BOTTOM, LEFT, RIGHT to position image at that location within the button.
777      * For the subclass ActionLinkField only LEFT and RIGHT are allowed.  When set to blank/null/IMAGE_ONLY, the image
778      * itself will be the Action, if no value is set the default is ALWAYS LEFT, you must explicitly set
779      * blank/null/IMAGE_ONLY to use ONLY the image as the Action.
780      *
781      * @return
782      */
783     public void setActionImagePlacement(String actionImagePlacement) {
784         this.actionImagePlacement = actionImagePlacement;
785     }
786 
787     /**
788      * Gets the script which needs to be invoked before the form is submitted
789      *
790      * <p>
791      * The preSubmitCall can carry out custom logic for the action before the submit occurs. The value should
792      * be given as one or more lines of script and should return a boolean. If false is returned from the call,
793      * the submit is not carried out. Furthermore, the preSubmitCall can refer to the request object through the
794      * variable 'kradRequest' or 'this'. This gives full access over the request for doing such things as
795      * adding additional data
796      * </p>
797      *
798      * <p>
799      * Examples 'return doFunction(kradRequest);', 'var valid=true;return valid;'
800      * </p>
801      *
802      * <p>
803      * The preSubmit call will be invoked both for ajax and non-ajax submits
804      * </p>
805      *
806      * @return String script text that will be invoked before form submission
807      */
808     @BeanTagAttribute(name="preSubmitCall")
809     public String getPreSubmitCall() {
810         return preSubmitCall;
811     }
812 
813     /**
814      * Setter for preSubmitCall
815      *
816      * @param preSubmitCall
817      */
818     public void setPreSubmitCall(String preSubmitCall) {
819         this.preSubmitCall = preSubmitCall;
820     }
821 
822     /**
823      * When this property is set to true it will submit the form using Ajax instead of the browser submit. Will default
824      * to updating the page contents
825      *
826      * @return boolean
827      */
828     @BeanTagAttribute(name="ajaxSubmit")
829     public boolean isAjaxSubmit() {
830         return ajaxSubmit;
831     }
832 
833     /**
834      * Setter for ajaxSubmit
835      *
836      * @param ajaxSubmit
837      */
838     public void setAjaxSubmit(boolean ajaxSubmit) {
839         this.ajaxSubmit = ajaxSubmit;
840     }
841 
842     /**
843      * Gets the return type for the ajax call
844      *
845      * <p>
846      * The ajax return type indicates how the response content will be handled in the client. Typical
847      * examples include updating a component, the page, or doing a redirect.
848      * </p>
849      *
850      * @return String return type
851      * @see org.kuali.rice.krad.uif.UifConstants.AjaxReturnTypes
852      */
853     @BeanTagAttribute(name="ajaxReturnType")
854     public String getAjaxReturnType() {
855         return this.ajaxReturnType;
856     }
857 
858     /**
859      * Setter for the type of ajax return
860      *
861      * @param ajaxReturnType
862      */
863     public void setAjaxReturnType(String ajaxReturnType) {
864         this.ajaxReturnType = ajaxReturnType;
865     }
866 
867     /**
868      * Indicates if the action response should be displayed in a lightbox
869      *
870      * @return boolean true if response should be rendered in a lightbox, false if not
871      */
872     @BeanTagAttribute(name="displayResponseInLightBox")
873     public boolean isDisplayResponseInLightBox() {
874         return StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey());
875     }
876 
877     /**
878      * Setter for indicating the response should be rendered in a lightbox
879      *
880      * @param displayResponseInLightBox
881      */
882     public void setDisplayResponseInLightBox(boolean displayResponseInLightBox) {
883         if (displayResponseInLightBox) {
884             this.ajaxReturnType = UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey();
885         }
886         // if display lightbox is false and it was previously true, set to default of update page
887         else if (StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey())) {
888             this.ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATEPAGE.getKey();
889         }
890     }
891 
892     /**
893      * Gets the script which will be invoked on a successful ajax call
894      *
895      * <p>
896      * The successCallback can carry out custom logic after a successful ajax submission has been made. The
897      * value can contain one or more script statements. In addition, the response contents can be accessed
898      * through the variable 'responseContents'
899      * </p>
900      *
901      * <p>
902      * Examples 'handleSuccessfulUpdate(responseContents);'
903      * </p>
904      *
905      * <p>
906      * The successCallback may only be specified when {@link #isAjaxSubmit()} is true
907      * </p>
908      *
909      * @return String containing script to be executed when the action is successful
910      */
911     @BeanTagAttribute(name="successCallback")
912     public String getSuccessCallback() {
913         return successCallback;
914     }
915 
916     /**
917      * Setter for successCallback
918      *
919      * @param successCallback
920      */
921     public void setSuccessCallback(String successCallback) {
922         this.successCallback = successCallback;
923     }
924 
925     /**
926      * Gets the script which will be invoked when the action fails due to problems in the ajax call or
927      * the return of an incident report
928      *
929      * <p>
930      * The errorCallback can carry out custom logic after a failed ajax submission. The
931      * value can contain one or more script statements. In addition, the response contents can be accessed
932      * through the variable 'responseContents'
933      * </p>
934      *
935      * <p>
936      * Examples 'handleFailedUpdate(responseContents);'
937      * </p>
938      *
939      * <p>
940      * The errorCallback may only be specified when {@link #isAjaxSubmit()} is true
941      * </p>
942      *
943      * @return String containing script to be executed when the action is successful
944      */
945     @BeanTagAttribute(name="errorCallback")
946     public String getErrorCallback() {
947         return errorCallback;
948     }
949 
950     /**
951      * Setter for errorCallback
952      *
953      * @param errorCallback
954      */
955     public void setErrorCallback(String errorCallback) {
956         this.errorCallback = errorCallback;
957     }
958 
959     /**
960      * Id for the component that should be refreshed after the action completes
961      *
962      * <p>
963      * Either refresh id or refresh property name can be set to configure the component that should
964      * be refreshed after the action completes. If both are blank, the page will be refreshed
965      * </p>
966      *
967      * @return String valid component id
968      */
969     @BeanTagAttribute(name="refreshId")
970     public String getRefreshId() {
971         return refreshId;
972     }
973 
974     /**
975      * Setter for the component refresh id
976      *
977      * @param refreshId
978      */
979     public void setRefreshId(String refreshId) {
980         this.refreshId = refreshId;
981     }
982 
983     /**
984      * Property name for the {@link org.kuali.rice.krad.uif.field.DataField} that should be refreshed after the action
985      * completes
986      *
987      * <p>
988      * Either refresh id or refresh property name can be set to configure the component that should
989      * be refreshed after the action completes. If both are blank, the page will be refreshed
990      * </p>
991      *
992      * <p>
993      * Property name will be adjusted to use the default binding path unless it contains the form prefix
994      * </p>
995      *
996      * @return String valid property name with an associated DataField
997      * @see org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX
998      */
999     @BeanTagAttribute(name="refreshPropertyName")
1000     public String getRefreshPropertyName() {
1001         return refreshPropertyName;
1002     }
1003 
1004     /**
1005      * Setter for the property name of the DataField that should be refreshed
1006      *
1007      * @param refreshPropertyName
1008      */
1009     public void setRefreshPropertyName(String refreshPropertyName) {
1010         this.refreshPropertyName = refreshPropertyName;
1011     }
1012 
1013     /**
1014      * Gets the loading message used by action's blockUI
1015      *
1016      * @returns String if String is not null, used in place of loading message
1017      */
1018     @BeanTagAttribute(name="loadingMessageText")
1019     public String getLoadingMessageText() {
1020         return loadingMessageText;
1021     }
1022 
1023     /**
1024      * When this property is set, it is used in place of the loading message text used by the blockUI
1025      *
1026      * @param loadingMessageText
1027      */
1028     public void setLoadingMessageText(String loadingMessageText) {
1029         this.loadingMessageText = loadingMessageText;
1030     }
1031 
1032     /**
1033      * Indicates whether blocking for the action should be disabled
1034      *
1035      * <p>
1036      * By default when an action is invoked part of the page or the entire window is blocked until
1037      * the action completes. If this property is set to true the blocking will not be displayed.
1038      * </p>
1039      *
1040      * <p>
1041      * Currently if an action returns a file download, this property should be set to true. If not, the blocking
1042      * will never get unblocked (because the page does not get notification a file was downloaded)
1043      * </p>
1044      *
1045      * @return boolean true if blocking should be disabled, false if not
1046      */
1047     @BeanTagAttribute(name="disableBlocking")
1048     public boolean isDisableBlocking() {
1049         return disableBlocking;
1050     }
1051 
1052     /**
1053      * Setter for disabling blocking when the action is invoked
1054      *
1055      * @param disableBlocking
1056      */
1057     public void setDisableBlocking(boolean disableBlocking) {
1058         this.disableBlocking = disableBlocking;
1059     }
1060 
1061     /**
1062      * @see org.kuali.rice.krad.uif.component.Component#completeValidation
1063      */
1064     @Override
1065     public void completeValidation(ValidationTrace tracer) {
1066         tracer.addBean(this);
1067 
1068         // Checks that a label or image ui is presence
1069         if (getActionLabel() == null && getActionImage() == null) {
1070             String currentValues [] = {"actionLabel =" + getActionLabel(),"actionImage =" + getActionImage()};
1071             tracer.createError("ActionLabel and/or actionImage must be set",currentValues);
1072         }
1073 
1074         // Checks that an action is set
1075         if (getJumpToIdAfterSubmit() != null && getJumpToNameAfterSubmit() != null) {
1076             String currentValues [] = {"jumpToIdAfterSubmit =" + getJumpToIdAfterSubmit(),"jumpToNameAfterSubmit =" + getJumpToNameAfterSubmit()};
1077             tracer.createWarning("Only 1 jumpTo property should be set",currentValues);
1078         }
1079         super.completeValidation(tracer.getCopy());
1080     }
1081 
1082     /**
1083      * Evaluate the disable condition on controls which disable it on each key up event
1084      *
1085      * @return true if evaluate on key up, false otherwise
1086      */
1087     @BeanTagAttribute(name="evaluateDisabledOnKeyUp")
1088     public boolean isEvaluateDisabledOnKeyUp() {
1089         return evaluateDisabledOnKeyUp;
1090     }
1091 
1092     /**
1093      * Set evaluateDisableOnKeyUp
1094      *
1095      * @param evaluateDisabledOnKeyUp
1096      */
1097     public void setEvaluateDisabledOnKeyUp(boolean evaluateDisabledOnKeyUp) {
1098         this.evaluateDisabledOnKeyUp = evaluateDisabledOnKeyUp;
1099     }
1100 
1101     /**
1102      * Get the disable condition js derived from the springEL, cannot be set.
1103      *
1104      * @return the disableConditionJs javascript to be evaluated
1105      */
1106     public String getDisabledConditionJs() {
1107         return disabledConditionJs;
1108     }
1109 
1110     /**
1111      * Control names to add handlers to for disable functionality, cannot be set
1112      *
1113      * @return control names to add handlers to for disable
1114      */
1115     public List<String> getDisabledConditionControlNames() {
1116         return disabledConditionControlNames;
1117     }
1118 
1119     /**
1120      * Gets the property names of fields that when changed, will disable this component
1121      *
1122      * @return the property names to monitor for change to disable this component
1123      */
1124     @BeanTagAttribute(name="disabledWhenChangedPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
1125     public List<String> getDisabledWhenChangedPropertyNames() {
1126         return disabledWhenChangedPropertyNames;
1127     }
1128 
1129     /**
1130      * Sets the property names of fields that when changed, will disable this component
1131      *
1132      * @param disabledWhenChangedPropertyNames
1133      */
1134     public void setDisabledWhenChangedPropertyNames(List<String> disabledWhenChangedPropertyNames) {
1135         this.disabledWhenChangedPropertyNames = disabledWhenChangedPropertyNames;
1136     }
1137 
1138     /**
1139      * Gets the property names of fields that when changed, will enable this component
1140      *
1141      * @return the property names to monitor for change to enable this component
1142      */
1143     @BeanTagAttribute(name="enabledWhenChangedPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
1144     public List<String> getEnabledWhenChangedPropertyNames() {
1145         return enabledWhenChangedPropertyNames;
1146     }
1147 
1148     /**
1149      * Sets the property names of fields that when changed, will enable this component
1150      *
1151      * @param enabledWhenChangedPropertyNames
1152      */
1153     public void setEnabledWhenChangedPropertyNames(List<String> enabledWhenChangedPropertyNames) {
1154         this.enabledWhenChangedPropertyNames = enabledWhenChangedPropertyNames;
1155     }
1156 }