View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.container;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  
22  import org.kuali.rice.core.api.util.KeyValue;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
24  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
25  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
26  import org.kuali.rice.krad.uif.UifConstants;
27  import org.kuali.rice.krad.uif.component.Component;
28  import org.kuali.rice.krad.uif.control.MultiValueControl;
29  import org.kuali.rice.krad.uif.field.InputField;
30  import org.kuali.rice.krad.uif.field.MessageField;
31  import org.kuali.rice.krad.uif.util.LifecycleElement;
32  import org.kuali.rice.krad.uif.util.ScriptUtils;
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 GroupBase {
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      * {@inheritDoc}
101      */
102     @Override
103     public void performInitialization(Object model) {
104         super.performInitialization(model);
105 
106         // move dialogGroup custom properties into the items property.
107         // where they will be rendered by group.jsp
108         List<Component> newItems = new ArrayList<Component>();
109         List<? extends Component> items = getItems();
110 
111         // do not add the custom properties if they are already present
112         if (!(items.contains(prompt))) {
113             newItems.add(prompt);
114         }
115 
116         if (!(items.contains(explanation))) {
117             newItems.add(explanation);
118         }
119 
120         newItems.addAll(getItems());
121 
122         if (!(items.contains(responseInputField))) {
123             newItems.add(responseInputField);
124         }
125 
126         this.setItems(newItems);
127     }
128 
129     /**
130      * The following actions are performed in this phase:
131      *
132      * <p>
133      * <ul>
134      * <li>set the promptText in the message</li>
135      * <li>sets whether to render explanation field</li>
136      * <li>set the options for the checkbox control to the availableResponses KeyValue property of
137      * this dialogGroup</li>
138      * <li>orders response buttons</li>
139      * </ul>
140      * </p>
141      *
142      * {@inheritDoc}
143      */
144     @Override
145     public void performApplyModel(Object model, LifecycleElement parent) {
146         super.performApplyModel(model, parent);
147 
148         // set the messageTest to the promptText
149         prompt.setMessageText(promptText);
150 
151         // hide or show explanation
152         explanation.setRender(displayExplanation);
153 
154         // add options to checkbox
155         if (responseInputField.getControl() != null && responseInputField.getControl() instanceof MultiValueControl) {
156             MultiValueControl multiValueControl = (MultiValueControl) responseInputField.getControl();
157 
158             if (reverseButtonOrder) {
159                 // reverse the button order (without changing original list)
160                 List<KeyValue> buttonList = new ArrayList<KeyValue>(availableResponses);
161                 Collections.reverse(buttonList);
162                 multiValueControl.setOptions(buttonList);
163             } else {
164                 multiValueControl.setOptions(availableResponses);
165             }
166         }
167     }
168 
169     /**
170      * The following actions are performed in this phase:
171      *
172      * <p>
173      * <ul>
174      * <li>handle render via ajax configuration</li>
175      * <li>adds script to the response input field for trigger the 'response' event</li>
176      * </ul>
177      * </p>
178      *
179      * @param model top level object containing the data
180      * @param parent parent component
181      */
182     @Override
183     public void performFinalize(Object model, LifecycleElement parent) {
184         super.performFinalize(model, parent);
185 
186         if (responseInputField != null) {
187             String responseInputSelector = "#" + responseInputField.getId() + " [name='" +
188                     responseInputField.getBindingInfo().getBindingPath() + "']";
189 
190             String onChangeScript = "var value = coerceValue(\"" + responseInputField.getBindingInfo().getBindingPath()
191                     + "\");";
192             onChangeScript += "jQuery('#" + getId() + "').trigger({type:'" + UifConstants.JsEvents.DIALOG_RESPONSE
193                     + "',value:value});";
194 
195             String onChangeHandler = "jQuery(\"" + responseInputSelector + "\").change(function(e){" + onChangeScript
196                     + "});";
197 
198             String onReadyScript = ScriptUtils.appendScript(getOnDocumentReadyScript(), onChangeHandler);
199             setOnDocumentReadyScript(onReadyScript);
200         }
201     }
202 
203     /**
204      * Override to add the handler script for the dialog response and show dialog events
205      *
206      * {@inheritDoc}
207      */
208     @Override
209     public String getEventHandlerScript() {
210         String handlerScript = super.getEventHandlerScript();
211 
212         handlerScript += ScriptUtils.buildEventHandlerScript(getId(), UifConstants.JsEvents.DIALOG_RESPONSE,
213                 getOnDialogResponseScript());
214 
215         handlerScript += ScriptUtils.buildEventHandlerScript(getId(), UifConstants.JsEvents.SHOW_DIALOG,
216                 getOnShowDialogScript());
217 
218         return handlerScript;
219     }
220 
221     /**
222      * Returns the text to be displayed as the prompt or main message in this simple dialog
223      *
224      * @return String containing the prompt text
225      */
226 
227     @BeanTagAttribute(name = "promptText")
228     public String getPromptText() {
229         return promptText;
230     }
231 
232     /**
233      * Sets the text String to display as the main message in this dialog
234      *
235      * @param promptText the String to be displayed as the main message
236      */
237     public void setPromptText(String promptText) {
238         this.promptText = promptText;
239     }
240 
241     /**
242      * Retrieves the Message element for this dialog
243      *
244      * @return Message the text element containing the message string
245      */
246     @BeanTagAttribute(name = "prompt", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
247     public MessageField getPrompt() {
248         return prompt;
249     }
250 
251     /**
252      * Sets the prompt Message for this dialog
253      *
254      * @param prompt The Message element for this dialog
255      */
256     public void setPrompt(MessageField prompt) {
257         this.prompt = prompt;
258     }
259 
260     /**
261      * Retrieves the explanation InputField used to gather user input text from the dialog
262      *
263      * <p>
264      * By default, the control for this input is configured as a TextAreaControl. It may be configured for
265      * other types of input fields.
266      * </p>
267      *
268      * @return InputField component
269      */
270     @BeanTagAttribute(name = "explanation", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
271     public InputField getExplanation() {
272         return explanation;
273     }
274 
275     /**
276      * Sets the InputField for gathering user text input
277      *
278      * @param explanation InputField
279      */
280     public void setExplanation(InputField explanation) {
281         this.explanation = explanation;
282     }
283 
284     /**
285      * determines if the explanation InputField is to be displayed in this dialog
286      *
287      * <p>
288      * False by default.
289      * </p>
290      *
291      * @return true if this user input is to be rendered, false if not
292      */
293     @BeanTagAttribute(name = "displayExplanation")
294     public boolean isDisplayExplanation() {
295         return displayExplanation;
296     }
297 
298     /**
299      * Sets whether to display the Explanation InputField on this dialog
300      *
301      * @param displayExplanation true if explanation control is to be displayed, false if not
302      */
303     public void setDisplayExplanation(boolean displayExplanation) {
304         this.displayExplanation = displayExplanation;
305     }
306 
307     /**
308      * Gets the choices provided for user response.
309      *
310      * <p>
311      * A List of KeyValue pairs for each of the choices provided on this dialog.
312      * </p>
313      *
314      * @return the List of response actions to provide the user
315      */
316     @BeanTagAttribute(name = "availableResponses", type = BeanTagAttribute.AttributeType.LISTBEAN)
317     public List<KeyValue> getAvailableResponses() {
318         return availableResponses;
319     }
320 
321     /**
322      * Sets the list of user responses to provide on this dialog
323      *
324      * @param availableResponses a List of KeyValue pairs representing the user response choices
325      */
326     public void setAvailableResponses(List<KeyValue> availableResponses) {
327         this.availableResponses = availableResponses;
328     }
329 
330     /**
331      * Retrieves the InputField containing the choices displayed in this dialog
332      *
333      * <p>
334      * By default, this InputField is configured to be a HorizontalCheckboxControl.
335      * Styling is then used to make the checkboxes appear to be buttons.
336      * The values of the availableResponses List are used as labels for the "buttons".
337      * </p>
338      *
339      * @return InputField component within this dialog
340      */
341     @BeanTagAttribute(name = "responseInputField", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
342     public InputField getResponseInputField() {
343         return responseInputField;
344     }
345 
346     /**
347      * Sets the type of InputField used to display the user choices in this dialog
348      *
349      * @param responseInputField a component used to display the response choices
350      */
351     public void setResponseInputField(InputField responseInputField) {
352         this.responseInputField = responseInputField;
353     }
354 
355     /**
356      * Determines the positioning order of the choices displayed on this dialog
357      *
358      * <p>
359      * Some page designers like the positive choice on the left and the negative choice on the right.
360      * Others, prefer just the opposite. This allows the order to easily be switched.
361      * </p>
362      *
363      * @return true if choices left to right
364      *         false if choices right to left
365      */
366     @BeanTagAttribute(name = "reverseButtonOrder")
367     public boolean isReverseButtonOrder() {
368         return reverseButtonOrder;
369     }
370 
371     /**
372      * Sets the display order of the choices displayed on this dialog
373      *
374      * <p>
375      * By default, the choices are displayed left to right
376      * </p>
377      *
378      * @param reverseButtonOrder true if buttons displayed left to right, false if right to left
379      */
380     public void setReverseButtonOrder(boolean reverseButtonOrder) {
381         this.reverseButtonOrder = reverseButtonOrder;
382     }
383 
384     /**
385      * Script that will be invoked when the response event is thrown
386      *
387      * <p>
388      * The dialog group will throw a custom event type 'dialogresponse.uif' when a change occurs for the response
389      * input field (for example one of the response options is selected). Script given here will bind to that
390      * event as a handler
391      * </p>
392      *
393      * @return javascript that will execute for the response event
394      */
395     @BeanTagAttribute(name = "onDialogResponseScript")
396     public String getOnDialogResponseScript() {
397         return onDialogResponseScript;
398     }
399 
400     /**
401      * Setter for the 'dialogresponse.uif' event handler code
402      *
403      * @param onDialogResponseScript
404      */
405     public void setOnDialogResponseScript(String onDialogResponseScript) {
406         this.onDialogResponseScript = onDialogResponseScript;
407     }
408 
409     /**
410      * Script that will get invoked when the dialog group is shown
411      *
412      * <p>
413      * Initially a dialog group will either be hidden in the DOM or not present at all (if retrieved via Ajax).
414      * When the dialog is triggered and shown, the 'showdialog.uif' event will be thrown and this script will
415      * be executed
416      * </p>
417      *
418      * @return JavaScript code to execute when the dialog is shown
419      */
420     @BeanTagAttribute(name = "onShowDialogScript")
421     public String getOnShowDialogScript() {
422         return onShowDialogScript;
423     }
424 
425     /**
426      * Setter for the 'showdialog.uif' event handler code
427      *
428      * @param onShowDialogScript
429      */
430     public void setOnShowDialogScript(String onShowDialogScript) {
431         this.onShowDialogScript = onShowDialogScript;
432     }
433 }