001package org.kuali.ole; 002 003import org.apache.commons.beanutils.PropertyUtils; 004import org.apache.commons.lang.StringUtils; 005import org.kuali.rice.core.api.CoreApiServiceLocator; 006import org.kuali.rice.core.api.config.property.ConfigurationService; 007import org.kuali.rice.core.api.encryption.EncryptionService; 008import org.kuali.rice.core.api.search.SearchOperator; 009import org.kuali.rice.core.api.util.RiceKeyConstants; 010import org.kuali.rice.core.api.util.type.TypeUtils; 011import org.kuali.rice.kim.api.identity.Person; 012import org.kuali.rice.krad.bo.ExternalizableBusinessObject; 013import org.kuali.rice.krad.datadictionary.BusinessObjectEntry; 014import org.kuali.rice.krad.datadictionary.RelationshipDefinition; 015import org.kuali.rice.krad.lookup.*; 016import org.kuali.rice.krad.service.*; 017import org.kuali.rice.krad.uif.UifConstants; 018import org.kuali.rice.krad.uif.UifParameters; 019import org.kuali.rice.krad.uif.UifPropertyPaths; 020import org.kuali.rice.krad.uif.control.Control; 021import org.kuali.rice.krad.uif.control.HiddenControl; 022import org.kuali.rice.krad.uif.control.ValueConfiguredControl; 023import org.kuali.rice.krad.uif.element.Action; 024import org.kuali.rice.krad.uif.field.InputField; 025import org.kuali.rice.krad.uif.field.LookupInputField; 026import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl; 027import org.kuali.rice.krad.uif.util.ComponentUtils; 028import org.kuali.rice.krad.uif.util.LookupInquiryUtils; 029import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 030import org.kuali.rice.krad.uif.view.LookupView; 031import org.kuali.rice.krad.uif.view.View; 032import org.kuali.rice.krad.util.*; 033import org.kuali.rice.krad.web.form.LookupForm; 034 035import java.security.GeneralSecurityException; 036import java.util.*; 037 038/** 039 * Created by pvsubrah on 5/5/14. 040 */ 041 042//TODO: This class will need to be retired once there is a permanent fix for the securefieldpaternmatching 043//TODO: in KRADUtils thats used bu the LookupableImpl 044 045public class OleLookupableImpl extends ViewHelperServiceImpl implements Lookupable { 046 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupableImpl.class); 047 048 private Class<?> dataObjectClass; 049 050 private transient ConfigurationService configurationService; 051 private transient DataObjectAuthorizationService dataObjectAuthorizationService; 052 private transient DataObjectMetaDataService dataObjectMetaDataService; 053 private transient DocumentDictionaryService documentDictionaryService; 054 private transient LookupService lookupService; 055 private transient EncryptionService encryptionService; 056 057 /** 058 * Initialization of Lookupable requires that the business object class be set for the 059 * {@link #initializeDataFieldFromDataDictionary(org.kuali.rice.krad.uif.view.View, 060 * org.kuali.rice.krad.uif.field.DataField)} method 061 * 062 * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#performInitialization(org.kuali.rice.krad.uif.view.View, 063 * java.lang.Object) 064 */ 065 @Override 066 public void performInitialization(View view, Object model) { 067 if (!LookupView.class.isAssignableFrom(view.getClass())) { 068 throw new IllegalArgumentException( 069 "View class '" + view.getClass() + " is not assignable from the '" + LookupView.class + "'"); 070 } 071 072 LookupView lookupView = (LookupView) view; 073 setDataObjectClass(lookupView.getDataObjectClassName()); 074 075 super.performInitialization(view, model); 076 } 077 078 /** 079 * @see org.kuali.rice.krad.lookup.Lookupable#initSuppressAction(org.kuali.rice.krad.web.form.LookupForm) 080 */ 081 @Override 082 public void initSuppressAction(LookupForm lookupForm) { 083 LookupViewAuthorizerBase lookupAuthorizer = (LookupViewAuthorizerBase) lookupForm.getView().getAuthorizer(); 084 Person user = GlobalVariables.getUserSession().getPerson(); 085 ((LookupView) lookupForm.getView()).setSuppressActions(!lookupAuthorizer.canInitiateDocument(lookupForm, user)); 086 } 087 088 /** 089 * @see org.kuali.rice.krad.lookup.Lookupable#performSearch 090 */ 091 @Override 092 public Collection<?> performSearch(LookupForm form, Map<String, String> searchCriteria, boolean bounded) { 093 Collection<?> displayList; 094 095 // TODO: force uppercase will be done in binding at some point 096 displayList = getSearchResults(form, LookupUtils.forceUppercase(getDataObjectClass(), searchCriteria), 097 !bounded); 098 099 // TODO delyea - is this the best way to set that the entire set has a returnable row? 100 for (Object object : displayList) { 101 if (isResultReturnable(object)) { 102 form.setAtLeastOneRowReturnable(true); 103 } 104 } 105 106 return displayList; 107 } 108 109 /** 110 * Get the search results of the lookup 111 * 112 * @param form lookup form instance containing the lookup data 113 * @param searchCriteria map of criteria currently set 114 * @param unbounded indicates whether the complete result should be returned. When set to false the result is 115 * limited (if necessary) to the max search result limit configured. 116 * @return the list of result objects, possibly bounded 117 */ 118 protected List<?> getSearchResults(LookupForm form, Map<String, String> searchCriteria, boolean unbounded) { 119 Collection<?> searchResults; 120 121 // removed blank search values and decrypt any encrypted search values 122 Map<String, String> nonBlankSearchCriteria = processSearchCriteria(form, searchCriteria); 123 124 // return empty search results (none found) when the search doesn't have any nonBlankSearchCriteria although 125 // a filtered search criteria is specified 126 if (nonBlankSearchCriteria == null) { 127 return new ArrayList<Object>(); 128 } 129 130 // if this class is an EBO, just call the module service to get the results 131 if (ExternalizableBusinessObject.class.isAssignableFrom(getDataObjectClass())) { 132 return getSearchResultsForEBO(nonBlankSearchCriteria, unbounded); 133 } 134 135 // if any of the properties refer to an embedded EBO, call the EBO 136 // lookups first and apply to the local lookup 137 try { 138 Integer searchResultsLimit = null; 139 140 if (!unbounded) { 141 searchResultsLimit = LookupUtils.getSearchResultsLimit(getDataObjectClass(), form); 142 } 143 144 if (LookupUtils.hasExternalBusinessObjectProperty(getDataObjectClass(), nonBlankSearchCriteria)) { 145 Map<String, String> eboSearchCriteria = adjustCriteriaForNestedEBOs(nonBlankSearchCriteria, unbounded); 146 147 if (LOG.isDebugEnabled()) { 148 LOG.debug("Passing these results into the lookup service: " + eboSearchCriteria); 149 } 150 151 // add those results as criteria run the normal search (but with the EBO criteria added) 152 searchResults = getLookupService().findCollectionBySearchHelper(getDataObjectClass(), eboSearchCriteria, 153 unbounded, searchResultsLimit); 154 generateLookupResultsMessages(form, eboSearchCriteria, searchResults, unbounded); 155 } else { 156 searchResults = getLookupService().findCollectionBySearchHelper(getDataObjectClass(), 157 nonBlankSearchCriteria, unbounded, searchResultsLimit); 158 generateLookupResultsMessages(form, nonBlankSearchCriteria, searchResults, unbounded); 159 } 160 161 } catch (IllegalAccessException e) { 162 throw new RuntimeException("Error trying to perform search", e); 163 } catch (InstantiationException e1) { 164 throw new RuntimeException("Error trying to perform search", e1); 165 } 166 167 if (searchResults == null) { 168 searchResults = new ArrayList<Object>(); 169 } else { 170 sortSearchResults(form, (List<?>) searchResults); 171 } 172 173 return (List<?>) searchResults; 174 } 175 176 /** 177 * Convenience method for setting an error message on the lookup results section 178 * 179 * @param form 180 * @param messageToDisplay 181 */ 182 public void generateErrorMessageForResults(LookupForm form, String messageToDisplay) { 183 GlobalVariables.getMessageMap().putErrorForSectionId("LookupResultMessages", messageToDisplay); 184 } 185 186 /** 187 * Helper function to render lookup results messages 188 * 189 * @param form 190 * @param searchCriteria 191 * @param searchResult 192 * @param unbounded 193 */ 194 protected void generateLookupResultsMessages(LookupForm form, Map<String, String> searchCriteria, 195 Collection<?> searchResult, boolean unbounded) { 196 String resultsPropertyName = "LookupResultMessages"; 197 List<String> pkLabels = new ArrayList<String>(); 198 199 Boolean usingPrimaryKey = getLookupService().allPrimaryKeyValuesPresentAndNotWildcard(getDataObjectClass(), 200 (Map<String, String>) searchCriteria); 201 202 Integer searchResultsLimit = LookupUtils.getSearchResultsLimit(getDataObjectClass(), form); 203 Long searchResultsSize = Long.valueOf(0); 204 205 if (searchResult instanceof CollectionIncomplete 206 && ((CollectionIncomplete) searchResult).getActualSizeIfTruncated() > 0) { 207 searchResultsSize = ((CollectionIncomplete) searchResult).getActualSizeIfTruncated(); 208 } else if (searchResult != null) { 209 searchResultsSize = Long.valueOf(searchResult.size()); 210 } 211 212 Boolean resultsExceedsLimit = !unbounded 213 && searchResultsLimit != null 214 && searchResultsSize > 0 215 && searchResultsSize > searchResultsLimit ? true : false; 216 217 if (usingPrimaryKey) { 218 List<String> pkNames = getDataObjectMetaDataService().listPrimaryKeyFieldNames(getDataObjectClass()); 219 for (String pkName : pkNames) { 220 pkLabels.add(getDataDictionaryService().getAttributeLabel(getDataObjectClass(), pkName)); 221 } 222 223 GlobalVariables.getMessageMap().putInfoForSectionId(resultsPropertyName, 224 RiceKeyConstants.INFO_LOOKUP_RESULTS_USING_PRIMARY_KEY, StringUtils.join(pkLabels, ",")); 225 } 226 227 if (searchResultsSize == 0) { 228 GlobalVariables.getMessageMap().putInfoForSectionId(resultsPropertyName, 229 RiceKeyConstants.INFO_LOOKUP_RESULTS_NONE_FOUND); 230 } else if (searchResultsSize == 1) { 231 GlobalVariables.getMessageMap().putInfoForSectionId(resultsPropertyName, 232 RiceKeyConstants.INFO_LOOKUP_RESULTS_DISPLAY_ONE); 233 } else if (searchResultsSize > 1) { 234 if (resultsExceedsLimit) { 235 GlobalVariables.getMessageMap().putInfoForSectionId(resultsPropertyName, 236 RiceKeyConstants.INFO_LOOKUP_RESULTS_EXCEEDS_LIMIT, searchResultsSize.toString(), 237 searchResultsLimit.toString()); 238 } else { 239 GlobalVariables.getMessageMap().putInfoForSectionId(resultsPropertyName, 240 RiceKeyConstants.INFO_LOOKUP_RESULTS_DISPLAY_ALL, searchResultsSize.toString()); 241 } 242 } 243 } 244 245 /** 246 * Sorts the given list of search results based on the lookup view's configured sort attributes 247 * 248 * <p> 249 * First if the posted view exists we grab the sort attributes from it. This will take into account expressions 250 * that might have been configured on the sort attributes. If the posted view does not exist (because we did a 251 * search from a get request or form session storage is off), we get the sort attributes from the view that we 252 * will be rendered (and was initialized before controller call). However, expressions will not be evaluated yet, 253 * thus if expressions were configured we don't know the results and can not sort the list 254 * </p> 255 * 256 * @param form - lookup form instance containing view information 257 * @param searchResults - list of search results to sort 258 * @TODO: revisit this when we have a solution for the posted view problem 259 */ 260 protected void sortSearchResults(LookupForm form, List<?> searchResults) { 261 List<String> defaultSortColumns = null; 262 boolean defaultSortAscending = true; 263 // first choice is to get default sort columns off posted view, since that will include the full 264 // lifecycle and expression evaluations 265 if (form.getPostedView() != null) { 266 defaultSortColumns = ((LookupView) form.getPostedView()).getDefaultSortAttributeNames(); 267 defaultSortAscending = ((LookupView) form.getPostedView()).isDefaultSortAscending(); 268 } 269 // now try view being built, if default sort attributes have any expression (entry is null) we can't use them 270 else if (form.getView() != null) { 271 defaultSortColumns = ((LookupView) form.getView()).getDefaultSortAttributeNames(); 272 defaultSortAscending = ((LookupView) form.getView()).isDefaultSortAscending(); 273 boolean hasExpression = false; 274 if (defaultSortColumns != null) { 275 for (String sortColumn : defaultSortColumns) { 276 if (sortColumn == null) { 277 hasExpression = true; 278 } 279 } 280 } 281 282 if (hasExpression) { 283 defaultSortColumns = null; 284 } 285 } 286 287 if ((defaultSortColumns != null) && (!defaultSortColumns.isEmpty())) { 288 BeanPropertyComparator comparator = new BeanPropertyComparator(defaultSortColumns, true); 289 if (defaultSortAscending) { 290 Collections.sort(searchResults, comparator); 291 } else { 292 Collections.sort(searchResults, Collections.reverseOrder(comparator)); 293 } 294 } 295 } 296 297 /** 298 * Process the search criteria to be used with the lookup 299 * 300 * <p> 301 * Processing entails primarily of the removal of filtered and unused/blank search criteria. Encrypted field 302 * values are decrypted in this process as well. 303 * </p> 304 * 305 * @param lookupForm lookup form instance containing the lookup data 306 * @param searchCriteria map of criteria currently set 307 * @return map with the non blank search criteria 308 */ 309 protected Map<String, String> processSearchCriteria(LookupForm lookupForm, Map<String, String> searchCriteria) { 310 Map<String, InputField> criteriaFields = new HashMap<String, InputField>(); 311 if (lookupForm.getPostedView() != null) { 312 criteriaFields = getCriteriaFieldsForValidation((LookupView) lookupForm.getPostedView(), lookupForm); 313 } 314 315 Map<String, String> filteredSearchCriteria = new HashMap<String, String>(searchCriteria); 316 for (String fieldName : searchCriteria.keySet()) { 317 InputField inputField = criteriaFields.get(fieldName); 318 if ((inputField == null) || !(inputField instanceof LookupInputField)) { 319 continue; 320 } 321 322 filteredSearchCriteria = ((LookupInputField) inputField).filterSearchCriteria(filteredSearchCriteria); 323 if (filteredSearchCriteria == null) { 324 return null; 325 } 326 } 327 328 Map<String, String> nonBlankSearchCriteria = new HashMap<String, String>(); 329 for (String fieldName : filteredSearchCriteria.keySet()) { 330 String fieldValue = filteredSearchCriteria.get(fieldName); 331 332 // don't add hidden criteria 333 InputField inputField = criteriaFields.get(fieldName); 334 if ((inputField != null) && (inputField.getControl() instanceof HiddenControl)) { 335 continue; 336 } 337 338 // only add criteria if non blank 339 if (StringUtils.isNotBlank(fieldValue)) { 340 if (fieldValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { 341 String encryptedValue = StringUtils.removeEnd(fieldValue, EncryptionService.ENCRYPTION_POST_PREFIX); 342 try { 343 if (CoreApiServiceLocator.getEncryptionService().isEnabled()) { 344 fieldValue = getEncryptionService().decrypt(encryptedValue); 345 } 346 } catch (GeneralSecurityException e) { 347 LOG.error("Error decrypting value for business object class " + getDataObjectClass() + 348 " attribute " + fieldName, e); 349 throw new RuntimeException( 350 "Error decrypting value for business object class " + getDataObjectClass() + 351 " attribute " + fieldName, e); 352 } 353 } 354 355 nonBlankSearchCriteria.put(fieldName, fieldValue); 356 } 357 } 358 359 return nonBlankSearchCriteria; 360 } 361 362 /** 363 * Get the search results of an {@linkExternalizableBusinessObject} 364 * 365 * @param searchCriteria map of criteria currently set 366 * @param unbounded indicates whether the complete result should be returned. When set to false the result is 367 * limited (if necessary) to the max search result limit configured. 368 * @return list of result objects, possibly bounded 369 */ 370 protected List<?> getSearchResultsForEBO(Map<String, String> searchCriteria, boolean unbounded) { 371 ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService( 372 getDataObjectClass()); 373 BusinessObjectEntry ddEntry = eboModuleService.getExternalizableBusinessObjectDictionaryEntry( 374 getDataObjectClass()); 375 376 Map<String, String> filteredFieldValues = new HashMap<String, String>(); 377 for (String fieldName : searchCriteria.keySet()) { 378 if (ddEntry.getAttributeNames().contains(fieldName)) { 379 filteredFieldValues.put(fieldName, searchCriteria.get(fieldName)); 380 } 381 } 382 383 List<?> searchResults = eboModuleService.getExternalizableBusinessObjectsListForLookup( 384 (Class<? extends ExternalizableBusinessObject>) getDataObjectClass(), (Map) filteredFieldValues, 385 unbounded); 386 387 return searchResults; 388 } 389 390 /** 391 * @param searchCriteria map of criteria currently set 392 * @param unbounded indicates whether the complete result should be returned. When set to false the result is 393 * limited (if necessary) to the max search result limit configured. 394 * @return 395 * @throws InstantiationException 396 * @throws IllegalAccessException 397 */ 398 protected Map<String, String> adjustCriteriaForNestedEBOs(Map<String, String> searchCriteria, 399 boolean unbounded) throws InstantiationException, IllegalAccessException { 400 if (LOG.isDebugEnabled()) { 401 LOG.debug("has EBO reference: " + getDataObjectClass()); 402 LOG.debug("properties: " + searchCriteria); 403 } 404 405 // remove the EBO criteria 406 Map<String, String> nonEboFieldValues = LookupUtils.removeExternalizableBusinessObjectFieldValues( 407 getDataObjectClass(), searchCriteria); 408 if (LOG.isDebugEnabled()) { 409 LOG.debug("Non EBO properties removed: " + nonEboFieldValues); 410 } 411 412 // get the list of EBO properties attached to this object 413 List<String> eboPropertyNames = LookupUtils.getExternalizableBusinessObjectProperties(getDataObjectClass(), 414 searchCriteria); 415 if (LOG.isDebugEnabled()) { 416 LOG.debug("EBO properties: " + eboPropertyNames); 417 } 418 419 // loop over those properties 420 for (String eboPropertyName : eboPropertyNames) { 421 // extract the properties as known to the EBO 422 Map<String, String> eboFieldValues = LookupUtils.getExternalizableBusinessObjectFieldValues(eboPropertyName, 423 searchCriteria); 424 if (LOG.isDebugEnabled()) { 425 LOG.debug("EBO properties for master EBO property: " + eboPropertyName); 426 LOG.debug("properties: " + eboFieldValues); 427 } 428 429 // run search against attached EBO's module service 430 ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService( 431 LookupUtils.getExternalizableBusinessObjectClass(getDataObjectClass(), eboPropertyName)); 432 433 // KULRICE-4401 made eboResults an empty list and only filled if 434 // service is found. 435 List<?> eboResults = Collections.emptyList(); 436 if (eboModuleService != null) { 437 eboResults = eboModuleService.getExternalizableBusinessObjectsListForLookup( 438 LookupUtils.getExternalizableBusinessObjectClass(getDataObjectClass(), eboPropertyName), 439 (Map) eboFieldValues, unbounded); 440 } else { 441 LOG.debug("EBO ModuleService is null: " + eboPropertyName); 442 } 443 // get the mapping/relationship between the EBO object and it's 444 // parent object 445 // use that to adjust the searchCriteria 446 447 // get the parent property type 448 Class<?> eboParentClass; 449 String eboParentPropertyName; 450 if (ObjectUtils.isNestedAttribute(eboPropertyName)) { 451 eboParentPropertyName = StringUtils.substringBeforeLast(eboPropertyName, "."); 452 try { 453 eboParentClass = PropertyUtils.getPropertyType(getDataObjectClass().newInstance(), 454 eboParentPropertyName); 455 } catch (Exception ex) { 456 throw new RuntimeException( 457 "Unable to create an instance of the business object class: " + getDataObjectClass() 458 .getName(), ex); 459 } 460 } else { 461 eboParentClass = getDataObjectClass(); 462 eboParentPropertyName = null; 463 } 464 465 if (LOG.isDebugEnabled()) { 466 LOG.debug("determined EBO parent class/property name: " + eboParentClass + "/" + eboParentPropertyName); 467 } 468 469 // look that up in the DD (BOMDS) 470 // find the appropriate relationship 471 // CHECK THIS: what if eboPropertyName is a nested attribute - 472 // need to strip off the eboParentPropertyName if not null 473 RelationshipDefinition rd = getDataObjectMetaDataService().getDictionaryRelationship(eboParentClass, 474 eboPropertyName); 475 if (LOG.isDebugEnabled()) { 476 LOG.debug("Obtained RelationshipDefinition for " + eboPropertyName); 477 LOG.debug(rd); 478 } 479 480 // copy the needed properties (primary only) to the field values KULRICE-4446 do 481 // so only if the relationship definition exists 482 // NOTE: this will work only for single-field PK unless the ORM 483 // layer is directly involved 484 // (can't make (field1,field2) in ( (v1,v2),(v3,v4) ) style 485 // queries in the lookup framework 486 if (ObjectUtils.isNotNull(rd)) { 487 if (rd.getPrimitiveAttributes().size() > 1) { 488 throw new RuntimeException( 489 "EBO Links don't work for relationships with multiple-field primary keys."); 490 } 491 String boProperty = rd.getPrimitiveAttributes().get(0).getSourceName(); 492 String eboProperty = rd.getPrimitiveAttributes().get(0).getTargetName(); 493 StringBuffer boPropertyValue = new StringBuffer(); 494 495 // loop over the results, making a string that the lookup 496 // DAO will convert into an 497 // SQL "IN" clause 498 for (Object ebo : eboResults) { 499 if (boPropertyValue.length() != 0) { 500 boPropertyValue.append(SearchOperator.OR.op()); 501 } 502 try { 503 boPropertyValue.append(PropertyUtils.getProperty(ebo, eboProperty).toString()); 504 } catch (Exception ex) { 505 LOG.warn("Unable to get value for " + eboProperty + " on " + ebo); 506 } 507 } 508 509 if (eboParentPropertyName == null) { 510 // non-nested property containing the EBO 511 nonEboFieldValues.put(boProperty, boPropertyValue.toString()); 512 } else { 513 // property nested within the main searched-for BO that 514 // contains the EBO 515 nonEboFieldValues.put(eboParentPropertyName + "." + boProperty, boPropertyValue.toString()); 516 } 517 } 518 } 519 520 return nonEboFieldValues; 521 } 522 523 /** 524 * @see org.kuali.rice.krad.lookup.Lookupable#performClear 525 */ 526 @Override 527 public Map<String, String> performClear(LookupForm form, Map<String, String> searchCriteria) { 528 Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>(); 529 if (form.getPostedView() == null) { 530 criteriaFieldMap = getCriteriaFieldsForValidation((LookupView) form.getPostedView(), form); 531 } 532 533 List<String> readOnlyFieldsList = form.getReadOnlyFieldsList(); 534 535 Map<String, String> clearedSearchCriteria = new HashMap<String, String>(); 536 for (Map.Entry<String, String> searchKeyValue : searchCriteria.entrySet()) { 537 String searchPropertyName = searchKeyValue.getKey(); 538 539 InputField inputField = criteriaFieldMap.get(searchPropertyName); 540 541 if (readOnlyFieldsList != null && readOnlyFieldsList.contains(searchPropertyName)) { 542 clearedSearchCriteria.put(searchPropertyName, searchKeyValue.getValue()); 543 } else if (inputField != null) { 544 // TODO: check secure fields 545 // if (field.isSecure()) { 546 // field.setSecure(false); 547 // field.setDisplayMaskValue(null); 548 // field.setEncryptedValue(null); 549 // } 550 551 // TODO: need formatting on default value and make sure it works when control converts 552 // from checkbox to radio 553 clearedSearchCriteria.put(searchPropertyName, inputField.getDefaultValue()); 554 } else { 555 clearedSearchCriteria.put(searchPropertyName, ""); 556 } 557 } 558 559 return clearedSearchCriteria; 560 } 561 562 /** 563 * @see org.kuali.rice.krad.lookup.Lookupable#validateSearchParameters 564 */ 565 @Override 566 public boolean validateSearchParameters(LookupForm form, Map<String, String> searchCriteria) { 567 boolean valid = true; 568 569 // if postedView is null then we are executing the search from get request, in which case we 570 // can't validate the criteria 571 if (form.getPostedView() == null) { 572 return valid; 573 } 574 575 Map<String, InputField> criteriaFields = getCriteriaFieldsForValidation((LookupView) form.getPostedView(), 576 form); 577 578 // build list of hidden properties configured with criteria fields 579 List<String> hiddenCriteria = new ArrayList<String>(); 580 for (InputField field : criteriaFields.values()) { 581 if (field.getAdditionalHiddenPropertyNames() != null) { 582 hiddenCriteria.addAll(field.getAdditionalHiddenPropertyNames()); 583 } 584 } 585 586 // validate required 587 // TODO: this will be done by the uif validation service at some point 588 for (Map.Entry<String, String> searchKeyValue : searchCriteria.entrySet()) { 589 String searchPropertyName = searchKeyValue.getKey(); 590 String searchPropertyValue = searchKeyValue.getValue(); 591 592 InputField inputField = criteriaFields.get(searchPropertyName); 593 594 String adjustedSearchPropertyPath = UifPropertyPaths.LOOKUP_CRITERIA + "[" + searchPropertyName + "]"; 595 if (inputField == null && hiddenCriteria.contains(adjustedSearchPropertyPath)) { 596 return valid; 597 } 598 599 // verify the property sent is a valid to search on 600 if ((inputField == null) && !searchPropertyName.contains( 601 KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX)) { 602 throw new RuntimeException("Invalid search field sent for property name: " + searchPropertyName); 603 } 604 605 if (inputField != null) { 606 if (StringUtils.isBlank(searchPropertyValue) && inputField.getRequired()) { 607 GlobalVariables.getMessageMap().putError(inputField.getPropertyName(), 608 RiceKeyConstants.ERROR_REQUIRED, inputField.getLabel()); 609 } 610 611 validateSearchParameterWildcardAndOperators(inputField, searchPropertyValue); 612 } 613 } 614 if (GlobalVariables.getMessageMap().hasErrors()) { 615 valid = false; 616 } 617 618 return valid; 619 } 620 621 /** 622 * Returns the criteria fields in a map keyed by the field property name. 623 * 624 * @param lookupView 625 * @param form lookup form instance containing the lookup data 626 * @return map of criteria fields 627 */ 628 protected Map<String, InputField> getCriteriaFieldsForValidation(LookupView lookupView, LookupForm form) { 629 Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>(); 630 631 if (lookupView.getCriteriaFields() == null) { 632 return criteriaFieldMap; 633 } 634 635 // TODO; need hooks for code generated components and also this doesn't have lifecycle which 636 // could change fields 637 List<InputField> fields = ComponentUtils.getComponentsOfTypeDeep(lookupView.getCriteriaFields(), 638 InputField.class); 639 for (InputField field : fields) { 640 criteriaFieldMap.put(field.getPropertyName(), field); 641 } 642 643 return criteriaFieldMap; 644 } 645 646 /** 647 * Validates that any wildcards contained within the search value are valid wilcards and allowed for the 648 * property type for which the field is searching 649 * 650 * @param inputField - attribute field instance for the field that is being searched 651 * @param searchPropertyValue - value given for field to search for 652 */ 653 protected void validateSearchParameterWildcardAndOperators(InputField inputField, String searchPropertyValue) { 654 if (StringUtils.isBlank(searchPropertyValue)) { 655 return; 656 } 657 658 // make sure a wildcard/operator is in the value 659 boolean found = false; 660 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 661 String queryCharacter = op.op(); 662 663 if (searchPropertyValue.contains(queryCharacter)) { 664 found = true; 665 } 666 } 667 668 if (!found) { 669 return; 670 } 671 672 String attributeLabel = inputField.getLabel(); 673 if ((LookupInputField.class.isAssignableFrom(inputField.getClass())) && (((LookupInputField) inputField) 674 .isDisableWildcardsAndOperators())) { 675 Object dataObjectExample = null; 676 try { 677 dataObjectExample = getDataObjectClass().newInstance(); 678 } catch (Exception e) { 679 LOG.error("Exception caught instantiating " + getDataObjectClass().getName(), e); 680 throw new RuntimeException("Cannot instantiate " + getDataObjectClass().getName(), e); 681 } 682 683 Class<?> propertyType = ObjectPropertyUtils.getPropertyType(getDataObjectClass(), 684 inputField.getPropertyName()); 685 if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || 686 TypeUtils.isTemporalClass(propertyType)) { 687 GlobalVariables.getMessageMap().putError(inputField.getPropertyName(), 688 RiceKeyConstants.ERROR_WILDCARDS_AND_OPERATORS_NOT_ALLOWED_ON_FIELD, attributeLabel); 689 } 690 691 if (TypeUtils.isStringClass(propertyType)) { 692 GlobalVariables.getMessageMap().putInfo(inputField.getPropertyName(), 693 RiceKeyConstants.INFO_WILDCARDS_AND_OPERATORS_TREATED_LITERALLY, attributeLabel); 694 } 695 } else { 696 if (getDataObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks( 697 getDataObjectClass(), inputField.getPropertyName())) { 698 if (!searchPropertyValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { 699 // encrypted values usually come from the DB, so we don't 700 // need to filter for wildcards 701 // wildcards are not allowed on restricted fields, because 702 // they are typically encrypted, and wildcard searches cannot be performed without 703 // decrypting every row, which is currently not supported by KRAD 704 705 GlobalVariables.getMessageMap().putError(inputField.getPropertyName(), 706 RiceKeyConstants.ERROR_SECURE_FIELD, attributeLabel); 707 } 708 } 709 } 710 } 711 712 /** 713 * @see org.kuali.rice.krad.lookup.Lookupable#getReturnUrlForResults 714 */ 715 public void getReturnUrlForResults(Action returnLink, Object model) { 716 LookupForm lookupForm = (LookupForm) model; 717 718 Map<String, Object> returnLinkContext = returnLink.getContext(); 719 LookupView lookupView = returnLinkContext == null ? null : (LookupView) returnLinkContext 720 .get(UifConstants.ContextVariableNames.VIEW); 721 Object dataObject = returnLinkContext == null ? null : returnLinkContext 722 .get(UifConstants.ContextVariableNames.LINE); 723 724 // don't render return link if the object is null or if the row is not returnable 725 if ((dataObject == null) || (!isResultReturnable(dataObject))) { 726 returnLink.setRender(false); 727 return; 728 } 729 730 // build return link href (href may contain single quotes) 731 String href = getReturnUrl(lookupView, lookupForm, dataObject); 732 if (StringUtils.isBlank(href)) { 733 returnLink.setRender(false); 734 return; 735 } 736 737 // build return link label and title 738 String linkLabel = getConfigurationService().getPropertyValueAsString( 739 KRADConstants.Lookup.TITLE_RETURN_URL_PREPENDTEXT_PROPERTY); 740 returnLink.setActionLabel(linkLabel); 741 742 List<String> returnKeys = getReturnKeys(lookupView, lookupForm, dataObject); 743 List<String> secureReturnKeys = lookupView.getAdditionalSecurePropertyNames(); 744 Map<String, String> returnKeyValues = getPropertyKeyValuesFromDataObject(returnKeys, dataObject); 745 746 String title = LookupInquiryUtils.getLinkTitleText(linkLabel, getDataObjectClass(), returnKeyValues); 747 returnLink.setTitle(title); 748 749 // Add the return target if it is set 750 String returnTarget = lookupView.getReturnTarget(); 751 if (returnTarget != null) { 752 returnLink.setActionScript("window.open(\"" + href + "\", '" + returnTarget + "');"); 753 754 // Add the close script if lookup is in a light box 755 if (!returnTarget.equals("_self")) { 756 // Add the return script if the returnByScript flag is set 757 if (lookupView.isReturnByScript()) { 758 Properties props = getReturnUrlParameters(lookupView, lookupForm, dataObject); 759 760 StringBuilder script = new StringBuilder("e.preventDefault();"); 761 for (String returnField : lookupForm.getFieldConversions().values()) { 762 if (props.containsKey(returnField)) { 763 Object value = props.get(returnField); 764 script = script.append( 765 "returnLookupResultByScript(\"" + returnField + "\", '" + value + "');"); 766 } 767 } 768 returnLink.setActionScript(script.append("closeLightbox();").toString()); 769 } else { 770 // Close the light box if return target is not _self or _parent 771 returnLink.setActionScript("e.preventDefault();closeLightbox();showLoading();" + 772 "returnLookupResultReload(\"" + href + "\", '" + returnTarget + "');"); 773 } 774 } 775 } else { 776 // If no return target is set return in same frame 777 // This is to insure that non light box lookups return correctly 778 returnLink.setActionScript("window.open(\"" + href + "\", '_self');"); 779 } 780 } 781 782 /** 783 * Builds the URL for returning the given data object result row 784 * 785 * <p> 786 * Note return URL will only be built if a return location is specified on the <code>LookupForm</code> 787 * </p> 788 * 789 * @param lookupView - lookup view instance containing lookup configuration 790 * @param lookupForm - lookup form instance containing the data 791 * @param dataObject - data object instance for the current line and for which the return URL is being built 792 * @return String return URL or blank if URL cannot be built 793 */ 794 protected String getReturnUrl(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 795 Properties props = getReturnUrlParameters(lookupView, lookupForm, dataObject); 796 797 String href = ""; 798 if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) { 799 href = UrlFactory.parameterizeUrl(lookupForm.getReturnLocation(), props); 800 } 801 802 return href; 803 } 804 805 /** 806 * Builds up a <code>Properties</code> object that will be used to provide the request parameters for the 807 * return URL link 808 * 809 * @param lookupView - lookup view instance containing lookup configuration 810 * @param lookupForm - lookup form instance containing the data 811 * @param dataObject - data object instance for the current line and for which the return URL is being built 812 * @return Properties instance containing request parameters for return URL 813 */ 814 protected Properties getReturnUrlParameters(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 815 Properties props = new Properties(); 816 props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL); 817 818 if (StringUtils.isNotBlank(lookupForm.getReturnFormKey())) { 819 props.put(UifParameters.FORM_KEY, lookupForm.getReturnFormKey()); 820 } 821 822 props.put(KRADConstants.REFRESH_CALLER, lookupView.getId()); 823 props.put(KRADConstants.REFRESH_DATA_OBJECT_CLASS, getDataObjectClass().getName()); 824 825 if (StringUtils.isNotBlank(lookupForm.getDocNum())) { 826 props.put(UifParameters.DOC_NUM, lookupForm.getDocNum()); 827 } 828 829 if (StringUtils.isNotBlank(lookupForm.getReferencesToRefresh())) { 830 props.put(KRADConstants.REFERENCES_TO_REFRESH, lookupForm.getReferencesToRefresh()); 831 } 832 833 List<String> returnKeys = getReturnKeys(lookupView, lookupForm, dataObject); 834 List<String> secureReturnKeys = lookupView.getAdditionalSecurePropertyNames(); 835 Map<String, String> returnKeyValues = getPropertyKeyValuesFromDataObject(returnKeys, dataObject); 836 837 for (String returnKey : returnKeyValues.keySet()) { 838 String returnValue = returnKeyValues.get(returnKey); 839 if (lookupForm.getFieldConversions().containsKey(returnKey)) { 840 returnKey = lookupForm.getFieldConversions().get(returnKey); 841 } 842 843 props.put(returnKey, returnValue); 844 } 845 // props.put(UifParameters.AJAX_REQUEST,"false"); 846 return props; 847 } 848 849 /** 850 * <p>Returns the configured return key property names or if not configured defaults to the primary keys 851 * for the data object class 852 * </p> 853 * 854 * @param lookupView - lookup view instance containing lookup configuration 855 * @param lookupForm - lookup form instance containing the data 856 * @param dataObject - data object instance 857 * @return List<String> property names which should be passed back on the return URL 858 */ 859 protected List<String> getReturnKeys(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 860 List<String> returnKeys; 861 if (lookupForm.getFieldConversions() != null && !lookupForm.getFieldConversions().isEmpty()) { 862 returnKeys = new ArrayList<String>(lookupForm.getFieldConversions().keySet()); 863 } else { 864 returnKeys = getDataObjectMetaDataService().listPrimaryKeyFieldNames(getDataObjectClass()); 865 } 866 867 return returnKeys; 868 } 869 870 /** 871 * @see org.kuali.rice.krad.lookup.Lookupable#getMaintenanceActionLink 872 */ 873 public void getMaintenanceActionLink(Action actionLink, Object model, String maintenanceMethodToCall) { 874 LookupForm lookupForm = (LookupForm) model; 875 Map<String, Object> actionLinkContext = actionLink.getContext(); 876 Object dataObject = actionLinkContext == null ? null : actionLinkContext 877 .get(UifConstants.ContextVariableNames.LINE); 878 879 List<String> pkNames = getDataObjectMetaDataService().listPrimaryKeyFieldNames(getDataObjectClass()); 880 881 // build maintenance link href 882 String href = getActionUrlHref(lookupForm, dataObject, maintenanceMethodToCall, pkNames); 883 if (StringUtils.isBlank(href)) { 884 actionLink.setRender(false); 885 return; 886 } 887 // TODO: need to handle returning anchor 888 actionLink.setActionScript("window.open('" + href + "', '_self');"); 889 890 // build action title 891 String prependTitleText = actionLink.getActionLabel() + " " + 892 getDataDictionaryService().getDataDictionary().getDataObjectEntry(getDataObjectClass().getName()) 893 .getObjectLabel() + " " + 894 getConfigurationService().getPropertyValueAsString( 895 KRADConstants.Lookup.TITLE_ACTION_URL_PREPENDTEXT_PROPERTY); 896 897 Map<String, String> primaryKeyValues = getPropertyKeyValuesFromDataObject(pkNames, dataObject); 898 String title = LookupInquiryUtils.getLinkTitleText(prependTitleText, getDataObjectClass(), primaryKeyValues); 899 actionLink.setTitle(title); 900 lookupForm.setAtLeastOneRowHasActions(true); 901 } 902 903 /** 904 * Generates a URL to perform a maintenance action on the given result data object 905 * 906 * <p> 907 * Will build a URL containing keys of the data object to invoke the given maintenance action method 908 * within the maintenance controller 909 * </p> 910 * 911 * @param dataObject - data object instance for the line to build the maintenance action link for 912 * @param methodToCall - method name on the maintenance controller that should be invoked 913 * @param pkNames - list of primary key field names for the data object whose key/value pairs will be added to 914 * the maintenance link 915 * @return String URL link for the maintenance action 916 */ 917 protected String getActionUrlHref(LookupForm lookupForm, Object dataObject, String methodToCall, 918 List<String> pkNames) { 919 LookupView lookupView = (LookupView) lookupForm.getView(); 920 921 Properties props = new Properties(); 922 props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, methodToCall); 923 924 Map<String, String> primaryKeyValues = getPropertyKeyValuesFromDataObject(pkNames, dataObject); 925 for (String primaryKey : primaryKeyValues.keySet()) { 926 String primaryKeyValue = primaryKeyValues.get(primaryKey); 927 928 props.put(primaryKey, primaryKeyValue); 929 } 930 931 if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) { 932 props.put(KRADConstants.RETURN_LOCATION_PARAMETER, lookupForm.getReturnLocation()); 933 } 934 935 props.put(UifParameters.DATA_OBJECT_CLASS_NAME, lookupForm.getDataObjectClassName()); 936 props.put(UifParameters.VIEW_TYPE_NAME, UifConstants.ViewType.MAINTENANCE.name()); 937 938 String maintenanceMapping = KRADConstants.Maintenance.REQUEST_MAPPING_MAINTENANCE; 939 if (lookupView != null && StringUtils.isNotBlank(lookupView.getMaintenanceUrlMapping())) { 940 maintenanceMapping = lookupView.getMaintenanceUrlMapping(); 941 } 942 943 return UrlFactory.parameterizeUrl(maintenanceMapping, props); 944 } 945 946 /** 947 * Sets the value for the attribute field control to contain the field conversion values for the line 948 * 949 * @see org.kuali.rice.krad.lookup.LookupableImpl#setMultiValueLookupSelect 950 */ 951 @Override 952 public void setMultiValueLookupSelect(InputField selectField, Object model) { 953 LookupForm lookupForm = (LookupForm) model; 954 Map<String, Object> selectFieldContext = selectField.getContext(); 955 Object lineDataObject = selectFieldContext == null ? null : selectFieldContext 956 .get(UifConstants.ContextVariableNames.LINE); 957 if (lineDataObject == null) { 958 throw new RuntimeException("Unable to get data object for line from component: " + selectField.getId()); 959 } 960 961 Control selectControl = ((InputField) selectField).getControl(); 962 if ((selectControl != null) && (selectControl instanceof ValueConfiguredControl)) { 963 String lineIdentifier = ""; 964 965 // get value for each field conversion from line and add to lineIdentifier 966 Map<String, String> fieldConversions = lookupForm.getFieldConversions(); 967 List<String> fromFieldNames = new ArrayList<String>(fieldConversions.keySet()); 968 Collections.sort(fromFieldNames); 969 for (String fromFieldName : fromFieldNames) { 970 Object fromFieldValue = ObjectPropertyUtils.getPropertyValue(lineDataObject, fromFieldName); 971 if (fromFieldValue != null) { 972 lineIdentifier += fromFieldValue; 973 } 974 lineIdentifier += ":"; 975 } 976 lineIdentifier = StringUtils.removeEnd(lineIdentifier, ":"); 977 978 ((ValueConfiguredControl) selectControl).setValue(lineIdentifier); 979 } 980 } 981 982 /** 983 * Determines if given data object has associated maintenance document that allows new or copy 984 * maintenance 985 * actions 986 * 987 * @return boolean true if the maintenance new or copy action is allowed for the data object instance, false 988 * otherwise 989 */ 990 public boolean allowsMaintenanceNewOrCopyAction() { 991 boolean allowsNewOrCopy = false; 992 993 String maintDocTypeName = getMaintenanceDocumentTypeName(); 994 if (StringUtils.isNotBlank(maintDocTypeName)) { 995 allowsNewOrCopy = getDataObjectAuthorizationService().canCreate(getDataObjectClass(), 996 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 997 } 998 999 return allowsNewOrCopy; 1000 } 1001 1002 /** 1003 * Determines if given data object has associated maintenance document that allows edit maintenance 1004 * actions 1005 * 1006 * @return boolean true if the maintenance edit action is allowed for the data object instance, false otherwise 1007 */ 1008 public boolean allowsMaintenanceEditAction(Object dataObject) { 1009 boolean allowsEdit = false; 1010 1011 String maintDocTypeName = getMaintenanceDocumentTypeName(); 1012 if (StringUtils.isNotBlank(maintDocTypeName)) { 1013 allowsEdit = getDataObjectAuthorizationService().canMaintain(dataObject, 1014 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 1015 } 1016 1017 return allowsEdit; 1018 } 1019 1020 /** 1021 * Determines if given data object has associated maintenance document that allows delete maintenance 1022 * actions. 1023 * 1024 * @return boolean true if the maintenance delete action is allowed for the data object instance, false otherwise 1025 */ 1026 public boolean allowsMaintenanceDeleteAction(Object dataObject) { 1027 boolean allowsMaintain = false; 1028 boolean allowsDelete = false; 1029 1030 String maintDocTypeName = getMaintenanceDocumentTypeName(); 1031 if (StringUtils.isNotBlank(maintDocTypeName)) { 1032 allowsMaintain = getDataObjectAuthorizationService().canMaintain(dataObject, 1033 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 1034 } 1035 1036 allowsDelete = getDocumentDictionaryService().getAllowsRecordDeletion(getDataObjectClass()); 1037 1038 return allowsDelete && allowsMaintain; 1039 } 1040 1041 /** 1042 * Returns the maintenance document type associated with the business object class or null if one does not exist. 1043 * 1044 * @return String representing the maintenance document type name 1045 */ 1046 protected String getMaintenanceDocumentTypeName() { 1047 DocumentDictionaryService dd = getDocumentDictionaryService(); 1048 String maintDocTypeName = dd.getMaintenanceDocumentTypeName(getDataObjectClass()); 1049 1050 return maintDocTypeName; 1051 } 1052 1053 /** 1054 * Determines whether a given data object that's returned as one of the lookup's results is considered returnable, 1055 * which means that for single-value lookups, a "return value" link may be rendered, and for multiple 1056 * value lookups, a checkbox is rendered. 1057 * 1058 * Note that this can be part of an authorization mechanism, but not the complete authorization mechanism. The 1059 * component that invoked the lookup/ lookup caller (e.g. document, nesting lookup, etc.) needs to check 1060 * that the object that was passed to it was returnable as well because there are ways around this method 1061 * (e.g. crafting a custom return URL). 1062 * 1063 * @param dataObject - an object from the search result set 1064 * @return true if the row is returnable and false if it is not 1065 */ 1066 protected boolean isResultReturnable(Object dataObject) { 1067 return true; 1068 } 1069 1070 /** 1071 * @see org.kuali.rice.krad.lookup.Lookupable#setDataObjectClass 1072 */ 1073 @Override 1074 public void setDataObjectClass(Class<?> dataObjectClass) { 1075 this.dataObjectClass = dataObjectClass; 1076 } 1077 1078 /** 1079 * @see org.kuali.rice.krad.lookup.Lookupable#getDataObjectClass 1080 */ 1081 @Override 1082 public Class<?> getDataObjectClass() { 1083 return this.dataObjectClass; 1084 } 1085 1086 public void setConfigurationService(ConfigurationService configurationService) { 1087 this.configurationService = configurationService; 1088 } 1089 1090 protected DataObjectAuthorizationService getDataObjectAuthorizationService() { 1091 if (dataObjectAuthorizationService == null) { 1092 this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService(); 1093 } 1094 return dataObjectAuthorizationService; 1095 } 1096 1097 public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) { 1098 this.dataObjectAuthorizationService = dataObjectAuthorizationService; 1099 } 1100 1101 protected DataObjectMetaDataService getDataObjectMetaDataService() { 1102 if (dataObjectMetaDataService == null) { 1103 this.dataObjectMetaDataService = KRADServiceLocatorWeb.getDataObjectMetaDataService(); 1104 } 1105 return dataObjectMetaDataService; 1106 } 1107 1108 public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) { 1109 this.dataObjectMetaDataService = dataObjectMetaDataService; 1110 } 1111 1112 public DocumentDictionaryService getDocumentDictionaryService() { 1113 if (documentDictionaryService == null) { 1114 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 1115 } 1116 return documentDictionaryService; 1117 } 1118 1119 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 1120 this.documentDictionaryService = documentDictionaryService; 1121 } 1122 1123 protected LookupService getLookupService() { 1124 if (lookupService == null) { 1125 this.lookupService = KRADServiceLocatorWeb.getLookupService(); 1126 } 1127 return lookupService; 1128 } 1129 1130 public void setLookupService(LookupService lookupService) { 1131 this.lookupService = lookupService; 1132 } 1133 1134 protected EncryptionService getEncryptionService() { 1135 if (encryptionService == null) { 1136 this.encryptionService = CoreApiServiceLocator.getEncryptionService(); 1137 } 1138 return encryptionService; 1139 } 1140 1141 public void setEncryptionService(EncryptionService encryptionService) { 1142 this.encryptionService = encryptionService; 1143 } 1144 1145 //TODO: This code snippet is from KRADUtils of Rice 2.3.2 1146 public Map<String, String> getPropertyKeyValuesFromDataObject(List<String> propertyNames, Object dataObject) { 1147 Map<String, String> propertyKeyValues = new HashMap<String, String>(); 1148 1149 if (dataObject == null) { 1150 return propertyKeyValues; 1151 } 1152 1153 // iterate through properties and add a map entry for each 1154 for (String propertyName : propertyNames) { 1155 Object propertyValue = ObjectPropertyUtils.getPropertyValue(dataObject, propertyName); 1156 if (propertyValue == null) { 1157 propertyValue = StringUtils.EMPTY; 1158 } 1159 1160 if (KRADServiceLocatorWeb.getDataObjectAuthorizationService() 1161 .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObject.getClass(), propertyName)) { 1162 try { 1163 if (CoreApiServiceLocator.getEncryptionService().isEnabled()) { 1164 propertyValue = CoreApiServiceLocator.getEncryptionService().encrypt(propertyValue) 1165 + EncryptionService.ENCRYPTION_POST_PREFIX; 1166 } 1167 } catch (GeneralSecurityException e) { 1168 throw new RuntimeException("Exception while trying to encrypt value for key/value map.", e); 1169 } 1170 } 1171 1172 // TODO: need to apply formatting to return value once util class is ready 1173 propertyKeyValues.put(propertyName, propertyValue.toString()); 1174 } 1175 1176 return propertyKeyValues; 1177 } 1178}