View Javadoc
1   /**
2    * Copyright 2005-2014 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 java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.rice.core.api.exception.RiceRuntimeException;
25  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
26  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
27  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
28  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
29  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
30  import org.kuali.rice.krad.uif.UifConstants;
31  import org.kuali.rice.krad.uif.UifParameters;
32  import org.kuali.rice.krad.uif.UifPropertyPaths;
33  import org.kuali.rice.krad.uif.component.Component;
34  import org.kuali.rice.krad.uif.component.ComponentSecurity;
35  import org.kuali.rice.krad.uif.field.DataField;
36  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
37  import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
38  import org.kuali.rice.krad.uif.util.LifecycleElement;
39  import org.kuali.rice.krad.uif.util.ScriptUtils;
40  import org.kuali.rice.krad.uif.util.UrlInfo;
41  import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
42  import org.kuali.rice.krad.uif.view.FormView;
43  import org.kuali.rice.krad.uif.view.View;
44  import org.kuali.rice.krad.util.KRADUtils;
45  
46  /**
47   * Field that presents an action that can be taken on the UI such as submitting
48   * the form or invoking a script.
49   *
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  @BeanTags({@BeanTag(name = "action-bean", parent = "Uif-Action"),
53          @BeanTag(name = "actionImage-bean", parent = "Uif-ActionImage"),
54          @BeanTag(name = "primaryActionButton-bean", parent = "Uif-PrimaryActionButton"),
55          @BeanTag(name = "secondaryActionButton-bean", parent = "Uif-SecondaryActionButton"),
56          @BeanTag(name = "primaryActionButton-small-bean", parent = "Uif-PrimaryActionButton-Small"),
57          @BeanTag(name = "secondaryActionButton-small-bean", parent = "Uif-SecondaryActionButton-Small"),
58          @BeanTag(name = "actionLink-bean", parent = "Uif-ActionLink"),
59          @BeanTag(name = "navigationActionLink-bean", parent = "Uif-NavigationActionLink"),
60          @BeanTag(name = "navigationActionButton-bean", parent = "Uif-NavigationActionButton"),
61          @BeanTag(name = "secondaryNavigationActionButton-bean", parent = "Uif-SecondaryNavigationActionButton"),
62          @BeanTag(name = "helpAction-bean", parent = "Uif-HelpAction"),
63          @BeanTag(name = "saveAction-bean", parent = "Uif-SaveAction"),
64          @BeanTag(name = "backAction-bean", parent = "Uif-BackAction"),
65          @BeanTag(name = "cancelAction-bean", parent = "Uif-CancelAction"),
66          @BeanTag(name = "checkFormAction-bean", parent = "Uif-CheckFormAction"),
67          @BeanTag(name = "addLineAction-bean", parent = "Uif-AddLineAction"),
68          @BeanTag(name = "deleteLineAction-bean", parent = "Uif-DeleteLineAction"),
69          @BeanTag(name = "saveLineAction-bean", parent = "Uif-SaveLineAction"),
70          @BeanTag(name = "addBlankLineAction-bean", parent = "Uif-AddBlankLineAction"),
71          @BeanTag(name = "addViaLightBoxAction-bean", parent = "Uif-AddViaLightBoxAction"),
72          @BeanTag(name = "toggleRowDetailsAction-bean", parent = "Uif-ToggleRowDetailsAction"),
73          @BeanTag(name = "expandDetailsAction-bean", parent = "Uif-ExpandDetailsAction"),
74          @BeanTag(name = "expandDetailsImageAction-bean", parent = "Uif-ExpandDetailsImageAction"),
75          @BeanTag(name = "jumpToTopLink-bean", parent = "Uif-JumpToTopLink"),
76          @BeanTag(name = "jumpToBottomLink-bean", parent = "Uif-JumpToBottomLink"),
77          @BeanTag(name = "expandDisclosuresButton-bean", parent = "Uif-ExpandDisclosuresButton"),
78          @BeanTag(name = "collapseDisclosuresButton-bean", parent = "Uif-CollapseDisclosuresButton"),
79          @BeanTag(name = "showInactiveCollectionItemsButton-bean", parent = "Uif-ShowInactiveCollectionItemsButton"),
80          @BeanTag(name = "hideInactiveCollectionItemsButton-bean", parent = "Uif-HideInactiveCollectionItemsButton"),
81          @BeanTag(name = "collectionQuickFinderAction-bean", parent = "Uif-CollectionQuickFinderAction")})
82  public class Action extends ContentElementBase {
83      private static final long serialVersionUID = 1025672792657238829L;
84  
85      private String methodToCall;
86      private String actionEvent;
87      private String navigateToPageId;
88  
89      private String actionScript;
90      private UrlInfo actionUrl;
91  
92      private String actionLabel;
93      private boolean renderInnerTextSpan;
94  
95      private Image actionImage;
96      private String actionImagePlacement;
97  
98      private String iconClass;
99      private String actionIconPlacement;
100 
101     private String jumpToIdAfterSubmit;
102     private String jumpToNameAfterSubmit;
103     private String focusOnIdAfterSubmit;
104 
105     private boolean performClientSideValidation;
106     private boolean performDirtyValidation;
107     private boolean clearDirtyOnAction;
108     private boolean dirtyOnAction;
109 
110     private String preSubmitCall;
111     private boolean ajaxSubmit;
112 
113     private String ajaxReturnType;
114     private String refreshId;
115     private String refreshPropertyName;
116 
117     private String successCallback;
118     private String errorCallback;
119 
120     private String loadingMessageText;
121     private boolean disableBlocking;
122 
123     private Map<String, String> additionalSubmitData;
124     private Map<String, String> actionParameters;
125 
126     private boolean evaluateDisabledOnKeyUp;
127 
128     private boolean disabled;
129     private String disabledReason;
130     private String disabledExpression;
131     private String disabledConditionJs;
132     private List<String> disabledConditionControlNames;
133 
134     private List<String> disabledWhenChangedPropertyNames;
135     private List<String> enabledWhenChangedPropertyNames;
136 
137     /**
138      * Sets initial field values and initializes collections.
139      */
140     public Action() {
141         super();
142 
143         actionImagePlacement = UifConstants.Position.LEFT.name();
144         actionIconPlacement = UifConstants.Position.LEFT.name();
145 
146         ajaxSubmit = true;
147 
148         successCallback = "";
149         errorCallback = "";
150         preSubmitCall = "";
151 
152         additionalSubmitData = new HashMap<String, String>();
153         actionParameters = new HashMap<String, String>();
154 
155         disabled = false;
156         disabledWhenChangedPropertyNames = new ArrayList<String>();
157         enabledWhenChangedPropertyNames = new ArrayList<String>();
158     }
159 
160     /**
161      * Sets the disabledExpression, if any, evaluates it and sets the disabled property.
162      *
163      * @param model top level object containing the data (could be the form or a
164      * @param parent parent component
165      */
166     public void performApplyModel(Object model, LifecycleElement parent) {
167         super.performApplyModel(model, parent);
168 
169         disabledExpression = this.getPropertyExpression("disabled");
170         if (disabledExpression != null) {
171             ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
172 
173             disabledExpression = expressionEvaluator.replaceBindingPrefixes(ViewLifecycle.getView(), this,
174                     disabledExpression);
175             disabled = (Boolean) expressionEvaluator.evaluateExpression(this.getContext(), disabledExpression);
176         }
177 
178         if (actionUrl != null) {
179             ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(actionUrl, false);
180             ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(ViewLifecycle.getView(),
181                     actionUrl, this.getContext());
182         }
183     }
184 
185     /**
186      * The following finalization is performed:
187      *
188      * <ul>
189      * <li>Add methodToCall action parameter if set and setup event code for
190      * setting action parameters</li>
191      * <li>Invoke method to build the data attributes and submit data for the action</li>
192      * <li>Compose the final onclick script for the action</li>
193      * <li>Parses the disabled expressions, if any, to equivalent javascript and evaluates the disable/enable when
194      * changed property names</li>
195      * </ul>
196      *
197      * {@inheritDoc}
198      */
199     @Override
200     public void performFinalize(Object model, LifecycleElement parent) {
201         super.performFinalize(model, parent);
202 
203         View view = ViewLifecycle.getView();
204         ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
205 
206         if (StringUtils.isNotEmpty(disabledExpression)
207                 && !disabledExpression.equalsIgnoreCase("true")
208                 && !disabledExpression.equalsIgnoreCase("false")) {
209             disabledConditionControlNames = new ArrayList<String>();
210             disabledConditionJs = ViewLifecycle.getExpressionEvaluator().parseExpression(disabledExpression,
211                     disabledConditionControlNames, this.getContext());
212         }
213 
214         List<String> adjustedDisablePropertyNames = new ArrayList<String>();
215         for (String propertyName : disabledWhenChangedPropertyNames) {
216             adjustedDisablePropertyNames.add(expressionEvaluator.replaceBindingPrefixes(view, this, propertyName));
217         }
218         disabledWhenChangedPropertyNames = adjustedDisablePropertyNames;
219 
220         List<String> adjustedEnablePropertyNames = new ArrayList<String>();
221         for (String propertyName : enabledWhenChangedPropertyNames) {
222             adjustedEnablePropertyNames.add(expressionEvaluator.replaceBindingPrefixes(view, this, propertyName));
223         }
224         enabledWhenChangedPropertyNames = adjustedEnablePropertyNames;
225 
226         // clear alt text to avoid screen reader confusion when using image in button with text
227         if (actionImage != null && StringUtils.isNotBlank(actionImagePlacement) && StringUtils.isNotBlank(
228                 actionLabel)) {
229             actionImage.setAltText("");
230         }
231 
232         // when icon only is set, add the icon class to the action
233         if (StringUtils.isNotBlank(iconClass) && (UifConstants.ICON_ONLY_PLACEMENT.equals(actionIconPlacement)
234                 || StringUtils.isBlank(actionLabel))) {
235             getCssClasses().add(iconClass);
236 
237             // force icon only placement
238             actionIconPlacement = UifConstants.ICON_ONLY_PLACEMENT;
239         }
240 
241         if (!actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT) && StringUtils.isNotBlank(actionEvent)) {
242             actionParameters.put(UifConstants.UrlParams.ACTION_EVENT, actionEvent);
243         }
244 
245         if (StringUtils.isNotBlank(navigateToPageId)) {
246             actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
247             if (StringUtils.isBlank(methodToCall)) {
248                 this.methodToCall = UifConstants.MethodToCallNames.NAVIGATE;
249             }
250         }
251 
252         if (!actionParameters.containsKey(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME) && StringUtils
253                 .isNotBlank(methodToCall)) {
254             actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME, methodToCall);
255         }
256 
257         setupRefreshAction(view);
258 
259         // Apply dirty check if it is enabled for the view and the action requires it
260         if (view instanceof FormView) {
261             performDirtyValidation = performDirtyValidation && ((FormView) view).isApplyDirtyCheck();
262         }
263 
264         if (StringUtils.isBlank(getActionScript()) && (actionUrl != null) && actionUrl.isFullyConfigured()) {
265             String actionScript = ScriptUtils.buildFunctionCall(UifConstants.JsFunctions.REDIRECT, actionUrl.getHref());
266 
267             setActionScript(actionScript);
268         }
269 
270         // add additional submit data as accessible binding paths, and method to call as accessible method
271         if (isRender()) {
272             for (String additionalSubmitPath : additionalSubmitData.keySet()) {
273                 ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(additionalSubmitPath);
274             }
275 
276             if (StringUtils.isNotBlank(methodToCall)) {
277                 ViewLifecycle.getViewPostMetadata().addAccessibleMethodToCall(methodToCall);
278             }
279         }
280 
281         buildActionData(view, model, parent);
282     }
283 
284     /**
285      * When the action is updating a component sets up the refresh script for the component (found by the
286      * given refresh id or refresh property name.
287      *
288      * @param view view instance the action belongs to
289      */
290     protected void setupRefreshAction(View view) {
291         // if refresh property or id is given, make return type update component
292         // TODO: what if the refresh id is the page id? we should set the return type as update page
293         if (StringUtils.isNotBlank(refreshPropertyName) || StringUtils.isNotBlank(refreshId)) {
294             ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATECOMPONENT.getKey();
295         }
296 
297         // if refresh property name is given, adjust the binding and then attempt to find the
298         // component in the view index
299         Component refreshComponent = null;
300         if (StringUtils.isNotBlank(refreshPropertyName)) {
301             // TODO: does this support all binding prefixes?
302             if (refreshPropertyName.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
303                 refreshPropertyName = StringUtils.removeStart(refreshPropertyName, UifConstants.NO_BIND_ADJUST_PREFIX);
304             } else if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
305                 refreshPropertyName = view.getDefaultBindingObjectPath() + "." + refreshPropertyName;
306             }
307 
308             DataField dataField = view.getViewIndex().getDataFieldByPath(refreshPropertyName);
309             if (dataField != null) {
310                 refreshComponent = dataField;
311                 refreshId = refreshComponent.getId();
312             }
313         } else if (StringUtils.isNotBlank(refreshId)) {
314             Component component = view.getViewIndex().getComponentById(refreshId);
315             if (component != null) {
316                 refreshComponent = component;
317             }
318         }
319 
320         if (refreshComponent != null) {
321             refreshComponent.setRefreshedByAction(true);
322         }
323     }
324 
325     /**
326      * Builds the data attributes that will be read client side to determine how to
327      * handle the action and the additional data that should be submitted with the action
328      *
329      * <p>
330      * Note these data attributes will be exposed as a data map client side. The simple attributes (non object
331      * value) are also written out as attributes on the action element.
332      * </p>
333      *
334      * @param view view instance the action belongs to
335      * @param model model object containing the view data
336      * @param parent component the holds the action
337      */
338     protected void buildActionData(View view, Object model, LifecycleElement parent) {
339         HashMap<String, String> actionDataAttributes = new HashMap<String, String>();
340 
341         Map<String, String> dataDefaults =
342                 (Map<String, String>) (KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
343                         UifConstants.ACTION_DEFAULTS_MAP_ID));
344 
345         // map properties to data attributes
346         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.AJAX_SUBMIT,
347                 Boolean.toString(ajaxSubmit));
348         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
349                 UifConstants.ActionDataAttributes.SUCCESS_CALLBACK, this.successCallback);
350         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.ERROR_CALLBACK,
351                 this.errorCallback);
352         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
353                 UifConstants.ActionDataAttributes.PRE_SUBMIT_CALL, this.preSubmitCall);
354         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
355                 UifConstants.ActionDataAttributes.LOADING_MESSAGE, this.loadingMessageText);
356         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
357                 UifConstants.ActionDataAttributes.DISABLE_BLOCKING, Boolean.toString(this.disableBlocking));
358         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
359                 UifConstants.ActionDataAttributes.AJAX_RETURN_TYPE, this.ajaxReturnType);
360         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.REFRESH_ID,
361                 this.refreshId);
362         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.VALIDATE,
363                 Boolean.toString(this.performClientSideValidation));
364         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
365                 UifConstants.ActionDataAttributes.DIRTY_ON_ACTION, Boolean.toString(this.dirtyOnAction));
366         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.CLEAR_DIRTY,
367                 Boolean.toString(this.clearDirtyOnAction));
368         addActionDataSettingsValue(actionDataAttributes, dataDefaults,
369                 UifConstants.ActionDataAttributes.PERFORM_DIRTY_VALIDATION, Boolean.toString(
370                 this.performDirtyValidation));
371 
372         // all action parameters should be submitted with action
373         Map<String, String> submitData = new HashMap<String, String>();
374         for (String key : actionParameters.keySet()) {
375             String parameterPath = key;
376             if (!key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
377                 parameterPath = UifPropertyPaths.ACTION_PARAMETERS + "[" + key + "]";
378             }
379             submitData.put(parameterPath, actionParameters.get(key));
380         }
381 
382         for (String key : additionalSubmitData.keySet()) {
383             submitData.put(key, additionalSubmitData.get(key));
384         }
385 
386         // if focus id not set default to focus on action
387         if (focusOnIdAfterSubmit.equalsIgnoreCase(UifConstants.Order.NEXT_INPUT.toString())) {
388             focusOnIdAfterSubmit = UifConstants.Order.NEXT_INPUT.toString() + ":" + this.getId();
389         }
390 
391         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.FOCUS_ID,
392                 focusOnIdAfterSubmit);
393 
394         if (StringUtils.isNotBlank(jumpToIdAfterSubmit)) {
395             addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.JUMP_TO_ID,
396                     jumpToIdAfterSubmit);
397         } else if (StringUtils.isNotBlank(jumpToNameAfterSubmit)) {
398             addActionDataSettingsValue(actionDataAttributes, dataDefaults,
399                     UifConstants.ActionDataAttributes.JUMP_TO_NAME, jumpToNameAfterSubmit);
400         }
401 
402         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.DataAttributes.SUBMIT_DATA,
403                 ScriptUtils.toJSON(submitData));
404 
405         // build final onclick script
406         String onClickScript = this.getOnClickScript();
407         if (StringUtils.isNotBlank(actionScript)) {
408             onClickScript = ScriptUtils.appendScript(onClickScript, actionScript);
409         } else {
410             onClickScript = ScriptUtils.appendScript(onClickScript, "actionInvokeHandler(this);");
411         }
412 
413         //stop action if the action is disabled
414         if (disabled) {
415             this.addStyleClass("disabled");
416             this.setSkipInTabOrder(true);
417         }
418 
419         // on click script becomes a data attribute for use in a global handler on the client
420         addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.DataAttributes.ONCLICK,
421                 KRADUtils.convertToHTMLAttributeSafeString(onClickScript));
422 
423         if (!actionDataAttributes.isEmpty()) {
424             this.getDataAttributes().putAll(actionDataAttributes);
425         }
426 
427         this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.ACTION);
428     }
429 
430     /**
431      * Adds the value passed to the valueMap with the key specified, if the value does not match the
432      * value which already exists in defaults (to avoid having to write out extra data that can later
433      * be derived from the defaults in the js client-side).
434      *
435      * @param valueMap the data map being constructed
436      * @param defaults defaults for validation messages
437      * @param key the variable name being added
438      * @param value the value set on this object as a String equivalent
439      */
440     protected void addActionDataSettingsValue(Map<String, String> valueMap, Map<String, String> defaults, String key,
441             String value) {
442         if (StringUtils.isBlank(value)) {
443             return;
444         }
445 
446         String defaultValue = defaults.get(key);
447         if (defaultValue == null || !value.equals(defaultValue)) {
448             valueMap.put(key, value);
449         }
450     }
451 
452     /**
453      * Name of the method that should be called when the action is selected
454      *
455      * <p>
456      * For a server side call (clientSideCall is false), gives the name of the
457      * method in the mapped controller that should be invoked when the action is
458      * selected. For client side calls gives the name of the script function
459      * that should be invoked when the action is selected
460      * </p>
461      *
462      * @return name of method to call
463      */
464     @BeanTagAttribute(name = "methodToCall")
465     public String getMethodToCall() {
466         return this.methodToCall;
467     }
468 
469     /**
470      * Setter for the actions method to call.
471      *
472      * @param methodToCall method to call
473      */
474     public void setMethodToCall(String methodToCall) {
475         this.methodToCall = methodToCall;
476     }
477 
478     /**
479      * Label text for the action
480      *
481      * <p>
482      * The label text is used by the template renderers to give a human readable
483      * label for the action. For buttons this generally is the button text,
484      * while for an action link it would be the links displayed text
485      * </p>
486      *
487      * @return label for action
488      */
489     @BeanTagAttribute(name = "actionLabel")
490     public String getActionLabel() {
491         return this.actionLabel;
492     }
493 
494     /**
495      * Setter for the actions label.
496      *
497      * @param actionLabel action label
498      */
499     public void setActionLabel(String actionLabel) {
500         this.actionLabel = actionLabel;
501     }
502 
503     /**
504      * When true, a span will be rendered around the actionLabel text.
505      *
506      * @return true if rendering a span around actionLabel, false otherwise
507      */
508     @BeanTagAttribute(name = "renderInnerTextSpan")
509     public boolean isRenderInnerTextSpan() {
510         return renderInnerTextSpan;
511     }
512 
513     /**
514      * Setter for {@link org.kuali.rice.krad.uif.element.Action#isRenderInnerTextSpan()}.
515      *
516      * @param renderInnerTextSpan property value
517      */
518     public void setRenderInnerTextSpan(boolean renderInnerTextSpan) {
519         this.renderInnerTextSpan = renderInnerTextSpan;
520     }
521 
522     /**
523      * Image to use for the action
524      *
525      * <p>
526      * When the action image component is set (and render is true) the image will be
527      * used to present the action as opposed to the default (input submit). For
528      * action link templates the image is used for the link instead of the
529      * action link text
530      * </p>
531      *
532      * @return action image
533      */
534     @BeanTagAttribute(name = "actionImage", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
535     public Image getActionImage() {
536         return this.actionImage;
537     }
538 
539     /**
540      * Setter for the action image field.
541      *
542      * @param actionImage action image
543      */
544     public void setActionImage(Image actionImage) {
545         this.actionImage = actionImage;
546     }
547 
548     /**
549      * The css class (some which exist in bootstrap css) to use to render an icon for this action.
550      *
551      * @return the icon css class
552      */
553     @BeanTagAttribute(name = "iconClass")
554     public String getIconClass() {
555         return iconClass;
556     }
557 
558     /**
559      * Setter for {@link org.kuali.rice.krad.uif.element.Action#getIconClass()}.
560      *
561      * @param iconClass property value
562      */
563     public void setIconClass(String iconClass) {
564         this.iconClass = iconClass;
565     }
566 
567     /**
568      * For an <code>Action</code> that is part of a
569      * <code>NavigationGroup</code>, the navigate to page id can be set to
570      * configure the page that should be navigated to when the action is
571      * selected.
572      *
573      * <p>
574      * Support exists in the <code>UifControllerBase</code> for handling
575      * navigation between pages.
576      * </p>
577      *
578      * @return id of page that should be rendered when the action item is
579      *         selected
580      */
581     @BeanTagAttribute(name = "navigateToPageId")
582     public String getNavigateToPageId() {
583         return this.navigateToPageId;
584     }
585 
586     /**
587      * Setter for {@link #getNavigateToPageId()}.
588      *
589      * @param navigateToPageId property value
590      */
591     public void setNavigateToPageId(String navigateToPageId) {
592         this.navigateToPageId = navigateToPageId;
593     }
594 
595     /**
596      * Name of the event that will be set when the action is invoked
597      *
598      * <p>
599      * Action events can be looked at by the view or components in order to render differently depending on
600      * the action requested.
601      * </p>
602      *
603      * @return action event name
604      * @see org.kuali.rice.krad.uif.UifConstants.ActionEvents
605      */
606     @BeanTagAttribute(name = "actionEvent")
607     public String getActionEvent() {
608         return actionEvent;
609     }
610 
611     /**
612      * Setter for {@link #getActionEvent()}.
613      *
614      * @param actionEvent property value
615      */
616     public void setActionEvent(String actionEvent) {
617         this.actionEvent = actionEvent;
618     }
619 
620     /**
621      * Map of additional data that will be posted when the action is invoked.
622      *
623      * <p>
624      * Each entry in this map will be sent as a request parameter when the action is chosen. Note this in
625      * addition to the form data that is sent. For example, suppose the model contained a property named
626      * number and a boolean named showActive, we can send values for this properties by adding the following
627      * entries to this map:
628      * {'number':'a13', 'showActive', 'true'}
629      * </p>
630      *
631      * <p>
632      * The additionalSubmitData map is different from the actionParameters map. All name/value pairs given as
633      * actionParameters populated the form map actionParameters. While name/value pair given in additionalSubmitData
634      * populate different form (model) properties.
635      * </p>
636      *
637      * @return additional key/value pairs to submit
638      */
639     @BeanTagAttribute(name = "additionalSubmitData", type = BeanTagAttribute.AttributeType.MAPVALUE)
640     public Map<String, String> getAdditionalSubmitData() {
641         return additionalSubmitData;
642     }
643 
644     /**
645      * Setter for map holding additional data to post.
646      *
647      * @param additionalSubmitData property value
648      */
649     public void setAdditionalSubmitData(Map<String, String> additionalSubmitData) {
650         this.additionalSubmitData = additionalSubmitData;
651     }
652 
653     /**
654      * Parameters that should be sent when the action is invoked
655      *
656      * <p>
657      * Action renderer will decide how the parameters are sent for the action
658      * (via script generated hiddens, or script parameters, ...)
659      * </p>
660      *
661      * <p>
662      * Can be set by other components such as the <code>CollectionGroup</code>
663      * to provide the context the action is in (such as the collection name and
664      * line the action applies to)
665      * </p>
666      *
667      * @return action parameters
668      */
669     @BeanTagAttribute(name = "actionParameters", type = BeanTagAttribute.AttributeType.MAPVALUE)
670     public Map<String, String> getActionParameters() {
671         return this.actionParameters;
672     }
673 
674     /**
675      * Setter for {@link #getActionParameters()}.
676      *
677      * @param actionParameters property value
678      */
679     public void setActionParameters(Map<String, String> actionParameters) {
680         this.actionParameters = actionParameters;
681     }
682 
683     /**
684      * Convenience method to add a parameter to the action parameters Map.
685      *
686      * @param parameterName name of parameter to add
687      * @param parameterValue value of parameter to add
688      */
689     public void addActionParameter(String parameterName, String parameterValue) {
690         if (actionParameters == null) {
691             this.actionParameters = new HashMap<String, String>();
692         }
693 
694         this.actionParameters.put(parameterName, parameterValue);
695     }
696 
697     /**
698      * Gets an action parameter by name.
699      *
700      * @param parameterName parameter name
701      * @return action parameter
702      */
703     public String getActionParameter(String parameterName) {
704         return this.actionParameters.get(parameterName);
705     }
706 
707     /**
708      * Action Security object that indicates what authorization (permissions) exist for the action.
709      *
710      * @return ActionSecurity instance
711      */
712     public ActionSecurity getActionSecurity() {
713         return (ActionSecurity) super.getComponentSecurity();
714     }
715 
716     /**
717      * Override to assert a {@link ActionSecurity} instance is set.
718      *
719      * @param componentSecurity instance of ActionSecurity
720      */
721     @Override
722     public void setComponentSecurity(ComponentSecurity componentSecurity) {
723         if ((componentSecurity != null) && !(componentSecurity instanceof ActionSecurity)) {
724             throw new RiceRuntimeException("Component security for Action should be instance of ActionSecurity");
725         }
726 
727         super.setComponentSecurity(componentSecurity);
728     }
729 
730     /**
731      * {@inheritDoc}
732      */
733     @Override
734     protected void initializeComponentSecurity() {
735         if (getComponentSecurity() == null) {
736             setComponentSecurity(KRADUtils.createNewObjectFromClass(ActionSecurity.class));
737         }
738     }
739 
740     /**
741      * Indicates whether or not to perform action auth.
742      *
743      * @return true to perform action auth
744      */
745     public boolean isPerformActionAuthz() {
746         initializeComponentSecurity();
747 
748         return getActionSecurity().isPerformActionAuthz();
749     }
750 
751     /**
752      * Setter for {@link #isPerformActionAuthz()}.
753      *
754      * @param performActionAuthz property value
755      */
756     public void setPerformActionAuthz(boolean performActionAuthz) {
757         initializeComponentSecurity();
758 
759         getActionSecurity().setPerformActionAuthz(performActionAuthz);
760     }
761 
762     /**
763      * Indicates whether or not to perform line action auth.
764      *
765      * @return true to perform line action auth
766      */
767     public boolean isPerformLineActionAuthz() {
768         initializeComponentSecurity();
769 
770         return getActionSecurity().isPerformLineActionAuthz();
771     }
772 
773     /**
774      * Setter for {@link #isPerformActionAuthz()}.
775      *
776      * @param performLineActionAuthz property value
777      */
778     public void setPerformLineActionAuthz(boolean performLineActionAuthz) {
779         initializeComponentSecurity();
780 
781         getActionSecurity().setPerformLineActionAuthz(performLineActionAuthz);
782     }
783 
784     /**
785      * Gets the id to jump to after submit.
786      *
787      * @return the jumpToIdAfterSubmit
788      */
789     @BeanTagAttribute(name = "jumpToIdAfterSubmit")
790     public String getJumpToIdAfterSubmit() {
791         return this.jumpToIdAfterSubmit;
792     }
793 
794     /**
795      * The id to jump to in the next page, the element with this id will be
796      * jumped to automatically when the new page is retrieved after a submit.
797      * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the
798      * resulting page. Passing in nothing for both jumpToIdAfterSubmit and
799      * jumpToNameAfterSubmit will result in this Action being jumped to by
800      * default if it is present on the new page. WARNING: jumpToIdAfterSubmit
801      * always takes precedence over jumpToNameAfterSubmit, if set.
802      *
803      * @param jumpToIdAfterSubmit the jumpToIdAfterSubmit to set
804      */
805     public void setJumpToIdAfterSubmit(String jumpToIdAfterSubmit) {
806         this.jumpToIdAfterSubmit = jumpToIdAfterSubmit;
807     }
808 
809     /**
810      * The name to jump to in the next page, the element with this name will be
811      * jumped to automatically when the new page is retrieved after a submit.
812      * Passing in nothing for both jumpToIdAfterSubmit and jumpToNameAfterSubmit
813      * will result in this Action being jumped to by default if it is
814      * present on the new page. WARNING: jumpToIdAfterSubmit always takes
815      * precedence over jumpToNameAfterSubmit, if set.
816      *
817      * @return the jumpToNameAfterSubmit
818      */
819     @BeanTagAttribute(name = "jumpToNameAfterSubmit")
820     public String getJumpToNameAfterSubmit() {
821         return this.jumpToNameAfterSubmit;
822     }
823 
824     /**
825      * Setter for {@link #getJumpToIdAfterSubmit()}.
826      *
827      * @param jumpToNameAfterSubmit the jumpToNameAfterSubmit to set
828      */
829     public void setJumpToNameAfterSubmit(String jumpToNameAfterSubmit) {
830         this.jumpToNameAfterSubmit = jumpToNameAfterSubmit;
831     }
832 
833     /**
834      * The element to place focus on in the new page after the new page
835      * is retrieved.
836      *
837      * <p>The following are allowed:
838      * <ul>
839      * <li>A valid element id</li>
840      * <li>"FIRST" will focus on the first visible input element on the form</li>
841      * <li>"SELF" will result in this Action being focused (action bean defaults to "SELF")</li>
842      * <li>"LINE_FIRST" will result in the first input of the collection line to be focused (if available)</li>
843      * <li>"NEXT_INPUT" will result in the next available input that exists after this Action to be focused
844      * (only if this action still exists on the page)</li>
845      * </ul>
846      * </p>
847      *
848      * @return the focusOnAfterSubmit
849      */
850     @BeanTagAttribute(name = "focusOnIdAfterSubmit")
851     public String getFocusOnIdAfterSubmit() {
852         return this.focusOnIdAfterSubmit;
853     }
854 
855     /**
856      * Setter for {@link #getFocusOnIdAfterSubmit()}.
857      *
858      * @param focusOnIdAfterSubmit the focusOnAfterSubmit to set
859      */
860     public void setFocusOnIdAfterSubmit(String focusOnIdAfterSubmit) {
861         this.focusOnIdAfterSubmit = focusOnIdAfterSubmit;
862     }
863 
864     /**
865      * Indicates whether the form data should be validated on the client side.
866      *
867      * @return true if validation should occur, false otherwise
868      */
869     @BeanTagAttribute(name = "performClientSideValidation")
870     public boolean isPerformClientSideValidation() {
871         return this.performClientSideValidation;
872     }
873 
874     /**
875      * Setter for the client side validation flag.
876      *
877      * @param performClientSideValidation property value
878      */
879     public void setPerformClientSideValidation(boolean performClientSideValidation) {
880         this.performClientSideValidation = performClientSideValidation;
881     }
882 
883     /**
884      * Client side javascript to be executed when this actionField is clicked.
885      *
886      * <p>
887      * This overrides the default action for this Action so the method
888      * called must explicitly submit, navigate, etc. through js, if necessary.
889      * In addition, this js occurs AFTER onClickScripts set on this field, it
890      * will be the last script executed by the click event. Sidenote: This js is
891      * always called after hidden actionParameters and methodToCall methods are
892      * written by the js to the html form.
893      * </p>
894      *
895      * @return the actionScript
896      */
897     @BeanTagAttribute(name = "actionScript")
898     public String getActionScript() {
899         return this.actionScript;
900     }
901 
902     /**
903      * Setter for {@link #getActionScript()}.
904      *
905      * @param actionScript the actionScript to set
906      */
907     public void setActionScript(String actionScript) {
908         if (StringUtils.isNotBlank(actionScript) && !StringUtils.endsWith(actionScript, ";")) {
909             actionScript = actionScript + ";";
910         }
911 
912         this.actionScript = actionScript;
913     }
914 
915     /**
916      * Url to open when the action item is selected
917      *
918      * <p>
919      * This makes the action behave like a standard link. Instead of posting the form, the configured URL will
920      * simply be opened (using window.open). For using standard post actions these does not need to be configured.
921      * </p>
922      *
923      * @return Url info instance for the configuration action link
924      */
925     @BeanTagAttribute(name = "actionUrl")
926     public UrlInfo getActionUrl() {
927         return actionUrl;
928     }
929 
930     /**
931      * Setter for {@link #getActionUrl()}.
932      *
933      * @param actionUrl property value
934      */
935     public void setActionUrl(UrlInfo actionUrl) {
936         this.actionUrl = actionUrl;
937     }
938 
939     /**
940      * Setter for {@link #isPerformDirtyValidation()}.
941      *
942      * @param performDirtyValidation the blockValidateDirty to set
943      */
944     public void setPerformDirtyValidation(boolean performDirtyValidation) {
945         this.performDirtyValidation = performDirtyValidation;
946     }
947 
948     /**
949      * Indicates whether or not to perform dirty validation.
950      *
951      * @return true to perform dirty validation
952      */
953     @BeanTagAttribute(name = "performDirtyValidation")
954     public boolean isPerformDirtyValidation() {
955         return performDirtyValidation;
956     }
957 
958     /**
959      * True to make this action clear the dirty flag before submitting.
960      *
961      * <p>This will clear both the dirtyForm flag on the form and the count of fields considered dirty on the
962      * client-side.  This will only be performed if this action is a request based action.</p>
963      *
964      * @return true if the dirty
965      */
966     @BeanTagAttribute(name = "clearDirtyOnAction")
967     public boolean isClearDirtyOnAction() {
968         return clearDirtyOnAction;
969     }
970 
971     /**
972      * Setter for {@link #isClearDirtyOnAction()}.
973      *
974      * @param clearDirtyOnAction property value
975      */
976     public void setClearDirtyOnAction(boolean clearDirtyOnAction) {
977         this.clearDirtyOnAction = clearDirtyOnAction;
978     }
979 
980     /**
981      * When true, this action will mark the form dirty by incrementing the dirty field count, but if this action
982      * refreshes the entire view this will be lost (most actions only refresh the page)
983      *
984      * <p>This will increase count of fields considered dirty on the
985      * client-side by 1.  This will only be performed if this action is a request based action.</p>
986      *
987      * @return true if this action is considered dirty, false otherwise
988      */
989     @BeanTagAttribute(name = "dirtyOnAction")
990     public boolean isDirtyOnAction() {
991         return dirtyOnAction;
992     }
993 
994     /**
995      * Set to true, if this action is considered one that changes the form's data (makes the form dirty).
996      *
997      * @param dirtyOnAction property value
998      */
999     public void setDirtyOnAction(boolean dirtyOnAction) {
1000         this.dirtyOnAction = dirtyOnAction;
1001     }
1002 
1003     /**
1004      * Indicates whether the action (input or button) is disabled (doesn't allow interaction).
1005      *
1006      * @return true if the action field is disabled, false if not
1007      */
1008     @BeanTagAttribute(name = "disabled")
1009     public boolean isDisabled() {
1010         return disabled;
1011     }
1012 
1013     /**
1014      * Setter for the disabled indicator.
1015      *
1016      * @param disabled property value
1017      */
1018     public void setDisabled(boolean disabled) {
1019         this.disabled = disabled;
1020     }
1021 
1022     /**
1023      * If the action field is disabled, gives a reason for why which will be displayed as a tooltip
1024      * on the action field (button).
1025      *
1026      * @return disabled reason text
1027      * @see #isDisabled()
1028      */
1029     @BeanTagAttribute(name = "disabledReason")
1030     public String getDisabledReason() {
1031         return disabledReason;
1032     }
1033 
1034     /**
1035      * Setter for the disabled reason text.
1036      *
1037      * @param disabledReason property value
1038      */
1039     public void setDisabledReason(String disabledReason) {
1040         this.disabledReason = disabledReason;
1041     }
1042 
1043     /**
1044      * Gets the action image placement.
1045      *
1046      * @return action image placement
1047      */
1048     @BeanTagAttribute(name = "actionImagePlacement")
1049     public String getActionImagePlacement() {
1050         return actionImagePlacement;
1051     }
1052 
1053     /**
1054      * Set to TOP, BOTTOM, LEFT, RIGHT to position image at that location within the button.
1055      * For the subclass ActionLinkField only LEFT and RIGHT are allowed.  When set to blank/null/IMAGE_ONLY, the image
1056      * itself will be the Action, if no value is set the default is ALWAYS LEFT, you must explicitly set
1057      * blank/null/IMAGE_ONLY to use ONLY the image as the Action.
1058      *
1059      * @param actionImagePlacement action image placement indicator
1060      */
1061     public void setActionImagePlacement(String actionImagePlacement) {
1062         this.actionImagePlacement = actionImagePlacement;
1063     }
1064 
1065     /**
1066      * Gets the action icon placement.
1067      *
1068      * @return action icon placement
1069      */
1070     @BeanTagAttribute(name = "actionIconPlacement")
1071     public String getActionIconPlacement() {
1072         return actionIconPlacement;
1073     }
1074 
1075     /**
1076      * Setter for {@link #getActionIconPlacement()}.
1077      *
1078      * @param actionIconPlacement property value
1079      */
1080     public void setActionIconPlacement(String actionIconPlacement) {
1081         this.actionIconPlacement = actionIconPlacement;
1082     }
1083 
1084     /**
1085      * Gets the script which needs to be invoked before the form is submitted
1086      *
1087      * <p>
1088      * The preSubmitCall can carry out custom logic for the action before the submit occurs. The value should
1089      * be given as one or more lines of script and should return a boolean. If false is returned from the call,
1090      * the submit is not carried out. Furthermore, the preSubmitCall can refer to the request object through the
1091      * variable 'kradRequest' or 'this'. This gives full access over the request for doing such things as
1092      * adding additional data
1093      * </p>
1094      *
1095      * <p>
1096      * Examples 'return doFunction(kradRequest);', 'var valid=true;return valid;'
1097      * </p>
1098      *
1099      * <p>
1100      * The preSubmit call will be invoked both for ajax and non-ajax submits
1101      * </p>
1102      *
1103      * @return script text that will be invoked before form submission
1104      */
1105     @BeanTagAttribute(name = "preSubmitCall")
1106     public String getPreSubmitCall() {
1107         return preSubmitCall;
1108     }
1109 
1110     /**
1111      * Setter for {@link #getPreSubmitCall()}.
1112      *
1113      * @param preSubmitCall property value
1114      */
1115     public void setPreSubmitCall(String preSubmitCall) {
1116         this.preSubmitCall = preSubmitCall;
1117     }
1118 
1119     /**
1120      * When this property is set to true it will submit the form using Ajax instead of the browser submit. Will default
1121      * to updating the page contents
1122      *
1123      * @return boolean
1124      */
1125     @BeanTagAttribute(name = "ajaxSubmit")
1126     public boolean isAjaxSubmit() {
1127         return ajaxSubmit;
1128     }
1129 
1130     /**
1131      * Setter for {@link #isAjaxSubmit()}.
1132      *
1133      * @param ajaxSubmit property value
1134      */
1135     public void setAjaxSubmit(boolean ajaxSubmit) {
1136         this.ajaxSubmit = ajaxSubmit;
1137     }
1138 
1139     /**
1140      * Gets the return type for the ajax call
1141      *
1142      * <p>
1143      * The ajax return type indicates how the response content will be handled in the client. Typical
1144      * examples include updating a component, the page, or doing a redirect.
1145      * </p>
1146      *
1147      * @return return type
1148      * @see org.kuali.rice.krad.uif.UifConstants.AjaxReturnTypes
1149      */
1150     @BeanTagAttribute(name = "ajaxReturnType")
1151     public String getAjaxReturnType() {
1152         return this.ajaxReturnType;
1153     }
1154 
1155     /**
1156      * Setter for the type of ajax return.
1157      *
1158      * @param ajaxReturnType property value
1159      */
1160     public void setAjaxReturnType(String ajaxReturnType) {
1161         this.ajaxReturnType = ajaxReturnType;
1162     }
1163 
1164     /**
1165      * Indicates if the action response should be displayed in a lightbox.
1166      *
1167      * @return true if response should be rendered in a lightbox, false if not
1168      */
1169     @BeanTagAttribute(name = "displayResponseInLightBox")
1170     public boolean isDisplayResponseInLightBox() {
1171         return StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey());
1172     }
1173 
1174     /**
1175      * Setter for indicating the response should be rendered in a lightbox.
1176      *
1177      * @param displayResponseInLightBox property value
1178      */
1179     public void setDisplayResponseInLightBox(boolean displayResponseInLightBox) {
1180         if (displayResponseInLightBox) {
1181             this.ajaxReturnType = UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey();
1182         }
1183         // if display lightbox is false and it was previously true, set to default of update page
1184         else if (StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey())) {
1185             this.ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATEPAGE.getKey();
1186         }
1187     }
1188 
1189     /**
1190      * Gets the script which will be invoked on a successful ajax call
1191      *
1192      * <p>
1193      * The successCallback can carry out custom logic after a successful ajax submission has been made. The
1194      * value can contain one or more script statements. In addition, the response contents can be accessed
1195      * through the variable 'responseContents'
1196      * </p>
1197      *
1198      * <p>
1199      * Examples 'handleSuccessfulUpdate(responseContents);'
1200      * </p>
1201      *
1202      * <p>
1203      * The successCallback may only be specified when {@link #isAjaxSubmit()} is true
1204      * </p>
1205      *
1206      * @return script to be executed when the action is successful
1207      */
1208     @BeanTagAttribute(name = "successCallback")
1209     public String getSuccessCallback() {
1210         return successCallback;
1211     }
1212 
1213     /**
1214      * Setter for successCallback.
1215      *
1216      * @param successCallback property value
1217      */
1218     public void setSuccessCallback(String successCallback) {
1219         this.successCallback = successCallback;
1220     }
1221 
1222     /**
1223      * Gets the script which will be invoked when the action fails due to problems in the ajax call or
1224      * the return of an incident report
1225      *
1226      * <p>
1227      * The errorCallback can carry out custom logic after a failed ajax submission. The
1228      * value can contain one or more script statements. In addition, the response contents can be accessed
1229      * through the variable 'responseContents'
1230      * </p>
1231      *
1232      * <p>
1233      * Examples 'handleFailedUpdate(responseContents);'
1234      * </p>
1235      *
1236      * <p>
1237      * The errorCallback may only be specified when {@link #isAjaxSubmit()} is true
1238      * </p>
1239      *
1240      * @return script to be executed when the action is successful
1241      */
1242     @BeanTagAttribute(name = "errorCallback")
1243     public String getErrorCallback() {
1244         return errorCallback;
1245     }
1246 
1247     /**
1248      * Setter for {@link #getErrorCallback()}.
1249      *
1250      * @param errorCallback property value
1251      */
1252     public void setErrorCallback(String errorCallback) {
1253         this.errorCallback = errorCallback;
1254     }
1255 
1256     /**
1257      * Id for the component that should be refreshed after the action completes
1258      *
1259      * <p>
1260      * Either refresh id or refresh property name can be set to configure the component that should
1261      * be refreshed after the action completes. If both are blank, the page will be refreshed
1262      * </p>
1263      *
1264      * @return valid component id
1265      */
1266     @BeanTagAttribute(name = "refreshId")
1267     public String getRefreshId() {
1268         return refreshId;
1269     }
1270 
1271     /**
1272      * Setter for the {@link #getRefreshId()}.
1273      *
1274      * @param refreshId property value
1275      */
1276     public void setRefreshId(String refreshId) {
1277         this.refreshId = refreshId;
1278     }
1279 
1280     /**
1281      * Property name for the {@link org.kuali.rice.krad.uif.field.DataField} that should be refreshed after the action
1282      * completes
1283      *
1284      * <p>
1285      * Either refresh id or refresh property name can be set to configure the component that should
1286      * be refreshed after the action completes. If both are blank, the page will be refreshed
1287      * </p>
1288      *
1289      * <p>
1290      * Property name will be adjusted to use the default binding path unless it contains the form prefix
1291      * </p>
1292      *
1293      * @return valid property name with an associated DataField
1294      * @see org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX
1295      */
1296     @BeanTagAttribute(name = "refreshPropertyName")
1297     public String getRefreshPropertyName() {
1298         return refreshPropertyName;
1299     }
1300 
1301     /**
1302      * Setter for the property name of the DataField that should be refreshed.
1303      *
1304      * @param refreshPropertyName property value
1305      */
1306     public void setRefreshPropertyName(String refreshPropertyName) {
1307         this.refreshPropertyName = refreshPropertyName;
1308     }
1309 
1310     /**
1311      * Gets the loading message used by action's blockUI.
1312      *
1313      * @return String if String is not null, used in place of loading message
1314      */
1315     @BeanTagAttribute(name = "loadingMessageText")
1316     public String getLoadingMessageText() {
1317         return loadingMessageText;
1318     }
1319 
1320     /**
1321      * When this property is set, it is used in place of the loading message text used by the blockUI.
1322      *
1323      * @param loadingMessageText property value
1324      */
1325     public void setLoadingMessageText(String loadingMessageText) {
1326         this.loadingMessageText = loadingMessageText;
1327     }
1328 
1329     /**
1330      * Indicates whether blocking for the action should be disabled
1331      *
1332      * <p>
1333      * By default when an action is invoked part of the page or the entire window is blocked until
1334      * the action completes. If this property is set to true the blocking will not be displayed.
1335      * </p>
1336      *
1337      * <p>
1338      * Currently if an action returns a file download, this property should be set to true. If not, the blocking
1339      * will never get unblocked (because the page does not get notification a file was downloaded)
1340      * </p>
1341      *
1342      * @return true if blocking should be disabled, false if not
1343      */
1344     @BeanTagAttribute(name = "disableBlocking")
1345     public boolean isDisableBlocking() {
1346         return disableBlocking;
1347     }
1348 
1349     /**
1350      * Setter for disabling blocking when the action is invoked.
1351      *
1352      * @param disableBlocking property value
1353      */
1354     public void setDisableBlocking(boolean disableBlocking) {
1355         this.disableBlocking = disableBlocking;
1356     }
1357 
1358     /**
1359      * Evaluate the disable condition on controls which disable it on each key up event.
1360      *
1361      * @return true if evaluate on key up, false otherwise
1362      */
1363     @BeanTagAttribute(name = "evaluateDisabledOnKeyUp")
1364     public boolean isEvaluateDisabledOnKeyUp() {
1365         return evaluateDisabledOnKeyUp;
1366     }
1367 
1368     /**
1369      * Setter for {@link #isEvaluateDisabledOnKeyUp()}.
1370      *
1371      * @param evaluateDisabledOnKeyUp property value
1372      */
1373     public void setEvaluateDisabledOnKeyUp(boolean evaluateDisabledOnKeyUp) {
1374         this.evaluateDisabledOnKeyUp = evaluateDisabledOnKeyUp;
1375     }
1376 
1377     /**
1378      * Get the disable condition js derived from the springEL, cannot be set.
1379      *
1380      * @return the disableConditionJs javascript to be evaluated
1381      */
1382     public String getDisabledConditionJs() {
1383         return disabledConditionJs;
1384     }
1385 
1386     /**
1387      * Sets the disabled condition javascript.
1388      *
1389      * @param disabledConditionJs property value
1390      */
1391     protected void setDisabledConditionJs(String disabledConditionJs) {
1392         this.disabledConditionJs = disabledConditionJs;
1393     }
1394 
1395     /**
1396      * Gets a list of control names to add handlers to for disable functionality, cannot be set.
1397      *
1398      * @return control names to add handlers to for disable
1399      */
1400     public List<String> getDisabledConditionControlNames() {
1401         return disabledConditionControlNames;
1402     }
1403 
1404     /**
1405      * Set disabled condition control names.
1406      *
1407      * @param disabledConditionControlNames property value
1408      */
1409     public void setDisabledConditionControlNames(List<String> disabledConditionControlNames) {
1410         this.disabledConditionControlNames = disabledConditionControlNames;
1411     }
1412 
1413     /**
1414      * Gets the property names of fields that when changed, will disable this component.
1415      *
1416      * @return the property names to monitor for change to disable this component
1417      */
1418     @BeanTagAttribute(name = "disabledWhenChangedPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
1419     public List<String> getDisabledWhenChangedPropertyNames() {
1420         return disabledWhenChangedPropertyNames;
1421     }
1422 
1423     /**
1424      * Sets the property names of fields that when changed, will disable this component.
1425      *
1426      * @param disabledWhenChangedPropertyNames property value
1427      */
1428     public void setDisabledWhenChangedPropertyNames(List<String> disabledWhenChangedPropertyNames) {
1429         this.disabledWhenChangedPropertyNames = disabledWhenChangedPropertyNames;
1430     }
1431 
1432     /**
1433      * Gets the property names of fields that when changed, will enable this component.
1434      *
1435      * @return the property names to monitor for change to enable this component
1436      */
1437     @BeanTagAttribute(name = "enabledWhenChangedPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
1438     public List<String> getEnabledWhenChangedPropertyNames() {
1439         return enabledWhenChangedPropertyNames;
1440     }
1441 
1442     /**
1443      * Sets the property names of fields that when changed, will enable this component.
1444      *
1445      * @param enabledWhenChangedPropertyNames property value
1446      */
1447     public void setEnabledWhenChangedPropertyNames(List<String> enabledWhenChangedPropertyNames) {
1448         this.enabledWhenChangedPropertyNames = enabledWhenChangedPropertyNames;
1449     }
1450 
1451     /**
1452      * Sets the disabled expression.
1453      *
1454      * @param disabledExpression property value
1455      */
1456     protected void setDisabledExpression(String disabledExpression) {
1457         this.disabledExpression = disabledExpression;
1458     }
1459 
1460     /**
1461      * {@inheritDoc}
1462      */
1463     @Override
1464     public void completeValidation(ValidationTrace tracer) {
1465         tracer.addBean(this);
1466 
1467         // Checks that an action is set
1468         if (getJumpToIdAfterSubmit() != null && getJumpToNameAfterSubmit() != null) {
1469             String currentValues[] = {"jumpToIdAfterSubmit =" + getJumpToIdAfterSubmit(),
1470                     "jumpToNameAfterSubmit =" + getJumpToNameAfterSubmit()};
1471             tracer.createWarning("Only 1 jumpTo property should be set", currentValues);
1472         }
1473         super.completeValidation(tracer.getCopy());
1474     }
1475 }