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 }