001/** 002 * Copyright 2005-2016 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.kew.actions; 017 018import org.apache.log4j.MDC; 019import org.kuali.rice.kew.actionrequest.ActionRequestValue; 020import org.kuali.rice.kew.actiontaken.ActionTakenValue; 021import org.kuali.rice.kew.api.action.ActionType; 022import org.kuali.rice.kew.api.exception.InvalidActionTakenException; 023import org.kuali.rice.kew.api.exception.WorkflowException; 024import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 025import org.kuali.rice.kew.service.KEWServiceLocator; 026import org.kuali.rice.kew.api.KewApiConstants; 027import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 028 029 030import java.util.Iterator; 031import java.util.List; 032 033 034/** 035 * Cancels a document at the request of a client app. 036 * 037 * @author Kuali Rice Team (rice.collab@kuali.org) 038 */ 039public class CancelAction extends ActionTakenEvent { 040 041 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CancelAction.class); 042 043 public CancelAction(DocumentRouteHeaderValue rh, PrincipalContract principal) { 044 this(ActionType.CANCEL, rh, principal, null); 045 } 046 047 public CancelAction(DocumentRouteHeaderValue rh, PrincipalContract principal, String annotation) { 048 this(ActionType.CANCEL, rh, principal, annotation); 049 } 050 051 // Template constructor for use by third parties 052 CancelAction(ActionType type, DocumentRouteHeaderValue rh, PrincipalContract principal, String annotation) { 053 super(type.getCode(), rh, principal, annotation); 054 } 055 056 /* (non-Javadoc) 057 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 058 */ 059 @Override 060 public String validateActionRules() { 061 return validateActionRules(getActionRequestService().findAllPendingRequests(routeHeader.getDocumentId())); 062 } 063 064 public String validateActionRules(List<ActionRequestValue> actionRequests) { 065 // FYI delyea: This is new validation check... was not being checked previously 066 if (!getRouteHeader().isValidActionToTake(getActionPerformedCode())) { 067 return "Document is not in a state to be cancelled"; 068 } 069 List<ActionRequestValue> filteredActionRequests = filterActionRequestsByCode(actionRequests, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); 070 if (!isActionCompatibleRequest(filteredActionRequests)) { 071 return "No request for the user is compatible with the Cancel Action"; 072 } 073 // check state before checking kim 074 if (! KEWServiceLocator.getDocumentTypePermissionService().canCancel(getPrincipal().getPrincipalId(), getRouteHeader())) { 075 return "User is not authorized to Cancel document"; 076 } 077 return ""; 078 } 079 080 /* (non-Javadoc) 081 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 082 */ 083 @Override 084 public boolean isActionCompatibleRequest(List<ActionRequestValue> requests) { 085 086 // can always cancel saved or initiated document 087 if (routeHeader.isStateInitiated() || routeHeader.isStateSaved()) { 088 return true; 089 } 090 091 boolean actionCompatible = false; 092 Iterator ars = requests.iterator(); 093 ActionRequestValue actionRequest = null; 094 095 while (ars.hasNext()) { 096 actionRequest = (ActionRequestValue) ars.next(); 097 String request = actionRequest.getActionRequested(); 098 099 // APPROVE and COMPLETE request matches CANCEL Taken code 100 if ( (KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equals(request)) || 101 (KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(request)) ) { 102 actionCompatible = true; 103 break; 104 } 105 } 106 107 return actionCompatible; 108 } 109 110 // Template method for subclasses 111 protected void markDocumentStatus() throws InvalidActionTakenException { 112 getRouteHeader().markDocumentCanceled(); 113 } 114 115 public void recordAction() throws InvalidActionTakenException { 116 MDC.put("docId", getRouteHeader().getDocumentId()); 117 updateSearchableAttributesIfPossible(); 118 119 LOG.debug("Canceling document : " + annotation); 120 121 List actionRequests = getActionRequestService().findAllValidRequests(getPrincipal().getPrincipalId(), getDocumentId(), KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); 122 LOG.debug("Checking to see if the action is legal"); 123 String errorMessage = validateActionRules(actionRequests); 124 if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) { 125 throw new InvalidActionTakenException(errorMessage); 126 } 127 128 LOG.debug("Record the cancel action"); 129 ActionTakenValue actionTaken = saveActionTaken(findDelegatorForActionRequests(actionRequests)); 130 131 LOG.debug("Deactivate all pending action requests"); 132 actionRequests = getActionRequestService().findPendingByDoc(getDocumentId()); 133 134 getActionRequestService().deactivateRequests(actionTaken, actionRequests); 135 notifyActionTaken(actionTaken); 136 137 LOG.debug("Canceling document"); 138 139 try { 140 String oldStatus = getRouteHeader().getDocRouteStatus(); 141 markDocumentStatus(); 142 String newStatus = getRouteHeader().getDocRouteStatus(); 143 KEWServiceLocator.getRouteHeaderService().saveRouteHeader(getRouteHeader()); 144 notifyStatusChange(newStatus, oldStatus); 145 } catch (WorkflowException ex) { 146 LOG.warn(ex, ex); 147 throw new InvalidActionTakenException(ex.getMessage()); 148 } 149 } 150}