View Javadoc

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