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