001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.field;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.config.property.ConfigurationService;
020import org.kuali.rice.krad.service.KRADServiceLocator;
021import org.kuali.rice.krad.uif.container.ContainerBase;
022import org.kuali.rice.krad.uif.container.PageGroup;
023import org.kuali.rice.krad.uif.view.View;
024import org.kuali.rice.krad.uif.component.Component;
025import org.kuali.rice.krad.util.ErrorMessage;
026import org.kuali.rice.krad.util.GlobalVariables;
027import org.kuali.rice.krad.util.MessageMap;
028import org.springframework.util.AutoPopulatingList;
029
030import java.text.MessageFormat;
031import java.util.ArrayList;
032import java.util.Arrays;
033import java.util.LinkedHashSet;
034import java.util.List;
035
036/**
037 * Field that displays error, warning, and info messages for the keys that are
038 * matched. By default, an ErrorsField will match on id and bindingPath (if this
039 * ErrorsField is for an InputField), but can be set to match on
040 * additionalKeys and nested components keys (of the its parentComponent).
041 *
042 * In addition, there are a variety of options which can be toggled to effect
043 * the display of these messages during both client and server side validation
044 * display. See documentation on each get method for more details on the effect
045 * of each option.
046 *
047 * @author Kuali Rice Team (rice.collab@kuali.org)
048 */
049public class ErrorsField extends FieldBase {
050    private static final long serialVersionUID = 780940788435330077L;
051
052    private List<String> additionalKeysToMatch;
053
054    private boolean fireGrowlsForMessages;
055    private String growlScript = "";
056
057    // Title variables
058    private String errorTitle;
059    private String warningTitle;
060    private String infoTitle;
061
062    private boolean displayErrorTitle;
063    private boolean displayWarningTitle;
064    private boolean displayInfoTitle;
065
066    // Field variables
067    private boolean highlightOnError;
068    private boolean displayFieldErrorIcon;
069
070    // Message construction variables
071    private boolean displayFieldLabelWithMessages;
072    private boolean combineMessages;
073
074    // Message display flags
075    private boolean displayNestedMessages;
076    private boolean allowMessageRepeat;
077
078    private boolean displayMessages;
079    private boolean displayErrorMessages;
080    private boolean displayInfoMessages;
081    private boolean displayWarningMessages;
082    private boolean displayCounts;
083    private boolean alternateContainer;
084
085    // Error messages
086    private List<String> errors;
087    private List<String> warnings;
088    private List<String> infos;
089
090    // Counts
091    private int errorCount;
092    private int warningCount;
093    private int infoCount;
094
095    // not used
096    private boolean displayLockMessages;
097
098    public ErrorsField() {
099        super();
100        alternateContainer = false;
101    }
102
103    /**
104     * PerformFinalize will generate the messages and counts used by the
105     * errorsField based on the keys that were matched from the MessageMap for
106     * this ErrorsField. It will also set up nestedComponents of its
107     * parentComponent correctly based on the flags that were chosen for this
108     * ErrorsField.
109     *
110     * @see org.kuali.rice.krad.uif.field.FieldBase#performFinalize(org.kuali.rice.krad.uif.view.View,
111     *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
112     */
113    @Override
114    public void performFinalize(View view, Object model, Component parent) {
115        super.performFinalize(view, model, parent);
116
117        generateMessages(true, view, model, parent);
118    }
119
120    public void generateMessages(boolean reset, View view, Object model, Component parent) {
121        if (reset) {
122            errors = new ArrayList<String>();
123            warnings = new ArrayList<String>();
124            infos = new ArrayList<String>();
125            errorCount = 0;
126            warningCount = 0;
127            infoCount = 0;
128        }
129
130        List<String> masterKeyList = getKeys(parent);
131        MessageMap messageMap = GlobalVariables.getMessageMap();
132
133        // TODO: need constants
134        if (!displayFieldLabelWithMessages) {
135            this.addStyleClass("noLabels");
136        }
137        if (!highlightOnError) {
138            this.addStyleClass("noHighlight");
139        }
140        if (displayFieldErrorIcon) {
141            this.addStyleClass("addFieldIcon");
142        }
143
144        if (displayMessages) {
145            if (displayNestedMessages) {
146                this.addNestedKeys(masterKeyList, parent);
147            }
148
149            for (String key : masterKeyList) {
150                if (displayErrorMessages) {
151                    errors.addAll(getMessages(view, key, messageMap.getErrorMessagesForProperty(key, true)));
152                }
153                if (displayWarningMessages) {
154                    warnings.addAll(getMessages(view, key, messageMap.getWarningMessagesForProperty(key, true)));
155                }
156                if (displayInfoMessages) {
157                    infos.addAll(getMessages(view, key, messageMap.getInfoMessagesForProperty(key, true)));
158                }
159            }
160        } else if (displayFieldErrorIcon) {
161            // Checks to see if any errors exist for this field, if they do set
162            // errorCount as positive
163            // so the jsp will call the corresponding js to show the icon
164            // messages do not need to be generated because they are not being shown
165            for (String key : masterKeyList) {
166                if (!messageMap.getErrorMessagesForProperty(key, true).isEmpty()) {
167                    errorCount = 1;
168                    break;
169                }
170            }
171        }
172
173        //Check for errors that are not matched on the page(only applies when parent is page)
174        if (parent instanceof PageGroup) {
175            if (errorCount < messageMap.getErrorCount()) {
176                List<String> diff = messageMap.getPropertiesWithErrors();
177                diff.removeAll(masterKeyList);
178                for (String key : diff) {
179                    errors.addAll(getMessages(view, key, messageMap.getErrorMessagesForProperty(key, true)));
180                }
181
182            }
183            if (warningCount < messageMap.getWarningCount()) {
184                List<String> diff = messageMap.getPropertiesWithWarnings();
185                diff.removeAll(masterKeyList);
186                for (String key : diff) {
187                    warnings.addAll(getMessages(view, key, messageMap.getWarningMessagesForProperty(key, true)));
188                }
189            }
190            if (infoCount < messageMap.getInfoCount()) {
191                List<String> diff = messageMap.getPropertiesWithInfo();
192                diff.removeAll(masterKeyList);
193                for (String key : diff) {
194                    infos.addAll(getMessages(view, key, messageMap.getInfoMessagesForProperty(key, true)));
195                }
196            }
197
198            // TODO: need constant
199            this.setId("errorsFieldForPage");
200        }
201
202        if (fireGrowlsForMessages) {
203            //set up growl script
204            growlScript = getGrowlScript(view);
205        }
206
207        //Remove any textual duplicates that may have snuck in, by converting to set and back to list
208        errors = new ArrayList<String>(new LinkedHashSet<String>(errors));
209        warnings = new ArrayList<String>(new LinkedHashSet<String>(warnings));
210        infos = new ArrayList<String>(new LinkedHashSet<String>(infos));
211
212        errorCount = errors.size();
213        warningCount = warnings.size();
214        infoCount = infos.size();
215
216        // dont display anything if there are no messages
217        if (errorCount + warningCount + infoCount == 0 || !displayMessages) {
218            // TODO: CSS constant
219            this.setStyle("display: none;");
220        } else {
221            this.setStyle("display: visible");
222        }
223    }
224
225    /**
226     * Gets all the messages from the list of lists passed in (which are
227     * lists of ErrorMessages associated to the key) and uses the configuration
228     * service to get the message String associated. This will also combine
229     * error messages per a field if that option is turned on. If
230     * displayFieldLabelWithMessages is turned on, it will also find the label
231     * by key passed in.
232     *
233     * @param view
234     * @param key
235     * @param lists
236     * @return
237     */
238    private List<String> getMessages(View view, String key, List<AutoPopulatingList<ErrorMessage>> lists) {
239        List<String> result = new ArrayList<String>();
240        for (List<ErrorMessage> errorList : lists) {
241            if (errorList != null && StringUtils.isNotBlank(key)) {
242                ConfigurationService configService = KRADServiceLocator.getKualiConfigurationService();
243                String comboMessage = "";
244                String label = "";
245
246                for (ErrorMessage e : errorList) {
247                    String message = configService.getPropertyValueAsString(e.getErrorKey());
248                    if (message == null) {
249                        message = "Intended message with key: " + e.getErrorKey() + " not found.";
250                    }
251                    if (e.getMessageParameters() != null) {
252                        message = message.replace("'", "''");
253                        message = MessageFormat.format(message, (Object[]) e.getMessageParameters());
254                    }
255                    if (displayFieldLabelWithMessages) {
256                        InputField field = (InputField) view.getViewIndex().getDataFieldByPath(key);
257                        if (field != null && field.getLabel() != null) {
258                            label = field.getLabel();
259                        }
260                    }
261
262                    // adding them to combo string instead of the list
263                    if (combineMessages) {
264                        if (comboMessage.isEmpty()) {
265                            comboMessage = message;
266                        } else {
267                            comboMessage = comboMessage + ",  " + message;
268                        }
269                    } else {
270                        // add it directly to the list - non combined messages
271                        if (StringUtils.isNotEmpty(label)) {
272                            result.add(label + " - " + message);
273                        } else {
274                            result.add(message);
275                        }
276
277                    }
278                }
279                // add the single combo string to the returned list
280                // combineMessages will also be checked in the template to
281                // further
282                // combine them
283                if (StringUtils.isNotEmpty(comboMessage)) {
284                    if (StringUtils.isNotEmpty(label)) {
285                        result.add(label + " - " + comboMessage);
286                    } else {
287                        result.add(comboMessage);
288                    }
289                }
290            }
291        }
292
293        return result;
294    }
295
296    /**
297     * Gets all the keys associated to this ErrorsField. This includes the id of
298     * the parent component, additional keys to match, and the bindingPath if
299     * this is an ErrorsField for an InputField. These are the keys that are
300     * used to match errors with their component and display them as part of its
301     * ErrorsField.
302     *
303     * @return
304     */
305    protected List<String> getKeys(Component parent) {
306        List<String> keyList = new ArrayList<String>();
307        if (additionalKeysToMatch != null) {
308            keyList.addAll(additionalKeysToMatch);
309        }
310        if (StringUtils.isNotBlank(parent.getId())) {
311            keyList.add(parent.getId());
312        }
313        if (parent instanceof InputField) {
314            if (((InputField) parent).getBindingInfo() != null && StringUtils.isNotEmpty(
315                    ((InputField) parent).getBindingInfo().getBindingPath())) {
316                keyList.add(((InputField) parent).getBindingInfo().getBindingPath());
317            }
318        }
319        // Will there be additional components to check beyond InputField?
320
321        return keyList;
322    }
323
324    /**
325     * Adds all the nestedKeys of this component by calling getKeys on each of
326     * its nestedComponents' ErrorsFields and adding them to the list. If
327     * allowMessageRepeat is false, it will also turn off error display for its
328     * parent's nestedComponents' ErrorsFields.
329     *
330     * @param keyList
331     * @param component
332     */
333    private void addNestedKeys(List<String> keyList, Component component) {
334        for (Component c : component.getComponentsForLifecycle()) {
335            ErrorsField ef = null;
336            if (c instanceof InputField) {
337                ef = ((InputField) c).getErrorsField();
338            } else if (c instanceof ContainerBase) {
339                ef = ((ContainerBase) c).getErrorsField();
340            }
341            if (ef != null) {
342                if (!allowMessageRepeat) {
343                    ef.setDisplayMessages(false);
344                }
345                keyList.addAll(ef.getKeys(c));
346                addNestedKeys(keyList, c);
347            }
348        }
349    }
350
351    /**
352     * ErrorTitle is the title that will be shown before any error
353     * messages/error counts are displayed
354     *
355     * @return
356     */
357    public String getErrorTitle() {
358        return this.errorTitle;
359    }
360
361    public void setErrorTitle(String errorTitle) {
362        this.errorTitle = errorTitle;
363    }
364
365    /**
366     * WarningTitle is the title that will be shown before any warning
367     * messages/warning counts are displayed
368     *
369     * @return
370     */
371    public String getWarningTitle() {
372        return this.warningTitle;
373    }
374
375    public void setWarningTitle(String warningTitle) {
376        this.warningTitle = warningTitle;
377    }
378
379    /**
380     * InfoTitle is the title that will be shown before any info messages/info
381     * counts are displayed
382     *
383     * @return
384     */
385    public String getInfoTitle() {
386        return this.infoTitle;
387    }
388
389    public void setInfoTitle(String infoTitle) {
390        this.infoTitle = infoTitle;
391    }
392
393    /**
394     * If displayErrorMessages is true, error messages will be displayed,
395     * otherwise they will not. Unlike many of the options contained on
396     * ErrorsField, this will not effect client side validations; ie this will
397     * not turn off errorMessage display for client side validation, as it may
398     * prevent a user from completing a form. To turn off client side validation
399     * AND its messaging use the applyClientSide flag on the Constraint itself.
400     *
401     * TODO this may be changed to: if this is set on a field it will attempt
402     * show client side validation errors in the closest parent container error
403     * container
404     *
405     * @return
406     */
407    public boolean isDisplayErrorMessages() {
408        return this.displayErrorMessages;
409    }
410
411    public void setDisplayErrorMessages(boolean displayErrorMessages) {
412        this.displayErrorMessages = displayErrorMessages;
413    }
414
415    /**
416     * If displayInfoMessages is true, info messages will be displayed,
417     * otherwise they will not. Client side validation has no concept of warning
418     * or info messages, so this will not effect client side functionality.
419     *
420     * @return
421     */
422    public boolean isDisplayInfoMessages() {
423        return this.displayInfoMessages;
424    }
425
426    public void setDisplayInfoMessages(boolean displayInfoMessages) {
427        this.displayInfoMessages = displayInfoMessages;
428    }
429
430    public boolean isDisplayLockMessages() {
431        return this.displayLockMessages;
432    }
433
434    /**
435     * This has no use - needs to be removed(?)
436     *
437     * @param displayLockMessages
438     */
439    public void setDisplayLockMessages(boolean displayLockMessages) {
440        this.displayLockMessages = displayLockMessages;
441    }
442
443    /**
444     * If displayWarningMessages is true, warning messages will be displayed,
445     * otherwise they will not. Client side validation has no concept of warning
446     * or info messages, so this will not effect client side functionality.
447     *
448     * @return
449     */
450    public boolean isDisplayWarningMessages() {
451        return this.displayWarningMessages;
452    }
453
454    public void setDisplayWarningMessages(boolean displayWarningMessages) {
455        this.displayWarningMessages = displayWarningMessages;
456    }
457
458    /**
459     * AdditionalKeysToMatch is an additional list of keys outside of the
460     * default keys that will be matched when messages are returned after a form
461     * is submitted. These keys are only used for displaying messages generated
462     * by the server and have no effect on client side validation error display.
463     *
464     * @return the additionalKeysToMatch
465     */
466    public List<String> getAdditionalKeysToMatch() {
467        return this.additionalKeysToMatch;
468    }
469
470    /**
471     * Convenience setter for additional keys to match that takes a string argument and
472     * splits on comma to build the list
473     *
474     * @param additionalKeysToMatch String to parse
475     */
476    public void setAdditionalKeysToMatch(String additionalKeysToMatch) {
477        if (StringUtils.isNotBlank(additionalKeysToMatch)) {
478            this.additionalKeysToMatch = Arrays.asList(StringUtils.split(additionalKeysToMatch, ","));
479        }
480    }
481
482    /**
483     * @param additionalKeysToMatch the additionalKeysToMatch to set
484     */
485    public void setAdditionalKeysToMatch(List<String> additionalKeysToMatch) {
486        this.additionalKeysToMatch = additionalKeysToMatch;
487    }
488
489    /**
490     * If true, the errorTitle set on this ErrorsField will be displayed along
491     * with the error messages. Otherwise, the title will not be displayed.
492     *
493     * @return the displayErrorTitle
494     */
495    public boolean isDisplayErrorTitle() {
496        return this.displayErrorTitle;
497    }
498
499    /**
500     * @param displayErrorTitle the displayErrorTitle to set
501     */
502    public void setDisplayErrorTitle(boolean displayErrorTitle) {
503        this.displayErrorTitle = displayErrorTitle;
504    }
505
506    /**
507     * If true, the warningTitle set on this ErrorsField will be displayed along
508     * with the warning messages. Otherwise, the title will not be displayed.
509     *
510     * @return the displayWarningTitle
511     */
512    public boolean isDisplayWarningTitle() {
513        return this.displayWarningTitle;
514    }
515
516    /**
517     * @param displayWarningTitle the displayWarningTitle to set
518     */
519    public void setDisplayWarningTitle(boolean displayWarningTitle) {
520        this.displayWarningTitle = displayWarningTitle;
521    }
522
523    /**
524     * If true, the infoTitle set on this ErrorsField will be displayed along
525     * with the info messages. Otherwise, the title will not be displayed.
526     *
527     * @return the displayInfoTitle
528     */
529    public boolean isDisplayInfoTitle() {
530        return this.displayInfoTitle;
531    }
532
533    /**
534     * @param displayInfoTitle the displayInfoTitle to set
535     */
536    public void setDisplayInfoTitle(boolean displayInfoTitle) {
537        this.displayInfoTitle = displayInfoTitle;
538    }
539
540    /**
541     * If true, the error messages will display the an InputField's title
542     * alongside the error, warning, and info messages related to it. This
543     * setting has no effect on messages which do not relate directly to a
544     * single InputField.
545     *
546     * @return the displayFieldLabelWithMessages
547     */
548    public boolean isDisplayFieldLabelWithMessages() {
549        return this.displayFieldLabelWithMessages;
550    }
551
552    /**
553     * @param displayFieldLabelWithMessages the displayFieldLabelWithMessages to set
554     */
555    public void setDisplayFieldLabelWithMessages(boolean displayFieldLabelWithMessages) {
556        this.displayFieldLabelWithMessages = displayFieldLabelWithMessages;
557    }
558
559    /**
560     * If true, error, warning, and info messages will be displayed (provided
561     * they are also set to display). Otherwise, no messages for this
562     * ErrorsField container will be displayed (including ones set to display).
563     * This is a global display on/off switch for all messages.
564     *
565     * @return the displayMessages
566     */
567    public boolean isDisplayMessages() {
568        return this.displayMessages;
569    }
570
571    /**
572     * @param displayMessages the displayMessages to set
573     */
574    public void setDisplayMessages(boolean displayMessages) {
575        this.displayMessages = displayMessages;
576    }
577
578    /**
579     * If true, this ErrorsField will show messages related to the nested
580     * components of its parent component, and not just those related only to
581     * its parent component. Otherwise, it will be up to the individual
582     * components to display their messages, if any, in their ErrorsField.
583     *
584     * @return the displayNestedMessages
585     */
586    public boolean isDisplayNestedMessages() {
587        return this.displayNestedMessages;
588    }
589
590    /**
591     * @param displayNestedMessages the displayNestedMessages to set
592     */
593    public void setDisplayNestedMessages(boolean displayNestedMessages) {
594        this.displayNestedMessages = displayNestedMessages;
595    }
596
597    /**
598     * Combines the messages for a single key into one concatenated message per
599     * key being matched, seperated by a comma
600     *
601     * @return the combineMessages
602     */
603    public boolean isCombineMessages() {
604        return this.combineMessages;
605    }
606
607    /**
608     * @param combineMessages the combineMessages to set
609     */
610    public void setCombineMessages(boolean combineMessages) {
611        this.combineMessages = combineMessages;
612    }
613
614    /**
615     * If true, when this is set on an ErrorsField whose parentComponent has
616     * nested Containers or AttributeFields, it will allow those fields to also
617     * show their ErrorsField messages. Otherwise, it will turn off the the
618     * display of those messages. This can be used to avoid repeating
619     * information to the user per field, if errors are already being displayed
620     * at the parent's level. This flag has no effect if displayNestedMessages
621     * is false on this ErrorsField.
622     *
623     * @return the allowMessageRepeat
624     */
625    public boolean isAllowMessageRepeat() {
626        return this.allowMessageRepeat;
627    }
628
629    /**
630     * @param allowMessageRepeat the allowMessageRepeat to set
631     */
632    public void setAllowMessageRepeat(boolean allowMessageRepeat) {
633        this.allowMessageRepeat = allowMessageRepeat;
634    }
635
636    /**
637     * displayCounts is true if the counts of errors, warning, and info messages
638     * within this ErrorsField should be displayed (includes count of nested
639     * messages if displayNestedMessages is true).
640     *
641     * @return
642     */
643    public boolean isDisplayCounts() {
644        return this.displayCounts;
645    }
646
647    /**
648     * @param displayCounts the displayCounts to set
649     */
650    public void setDisplayCounts(boolean displayCounts) {
651        this.displayCounts = displayCounts;
652    }
653
654    /**
655     * The list of error messages found for the keys that were matched on this
656     * ErrorsField This is generated and cannot be set
657     *
658     * @return the errors
659     */
660    public List<String> getErrors() {
661        return this.errors;
662    }
663
664    /**
665     * The list of warning messages found for the keys that were matched on this
666     * ErrorsField This is generated and cannot be set
667     *
668     * @return the warnings
669     */
670    public List<String> getWarnings() {
671        return this.warnings;
672    }
673
674    /**
675     * The list of info messages found for the keys that were matched on this
676     * ErrorsField This is generated and cannot be set
677     *
678     * @return the infos
679     */
680    public List<String> getInfos() {
681        return this.infos;
682    }
683
684    /**
685     * The count of error messages found for the keys that were matched on this
686     * ErrorsField This is generated and cannot be set
687     *
688     * @return the errorCount
689     */
690    public int getErrorCount() {
691        return this.errorCount;
692    }
693
694    /**
695     * The count of warning messages found for the keys that were matched on
696     * this ErrorsField This is generated and cannot be set
697     *
698     * @return the warningCount
699     */
700    public int getWarningCount() {
701        return this.warningCount;
702    }
703
704    /**
705     * The count of info messages found for the keys that were matched on this
706     * ErrorsField This is generated and cannot be set
707     *
708     * @return the infoCount
709     */
710    public int getInfoCount() {
711        return this.infoCount;
712    }
713
714    /**
715     * If this is true, the display of messages is being handled by another
716     * container. The ErrorsField html generated by the jsp will still be used,
717     * but it will be placed in different location within the page than the
718     * default to accommodate an alternate layout. This flag is used by
719     * BoxLayoutManager.
720     *
721     * This flag only applies to ErrorsFields whose parentComponents are
722     * AttributeFields.
723     *
724     * @return the alternateContainer
725     */
726    public boolean isAlternateContainer() {
727        return this.alternateContainer;
728    }
729
730    /**
731     * @param alternateContainer the alternateContainer to set
732     */
733    public void setAlternateContainer(boolean alternateContainer) {
734        this.alternateContainer = alternateContainer;
735    }
736
737    /**
738     * If true, displays an icon next to each field that has an error (default
739     * KNS look). Otherwise, this icon will not be displayed. Note that any icon
740     * set through css for the message containers will still appear and this
741     * only relates to the icon directly to the right of an input field.
742     *
743     * This flag should only be set on InputField ErrorsFields.
744     *
745     * @return the displayFieldErrorIcon
746     */
747    public boolean isDisplayFieldErrorIcon() {
748        return this.displayFieldErrorIcon;
749    }
750
751    /**
752     * @param displayFieldErrorIcon the displayFieldErrorIcon to set
753     */
754    public void setDisplayFieldErrorIcon(boolean displayFieldErrorIcon) {
755        this.displayFieldErrorIcon = displayFieldErrorIcon;
756    }
757
758    /**
759     * If true, highlights the parentComponent's container when it has an
760     * error/warning/info. Otherwise, this highlighting will not be displayed.
761     * Note that the css can be changed per a type of highlighting, if showing a
762     * different color or no color per type of message is desired.
763     *
764     * @return the highlightOnError
765     */
766    public void setHighlightOnError(boolean highlightOnError) {
767        this.highlightOnError = highlightOnError;
768    }
769
770    /**
771     * @return the highlightOnError
772     */
773    public boolean isHighlightOnError() {
774        return highlightOnError;
775    }
776
777    private String getGrowlScript(View view) {
778        // growls are setup here because they are relevant to the current page, but their
779        // settings are global to the view
780        String growlScript = "";
781        if (view.isGrowlMessagingEnabled()) {
782            ConfigurationService configService = KRADServiceLocator.getKualiConfigurationService();
783            MessageMap messageMap = GlobalVariables.getMessageMap();
784            if (messageMap.hasErrors()) {
785                String message = configService.getPropertyValueAsString("growl.hasErrors");
786                if (StringUtils.isNotBlank(message)) {
787                    growlScript =
788                            growlScript + "showGrowl('" + message + "', '" + configService.getPropertyValueAsString(
789                                    "general.error") + "', 'errorGrowl');";
790                }
791            }
792
793            if (messageMap.hasWarnings()) {
794                String message = configService.getPropertyValueAsString("growl.hasWarnings");
795                if (StringUtils.isNotBlank(message)) {
796                    growlScript =
797                            growlScript + "showGrowl('" + message + "', '" + configService.getPropertyValueAsString(
798                                    "general.warning") + "', 'warningGrowl');";
799                }
800            }
801
802            if (messageMap.hasInfo()) {
803                List<String> properties = messageMap.getPropertiesWithInfo();
804                String message = "";
805                for (String property : properties) {
806                    List<AutoPopulatingList<ErrorMessage>> lists = messageMap.getInfoMessagesForProperty(property,
807                            true);
808                    for (List<ErrorMessage> errorList : lists) {
809                        if (errorList != null) {
810                            for (ErrorMessage e : errorList) {
811                                if (StringUtils.isBlank(message)) {
812                                    message = configService.getPropertyValueAsString(e.getErrorKey());
813                                } else {
814                                    message = message + "<br/>" + configService.getPropertyValueAsString(
815                                            e.getErrorKey());
816                                }
817                                if (e.getMessageParameters() != null) {
818                                    message = message.replace("'", "''");
819                                    message = MessageFormat.format(message, (Object[]) e.getMessageParameters());
820                                }
821                            }
822                        }
823                    }
824                }
825
826                if (StringUtils.isNotBlank(message)) {
827                    growlScript =
828                            growlScript + "showGrowl('" + message + "', '" + configService.getPropertyValueAsString(
829                                    "general.info") + "', 'infoGrowl');";
830                }
831            }
832        }
833        return growlScript;
834    }
835
836    public boolean isFireGrowlsForMessages() {
837        return fireGrowlsForMessages;
838    }
839
840    public void setFireGrowlsForMessages(boolean fireGrowlsForMessages) {
841        this.fireGrowlsForMessages = fireGrowlsForMessages;
842    }
843
844    public String getGrowlScript() {
845        return growlScript;
846    }
847}