001 /**
002 * Copyright 2005-2013 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 */
016 package org.kuali.rice.krad.uif.view;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
020 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
021 import org.kuali.rice.krad.uif.UifConstants.ViewType;
022 import org.kuali.rice.krad.uif.UifPropertyPaths;
023 import org.kuali.rice.krad.uif.container.CollectionGroup;
024 import org.kuali.rice.krad.uif.container.Group;
025 import org.kuali.rice.krad.uif.component.Component;
026 import org.kuali.rice.krad.uif.component.RequestParameter;
027 import org.kuali.rice.krad.uif.control.Control;
028 import org.kuali.rice.krad.uif.control.TextAreaControl;
029 import org.kuali.rice.krad.uif.control.TextControl;
030 import org.kuali.rice.krad.uif.element.Link;
031 import org.kuali.rice.krad.uif.field.Field;
032 import org.kuali.rice.krad.uif.field.FieldGroup;
033 import org.kuali.rice.krad.uif.field.InputField;
034 import org.kuali.rice.krad.uif.field.LookupInputField;
035 import org.kuali.rice.krad.uif.util.ComponentFactory;
036 import org.kuali.rice.krad.uif.util.ComponentUtils;
037 import org.kuali.rice.krad.util.KRADConstants;
038 import org.kuali.rice.krad.web.form.LookupForm;
039
040 import java.util.ArrayList;
041 import java.util.Arrays;
042 import java.util.HashMap;
043 import java.util.List;
044
045 /**
046 * View type for Maintenance documents
047 *
048 * <p>
049 * Supports doing a search against a data object class or performing a more advanced query. The view
050 * type is primarily made up of two groups, the search (or criteria) group and the results group. Many
051 * options are supported on the view to enable/disable certain features, like what actions are available
052 * on the search results.
053 * </p>
054 *
055 * <p>
056 * Works in conjunction with <code>LookupableImpl</code> which customizes the view and carries out the
057 * business functionality
058 * </p>
059 *
060 * @author Kuali Rice Team (rice.collab@kuali.org)
061 */
062 @BeanTag(name = "lookupView-bean", parent = "Uif-LookupView")
063 public class LookupView extends FormView {
064 private static final long serialVersionUID = 716926008488403616L;
065
066 private Class<?> dataObjectClassName;
067
068 private Group criteriaGroup;
069 private CollectionGroup resultsGroup;
070
071 private FieldGroup resultsActionsFieldGroup;
072 private Field resultsReturnField;
073
074 private List<Component> criteriaFields;
075 private List<Component> resultFields;
076 private List<String> defaultSortAttributeNames;
077
078 protected boolean defaultSortAscending = true;
079
080 @RequestParameter
081 private boolean hideReturnLinks = false;
082 @RequestParameter
083 private boolean suppressActions = false;
084 @RequestParameter
085 private boolean showMaintenanceLinks = false;
086 @RequestParameter
087 private boolean multipleValuesSelect = false;
088
089 @RequestParameter
090 private String returnTarget;
091
092 @RequestParameter
093 private boolean returnByScript;
094
095 private boolean lookupCriteriaEnabled = true;
096 private boolean supplementalActionsEnabled = false;
097 private boolean disableSearchButtons = false;
098
099 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 }