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 }