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.krad.lookup; 017 018import org.apache.commons.lang.BooleanUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.CoreApiServiceLocator; 021import org.kuali.rice.core.api.encryption.EncryptionService; 022import org.kuali.rice.core.api.search.SearchOperator; 023import org.kuali.rice.core.api.util.RiceKeyConstants; 024import org.kuali.rice.core.api.util.type.TypeUtils; 025import org.kuali.rice.krad.bo.ExternalizableBusinessObject; 026import org.kuali.rice.krad.datadictionary.BusinessObjectEntry; 027import org.kuali.rice.krad.datadictionary.DataObjectEntry; 028import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint; 029import org.kuali.rice.krad.service.DataObjectAuthorizationService; 030import org.kuali.rice.krad.service.DocumentDictionaryService; 031import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 032import org.kuali.rice.krad.service.LookupService; 033import org.kuali.rice.krad.service.ModuleService; 034import org.kuali.rice.krad.uif.UifConstants; 035import org.kuali.rice.krad.uif.UifParameters; 036import org.kuali.rice.krad.uif.UifPropertyPaths; 037import org.kuali.rice.krad.uif.control.Control; 038import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControl; 039import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControlPostData; 040import org.kuali.rice.krad.uif.control.HiddenControl; 041import org.kuali.rice.krad.uif.control.ValueConfiguredControl; 042import org.kuali.rice.krad.uif.element.Link; 043import org.kuali.rice.krad.uif.field.InputField; 044import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata; 045import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl; 046import org.kuali.rice.krad.uif.util.ComponentUtils; 047import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 048import org.kuali.rice.krad.uif.util.ScriptUtils; 049import org.kuali.rice.krad.util.BeanPropertyComparator; 050import org.kuali.rice.krad.util.ErrorMessage; 051import org.kuali.rice.krad.util.GlobalVariables; 052import org.kuali.rice.krad.util.KRADConstants; 053import org.kuali.rice.krad.util.KRADUtils; 054import org.kuali.rice.krad.util.MessageMap; 055import org.kuali.rice.krad.util.UrlFactory; 056 057import java.security.GeneralSecurityException; 058import java.util.ArrayList; 059import java.util.Collection; 060import java.util.Collections; 061import java.util.HashMap; 062import java.util.HashSet; 063import java.util.List; 064import java.util.Map; 065import java.util.Properties; 066import java.util.Set; 067import java.util.regex.Matcher; 068import java.util.regex.Pattern; 069 070/** 071 * View helper service that implements {@link Lookupable} and executes a search using the 072 * {@link org.kuali.rice.krad.service.LookupService}. 073 * 074 * @author Kuali Rice Team (rice.collab@kuali.org) 075 * @see LookupForm 076 * @see LookupView 077 * @see org.kuali.rice.krad.service.LookupService 078 */ 079public class LookupableImpl extends ViewHelperServiceImpl implements Lookupable { 080 private static final long serialVersionUID = 1885161468871327740L; 081 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupableImpl.class); 082 083 private Class<?> dataObjectClass; 084 085 private transient DataObjectAuthorizationService dataObjectAuthorizationService; 086 private transient DocumentDictionaryService documentDictionaryService; 087 private transient LookupService lookupService; 088 private transient EncryptionService encryptionService; 089 090 /** 091 * {@inheritDoc} 092 */ 093 @Override 094 public Collection<?> performSearch(LookupForm form, Map<String, String> searchCriteria, boolean bounded) { 095 // removed blank search values and decrypt any encrypted search values 096 Map<String, String> adjustedSearchCriteria = processSearchCriteria(form, searchCriteria); 097 098 boolean isValidCriteria = validateSearchParameters(form, adjustedSearchCriteria); 099 if (!isValidCriteria) { 100 return new ArrayList<Object>(); 101 } 102 103 List<String> wildcardAsLiteralSearchCriteria = identifyWildcardDisabledFields(form, adjustedSearchCriteria); 104 105 Integer searchResultsLimit = null; 106 if (bounded) { 107 searchResultsLimit = LookupUtils.getSearchResultsLimit(getDataObjectClass(), form); 108 } 109 110 // return empty search results (none found) when the search doesn't have any adjustedSearchCriteria although 111 // a filtered search criteria is specified 112 if (adjustedSearchCriteria == null) { 113 MessageMap messageMap = GlobalVariables.getMessageMap(); 114 messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 115 RiceKeyConstants.INFO_LOOKUP_RESULTS_NONE_FOUND); 116 return new ArrayList<Object>(); 117 } 118 119 Collection<?> searchResults = null; 120 121 // if this class is an EBO, call the module service to get the results, otherwise call the lookup search 122 if (ExternalizableBusinessObject.class.isAssignableFrom(getDataObjectClass())) { 123 searchResults = getSearchResultsForEBO(adjustedSearchCriteria, !bounded); 124 } else { 125 searchResults = getSearchResults(adjustedSearchCriteria, wildcardAsLiteralSearchCriteria, !bounded, 126 searchResultsLimit); 127 } 128 129 generateLookupResultsMessages(adjustedSearchCriteria, searchResults, bounded, searchResultsLimit); 130 131 Collection<?> sortedResults; 132 if (searchResults != null) { 133 sortedResults = new ArrayList<Object>(searchResults); 134 135 sortSearchResults(form, (List<?>) sortedResults); 136 } else { 137 sortedResults = new ArrayList<Object>(); 138 } 139 140 return sortedResults; 141 } 142 143 /** 144 * Invoked to execute the search with the given criteria and restrictions. 145 * 146 * @param adjustedSearchCriteria map of criteria that has been adjusted (encyrption, ebos, etc) 147 * @param wildcardAsLiteralSearchCriteria map of criteria to treat as literals (wildcards disabled) 148 * @param bounded indicates whether the search should be bounded 149 * @param searchResultsLimit for bounded searches, the result limit 150 * @return Collection<?> collection of data object instances from the search results 151 */ 152 protected Collection<?> executeSearch(Map<String, String> adjustedSearchCriteria, 153 List<String> wildcardAsLiteralSearchCriteria, boolean bounded, Integer searchResultsLimit) { 154 return getLookupService().findCollectionBySearchHelper(getDataObjectClass(), adjustedSearchCriteria, 155 wildcardAsLiteralSearchCriteria, !bounded, searchResultsLimit); 156 } 157 158 /** 159 * Filters the search criteria to be used with the lookup. 160 * 161 * <p>Processing entails primarily of the removal of filtered and unused/blank search criteria. Encrypted field 162 * values are decrypted, and date range fields are combined into a single criteria entry.</p> 163 * 164 * <p>In special cases additional non-valid criteria may be included. E.g. with the KIM User Control as a criteria 165 * the principal name may be passed so that it is displayed on the control. The filtering removes these values 166 * based on the viewPostMetadata. When calling the search directly (methodToCall=search) the viewPostMetadata is 167 * not set before filtering therefore non-valid criteria are not supported in these cases.</p> 168 * 169 * @param lookupForm lookup form instance containing the lookup data 170 * @param searchCriteria map of criteria to process 171 * @return map of processed criteria 172 */ 173 protected Map<String, String> processSearchCriteria(LookupForm lookupForm, Map<String, String> searchCriteria) { 174 Map<String, InputField> criteriaFields = new HashMap<String, InputField>(); 175 if (lookupForm.getView() != null) { 176 criteriaFields = getCriteriaFieldsForValidation((LookupView) lookupForm.getView(), lookupForm); 177 } 178 179 // combine date range criteria 180 Map<String, String> filteredSearchCriteria = LookupUtils.preprocessDateFields(searchCriteria); 181 182 // allow lookup inputs to filter the criteria 183 for (String fieldName : searchCriteria.keySet()) { 184 InputField inputField = criteriaFields.get(fieldName); 185 186 if ((inputField == null) || !(inputField instanceof LookupInputField)) { 187 continue; 188 } 189 190 LookupInputField lookupInputField = (LookupInputField) inputField; 191 String propertyName = lookupInputField.getPropertyName(); 192 193 // get the post data for the filterable controls 194 ViewPostMetadata viewPostMetadata = lookupForm.getViewPostMetadata(); 195 if (viewPostMetadata != null) { 196 Object componentPostData = viewPostMetadata.getComponentPostData(lookupForm.getViewId(), 197 UifConstants.PostMetadata.FILTERABLE_LOOKUP_CRITERIA); 198 Map<String, FilterableLookupCriteriaControlPostData> filterableLookupCriteria = 199 (Map<String, FilterableLookupCriteriaControlPostData>) componentPostData; 200 201 // first filter the results using the filter on the control 202 if (filterableLookupCriteria != null && filterableLookupCriteria.containsKey(propertyName)) { 203 FilterableLookupCriteriaControlPostData postData = filterableLookupCriteria.get(propertyName); 204 Class<? extends FilterableLookupCriteriaControl> controlClass = postData.getControlClass(); 205 FilterableLookupCriteriaControl control = KRADUtils.createNewObjectFromClass(controlClass); 206 207 filteredSearchCriteria = control.filterSearchCriteria(propertyName, filteredSearchCriteria, 208 postData); 209 } 210 211 // second filter the results using the filter in the input field 212 filteredSearchCriteria = lookupInputField.filterSearchCriteria(filteredSearchCriteria); 213 214 // early return if we have no results 215 if (filteredSearchCriteria == null) { 216 return null; 217 } 218 } 219 } 220 221 // decryption any encrypted search values 222 Map<String, String> processedSearchCriteria = new HashMap<String, String>(); 223 for (String fieldName : filteredSearchCriteria.keySet()) { 224 String fieldValue = filteredSearchCriteria.get(fieldName); 225 226 // do not add hidden or blank criteria 227 InputField inputField = criteriaFields.get(fieldName); 228 if (((inputField != null) && (inputField.getControl() instanceof HiddenControl)) || StringUtils.isBlank( 229 fieldValue)) { 230 continue; 231 } 232 233 // check security on field 234 boolean isSecure = KRADUtils.isSecure(fieldName, dataObjectClass); 235 236 if (StringUtils.endsWith(fieldValue, EncryptionService.ENCRYPTION_POST_PREFIX)) { 237 fieldValue = StringUtils.removeEnd(fieldValue, EncryptionService.ENCRYPTION_POST_PREFIX); 238 isSecure = true; 239 } 240 241 // decrypt if the value is secure 242 if (isSecure) { 243 try { 244 if (CoreApiServiceLocator.getEncryptionService().isEnabled()) { 245 fieldValue = getEncryptionService().decrypt(fieldValue); 246 } 247 } catch (GeneralSecurityException e) { 248 String message = "Data object class " + dataObjectClass + " property " + fieldName 249 + " should have been encrypted, but there was a problem decrypting it."; 250 LOG.error(message); 251 252 throw new RuntimeException(message, e); 253 } 254 } 255 256 processedSearchCriteria.put(fieldName, fieldValue); 257 } 258 259 return processedSearchCriteria; 260 } 261 262 /** 263 * Determines which searchCriteria have been configured with wildcard characters disabled. 264 * 265 * @param lookupForm form used to collect search criteria 266 * @param searchCriteria Map of property names and values to use as search parameters 267 * @return List of property names which have wildcard characters disabled 268 */ 269 protected List<String> identifyWildcardDisabledFields(LookupForm lookupForm, Map<String, String> searchCriteria) { 270 List<String> wildcardAsLiteralPropertyNames = new ArrayList<String>(); 271 272 if (searchCriteria != null) { 273 Map<String, InputField> criteriaFields = new HashMap<String, InputField>(); 274 if (lookupForm.getView() != null) { 275 criteriaFields = getCriteriaFieldsForValidation((LookupView) lookupForm.getView(), lookupForm); 276 } 277 278 for (String fieldName : searchCriteria.keySet()) { 279 InputField inputField = criteriaFields.get(fieldName); 280 if ((inputField == null) || !(inputField instanceof LookupInputField)) { 281 continue; 282 } 283 284 if ((LookupInputField.class.isAssignableFrom(inputField.getClass())) && (((LookupInputField) inputField) 285 .isDisableWildcardsAndOperators())) { 286 wildcardAsLiteralPropertyNames.add(fieldName); 287 } 288 } 289 } 290 291 return wildcardAsLiteralPropertyNames; 292 } 293 294 /** 295 * Invoked to perform validation on the search criteria before the search is performed. 296 * 297 * <li>Check required criteria have a value</li> 298 * <li>Check that criteria data type supports wildcards/operators</li> 299 * <li>Check that wildcards/operators are not used on a secure criteria</li> 300 * <li>Display info message when wildcards/operators are disabled</li> 301 * <li>Throw exception when invalid criteria are specified</li> 302 * 303 * @param form lookup form instance containing the lookup data 304 * @param searchCriteria map of criteria where key is search property name and value is 305 * search value (which can include wildcards) 306 * @return boolean true if validation was successful, false if there were errors and the search 307 * should not be performed 308 */ 309 protected boolean validateSearchParameters(LookupForm form, Map<String, String> searchCriteria) { 310 boolean valid = true; 311 312 if (searchCriteria == null) { 313 return valid; 314 } 315 316 // The form view can't be relied upon since the complete lifecycle hasn't ran against it. Instead 317 // the viewPostMetadata is being used for the validation. 318 // If the view was not previously posted then it's impossible to validate the search parameters because 319 // of the missing viewPostMetadata. When this happens we assume the search parameters are correct. 320 // (Calling the search controller method directly without displaying the lookup first can cause 321 // this situation.) 322 if (form.getViewPostMetadata() == null) { 323 return valid; 324 } 325 326 Set<String> unprocessedSearchCriteria = new HashSet<String>(searchCriteria.keySet()); 327 for (Map.Entry<String, Map<String, Object>> lookupCriteria : form.getViewPostMetadata().getLookupCriteria() 328 .entrySet()) { 329 String propertyName = lookupCriteria.getKey(); 330 Map<String, Object> lookupCriteriaAttributes = lookupCriteria.getValue(); 331 332 unprocessedSearchCriteria.remove(propertyName); 333 334 if (isCriteriaRequired(lookupCriteriaAttributes) && StringUtils.isBlank(searchCriteria.get(propertyName))) { 335 GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_REQUIRED, 336 getCriteriaLabel(form, (String) lookupCriteriaAttributes.get( 337 UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))); 338 } 339 340 ValidCharactersConstraint constraint = getSearchCriteriaConstraint(lookupCriteriaAttributes); 341 if (constraint != null) { 342 validateSearchParameterConstraint(form, propertyName, lookupCriteriaAttributes, searchCriteria.get( 343 propertyName), constraint); 344 } 345 346 if (searchCriteria.containsKey(propertyName)) { 347 validateSearchParameterWildcardAndOperators(form, propertyName, lookupCriteriaAttributes, 348 searchCriteria.get(propertyName)); 349 } 350 } 351 352 // Remove any unprocessedSearchCriteria that are marked as readOnly 353 for (String readOnlyItem : form.getReadOnlyFieldsList()) { 354 unprocessedSearchCriteria.remove(readOnlyItem); 355 } 356 357 if (!unprocessedSearchCriteria.isEmpty()) { 358 throw new RuntimeException( 359 "Invalid search value sent for property name(s): " + unprocessedSearchCriteria.toString()); 360 } 361 362 if (GlobalVariables.getMessageMap().hasErrors()) { 363 valid = false; 364 } 365 366 return valid; 367 } 368 369 /** 370 * Validates that any wildcards contained within the search value are valid wildcards and allowed for the 371 * property type for which the field is searching. 372 * 373 * @param form lookup form instance containing the lookup data 374 * @param propertyName property name of the search criteria field to be validated 375 * @param searchPropertyValue value given for field to search for 376 */ 377 protected void validateSearchParameterWildcardAndOperators(LookupForm form, String propertyName, 378 Map<String, Object> lookupCriteriaAttributes, String searchPropertyValue) { 379 if (StringUtils.isBlank(searchPropertyValue)) { 380 return; 381 } 382 383 // make sure a wildcard/operator is in the value 384 boolean found = false; 385 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 386 String queryCharacter = op.op(); 387 388 if (searchPropertyValue.contains(queryCharacter)) { 389 found = true; 390 } 391 } 392 393 // no query characters to validate 394 if (!found) { 395 return; 396 } 397 398 if (isCriteriaWildcardDisabled(lookupCriteriaAttributes)) { 399 Class<?> propertyType = ObjectPropertyUtils.getPropertyType(getDataObjectClass(), propertyName); 400 401 if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || 402 TypeUtils.isTemporalClass(propertyType)) { 403 GlobalVariables.getMessageMap().putError(propertyName, 404 RiceKeyConstants.ERROR_WILDCARDS_AND_OPERATORS_NOT_ALLOWED_ON_FIELD, getCriteriaLabel(form, (String) lookupCriteriaAttributes.get( 405 UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))); 406 } else if (TypeUtils.isStringClass(propertyType)) { 407 GlobalVariables.getMessageMap().putInfo(propertyName, 408 RiceKeyConstants.INFO_WILDCARDS_AND_OPERATORS_TREATED_LITERALLY, getCriteriaLabel(form, (String) lookupCriteriaAttributes.get( 409 UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))); 410 } 411 } else if (isCriteriaSecure(lookupCriteriaAttributes)) { 412 GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_SECURE_FIELD, 413 getCriteriaLabel(form, (String) lookupCriteriaAttributes.get( 414 UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))); 415 } 416 } 417 418 /** 419 * Validates that the searchPropertyValue is a valid value based on any constraint that may exist for the property 420 * 421 * @param form lookup form instance containing the lookup data 422 * @param propertyName property name of the search criteria field to be validated 423 * @param lookupCriteriaAttributes attributes for the lookup criteria 424 * @param searchPropertyValue value given for field to search for 425 * @param validCharactersConstraint constraint on the lookup criteria field 426 */ 427 protected void validateSearchParameterConstraint(LookupForm form, String propertyName, 428 Map<String, Object> lookupCriteriaAttributes, String searchPropertyValue, 429 ValidCharactersConstraint validCharactersConstraint) { 430 if (StringUtils.isBlank(searchPropertyValue)) { 431 return; 432 } 433 434 Matcher matcher = Pattern.compile(validCharactersConstraint.getValue()).matcher(searchPropertyValue); 435 if (!matcher.find()) { 436 String[] prefixParams = {getCriteriaLabel(form, (String) lookupCriteriaAttributes.get( 437 UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))}; 438 ErrorMessage errorMessage = new ErrorMessage(validCharactersConstraint.getMessageKey(), 439 validCharactersConstraint.getValidationMessageParamsArray()); 440 errorMessage.setMessagePrefixKey(UifConstants.Messages.PROPERTY_NAME_PREFIX); 441 errorMessage.setMessagePrefixParameters(prefixParams); 442 GlobalVariables.getMessageMap().putError(propertyName, errorMessage); 443 } 444 } 445 446 /** 447 * Returns the label of the search criteria field. 448 * 449 * @param form lookup form instance containing the lookup data 450 * @param componentId component id of the search criteria field 451 * @return label of the search criteria field 452 */ 453 protected String getCriteriaLabel(LookupForm form, String componentId) { 454 return (String) form.getViewPostMetadata().getComponentPostData(componentId, UifConstants.PostMetadata.LABEL); 455 } 456 457 /** 458 * Indicator if wildcards and operators are disabled on this search criteria. 459 * 460 * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field 461 * @return true if wildcards and operators are disabled, false otherwise 462 */ 463 protected boolean isCriteriaWildcardDisabled(Map lookupCriteria) { 464 return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.DISABLE_WILDCARDS_AND_OPERATORS)); 465 } 466 467 /** 468 * Indicator if the search criteria is required. 469 * 470 * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field 471 * @return true if the search criteria is required, false otherwise 472 */ 473 protected boolean isCriteriaRequired(Map lookupCriteria) { 474 return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.REQUIRED)); 475 } 476 477 /** 478 * Indicator if the search criteria is on a secure field. 479 * 480 * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field 481 * @return true if the search criteria is a secure field, false otherwise 482 */ 483 protected boolean isCriteriaSecure(Map lookupCriteria) { 484 return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.SECURE_VALUE)); 485 } 486 487 /** 488 * Indicator if the search criteria has a valid Characters Constraint. 489 * 490 * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field 491 * @return the ValidCharactersConstraint if the search criteria has a valid characters constraint 492 */ 493 protected ValidCharactersConstraint getSearchCriteriaConstraint(Map lookupCriteria) { 494 return (ValidCharactersConstraint) lookupCriteria.get( 495 UifConstants.LookupCriteriaPostMetadata.VALID_CHARACTERS_CONSTRAINT); 496 } 497 498 /** 499 * Generates messages for the user based on the search results. 500 * 501 * <p>Messages are generated for the number of results, if the results exceed the result limit, and if the 502 * search was done using the primary keys for the data object.</p> 503 * 504 * @param searchCriteria map of search criteria that was used for the search 505 * @param searchResults list of result data objects from the search 506 * @param bounded whether the search was bounded 507 * @param searchResultsLimit maximum number of search results to return 508 */ 509 protected void generateLookupResultsMessages(Map<String, String> searchCriteria, Collection<?> searchResults, 510 boolean bounded, Integer searchResultsLimit) { 511 MessageMap messageMap = GlobalVariables.getMessageMap(); 512 513 Long searchResultsSize = Long.valueOf(0); 514 if (searchResults instanceof CollectionIncomplete 515 && ((CollectionIncomplete<?>) searchResults).getActualSizeIfTruncated() > 0) { 516 searchResultsSize = ((CollectionIncomplete<?>) searchResults).getActualSizeIfTruncated(); 517 } else if (searchResults != null) { 518 searchResultsSize = Long.valueOf(searchResults.size()); 519 } 520 521 if (searchResultsSize == 0) { 522 messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 523 RiceKeyConstants.INFO_LOOKUP_RESULTS_NONE_FOUND); 524 } else if (searchResultsSize == 1) { 525 messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 526 RiceKeyConstants.INFO_LOOKUP_RESULTS_DISPLAY_ONE); 527 } else if (searchResultsSize > 1) { 528 boolean resultsExceedsLimit = 529 bounded && (searchResultsLimit != null) && (searchResultsSize > searchResultsLimit); 530 531 if (resultsExceedsLimit) { 532 messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 533 RiceKeyConstants.INFO_LOOKUP_RESULTS_EXCEEDS_LIMIT, searchResultsSize.toString(), 534 searchResultsLimit.toString()); 535 } 536 } 537 538 Boolean usingPrimaryKey = getLookupService().allPrimaryKeyValuesPresentAndNotWildcard(getDataObjectClass(), 539 searchCriteria); 540 541 if (usingPrimaryKey) { 542 List<String> pkNames = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass()); 543 544 List<String> pkLabels = new ArrayList<String>(); 545 for (String pkName : pkNames) { 546 pkLabels.add(getDataDictionaryService().getAttributeLabel(getDataObjectClass(), pkName)); 547 } 548 549 messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES, 550 RiceKeyConstants.INFO_LOOKUP_RESULTS_USING_PRIMARY_KEY, StringUtils.join(pkLabels, ",")); 551 } 552 } 553 554 /** 555 * Sorts the given list of search results based on the lookup view's configured sort attributes. 556 * 557 * <p>First if the posted view exists we grab the sort attributes from it. This will take into account expressions 558 * that might have been configured on the sort attributes. If the posted view does not exist (because we did a 559 * search from a get request or form session storage is off), we get the sort attributes from the view that we 560 * will be rendered (and was initialized before controller call). However, expressions will not be evaluated yet, 561 * thus if expressions were configured we don't know the results and can not sort the list</p> 562 * 563 * @param form lookup form instance containing view information 564 * @param searchResults list of search results to sort 565 * @TODO: revisit this when we have a solution for the posted view problem 566 */ 567 protected void sortSearchResults(LookupForm form, List<?> searchResults) { 568 List<String> defaultSortColumns = null; 569 boolean defaultSortAscending = true; 570 571 if (form.getView() != null) { 572 defaultSortColumns = ((LookupView) form.getView()).getDefaultSortAttributeNames(); 573 defaultSortAscending = ((LookupView) form.getView()).isDefaultSortAscending(); 574 } 575 576 boolean hasExpression = false; 577 if (defaultSortColumns != null) { 578 for (String sortColumn : defaultSortColumns) { 579 if (sortColumn == null) { 580 hasExpression = true; 581 } 582 } 583 } 584 585 if (hasExpression) { 586 defaultSortColumns = null; 587 } 588 589 if ((defaultSortColumns != null) && (!defaultSortColumns.isEmpty())) { 590 BeanPropertyComparator comparator = new BeanPropertyComparator(defaultSortColumns, true); 591 if (defaultSortAscending) { 592 Collections.sort(searchResults, comparator); 593 } else { 594 Collections.sort(searchResults, Collections.reverseOrder(comparator)); 595 } 596 } 597 } 598 599 /** 600 * Performs a normal search using the {@link LookupService}. 601 * 602 * @param searchCriteria map of criteria currently set 603 * @param wildcardAsLiteralSearchCriteria list of property names which have wildcard characters disabled 604 * @param unbounded indicates whether the complete result should be returned. When set to false the result is 605 * limited (if necessary) to the max search result limit configured. 606 * @param searchResultsLimit result set limit 607 * @return list of result objects, possibly bounded 608 */ 609 protected Collection<?> getSearchResults(Map<String, String> searchCriteria, 610 List<String> wildcardAsLiteralSearchCriteria, boolean unbounded, Integer searchResultsLimit) { 611 // if any of the properties refer to an embedded EBO, call the EBO lookups first and apply to the local lookup 612 try { 613 if (LookupUtils.hasExternalBusinessObjectProperty(getDataObjectClass(), searchCriteria)) { 614 searchCriteria = LookupUtils.adjustCriteriaForNestedEBOs(getDataObjectClass(), 615 searchCriteria, unbounded); 616 617 if (LOG.isDebugEnabled()) { 618 LOG.debug("Passing these results into the lookup service: " + searchCriteria); 619 } 620 } 621 } catch (IllegalAccessException e) { 622 throw new RuntimeException("Error trying to check for nested external business objects", e); 623 } catch (InstantiationException e1) { 624 throw new RuntimeException("Error trying to check for nested external business objects", e1); 625 } 626 627 // invoke the lookup search to carry out the search 628 return executeSearch(searchCriteria, wildcardAsLiteralSearchCriteria, !unbounded, searchResultsLimit); 629 } 630 631 /** 632 * Performs a search against an {@link org.kuali.rice.krad.bo.ExternalizableBusinessObject} by invoking the 633 * module service 634 * 635 * @param searchCriteria map of criteria currently set 636 * @param unbounded indicates whether the complete result should be returned. When set to false the result is 637 * limited (if necessary) to the max search result limit configured. 638 * @return list of result objects, possibly bounded 639 */ 640 protected List<?> getSearchResultsForEBO(Map<String, String> searchCriteria, boolean unbounded) { 641 ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService( 642 getDataObjectClass()); 643 644 BusinessObjectEntry ddEntry = eboModuleService.getExternalizableBusinessObjectDictionaryEntry( 645 getDataObjectClass()); 646 647 Map<String, String> filteredFieldValues = new HashMap<String, String>(); 648 for (String fieldName : searchCriteria.keySet()) { 649 if (ddEntry.getAttributeNames().contains(fieldName)) { 650 filteredFieldValues.put(fieldName, searchCriteria.get(fieldName)); 651 } 652 } 653 654 Map<String, Object> translatedValues = KRADUtils.coerceRequestParameterTypes( 655 (Class<? extends ExternalizableBusinessObject>) getDataObjectClass(), filteredFieldValues); 656 657 List<?> searchResults = eboModuleService.getExternalizableBusinessObjectsListForLookup( 658 (Class<? extends ExternalizableBusinessObject>) getDataObjectClass(), (Map) translatedValues, 659 unbounded); 660 661 return searchResults; 662 } 663 664 /** 665 * {@inheritDoc} 666 */ 667 @Override 668 public Map<String, String> performClear(LookupForm form, Map<String, String> lookupCriteria) { 669 Map<String, String> clearedLookupCriteria = new HashMap<String, String>(); 670 671 Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>(); 672 if (form.getView() != null) { 673 criteriaFieldMap = getCriteriaFieldsForValidation((LookupView) form.getView(), form); 674 } 675 676 // fields marked as read only through the initial request should not be cleared 677 List<String> readOnlyFieldsList = form.getReadOnlyFieldsList(); 678 679 for (Map.Entry<String, String> searchKeyValue : lookupCriteria.entrySet()) { 680 String searchPropertyName = searchKeyValue.getKey(); 681 String searchPropertyValue = searchKeyValue.getValue(); 682 683 InputField inputField = criteriaFieldMap.get(searchPropertyName); 684 685 if (readOnlyFieldsList == null || !readOnlyFieldsList.contains(searchPropertyName)) { 686 if (inputField != null && inputField.getDefaultValue() != null) { 687 searchPropertyValue = inputField.getDefaultValue().toString(); 688 } else { 689 searchPropertyValue = ""; 690 } 691 } 692 693 clearedLookupCriteria.put(searchPropertyName, searchPropertyValue); 694 } 695 696 return clearedLookupCriteria; 697 } 698 699 /** 700 * {@inheritDoc} 701 */ 702 @Override 703 public void buildReturnUrlForResult(Link returnLink, Object model) { 704 LookupForm lookupForm = (LookupForm) model; 705 706 Map<String, Object> returnLinkContext = returnLink.getContext(); 707 LookupView lookupView = returnLinkContext == null ? null : (LookupView) returnLinkContext.get( 708 UifConstants.ContextVariableNames.VIEW); 709 Object dataObject = returnLinkContext == null ? null : returnLinkContext.get( 710 UifConstants.ContextVariableNames.LINE); 711 712 // don't render return link if the object is null or if the row is not returnable 713 if ((dataObject == null) || (!isResultReturnable(dataObject))) { 714 returnLink.setRender(false); 715 716 return; 717 } 718 719 String dataReturnValue = "true"; 720 if (lookupForm.isReturnByScript()) { 721 Map<String, String> translatedKeyValues = getTranslatedReturnKeyValues(lookupView, lookupForm, dataObject); 722 723 dataReturnValue = ScriptUtils.translateValue(translatedKeyValues); 724 725 returnLink.setHref("#"); 726 727 String dialogId = lookupForm.getShowDialogId(); 728 if (StringUtils.isNotBlank(dialogId)) { 729 returnLink.setHref(returnLink.getHref() + dialogId); 730 } 731 } else if (StringUtils.isBlank(returnLink.getHref())) { 732 String href = getReturnUrl(lookupView, lookupForm, dataObject); 733 734 if (StringUtils.isNotBlank(href)) { 735 returnLink.setHref(href); 736 } else { 737 returnLink.setRender(false); 738 return; 739 } 740 741 String target = lookupForm.getReturnTarget(); 742 743 if (StringUtils.isNotBlank(target)) { 744 returnLink.setTarget(target); 745 } 746 } 747 748 // add data attribute for attaching event handlers on the return links 749 returnLink.addDataAttribute(UifConstants.DataAttributes.RETURN, dataReturnValue); 750 751 // build return link title if not already set 752 if (StringUtils.isBlank(returnLink.getTitle())) { 753 String linkLabel = StringUtils.defaultIfBlank(getConfigurationService().getPropertyValueAsString( 754 KRADConstants.Lookup.TITLE_RETURN_URL_PREPENDTEXT_PROPERTY), StringUtils.EMPTY); 755 756 Map<String, String> returnKeyValues = getReturnKeyValues(lookupView, lookupForm, dataObject); 757 758 String title = KRADUtils.buildAttributeTitleString(linkLabel, getDataObjectClass(), returnKeyValues); 759 returnLink.setTitle(title); 760 } 761 } 762 763 /** 764 * Determines whether a given data object that's returned as one of the lookup's results is considered returnable, 765 * which means that for single-value lookups, a "return value" link may be rendered, and for multiple 766 * value lookups, a checkbox is rendered. 767 * 768 * <p>Note that this can be part of an authorization mechanism, but not the complete authorization mechanism. The 769 * component that invoked the lookup/ lookup caller (e.g. document, nesting lookup, etc.) needs to check 770 * that the object that was passed to it was returnable as well because there are ways around this method 771 * (e.g. crafting a custom return URL).</p> 772 * 773 * @param dataObject an object from the search result set 774 * @return true if the row is returnable and false if it is not 775 */ 776 protected boolean isResultReturnable(Object dataObject) { 777 return true; 778 } 779 780 /** 781 * Builds the URL for returning the given data object result row. 782 * 783 * <p>Note return URL will only be built if a return location is specified on the lookup form</p> 784 * 785 * @param lookupView lookup view instance containing lookup configuration 786 * @param lookupForm lookup form instance containing the data 787 * @param dataObject data object instance for the current line and for which the return URL is being built 788 * @return String return URL or blank if URL cannot be built 789 */ 790 protected String getReturnUrl(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 791 Properties props = getReturnUrlParameters(lookupView, lookupForm, dataObject); 792 793 String href = ""; 794 if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) { 795 href = UrlFactory.parameterizeUrl(lookupForm.getReturnLocation(), props); 796 } 797 798 return href; 799 } 800 801 /** 802 * Builds up a {@code Properties} object that will be used to provide the request parameters for the 803 * return URL link 804 * 805 * @param lookupView lookup view instance containing lookup configuration 806 * @param lookupForm lookup form instance containing the data 807 * @param dataObject data object instance for the current line and for which the return URL is being built 808 * @return Properties instance containing request parameters for return URL 809 */ 810 protected Properties getReturnUrlParameters(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 811 Properties props = new Properties(); 812 props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL); 813 814 if (StringUtils.isNotBlank(lookupForm.getReturnFormKey())) { 815 props.put(UifParameters.FORM_KEY, lookupForm.getReturnFormKey()); 816 } 817 818 props.put(KRADConstants.REFRESH_CALLER, lookupView.getId()); 819 props.put(KRADConstants.REFRESH_DATA_OBJECT_CLASS, getDataObjectClass().getName()); 820 821 if (StringUtils.isNotBlank(lookupForm.getReferencesToRefresh())) { 822 props.put(UifParameters.REFERENCES_TO_REFRESH, lookupForm.getReferencesToRefresh()); 823 } 824 825 // setup action parameters for cases where the lookup request came from another dialog like edit line 826 String selectedCollectionId = null; 827 String selectedCollectionPath = null; 828 String selectedLineIndex = null; 829 if (lookupForm.getInitialRequestParameters() != null) { 830 String[] ids = lookupForm.getInitialRequestParameters().get(UifParameters.SELECTED_COLLECTION_ID); 831 if (ids != null && ids.length > 0) { 832 selectedCollectionId = ids[0]; 833 } 834 835 String[] paths = lookupForm.getInitialRequestParameters().get(UifParameters.SELECTED_COLLECTION_PATH); 836 if (paths != null && paths.length > 0) { 837 selectedCollectionPath = paths[0]; 838 } 839 840 String[] lines = lookupForm.getInitialRequestParameters().get(UifParameters.SELECTED_LINE_INDEX); 841 if (lines != null && lines.length > 0) { 842 selectedLineIndex = lines[0]; 843 } 844 } 845 846 if (StringUtils.isNotBlank(selectedLineIndex)) { 847 props.put(UifPropertyPaths.ACTION_PARAMETERS + "[" + UifParameters.SELECTED_LINE_INDEX + "]", 848 selectedLineIndex); 849 } 850 851 if (StringUtils.isNotBlank(selectedCollectionId)) { 852 props.put(UifPropertyPaths.ACTION_PARAMETERS + "[" + UifParameters.SELECTED_COLLECTION_ID + "]", 853 selectedCollectionId); 854 } 855 856 if (StringUtils.isNotBlank(selectedCollectionPath)) { 857 props.put(UifPropertyPaths.ACTION_PARAMETERS + "[" + UifParameters.SELECTED_COLLECTION_PATH + "]", 858 selectedCollectionPath); 859 } 860 861 String dialogId = lookupForm.getShowDialogId(); 862 if (StringUtils.isNotBlank(dialogId)) { 863 props.put(UifPropertyPaths.ACTION_PARAMETERS + "[" + UifParameters.DIALOG_ID + "]", dialogId); 864 } 865 866 if (StringUtils.isNotBlank(lookupForm.getQuickfinderId())) { 867 props.put(UifParameters.QUICKFINDER_ID, lookupForm.getQuickfinderId()); 868 } 869 870 Map<String, String> returnKeyValues = getTranslatedReturnKeyValues(lookupView, lookupForm, dataObject); 871 props.putAll(returnKeyValues); 872 873 return props; 874 } 875 876 /** 877 * Returns a map of the configured return keys translated to their corresponding field conversion with 878 * the associated values. 879 * 880 * @param lookupView lookup view instance containing lookup configuration 881 * @param lookupForm lookup form instance containing the data 882 * @param dataObject data object instance 883 * @return Map<String, String> map of translated return key/value pairs 884 */ 885 protected Map<String, String> getTranslatedReturnKeyValues(LookupView lookupView, LookupForm lookupForm, 886 Object dataObject) { 887 Map<String, String> translatedKeyValues = new HashMap<String, String>(); 888 889 Map<String, String> returnKeyValues = getReturnKeyValues(lookupView, lookupForm, dataObject); 890 891 for (String returnKey : returnKeyValues.keySet()) { 892 String returnValue = returnKeyValues.get(returnKey); 893 894 // get name of the property on the calling view to pass back the parameter value as 895 if (lookupForm.getFieldConversions().containsKey(returnKey)) { 896 returnKey = lookupForm.getFieldConversions().get(returnKey); 897 } 898 899 translatedKeyValues.put(returnKey, returnValue); 900 } 901 902 return translatedKeyValues; 903 } 904 905 /** 906 * Returns a map of the configured return keys with their selected values. 907 * 908 * @param lookupView lookup view instance containing lookup configuration 909 * @param lookupForm lookup form instance containing the data 910 * @param dataObject data object instance 911 * @return Map<String, String> map of return key/value pairs 912 */ 913 protected Map<String, String> getReturnKeyValues(LookupView lookupView, LookupForm lookupForm, Object dataObject) { 914 List<String> returnKeys; 915 916 if (lookupForm.getFieldConversions() != null && !lookupForm.getFieldConversions().isEmpty()) { 917 returnKeys = new ArrayList<String>(lookupForm.getFieldConversions().keySet()); 918 } else { 919 returnKeys = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass()); 920 } 921 922 List<String> secureReturnKeys = lookupView.getAdditionalSecurePropertyNames(); 923 924 return KRADUtils.getPropertyKeyValuesFromDataObject(returnKeys, secureReturnKeys, dataObject); 925 } 926 927 /** 928 * {@inheritDoc} 929 */ 930 @Override 931 public void buildMaintenanceActionLink(Link actionLink, Object model, String maintenanceMethodToCall) { 932 LookupForm lookupForm = (LookupForm) model; 933 934 Map<String, Object> actionLinkContext = actionLink.getContext(); 935 Object dataObject = actionLinkContext == null ? null : actionLinkContext.get( 936 UifConstants.ContextVariableNames.LINE); 937 938 List<String> pkNames = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass()); 939 940 // build maintenance link href if needed 941 if (StringUtils.isBlank(actionLink.getHref())) { 942 String href = getMaintenanceActionUrl(lookupForm, dataObject, maintenanceMethodToCall, pkNames); 943 if (StringUtils.isBlank(href)) { 944 actionLink.setRender(false); 945 946 return; 947 } 948 949 actionLink.setHref(href); 950 } 951 952 // build action title if not set 953 if (StringUtils.isBlank(actionLink.getTitle())) { 954 List<String> linkLabels = new ArrayList<String>(); 955 956 // get the link text 957 String linkText = actionLink.getLinkText(); 958 959 // if the link text is available, then add it to the link label 960 if (StringUtils.isNotBlank(linkText)) { 961 linkLabels.add(linkText); 962 } 963 964 // get the data object label 965 DataObjectEntry dataObjectEntry = getDataDictionaryService().getDataDictionary().getDataObjectEntry( 966 getDataObjectClass().getName()); 967 String dataObjectLabel = dataObjectEntry != null ? dataObjectEntry.getObjectLabel() : null; 968 969 // if the data object label is available, then add it to the link label 970 if (StringUtils.isNotBlank(dataObjectLabel)) { 971 linkLabels.add(dataObjectLabel); 972 } 973 974 // get the prepend text 975 String titleActionUrlPrependText = getConfigurationService().getPropertyValueAsString( 976 KRADConstants.Lookup.TITLE_ACTION_URL_PREPENDTEXT_PROPERTY); 977 978 // get the primary keys for the object 979 Map<String, String> primaryKeyValues = KRADUtils.getPropertyKeyValuesFromDataObject(pkNames, dataObject); 980 981 // if the prepend text is available and there are primary key values, then add it to the link label 982 if (StringUtils.isNotBlank(titleActionUrlPrependText) && !primaryKeyValues.isEmpty()) { 983 linkLabels.add(titleActionUrlPrependText); 984 } 985 986 String linkLabel = StringUtils.defaultIfBlank(StringUtils.join(linkLabels, " "), StringUtils.EMPTY); 987 String title = KRADUtils.buildAttributeTitleString(linkLabel, getDataObjectClass(), primaryKeyValues); 988 actionLink.setTitle(title); 989 } 990 } 991 992 /** 993 * Generates a URL to perform a maintenance action on the given result data object. 994 * 995 * <p>Will build a URL containing keys of the data object to invoke the given maintenance action method 996 * within the maintenance controller</p> 997 * 998 * @param lookupForm lookup form 999 * @param dataObject data object instance for the line to build the maintenance action link for 1000 * @param methodToCall method name on the maintenance controller that should be invoked 1001 * @param pkNames list of primary key field names for the data object whose key/value pairs will be added to 1002 * the maintenance link 1003 * @return String URL link for the maintenance action 1004 */ 1005 protected String getMaintenanceActionUrl(LookupForm lookupForm, Object dataObject, String methodToCall, 1006 List<String> pkNames) { 1007 LookupView lookupView = (LookupView) lookupForm.getView(); 1008 1009 Properties props = new Properties(); 1010 props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, methodToCall); 1011 1012 Map<String, String> primaryKeyValues = KRADUtils.getPropertyKeyValuesFromDataObject(pkNames, dataObject); 1013 for (String primaryKey : primaryKeyValues.keySet()) { 1014 String primaryKeyValue = primaryKeyValues.get(primaryKey); 1015 1016 props.put(primaryKey, primaryKeyValue); 1017 } 1018 1019 if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) { 1020 props.put(KRADConstants.RETURN_LOCATION_PARAMETER, lookupForm.getReturnLocation()); 1021 } 1022 1023 props.put(UifParameters.DATA_OBJECT_CLASS_NAME, lookupForm.getDataObjectClassName()); 1024 props.put(UifParameters.VIEW_TYPE_NAME, UifConstants.ViewType.MAINTENANCE.name()); 1025 1026 String maintenanceMapping = KRADConstants.Maintenance.REQUEST_MAPPING_MAINTENANCE; 1027 if (lookupView != null && StringUtils.isNotBlank(lookupView.getMaintenanceUrlMapping())) { 1028 maintenanceMapping = lookupView.getMaintenanceUrlMapping(); 1029 } 1030 1031 return UrlFactory.parameterizeUrl(maintenanceMapping, props); 1032 } 1033 1034 /** 1035 * {@inheritDoc} 1036 */ 1037 @Override 1038 public void buildMultiValueSelectField(InputField selectField, Object model) { 1039 LookupForm lookupForm = (LookupForm) model; 1040 1041 Map<String, Object> selectFieldContext = selectField.getContext(); 1042 Object lineDataObject = selectFieldContext == null ? null : selectFieldContext.get( 1043 UifConstants.ContextVariableNames.LINE); 1044 if (lineDataObject == null) { 1045 throw new RuntimeException("Unable to get data object for line from component: " + selectField.getId()); 1046 } 1047 1048 Control selectControl = selectField.getControl(); 1049 if ((selectControl != null) && (selectControl instanceof ValueConfiguredControl)) { 1050 // get value for each field conversion from line and add to lineIdentifier 1051 List<String> fromFieldNames = new ArrayList<String>(lookupForm.getFieldConversions().keySet()); 1052 // Per KULRICE-12125 we need to remove secure field names from this list. 1053 fromFieldNames = new ArrayList<String>(KRADUtils.getPropertyKeyValuesFromDataObject(fromFieldNames, 1054 lineDataObject).keySet()); 1055 1056 Collections.sort(fromFieldNames); 1057 lookupForm.setMultiValueReturnFields(fromFieldNames); 1058 String lineIdentifier = ""; 1059 for (String fromFieldName : fromFieldNames) { 1060 Object fromFieldValue = ObjectPropertyUtils.getPropertyValue(lineDataObject, fromFieldName); 1061 1062 if (fromFieldValue != null) { 1063 lineIdentifier += fromFieldValue; 1064 } 1065 1066 lineIdentifier += ":"; 1067 } 1068 lineIdentifier = StringUtils.removeEnd(lineIdentifier, ":"); 1069 1070 ((ValueConfiguredControl) selectControl).setValue(lineIdentifier); 1071 } 1072 } 1073 1074 /** 1075 * {@inheritDoc} 1076 */ 1077 @Override 1078 public boolean allowsMaintenanceNewOrCopyAction() { 1079 boolean allowsNewOrCopy = false; 1080 1081 String maintDocTypeName = getMaintenanceDocumentTypeName(); 1082 if (StringUtils.isNotBlank(maintDocTypeName)) { 1083 allowsNewOrCopy = getDataObjectAuthorizationService().canCreate(getDataObjectClass(), 1084 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 1085 } 1086 1087 return allowsNewOrCopy; 1088 } 1089 1090 /** 1091 * {@inheritDoc} 1092 */ 1093 @Override 1094 public boolean allowsMaintenanceEditAction(Object dataObject) { 1095 boolean allowsEdit = false; 1096 1097 String maintDocTypeName = getMaintenanceDocumentTypeName(); 1098 if (StringUtils.isNotBlank(maintDocTypeName)) { 1099 allowsEdit = getDataObjectAuthorizationService().canMaintain(dataObject, 1100 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 1101 } 1102 1103 return allowsEdit; 1104 } 1105 1106 /** 1107 * {@inheritDoc} 1108 */ 1109 @Override 1110 public boolean allowsMaintenanceDeleteAction(Object dataObject) { 1111 boolean allowsMaintain = false; 1112 1113 String maintDocTypeName = getMaintenanceDocumentTypeName(); 1114 if (StringUtils.isNotBlank(maintDocTypeName)) { 1115 allowsMaintain = getDataObjectAuthorizationService().canMaintain(dataObject, 1116 GlobalVariables.getUserSession().getPerson(), maintDocTypeName); 1117 } 1118 1119 boolean allowsDelete = getDocumentDictionaryService().getAllowsRecordDeletion(getDataObjectClass()); 1120 1121 return allowsDelete && allowsMaintain; 1122 } 1123 1124 /** 1125 * Returns the maintenance document type associated with the business object class or null if one does not exist. 1126 * 1127 * @return String representing the maintenance document type name 1128 */ 1129 protected String getMaintenanceDocumentTypeName() { 1130 DocumentDictionaryService dd = getDocumentDictionaryService(); 1131 1132 return dd.getMaintenanceDocumentTypeName(getDataObjectClass()); 1133 } 1134 1135 /** 1136 * Returns the criteria fields in a map keyed by the field property name. 1137 * 1138 * @param lookupView lookup view instance to pull criteria fields from 1139 * @param form lookup form instance containing the lookup data 1140 * @return map of criteria fields 1141 */ 1142 protected Map<String, InputField> getCriteriaFieldsForValidation(LookupView lookupView, LookupForm form) { 1143 Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>(); 1144 1145 if (lookupView.getCriteriaFields() == null) { 1146 return criteriaFieldMap; 1147 } 1148 1149 List<InputField> fields = null; 1150 if (lookupView.getCriteriaGroup().getItems().size() > 0) { 1151 fields = ComponentUtils.getNestedContainerComponents(lookupView.getCriteriaGroup(), InputField.class); 1152 } else if (lookupView.getCriteriaFields().size() > 0) { 1153 // If criteriaGroup items are empty look to see if criteriaFields has any input components. 1154 // This is to ensure that if initializeGroup hasn't been called on the view, the validations will still happen on criteriaFields 1155 fields = ComponentUtils.getComponentsOfType(lookupView.getCriteriaFields(), InputField.class); 1156 } else { 1157 fields = new ArrayList<InputField>(); 1158 } 1159 for (InputField field : fields) { 1160 criteriaFieldMap.put(field.getPropertyName(), field); 1161 } 1162 1163 return criteriaFieldMap; 1164 } 1165 1166 /** 1167 * {@inheritDoc} 1168 */ 1169 @Override 1170 public Class<?> getDataObjectClass() { 1171 return this.dataObjectClass; 1172 } 1173 1174 /** 1175 * {@inheritDoc} 1176 */ 1177 @Override 1178 public void setDataObjectClass(Class<?> dataObjectClass) { 1179 this.dataObjectClass = dataObjectClass; 1180 } 1181 1182 protected DataObjectAuthorizationService getDataObjectAuthorizationService() { 1183 if (dataObjectAuthorizationService == null) { 1184 this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService(); 1185 } 1186 return dataObjectAuthorizationService; 1187 } 1188 1189 public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) { 1190 this.dataObjectAuthorizationService = dataObjectAuthorizationService; 1191 } 1192 1193 public DocumentDictionaryService getDocumentDictionaryService() { 1194 if (documentDictionaryService == null) { 1195 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 1196 } 1197 return documentDictionaryService; 1198 } 1199 1200 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 1201 this.documentDictionaryService = documentDictionaryService; 1202 } 1203 1204 protected LookupService getLookupService() { 1205 if (lookupService == null) { 1206 this.lookupService = KRADServiceLocatorWeb.getLookupService(); 1207 } 1208 return lookupService; 1209 } 1210 1211 public void setLookupService(LookupService lookupService) { 1212 this.lookupService = lookupService; 1213 } 1214 1215 protected EncryptionService getEncryptionService() { 1216 if (encryptionService == null) { 1217 this.encryptionService = CoreApiServiceLocator.getEncryptionService(); 1218 } 1219 return encryptionService; 1220 } 1221 1222 public void setEncryptionService(EncryptionService encryptionService) { 1223 this.encryptionService = encryptionService; 1224 } 1225 1226}