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.rules; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreApiServiceLocator; 020import org.kuali.rice.core.api.config.property.ConfigurationService; 021import org.kuali.rice.core.api.util.RiceKeyConstants; 022import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 023import org.kuali.rice.coreservice.framework.parameter.ParameterConstants; 024import org.kuali.rice.kew.api.KewApiConstants; 025import org.kuali.rice.kew.api.KewApiServiceLocator; 026import org.kuali.rice.kew.api.doctype.DocumentType; 027import org.kuali.rice.kew.api.doctype.DocumentTypeService; 028import org.kuali.rice.kim.api.KimConstants; 029import org.kuali.rice.kim.api.group.Group; 030import org.kuali.rice.kim.api.group.GroupService; 031import org.kuali.rice.kim.api.identity.Person; 032import org.kuali.rice.kim.api.identity.PersonService; 033import org.kuali.rice.kim.api.permission.PermissionService; 034import org.kuali.rice.kim.api.services.KimApiServiceLocator; 035import org.kuali.rice.krad.bo.AdHocRoutePerson; 036import org.kuali.rice.krad.bo.AdHocRouteRecipient; 037import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 038import org.kuali.rice.krad.bo.DocumentHeader; 039import org.kuali.rice.krad.bo.Note; 040import org.kuali.rice.krad.document.Document; 041import org.kuali.rice.krad.document.TransactionalDocument; 042import org.kuali.rice.krad.maintenance.MaintenanceDocument; 043import org.kuali.rice.krad.rules.rule.AddAdHocRoutePersonRule; 044import org.kuali.rice.krad.rules.rule.AddAdHocRouteWorkgroupRule; 045import org.kuali.rice.krad.rules.rule.AddCollectionLineRule; 046import org.kuali.rice.krad.rules.rule.AddNoteRule; 047import org.kuali.rice.krad.rules.rule.ApproveDocumentRule; 048import org.kuali.rice.krad.rules.rule.CompleteDocumentRule; 049import org.kuali.rice.krad.rules.rule.RouteDocumentRule; 050import org.kuali.rice.krad.rules.rule.SaveDocumentRule; 051import org.kuali.rice.krad.rules.rule.SendAdHocRequestsRule; 052import org.kuali.rice.krad.rules.rule.event.AddCollectionLineEvent; 053import org.kuali.rice.krad.rules.rule.event.ApproveDocumentEvent; 054import org.kuali.rice.krad.service.DataDictionaryService; 055import org.kuali.rice.krad.service.DictionaryValidationService; 056import org.kuali.rice.krad.service.DocumentDictionaryService; 057import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 058import org.kuali.rice.krad.uif.UifPropertyPaths; 059import org.kuali.rice.krad.util.GlobalVariables; 060import org.kuali.rice.krad.util.KRADConstants; 061import org.kuali.rice.krad.util.KRADPropertyConstants; 062import org.kuali.rice.krad.util.KRADUtils; 063import org.kuali.rice.krad.util.MessageMap; 064import org.kuali.rice.krad.util.RouteToCompletionUtil; 065 066import java.util.HashMap; 067import java.util.List; 068import java.util.Map; 069 070/** 071 * Contains all of the business rules that are common to all documents. 072 * 073 * @author Kuali Rice Team (rice.collab@kuali.org) 074 */ 075public abstract class DocumentRuleBase implements SaveDocumentRule, RouteDocumentRule, ApproveDocumentRule, AddNoteRule, 076 AddAdHocRoutePersonRule, AddAdHocRouteWorkgroupRule, SendAdHocRequestsRule, CompleteDocumentRule, 077 AddCollectionLineRule { 078 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentRuleBase.class); 079 080 private static PersonService personService; 081 private static DictionaryValidationService dictionaryValidationService; 082 private static DocumentDictionaryService documentDictionaryService; 083 private static ConfigurationService kualiConfigurationService; 084 private static GroupService groupService; 085 private static PermissionService permissionService; 086 private static DocumentTypeService documentTypeService; 087 private static DataDictionaryService dataDictionaryService; 088 089 // just some arbitrarily high max depth that's unlikely to occur in real life to prevent recursion problems 090 private int maxDictionaryValidationDepth = 100; 091 092 /** 093 * Verifies that the document's overview fields are valid - it does required and format checks. 094 * 095 * @param document 096 * @return boolean True if the document description is valid, false otherwise. 097 */ 098 public boolean isDocumentOverviewValid(Document document) { 099 // add in the documentHeader path 100 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 101 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_HEADER_PROPERTY_NAME); 102 103 // check the document header for fields like the description 104 getDictionaryValidationService().validateBusinessObject(document.getDocumentHeader()); 105 validateSensitiveDataValue(KRADPropertyConstants.EXPLANATION, document.getDocumentHeader().getExplanation(), 106 getDataDictionaryService().getAttributeLabel(DocumentHeader.class, KRADPropertyConstants.EXPLANATION)); 107 validateSensitiveDataValue(KRADPropertyConstants.DOCUMENT_DESCRIPTION, 108 document.getDocumentHeader().getDocumentDescription(), getDataDictionaryService().getAttributeLabel( 109 DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION)); 110 111 // drop the error path keys off now 112 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_HEADER_PROPERTY_NAME); 113 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 114 115 return GlobalVariables.getMessageMap().hasNoErrors(); 116 } 117 118 /** 119 * Validates the document attributes against the data dictionary. 120 * 121 * @param document 122 * @param validateRequired if true, then an error will be retruned if a DD required field is empty. if false, no 123 * required 124 * checking is done 125 * @return True if the document attributes are valid, false otherwise. 126 */ 127 public boolean isDocumentAttributesValid(Document document, boolean validateRequired) { 128 // start updating the error path name 129 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 130 131 // check the document for fields like explanation and org doc # 132 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, 133 getMaxDictionaryValidationDepth(), validateRequired); 134 135 // drop the error path keys off now 136 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 137 138 return GlobalVariables.getMessageMap().hasNoErrors(); 139 } 140 141 /** 142 * Runs all business rules needed prior to saving. This includes both common rules for all documents, plus 143 * class-specific 144 * business rules. This method will only return false if it fails the isValidForSave() test. Otherwise, it will 145 * always return 146 * positive regardless of the outcome of the business rules. However, any error messages resulting from the business 147 * rules will 148 * still be populated, for display to the consumer of this service. 149 * 150 * @see org.kuali.rice.krad.rules.rule.SaveDocumentRule#processSaveDocument(org.kuali.rice.krad.document.Document) 151 */ 152 public boolean processSaveDocument(Document document) { 153 boolean isValid = true; 154 155 isValid = isDocumentOverviewValid(document); 156 157 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 158 159 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, 160 getMaxDictionaryValidationDepth(), false); 161 getDictionaryValidationService().validateDefaultExistenceChecksForTransDoc((TransactionalDocument) document); 162 163 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 164 165 isValid &= GlobalVariables.getMessageMap().hasNoErrors(); 166 isValid &= processCustomSaveDocumentBusinessRules(document); 167 168 return isValid; 169 } 170 171 /** 172 * This method should be overridden by children rule classes as a hook to implement document specific business rule 173 * checks for 174 * the "save document" event. 175 * 176 * @param document 177 * @return boolean True if the rules checks passed, false otherwise. 178 */ 179 protected boolean processCustomSaveDocumentBusinessRules(Document document) { 180 return true; 181 } 182 183 /** 184 * Runs all business rules needed prior to routing. This includes both common rules for all maintenance documents, 185 * plus 186 * class-specific business rules. This method will return false if any business rule fails, or if the document is in 187 * an invalid 188 * state, and not routable (see isDocumentValidForRouting()). 189 * 190 * @see org.kuali.rice.krad.rules.rule.RouteDocumentRule#processRouteDocument(org.kuali.rice.krad.document.Document) 191 */ 192 public boolean processRouteDocument(Document document) { 193 boolean isValid = true; 194 195 isValid = isDocumentOverviewValid(document); 196 197 boolean completeRequestPending = RouteToCompletionUtil.checkIfAtleastOneAdHocCompleteRequestExist(document); 198 199 // Validate the document if the header is valid and no pending completion requests 200 if (isValid && !completeRequestPending) { 201 isValid &= isDocumentAttributesValid(document, true); 202 isValid &= processCustomRouteDocumentBusinessRules(document); 203 } 204 205 return isValid; 206 } 207 208 /** 209 * This method should be overridden by children rule classes as a hook to implement document specific business rule 210 * checks for 211 * the "route document" event. 212 * 213 * @param document 214 * @return boolean True if the rules checks passed, false otherwise. 215 */ 216 protected boolean processCustomRouteDocumentBusinessRules(Document document) { 217 return true; 218 } 219 220 /** 221 * Runs all business rules needed prior to approving. This includes both common rules for all documents, plus 222 * class-specific 223 * business rules. This method will return false if any business rule fails, or if the document is in an invalid 224 * state, and not 225 * approveble. 226 * 227 * @see org.kuali.rice.krad.rules.rule.ApproveDocumentRule#processApproveDocument(org.kuali.rice.krad.rules.rule.event.ApproveDocumentEvent) 228 */ 229 public boolean processApproveDocument(ApproveDocumentEvent approveEvent) { 230 boolean isValid = true; 231 232 isValid = processCustomApproveDocumentBusinessRules(approveEvent); 233 234 return isValid; 235 } 236 237 /** 238 * This method should be overridden by children rule classes as a hook to implement document specific business rule 239 * checks for 240 * the "approve document" event. 241 * 242 * @param approveEvent 243 * @return boolean True if the rules checks passed, false otherwise. 244 */ 245 protected boolean processCustomApproveDocumentBusinessRules(ApproveDocumentEvent approveEvent) { 246 return true; 247 } 248 249 /** 250 * {@inheritDoc} 251 * 252 * This base implementation just runs the custom rules. 253 */ 254 @Override 255 public boolean processAddCollectionLine(AddCollectionLineEvent addEvent) { 256 boolean isValid = true; 257 258 isValid = processCustomAddCollectionLineBusinessRules(addEvent); 259 260 return isValid; 261 } 262 263 264 /** 265 * This method should be overridden to provide custom rules for processing adding to collections. 266 * 267 * @param addEvent the event containing all of the object necessary to run the rules 268 * 269 * @return true if validation succeeds, false otherwise 270 */ 271 protected boolean processCustomAddCollectionLineBusinessRules(AddCollectionLineEvent addEvent) { 272 return true; 273 } 274 275 /** 276 * Runs all business rules needed prior to adding a document note. This method will return false if any business 277 * rule fails 278 */ 279 public boolean processAddNote(Document document, Note note) { 280 boolean isValid = true; 281 282 isValid &= isNoteValid(note); 283 isValid &= processCustomAddNoteBusinessRules(document, note); 284 285 return isValid; 286 } 287 288 /** 289 * Verifies that the note's fields are valid - it does required and format checks. 290 * 291 * @param note 292 * @return boolean True if the document description is valid, false otherwise. 293 */ 294 public boolean isNoteValid(Note note) { 295 // add the error path keys on the stack 296 GlobalVariables.getMessageMap().addToErrorPath(UifPropertyPaths.NEW_COLLECTION_LINES 297 + "['" 298 + KRADConstants.DOCUMENT_PROPERTY_NAME 299 + "." 300 + KRADConstants.NOTES_PROPERTY_NAME 301 + "']"); 302 303 // check the document header for fields like the description 304 getDictionaryValidationService().validateBusinessObject(note); 305 306 validateSensitiveDataValue(KRADConstants.NOTE_TEXT_PROPERTY_NAME, note.getNoteText(), 307 getDataDictionaryService().getAttributeLabel(Note.class, KRADConstants.NOTE_TEXT_PROPERTY_NAME)); 308 309 // drop the error path keys off now 310 GlobalVariables.getMessageMap().removeFromErrorPath(UifPropertyPaths.NEW_COLLECTION_LINES 311 + "['" 312 + KRADConstants.DOCUMENT_PROPERTY_NAME 313 + "." 314 + KRADConstants.NOTES_PROPERTY_NAME 315 + "']"); 316 317 return GlobalVariables.getMessageMap().hasNoErrors(); 318 } 319 320 /** 321 * This method should be overridden by children rule classes as a hook to implement document specific business rule 322 * checks for 323 * the "add document note" event. 324 * 325 * @param document 326 * @param note 327 * @return boolean True if the rules checks passed, false otherwise. 328 */ 329 protected boolean processCustomAddNoteBusinessRules(Document document, Note note) { 330 return true; 331 } 332 333 /** 334 * @see org.kuali.rice.krad.rules.rule.AddAdHocRoutePersonRule#processAddAdHocRoutePerson(org.kuali.rice.krad.document.Document, 335 * org.kuali.rice.krad.bo.AdHocRoutePerson) 336 */ 337 public boolean processAddAdHocRoutePerson(Document document, AdHocRoutePerson adHocRoutePerson) { 338 boolean isValid = true; 339 340 isValid &= isAddHocRoutePersonValid(document, adHocRoutePerson); 341 342 isValid &= processCustomAddAdHocRoutePersonBusinessRules(document, adHocRoutePerson); 343 return isValid; 344 } 345 346 /** 347 * @see org.kuali.rice.krad.rules.rule.SendAdHocRequestsRule#processSendAdHocRequests(org.kuali.rice.krad.document.Document) 348 */ 349 public boolean processSendAdHocRequests(Document document) { 350 boolean isValid = true; 351 352 isValid &= isAdHocRouteRecipientsValid(document); 353 isValid &= processCustomSendAdHocRequests(document); 354 355 return isValid; 356 } 357 358 protected boolean processCustomSendAdHocRequests(Document document) { 359 return true; 360 } 361 362 /** 363 * Checks the adhoc route recipient list to ensure there are recipients or 364 * else throws an error that at least one recipient is required. 365 * 366 * @param document 367 * @return true if all adhoc route recipients are valid 368 */ 369 protected boolean isAdHocRouteRecipientsValid(Document document) { 370 boolean isValid = true; 371 MessageMap errorMap = GlobalVariables.getMessageMap(); 372 373 if (errorMap.getErrorPath().size() == 0) { 374 // add the error path keys on the stack 375 errorMap.addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 376 } 377 378 if ((document.getAdHocRoutePersons() == null || document.getAdHocRoutePersons().isEmpty()) && (document 379 .getAdHocRouteWorkgroups() == null || document.getAdHocRouteWorkgroups().isEmpty())) { 380 381 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, "error.adhoc.missing.recipients"); 382 isValid = false; 383 } 384 385 // drop the error path keys off now 386 errorMap.removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 387 388 return isValid; 389 } 390 391 /** 392 * Verifies that the adHocRoutePerson's fields are valid - it does required and format checks. 393 * 394 * @param person 395 * @return boolean True if valid, false otherwise. 396 */ 397 public boolean isAddHocRoutePersonValid(Document document, AdHocRoutePerson person) { 398 MessageMap errorMap = GlobalVariables.getMessageMap(); 399 400 // new recipients are not embedded in the error path; existing lines should be 401 if (errorMap.getErrorPath().size() == 0) { 402 // add the error path keys on the stack 403 errorMap.addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 404 } 405 406 String actionRequestedCode = person.getActionRequested(); 407 if (StringUtils.isNotBlank(person.getId())) { 408 Person user = getPersonService().getPersonByPrincipalName(person.getId()); 409 410 if (user == null) { 411 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 412 RiceKeyConstants.ERROR_INVALID_ADHOC_PERSON_ID); 413 } 414 else if (!getPermissionService().hasPermission(user.getPrincipalId(), 415 KimConstants.KIM_TYPE_DEFAULT_NAMESPACE, KimConstants.PermissionNames.LOG_IN)) { 416 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 417 RiceKeyConstants.ERROR_INACTIVE_ADHOC_PERSON_ID); 418 } 419 else if(this.isAdHocRouteCompletionToInitiator(document, user, actionRequestedCode)){ 420 // KULRICE-7419: Adhoc route completion validation rule (should not route to initiator for completion) 421 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 422 RiceKeyConstants.ERROR_ADHOC_COMPLETE_PERSON_IS_INITIATOR); 423 } 424 else if(StringUtils.equals(actionRequestedCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ) && this.hasAdHocRouteCompletion(document, person)){ 425 // KULRICE-8760: Multiple complete adhoc requests should not be allowed on the same document 426 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 427 RiceKeyConstants.ERROR_ADHOC_COMPLETE_MORE_THAN_ONE); 428 } 429 else { 430 Class docOrBoClass = null; 431 if (document instanceof MaintenanceDocument) { 432 docOrBoClass = ((MaintenanceDocument) document).getNewMaintainableObject().getDataObjectClass(); 433 } else { 434 docOrBoClass = document.getClass(); 435 } 436 437 if (!getDocumentDictionaryService().getDocumentAuthorizer(document).canReceiveAdHoc(document, user, actionRequestedCode)) { 438 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 439 RiceKeyConstants.ERROR_UNAUTHORIZED_ADHOC_PERSON_ID); 440 } 441 } 442 } else { 443 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.ID, 444 RiceKeyConstants.ERROR_MISSING_ADHOC_PERSON_ID); 445 } 446 447 // drop the error path keys off now 448 errorMap.removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_PERSON_PROPERTY_NAME); 449 450 return GlobalVariables.getMessageMap().hasNoErrors(); 451 } 452 453 /** 454 * KULRICE-7419: Adhoc route completion validation rule (should not route to initiator for completion) 455 * 456 * determine whether the document initiator is the same as the adhoc recipient for completion 457 */ 458 protected boolean isAdHocRouteCompletionToInitiator(Document document, Person person, String actionRequestCode){ 459 if(!StringUtils.equals(actionRequestCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ)){ 460 return false; 461 } 462 463 String documentInitiator = document.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(); 464 String adhocRecipient = person.getPrincipalId(); 465 466 return StringUtils.equals(documentInitiator, adhocRecipient); 467 } 468 469 /** 470 * KULRICE-8760: check whether there is any other complete adhoc request on the given document 471 */ 472 protected boolean hasAdHocRouteCompletion(Document document, AdHocRouteRecipient adHocRouteRecipient){ 473 List<AdHocRoutePerson> adHocRoutePersons = document.getAdHocRoutePersons(); 474 if(KRADUtils.isNotNull(adHocRoutePersons)){ 475 for(AdHocRoutePerson adhocRecipient : adHocRoutePersons){ 476 // the given adhoc route recipient doesn't count 477 if(adHocRouteRecipient==adhocRecipient){ 478 continue; 479 } 480 481 String actionRequestCode = adhocRecipient.getActionRequested(); 482 if(StringUtils.equals(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, actionRequestCode)){ 483 return true; 484 } 485 } 486 } 487 488 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = document.getAdHocRouteWorkgroups(); 489 if(KRADUtils.isNotNull(adHocRouteWorkgroups)){ 490 for(AdHocRouteWorkgroup adhocRecipient : adHocRouteWorkgroups){ 491 // the given adhoc route recipient doesn't count 492 if(adHocRouteRecipient==adhocRecipient){ 493 continue; 494 } 495 496 String actionRequestCode = adhocRecipient.getActionRequested(); 497 if(StringUtils.equals(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, actionRequestCode)){ 498 return true; 499 } 500 } 501 } 502 503 return false; 504 } 505 506 /** 507 * This method should be overridden by children rule classes as a hook to implement document specific business rule 508 * checks for 509 * the "add ad hoc route person" event. 510 * 511 * @param document 512 * @param person 513 * @return boolean True if the rules checks passed, false otherwise. 514 */ 515 protected boolean processCustomAddAdHocRoutePersonBusinessRules(Document document, AdHocRoutePerson person) { 516 return true; 517 } 518 519 /** 520 * @see org.kuali.rice.krad.rules.rule.AddAdHocRouteWorkgroupRule#processAddAdHocRouteWorkgroup(org.kuali.rice.krad.document.Document, 521 * org.kuali.rice.krad.bo.AdHocRouteWorkgroup) 522 */ 523 public boolean processAddAdHocRouteWorkgroup(Document document, AdHocRouteWorkgroup adHocRouteWorkgroup) { 524 boolean isValid = true; 525 526 isValid &= isAddHocRouteWorkgroupValid(document, adHocRouteWorkgroup); 527 528 isValid &= processCustomAddAdHocRouteWorkgroupBusinessRules(document, adHocRouteWorkgroup); 529 return isValid; 530 } 531 532 /** 533 * Verifies that the adHocRouteWorkgroup's fields are valid - it does required and format checks. 534 * 535 * @param workgroup 536 * @return boolean True if valid, false otherwise. 537 */ 538 public boolean isAddHocRouteWorkgroupValid(Document document, AdHocRouteWorkgroup workgroup) { 539 MessageMap errorMap = GlobalVariables.getMessageMap(); 540 541 // new recipients are not embedded in the error path; existing lines should be 542 if (errorMap.getErrorPath().size() == 0) { 543 // add the error path keys on the stack 544 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME); 545 } 546 547 if (workgroup.getRecipientName() != null && workgroup.getRecipientNamespaceCode() != null) { 548 // validate that they are a workgroup from the workgroup service by looking them up 549 try { 550 Group group = getGroupService().getGroupByNamespaceCodeAndName(workgroup.getRecipientNamespaceCode(), 551 workgroup.getRecipientName()); 552 553 String actionRequestedCode = workgroup.getActionRequested(); 554 if (group == null || !group.isActive()) { 555 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 556 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 557 RiceKeyConstants.ERROR_INVALID_ADHOC_WORKGROUP_ID); 558 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 559 } 560 else if(StringUtils.equals(actionRequestedCode, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ) && this.hasAdHocRouteCompletion(document, workgroup)){ 561 // KULRICE-8760: Multiple complete adhoc requests should not be allowed on the same document 562 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, 563 RiceKeyConstants.ERROR_ADHOC_COMPLETE_MORE_THAN_ONE); 564 } 565 else { 566 org.kuali.rice.kew.api.document.WorkflowDocumentService 567 wds = KewApiServiceLocator.getWorkflowDocumentService(); 568 DocumentType documentType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName( 569 wds.getDocument(document.getDocumentNumber()).getDocumentTypeName()); 570 Map<String, String> permissionDetails = buildDocumentTypeActionRequestPermissionDetails( 571 documentType, workgroup.getActionRequested()); 572 if (useKimPermission(KewApiConstants.KEW_NAMESPACE, KewApiConstants.AD_HOC_REVIEW_PERMISSION, permissionDetails) ){ 573 List<String> principalIds = getGroupService().getMemberPrincipalIds(group.getId()); 574 // if any member of the group is not allowed to receive the request, then the group may not receive it 575 for (String principalId : principalIds) { 576 if (!getPermissionService().isAuthorizedByTemplate(principalId, 577 KewApiConstants.KEW_NAMESPACE, KewApiConstants.AD_HOC_REVIEW_PERMISSION, 578 permissionDetails, new HashMap<String, String>())) { 579 580 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 581 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 582 RiceKeyConstants.ERROR_UNAUTHORIZED_ADHOC_WORKGROUP_ID); 583 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 584 585 break; 586 } 587 } 588 } 589 } 590 } catch (Exception e) { 591 LOG.error("isAddHocRouteWorkgroupValid(AdHocRouteWorkgroup)", e); 592 593 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 594 RiceKeyConstants.ERROR_INVALID_ADHOC_WORKGROUP_ID); 595 596 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 597 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE); 598 } 599 } else { 600 // KULRICE-8091: Adhoc routing tab utilizing Groups on all documents missing asterisks 601 if(workgroup.getRecipientNamespaceCode()==null) { 602 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAMESPACE_CODE, RiceKeyConstants.ERROR_ADHOC_INVALID_WORKGROUP_NAMESPACE_MISSING); 603 } 604 605 if(workgroup.getRecipientName()==null) { 606 GlobalVariables.getMessageMap().putError(KRADPropertyConstants.RECIPIENT_NAME, 607 RiceKeyConstants.ERROR_MISSING_ADHOC_WORKGROUP_ID); 608 } 609 } 610 611 // drop the error path keys off now 612 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.NEW_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME); 613 614 return GlobalVariables.getMessageMap().hasNoErrors(); 615 } 616 /** 617 * This method should be overridden by children rule classes as a hook to implement document specific business rule 618 * checks for 619 * the "add ad hoc route workgroup" event. 620 * 621 * @param document 622 * @param workgroup 623 * @return boolean True if the rules checks passed, false otherwise. 624 */ 625 protected boolean processCustomAddAdHocRouteWorkgroupBusinessRules(Document document, 626 AdHocRouteWorkgroup workgroup) { 627 return true; 628 } 629 630 /** 631 * Gets the maximum number of levels the data-dictionary based validation will recurse for the document 632 */ 633 public int getMaxDictionaryValidationDepth() { 634 return this.maxDictionaryValidationDepth; 635 } 636 637 /** 638 * Gets the maximum number of levels the data-dictionary based validation will recurse for the document 639 */ 640 public void setMaxDictionaryValidationDepth(int maxDictionaryValidationDepth) { 641 if (maxDictionaryValidationDepth < 0) { 642 LOG.error("Dictionary validation depth should be greater than or equal to 0. Value received was: " 643 + maxDictionaryValidationDepth); 644 throw new RuntimeException( 645 "Dictionary validation depth should be greater than or equal to 0. Value received was: " 646 + maxDictionaryValidationDepth); 647 } 648 this.maxDictionaryValidationDepth = maxDictionaryValidationDepth; 649 } 650 651 protected boolean validateSensitiveDataValue(String fieldName, String fieldValue, String fieldLabel) { 652 boolean dataValid = true; 653 654 if (fieldValue == null) { 655 return dataValid; 656 } 657 658 boolean patternFound = KRADUtils.containsSensitiveDataPatternMatch(fieldValue); 659 boolean warnForSensitiveData = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 660 KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT, 661 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND); 662 if (patternFound && !warnForSensitiveData) { 663 dataValid = false; 664 GlobalVariables.getMessageMap().putError(fieldName, 665 RiceKeyConstants.ERROR_DOCUMENT_FIELD_CONTAINS_POSSIBLE_SENSITIVE_DATA, fieldLabel); 666 } 667 668 return dataValid; 669 } 670 671 /** 672 * Business rules check will include all save action rules and any custom rules required by the document specific rule implementation 673 * 674 * @param document Document 675 * @return true if all validations are passed 676 */ 677 public boolean processCompleteDocument(Document document) { 678 boolean isValid = true; 679 isValid &= processSaveDocument(document); 680 isValid &= processCustomCompleteDocumentBusinessRules(document); 681 return isValid; 682 } 683 684 /** 685 * Hook method for deriving business rule classes to provide custom validations required during completion action 686 * 687 * @param document 688 * @return default is true 689 */ 690 protected boolean processCustomCompleteDocumentBusinessRules(Document document) { 691 return true; 692 } 693 694 protected boolean useKimPermission(String namespace, String permissionTemplateName, Map<String, String> permissionDetails) { 695 Boolean b = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.ALL_DETAIL_TYPE, KewApiConstants.KIM_PRIORITY_ON_DOC_TYP_PERMS_IND); 696 if (b == null || b) { 697 return getPermissionService().isPermissionDefinedByTemplate(namespace, permissionTemplateName, 698 permissionDetails); 699 } 700 return false; 701 } 702 protected Map<String, String> buildDocumentTypeActionRequestPermissionDetails(DocumentType documentType, String actionRequestCode) { 703 Map<String, String> details = buildDocumentTypePermissionDetails(documentType); 704 if (!StringUtils.isBlank(actionRequestCode)) { 705 details.put(KewApiConstants.ACTION_REQUEST_CD_DETAIL, actionRequestCode); 706 } 707 return details; 708 } 709 710 protected Map<String, String> buildDocumentTypePermissionDetails(DocumentType documentType) { 711 Map<String, String> details = new HashMap<String, String>(); 712 details.put(KewApiConstants.DOCUMENT_TYPE_NAME_DETAIL, documentType.getName()); 713 return details; 714 } 715 716 protected DataDictionaryService getDataDictionaryService() { 717 if (dataDictionaryService == null) { 718 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 719 } 720 return dataDictionaryService; 721 } 722 723 protected PersonService getPersonService() { 724 if (personService == null) { 725 personService = KimApiServiceLocator.getPersonService(); 726 } 727 return personService; 728 } 729 730 public static GroupService getGroupService() { 731 if (groupService == null) { 732 groupService = KimApiServiceLocator.getGroupService(); 733 } 734 return groupService; 735 } 736 737 public static PermissionService getPermissionService() { 738 if (permissionService == null) { 739 permissionService = KimApiServiceLocator.getPermissionService(); 740 } 741 return permissionService; 742 } 743 744 protected DictionaryValidationService getDictionaryValidationService() { 745 if (dictionaryValidationService == null) { 746 dictionaryValidationService = KRADServiceLocatorWeb.getDictionaryValidationService(); 747 } 748 return dictionaryValidationService; 749 } 750 751 protected ConfigurationService getKualiConfigurationService() { 752 if (kualiConfigurationService == null) { 753 kualiConfigurationService = CoreApiServiceLocator.getKualiConfigurationService(); 754 } 755 return kualiConfigurationService; 756 } 757 758 protected static DocumentDictionaryService getDocumentDictionaryService() { 759 if (documentDictionaryService == null) { 760 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 761 } 762 return documentDictionaryService; 763 } 764 765 public static void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 766 DocumentRuleBase.documentDictionaryService = documentDictionaryService; 767 } 768}