001 /** 002 * Copyright 2005-2012 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.widget; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.krad.bo.DataObjectRelationship; 020 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 021 import org.kuali.rice.krad.uif.UifParameters; 022 import org.kuali.rice.krad.uif.container.CollectionGroup; 023 import org.kuali.rice.krad.uif.field.InputField; 024 import org.kuali.rice.krad.uif.view.View; 025 import org.kuali.rice.krad.uif.component.BindingInfo; 026 import org.kuali.rice.krad.uif.component.Component; 027 import org.kuali.rice.krad.uif.field.ActionField; 028 import org.kuali.rice.krad.uif.util.ViewModelUtils; 029 import org.kuali.rice.krad.util.KRADUtils; 030 031 import java.util.HashMap; 032 import java.util.List; 033 import java.util.Map; 034 035 /** 036 * Widget for navigating to a lookup from a field (called a quickfinder) 037 * 038 * @author Kuali Rice Team (rice.collab@kuali.org) 039 */ 040 public class QuickFinder extends WidgetBase { 041 private static final long serialVersionUID = 3302390972815386785L; 042 043 // lookup configuration 044 private String baseLookupUrl; 045 private String dataObjectClassName; 046 private String viewName; 047 048 private String referencesToRefresh; 049 050 private Map<String, String> fieldConversions; 051 private Map<String, String> lookupParameters; 052 053 // lookup view options 054 private String readOnlySearchFields; 055 056 private Boolean hideReturnLink; 057 private Boolean suppressActions; 058 private Boolean autoSearch; 059 private Boolean lookupCriteriaEnabled; 060 private Boolean supplementalActionsEnabled; 061 private Boolean disableSearchButtons; 062 private Boolean headerBarEnabled; 063 private Boolean showMaintenanceLinks; 064 065 private Boolean multipleValuesSelect; 066 private String lookupCollectionName; 067 068 private ActionField quickfinderActionField; 069 070 public QuickFinder() { 071 super(); 072 073 fieldConversions = new HashMap<String, String>(); 074 lookupParameters = new HashMap<String, String>(); 075 } 076 077 /** 078 * The following finalization is performed: 079 * 080 * <ul> 081 * <li> 082 * Sets defaults on collectionLookup such as collectionName, and the class if not set 083 * 084 * <p> 085 * If the data object class was not configured for the lookup, the class configured for the collection group will 086 * be used if it has a lookup defined. If not data object class is found for the lookup it will be disabled. The 087 * collection name is also defaulted to the binding path for this collection group, so the results returned from 088 * the lookup will populate this collection. Finally field conversions will be generated based on the PK fields of 089 * the collection object class 090 * </p> 091 * </li> 092 * </ul> 093 * 094 * @see org.kuali.rice.krad.uif.widget.Widget#performFinalize(org.kuali.rice.krad.uif.view.View, 095 * java.lang.Object, org.kuali.rice.krad.uif.component.Component) 096 */ 097 @Override 098 public void performFinalize(View view, Object model, Component parent) { 099 super.performFinalize(view, model, parent); 100 101 if (!isRender()) { 102 return; 103 } 104 105 if (parent instanceof InputField) { 106 InputField field = (InputField) parent; 107 108 // determine lookup class, field conversions and lookup parameters in 109 // not set 110 if (StringUtils.isBlank(dataObjectClassName)) { 111 DataObjectRelationship relationship = getRelationshipForField(view, model, field); 112 113 // if no relationship found cannot have a quickfinder 114 if (relationship == null) { 115 setRender(false); 116 return; 117 } 118 119 dataObjectClassName = relationship.getRelatedClass().getName(); 120 121 if ((fieldConversions == null) || fieldConversions.isEmpty()) { 122 generateFieldConversions(field, relationship); 123 } 124 125 if ((lookupParameters == null) || lookupParameters.isEmpty()) { 126 generateLookupParameters(field, relationship); 127 } 128 } 129 130 // adjust paths based on associated attribute field 131 updateFieldConversions(field.getBindingInfo()); 132 updateLookupParameters(field.getBindingInfo()); 133 } else if (parent instanceof CollectionGroup) { 134 CollectionGroup collectionGroup = (CollectionGroup) parent; 135 136 // check to see if data object class is configured for lookup, if so we will assume it should be enabled 137 // if not and the class configured for the collection group is lookupable, use that 138 if (StringUtils.isBlank(getDataObjectClassName())) { 139 Class<?> collectionObjectClass = collectionGroup.getCollectionObjectClass(); 140 boolean isCollectionClassLookupable = KRADServiceLocatorWeb.getViewDictionaryService().isLookupable( 141 collectionObjectClass); 142 if (isCollectionClassLookupable) { 143 setDataObjectClassName(collectionObjectClass.getName()); 144 145 if ((fieldConversions == null) || fieldConversions.isEmpty()) { 146 // use PK fields for collection class 147 List<String> collectionObjectPKFields = 148 KRADServiceLocatorWeb.getDataObjectMetaDataService().listPrimaryKeyFieldNames( 149 collectionObjectClass); 150 151 for (String pkField : collectionObjectPKFields) { 152 fieldConversions.put(pkField, pkField); 153 } 154 } 155 } else { 156 // no available data object class to lookup so need to disable quickfinder 157 setRender(false); 158 } 159 } 160 161 // set the lookup return collection name to this collection path 162 if (isRender() && StringUtils.isBlank(getLookupCollectionName())) { 163 setLookupCollectionName(collectionGroup.getBindingInfo().getBindingPath()); 164 } 165 } 166 167 quickfinderActionField.addActionParameter(UifParameters.BASE_LOOKUP_URL, baseLookupUrl); 168 quickfinderActionField.addActionParameter(UifParameters.DATA_OBJECT_CLASS_NAME, dataObjectClassName); 169 170 if (!fieldConversions.isEmpty()) { 171 quickfinderActionField.addActionParameter(UifParameters.CONVERSION_FIELDS, 172 KRADUtils.buildMapParameterString(fieldConversions)); 173 } 174 175 if (!lookupParameters.isEmpty()) { 176 quickfinderActionField.addActionParameter(UifParameters.LOOKUP_PARAMETERS, 177 KRADUtils.buildMapParameterString(lookupParameters)); 178 } 179 180 addActionParameterIfNotNull(UifParameters.VIEW_NAME, viewName); 181 addActionParameterIfNotNull(UifParameters.READ_ONLY_FIELDS, readOnlySearchFields); 182 addActionParameterIfNotNull(UifParameters.HIDE_RETURN_LINK, hideReturnLink); 183 addActionParameterIfNotNull(UifParameters.SUPRESS_ACTIONS, suppressActions); 184 addActionParameterIfNotNull(UifParameters.REFERENCES_TO_REFRESH, referencesToRefresh); 185 addActionParameterIfNotNull(UifParameters.AUTO_SEARCH, autoSearch); 186 addActionParameterIfNotNull(UifParameters.LOOKUP_CRITERIA_ENABLED, lookupCriteriaEnabled); 187 addActionParameterIfNotNull(UifParameters.SUPPLEMENTAL_ACTIONS_ENABLED, supplementalActionsEnabled); 188 addActionParameterIfNotNull(UifParameters.DISABLE_SEARCH_BUTTONS, disableSearchButtons); 189 addActionParameterIfNotNull(UifParameters.HEADER_BAR_ENABLED, headerBarEnabled); 190 addActionParameterIfNotNull(UifParameters.SHOW_MAINTENANCE_LINKS, showMaintenanceLinks); 191 addActionParameterIfNotNull(UifParameters.MULTIPLE_VALUES_SELECT, multipleValuesSelect); 192 addActionParameterIfNotNull(UifParameters.LOOKUP_COLLECTION_NAME, lookupCollectionName); 193 194 // TODO: 195 // org.kuali.rice.kns.util.FieldUtils.populateQuickfinderDefaultsForLookup(Class, 196 // String, Field) 197 } 198 199 protected void addActionParameterIfNotNull(String parameterName, Object parameterValue) { 200 if ((parameterValue != null) && StringUtils.isNotBlank(parameterValue.toString())) { 201 quickfinderActionField.addActionParameter(parameterName, parameterValue.toString()); 202 } 203 } 204 205 protected DataObjectRelationship getRelationshipForField(View view, Object model, InputField field) { 206 String propertyName = field.getBindingInfo().getBindingName(); 207 208 // get object instance and class for parent 209 Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, field); 210 Class<?> parentObjectClass = null; 211 if (parentObject != null) { 212 parentObjectClass = parentObject.getClass(); 213 } 214 215 // get relationship from metadata service 216 return KRADServiceLocatorWeb.getDataObjectMetaDataService().getDataObjectRelationship(parentObject, 217 parentObjectClass, propertyName, "", true, true, false); 218 } 219 220 protected void generateFieldConversions(InputField field, DataObjectRelationship relationship) { 221 fieldConversions = new HashMap<String, String>(); 222 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { 223 String fromField = entry.getValue(); 224 String toField = entry.getKey(); 225 226 // TODO: displayedFieldnames in 227 // org.kuali.rice.kns.lookup.LookupUtils.generateFieldConversions(BusinessObject, 228 // String, DataObjectRelationship, String, List, String) 229 230 fieldConversions.put(fromField, toField); 231 } 232 } 233 234 protected void generateLookupParameters(InputField field, DataObjectRelationship relationship) { 235 lookupParameters = new HashMap<String, String>(); 236 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { 237 String fromField = entry.getKey(); 238 String toField = entry.getValue(); 239 240 // TODO: displayedFieldnames and displayedQFFieldNames in 241 // generateLookupParameters(BusinessObject, 242 // String, DataObjectRelationship, String, List, String) 243 244 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals( 245 fromField)) { 246 lookupParameters.put(fromField, toField); 247 } 248 } 249 } 250 251 /** 252 * Adjusts the path on the field conversion to property to match the binding 253 * path prefix of the given <code>BindingInfo</code> 254 * 255 * @param bindingInfo - binding info instance to copy binding path prefix from 256 */ 257 public void updateFieldConversions(BindingInfo bindingInfo) { 258 Map<String, String> adjustedFieldConversions = new HashMap<String, String>(); 259 for (String fromField : fieldConversions.keySet()) { 260 String toField = fieldConversions.get(fromField); 261 String adjustedToFieldPath = bindingInfo.getPropertyAdjustedBindingPath(toField); 262 263 adjustedFieldConversions.put(fromField, adjustedToFieldPath); 264 } 265 266 this.fieldConversions = adjustedFieldConversions; 267 } 268 269 /** 270 * Adjusts the path on the lookup parameter from property to match the binding 271 * path prefix of the given <code>BindingInfo</code> 272 * 273 * @param bindingInfo - binding info instance to copy binding path prefix from 274 */ 275 public void updateLookupParameters(BindingInfo bindingInfo) { 276 Map<String, String> adjustedLookupParameters = new HashMap<String, String>(); 277 for (String fromField : lookupParameters.keySet()) { 278 String toField = lookupParameters.get(fromField); 279 String adjustedFromFieldPath = bindingInfo.getPropertyAdjustedBindingPath(fromField); 280 281 adjustedLookupParameters.put(adjustedFromFieldPath, toField); 282 } 283 284 this.lookupParameters = adjustedLookupParameters; 285 } 286 287 /** 288 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle() 289 */ 290 @Override 291 public List<Component> getComponentsForLifecycle() { 292 List<Component> components = super.getComponentsForLifecycle(); 293 294 components.add(quickfinderActionField); 295 296 return components; 297 } 298 299 /** 300 * Returns the URL for the lookup for which parameters will be added 301 * 302 * <p> 303 * The base URL includes the domain, context, and controller mapping for the lookup invocation. Parameters are 304 * then added based on configuration to complete the URL. This is generally defaulted to the application URL and 305 * internal KRAD servlet mapping, but can be changed to invoke another application such as the Rice standalone 306 * server 307 * </p> 308 * 309 * @return String lookup base URL 310 */ 311 public String getBaseLookupUrl() { 312 return this.baseLookupUrl; 313 } 314 315 /** 316 * Setter for the lookup base url (comain, context, and controller) 317 * 318 * @param baseLookupUrl 319 */ 320 public void setBaseLookupUrl(String baseLookupUrl) { 321 this.baseLookupUrl = baseLookupUrl; 322 } 323 324 /** 325 * Full class name the lookup should be provided for 326 * 327 * <p> 328 * This is passed on to the lookup request for the data object the lookup should be rendered for. This is then 329 * used by the lookup framework to select the lookup view (if more than one lookup view exists for the same 330 * data object class name, the {@link #getViewName()} property should be specified to select the view to render). 331 * </p> 332 * 333 * @return String lookup class name 334 */ 335 public String getDataObjectClassName() { 336 return this.dataObjectClassName; 337 } 338 339 /** 340 * Setter for the class name that lookup should be provided for 341 * 342 * @param dataObjectClassName 343 */ 344 public void setDataObjectClassName(String dataObjectClassName) { 345 this.dataObjectClassName = dataObjectClassName; 346 } 347 348 public String getViewName() { 349 return this.viewName; 350 } 351 352 public void setViewName(String viewName) { 353 this.viewName = viewName; 354 } 355 356 public String getReferencesToRefresh() { 357 return this.referencesToRefresh; 358 } 359 360 public void setReferencesToRefresh(String referencesToRefresh) { 361 this.referencesToRefresh = referencesToRefresh; 362 } 363 364 public Map<String, String> getFieldConversions() { 365 return this.fieldConversions; 366 } 367 368 public void setFieldConversions(Map<String, String> fieldConversions) { 369 this.fieldConversions = fieldConversions; 370 } 371 372 public Map<String, String> getLookupParameters() { 373 return this.lookupParameters; 374 } 375 376 public void setLookupParameters(Map<String, String> lookupParameters) { 377 this.lookupParameters = lookupParameters; 378 } 379 380 public String getReadOnlySearchFields() { 381 return this.readOnlySearchFields; 382 } 383 384 public void setReadOnlySearchFields(String readOnlySearchFields) { 385 this.readOnlySearchFields = readOnlySearchFields; 386 } 387 388 public Boolean getHideReturnLink() { 389 return this.hideReturnLink; 390 } 391 392 public void setHideReturnLink(Boolean hideReturnLink) { 393 this.hideReturnLink = hideReturnLink; 394 } 395 396 public Boolean getSuppressActions() { 397 return suppressActions; 398 } 399 400 public void setSuppressActions(Boolean suppressActions) { 401 this.suppressActions = suppressActions; 402 } 403 404 public Boolean getAutoSearch() { 405 return this.autoSearch; 406 } 407 408 public void setAutoSearch(Boolean autoSearch) { 409 this.autoSearch = autoSearch; 410 } 411 412 public Boolean getLookupCriteriaEnabled() { 413 return this.lookupCriteriaEnabled; 414 } 415 416 public void setLookupCriteriaEnabled(Boolean lookupCriteriaEnabled) { 417 this.lookupCriteriaEnabled = lookupCriteriaEnabled; 418 } 419 420 public Boolean getSupplementalActionsEnabled() { 421 return this.supplementalActionsEnabled; 422 } 423 424 public void setSupplementalActionsEnabled(Boolean supplementalActionsEnabled) { 425 this.supplementalActionsEnabled = supplementalActionsEnabled; 426 } 427 428 public Boolean getDisableSearchButtons() { 429 return this.disableSearchButtons; 430 } 431 432 public void setDisableSearchButtons(Boolean disableSearchButtons) { 433 this.disableSearchButtons = disableSearchButtons; 434 } 435 436 public Boolean getHeaderBarEnabled() { 437 return this.headerBarEnabled; 438 } 439 440 public void setHeaderBarEnabled(Boolean headerBarEnabled) { 441 this.headerBarEnabled = headerBarEnabled; 442 } 443 444 public Boolean getShowMaintenanceLinks() { 445 return this.showMaintenanceLinks; 446 } 447 448 public void setShowMaintenanceLinks(Boolean showMaintenanceLinks) { 449 this.showMaintenanceLinks = showMaintenanceLinks; 450 } 451 452 public ActionField getQuickfinderActionField() { 453 return this.quickfinderActionField; 454 } 455 456 public void setQuickfinderActionField(ActionField quickfinderActionField) { 457 this.quickfinderActionField = quickfinderActionField; 458 } 459 460 /** 461 * Indicates whether a multi-values lookup should be requested 462 * 463 * @return boolean true if multi-value lookup should be requested, false for normal lookup 464 */ 465 public Boolean getMultipleValuesSelect() { 466 return multipleValuesSelect; 467 } 468 469 /** 470 * Setter for the multi-values lookup indicator 471 * 472 * @param multipleValuesSelect 473 */ 474 public void setMultipleValuesSelect(Boolean multipleValuesSelect) { 475 this.multipleValuesSelect = multipleValuesSelect; 476 } 477 478 /** 479 * For the case of multi-value lookup, indicates the collection that should be populated with 480 * the return results 481 * 482 * <p> 483 * Note when the quickfinder is associated with a <code>CollectionGroup</code>, this property is 484 * set automatically from the collection name associated with the group 485 * </p> 486 * 487 * @return String collection name (must be full binding path) 488 */ 489 public String getLookupCollectionName() { 490 return lookupCollectionName; 491 } 492 493 /** 494 * Setter for the name of the collection that should be populated with lookup results 495 * 496 * @param lookupCollectionName 497 */ 498 public void setLookupCollectionName(String lookupCollectionName) { 499 this.lookupCollectionName = lookupCollectionName; 500 } 501 }