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    }