001 /** 002 * Copyright 2005-2013 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 */ 016 package org.kuali.rice.kew.actions; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.log4j.MDC; 020 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 021 import org.kuali.rice.kew.actionrequest.ActionRequestFactory; 022 import org.kuali.rice.kew.actionrequest.ActionRequestValue; 023 import org.kuali.rice.kew.actionrequest.Recipient; 024 import org.kuali.rice.kew.actiontaken.ActionTakenValue; 025 import org.kuali.rice.kew.api.exception.WorkflowException; 026 import org.kuali.rice.kew.engine.node.RouteNodeInstance; 027 import org.kuali.rice.kew.api.exception.InvalidActionTakenException; 028 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 029 import org.kuali.rice.kew.service.KEWServiceLocator; 030 import org.kuali.rice.kew.api.KewApiConstants; 031 import org.kuali.rice.kew.util.Utilities; 032 import org.kuali.rice.kim.api.group.Group; 033 import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 034 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 035 import org.kuali.rice.krad.util.KRADConstants; 036 037 import java.util.Collection; 038 import java.util.HashSet; 039 import java.util.Iterator; 040 import java.util.List; 041 import java.util.Set; 042 043 044 /** 045 * Disapproves a document. This deactivates all requests on the document and sends 046 * acknowlegde requests to anybody who had already completed or approved the document. 047 * 048 * @author Kuali Rice Team (rice.collab@kuali.org) 049 */ 050 public class DisapproveAction extends ActionTakenEvent { 051 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DisapproveAction.class); 052 053 /** 054 * @param rh RouteHeader for the document upon which the action is taken. 055 * @param principal User taking the action. 056 */ 057 public DisapproveAction(DocumentRouteHeaderValue rh, PrincipalContract principal) { 058 super(KewApiConstants.ACTION_TAKEN_DENIED_CD, rh, principal); 059 } 060 061 /** 062 * @param rh RouteHeader for the document upon which the action is taken. 063 * @param principal User taking the action. 064 * @param annotation User comment on the action taken 065 */ 066 public DisapproveAction(DocumentRouteHeaderValue rh, PrincipalContract principal, String annotation) { 067 super(KewApiConstants.ACTION_TAKEN_DENIED_CD, rh, principal, annotation); 068 } 069 070 /* (non-Javadoc) 071 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 072 */ 073 @Override 074 public String validateActionRules() { 075 return validateActionRules(getActionRequestService().findAllPendingRequests(routeHeader.getDocumentId())); 076 } 077 078 public String validateActionRules(List<ActionRequestValue> actionRequests) { 079 if (!getRouteHeader().isValidActionToTake(getActionPerformedCode())) { 080 return "Document is not in a state to be disapproved"; 081 } 082 List<ActionRequestValue> filteredActionRequests = filterActionRequestsByCode(actionRequests, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); 083 if (!isActionCompatibleRequest(filteredActionRequests)) { 084 return "No request for the user is compatible " + "with the DISAPPROVE or DENY action"; 085 } 086 return ""; 087 } 088 089 /* (non-Javadoc) 090 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 091 */ 092 @Override 093 public boolean isActionCompatibleRequest(List requests) { 094 // can always cancel saved or initiated document 095 if (routeHeader.isStateInitiated() || routeHeader.isStateSaved()) { 096 return true; 097 } 098 099 boolean actionCompatible = false; 100 Iterator ars = requests.iterator(); 101 ActionRequestValue actionRequest = null; 102 103 while (ars.hasNext()) { 104 actionRequest = (ActionRequestValue) ars.next(); 105 String request = actionRequest.getActionRequested(); 106 107 // APPROVE request matches all but FYI and ACK 108 if ( (KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equals(request)) || 109 (KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(request)) ) { 110 actionCompatible = true; 111 break; 112 } 113 } 114 115 return actionCompatible; 116 } 117 118 /** 119 * Records the disapprove action. - Checks to make sure the document status allows the action. - Checks that the user has not taken a previous action. - Deactivates the pending requests for this user - Records the action 120 * 121 * @throws org.kuali.rice.kew.api.exception.InvalidActionTakenException 122 */ 123 public void recordAction() throws InvalidActionTakenException { 124 MDC.put("docId", getRouteHeader().getDocumentId()); 125 updateSearchableAttributesIfPossible(); 126 127 LOG.debug("Disapproving document : " + annotation); 128 129 List actionRequests = getActionRequestService().findAllValidRequests(getPrincipal().getPrincipalId(), getDocumentId(), KewApiConstants.ACTION_REQUEST_COMPLETE_REQ); 130 LOG.debug("Checking to see if the action is legal"); 131 String errorMessage = validateActionRules(actionRequests); 132 if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) { 133 throw new InvalidActionTakenException(errorMessage); 134 } 135 136 LOG.debug("Record the disapproval action"); 137 Recipient delegator = findDelegatorForActionRequests(actionRequests); 138 ActionTakenValue actionTaken = saveActionTaken(delegator); 139 140 LOG.debug("Deactivate all pending action requests"); 141 actionRequests = getActionRequestService().findPendingByDoc(getDocumentId()); 142 getActionRequestService().deactivateRequests(actionTaken, actionRequests); 143 notifyActionTaken(actionTaken); 144 145 LOG.debug("Sending Acknowledgements to all previous approvers/completers"); 146 // Generate the notification requests in the first node we find that the current user has an approve request 147 RouteNodeInstance notificationNodeInstance = null; 148 // if (actionRequests.size() > 0) { //I don't see why this matters let me know if it does rk 149 notificationNodeInstance = ((ActionRequestValue)actionRequests.get(0)).getNodeInstance(); 150 // } 151 generateAcknowledgementsToPreviousActionTakers(notificationNodeInstance); 152 153 LOG.debug("Disapproving document"); 154 try { 155 String oldStatus = getRouteHeader().getDocRouteStatus(); 156 routeHeader.markDocumentDisapproved(); 157 String newStatus = getRouteHeader().getDocRouteStatus(); 158 KEWServiceLocator.getRouteHeaderService().saveRouteHeader(routeHeader); 159 notifyStatusChange(newStatus, oldStatus); 160 } catch (WorkflowException ex) { 161 LOG.warn(ex, ex); 162 throw new InvalidActionTakenException(ex.getMessage()); 163 } 164 } 165 }