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.view;
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.uif.UifConstants.ViewType;
22  import org.kuali.rice.krad.uif.UifPropertyPaths;
23  import org.kuali.rice.krad.uif.container.CollectionGroup;
24  import org.kuali.rice.krad.uif.container.Group;
25  import org.kuali.rice.krad.uif.component.Component;
26  import org.kuali.rice.krad.uif.component.RequestParameter;
27  import org.kuali.rice.krad.uif.control.Control;
28  import org.kuali.rice.krad.uif.control.TextAreaControl;
29  import org.kuali.rice.krad.uif.control.TextControl;
30  import org.kuali.rice.krad.uif.element.Link;
31  import org.kuali.rice.krad.uif.field.Field;
32  import org.kuali.rice.krad.uif.field.FieldGroup;
33  import org.kuali.rice.krad.uif.field.InputField;
34  import org.kuali.rice.krad.uif.field.LookupInputField;
35  import org.kuali.rice.krad.uif.util.ComponentFactory;
36  import org.kuali.rice.krad.uif.util.ComponentUtils;
37  import org.kuali.rice.krad.util.KRADConstants;
38  import org.kuali.rice.krad.web.form.LookupForm;
39  
40  import java.util.ArrayList;
41  import java.util.Arrays;
42  import java.util.HashMap;
43  import java.util.List;
44  
45  /**
46   * View type for Maintenance documents
47   *
48   * <p>
49   * Supports doing a search against a data object class or performing a more advanced query. The view
50   * type is primarily made up of two groups, the search (or criteria) group and the results group. Many
51   * options are supported on the view to enable/disable certain features, like what actions are available
52   * on the search results.
53   * </p>
54   *
55   * <p>
56   * Works in conjunction with <code>LookupableImpl</code> which customizes the view and carries out the
57   * business functionality
58   * </p>
59   *
60   * @author Kuali Rice Team (rice.collab@kuali.org)
61   */
62  @BeanTag(name = "lookupView-bean", parent = "Uif-LookupView")
63  public class LookupView extends FormView {
64      private static final long serialVersionUID = 716926008488403616L;
65  
66      private Class<?> dataObjectClassName;
67  
68      private Group criteriaGroup;
69      private CollectionGroup resultsGroup;
70  
71      private FieldGroup resultsActionsFieldGroup;
72      private Field resultsReturnField;
73  
74      private List<Component> criteriaFields;
75      private List<Component> resultFields;
76      private List<String> defaultSortAttributeNames;
77  
78      protected boolean defaultSortAscending = true;
79  
80      @RequestParameter
81      private boolean hideReturnLinks = false;
82      @RequestParameter
83      private boolean suppressActions = false;
84      @RequestParameter
85      private boolean showMaintenanceLinks = false;
86      @RequestParameter
87      private boolean multipleValuesSelect = false;
88  
89      @RequestParameter
90      private String returnTarget;
91  
92      @RequestParameter
93      private boolean returnByScript;
94  
95      private boolean lookupCriteriaEnabled = true;
96      private boolean supplementalActionsEnabled = false;
97      private boolean disableSearchButtons = false;
98  
99      private Integer resultSetLimit = null;
100 
101     private String maintenanceUrlMapping;
102 
103     private FieldGroup rangeFieldGroupPrototype;
104 
105     public LookupView() {
106         super();
107 
108         setViewTypeName(ViewType.LOOKUP);
109         setApplyDirtyCheck(false);
110     }
111 
112     /**
113      * The following initialization is performed:
114      *
115      * <ul>
116      * <li>Set the abstractTypeClasses map for the lookup object path</li>
117      * </ul>
118      *
119      * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(org.kuali.rice.krad.uif.view.View,
120      *      java.lang.Object)
121      */
122     @Override
123     public void performInitialization(View view, Object model) {
124         initializeGroups();
125         if (getItems().isEmpty()) {
126             setItems(Arrays.asList(getCriteriaGroup(), getResultsGroup()));
127         }
128 
129         super.performInitialization(view, model);
130 
131         // if this is a multi-value lookup, don't show return column
132         if (multipleValuesSelect) {
133             hideReturnLinks = true;
134         }
135 
136         getObjectPathToConcreteClassMapping().put(UifPropertyPaths.LOOKUP_CRITERIA, getDataObjectClassName());
137         if (StringUtils.isNotBlank(getDefaultBindingObjectPath())) {
138             getObjectPathToConcreteClassMapping().put(getDefaultBindingObjectPath(), getDataObjectClassName());
139         }
140     }
141 
142     protected void initializeGroups() {
143         if ((getCriteriaGroup() != null) && (getCriteriaGroup().getItems().isEmpty())) {
144             getCriteriaGroup().setItems(getCriteriaFields());
145         }
146 
147         if (getResultsGroup() != null) {
148             if ((getResultsGroup().getItems().isEmpty()) && (getResultFields() != null)) {
149                 getResultsGroup().setItems(getResultFields());
150             }
151             if (getResultsGroup().getCollectionObjectClass() == null) {
152                 getResultsGroup().setCollectionObjectClass(getDataObjectClassName());
153             }
154         }
155     }
156 
157     /**
158      * @see org.kuali.rice.krad.uif.container.ContainerBase#performApplyModel(View, Object,
159      * org.kuali.rice.krad.uif.component.Component)
160      */
161     @Override
162     public void performApplyModel(View view, Object model, Component parent) {
163         LookupForm lookupForm = (LookupForm) model;
164 
165         // TODO: need to check lookupForm.isAtLeastOneRowHasActions() somewhere
166         if (!isSuppressActions() && isShowMaintenanceLinks()) {
167             ((List<Component>) getResultsGroup().getItems()).add(0, getResultsActionsFieldGroup());
168         }
169 
170         if (StringUtils.isNotBlank(lookupForm.getReturnFormKey()) &&
171                 StringUtils.isNotBlank(lookupForm.getReturnLocation()) && !isHideReturnLinks()) {
172             ((List<Component>) getResultsGroup().getItems()).add(0, getResultsReturnField());
173         }
174 
175         setupLookupCriteriaFields();
176 
177         super.performApplyModel(view, model, parent);
178     }
179 
180     /**
181      * Helper method to do any lookup specific changes to the criteria fields
182      */
183     private void setupLookupCriteriaFields() {
184 
185         int rangeIndex = 0;
186         HashMap<Integer, Component> dateRangeFieldMap = new HashMap<Integer, Component>();
187 
188         for (Component criteriaField : criteriaGroup.getItems()) {
189 
190             // Set the max length on the controls to allow for wildcards
191             Control control = ((InputField)criteriaField).getControl();
192             if (control instanceof TextControl) {
193                 ((TextControl) control).setMaxLength(null);
194             } else if (control instanceof TextAreaControl) {
195                 ((TextAreaControl) control).setMaxLength(null);
196             }
197 
198             if (((LookupInputField)criteriaField).isRanged()) {
199                 // Create field group
200                 FieldGroup rangeFieldGroup = ComponentUtils.copy(rangeFieldGroupPrototype, criteriaField.getId());
201                 rangeFieldGroup.setLabel(((LookupInputField)criteriaField).getLabel());
202                 List<Component> fieldGroupItems = new ArrayList<Component>();
203 
204                 // Create a new from date field
205                 LookupInputField fromDate = (LookupInputField)ComponentUtils.copy(criteriaField, KRADConstants.LOOKUP_DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL);
206                 fromDate.getBindingInfo().setBindingName(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
207                 fromDate.setPropertyName(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
208 
209                 // Set the criteria fields labels
210                 fromDate.setLabel("");
211                 fromDate.getFieldLabel().setRenderColon(false);
212                 ((LookupInputField)criteriaField).setLabel("to");
213                 ((LookupInputField)criteriaField).getFieldLabel().setRenderColon(false);
214 
215                 // Add the cirteria fields to the field group
216                 fieldGroupItems.add(fromDate);
217                 fieldGroupItems.add(criteriaField);
218                 rangeFieldGroup.setItems(fieldGroupItems);
219 
220                 // Add fieldgroup to map with index as key
221                 dateRangeFieldMap.put(rangeIndex, rangeFieldGroup);
222             }
223             rangeIndex++;
224         }
225 
226         // Replace original fields with range fieldgroups
227         List<Component> itemList = (List<Component>)criteriaGroup.getItems();
228         for (Integer index : dateRangeFieldMap.keySet()) {
229             itemList.set(index, dateRangeFieldMap.get(index));
230         }
231 
232         criteriaGroup.setItems(itemList);
233     }
234 
235     /**
236      * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
237      */
238     @Override
239     public List<Component> getComponentPrototypes() {
240         List<Component> components = super.getComponentPrototypes();
241 
242         components.add(criteriaGroup);
243         components.add(resultsGroup);
244         components.add(resultsActionsFieldGroup);
245         components.add(resultsReturnField);
246         components.addAll(criteriaFields);
247         components.addAll(resultFields);
248 
249         components.add(rangeFieldGroupPrototype);
250 
251         return components;
252     }
253 
254     public void applyConditionalLogicForFieldDisplay() {
255         // TODO: work into view lifecycle
256         //	    LookupViewHelperService lookupViewHelperService = (LookupViewHelperService) getViewHelperService();
257         //		Set<String> readOnlyFields = lookupViewHelperService.getConditionallyReadOnlyPropertyNames();
258         //		Set<String> requiredFields = lookupViewHelperService.getConditionallyRequiredPropertyNames();
259         //		Set<String> hiddenFields = lookupViewHelperService.getConditionallyHiddenPropertyNames();
260         //		if ( (readOnlyFields != null && !readOnlyFields.isEmpty()) ||
261         //			 (requiredFields != null && !requiredFields.isEmpty()) ||
262         //			 (hiddenFields != null && !hiddenFields.isEmpty())
263         //			) {
264         //			for (Field field : getResultsGroup().getItems()) {
265         //				if (InputField.class.isAssignableFrom(field.getClass())) {
266         //					InputField attributeField = (InputField) field;
267         //					if (readOnlyFields != null && readOnlyFields.contains(attributeField.getBindingInfo().getBindingName())) {
268         //						attributeField.setReadOnly(true);
269         //					}
270         //					if (requiredFields != null && requiredFields.contains(attributeField.getBindingInfo().getBindingName())) {
271         //						attributeField.setRequired(Boolean.TRUE);
272         //					}
273         //					if (hiddenFields != null && hiddenFields.contains(attributeField.getBindingInfo().getBindingName())) {
274         //						attributeField.setControl(LookupInquiryUtils.generateCustomLookupControlFromExisting(HiddenControl.class, null));
275         //					}
276         //				}
277         //	        }
278         //		}
279     }
280 
281     /**
282      * Class name for the object the lookup applies to
283      *
284      * <p>
285      * The object class name is used to pick up a dictionary entry which will
286      * feed the attribute field definitions and other configuration. In addition
287      * it is to configure the <code>Lookupable</code> which will carry out the
288      * lookup action
289      * </p>
290      *
291      * @return Class<?> lookup data object class
292      */
293     @BeanTagAttribute(name="dataObjectClassName")
294     public Class<?> getDataObjectClassName() {
295         return this.dataObjectClassName;
296     }
297 
298     /**
299      * Setter for the object class name
300      *
301      * @param dataObjectClassName
302      */
303     public void setDataObjectClassName(Class<?> dataObjectClassName) {
304         this.dataObjectClassName = dataObjectClassName;
305     }
306 
307     /**
308      * @return the hideReturnLinks
309      */
310     @BeanTagAttribute(name="hideReturnLinks")
311     public boolean isHideReturnLinks() {
312         return this.hideReturnLinks;
313     }
314 
315     /**
316      * @param hideReturnLinks the hideReturnLinks to set
317      */
318     public void setHideReturnLinks(boolean hideReturnLinks) {
319         this.hideReturnLinks = hideReturnLinks;
320     }
321 
322     /**
323      * @return the suppressActions
324      */
325     @BeanTagAttribute(name="isSuppressActions")
326     public boolean isSuppressActions() {
327         return this.suppressActions;
328     }
329 
330     /**
331      * @param suppressActions the suppressActions to set
332      */
333     public void setSuppressActions(boolean suppressActions) {
334         this.suppressActions = suppressActions;
335     }
336 
337     /**
338      * @return the showMaintenanceLinks
339      */
340     @BeanTagAttribute(name="showMaintenanceLinks")
341     public boolean isShowMaintenanceLinks() {
342         return this.showMaintenanceLinks;
343     }
344 
345     /**
346      * @param showMaintenanceLinks the showMaintenanceLinks to set
347      */
348     public void setShowMaintenanceLinks(boolean showMaintenanceLinks) {
349         this.showMaintenanceLinks = showMaintenanceLinks;
350     }
351 
352     /**
353      * Indicates whether multiple values select should be enabled for the lookup
354      *
355      * <p>
356      * When set to true, the select field is enabled for the lookup results group that allows the user
357      * to select one or more rows for returning
358      * </p>
359      *
360      * @return boolean true if multiple values should be enabled, false otherwise
361      */
362     @BeanTagAttribute(name="multipleValueSelect")
363     public boolean isMultipleValuesSelect() {
364         return multipleValuesSelect;
365     }
366 
367     /**
368      * Setter for the multiple values select indicator
369      *
370      * @param multipleValuesSelect
371      */
372     public void setMultipleValuesSelect(boolean multipleValuesSelect) {
373         this.multipleValuesSelect = multipleValuesSelect;
374     }
375 
376     /**
377      * @return the resultsActionsField
378      */
379     @BeanTagAttribute(name="resultActionsFieldGroup",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
380     public FieldGroup getResultsActionsFieldGroup() {
381         return this.resultsActionsFieldGroup;
382     }
383 
384     /**
385      * @param resultsActionsFieldGroup the resultsActionsField to set
386      */
387     public void setResultsActionsFieldGroup(FieldGroup resultsActionsFieldGroup) {
388         this.resultsActionsFieldGroup = resultsActionsFieldGroup;
389     }
390 
391     /**
392      * @return the resultsReturnField
393      */
394     @BeanTagAttribute(name="resultReturnField",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
395     public Field getResultsReturnField() {
396         return this.resultsReturnField;
397     }
398 
399     /**
400      * @param resultsReturnField the resultsReturnField to set
401      */
402     public void setResultsReturnField(Field resultsReturnField) {
403         this.resultsReturnField = resultsReturnField;
404     }
405 
406     @BeanTagAttribute(name="criteriaGroup",type = BeanTagAttribute.AttributeType.SINGLEBEAN)
407     public Group getCriteriaGroup() {
408         return this.criteriaGroup;
409     }
410 
411     public void setCriteriaGroup(Group criteriaGroup) {
412         this.criteriaGroup = criteriaGroup;
413     }
414 
415     @BeanTagAttribute(name="resultsGroup",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
416     public CollectionGroup getResultsGroup() {
417         return this.resultsGroup;
418     }
419 
420     public void setResultsGroup(CollectionGroup resultsGroup) {
421         this.resultsGroup = resultsGroup;
422     }
423 
424     @BeanTagAttribute(name="criteriaFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
425     public List<Component> getCriteriaFields() {
426         return this.criteriaFields;
427     }
428 
429     public void setCriteriaFields(List<Component> criteriaFields) {
430         this.criteriaFields = criteriaFields;
431     }
432 
433     @BeanTagAttribute(name="resultFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
434     public List<Component> getResultFields() {
435         return this.resultFields;
436     }
437 
438     public void setResultFields(List<Component> resultFields) {
439         this.resultFields = resultFields;
440     }
441 
442     @BeanTagAttribute(name="defaultSortAttributeNames",type= BeanTagAttribute.AttributeType.LISTVALUE)
443     public List<String> getDefaultSortAttributeNames() {
444         return this.defaultSortAttributeNames;
445     }
446 
447     public void setDefaultSortAttributeNames(List<String> defaultSortAttributeNames) {
448         this.defaultSortAttributeNames = defaultSortAttributeNames;
449     }
450 
451     @BeanTagAttribute(name="defaultSortAscending")
452     public boolean isDefaultSortAscending() {
453         return this.defaultSortAscending;
454     }
455 
456     public void setDefaultSortAscending(boolean defaultSortAscending) {
457         this.defaultSortAscending = defaultSortAscending;
458     }
459 
460     /**
461      * Retrieves the maximum number of records that will be listed
462      * as a result of the lookup search
463      *
464      * @return Integer result set limit
465      */
466     @BeanTagAttribute(name="resultSetLimit")
467     public Integer getResultSetLimit() {
468         return resultSetLimit;
469     }
470 
471     /**
472      * Setter for the result list limit
473      *
474      * @param resultSetLimit Integer specifying limit
475      */
476     public void setResultSetLimit(Integer resultSetLimit) {
477         this.resultSetLimit = resultSetLimit;
478     }
479 
480     /**
481      * Indicates whether a result set limit has been specified for the
482      * view
483      *
484      * @return true if this instance has a result set limit
485      */
486     public boolean hasResultSetLimit() {
487         return (resultSetLimit != null);
488     }
489 
490     /**
491      * @param returnTarget the returnTarget to set
492      */
493     public void setReturnTarget(String returnTarget) {
494         this.returnTarget = returnTarget;
495     }
496 
497     /**
498      * @return the returnTarget
499      */
500     @BeanTagAttribute(name="returnTarget")
501     public String getReturnTarget() {
502         return returnTarget;
503     }
504 
505     /**
506      * @return the returnByScript
507      */
508     @BeanTagAttribute(name="returnByScript")
509     public boolean isReturnByScript() {
510         return returnByScript;
511     }
512 
513     /**
514      * Setter for the flag to indicate that lookups will return the value
515      * by script and not a post
516      *
517      * @param returnByScript the returnByScript flag
518      */
519     public void setReturnByScript(boolean returnByScript) {
520         this.returnByScript = returnByScript;
521     }
522 
523     /**
524      * String that maps to the maintenance controller for the maintenance document (if any) associated with the
525      * lookup data object class
526      *
527      * <p>
528      * Mapping will be used to build the maintenance action links (such as edit, copy, and new). If not given, the
529      * default maintenance mapping will be used
530      * </p>
531      *
532      * @return String mapping string
533      */
534     @BeanTagAttribute(name="maintenanceUrlMapping")
535     public String getMaintenanceUrlMapping() {
536         return maintenanceUrlMapping;
537     }
538 
539     /**
540      * Setter for the URL mapping string that will be used to build up maintenance action URLs
541      *
542      * @param maintenanceUrlMapping
543      */
544     public void setMaintenanceUrlMapping(String maintenanceUrlMapping) {
545         this.maintenanceUrlMapping = maintenanceUrlMapping;
546     }
547 
548     /**
549      * The field group prototype that will be copied and used for range fields
550      *
551      * @return FieldGroup
552      */
553     public FieldGroup getRangeFieldGroupPrototype() {
554         return rangeFieldGroupPrototype;
555     }
556 
557     /**
558      * Setter for the range FieldGroup prototype
559      *
560      * @param rangeFieldGroupPrototype
561      */
562     public void setRangeFieldGroupPrototype(FieldGroup rangeFieldGroupPrototype) {
563         this.rangeFieldGroupPrototype = rangeFieldGroupPrototype;
564     }
565 }