001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.element;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.exception.RiceRuntimeException;
021import org.kuali.rice.krad.datadictionary.parse.BeanTag;
022import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
023import org.kuali.rice.krad.datadictionary.parse.BeanTags;
024import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
025import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
026import org.kuali.rice.krad.uif.UifConstants;
027import org.kuali.rice.krad.uif.UifParameters;
028import org.kuali.rice.krad.uif.UifPropertyPaths;
029import org.kuali.rice.krad.uif.component.Component;
030import org.kuali.rice.krad.uif.component.ComponentSecurity;
031import org.kuali.rice.krad.uif.container.DialogGroup;
032import org.kuali.rice.krad.uif.container.Group;
033import org.kuali.rice.krad.uif.field.DataField;
034import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
035import org.kuali.rice.krad.uif.util.ComponentFactory;
036import org.kuali.rice.krad.uif.util.LifecycleElement;
037import org.kuali.rice.krad.uif.util.ScriptUtils;
038import org.kuali.rice.krad.uif.util.UrlInfo;
039import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
040import org.kuali.rice.krad.uif.view.FormView;
041import org.kuali.rice.krad.uif.view.View;
042import org.kuali.rice.krad.util.KRADUtils;
043
044import java.util.ArrayList;
045import java.util.HashMap;
046import java.util.List;
047import java.util.Map;
048
049/**
050 * Field that presents an action that can be taken on the UI such as submitting the form or invoking a script.
051 *
052 * @author Kuali Rice Team (rice.collab@kuali.org)
053 */
054@BeanTags({@BeanTag(name = "action", parent = "Uif-Action"),
055        @BeanTag(name = "actionImage", parent = "Uif-ActionImage"),
056        @BeanTag(name = "button", parent = "Uif-PrimaryActionButton"),
057        @BeanTag(name = "secondaryButton", parent = "Uif-SecondaryActionButton"),
058        @BeanTag(name = "buttonLarge", parent = "Uif-PrimaryActionButton-Large"),
059        @BeanTag(name = "secondaryButtonLarge", parent = "Uif-SecondaryActionButton-Large"),
060        @BeanTag(name = "buttonSmall", parent = "Uif-PrimaryActionButton-Small"),
061        @BeanTag(name = "secondaryButtonSmall", parent = "Uif-SecondaryActionButton-Small"),
062        @BeanTag(name = "buttonMini", parent = "Uif-PrimaryActionButton-Mini"),
063        @BeanTag(name = "secondaryButtonMini", parent = "Uif-SecondaryActionButton-Mini"),
064        @BeanTag(name = "actionLink", parent = "Uif-ActionLink"),
065        @BeanTag(name = "navigationActionLink", parent = "Uif-NavigationActionLink"),
066        @BeanTag(name = "navigationButton", parent = "Uif-NavigationActionButton"),
067        @BeanTag(name = "secondaryNavigationActionButton", parent = "Uif-SecondaryNavigationActionButton")})
068public class Action extends ContentElementBase {
069    private static final long serialVersionUID = 1025672792657238829L;
070
071    private String methodToCall;
072    private String actionEvent;
073    private String navigateToPageId;
074    private List<String> fieldsToSend;
075
076    private String actionScript;
077    private UrlInfo actionUrl;
078
079    private String actionLabel;
080    private boolean renderInnerTextSpan;
081
082    private Image actionImage;
083    private String actionImagePlacement;
084
085    private String iconClass;
086    private String actionIconPlacement;
087
088    private String jumpToIdAfterSubmit;
089    private String jumpToNameAfterSubmit;
090    private String focusOnIdAfterSubmit;
091
092    private boolean performClientSideValidation;
093    private boolean performDirtyValidation;
094    private boolean clearDirtyOnAction;
095    private boolean dirtyOnAction;
096
097    private String preSubmitCall;
098    private String confirmationPromptText;
099    private DialogGroup confirmationDialog;
100
101    private String dialogDismissOption;
102    private String dialogResponse;
103
104    private boolean ajaxSubmit;
105    private String ajaxReturnType;
106    private String refreshId;
107    private String refreshPropertyName;
108
109    private String successCallback;
110    private String errorCallback;
111
112    private String loadingMessageText;
113    private boolean disableBlocking;
114
115    private Map<String, String> additionalSubmitData;
116    private Map<String, String> actionParameters;
117
118    private boolean evaluateDisabledOnKeyUp;
119
120    private boolean defaultEnterKeyAction;
121
122    private boolean disabled;
123    private String disabledReason;
124    private String disabledExpression;
125    private String disabledConditionJs;
126    private List<String> disabledConditionControlNames;
127
128    private List<String> disabledWhenChangedPropertyNames;
129    private List<String> enabledWhenChangedPropertyNames;
130
131    /**
132     * Sets initial field values and initializes collections.
133     */
134    public Action() {
135        super();
136
137        actionImagePlacement = UifConstants.Position.LEFT.name();
138        actionIconPlacement = UifConstants.Position.LEFT.name();
139
140        ajaxSubmit = true;
141
142        successCallback = "";
143        errorCallback = "";
144        preSubmitCall = "";
145
146        additionalSubmitData = new HashMap<String, String>();
147        actionParameters = new HashMap<String, String>();
148
149        disabled = false;
150        disabledWhenChangedPropertyNames = new ArrayList<String>();
151        enabledWhenChangedPropertyNames = new ArrayList<String>();
152    }
153
154    /**
155     * Sets the disabledExpression, if any, evaluates it and sets the disabled property.
156     *
157     * @param model top level object containing the data (could be the form or a
158     * @param parent parent component
159     */
160    public void performApplyModel(Object model, LifecycleElement parent) {
161        super.performApplyModel(model, parent);
162
163        disabledExpression = this.getPropertyExpression("disabled");
164        if (disabledExpression != null) {
165            ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
166
167            disabledExpression = expressionEvaluator.replaceBindingPrefixes(ViewLifecycle.getView(), this,
168                    disabledExpression);
169            disabled = (Boolean) expressionEvaluator.evaluateExpression(this.getContext(), disabledExpression);
170        }
171
172        if (actionUrl != null) {
173            ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(actionUrl, false);
174            ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(ViewLifecycle.getView(),
175                    actionUrl, this.getContext());
176        }
177
178        if (StringUtils.isNotBlank(confirmationPromptText) && (confirmationDialog != null) && StringUtils.isBlank(
179                confirmationDialog.getPromptText())) {
180            confirmationDialog.setPromptText(confirmationPromptText);
181        }
182
183        addConfirmDialogToView();
184    }
185
186    /**
187     * For confirm text without a dialog, add instance of yes no dialog to view so it is already available
188     * on the client for dynamic dialog creation.
189     */
190    protected void addConfirmDialogToView() {
191        if (StringUtils.isBlank(confirmationPromptText) || (confirmationDialog != null)) {
192            return;
193        }
194
195        boolean containsYesNoDialog = false;
196
197        List<Group> viewDialogs = ViewLifecycle.getView().getDialogs();
198        if (viewDialogs == null) {
199            viewDialogs = new ArrayList<Group>();
200        } else {
201            for (Group dialogGroup : viewDialogs) {
202                if (StringUtils.equals(ComponentFactory.YES_NO_DIALOG, dialogGroup.getId())) {
203                    containsYesNoDialog = true;
204                }
205            }
206        }
207
208        if (!containsYesNoDialog) {
209            Group confirmDialog = ComponentFactory.getYesNoDialog();
210            confirmDialog.setId(ComponentFactory.YES_NO_DIALOG);
211
212            viewDialogs.add(confirmDialog);
213        }
214    }
215
216    /**
217     * The following finalization is performed:
218     *
219     * <ul>
220     * <li>Add methodToCall action parameter if set and setup event code for
221     * setting action parameters</li>
222     * <li>Invoke method to build the data attributes and submit data for the action</li>
223     * <li>Compose the final onclick script for the action</li>
224     * <li>Parses the disabled expressions, if any, to equivalent javascript and evaluates the disable/enable when
225     * changed property names</li>
226     * </ul>
227     *
228     * {@inheritDoc}
229     */
230    @Override
231    public void performFinalize(Object model, LifecycleElement parent) {
232        super.performFinalize(model, parent);
233
234        View view = ViewLifecycle.getView();
235        ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
236
237        if (StringUtils.isNotEmpty(disabledExpression)
238                && !disabledExpression.equalsIgnoreCase("true")
239                && !disabledExpression.equalsIgnoreCase("false")) {
240            disabledConditionControlNames = new ArrayList<String>();
241            disabledConditionJs = ViewLifecycle.getExpressionEvaluator().parseExpression(disabledExpression,
242                    disabledConditionControlNames, this.getContext());
243        }
244
245        List<String> adjustedDisablePropertyNames = new ArrayList<String>();
246        for (String propertyName : disabledWhenChangedPropertyNames) {
247            adjustedDisablePropertyNames.add(expressionEvaluator.replaceBindingPrefixes(view, this, propertyName));
248        }
249        disabledWhenChangedPropertyNames = adjustedDisablePropertyNames;
250
251        List<String> adjustedEnablePropertyNames = new ArrayList<String>();
252        for (String propertyName : enabledWhenChangedPropertyNames) {
253            adjustedEnablePropertyNames.add(expressionEvaluator.replaceBindingPrefixes(view, this, propertyName));
254        }
255        enabledWhenChangedPropertyNames = adjustedEnablePropertyNames;
256
257        // clear alt text to avoid screen reader confusion when using image in button with text
258        if (actionImage != null && StringUtils.isNotBlank(actionImagePlacement) && StringUtils.isNotBlank(actionLabel)) {
259            actionImage.setAltText("");
260        }
261
262        // when icon only is set, add the icon class to the action
263        if (StringUtils.isNotBlank(iconClass) && (UifConstants.ICON_ONLY_PLACEMENT.equals(actionIconPlacement)
264                || StringUtils.isBlank(actionLabel))) {
265            getCssClasses().add(iconClass);
266
267            // force icon only placement
268            actionIconPlacement = UifConstants.ICON_ONLY_PLACEMENT;
269        }
270
271        if (!actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT) && StringUtils.isNotBlank(actionEvent)) {
272            actionParameters.put(UifConstants.UrlParams.ACTION_EVENT, actionEvent);
273        }
274
275        if (StringUtils.isNotBlank(navigateToPageId)) {
276            actionParameters.put(UifParameters.NAVIGATE_TO_PAGE_ID, navigateToPageId);
277            if (StringUtils.isBlank(methodToCall)) {
278                this.methodToCall = UifConstants.MethodToCallNames.NAVIGATE;
279            }
280        }
281
282        if (!actionParameters.containsKey(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME) && StringUtils
283                .isNotBlank(methodToCall)) {
284            actionParameters.put(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME, methodToCall);
285        }
286
287        setupRefreshAction(view);
288
289        // Apply dirty check if it is enabled for the view and the action requires it
290        if (view instanceof FormView) {
291            performDirtyValidation = performDirtyValidation && ((FormView) view).isApplyDirtyCheck();
292        }
293
294        if (StringUtils.isBlank(getActionScript()) && (actionUrl != null) && actionUrl.isFullyConfigured()) {
295            String actionScript = ScriptUtils.buildFunctionCall(UifConstants.JsFunctions.REDIRECT, actionUrl.getHref());
296            setActionScript(actionScript);
297
298            if (StringUtils.isNotBlank(actionUrl.getMethodToCall())) {
299                ViewLifecycle.getViewPostMetadata().addAvailableMethodToCall(actionUrl.getMethodToCall());
300            }
301        }
302
303        // add the method to call as an available method
304        if (StringUtils.isBlank(getActionScript()) && StringUtils.isNotBlank(methodToCall)) {
305            ViewLifecycle.getViewPostMetadata().addAvailableMethodToCall(methodToCall);
306        }
307
308        // add additional submit data as accessible binding paths, and method to call as accessible method
309        if (isRender()) {
310            for (String additionalSubmitPath : additionalSubmitData.keySet()) {
311                ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(additionalSubmitPath);
312            }
313
314            if ((actionUrl != null) && actionUrl.isFullyConfigured() && StringUtils.isNotBlank(
315                    actionUrl.getMethodToCall())) {
316                ViewLifecycle.getViewPostMetadata().addAccessibleMethodToCall(actionUrl.getMethodToCall());
317            } else if (StringUtils.isBlank(getActionScript()) && StringUtils.isNotBlank(methodToCall)) {
318                ViewLifecycle.getViewPostMetadata().addAccessibleMethodToCall(methodToCall);
319            }
320        }
321
322        buildActionData(view, model, parent);
323    }
324
325    /**
326     * When the action is updating a component sets up the refresh script for the component (found by the
327     * given refresh id or refresh property name.
328     *
329     * @param view view instance the action belongs to
330     */
331    protected void setupRefreshAction(View view) {
332        // if refresh property or id is given, make return type update component
333        // TODO: what if the refresh id is the page id? we should set the return type as update page
334        if (StringUtils.isNotBlank(refreshPropertyName) || StringUtils.isNotBlank(refreshId)) {
335            ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATECOMPONENT.getKey();
336        }
337
338        // if refresh property name is given, adjust the binding and then attempt to find the
339        // component in the view index
340        Component refreshComponent = null;
341        if (StringUtils.isNotBlank(refreshPropertyName)) {
342            // TODO: does this support all binding prefixes?
343            if (refreshPropertyName.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
344                refreshPropertyName = StringUtils.removeStart(refreshPropertyName, UifConstants.NO_BIND_ADJUST_PREFIX);
345            } else if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
346                refreshPropertyName = view.getDefaultBindingObjectPath() + "." + refreshPropertyName;
347            }
348
349            DataField dataField = view.getViewIndex().getDataFieldByPath(refreshPropertyName);
350            if (dataField != null) {
351                refreshComponent = dataField;
352                refreshId = refreshComponent.getId();
353            }
354        } else if (StringUtils.isNotBlank(refreshId)) {
355            Component component = view.getViewIndex().getComponentById(refreshId);
356            if (component != null) {
357                refreshComponent = component;
358            }
359        }
360
361        if (refreshComponent != null) {
362            refreshComponent.setRefreshedByAction(true);
363        }
364    }
365
366    /**
367     * Builds the data attributes that will be read client side to determine how to
368     * handle the action and the additional data that should be submitted with the action
369     *
370     * <p>
371     * Note these data attributes will be exposed as a data map client side. The simple attributes (non object
372     * value) are also written out as attributes on the action element.
373     * </p>
374     *
375     * @param view view instance the action belongs to
376     * @param model model object containing the view data
377     * @param parent component the holds the action
378     */
379    protected void buildActionData(View view, Object model, LifecycleElement parent) {
380        HashMap<String, String> actionDataAttributes = new HashMap<String, String>();
381
382        Map<String, String> dataDefaults =
383                (Map<String, String>) (KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
384                        UifConstants.ACTION_DEFAULTS_MAP_ID));
385
386        // map properties to data attributes
387        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.AJAX_SUBMIT,
388                Boolean.toString(ajaxSubmit));
389        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
390                UifConstants.ActionDataAttributes.SUCCESS_CALLBACK, this.successCallback);
391        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.ERROR_CALLBACK,
392                this.errorCallback);
393        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
394                UifConstants.ActionDataAttributes.PRE_SUBMIT_CALL, this.preSubmitCall);
395        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
396                UifConstants.ActionDataAttributes.LOADING_MESSAGE, this.loadingMessageText);
397        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
398                UifConstants.ActionDataAttributes.DISABLE_BLOCKING, Boolean.toString(this.disableBlocking));
399        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
400                UifConstants.ActionDataAttributes.AJAX_RETURN_TYPE, this.ajaxReturnType);
401        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.REFRESH_ID,
402                this.refreshId);
403        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.VALIDATE,
404                Boolean.toString(this.performClientSideValidation));
405        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
406                UifConstants.ActionDataAttributes.DIRTY_ON_ACTION, Boolean.toString(this.dirtyOnAction));
407        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.CLEAR_DIRTY,
408                Boolean.toString(this.clearDirtyOnAction));
409        addActionDataSettingsValue(actionDataAttributes, dataDefaults,
410                UifConstants.ActionDataAttributes.PERFORM_DIRTY_VALIDATION, Boolean.toString(
411                this.performDirtyValidation));
412
413        if (CollectionUtils.isNotEmpty(fieldsToSend)) {
414            addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.FIELDS_TO_SEND,
415                    ScriptUtils.translateValue(this.fieldsToSend));
416        }
417
418        if (confirmationDialog != null) {
419            addDataAttribute(UifConstants.ActionDataAttributes.CONFIRM_DIALOG_ID, confirmationDialog.getId());
420        } else if (StringUtils.isNotBlank(confirmationPromptText)) {
421            addDataAttribute(UifConstants.ActionDataAttributes.CONFIRM_PROMPT_TEXT, confirmationPromptText);
422        }
423
424        if (StringUtils.isNotBlank(dialogDismissOption)) {
425            addDataAttribute(UifConstants.DataAttributes.DISMISS_DIALOG_OPTION, dialogDismissOption);
426        }
427
428        if (StringUtils.isNotBlank(dialogResponse)) {
429            addDataAttribute(UifConstants.DataAttributes.DISMISS_RESPONSE, dialogResponse);
430        }
431
432        // all action parameters should be submitted with action
433        Map<String, String> submitData = new HashMap<String, String>();
434        for (String key : actionParameters.keySet()) {
435            String parameterPath = key;
436            if (!key.equals(UifConstants.CONTROLLER_METHOD_DISPATCH_PARAMETER_NAME)) {
437                parameterPath = UifPropertyPaths.ACTION_PARAMETERS + "[" + key + "]";
438            }
439            submitData.put(parameterPath, actionParameters.get(key));
440        }
441
442        for (String key : additionalSubmitData.keySet()) {
443            submitData.put(key, additionalSubmitData.get(key));
444        }
445
446        // if focus id not set default to focus on action
447        if (focusOnIdAfterSubmit.equalsIgnoreCase(UifConstants.Order.NEXT_INPUT.toString())) {
448            focusOnIdAfterSubmit = UifConstants.Order.NEXT_INPUT.toString() + ":" + this.getId();
449        }
450
451        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.FOCUS_ID,
452                focusOnIdAfterSubmit);
453
454        if (StringUtils.isNotBlank(jumpToIdAfterSubmit)) {
455            addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.ActionDataAttributes.JUMP_TO_ID,
456                    jumpToIdAfterSubmit);
457        } else if (StringUtils.isNotBlank(jumpToNameAfterSubmit)) {
458            addActionDataSettingsValue(actionDataAttributes, dataDefaults,
459                    UifConstants.ActionDataAttributes.JUMP_TO_NAME, jumpToNameAfterSubmit);
460        }
461
462        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.DataAttributes.SUBMIT_DATA,
463                ScriptUtils.toJSON(submitData));
464
465        // build final onclick script
466        String onClickScript = this.getOnClickScript();
467        if (StringUtils.isNotBlank(actionScript)) {
468            onClickScript = ScriptUtils.appendScript(onClickScript, actionScript);
469        } else {
470            onClickScript = ScriptUtils.appendScript(onClickScript, "actionInvokeHandler(this);");
471        }
472
473        //stop action if the action is disabled
474        if (disabled) {
475            this.addStyleClass("disabled");
476            this.setSkipInTabOrder(true);
477        }
478
479        // on click script becomes a data attribute for use in a global handler on the client
480        addActionDataSettingsValue(actionDataAttributes, dataDefaults, UifConstants.DataAttributes.ONCLICK,
481                KRADUtils.convertToHTMLAttributeSafeString(onClickScript));
482
483        if (!actionDataAttributes.isEmpty()) {
484            this.getDataAttributes().putAll(actionDataAttributes);
485        }
486
487        this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.ACTION);
488
489        // add data attribute if this is the primary action
490        if (this.isDefaultEnterKeyAction()) {
491            this.addDataAttribute(UifConstants.DataAttributes.DEFAULT_ENTER_KEY_ACTION,
492                    Boolean.toString(this.isDefaultEnterKeyAction()));
493        }
494    }
495
496    /**
497     * Adds the value passed to the valueMap with the key specified, if the value does not match the
498     * value which already exists in defaults (to avoid having to write out extra data that can later
499     * be derived from the defaults in the js client-side).
500     *
501     * @param valueMap the data map being constructed
502     * @param defaults defaults for validation messages
503     * @param key the variable name being added
504     * @param value the value set on this object as a String equivalent
505     */
506    protected void addActionDataSettingsValue(Map<String, String> valueMap, Map<String, String> defaults, String key,
507            String value) {
508        if (StringUtils.isBlank(value)) {
509            return;
510        }
511
512        String defaultValue = defaults.get(key);
513        if (defaultValue == null || !value.equals(defaultValue)) {
514            valueMap.put(key, value);
515        }
516    }
517
518    /**
519     * Name of the method that should be called when the action is selected
520     *
521     * <p>
522     * For a server side call (clientSideCall is false), gives the name of the
523     * method in the mapped controller that should be invoked when the action is
524     * selected. For client side calls gives the name of the script function
525     * that should be invoked when the action is selected
526     * </p>
527     *
528     * @return name of method to call
529     */
530    @BeanTagAttribute
531    public String getMethodToCall() {
532        return this.methodToCall;
533    }
534
535    /**
536     * Setter for the actions method to call.
537     *
538     * @param methodToCall method to call
539     */
540    public void setMethodToCall(String methodToCall) {
541        this.methodToCall = methodToCall;
542    }
543
544    /**
545     * Label text for the action
546     *
547     * <p>
548     * The label text is used by the template renderers to give a human readable
549     * label for the action. For buttons this generally is the button text,
550     * while for an action link it would be the links displayed text
551     * </p>
552     *
553     * @return label for action
554     */
555    @BeanTagAttribute
556    public String getActionLabel() {
557        return this.actionLabel;
558    }
559
560    /**
561     * Setter for the actions label.
562     *
563     * @param actionLabel action label
564     */
565    public void setActionLabel(String actionLabel) {
566        this.actionLabel = actionLabel;
567    }
568
569    /**
570     * When true, a span will be rendered around the actionLabel text.
571     *
572     * @return true if rendering a span around actionLabel, false otherwise
573     */
574    @BeanTagAttribute
575    public boolean isRenderInnerTextSpan() {
576        return renderInnerTextSpan;
577    }
578
579    /**
580     * Setter for {@link org.kuali.rice.krad.uif.element.Action#isRenderInnerTextSpan()}.
581     *
582     * @param renderInnerTextSpan property value
583     */
584    public void setRenderInnerTextSpan(boolean renderInnerTextSpan) {
585        this.renderInnerTextSpan = renderInnerTextSpan;
586    }
587
588    /**
589     * Image to use for the action
590     *
591     * <p>
592     * When the action image component is set (and render is true) the image will be
593     * used to present the action as opposed to the default (input submit). For
594     * action link templates the image is used for the link instead of the
595     * action link text
596     * </p>
597     *
598     * @return action image
599     */
600    @BeanTagAttribute
601    public Image getActionImage() {
602        return this.actionImage;
603    }
604
605    /**
606     * Setter for the action image field.
607     *
608     * @param actionImage action image
609     */
610    public void setActionImage(Image actionImage) {
611        this.actionImage = actionImage;
612    }
613
614    /**
615     * The css class (some which exist in bootstrap css) to use to render an icon for this action.
616     *
617     * @return the icon css class
618     */
619    @BeanTagAttribute
620    public String getIconClass() {
621        return iconClass;
622    }
623
624    /**
625     * Setter for {@link org.kuali.rice.krad.uif.element.Action#getIconClass()}.
626     *
627     * @param iconClass property value
628     */
629    public void setIconClass(String iconClass) {
630        this.iconClass = iconClass;
631    }
632
633    /**
634     * For an <code>Action</code> that is part of a
635     * <code>NavigationGroup</code>, the navigate to page id can be set to
636     * configure the page that should be navigated to when the action is
637     * selected.
638     *
639     * <p>
640     * Support exists in the <code>UifControllerBase</code> for handling
641     * navigation between pages.
642     * </p>
643     *
644     * @return id of page that should be rendered when the action item is
645     *         selected
646     */
647    @BeanTagAttribute
648    public String getNavigateToPageId() {
649        return this.navigateToPageId;
650    }
651
652    /**
653     * Setter for {@link #getNavigateToPageId()}.
654     *
655     * @param navigateToPageId property value
656     */
657    public void setNavigateToPageId(String navigateToPageId) {
658        this.navigateToPageId = navigateToPageId;
659    }
660
661    /**
662     * Limits the field data to send on a refresh methodToCall server call to the names/group id/field id
663     * specified in this list.
664     *
665     * <p>The names in the list should be the propertyNames of the fields sent with this request.  A wildcard("*")
666     * can be used at the END of a name to specify all fields with names that begin with the string
667     * before the wildcard.  If the array contains 1 item with the keyword "NONE", then no form fields are sent.
668     * In addition, A group id or field id with the "#" id selector prefix can be used to send all inputs which
669     * are nested within them. Note that this only limits the fields which exist on the form and data required
670     * by the KRAD framework is still sent (eg, methodToCall, formKey, sessionId, etc.)</p>
671     *
672     * @return the only input fields to send by name with the action request
673     */
674    @BeanTagAttribute
675    public List<String> getFieldsToSend() {
676        return fieldsToSend;
677    }
678
679    /**
680     * @see Action#fieldsToSend
681     */
682    public void setFieldsToSend(List<String> fieldsToSend) {
683        this.fieldsToSend = fieldsToSend;
684    }
685
686    /**
687     * Name of the event that will be set when the action is invoked
688     *
689     * <p>
690     * Action events can be looked at by the view or components in order to render differently depending on
691     * the action requested.
692     * </p>
693     *
694     * @return action event name
695     * @see org.kuali.rice.krad.uif.UifConstants.ActionEvents
696     */
697    @BeanTagAttribute
698    public String getActionEvent() {
699        return actionEvent;
700    }
701
702    /**
703     * Setter for {@link #getActionEvent()}.
704     *
705     * @param actionEvent property value
706     */
707    public void setActionEvent(String actionEvent) {
708        this.actionEvent = actionEvent;
709    }
710
711    /**
712     * Map of additional data that will be posted when the action is invoked.
713     *
714     * <p>
715     * Each entry in this map will be sent as a request parameter when the action is chosen. Note this in
716     * addition to the form data that is sent. For example, suppose the model contained a property named
717     * number and a boolean named showActive, we can send values for this properties by adding the following
718     * entries to this map:
719     * {'number':'a13', 'showActive', 'true'}
720     * </p>
721     *
722     * <p>
723     * The additionalSubmitData map is different from the actionParameters map. All name/value pairs given as
724     * actionParameters populated the form map actionParameters. While name/value pair given in additionalSubmitData
725     * populate different form (model) properties.
726     * </p>
727     *
728     * @return additional key/value pairs to submit
729     */
730    @BeanTagAttribute
731    public Map<String, String> getAdditionalSubmitData() {
732        return additionalSubmitData;
733    }
734
735    /**
736     * Setter for map holding additional data to post.
737     *
738     * @param additionalSubmitData property value
739     */
740    public void setAdditionalSubmitData(Map<String, String> additionalSubmitData) {
741        this.additionalSubmitData = additionalSubmitData;
742    }
743
744    /**
745     * Parameters that should be sent when the action is invoked
746     *
747     * <p>
748     * Action renderer will decide how the parameters are sent for the action
749     * (via script generated hiddens, or script parameters, ...)
750     * </p>
751     *
752     * <p>
753     * Can be set by other components such as the <code>CollectionGroup</code>
754     * to provide the context the action is in (such as the collection name and
755     * line the action applies to)
756     * </p>
757     *
758     * @return action parameters
759     */
760    @BeanTagAttribute
761    public Map<String, String> getActionParameters() {
762        return this.actionParameters;
763    }
764
765    /**
766     * Setter for {@link #getActionParameters()}.
767     *
768     * @param actionParameters property value
769     */
770    public void setActionParameters(Map<String, String> actionParameters) {
771        this.actionParameters = actionParameters;
772    }
773
774    /**
775     * Convenience method to add a parameter to the action parameters Map.
776     *
777     * @param parameterName name of parameter to add
778     * @param parameterValue value of parameter to add
779     */
780    public void addActionParameter(String parameterName, String parameterValue) {
781        if (actionParameters == null) {
782            this.actionParameters = new HashMap<String, String>();
783        }
784
785        this.actionParameters.put(parameterName, parameterValue);
786    }
787
788    /**
789     * Gets an action parameter by name.
790     *
791     * @param parameterName parameter name
792     * @return action parameter
793     */
794    public String getActionParameter(String parameterName) {
795        return this.actionParameters.get(parameterName);
796    }
797
798    /**
799     * Action Security object that indicates what authorization (permissions) exist for the action.
800     *
801     * @return ActionSecurity instance
802     */
803    public ActionSecurity getActionSecurity() {
804        return (ActionSecurity) super.getComponentSecurity();
805    }
806
807    /**
808     * Override to assert a {@link ActionSecurity} instance is set.
809     *
810     * @param componentSecurity instance of ActionSecurity
811     */
812    @Override
813    public void setComponentSecurity(ComponentSecurity componentSecurity) {
814        if ((componentSecurity != null) && !(componentSecurity instanceof ActionSecurity)) {
815            throw new RiceRuntimeException("Component security for Action should be instance of ActionSecurity");
816        }
817
818        super.setComponentSecurity(componentSecurity);
819    }
820
821    /**
822     * {@inheritDoc}
823     */
824    @Override
825    protected void initializeComponentSecurity() {
826        if (getComponentSecurity() == null) {
827            setComponentSecurity(KRADUtils.createNewObjectFromClass(ActionSecurity.class));
828        }
829    }
830
831    /**
832     * Indicates whether or not to perform action auth.
833     *
834     * @return true to perform action auth
835     */
836    @BeanTagAttribute
837    public boolean isPerformActionAuthz() {
838        initializeComponentSecurity();
839
840        return getActionSecurity().isPerformActionAuthz();
841    }
842
843    /**
844     * Setter for {@link #isPerformActionAuthz()}.
845     *
846     * @param performActionAuthz property value
847     */
848    public void setPerformActionAuthz(boolean performActionAuthz) {
849        initializeComponentSecurity();
850
851        getActionSecurity().setPerformActionAuthz(performActionAuthz);
852    }
853
854    /**
855     * Indicates whether or not to perform line action auth.
856     *
857     * @return true to perform line action auth
858     */
859    @BeanTagAttribute
860    public boolean isPerformLineActionAuthz() {
861        initializeComponentSecurity();
862
863        return getActionSecurity().isPerformLineActionAuthz();
864    }
865
866    /**
867     * Setter for {@link #isPerformActionAuthz()}.
868     *
869     * @param performLineActionAuthz property value
870     */
871    public void setPerformLineActionAuthz(boolean performLineActionAuthz) {
872        initializeComponentSecurity();
873
874        getActionSecurity().setPerformLineActionAuthz(performLineActionAuthz);
875    }
876
877    /**
878     * Gets the id to jump to after submit.
879     *
880     * @return the jumpToIdAfterSubmit
881     */
882    @BeanTagAttribute
883    public String getJumpToIdAfterSubmit() {
884        return this.jumpToIdAfterSubmit;
885    }
886
887    /**
888     * The id to jump to in the next page, the element with this id will be
889     * jumped to automatically when the new page is retrieved after a submit.
890     * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the
891     * resulting page. Passing in nothing for both jumpToIdAfterSubmit and
892     * jumpToNameAfterSubmit will result in this Action being jumped to by
893     * default if it is present on the new page. WARNING: jumpToIdAfterSubmit
894     * always takes precedence over jumpToNameAfterSubmit, if set.
895     *
896     * @param jumpToIdAfterSubmit the jumpToIdAfterSubmit to set
897     */
898    public void setJumpToIdAfterSubmit(String jumpToIdAfterSubmit) {
899        this.jumpToIdAfterSubmit = jumpToIdAfterSubmit;
900    }
901
902    /**
903     * The name to jump to in the next page, the element with this name will be
904     * jumped to automatically when the new page is retrieved after a submit.
905     * Passing in nothing for both jumpToIdAfterSubmit and jumpToNameAfterSubmit
906     * will result in this Action being jumped to by default if it is
907     * present on the new page. WARNING: jumpToIdAfterSubmit always takes
908     * precedence over jumpToNameAfterSubmit, if set.
909     *
910     * @return the jumpToNameAfterSubmit
911     */
912    @BeanTagAttribute
913    public String getJumpToNameAfterSubmit() {
914        return this.jumpToNameAfterSubmit;
915    }
916
917    /**
918     * Setter for {@link #getJumpToIdAfterSubmit()}.
919     *
920     * @param jumpToNameAfterSubmit the jumpToNameAfterSubmit to set
921     */
922    public void setJumpToNameAfterSubmit(String jumpToNameAfterSubmit) {
923        this.jumpToNameAfterSubmit = jumpToNameAfterSubmit;
924    }
925
926    /**
927     * The element to place focus on in the new page after the new page
928     * is retrieved.
929     *
930     * <p>The following are allowed:
931     * <ul>
932     * <li>A valid element id</li>
933     * <li>"FIRST" will focus on the first visible input element on the form</li>
934     * <li>"SELF" will result in this Action being focused (action bean defaults to "SELF")</li>
935     * <li>"LINE_FIRST" will result in the first input of the collection line to be focused (if available)</li>
936     * <li>"NEXT_INPUT" will result in the next available input that exists after this Action to be focused
937     * (only if this action still exists on the page)</li>
938     * </ul>
939     * </p>
940     *
941     * @return the focusOnAfterSubmit
942     */
943    @BeanTagAttribute
944    public String getFocusOnIdAfterSubmit() {
945        return this.focusOnIdAfterSubmit;
946    }
947
948    /**
949     * Setter for {@link #getFocusOnIdAfterSubmit()}.
950     *
951     * @param focusOnIdAfterSubmit the focusOnAfterSubmit to set
952     */
953    public void setFocusOnIdAfterSubmit(String focusOnIdAfterSubmit) {
954        this.focusOnIdAfterSubmit = focusOnIdAfterSubmit;
955    }
956
957    /**
958     * Indicates whether the form data should be validated on the client side.
959     *
960     * @return true if validation should occur, false otherwise
961     */
962    @BeanTagAttribute
963    public boolean isPerformClientSideValidation() {
964        return this.performClientSideValidation;
965    }
966
967    /**
968     * Setter for the client side validation flag.
969     *
970     * @param performClientSideValidation property value
971     */
972    public void setPerformClientSideValidation(boolean performClientSideValidation) {
973        this.performClientSideValidation = performClientSideValidation;
974    }
975
976    /**
977     * Client side javascript to be executed when this actionField is clicked.
978     *
979     * <p>
980     * This overrides the default action for this Action so the method
981     * called must explicitly submit, navigate, etc. through js, if necessary.
982     * In addition, this js occurs AFTER onClickScripts set on this field, it
983     * will be the last script executed by the click event. Sidenote: This js is
984     * always called after hidden actionParameters and methodToCall methods are
985     * written by the js to the html form.
986     * </p>
987     *
988     * @return the actionScript
989     */
990    @BeanTagAttribute
991    public String getActionScript() {
992        return this.actionScript;
993    }
994
995    /**
996     * Setter for {@link #getActionScript()}.
997     *
998     * @param actionScript the actionScript to set
999     */
1000    public void setActionScript(String actionScript) {
1001        if (StringUtils.isNotBlank(actionScript) && !StringUtils.endsWith(actionScript, ";")) {
1002            actionScript = actionScript + ";";
1003        }
1004
1005        this.actionScript = actionScript;
1006    }
1007
1008    /**
1009     * Url to open when the action item is selected
1010     *
1011     * <p>
1012     * This makes the action behave like a standard link. Instead of posting the form, the configured URL will
1013     * simply be opened (using window.open). For using standard post actions these does not need to be configured.
1014     * </p>
1015     *
1016     * @return Url info instance for the configuration action link
1017     */
1018    @BeanTagAttribute
1019    public UrlInfo getActionUrl() {
1020        return actionUrl;
1021    }
1022
1023    /**
1024     * Setter for {@link #getActionUrl()}.
1025     *
1026     * @param actionUrl property value
1027     */
1028    public void setActionUrl(UrlInfo actionUrl) {
1029        this.actionUrl = actionUrl;
1030    }
1031
1032    /**
1033     * Setter for {@link #isPerformDirtyValidation()}.
1034     *
1035     * @param performDirtyValidation the blockValidateDirty to set
1036     */
1037    public void setPerformDirtyValidation(boolean performDirtyValidation) {
1038        this.performDirtyValidation = performDirtyValidation;
1039    }
1040
1041    /**
1042     * Indicates whether or not to perform dirty validation.
1043     *
1044     * @return true to perform dirty validation
1045     */
1046    @BeanTagAttribute
1047    public boolean isPerformDirtyValidation() {
1048        return performDirtyValidation;
1049    }
1050
1051    /**
1052     * True to make this action clear the dirty flag before submitting.
1053     *
1054     * <p>This will clear both the dirtyForm flag on the form and the count of fields considered dirty on the
1055     * client-side.  This will only be performed if this action is a request based action.</p>
1056     *
1057     * @return true if the dirty
1058     */
1059    @BeanTagAttribute
1060    public boolean isClearDirtyOnAction() {
1061        return clearDirtyOnAction;
1062    }
1063
1064    /**
1065     * Setter for {@link #isClearDirtyOnAction()}.
1066     *
1067     * @param clearDirtyOnAction property value
1068     */
1069    public void setClearDirtyOnAction(boolean clearDirtyOnAction) {
1070        this.clearDirtyOnAction = clearDirtyOnAction;
1071    }
1072
1073    /**
1074     * When true, this action will mark the form dirty by incrementing the dirty field count, but if this action
1075     * refreshes the entire view this will be lost (most actions only refresh the page)
1076     *
1077     * <p>This will increase count of fields considered dirty on the
1078     * client-side by 1.  This will only be performed if this action is a request based action.</p>
1079     *
1080     * @return true if this action is considered dirty, false otherwise
1081     */
1082    @BeanTagAttribute
1083    public boolean isDirtyOnAction() {
1084        return dirtyOnAction;
1085    }
1086
1087    /**
1088     * Set to true, if this action is considered one that changes the form's data (makes the form dirty).
1089     *
1090     * @param dirtyOnAction property value
1091     */
1092    public void setDirtyOnAction(boolean dirtyOnAction) {
1093        this.dirtyOnAction = dirtyOnAction;
1094    }
1095
1096    /**
1097     * Indicates whether the action (input or button) is disabled (doesn't allow interaction).
1098     *
1099     * @return true if the action field is disabled, false if not
1100     */
1101    @BeanTagAttribute
1102    public boolean isDisabled() {
1103        return disabled;
1104    }
1105
1106    /**
1107     * Setter for the disabled indicator.
1108     *
1109     * @param disabled property value
1110     */
1111    public void setDisabled(boolean disabled) {
1112        this.disabled = disabled;
1113    }
1114
1115    /**
1116     * If the action field is disabled, gives a reason for why which will be displayed as a tooltip
1117     * on the action field (button).
1118     *
1119     * @return disabled reason text
1120     * @see #isDisabled()
1121     */
1122    @BeanTagAttribute
1123    public String getDisabledReason() {
1124        return disabledReason;
1125    }
1126
1127    /**
1128     * Setter for the disabled reason text.
1129     *
1130     * @param disabledReason property value
1131     */
1132    public void setDisabledReason(String disabledReason) {
1133        this.disabledReason = disabledReason;
1134    }
1135
1136    /**
1137     * Gets the action image placement.
1138     *
1139     * @return action image placement
1140     */
1141    @BeanTagAttribute
1142    public String getActionImagePlacement() {
1143        return actionImagePlacement;
1144    }
1145
1146    /**
1147     * Set to TOP, BOTTOM, LEFT, RIGHT to position image at that location within the button.
1148     * For the subclass ActionLinkField only LEFT and RIGHT are allowed.  When set to blank/null/IMAGE_ONLY, the image
1149     * itself will be the Action, if no value is set the default is ALWAYS LEFT, you must explicitly set
1150     * blank/null/IMAGE_ONLY to use ONLY the image as the Action.
1151     *
1152     * @param actionImagePlacement action image placement indicator
1153     */
1154    public void setActionImagePlacement(String actionImagePlacement) {
1155        this.actionImagePlacement = actionImagePlacement;
1156    }
1157
1158    /**
1159     * Gets the action icon placement.
1160     *
1161     * @return action icon placement
1162     */
1163    @BeanTagAttribute
1164    public String getActionIconPlacement() {
1165        return actionIconPlacement;
1166    }
1167
1168    /**
1169     * Setter for {@link #getActionIconPlacement()}.
1170     *
1171     * @param actionIconPlacement property value
1172     */
1173    public void setActionIconPlacement(String actionIconPlacement) {
1174        this.actionIconPlacement = actionIconPlacement;
1175    }
1176
1177    /**
1178     * Gets the script which needs to be invoked before the form is submitted
1179     *
1180     * <p>
1181     * The preSubmitCall can carry out custom logic for the action before the submit occurs. The value should
1182     * be given as one or more lines of script and should return a boolean. If false is returned from the call,
1183     * the submit is not carried out. Furthermore, the preSubmitCall can refer to the request object through the
1184     * variable 'kradRequest' or 'this'. This gives full access over the request for doing such things as
1185     * adding additional data
1186     * </p>
1187     *
1188     * <p>
1189     * Examples 'return doFunction(kradRequest);', 'var valid=true;return valid;'
1190     * </p>
1191     *
1192     * <p>
1193     * The preSubmit call will be invoked both for ajax and non-ajax submits
1194     * </p>
1195     *
1196     * @return script text that will be invoked before form submission
1197     */
1198    @BeanTagAttribute
1199    public String getPreSubmitCall() {
1200        return preSubmitCall;
1201    }
1202
1203    /**
1204     * Setter for {@link #getPreSubmitCall()}.
1205     *
1206     * @param preSubmitCall property value
1207     */
1208    public void setPreSubmitCall(String preSubmitCall) {
1209        this.preSubmitCall = preSubmitCall;
1210    }
1211
1212    /**
1213     * Text to display as a confirmation of the action.
1214     *
1215     * <p>When this text is displayed the user will receive a confirmation when the action is taken. The user
1216     * can then cancel the action, or continue. If set, {@link Action#getConfirmationDialog()} will be used
1217     * to build the dialog. Otherwise, the dialog is created dynamically on the client.</p>
1218     *
1219     * @return text to display in a confirmation for the action
1220     */
1221    public String getConfirmationPromptText() {
1222        return confirmationPromptText;
1223    }
1224
1225    /**
1226     * @see Action#getConfirmationPromptText()
1227     */
1228    public void setConfirmationPromptText(String confirmationPromptText) {
1229        this.confirmationPromptText = confirmationPromptText;
1230    }
1231
1232    /**
1233     * Dialog to use an a confirmation for the action.
1234     *
1235     * <p>For custom confirmation dialogs this can be set to any valid dialog group. It is expected that the
1236     * dialog have at least one action with the dialog response of 'true' to continue the action.</p>
1237     *
1238     * @return dialog group instance to use an a confirmation
1239     */
1240    public DialogGroup getConfirmationDialog() {
1241        return confirmationDialog;
1242    }
1243
1244    /**
1245     * @see Action#getConfirmationDialog()
1246     */
1247    public void setConfirmationDialog(DialogGroup confirmationDialog) {
1248        this.confirmationDialog = confirmationDialog;
1249    }
1250
1251    /**
1252     * If the action is within a {@link org.kuali.rice.krad.uif.container.DialogGroup} it can be configured to
1253     * dismiss the dialog using this property.
1254     *
1255     * <p>A dialog can be dismissed at various points of the action using the values:
1256     *    IMMEDIATE - dismiss dialog right away (and do nothig further)
1257     *    PRESUBMIT - run the action presubmit (which can include validation), if successful close the dialog and
1258     *                do nothing further
1259     *    REQUEST - carry out the action request as usual and dismiss the dialog when the server request is made
1260     * </p>
1261     *
1262     * <p>Note the id for the dialog that will be dismissed is automatically associated with the action when
1263     * the dialog is shown.</p>
1264     *
1265     * @return String option for dismissing a dialog
1266     */
1267    public String getDialogDismissOption() {
1268        return dialogDismissOption;
1269    }
1270
1271    /**
1272     * @see Action#getDialogDismissOption()
1273     */
1274    public void setDialogDismissOption(String dialogDismissOption) {
1275        this.dialogDismissOption = dialogDismissOption;
1276    }
1277
1278    /**
1279     * If the action is within a {@link org.kuali.rice.krad.uif.container.DialogGroup} it can be configured to
1280     * return a response using this property.
1281     *
1282     * <p>Dialogs can be used to get a response from a user, either a simple confirmation (true or false), or to
1283     * choice from a list of options. The responses for the dialog are created with action components. The property
1284     * specifies the action value that should be returned (when chosen) to the dialog response handlers. For example,
1285     * in a simple confirmation one action will have a dialog response 'false', and the other will have a dialog
1286     * response 'true'.</p>
1287     *
1288     * @return String dialog response value
1289     */
1290    public String getDialogResponse() {
1291        return dialogResponse;
1292    }
1293
1294    /**
1295     * @see Action#getDialogResponse()
1296     */
1297    public void setDialogResponse(String dialogResponse) {
1298        this.dialogResponse = dialogResponse;
1299    }
1300
1301    /**
1302     * When this property is set to true it will submit the form using Ajax instead of the browser submit. Will default
1303     * to updating the page contents
1304     *
1305     * @return boolean
1306     */
1307    @BeanTagAttribute
1308    public boolean isAjaxSubmit() {
1309        return ajaxSubmit;
1310    }
1311
1312    /**
1313     * Setter for {@link #isAjaxSubmit()}.
1314     *
1315     * @param ajaxSubmit property value
1316     */
1317    public void setAjaxSubmit(boolean ajaxSubmit) {
1318        this.ajaxSubmit = ajaxSubmit;
1319    }
1320
1321    /**
1322     * Gets the return type for the ajax call
1323     *
1324     * <p>
1325     * The ajax return type indicates how the response content will be handled in the client. Typical
1326     * examples include updating a component, the page, or doing a redirect.
1327     * </p>
1328     *
1329     * @return return type
1330     * @see org.kuali.rice.krad.uif.UifConstants.AjaxReturnTypes
1331     */
1332    @BeanTagAttribute
1333    public String getAjaxReturnType() {
1334        return this.ajaxReturnType;
1335    }
1336
1337    /**
1338     * Setter for the type of ajax return.
1339     *
1340     * @param ajaxReturnType property value
1341     */
1342    public void setAjaxReturnType(String ajaxReturnType) {
1343        this.ajaxReturnType = ajaxReturnType;
1344    }
1345
1346    /**
1347     * Indicates if the action response should be displayed in a lightbox.
1348     *
1349     * @return true if response should be rendered in a lightbox, false if not
1350     */
1351    @BeanTagAttribute
1352    public boolean isDisplayResponseInLightBox() {
1353        return StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey());
1354    }
1355
1356    /**
1357     * Setter for indicating the response should be rendered in a lightbox.
1358     *
1359     * @param displayResponseInLightBox property value
1360     */
1361    public void setDisplayResponseInLightBox(boolean displayResponseInLightBox) {
1362        if (displayResponseInLightBox) {
1363            this.ajaxReturnType = UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey();
1364        }
1365        // if display lightbox is false and it was previously true, set to default of update page
1366        else if (StringUtils.equals(this.ajaxReturnType, UifConstants.AjaxReturnTypes.DISPLAYLIGHTBOX.getKey())) {
1367            this.ajaxReturnType = UifConstants.AjaxReturnTypes.UPDATEPAGE.getKey();
1368        }
1369    }
1370
1371    /**
1372     * Gets the script which will be invoked on a successful ajax call
1373     *
1374     * <p>
1375     * The successCallback can carry out custom logic after a successful ajax submission has been made. The
1376     * value can contain one or more script statements. In addition, the response contents can be accessed
1377     * through the variable 'responseContents'
1378     * </p>
1379     *
1380     * <p>
1381     * Examples 'handleSuccessfulUpdate(responseContents);'
1382     * </p>
1383     *
1384     * <p>
1385     * The successCallback may only be specified when {@link #isAjaxSubmit()} is true
1386     * </p>
1387     *
1388     * @return script to be executed when the action is successful
1389     */
1390    @BeanTagAttribute
1391    public String getSuccessCallback() {
1392        return successCallback;
1393    }
1394
1395    /**
1396     * Setter for successCallback.
1397     *
1398     * @param successCallback property value
1399     */
1400    public void setSuccessCallback(String successCallback) {
1401        this.successCallback = successCallback;
1402    }
1403
1404    /**
1405     * Gets the script which will be invoked when the action fails due to problems in the ajax call or
1406     * the return of an incident report
1407     *
1408     * <p>
1409     * The errorCallback can carry out custom logic after a failed ajax submission. The
1410     * value can contain one or more script statements. In addition, the response contents can be accessed
1411     * through the variable 'responseContents'
1412     * </p>
1413     *
1414     * <p>
1415     * Examples 'handleFailedUpdate(responseContents);'
1416     * </p>
1417     *
1418     * <p>
1419     * The errorCallback may only be specified when {@link #isAjaxSubmit()} is true
1420     * </p>
1421     *
1422     * @return script to be executed when the action is successful
1423     */
1424    @BeanTagAttribute
1425    public String getErrorCallback() {
1426        return errorCallback;
1427    }
1428
1429    /**
1430     * Setter for {@link #getErrorCallback()}.
1431     *
1432     * @param errorCallback property value
1433     */
1434    public void setErrorCallback(String errorCallback) {
1435        this.errorCallback = errorCallback;
1436    }
1437
1438    /**
1439     * Id for the component that should be refreshed after the action completes
1440     *
1441     * <p>
1442     * Either refresh id or refresh property name can be set to configure the component that should
1443     * be refreshed after the action completes. If both are blank, the page will be refreshed
1444     * </p>
1445     *
1446     * @return valid component id
1447     */
1448    @BeanTagAttribute
1449    public String getRefreshId() {
1450        return refreshId;
1451    }
1452
1453    /**
1454     * Setter for the {@link #getRefreshId()}.
1455     *
1456     * @param refreshId property value
1457     */
1458    public void setRefreshId(String refreshId) {
1459        this.refreshId = refreshId;
1460    }
1461
1462    /**
1463     * Property name for the {@link org.kuali.rice.krad.uif.field.DataField} that should be refreshed after the action
1464     * completes
1465     *
1466     * <p>
1467     * Either refresh id or refresh property name can be set to configure the component that should
1468     * be refreshed after the action completes. If both are blank, the page will be refreshed
1469     * </p>
1470     *
1471     * <p>
1472     * Property name will be adjusted to use the default binding path unless it contains the form prefix
1473     * </p>
1474     *
1475     * @return valid property name with an associated DataField
1476     * @see org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX
1477     */
1478    @BeanTagAttribute
1479    public String getRefreshPropertyName() {
1480        return refreshPropertyName;
1481    }
1482
1483    /**
1484     * Setter for the property name of the DataField that should be refreshed.
1485     *
1486     * @param refreshPropertyName property value
1487     */
1488    public void setRefreshPropertyName(String refreshPropertyName) {
1489        this.refreshPropertyName = refreshPropertyName;
1490    }
1491
1492    /**
1493     * Gets the loading message used by action's blockUI.
1494     *
1495     * @return String if String is not null, used in place of loading message
1496     */
1497    @BeanTagAttribute
1498    public String getLoadingMessageText() {
1499        return loadingMessageText;
1500    }
1501
1502    /**
1503     * When this property is set, it is used in place of the loading message text used by the blockUI.
1504     *
1505     * @param loadingMessageText property value
1506     */
1507    public void setLoadingMessageText(String loadingMessageText) {
1508        this.loadingMessageText = loadingMessageText;
1509    }
1510
1511    /**
1512     * Indicates whether blocking for the action should be disabled
1513     *
1514     * <p>
1515     * By default when an action is invoked part of the page or the entire window is blocked until
1516     * the action completes. If this property is set to true the blocking will not be displayed.
1517     * </p>
1518     *
1519     * <p>
1520     * Currently if an action returns a file download, this property should be set to true. If not, the blocking
1521     * will never get unblocked (because the page does not get notification a file was downloaded)
1522     * </p>
1523     *
1524     * @return true if blocking should be disabled, false if not
1525     */
1526    @BeanTagAttribute
1527    public boolean isDisableBlocking() {
1528        return disableBlocking;
1529    }
1530
1531    /**
1532     * Setter for disabling blocking when the action is invoked.
1533     *
1534     * @param disableBlocking property value
1535     */
1536    public void setDisableBlocking(boolean disableBlocking) {
1537        this.disableBlocking = disableBlocking;
1538    }
1539
1540    /**
1541     * Evaluate the disable condition on controls which disable it on each key up event.
1542     *
1543     * @return true if evaluate on key up, false otherwise
1544     */
1545    @BeanTagAttribute
1546    public boolean isEvaluateDisabledOnKeyUp() {
1547        return evaluateDisabledOnKeyUp;
1548    }
1549
1550    /**
1551     * Setter for {@link #isEvaluateDisabledOnKeyUp()}.
1552     *
1553     * @param evaluateDisabledOnKeyUp property value
1554     */
1555    public void setEvaluateDisabledOnKeyUp(boolean evaluateDisabledOnKeyUp) {
1556        this.evaluateDisabledOnKeyUp = evaluateDisabledOnKeyUp;
1557    }
1558
1559    /**
1560     * Evaluate if this action is the default action for a page, view, group, or section.
1561     *
1562     * @return true if this action is default, false otherwise
1563     */
1564    @BeanTagAttribute(name = "defaultEnterKeyAction")
1565    public boolean isDefaultEnterKeyAction() {
1566        return this.defaultEnterKeyAction;
1567    }
1568
1569    /**
1570     * @see  #isDefaultEnterKeyAction()
1571     */
1572    public void setDefaultEnterKeyAction(boolean defaultEnterKeyAction) {
1573        this.defaultEnterKeyAction = defaultEnterKeyAction;
1574    }
1575
1576    /**
1577     * Get the disable condition js derived from the springEL, cannot be set.
1578     *
1579     * @return the disableConditionJs javascript to be evaluated
1580     */
1581    public String getDisabledConditionJs() {
1582        return disabledConditionJs;
1583    }
1584
1585    /**
1586     * Sets the disabled condition javascript.
1587     *
1588     * @param disabledConditionJs property value
1589     */
1590    protected void setDisabledConditionJs(String disabledConditionJs) {
1591        this.disabledConditionJs = disabledConditionJs;
1592    }
1593
1594    /**
1595     * Gets a list of control names to add handlers to for disable functionality, cannot be set.
1596     *
1597     * @return control names to add handlers to for disable
1598     */
1599    public List<String> getDisabledConditionControlNames() {
1600        return disabledConditionControlNames;
1601    }
1602
1603    /**
1604     * Set disabled condition control names.
1605     *
1606     * @param disabledConditionControlNames property value
1607     */
1608    public void setDisabledConditionControlNames(List<String> disabledConditionControlNames) {
1609        this.disabledConditionControlNames = disabledConditionControlNames;
1610    }
1611
1612    /**
1613     * Gets the property names of fields that when changed, will disable this component.
1614     *
1615     * @return the property names to monitor for change to disable this component
1616     */
1617    @BeanTagAttribute
1618    public List<String> getDisabledWhenChangedPropertyNames() {
1619        return disabledWhenChangedPropertyNames;
1620    }
1621
1622    /**
1623     * Sets the property names of fields that when changed, will disable this component.
1624     *
1625     * @param disabledWhenChangedPropertyNames property value
1626     */
1627    public void setDisabledWhenChangedPropertyNames(List<String> disabledWhenChangedPropertyNames) {
1628        this.disabledWhenChangedPropertyNames = disabledWhenChangedPropertyNames;
1629    }
1630
1631    /**
1632     * Gets the property names of fields that when changed, will enable this component.
1633     *
1634     * @return the property names to monitor for change to enable this component
1635     */
1636    @BeanTagAttribute
1637    public List<String> getEnabledWhenChangedPropertyNames() {
1638        return enabledWhenChangedPropertyNames;
1639    }
1640
1641    /**
1642     * Sets the property names of fields that when changed, will enable this component.
1643     *
1644     * @param enabledWhenChangedPropertyNames property value
1645     */
1646    public void setEnabledWhenChangedPropertyNames(List<String> enabledWhenChangedPropertyNames) {
1647        this.enabledWhenChangedPropertyNames = enabledWhenChangedPropertyNames;
1648    }
1649
1650    /**
1651     * Sets the disabled expression.
1652     *
1653     * @param disabledExpression property value
1654     */
1655    protected void setDisabledExpression(String disabledExpression) {
1656        this.disabledExpression = disabledExpression;
1657    }
1658
1659    /**
1660     * {@inheritDoc}
1661     */
1662    @Override
1663    public void completeValidation(ValidationTrace tracer) {
1664        tracer.addBean(this);
1665
1666        // Checks that an action is set
1667        if (getJumpToIdAfterSubmit() != null && getJumpToNameAfterSubmit() != null) {
1668            String currentValues[] = {"jumpToIdAfterSubmit =" + getJumpToIdAfterSubmit(),
1669                    "jumpToNameAfterSubmit =" + getJumpToNameAfterSubmit()};
1670            tracer.createWarning("Only 1 jumpTo property should be set", currentValues);
1671        }
1672        super.completeValidation(tracer.getCopy());
1673    }
1674}