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.element;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.container.CollectionGroup;
25  import org.kuali.rice.krad.uif.container.Container;
26  import org.kuali.rice.krad.uif.container.LightTable;
27  import org.kuali.rice.krad.uif.container.PageGroup;
28  import org.kuali.rice.krad.uif.field.FieldGroup;
29  import org.kuali.rice.krad.uif.field.InputField;
30  import org.kuali.rice.krad.uif.layout.StackedLayoutManager;
31  import org.kuali.rice.krad.uif.layout.TableLayoutManager;
32  import org.kuali.rice.krad.uif.util.ScriptUtils;
33  import org.kuali.rice.krad.uif.view.View;
34  import org.kuali.rice.krad.util.GlobalVariables;
35  
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  
41  /**
42   * ValidationMessages for logic and options specific to groups
43   */
44  @BeanTag(name = "groupValidationMessages-bean", parent = "Uif-GroupValidationMessages")
45  public class GroupValidationMessages extends ValidationMessages {
46      private static final long serialVersionUID = -5389990220206079052L;
47  
48      private boolean displayFieldLabelWithMessages;
49      private boolean collapseAdditionalFieldLinkMessages;
50      private boolean displayHeaderMessageSummary;
51  
52      private static final String SECTION_TOKEN = "s$";
53      private static final String FIELDGROUP_TOKEN = "f$";
54      private static final String TABLE_COLLECTION_TOKEN = "c$";
55  
56      @Override
57      /**
58       * Calls super and adds dataAttributes that are appropriate for group level validationMessages
59       * data.  This data is used by the validation framework clientside.
60       *
61       * Some special handling at this level includes retrieving the groups and fields generated by
62       * different collection layouts and handling page and fieldGroup scenarios slightly differently
63       * due to the nature of how they are built out in the js.
64       *
65       * @see krad.validate.js
66       */
67      public void generateMessages(boolean reset, View view, Object model, Component parent) {
68          super.generateMessages(reset, view, model, parent);
69  
70          Object parentContainer = parent.getContext().get(UifConstants.ContextVariableNames.PARENT);
71  
72          List<? extends Component> items = ((Container) parent).getItems();
73          boolean skipSections = false;
74          boolean isTableCollection = false;
75  
76          // Handle the special CollectionGroup case by getting the StackedGroups and DataFields generated by them
77          if (parent instanceof CollectionGroup && ((CollectionGroup) parent).getLayoutManager() instanceof StackedLayoutManager) {
78              items = ((StackedLayoutManager) ((CollectionGroup) parent).getLayoutManager()).getStackedGroups();
79          } else if ((parent instanceof CollectionGroup
80                  && ((CollectionGroup) parent).getLayoutManager() instanceof TableLayoutManager)
81                  || parent instanceof LightTable) {
82              // order is not needed  so null items
83              items = null;
84              skipSections = true;
85              isTableCollection = true;
86          }
87  
88          List<String> sectionIds = new ArrayList<String>();
89          List<String> fieldOrder = new ArrayList<String>();
90          collectIdsFromItems(items, sectionIds, fieldOrder, skipSections);
91  
92          boolean pageLevel = false;
93          boolean forceShow = false;
94          boolean showPageSummaryHeader = true;
95          if (parent instanceof PageGroup) {
96              pageLevel = true;
97              forceShow = true;
98              parent.addDataAttribute(UifConstants.DataAttributes.SERVER_MESSAGES, Boolean.toString(
99                      GlobalVariables.getMessageMap().hasMessages()));
100             if (this instanceof PageValidationMessages) {
101                 showPageSummaryHeader = ((PageValidationMessages) this).isShowPageSummaryHeader();
102             }
103         } else if (parentContainer instanceof FieldGroup) {
104             String role = ((FieldGroup) parentContainer).getDataAttributes().get(UifConstants.DataAttributes.ROLE);
105             if (StringUtils.isNotBlank(role) && role.equals("detailsFieldGroup")){
106                 forceShow = false;
107             }
108             else{
109                 //note this means container of the parent is a FieldGroup
110                 forceShow = true;
111             }
112         }
113 
114         boolean hasMessages = false;
115         if (!this.getErrors().isEmpty() || !this.getWarnings().isEmpty() || !this.getInfos().isEmpty()) {
116             hasMessages = true;
117         }
118 
119         HashMap<String, Object> validationMessagesDataAttributes = new HashMap<String, Object>();
120 
121         Map<String, String> dataDefaults =
122                 (Map<String, String>) (KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryObject(
123                         "Uif-GroupValidationMessages-DataDefaults"));
124 
125         //add necessary data attributes to map
126         //display related
127         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
128                 UifConstants.DataAttributes.SUMMARIZE, true);
129         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
130                 UifConstants.DataAttributes.DISPLAY_MESSAGES, this.isDisplayMessages());
131         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
132                 UifConstants.DataAttributes.COLLAPSE_FIELD_MESSAGES, collapseAdditionalFieldLinkMessages);
133         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
134                 UifConstants.DataAttributes.SHOW_PAGE_SUMMARY_HEADER, showPageSummaryHeader);
135         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
136                 UifConstants.DataAttributes.DISPLAY_LABEL, displayFieldLabelWithMessages);
137         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
138                 UifConstants.DataAttributes.DISPLAY_HEADER_SUMMARY, displayHeaderMessageSummary);
139         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
140             UifConstants.DataAttributes.IS_TABLE_COLLECTION, isTableCollection);
141 
142         //options
143         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
144                 UifConstants.DataAttributes.HAS_OWN_MESSAGES, hasMessages);
145         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
146                 UifConstants.DataAttributes.PAGE_LEVEL, pageLevel);
147         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
148                 UifConstants.DataAttributes.FORCE_SHOW, forceShow);
149 
150         //order related
151         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
152                 UifConstants.DataAttributes.SECTIONS, sectionIds);
153         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
154                 UifConstants.DataAttributes.ORDER, fieldOrder);
155 
156         //server messages
157         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
158                 UifConstants.DataAttributes.SERVER_ERRORS, ScriptUtils.escapeHtml(this.getErrors()));
159         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
160                 UifConstants.DataAttributes.SERVER_WARNINGS, ScriptUtils.escapeHtml(this.getWarnings()));
161         this.addValidationDataSettingsValue(validationMessagesDataAttributes, dataDefaults,
162                 UifConstants.DataAttributes.SERVER_INFO, ScriptUtils.escapeHtml(this.getInfos()));
163 
164         if (!validationMessagesDataAttributes.isEmpty()){
165             parent.addDataAttribute(UifConstants.DataAttributes.VALIDATION_MESSAGES, ScriptUtils.translateValue(
166                     validationMessagesDataAttributes));
167         }
168     }
169 
170     /**
171      * Collects all the ids from the items passed into this method.
172      *
173      * <p>Puts the ids of items determined to be sections
174      * into the sectionIds list, and orders all items by the order they appear on the page in the order list with
175      * special identifiers
176      * to determine the type of item they are (used by the client js).  When skipSections is true do not
177      * include sectionIds found in the lists.</p>
178      *
179      * @param items items of the group
180      * @param sectionIds list to put section ids into
181      * @param order list to put order of ids into (both fields and sections)
182      * @param skipSections skip adding sections
183      */
184     protected void collectIdsFromItems(List<? extends Component> items, List<String> sectionIds, List<String> order,
185             boolean skipSections) {
186 
187         if (items != null) {
188             for (Component component : items) {
189                 String id = component.getId().replace("@id@", "");
190                 if (component instanceof Container || component instanceof FieldGroup) {
191                     if (component instanceof FieldGroup) {
192                         if (!skipSections &&
193                                 ((FieldGroup) component).getFieldLabel().isRender() &&
194                                 !((FieldGroup) component).getFieldLabel().isHidden() &&
195                                 (StringUtils.isNotEmpty(((FieldGroup) component).getLabel()) || StringUtils.isNotEmpty(
196                                         ((FieldGroup) component).getFieldLabel().getLabelText()))) {
197                             sectionIds.add(id);
198                             order.add(FIELDGROUP_TOKEN + id);
199                             continue;
200                         } else {
201                             component = ((FieldGroup) component).getGroup();
202                             if (component == null) {
203                                 continue;
204                             }
205                         }
206                     }
207 
208                     //If any kind of header text is showing consider this group a section
209                     if (!skipSections
210                             && ((Container) component).getHeader() != null
211                             && ((Container) component).getHeader().isRender()
212                             && (StringUtils.isNotBlank(((Container) component).getHeader().getHeaderText()) || StringUtils
213                             .isNotBlank(component.getTitle()))) {
214                         sectionIds.add(id);
215                         order.add(SECTION_TOKEN + id);
216                     } else if ((component instanceof CollectionGroup
217                                     && ((CollectionGroup) component).getLayoutManager() instanceof TableLayoutManager)
218                                     || component instanceof LightTable){
219                         order.add(TABLE_COLLECTION_TOKEN + id);
220                     } else {
221                         collectIdsFromItems(((Container) component).getItems(), sectionIds, order, skipSections);
222                     }
223                 } else if (component instanceof InputField) {
224                     order.add(id);
225                 }
226             }
227         }
228     }
229 
230     /**
231      * If true, the error messages will display the an InputField's title
232      * alongside the error, warning, and info messages related to it. This
233      * setting has no effect on messages which do not relate directly to a
234      * single InputField.
235      *
236      * @return the displayFieldLabelWithMessages
237      */
238     @BeanTagAttribute(name = "displayFieldLabelWithMessages")
239     public boolean isDisplayFieldLabelWithMessages() {
240         return this.displayFieldLabelWithMessages;
241     }
242 
243     /**
244      * If true, the error messages will display the an InputField's title
245      * alongside the error, warning, and info messages related to it. This
246      * setting has no effect on messages which do not relate directly to a
247      * single InputField.
248      *
249      * @param displayFieldLabelWithMessages the displayFieldLabelWithMessages to set
250      */
251     public void setDisplayFieldLabelWithMessages(boolean displayFieldLabelWithMessages) {
252         this.displayFieldLabelWithMessages = displayFieldLabelWithMessages;
253     }
254 
255     /**
256      * When collapseAdditionalFieldLinkMessages is set to true, the messages generated on field links will be
257      * summarized to limit the space they take up with an appendage similar to [+n message type] appended for
258      * additional
259      * messages that are omitted.  When this flag is false, all messages will be part of the link separated by
260      * a comma.
261      *
262      * @return if field link messages are being collapsed
263      */
264     @BeanTagAttribute(name = "collapseAdditionalFieldLinkMessages")
265     public boolean isCollapseAdditionalFieldLinkMessages() {
266         return collapseAdditionalFieldLinkMessages;
267     }
268 
269     /**
270      * Set collapseAdditionalFieldLinkMessages
271      *
272      * @param collapseAdditionalFieldLinkMessages - true if field link messages are being collapsed
273      */
274     public void setCollapseAdditionalFieldLinkMessages(boolean collapseAdditionalFieldLinkMessages) {
275         this.collapseAdditionalFieldLinkMessages = collapseAdditionalFieldLinkMessages;
276     }
277 
278     /**
279      * If true, the header message summary will display (this is the message count message appended to section
280      * headers).
281      *
282      * @return true if the summary will display, false otherwise
283      */
284     @BeanTagAttribute(name = "displayHeaderMessageSummary")
285     public boolean isDisplayHeaderMessageSummary() {
286         return displayHeaderMessageSummary;
287     }
288 
289     /**
290      * Sets whether the header message summary will display or not for this section/page.
291      *
292      * @param displayHeaderMessageSummary
293      */
294     public void setDisplayHeaderMessageSummary(boolean displayHeaderMessageSummary) {
295         this.displayHeaderMessageSummary = displayHeaderMessageSummary;
296     }
297 }