View Javadoc
1   /**
2    * Copyright 2005-2015 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.container;
17  
18  import org.kuali.rice.core.api.util.KeyValue;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.control.MultiValueControl;
25  import org.kuali.rice.krad.uif.field.InputField;
26  import org.kuali.rice.krad.uif.field.MessageField;
27  import org.kuali.rice.krad.uif.util.ScriptUtils;
28  import org.kuali.rice.krad.uif.view.View;
29  
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.List;
33  
34  /**
35   * Special type of <code>Group</code> that presents a the content for a modal dialog
36   *
37   * <p>
38   * This type of group will be hidden when the main view is displayed. It will be used as
39   * content inside the LightBox widget when the modal dialog is displayed.
40   * For convenience, this group contains a standard set of components for commonly used modal dialogs
41   * <ul>
42   * <li>a prompt to display in the lightbox</li>
43   * <li>an optional explanation <code>InputField</code> for holding the user's textual response</li>
44   * <li>a set of response options for the user to choose from</li>
45   * </ul>
46   *
47   * <p>
48   * The DialogGroup may also serve as a base class for more complex dialogs.
49   * The default settings for this DialogGroup is to display a prompt message
50   * with two buttons labeled OK and Cancel.
51   * The optional explanation <code>TextAreaControl</code> is hidden by default.
52   * </p>
53   *
54   * <p>
55   * The prompt text, number of user options and their corresponding label are configurable.
56   * The <code>InputField</code> for the explanation is <code>TextAreaControl</code> by default.
57   * It may be configured to other types of InputFields.
58   * The Component for ResponseInputField is a <code>HorizontalCheckboxGroup</code> by default.
59   * JQuery styling is then used to style the checkboxes as buttons. The ResponseInputField may
60   * be configured to other <code>InputField</code> types.
61   * </p>
62   *
63   * @author Kuali Rice Team (rice.collab@kuali.org)
64   */
65  @BeanTags({@BeanTag(name = "dialogGroup-bean", parent = "Uif-DialogGroup"),
66          @BeanTag(name = "sensitiveData-dialogGroup-bean", parent = "Uif-SensitiveData-DialogGroup"),
67          @BeanTag(name = "ok-cancel-dialogGroup-bean", parent = "Uif-OK-Cancel-DialogGroup"),
68          @BeanTag(name = "yes-no-dialogGroup-bean", parent = "Uif-Yes-No-DialogGroup"),
69          @BeanTag(name = "true-false-dialogGroup-bean", parent = "Uif-True-False-DialogGroup"),
70          @BeanTag(name = "checkbox-dialogGroup-bean", parent = "Uif-Checkbox-DialogGroup"),
71          @BeanTag(name = "radioButton-dialogGroup-bean", parent = "Uif-RadioButton-DialogGroup")})
72  public class DialogGroup extends Group {
73      private static final long serialVersionUID = 1L;
74  
75      private String promptText;
76      private List<KeyValue> availableResponses;
77  
78      private MessageField prompt;
79      private InputField explanation;
80      private InputField responseInputField;
81  
82      private boolean reverseButtonOrder;
83      private boolean displayExplanation;
84  
85      private String onDialogResponseScript;
86      private String onShowDialogScript;
87  
88      public DialogGroup() {
89          super();
90      }
91  
92      /**
93       * The following actions are performed in this phase:
94       *
95       * <ul>
96       * <li>Move custom dialogGroup properties prompt, explanation, and responseInputField into items collection if they
97       * are not already present</li>
98       * </ul>
99       *
100      * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
101      *      java.lang.Object)
102      */
103     @Override
104     public void performInitialization(View view, Object model) {
105         super.performInitialization(view, model);
106 
107         // move dialogGroup custom properties into the items property.
108         // where they will be rendered by group.jsp
109         List<Component> newItems = new ArrayList<Component>();
110         List<? extends Component> items = getItems();
111 
112         // do not add the custom properties if they are already present
113         if (!(items.contains(prompt))) {
114             view.assignComponentIds(prompt);
115             newItems.add(prompt);
116         }
117 
118         if (!(items.contains(explanation))) {
119             view.assignComponentIds(explanation);
120             newItems.add(explanation);
121         }
122 
123         newItems.addAll(getItems());
124 
125         if (!(items.contains(responseInputField))) {
126             view.assignComponentIds(responseInputField);
127             newItems.add(responseInputField);
128         }
129 
130         this.setItems(newItems);
131     }
132 
133     /**
134      * The following actions are performed in this phase:
135      *
136      * <p>
137      * <ul>
138      * <li>set the promptText in the message</li>
139      * <li>sets whether to render explanation field</li>
140      * <li>set the options for the checkbox control to the availableResponses KeyValue property of
141      * this dialogGroup</li>
142      * <li>orders response buttons</li>
143      * </ul>
144      * </p>
145      *
146      * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View,
147      *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
148      */
149     @Override
150     public void performApplyModel(View view, Object model, Component parent) {
151         super.performApplyModel(view, model, parent);
152 
153         // set the messageTest to the promptText
154         prompt.setMessageText(promptText);
155 
156         // hide or show explanation
157         explanation.setRender(displayExplanation);
158 
159         // add options to checkbox
160         if (responseInputField.getControl() != null && responseInputField.getControl() instanceof MultiValueControl) {
161             MultiValueControl multiValueControl = (MultiValueControl) responseInputField.getControl();
162 
163             if (reverseButtonOrder) {
164                 // reverse the button order (without changing original list)
165                 List<KeyValue> buttonList = new ArrayList<KeyValue>(availableResponses);
166                 Collections.reverse(buttonList);
167                 multiValueControl.setOptions(buttonList);
168             } else {
169                 multiValueControl.setOptions(availableResponses);
170             }
171         }
172     }
173 
174     /**
175      * The following actions are performed in this phase:
176      *
177      * <p>
178      * <ul>
179      * <li>handle render via ajax configuration</li>
180      * <li>adds script to the response input field for trigger the 'response' event</li>
181      * </ul>
182      * </p>
183      *
184      * @param view view instance that should be finalized for rendering
185      * @param model top level object containing the data
186      * @param parent parent component
187      */
188     @Override
189     public void performFinalize(View view, Object model, Component parent) {
190         super.performFinalize(view, model, parent);
191 
192         if (responseInputField != null) {
193             String responseInputSelector = "#" + responseInputField.getId() + " [name='" +
194                     responseInputField.getBindingInfo().getBindingPath() + "']";
195 
196             String onChangeScript = "var value = coerceValue(\"" + responseInputField.getBindingInfo().getBindingPath()
197                     + "\");";
198             onChangeScript += "jQuery('#" + getId() + "').trigger({type:'" + UifConstants.JsEvents.DIALOG_RESPONSE
199                     + "',value:value});";
200 
201             String onChangeHandler = "jQuery(\"" + responseInputSelector + "\").change(function(e){" + onChangeScript
202                     + "});";
203 
204             String onReadyScript = ScriptUtils.appendScript(getOnDocumentReadyScript(), onChangeHandler);
205             setOnDocumentReadyScript(onReadyScript);
206         }
207     }
208 
209     /**
210      * Override to add the handler script for the dialog response and show dialog events
211      *
212      * @see org.kuali.rice.krad.uif.component.Component#getEventHandlerScript()
213      */
214     @Override
215     public String getEventHandlerScript() {
216         String handlerScript = super.getEventHandlerScript();
217 
218         handlerScript += ScriptUtils.buildEventHandlerScript(getId(), UifConstants.JsEvents.DIALOG_RESPONSE,
219                 getOnDialogResponseScript());
220 
221         handlerScript += ScriptUtils.buildEventHandlerScript(getId(), UifConstants.JsEvents.SHOW_DIALOG,
222                 getOnShowDialogScript());
223 
224         return handlerScript;
225     }
226 
227     /**
228      * Returns the text to be displayed as the prompt or main message in this simple dialog
229      *
230      * @return String containing the prompt text
231      */
232 
233     @BeanTagAttribute(name = "promptText")
234     public String getPromptText() {
235         return promptText;
236     }
237 
238     /**
239      * Sets the text String to display as the main message in this dialog
240      *
241      * @param promptText the String to be displayed as the main message
242      */
243     public void setPromptText(String promptText) {
244         this.promptText = promptText;
245     }
246 
247     /**
248      * Retrieves the Message element for this dialog
249      *
250      * @return Message the text element containing the message string
251      */
252     @BeanTagAttribute(name = "prompt", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
253     public MessageField getPrompt() {
254         return prompt;
255     }
256 
257     /**
258      * Sets the prompt Message for this dialog
259      *
260      * @param prompt The Message element for this dialog
261      */
262     public void setPrompt(MessageField prompt) {
263         this.prompt = prompt;
264     }
265 
266     /**
267      * Retrieves the explanation InputField used to gather user input text from the dialog
268      *
269      * <p>
270      * By default, the control for this input is configured as a TextAreaControl. It may be configured for
271      * other types of input fields.
272      * </p>
273      *
274      * @return InputField component
275      */
276     @BeanTagAttribute(name = "explanation", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
277     public InputField getExplanation() {
278         return explanation;
279     }
280 
281     /**
282      * Sets the InputField for gathering user text input
283      *
284      * @param explanation InputField
285      */
286     public void setExplanation(InputField explanation) {
287         this.explanation = explanation;
288     }
289 
290     /**
291      * determines if the explanation InputField is to be displayed in this dialog
292      *
293      * <p>
294      * False by default.
295      * </p>
296      *
297      * @return true if this user input is to be rendered, false if not
298      */
299     @BeanTagAttribute(name = "displayExplanation")
300     public boolean isDisplayExplanation() {
301         return displayExplanation;
302     }
303 
304     /**
305      * Sets whether to display the Explanation InputField on this dialog
306      *
307      * @param displayExplanation true if explanation control is to be displayed, false if not
308      */
309     public void setDisplayExplanation(boolean displayExplanation) {
310         this.displayExplanation = displayExplanation;
311     }
312 
313     /**
314      * Gets the choices provided for user response.
315      *
316      * <p>
317      * A List of KeyValue pairs for each of the choices provided on this dialog.
318      * </p>
319      *
320      * @return the List of response actions to provide the user
321      */
322     @BeanTagAttribute(name = "availableResponses", type = BeanTagAttribute.AttributeType.LISTBEAN)
323     public List<KeyValue> getAvailableResponses() {
324         return availableResponses;
325     }
326 
327     /**
328      * Sets the list of user responses to provide on this dialog
329      *
330      * @param availableResponses a List of KeyValue pairs representing the user response choices
331      */
332     public void setAvailableResponses(List<KeyValue> availableResponses) {
333         this.availableResponses = availableResponses;
334     }
335 
336     /**
337      * Retrieves the InputField containing the choices displayed in this dialog
338      *
339      * <p>
340      * By default, this InputField is configured to be a HorizontalCheckboxControl.
341      * Styling is then used to make the checkboxes appear to be buttons.
342      * The values of the availableResponses List are used as labels for the "buttons".
343      * </p>
344      *
345      * @return InputField component within this dialog
346      */
347     @BeanTagAttribute(name = "responseInputField", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
348     public InputField getResponseInputField() {
349         return responseInputField;
350     }
351 
352     /**
353      * Sets the type of InputField used to display the user choices in this dialog
354      *
355      * @param responseInputField a component used to display the response choices
356      */
357     public void setResponseInputField(InputField responseInputField) {
358         this.responseInputField = responseInputField;
359     }
360 
361     /**
362      * Determines the positioning order of the choices displayed on this dialog
363      *
364      * <p>
365      * Some page designers like the positive choice on the left and the negative choice on the right.
366      * Others, prefer just the opposite. This allows the order to easily be switched.
367      * </p>
368      *
369      * @return true if choices left to right
370      *         false if choices right to left
371      */
372     @BeanTagAttribute(name = "reverseButtonOrder")
373     public boolean isReverseButtonOrder() {
374         return reverseButtonOrder;
375     }
376 
377     /**
378      * Sets the display order of the choices displayed on this dialog
379      *
380      * <p>
381      * By default, the choices are displayed left to right
382      * </p>
383      *
384      * @param reverseButtonOrder true if buttons displayed left to right, false if right to left
385      */
386     public void setReverseButtonOrder(boolean reverseButtonOrder) {
387         this.reverseButtonOrder = reverseButtonOrder;
388     }
389 
390     /**
391      * Script that will be invoked when the response event is thrown
392      *
393      * <p>
394      * The dialog group will throw a custom event type 'dialogresponse.uif' when a change occurs for the response
395      * input field (for example one of the response options is selected). Script given here will bind to that
396      * event as a handler
397      * </p>
398      *
399      * @return javascript that will execute for the response event
400      */
401     @BeanTagAttribute(name = "onDialogResponseScript")
402     public String getOnDialogResponseScript() {
403         return onDialogResponseScript;
404     }
405 
406     /**
407      * Setter for the 'dialogresponse.uif' event handler code
408      *
409      * @param onDialogResponseScript
410      */
411     public void setOnDialogResponseScript(String onDialogResponseScript) {
412         this.onDialogResponseScript = onDialogResponseScript;
413     }
414 
415     /**
416      * Script that will get invoked when the dialog group is shown
417      *
418      * <p>
419      * Initially a dialog group will either be hidden in the DOM or not present at all (if retrieved via Ajax).
420      * When the dialog is triggered and shown, the 'showdialog.uif' event will be thrown and this script will
421      * be executed
422      * </p>
423      *
424      * @return JavaScript code to execute when the dialog is shown
425      */
426     @BeanTagAttribute(name = "onShowDialogScript")
427     public String getOnShowDialogScript() {
428         return onShowDialogScript;
429     }
430 
431     /**
432      * Setter for the 'showdialog.uif' event handler code
433      *
434      * @param onShowDialogScript
435      */
436     public void setOnShowDialogScript(String onShowDialogScript) {
437         this.onShowDialogScript = onShowDialogScript;
438     }
439 
440     /**
441      * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
442      */
443     @Override
444     protected <T> void copyProperties(T component) {
445         super.copyProperties(component);
446         DialogGroup dialogGroupCopy = (DialogGroup) component;
447 
448         if (this.availableResponses != null) {
449             dialogGroupCopy.setAvailableResponses(new ArrayList<KeyValue>(this.availableResponses));
450         }
451 
452         dialogGroupCopy.setDisplayExplanation(this.displayExplanation);
453         dialogGroupCopy.setOnDialogResponseScript(this.onDialogResponseScript);
454         dialogGroupCopy.setOnShowDialogScript(this.onShowDialogScript);
455 
456         if (this.prompt != null) {
457             dialogGroupCopy.setPrompt((MessageField)this.prompt.copy());
458         }
459 
460         dialogGroupCopy.setPromptText(this.promptText);
461         dialogGroupCopy.setReverseButtonOrder(this.reverseButtonOrder);
462 
463         if (this.explanation != null) {
464             dialogGroupCopy.setExplanation((InputField) this.explanation.copy());
465         }
466 
467         if (this.responseInputField != null) {
468             dialogGroupCopy.setResponseInputField((InputField) this.responseInputField.copy());
469         }
470     }
471 }