001/** 002 * Copyright 2005-2018 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 */ 016package org.kuali.rice.kns.lookup; 017 018import java.security.GeneralSecurityException; 019import java.sql.Date; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Properties; 028import java.util.Set; 029 030import org.apache.commons.lang.StringUtils; 031import org.kuali.rice.core.api.CoreApiServiceLocator; 032import org.kuali.rice.core.api.config.property.ConfigContext; 033import org.kuali.rice.core.api.config.property.ConfigurationService; 034import org.kuali.rice.core.api.encryption.EncryptionService; 035import org.kuali.rice.core.api.mo.common.GloballyUnique; 036import org.kuali.rice.core.api.search.SearchOperator; 037import org.kuali.rice.core.api.util.RiceKeyConstants; 038import org.kuali.rice.core.api.util.cache.CopiedObject; 039import org.kuali.rice.core.api.util.type.TypeUtils; 040import org.kuali.rice.core.web.format.DateFormatter; 041import org.kuali.rice.core.web.format.Formatter; 042import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 043import org.kuali.rice.coreservice.framework.parameter.ParameterService; 044import org.kuali.rice.kim.api.identity.Person; 045import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions; 046import org.kuali.rice.kns.document.authorization.FieldRestriction; 047import org.kuali.rice.kns.inquiry.Inquirable; 048import org.kuali.rice.kns.service.BusinessObjectAuthorizationService; 049import org.kuali.rice.kns.service.BusinessObjectDictionaryService; 050import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 051import org.kuali.rice.kns.service.KNSServiceLocator; 052import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService; 053import org.kuali.rice.kns.util.FieldUtils; 054import org.kuali.rice.kns.util.KNSConstants; 055import org.kuali.rice.kns.util.WebUtils; 056import org.kuali.rice.kns.web.comparator.CellComparatorHelper; 057import org.kuali.rice.kns.web.struts.form.LookupForm; 058import org.kuali.rice.kns.web.struts.form.MultipleValueLookupForm; 059import org.kuali.rice.kns.web.ui.Column; 060import org.kuali.rice.kns.web.ui.Field; 061import org.kuali.rice.kns.web.ui.ResultRow; 062import org.kuali.rice.kns.web.ui.Row; 063import org.kuali.rice.krad.bo.BusinessObject; 064import org.kuali.rice.krad.datadictionary.AttributeSecurity; 065import org.kuali.rice.krad.datadictionary.mask.MaskFormatter; 066import org.kuali.rice.krad.exception.ValidationException; 067import org.kuali.rice.krad.service.BusinessObjectService; 068import org.kuali.rice.krad.service.DataDictionaryService; 069import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 070import org.kuali.rice.krad.service.LookupService; 071import org.kuali.rice.krad.service.PersistenceStructureService; 072import org.kuali.rice.krad.service.SequenceAccessorService; 073import org.kuali.rice.krad.util.GlobalVariables; 074import org.kuali.rice.krad.util.KRADConstants; 075import org.kuali.rice.krad.util.ObjectUtils; 076import org.kuali.rice.krad.util.UrlFactory; 077 078/** 079 * This class declares many of the common spring injected properties, the get/set-ers for them, 080 * and some common util methods that require the injected services 081 * 082 * @deprecated Use {@link org.kuali.rice.krad.lookup.LookupableImpl}. 083 */ 084@Deprecated 085public abstract class AbstractLookupableHelperServiceImpl implements LookupableHelperService { 086 087 protected static final String TITLE_RETURN_URL_PREPENDTEXT_PROPERTY = "title.return.url.value.prependtext"; 088 protected static final String TITLE_ACTION_URL_PREPENDTEXT_PROPERTY = "title.action.url.value.prependtext"; 089 protected static final String ACTION_URLS_CHILDREN_SEPARATOR = " | "; 090 protected static final String ACTION_URLS_CHILDREN_STARTER = " ["; 091 protected static final String ACTION_URLS_CHILDREN_END = "]"; 092 protected static final String ACTION_URLS_SEPARATOR = " "; 093 protected static final String ACTION_URLS_EMPTY = " "; 094 095 protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AbstractLookupableHelperServiceImpl.class); 096 097 protected Class businessObjectClass; 098 protected Map<String, String[]> parameters; 099 protected BusinessObjectDictionaryService businessObjectDictionaryService; 100 protected BusinessObjectMetaDataService businessObjectMetaDataService; 101 protected DataDictionaryService dataDictionaryService; 102 protected PersistenceStructureService persistenceStructureService; 103 protected EncryptionService encryptionService; 104 protected List<String> readOnlyFieldsList; 105 protected String backLocation; 106 protected String docFormKey; 107 protected Map fieldConversions; 108 protected LookupService lookupService; 109 protected List<Row> rows; 110 protected String referencesToRefresh; 111 protected SequenceAccessorService sequenceAccessorService; 112 protected BusinessObjectService businessObjectService; 113 protected LookupResultsService lookupResultsService; 114 protected String docNum; 115 protected ConfigurationService configurationService; 116 protected ParameterService parameterService; 117 protected BusinessObjectAuthorizationService businessObjectAuthorizationService; 118 119 /** 120 * @return the docNum 121 */ 122 public String getDocNum() { 123 return this.docNum; 124 } 125 126 /** 127 * @param docNum the docNum to set 128 */ 129 public void setDocNum(String docNum) { 130 this.docNum = docNum; 131 } 132 133 public AbstractLookupableHelperServiceImpl() { 134 rows = null; 135 } 136 137 /** 138 * This implementation always returns false. 139 * 140 * @see LookupableHelperService#checkForAdditionalFields(java.util.Map) 141 */ 142 public boolean checkForAdditionalFields(Map<String, String> fieldValues) { 143 return false; 144 } 145 146 /** 147 * @see LookupableHelperService#getBusinessObjectClass() 148 */ 149 public Class getBusinessObjectClass() { 150 return businessObjectClass; 151 } 152 153 /** 154 * @see LookupableHelperService#setBusinessObjectClass(java.lang.Class) 155 */ 156 public void setBusinessObjectClass(Class businessObjectClass) { 157 this.businessObjectClass = businessObjectClass; 158 setRows(); 159 } 160 161 /** 162 * @see LookupableHelperService#getParameters() 163 */ 164 public Map<String, String[]> getParameters() { 165 return parameters; 166 } 167 168 /** 169 * @see LookupableHelperService#setParameters(java.util.Map) 170 */ 171 public void setParameters(Map<String, String[]> parameters) { 172 this.parameters = parameters; 173 } 174 175 /** 176 * Gets the dataDictionaryService attribute. 177 * 178 * @return Returns the dataDictionaryService. 179 */ 180 public DataDictionaryService getDataDictionaryService() { 181 return dataDictionaryService != null ? dataDictionaryService : KRADServiceLocatorWeb.getDataDictionaryService(); 182 } 183 184 /** 185 * Sets the dataDictionaryService attribute value. 186 * 187 * @param dataDictionaryService The dataDictionaryService to set. 188 */ 189 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 190 this.dataDictionaryService = dataDictionaryService; 191 } 192 193 /** 194 * Gets the businessObjectDictionaryService attribute. 195 * 196 * @return Returns the businessObjectDictionaryService. 197 */ 198 public BusinessObjectDictionaryService getBusinessObjectDictionaryService() { 199 return businessObjectDictionaryService != null ? businessObjectDictionaryService : KNSServiceLocator 200 .getBusinessObjectDictionaryService(); 201 } 202 203 /** 204 * Sets the businessObjectDictionaryService attribute value. 205 * 206 * @param businessObjectDictionaryService 207 * The businessObjectDictionaryService to set. 208 */ 209 public void setBusinessObjectDictionaryService(BusinessObjectDictionaryService businessObjectDictionaryService) { 210 this.businessObjectDictionaryService = businessObjectDictionaryService; 211 } 212 213 /** 214 * Gets the businessObjectMetaDataService attribute. 215 * 216 * @return Returns the businessObjectMetaDataService. 217 */ 218 public BusinessObjectMetaDataService getBusinessObjectMetaDataService() { 219 return businessObjectMetaDataService != null ? businessObjectMetaDataService : KNSServiceLocator 220 .getBusinessObjectMetaDataService(); 221 } 222 223 /** 224 * Sets the businessObjectMetaDataService attribute value. 225 * 226 * @param businessObjectMetaDataService The businessObjectMetaDataService to set. 227 */ 228 public void setBusinessObjectMetaDataService(BusinessObjectMetaDataService businessObjectMetaDataService) { 229 this.businessObjectMetaDataService = businessObjectMetaDataService; 230 } 231 232 /** 233 * Gets the persistenceStructureService attribute. 234 * 235 * @return Returns the persistenceStructureService. 236 */ 237 protected PersistenceStructureService getPersistenceStructureService() { 238 return persistenceStructureService != null ? persistenceStructureService : KNSServiceLocator 239 .getPersistenceStructureService(); 240 } 241 242 /** 243 * Sets the persistenceStructureService attribute value. 244 * 245 * @param persistenceStructureService The persistenceStructureService to set. 246 */ 247 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { 248 this.persistenceStructureService = persistenceStructureService; 249 } 250 251 /** 252 * Gets the encryptionService attribute. 253 * 254 * @return Returns the encryptionService. 255 */ 256 protected EncryptionService getEncryptionService() { 257 return encryptionService != null ? encryptionService : CoreApiServiceLocator.getEncryptionService(); 258 } 259 260 /** 261 * Sets the encryptionService attribute value. 262 * 263 * @param encryptionService The encryptionService to set. 264 */ 265 public void setEncryptionService(EncryptionService encryptionService) { 266 this.encryptionService = encryptionService; 267 } 268 269 protected MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService; 270 271 public MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() { 272 if (maintenanceDocumentDictionaryService == null) { 273 maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService(); 274 } 275 return maintenanceDocumentDictionaryService; 276 } 277 278 279 public BusinessObjectAuthorizationService getBusinessObjectAuthorizationService() { 280 if (businessObjectAuthorizationService == null) { 281 businessObjectAuthorizationService = KNSServiceLocator.getBusinessObjectAuthorizationService(); 282 } 283 return businessObjectAuthorizationService; 284 } 285 286 protected Inquirable kualiInquirable; 287 288 public Inquirable getKualiInquirable() { 289 if (kualiInquirable == null) { 290 kualiInquirable = KNSServiceLocator.getKualiInquirable(); 291 } 292 return kualiInquirable; 293 } 294 295 public void setMaintenanceDocumentDictionaryService(MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService) { 296 this.maintenanceDocumentDictionaryService = maintenanceDocumentDictionaryService; 297 } 298 299 public void setKualiInquirable(Inquirable kualiInquirable) { 300 this.kualiInquirable = kualiInquirable; 301 } 302 303 304 public ConfigurationService getKualiConfigurationService() { 305 if (configurationService == null) { 306 configurationService = CoreApiServiceLocator.getKualiConfigurationService(); 307 } 308 return configurationService; 309 } 310 311 public void setParameterService(ConfigurationService configurationService) { 312 this.configurationService = configurationService; 313 } 314 315 316 public ParameterService getParameterService() { 317 if (parameterService == null) { 318 parameterService = CoreFrameworkServiceLocator.getParameterService(); 319 } 320 return parameterService; 321 } 322 323 public void setParameterService(ParameterService parameterService) { 324 this.parameterService = parameterService; 325 } 326 327 /** 328 * Determines if underlying lookup bo has associated maintenance document that allows new or copy maintenance actions. 329 * 330 * @return true if bo has maint doc that allows new or copy actions 331 */ 332 public boolean allowsMaintenanceNewOrCopyAction() { 333 boolean allowsNewOrCopy = false; 334 335 String maintDocTypeName = getMaintenanceDocumentTypeName(); 336 Class boClass = this.getBusinessObjectClass(); 337 338 if (StringUtils.isNotBlank(maintDocTypeName)) { 339 allowsNewOrCopy = getBusinessObjectAuthorizationService().canCreate(boClass, GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 340 } 341 return allowsNewOrCopy; 342 } 343 344 protected boolean allowsMaintenanceEditAction(BusinessObject businessObject) { 345 boolean allowsEdit = false; 346 347 String maintDocTypeName = getMaintenanceDocumentTypeName(); 348 349 if (StringUtils.isNotBlank(maintDocTypeName)) { 350 allowsEdit = getBusinessObjectAuthorizationService().canMaintain(businessObject, GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 351 } 352 return allowsEdit; 353 } 354 355 356 /** 357 * Build a maintenance url. 358 * 359 * @param bo - business object representing the record for maint. 360 * @param methodToCall - maintenance action 361 * @return 362 */ 363 final public String getMaintenanceUrl(BusinessObject businessObject, HtmlData htmlData, List pkNames, BusinessObjectRestrictions businessObjectRestrictions) { 364 htmlData.setTitle(getActionUrlTitleText(businessObject, htmlData.getDisplayText(), pkNames, businessObjectRestrictions)); 365 return htmlData.constructCompleteHtmlTag(); 366 } 367 368 /** 369 * This method is called by performLookup method to generate action urls. 370 * It calls the method getCustomActionUrls to get html data, calls getMaintenanceUrl to get the actual html tag, 371 * and returns a formatted/concatenated string of action urls. 372 * 373 * @see LookupableHelperService#getActionUrls(org.kuali.rice.krad.bo.BusinessObject) 374 */ 375 final public String getActionUrls(BusinessObject businessObject, List pkNames, BusinessObjectRestrictions businessObjectRestrictions) { 376 StringBuffer actions = new StringBuffer(); 377 List<HtmlData> htmlDataList = getCustomActionUrls(businessObject, pkNames); 378 for (HtmlData htmlData : htmlDataList) { 379 actions.append(getMaintenanceUrl(businessObject, htmlData, pkNames, businessObjectRestrictions)); 380 if (htmlData.getChildUrlDataList() != null) { 381 if (htmlData.getChildUrlDataList().size() > 0) { 382 actions.append(ACTION_URLS_CHILDREN_STARTER); 383 for (HtmlData childURLData : htmlData.getChildUrlDataList()) { 384 actions.append(getMaintenanceUrl(businessObject, childURLData, pkNames, businessObjectRestrictions)); 385 actions.append(ACTION_URLS_CHILDREN_SEPARATOR); 386 } 387 if (actions.toString().endsWith(ACTION_URLS_CHILDREN_SEPARATOR)) 388 actions.delete(actions.length() - ACTION_URLS_CHILDREN_SEPARATOR.length(), actions.length()); 389 actions.append(ACTION_URLS_CHILDREN_END); 390 } 391 } 392 actions.append(ACTION_URLS_SEPARATOR); 393 } 394 if (actions.toString().endsWith(ACTION_URLS_SEPARATOR)) 395 actions.delete(actions.length() - ACTION_URLS_SEPARATOR.length(), actions.length()); 396 return actions.toString(); 397 } 398 399 /** 400 * Child classes should override this method if they want to return some other action urls. 401 * 402 * @returns This default implementation returns links to edit and copy maintenance action for 403 * the current maintenance record if the business object class has an associated maintenance document. 404 * Also checks value of allowsNewOrCopy in maintenance document xml before rendering the copy link. 405 * @see LookupableHelperService#getCustomActionUrls(org.kuali.rice.krad.bo.BusinessObject, java.util.List, java.util.List pkNames) 406 */ 407 public List<HtmlData> getCustomActionUrls(BusinessObject businessObject, List pkNames) { 408 List<HtmlData> htmlDataList = new ArrayList<HtmlData>(); 409 if (allowsMaintenanceEditAction(businessObject)) { 410 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL, pkNames)); 411 } 412 if (allowsMaintenanceNewOrCopyAction()) { 413 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_COPY_METHOD_TO_CALL, pkNames)); 414 } 415 if (allowsMaintenanceDeleteAction(businessObject)) { 416 htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_DELETE_METHOD_TO_CALL, pkNames)); 417 } 418 return htmlDataList; 419 } 420 421 /** 422 * This method ... 423 * for KULRice 3070 424 * 425 * @return 426 */ 427 protected boolean allowsMaintenanceDeleteAction(BusinessObject businessObject) { 428 429 boolean allowsMaintain = false; 430 boolean allowsDelete = false; 431 432 String maintDocTypeName = getMaintenanceDocumentTypeName(); 433 434 if (StringUtils.isNotBlank(maintDocTypeName)) { 435 allowsMaintain = getBusinessObjectAuthorizationService().canMaintain(businessObject, GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 436 } 437 438 allowsDelete = KNSServiceLocator.getMaintenanceDocumentDictionaryService().getAllowsRecordDeletion(businessObjectClass); 439 440 return allowsDelete && allowsMaintain; 441 } 442 443 /** 444 * This method constructs an AnchorHtmlData. 445 * This method can be overriden by child classes if they want to construct the html data in a different way. 446 * Foe example, if they want different type of html tag, like input/image. 447 * 448 * @param businessObject 449 * @param methodToCall 450 * @param displayText 451 * @param pkNames 452 * @return 453 */ 454 protected HtmlData.AnchorHtmlData getUrlData(BusinessObject businessObject, String methodToCall, String displayText, List pkNames) { 455 456 String href = getActionUrlHref(businessObject, methodToCall, pkNames); 457 //String title = StringUtils.isBlank(href)?"":getActionUrlTitleText(businessObject, displayText, pkNames); 458 HtmlData.AnchorHtmlData anchorHtmlData = new HtmlData.AnchorHtmlData(href, methodToCall, displayText); 459 return anchorHtmlData; 460 } 461 462 /** 463 * This method calls its overloaded method with displayText as methodToCall 464 * 465 * @param businessObject 466 * @param methodToCall 467 * @param pkNames 468 * @return 469 */ 470 protected HtmlData.AnchorHtmlData getUrlData(BusinessObject businessObject, String methodToCall, List pkNames) { 471 return getUrlData(businessObject, methodToCall, methodToCall, pkNames); 472 } 473 474 /** 475 * A utility method that returns an empty list of action urls. 476 * 477 * @return 478 */ 479 protected List<HtmlData> getEmptyActionUrls() { 480 return new ArrayList<HtmlData>(); 481 } 482 483 protected HtmlData getEmptyAnchorHtmlData() { 484 return new HtmlData.AnchorHtmlData(); 485 } 486 487 /** 488 * This method generates and returns href for the given parameters. 489 * This method can be overridden by child classes if they have to generate href differently. 490 * For example, refer to IntendedIncumbentLookupableHelperServiceImpl 491 * 492 * @param businessObject 493 * @param methodToCall 494 * @param pkNames 495 * @return 496 */ 497 protected String getActionUrlHref(BusinessObject businessObject, String methodToCall, List pkNames) { 498 Properties parameters = new Properties(); 499 parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, methodToCall); 500 // TODO: why is this not using the businessObject parmeter's class? 501 parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, businessObject.getClass().getName()); 502 parameters.putAll(getParametersFromPrimaryKey(businessObject, pkNames)); 503 if (StringUtils.isNotBlank(getReturnLocation())) { 504 parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation()); 505 } 506 return UrlFactory.parameterizeUrl(KRADConstants.MAINTENANCE_ACTION, parameters); 507 } 508 509 protected Properties getParametersFromPrimaryKey(BusinessObject businessObject, List pkNames) { 510 Properties parameters = new Properties(); 511 for (Iterator iter = pkNames.iterator(); iter.hasNext();) { 512 String fieldNm = (String) iter.next(); 513 514 // If we cannot find the attribute in the data dictionary, then we cannot determine whether it should be encrypted 515 if (getDataDictionaryService().getAttributeDefinition(businessObjectClass.getName(), fieldNm) == null) { 516 String errorMessage = "The field " + fieldNm + " could not be found in the data dictionary for class " 517 + businessObjectClass.getName() + ", and thus it could not be determined whether it is a secure field."; 518 519 if (ConfigContext.getCurrentContextConfig().getBooleanProperty(KNSConstants.EXCEPTION_ON_MISSING_FIELD_CONVERSION_ATTRIBUTE, false)) { 520 throw new RuntimeException(errorMessage); 521 } else { 522 LOG.error(errorMessage); 523 continue; 524 } 525 } 526 527 Object fieldVal = ObjectUtils.getPropertyValue(businessObject, fieldNm); 528 if (fieldVal == null) { 529 fieldVal = KRADConstants.EMPTY_STRING; 530 } 531 if (fieldVal instanceof java.sql.Date) { 532 String formattedString = ""; 533 if (Formatter.findFormatter(fieldVal.getClass()) != null) { 534 Formatter formatter = Formatter.getFormatter(fieldVal.getClass()); 535 formattedString = (String) formatter.format(fieldVal); 536 fieldVal = formattedString; 537 } 538 } 539 540 // secure values are not passed in urls 541 if (getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, fieldNm)) { 542 LOG.warn("field name " + fieldNm + " is a secure value and not included in pk parameter results"); 543 continue; 544 } 545 546 parameters.put(fieldNm, fieldVal.toString()); 547 } 548 return parameters; 549 } 550 551 /** 552 * This method generates and returns title text for action urls. 553 * Child classes can override this if they want to generate the title text differently. 554 * For example, refer to BatchJobStatusLookupableHelperServiceImpl 555 * 556 * @param businessObject 557 * @param displayText 558 * @param pkNames 559 * @return 560 */ 561 protected String getActionUrlTitleText(BusinessObject businessObject, String displayText, List pkNames, BusinessObjectRestrictions businessObjectRestrictions) { 562 String prependTitleText = displayText + " " 563 + getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(getBusinessObjectClass().getName()).getObjectLabel() 564 + " " 565 + this.getKualiConfigurationService().getPropertyValueAsString(TITLE_ACTION_URL_PREPENDTEXT_PROPERTY); 566 return HtmlData.getTitleText(prependTitleText, businessObject, pkNames, businessObjectRestrictions); 567 } 568 569 /** 570 * Returns the maintenance document type associated with the business object class or null if one does not 571 * exist. 572 * 573 * @return String representing the maintenance document type name 574 */ 575 protected String getMaintenanceDocumentTypeName() { 576 MaintenanceDocumentDictionaryService dd = getMaintenanceDocumentDictionaryService(); 577 String maintDocTypeName = dd.getDocumentTypeName(getBusinessObjectClass()); 578 return maintDocTypeName; 579 } 580 581 /** 582 * Gets the readOnlyFieldsList attribute. 583 * 584 * @return Returns the readOnlyFieldsList. 585 */ 586 public List<String> getReadOnlyFieldsList() { 587 return readOnlyFieldsList; 588 } 589 590 591 /** 592 * Sets the readOnlyFieldsList attribute value. 593 * 594 * @param readOnlyFieldsList The readOnlyFieldsList to set. 595 */ 596 public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) { 597 this.readOnlyFieldsList = readOnlyFieldsList; 598 } 599 600 protected HashMap<String, Boolean> noLookupResultFieldInquiryCache = new HashMap<String, Boolean>(); 601 protected HashMap<Class, Class> inquirableClassCache = new HashMap<Class, Class>(); 602 protected HashMap<String, Boolean> forceLookupResultFieldInquiryCache = new HashMap<String, Boolean>(); 603 604 /** 605 * Returns the inquiry url for a field if one exist. 606 * 607 * @param bo the business object instance to build the urls for 608 * @param propertyName the property which links to an inquirable 609 * @return String url to inquiry 610 */ 611 public HtmlData getInquiryUrl(BusinessObject bo, String propertyName) { 612 HtmlData inquiryUrl = new HtmlData.AnchorHtmlData(); 613 614 String cacheKey = bo.getClass().getName() + "." + propertyName; 615 Boolean noLookupResultFieldInquiry = noLookupResultFieldInquiryCache.get(cacheKey); 616 if (noLookupResultFieldInquiry == null) { 617 noLookupResultFieldInquiry = getBusinessObjectDictionaryService().noLookupResultFieldInquiry(bo.getClass(), propertyName); 618 if (noLookupResultFieldInquiry == null) { 619 noLookupResultFieldInquiry = Boolean.TRUE; 620 } 621 noLookupResultFieldInquiryCache.put(cacheKey, noLookupResultFieldInquiry); 622 } 623 if (!noLookupResultFieldInquiry) { 624 625 Class<Inquirable> inquirableClass = inquirableClassCache.get(bo.getClass()); 626 if (!inquirableClassCache.containsKey(bo.getClass())) { 627 inquirableClass = getBusinessObjectDictionaryService().getInquirableClass(bo.getClass()); 628 inquirableClassCache.put(bo.getClass(), inquirableClass); 629 } 630 Inquirable inq = null; 631 try { 632 if (inquirableClass != null) { 633 inq = inquirableClass.newInstance(); 634 } else { 635 inq = getKualiInquirable(); 636 if (LOG.isDebugEnabled()) { 637 LOG.debug("Default Inquirable Class: " + inq.getClass()); 638 } 639 } 640 Boolean forceLookupResultFieldInquiry = forceLookupResultFieldInquiryCache.get(cacheKey); 641 if (forceLookupResultFieldInquiry == null) { 642 forceLookupResultFieldInquiry = getBusinessObjectDictionaryService().forceLookupResultFieldInquiry(bo.getClass(), propertyName); 643 if (forceLookupResultFieldInquiry == null) { 644 forceLookupResultFieldInquiry = Boolean.FALSE; 645 } 646 forceLookupResultFieldInquiryCache.put(cacheKey, forceLookupResultFieldInquiry); 647 } 648 inquiryUrl = inq.getInquiryUrl(bo, propertyName, forceLookupResultFieldInquiry); 649 } catch (Exception ex) { 650 LOG.error("unable to create inquirable to get inquiry URL", ex); 651 } 652 } 653 654 return inquiryUrl; 655 } 656 657 protected CopiedObject<ArrayList<Column>> resultColumns = null; 658 659 /** 660 * Constructs the list of columns for the search results. All properties for the column objects come from the DataDictionary. 661 */ 662 public List<Column> getColumns() { 663 if (resultColumns == null) { 664 ArrayList<Column> columns = new ArrayList<Column>(); 665 for (String attributeName : getBusinessObjectDictionaryService().getLookupResultFieldNames(getBusinessObjectClass())) { 666 Column column = new Column(); 667 column.setPropertyName(attributeName); 668 String columnTitle = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), attributeName); 669 Boolean useShortLabel = getBusinessObjectDictionaryService().getLookupResultFieldUseShortLabel(businessObjectClass, attributeName); 670 if (useShortLabel != null && useShortLabel) { 671 columnTitle = getDataDictionaryService().getAttributeShortLabel(getBusinessObjectClass(), attributeName); 672 } 673 if (StringUtils.isBlank(columnTitle)) { 674 columnTitle = getDataDictionaryService().getCollectionLabel(getBusinessObjectClass(), attributeName); 675 } 676 column.setColumnTitle(columnTitle); 677 column.setMaxLength(getColumnMaxLength(attributeName)); 678 679 if (!businessObjectClass.isInterface()) { 680 try { 681 column.setFormatter(ObjectUtils.getFormatterWithDataDictionary(getBusinessObjectClass() 682 .newInstance(), attributeName)); 683 } catch (InstantiationException e) { 684 LOG.info("Unable to get new instance of business object class: " + businessObjectClass.getName(), e); 685 // just swallow exception and leave formatter blank 686 } catch (IllegalAccessException e) { 687 LOG.info("Unable to get new instance of business object class: " + businessObjectClass.getName(), e); 688 // just swallow exception and leave formatter blank 689 } 690 } 691 692 String alternateDisplayPropertyName = getBusinessObjectDictionaryService() 693 .getLookupFieldAlternateDisplayAttributeName(getBusinessObjectClass(), attributeName); 694 if (StringUtils.isNotBlank(alternateDisplayPropertyName)) { 695 column.setAlternateDisplayPropertyName(alternateDisplayPropertyName); 696 } 697 698 String additionalDisplayPropertyName = getBusinessObjectDictionaryService() 699 .getLookupFieldAdditionalDisplayAttributeName(getBusinessObjectClass(), attributeName); 700 if (StringUtils.isNotBlank(additionalDisplayPropertyName)) { 701 column.setAdditionalDisplayPropertyName(additionalDisplayPropertyName); 702 } else { 703 boolean translateCodes = getBusinessObjectDictionaryService().tranlateCodesInLookup(getBusinessObjectClass()); 704 if (translateCodes) { 705 FieldUtils.setAdditionalDisplayPropertyForCodes(getBusinessObjectClass(), attributeName, column); 706 } 707 } 708 709 column.setTotal(getBusinessObjectDictionaryService().getLookupResultFieldTotal(getBusinessObjectClass(), attributeName)); 710 711 columns.add(column); 712 } 713 resultColumns = ObjectUtils.deepCopyForCaching(columns); 714 return columns; 715 } 716 return resultColumns.getContent(); 717 } 718 719 protected static Integer RESULTS_DEFAULT_MAX_COLUMN_LENGTH = null; 720 721 protected int getColumnMaxLength(String attributeName) { 722 Integer fieldDefinedMaxLength = getBusinessObjectDictionaryService().getLookupResultFieldMaxLength(getBusinessObjectClass(), attributeName); 723 if (fieldDefinedMaxLength == null) { 724 if (RESULTS_DEFAULT_MAX_COLUMN_LENGTH == null) { 725 try { 726 RESULTS_DEFAULT_MAX_COLUMN_LENGTH = Integer.valueOf(getParameterService().getParameterValueAsString( 727 KRADConstants.KNS_NAMESPACE, KRADConstants.DetailTypes.LOOKUP_PARM_DETAIL_TYPE, KRADConstants.RESULTS_DEFAULT_MAX_COLUMN_LENGTH)); 728 } catch (NumberFormatException ex) { 729 LOG.error("Lookup field max length parameter not found and unable to parse default set in system parameters (RESULTS_DEFAULT_MAX_COLUMN_LENGTH)."); 730 } 731 } 732 return RESULTS_DEFAULT_MAX_COLUMN_LENGTH.intValue(); 733 } 734 return fieldDefinedMaxLength.intValue(); 735 } 736 737 /** 738 * @return Returns the backLocation. 739 */ 740 public String getBackLocation() { 741 return WebUtils.sanitizeBackLocation(backLocation); 742 } 743 744 /** 745 * @param backLocation The backLocation to set. 746 */ 747 public void setBackLocation(String backLocation) { 748 this.backLocation = backLocation; 749 } 750 751 /** 752 * @see LookupableHelperService#getReturnLocation() 753 */ 754 public String getReturnLocation() { 755 return backLocation; 756 } 757 758 /** 759 * This method is for lookupable implementations 760 * 761 * @see LookupableHelperService#getReturnUrl(org.kuali.rice.krad.bo.BusinessObject, java.util.Map, java.lang.String, java.util.List) 762 */ 763 final public HtmlData getReturnUrl(BusinessObject businessObject, Map fieldConversions, String lookupImpl, List returnKeys, BusinessObjectRestrictions businessObjectRestrictions) { 764 String href = getReturnHref(businessObject, fieldConversions, lookupImpl, returnKeys); 765 String returnUrlAnchorLabel = 766 this.getKualiConfigurationService().getPropertyValueAsString(TITLE_RETURN_URL_PREPENDTEXT_PROPERTY); 767 HtmlData.AnchorHtmlData anchor = new HtmlData.AnchorHtmlData(href, HtmlData.getTitleText(returnUrlAnchorLabel, businessObject, returnKeys, businessObjectRestrictions)); 768 anchor.setDisplayText(returnUrlAnchorLabel); 769 return anchor; 770 } 771 772 /** 773 * This method is for lookupable implementations 774 * 775 * @param businessObject 776 * @param fieldConversions 777 * @param lookupImpl 778 * @param returnKeys 779 * @return 780 */ 781 final protected String getReturnHref(BusinessObject businessObject, Map fieldConversions, String lookupImpl, List returnKeys) { 782 if (StringUtils.isNotBlank(backLocation)) { 783 return UrlFactory.parameterizeUrl(backLocation, getParameters( 784 businessObject, fieldConversions, lookupImpl, returnKeys)); 785 } 786 return ""; 787 } 788 789 /** 790 * @see LookupableHelperService#getReturnUrl(org.kuali.core.bo.BusinessObject, java.util.Map, java.lang.String) 791 */ 792 public HtmlData getReturnUrl(BusinessObject businessObject, LookupForm lookupForm, List returnKeys, BusinessObjectRestrictions businessObjectRestrictions) { 793 Properties parameters = getParameters(businessObject, lookupForm.getFieldConversions(), 794 lookupForm.getLookupableImplServiceName(), returnKeys); 795 if (StringUtils.isEmpty(lookupForm.getHtmlDataType()) || HtmlData.ANCHOR_HTML_DATA_TYPE.equals(lookupForm.getHtmlDataType())) 796 return getReturnAnchorHtmlData(businessObject, parameters, lookupForm, returnKeys, businessObjectRestrictions); 797 else 798 return getReturnInputHtmlData(businessObject, parameters, lookupForm, returnKeys, businessObjectRestrictions); 799 } 800 801 protected HtmlData getReturnInputHtmlData(BusinessObject businessObject, Properties parameters, LookupForm lookupForm, List returnKeys, BusinessObjectRestrictions businessObjectRestrictions) { 802 String returnUrlAnchorLabel = 803 this.getKualiConfigurationService().getPropertyValueAsString(TITLE_RETURN_URL_PREPENDTEXT_PROPERTY); 804 String name = KRADConstants.MULTIPLE_VALUE_LOOKUP_SELECTED_OBJ_ID_PARAM_PREFIX + lookupForm.getLookupObjectId(); 805 HtmlData.InputHtmlData input = new HtmlData.InputHtmlData(name, HtmlData.InputHtmlData.CHECKBOX_INPUT_TYPE); 806 input.setTitle(HtmlData.getTitleText(returnUrlAnchorLabel, businessObject, returnKeys, businessObjectRestrictions)); 807 if (((MultipleValueLookupForm) lookupForm).getCompositeObjectIdMap() == null || 808 ((MultipleValueLookupForm) lookupForm).getCompositeObjectIdMap().get( 809 ((GloballyUnique) businessObject).getObjectId()) == null) { 810 input.setChecked(""); 811 } else { 812 input.setChecked(HtmlData.InputHtmlData.CHECKBOX_CHECKED_VALUE); 813 } 814 input.setValue(HtmlData.InputHtmlData.CHECKBOX_CHECKED_VALUE); 815 return input; 816 } 817 818 protected HtmlData getReturnAnchorHtmlData(BusinessObject businessObject, Properties parameters, LookupForm lookupForm, List returnKeys, BusinessObjectRestrictions businessObjectRestrictions) { 819 String returnUrlAnchorLabel = 820 this.getKualiConfigurationService().getPropertyValueAsString(TITLE_RETURN_URL_PREPENDTEXT_PROPERTY); 821 HtmlData.AnchorHtmlData anchor = new HtmlData.AnchorHtmlData( 822 getReturnHref(parameters, lookupForm, returnKeys), 823 HtmlData.getTitleText(returnUrlAnchorLabel, businessObject, returnKeys, businessObjectRestrictions)); 824 anchor.setDisplayText(returnUrlAnchorLabel); 825 return anchor; 826 } 827 828 protected String getReturnHref(Properties parameters, LookupForm lookupForm, List returnKeys) { 829 if (StringUtils.isNotBlank(backLocation)) { 830 String href = UrlFactory.parameterizeUrl(backLocation, parameters); 831 return addToReturnHref(href, lookupForm); 832 } 833 return ""; 834 } 835 836 protected String addToReturnHref(String href, LookupForm lookupForm) { 837 String lookupAnchor = ""; 838 if (StringUtils.isNotEmpty(lookupForm.getAnchor())) { 839 lookupAnchor = lookupForm.getAnchor(); 840 } 841 href += "&anchor=" + lookupAnchor + "&docNum=" + (StringUtils.isEmpty(getDocNum()) ? "" : getDocNum()); 842 return href; 843 } 844 845 protected Properties getParameters(BusinessObject bo, Map<String, String> fieldConversions, String lookupImpl, List returnKeys) { 846 Properties parameters = new Properties(); 847 parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL); 848 if (getDocFormKey() != null) { 849 parameters.put(KRADConstants.DOC_FORM_KEY, getDocFormKey()); 850 } 851 if (lookupImpl != null) { 852 parameters.put(KRADConstants.REFRESH_CALLER, lookupImpl); 853 } 854 if (getDocNum() != null) { 855 parameters.put(KRADConstants.DOC_NUM, getDocNum()); 856 } 857 858 if (getReferencesToRefresh() != null) { 859 parameters.put(KRADConstants.REFERENCES_TO_REFRESH, getReferencesToRefresh()); 860 } 861 862 Iterator returnKeysIt = getReturnKeys().iterator(); 863 while (returnKeysIt.hasNext()) { 864 String fieldNm = (String) returnKeysIt.next(); 865 866 // If we cannot find the attribute in the data dictionary, then we cannot determine whether it should be encrypted 867 if (getDataDictionaryService().getAttributeDefinition(businessObjectClass.getName(), fieldNm) == null) { 868 String errorMessage = "The field " + fieldNm + " could not be found in the data dictionary for class " 869 + businessObjectClass.getName() + ", and thus it could not be determined whether it is a secure field."; 870 871 if (ConfigContext.getCurrentContextConfig().getBooleanProperty(KNSConstants.EXCEPTION_ON_MISSING_FIELD_CONVERSION_ATTRIBUTE, false)) { 872 throw new RuntimeException(errorMessage); 873 } else { 874 LOG.error(errorMessage); 875 continue; 876 } 877 } 878 879 Object fieldVal = ObjectUtils.getPropertyValue(bo, fieldNm); 880 if (fieldVal == null) { 881 fieldVal = KRADConstants.EMPTY_STRING; 882 } 883 884 if (getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, fieldNm)) { 885 LOG.warn("field name " + fieldNm + " is a secure value and not included in parameter results"); 886 continue; 887 } 888 889 //need to format date in url 890 if (fieldVal instanceof Date) { 891 DateFormatter dateFormatter = new DateFormatter(); 892 fieldVal = dateFormatter.format(fieldVal); 893 } 894 895 if (fieldConversions.containsKey(fieldNm)) { 896 fieldNm = (String) fieldConversions.get(fieldNm); 897 } 898 899 parameters.put(fieldNm, fieldVal.toString()); 900 } 901 902 return parameters; 903 } 904 905 /** 906 * @return a List of the names of fields which are marked in data dictionary as return fields. 907 */ 908 public List<String> getReturnKeys() { 909 List<String> returnKeys; 910 if (fieldConversions != null && !fieldConversions.isEmpty()) { 911 returnKeys = new ArrayList<String>(fieldConversions.keySet()); 912 } else { 913 returnKeys = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(getBusinessObjectClass()); 914 } 915 916 return returnKeys; 917 } 918 919 /** 920 * Gets the docFormKey attribute. 921 * 922 * @return Returns the docFormKey. 923 */ 924 public String getDocFormKey() { 925 return docFormKey; 926 } 927 928 /** 929 * Sets the docFormKey attribute value. 930 * 931 * @param docFormKey The docFormKey to set. 932 */ 933 public void setDocFormKey(String docFormKey) { 934 this.docFormKey = docFormKey; 935 } 936 937 /** 938 * @see LookupableHelperService#setFieldConversions(java.util.Map) 939 */ 940 public void setFieldConversions(Map fieldConversions) { 941 this.fieldConversions = fieldConversions; 942 } 943 944 /** 945 * Gets the lookupService attribute. 946 * 947 * @return Returns the lookupService. 948 */ 949 protected LookupService getLookupService() { 950 return lookupService != null ? lookupService : KRADServiceLocatorWeb.getLookupService(); 951 } 952 953 /** 954 * Sets the lookupService attribute value. 955 * 956 * @param lookupService The lookupService to set. 957 */ 958 public void setLookupService(LookupService lookupService) { 959 this.lookupService = lookupService; 960 } 961 962 /** 963 * Uses the DD to determine which is the default sort order. 964 * 965 * @return property names that will be used to sort on by default 966 */ 967 public List<String> getDefaultSortColumns() { 968 return getBusinessObjectDictionaryService().getLookupDefaultSortFieldNames(getBusinessObjectClass()); 969 } 970 971 /** 972 * Checks that any required search fields have value. 973 * 974 * @see LookupableHelperService#validateSearchParameters(java.util.Map) 975 */ 976 public void validateSearchParameters(Map<String, String> fieldValues) { 977 List<String> lookupFieldAttributeList = null; 978 if (getBusinessObjectMetaDataService().isLookupable(getBusinessObjectClass())) { 979 lookupFieldAttributeList = getBusinessObjectMetaDataService().getLookupableFieldNames(getBusinessObjectClass()); 980 } 981 if (lookupFieldAttributeList == null) { 982 throw new RuntimeException("Lookup not defined for business object " + getBusinessObjectClass()); 983 } 984 for (Iterator iter = lookupFieldAttributeList.iterator(); iter.hasNext();) { 985 String attributeName = (String) iter.next(); 986 if (fieldValues.containsKey(attributeName)) { 987 // get label of attribute for message 988 String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), attributeName); 989 990 String attributeValue = (String) fieldValues.get(attributeName); 991 992 // check for required if field does not have value 993 if (StringUtils.isBlank(attributeValue)) { 994 if ((getBusinessObjectDictionaryService().getLookupAttributeRequired(getBusinessObjectClass(), attributeName)).booleanValue()) { 995 GlobalVariables.getMessageMap().putError(attributeName, RiceKeyConstants.ERROR_REQUIRED, attributeLabel); 996 } 997 } 998 validateSearchParameterWildcardAndOperators(attributeName, attributeValue); 999 } 1000 } 1001 1002 if (GlobalVariables.getMessageMap().hasErrors()) { 1003 throw new ValidationException("errors in search criteria"); 1004 } 1005 } 1006 1007 protected void validateSearchParameterWildcardAndOperators(String attributeName, String attributeValue) { 1008 if (StringUtils.isBlank(attributeValue)) 1009 return; 1010 1011 // make sure a wildcard/operator is in the value 1012 boolean found = false; 1013 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 1014 String queryCharacter = op.op(); 1015 1016 if (attributeValue.contains(queryCharacter)) { 1017 found = true; 1018 } 1019 } 1020 if (!found) 1021 return; 1022 1023 String attributeLabel = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), attributeName); 1024 if (getBusinessObjectDictionaryService().isLookupFieldTreatWildcardsAndOperatorsAsLiteral(businessObjectClass, attributeName)) { 1025 BusinessObject example = null; 1026 try { 1027 example = (BusinessObject) businessObjectClass.newInstance(); 1028 } catch (Exception e) { 1029 LOG.error("Exception caught instantiating " + businessObjectClass.getName(), e); 1030 throw new RuntimeException("Cannot instantiate " + businessObjectClass.getName(), e); 1031 } 1032 1033 Class propertyType = ObjectUtils.getPropertyType(example, attributeName, getPersistenceStructureService()); 1034 if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || TypeUtils.isTemporalClass(propertyType)) { 1035 GlobalVariables.getMessageMap().putError(attributeName, RiceKeyConstants.ERROR_WILDCARDS_AND_OPERATORS_NOT_ALLOWED_ON_FIELD, attributeLabel); 1036 } 1037 if (TypeUtils.isStringClass(propertyType)) { 1038 GlobalVariables.getMessageMap().putInfo(attributeName, RiceKeyConstants.INFO_WILDCARDS_AND_OPERATORS_TREATED_LITERALLY, attributeLabel); 1039 } 1040 } else { 1041 if (getBusinessObjectAuthorizationService().attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, attributeName)) { 1042 if (!attributeValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { 1043 // encrypted values usually come from the DB, so we don't need to filter for wildcards 1044 1045 // wildcards are not allowed on restricted fields, because they are typically encrypted, and wildcard searches cannot be performed without 1046 // decrypting every row, which is currently not supported by KNS 1047 1048 GlobalVariables.getMessageMap().putError(attributeName, RiceKeyConstants.ERROR_SECURE_FIELD, attributeLabel); 1049 } 1050 } 1051 } 1052 } 1053 1054 /** 1055 * Constructs the list of rows for the search fields. All properties for the field objects come 1056 * from the DataDictionary. To be called by setBusinessObject 1057 */ 1058 protected void setRows() { 1059 List<String> lookupFieldAttributeList = null; 1060 if (getBusinessObjectMetaDataService().isLookupable(getBusinessObjectClass())) { 1061 lookupFieldAttributeList = getBusinessObjectMetaDataService().getLookupableFieldNames( 1062 getBusinessObjectClass()); 1063 } 1064 if (lookupFieldAttributeList == null) { 1065 throw new RuntimeException("Lookup not defined for business object " + getBusinessObjectClass()); 1066 } 1067 1068 // construct field object for each search attribute 1069 List fields = new ArrayList(); 1070 try { 1071 fields = FieldUtils.createAndPopulateFieldsForLookup(lookupFieldAttributeList, getReadOnlyFieldsList(), 1072 getBusinessObjectClass()); 1073 } catch (InstantiationException e) { 1074 throw new RuntimeException("Unable to create instance of business object class" + e.getMessage()); 1075 } catch (IllegalAccessException e) { 1076 throw new RuntimeException("Unable to create instance of business object class" + e.getMessage()); 1077 } 1078 1079 int numCols = getBusinessObjectDictionaryService().getLookupNumberOfColumns(this.getBusinessObjectClass()); 1080 1081 this.rows = FieldUtils.wrapFields(fields, numCols); 1082 } 1083 1084 public List<Row> getRows() { 1085 return rows; 1086 } 1087 1088 public abstract List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues); 1089 1090 /** 1091 * This implementation of this method throws an UnsupportedOperationException, since not every implementation 1092 * may actually want to use this operation. Subclasses desiring other behaviors 1093 * will need to override this. 1094 * 1095 * @see LookupableHelperService#getSearchResultsUnbounded(java.util.Map) 1096 */ 1097 public List<? extends BusinessObject> getSearchResultsUnbounded(Map<String, String> fieldValues) { 1098 throw new UnsupportedOperationException("Lookupable helper services do not always support getSearchResultsUnbounded"); 1099 } 1100 1101 /** 1102 * Performs the lookup and returns a collection of lookup items 1103 * 1104 * @param lookupForm 1105 * @param resultTable 1106 * @param bounded 1107 * @return 1108 */ 1109 public Collection<? extends BusinessObject> performLookup(LookupForm lookupForm, Collection<ResultRow> resultTable, boolean bounded) { 1110 Map lookupFormFields = lookupForm.getFieldsForLookup(); 1111 1112 setBackLocation((String) lookupFormFields.get(KRADConstants.BACK_LOCATION)); 1113 setDocFormKey((String) lookupFormFields.get(KRADConstants.DOC_FORM_KEY)); 1114 Collection<? extends BusinessObject> displayList; 1115 1116 LookupUtils.preProcessRangeFields(lookupFormFields); 1117 1118 // call search method to get results 1119 if (bounded) { 1120 displayList = getSearchResults(lookupFormFields); 1121 } else { 1122 displayList = getSearchResultsUnbounded(lookupFormFields); 1123 } 1124 1125 boolean hasReturnableRow = false; 1126 1127 List<String> returnKeys = getReturnKeys(); 1128 List<String> pkNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(getBusinessObjectClass()); 1129 Person user = GlobalVariables.getUserSession().getPerson(); 1130 1131 // iterate through result list and wrap rows with return url and action 1132 // urls 1133 for (BusinessObject element : displayList) { 1134 BusinessObject baseElement = element; 1135 //if ebo, then use base BO to get lookupId and find restrictions 1136 //we don't need to do this anymore as the BO is required to implement the EBO interface as of this time 1137 //if this needs reimplemented in the future, one should consider what happens/needs to happen 1138 //with the base BO fields (OBJ ID in particular) as they are all null/empty on new instantiation 1139 //which will fail if we try to depend on any values within it. 1140 //KULRICE-7223 1141// if (ExternalizableBusinessObjectUtils.isExternalizableBusinessObject(element.getClass())) { 1142// try { 1143// baseElement = (BusinessObject)this.getBusinessObjectClass().newInstance(); 1144// } catch (InstantiationException e) { 1145// e.printStackTrace(); 1146// } catch (IllegalAccessException e) { 1147// e.printStackTrace(); 1148// } 1149// } 1150 1151 final String lookupId = KNSServiceLocator.getLookupResultsService().getLookupId(baseElement); 1152 if (lookupId != null) { 1153 lookupForm.setLookupObjectId(lookupId); 1154 } 1155 1156 BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService() 1157 .getLookupResultRestrictions(element, user); 1158 1159 HtmlData returnUrl = getReturnUrl(element, lookupForm, returnKeys, businessObjectRestrictions); 1160 String actionUrls = getActionUrls(element, pkNames, businessObjectRestrictions); 1161 // Fix for JIRA - KFSMI-2417 1162 if ("".equals(actionUrls)) { 1163 actionUrls = ACTION_URLS_EMPTY; 1164 } 1165 1166 List<Column> columns = getColumns(); 1167 for (Iterator iterator = columns.iterator(); iterator.hasNext();) { 1168 Column col = (Column) iterator.next(); 1169 1170 String propValue = ObjectUtils.getFormattedPropertyValue(element, col.getPropertyName(), col.getFormatter()); 1171 Class propClass = getPropertyClass(element, col.getPropertyName()); 1172 1173 col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass)); 1174 col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass)); 1175 1176 String propValueBeforePotientalMasking = propValue; 1177 propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, 1178 businessObjectRestrictions); 1179 col.setPropertyValue(propValue); 1180 1181 // if property value is masked, don't display additional or alternate properties, or allow totals 1182 if (StringUtils.equals(propValueBeforePotientalMasking, propValue)) { 1183 if (StringUtils.isNotBlank(col.getAlternateDisplayPropertyName())) { 1184 String alternatePropertyValue = ObjectUtils.getFormattedPropertyValue(element, col 1185 .getAlternateDisplayPropertyName(), null); 1186 col.setPropertyValue(alternatePropertyValue); 1187 } 1188 1189 if (StringUtils.isNotBlank(col.getAdditionalDisplayPropertyName())) { 1190 String additionalPropertyValue = ObjectUtils.getFormattedPropertyValue(element, col 1191 .getAdditionalDisplayPropertyName(), null); 1192 col.setPropertyValue(col.getPropertyValue() + " *-* " + additionalPropertyValue); 1193 } 1194 } else { 1195 col.setTotal(false); 1196 } 1197 1198 if (col.isTotal()) { 1199 Object unformattedPropValue = ObjectUtils.getPropertyValue(element, col.getPropertyName()); 1200 col.setUnformattedPropertyValue(unformattedPropValue); 1201 } 1202 1203 if (StringUtils.isNotBlank(propValue)) { 1204 col.setColumnAnchor(getInquiryUrl(element, col.getPropertyName())); 1205 } 1206 } 1207 1208 ResultRow row = new ResultRow(columns, returnUrl.constructCompleteHtmlTag(), actionUrls); 1209 row.setRowId(returnUrl.getName()); 1210 row.setReturnUrlHtmlData(returnUrl); 1211 1212 // because of concerns of the BO being cached in session on the 1213 // ResultRow, 1214 // let's only attach it when needed (currently in the case of 1215 // export) 1216 if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) { 1217 row.setBusinessObject(element); 1218 } 1219 1220 if (lookupId != null) { 1221 row.setObjectId(lookupId); 1222 } 1223 1224 boolean rowReturnable = isResultReturnable(element); 1225 row.setRowReturnable(rowReturnable); 1226 if (rowReturnable) { 1227 hasReturnableRow = true; 1228 } 1229 resultTable.add(row); 1230 } 1231 1232 lookupForm.setHasReturnableRow(hasReturnableRow); 1233 1234 return displayList; 1235 } 1236 1237 /** 1238 * Gets the Class for the property in the given BusinessObject instance, if 1239 * property is not accessible then runtime exception is thrown 1240 * 1241 * @param element BusinessObject instance that contains property 1242 * @param propertyName Name of property in BusinessObject to get class for 1243 * @return Type for property as Class 1244 */ 1245 protected Class getPropertyClass(BusinessObject element, String propertyName) { 1246 Class propClass = null; 1247 1248 try { 1249 propClass = ObjectUtils.getPropertyType(element, propertyName, getPersistenceStructureService()); 1250 1251 } catch (Exception e) { 1252 throw new RuntimeException("Cannot access PropertyType for property " + "'" + propertyName + "' " 1253 + " on an instance of '" + element.getClass().getName() + "'.", e); 1254 } 1255 1256 return propClass; 1257 } 1258 1259 1260 1261 protected String maskValueIfNecessary(Class businessObjectClass, String propertyName, String propertyValue, BusinessObjectRestrictions businessObjectRestrictions) { 1262 String maskedPropertyValue = propertyValue; 1263 if (businessObjectRestrictions != null) { 1264 FieldRestriction fieldRestriction = businessObjectRestrictions.getFieldRestriction(propertyName); 1265 if (fieldRestriction != null && (fieldRestriction.isMasked() || fieldRestriction.isPartiallyMasked())) { 1266 maskedPropertyValue = fieldRestriction.getMaskFormatter().maskValue(propertyValue); 1267 } 1268 } 1269 return maskedPropertyValue; 1270 } 1271 1272 1273 protected void setReferencesToRefresh(String referencesToRefresh) { 1274 this.referencesToRefresh = referencesToRefresh; 1275 } 1276 1277 public String getReferencesToRefresh() { 1278 return referencesToRefresh; 1279 } 1280 1281 protected SequenceAccessorService getSequenceAccessorService() { 1282 return sequenceAccessorService != null ? sequenceAccessorService : KNSServiceLocator 1283 .getSequenceAccessorService(); 1284 } 1285 1286 public void setSequenceAccessorService(SequenceAccessorService sequenceAccessorService) { 1287 this.sequenceAccessorService = sequenceAccessorService; 1288 } 1289 1290 public BusinessObjectService getBusinessObjectService() { 1291 return businessObjectService != null ? businessObjectService : KNSServiceLocator.getBusinessObjectService(); 1292 } 1293 1294 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 1295 this.businessObjectService = businessObjectService; 1296 } 1297 1298 protected LookupResultsService getLookupResultsService() { 1299 return lookupResultsService != null ? lookupResultsService : KNSServiceLocator.getLookupResultsService(); 1300 } 1301 1302 public void setLookupResultsService(LookupResultsService lookupResultsService) { 1303 this.lookupResultsService = lookupResultsService; 1304 } 1305 1306 /** 1307 * @return false always, subclasses should override to do something smarter 1308 * @see LookupableHelperService#isSearchUsingOnlyPrimaryKeyValues() 1309 */ 1310 public boolean isSearchUsingOnlyPrimaryKeyValues() { 1311 // by default, this implementation returns false, as lookups may not necessarily support this 1312 return false; 1313 } 1314 1315 /** 1316 * Returns "N/A" 1317 * 1318 * @return "N/A" 1319 * @see LookupableHelperService#getPrimaryKeyFieldLabels() 1320 */ 1321 public String getPrimaryKeyFieldLabels() { 1322 return KRADConstants.NOT_AVAILABLE_STRING; 1323 } 1324 1325 /** 1326 * @see LookupableHelperService#isResultReturnable(org.kuali.core.bo.BusinessObject) 1327 */ 1328 public boolean isResultReturnable(BusinessObject object) { 1329 return true; 1330 } 1331 1332 /** 1333 * This method does the logic for the clear action. 1334 * 1335 * @see LookupableHelperService#performClear() 1336 */ 1337 public void performClear(LookupForm lookupForm) { 1338 for (Iterator iter = this.getRows().iterator(); iter.hasNext();) { 1339 Row row = (Row) iter.next(); 1340 for (Iterator iterator = row.getFields().iterator(); iterator.hasNext();) { 1341 Field field = (Field) iterator.next(); 1342 if (field.isSecure()) { 1343 field.setSecure(false); 1344 field.setDisplayMaskValue(null); 1345 field.setEncryptedValue(null); 1346 } 1347 1348 if (!field.getFieldType().equals(Field.RADIO)) { 1349 field.setPropertyValue(field.getDefaultValue()); 1350 if (field.getFieldType().equals(Field.MULTISELECT)) { 1351 field.setPropertyValues(null); 1352 } 1353 } 1354 } 1355 } 1356 } 1357 1358 /** 1359 * @see LookupableHelperService#shouldDisplayHeaderNonMaintActions() 1360 */ 1361 public boolean shouldDisplayHeaderNonMaintActions() { 1362 return true; 1363 } 1364 1365 /** 1366 * @see LookupableHelperService#shouldDisplayLookupCriteria() 1367 */ 1368 public boolean shouldDisplayLookupCriteria() { 1369 return true; 1370 } 1371 1372 /** 1373 * @see LookupableHelperService#getSupplementalMenuBar() 1374 */ 1375 public String getSupplementalMenuBar() { 1376 return new String(); 1377 } 1378 1379 /** 1380 * @see LookupableHelperService#getTitle() 1381 */ 1382 public String getTitle() { 1383 return getBusinessObjectDictionaryService().getLookupTitle(getBusinessObjectClass()); 1384 } 1385 1386 /** 1387 * @see LookupableHelperService#performCustomAction(boolean) 1388 */ 1389 public boolean performCustomAction(boolean ignoreErrors) { 1390 return false; 1391 } 1392 1393 /** 1394 * @see Lookupable#getExtraField() 1395 */ 1396 public Field getExtraField() { 1397 return null; 1398 } 1399 1400 public boolean allowsNewOrCopyAction(String documentTypeName) { 1401 throw new UnsupportedOperationException("Function not supported."); 1402 } 1403 1404 /** 1405 * Functional requirements state that users are able to perform searches using criteria values that they are not allowed to view. 1406 * 1407 * @see LookupableHelperService#applyFieldAuthorizationsFromNestedLookups(org.kuali.rice.krad.web.ui.Field) 1408 */ 1409 public void applyFieldAuthorizationsFromNestedLookups(Field field) { 1410 BusinessObjectAuthorizationService boAuthzService = this.getBusinessObjectAuthorizationService(); 1411 if (!Field.MULTI_VALUE_FIELD_TYPES.contains(field.getFieldType())) { 1412 if (field.getPropertyValue() != null && field.getPropertyValue().endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) { 1413 if (boAuthzService.attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, field.getPropertyName())) { 1414 AttributeSecurity attributeSecurity = getDataDictionaryService().getAttributeSecurity(businessObjectClass.getName(), field.getPropertyName()); 1415 Person user = GlobalVariables.getUserSession().getPerson(); 1416 String decryptedValue = ""; 1417 try { 1418 String cipherText = StringUtils.removeEnd(field.getPropertyValue(), EncryptionService.ENCRYPTION_POST_PREFIX); 1419 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 1420 decryptedValue = getEncryptionService().decrypt(cipherText); 1421 } 1422 } catch (GeneralSecurityException e) { 1423 throw new RuntimeException("Error decrypting value for business object " + businessObjectClass + " attribute " + field.getPropertyName(), e); 1424 } 1425 if (attributeSecurity.isMask() && !boAuthzService.canFullyUnmaskField(user, 1426 businessObjectClass, field.getPropertyName(), null)) { 1427 MaskFormatter maskFormatter = attributeSecurity.getMaskFormatter(); 1428 field.setEncryptedValue(field.getPropertyValue()); 1429 field.setDisplayMaskValue(maskFormatter.maskValue(decryptedValue)); 1430 field.setSecure(true); 1431 } else if (attributeSecurity.isPartialMask() && !boAuthzService.canPartiallyUnmaskField(user, 1432 businessObjectClass, field.getPropertyName(), null)) { 1433 MaskFormatter maskFormatter = attributeSecurity.getPartialMaskFormatter(); 1434 field.setEncryptedValue(field.getPropertyValue()); 1435 field.setDisplayMaskValue(maskFormatter.maskValue(decryptedValue)); 1436 field.setSecure(true); 1437 } else { 1438 field.setPropertyValue(org.kuali.rice.krad.lookup.LookupUtils 1439 .forceUppercase(businessObjectClass, field.getPropertyName(), decryptedValue)); 1440 } 1441 } else { 1442 throw new RuntimeException("Field " + field.getPersonNameAttributeName() + " was encrypted on " + businessObjectClass.getName() + 1443 " lookup was encrypted when it should not have been encrypted according to the data dictionary."); 1444 } 1445 } 1446 } else { 1447 if (boAuthzService.attributeValueNeedsToBeEncryptedOnFormsAndLinks(businessObjectClass, field.getPropertyName())) { 1448 LOG.error("Cannot handle multiple value field types that have field authorizations, please implement custom lookupable helper service"); 1449 throw new RuntimeException("Cannot handle multiple value field types that have field authorizations."); 1450 } 1451 } 1452 } 1453 1454 /** 1455 * Calls methods that can be overridden by child lookupables to implement conditional logic for setting 1456 * read-only, required, and hidden attributes. Called in the last part of the lookup lifecycle so the 1457 * fields values that will be sent will be correctly reflected in the rows (like after a clear). 1458 * 1459 * @see #getConditionallyReadOnlyPropertyNames() 1460 * @see #getConditionallyRequiredPropertyNames() 1461 * @see #getConditionallyHiddenPropertyNames() 1462 * @see LookupableHelperService#applyConditionalLogicForFieldDisplay() 1463 */ 1464 public void applyConditionalLogicForFieldDisplay() { 1465 Set<String> readOnlyFields = getConditionallyReadOnlyPropertyNames(); 1466 Set<String> requiredFields = getConditionallyRequiredPropertyNames(); 1467 Set<String> hiddenFields = getConditionallyHiddenPropertyNames(); 1468 1469 for (Iterator iter = this.getRows().iterator(); iter.hasNext();) { 1470 Row row = (Row) iter.next(); 1471 for (Iterator iterator = row.getFields().iterator(); iterator.hasNext();) { 1472 Field field = (Field) iterator.next(); 1473 1474 if (readOnlyFields != null && readOnlyFields.contains(field.getPropertyName())) { 1475 field.setReadOnly(true); 1476 } 1477 1478 if (requiredFields != null && requiredFields.contains(field.getPropertyName())) { 1479 field.setFieldRequired(true); 1480 } 1481 1482 if (hiddenFields != null && hiddenFields.contains(field.getPropertyName())) { 1483 field.setFieldType(Field.HIDDEN); 1484 } 1485 } 1486 } 1487 } 1488 1489 /** 1490 * @return Set of property names that should be set as read only based on the current search 1491 * contents, note request parms containing search field values can be retrieved with 1492 * {@link #getParameters()} 1493 */ 1494 public Set<String> getConditionallyReadOnlyPropertyNames() { 1495 return new HashSet<String>(); 1496 } 1497 1498 /** 1499 * @return Set of property names that should be set as required based on the current search 1500 * contents, note request parms containing search field values can be retrieved with 1501 * {@link #getParameters()} 1502 */ 1503 public Set<String> getConditionallyRequiredPropertyNames() { 1504 return new HashSet<String>(); 1505 } 1506 1507 /** 1508 * @return Set of property names that should be set as hidden based on the current search 1509 * contents, note request parms containing search field values can be retrieved with 1510 * {@link #getParameters()} 1511 */ 1512 public Set<String> getConditionallyHiddenPropertyNames() { 1513 return new HashSet<String>(); 1514 } 1515 1516 /** 1517 * Helper method to get the value for a property out of the row-field graph. If property is 1518 * multi-value then the values will be joined by a semi-colon. 1519 * 1520 * @param propertyName - name of property to retrieve value for 1521 * @return current property value as a String 1522 */ 1523 protected String getCurrentSearchFieldValue(String propertyName) { 1524 String currentValue = null; 1525 1526 boolean fieldFound = false; 1527 for (Iterator iter = this.getRows().iterator(); iter.hasNext();) { 1528 Row row = (Row) iter.next(); 1529 for (Iterator iterator = row.getFields().iterator(); iterator.hasNext();) { 1530 Field field = (Field) iterator.next(); 1531 1532 if (StringUtils.equalsIgnoreCase(propertyName, field.getPropertyName())) { 1533 if (Field.MULTI_VALUE_FIELD_TYPES.contains(field.getFieldType())) { 1534 currentValue = StringUtils.join(field.getPropertyValues(), ";"); 1535 } else { 1536 currentValue = field.getPropertyValue(); 1537 } 1538 fieldFound = true; 1539 } 1540 1541 if (fieldFound) { 1542 break; 1543 } 1544 } 1545 1546 if (fieldFound) { 1547 break; 1548 } 1549 } 1550 1551 return currentValue; 1552 } 1553}