View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.actions;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.MDC;
20  import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
21  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
22  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
23  import org.kuali.rice.kew.actionrequest.Recipient;
24  import org.kuali.rice.kew.actiontaken.ActionTakenValue;
25  import org.kuali.rice.kew.api.exception.WorkflowException;
26  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
27  import org.kuali.rice.kew.api.exception.InvalidActionTakenException;
28  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
29  import org.kuali.rice.kew.service.KEWServiceLocator;
30  import org.kuali.rice.kew.api.KewApiConstants;
31  import org.kuali.rice.kew.util.Utilities;
32  import org.kuali.rice.kim.api.group.Group;
33  import org.kuali.rice.kim.api.identity.principal.PrincipalContract;
34  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
35  import org.kuali.rice.krad.util.KRADConstants;
36  
37  import java.util.Collection;
38  import java.util.HashSet;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.Set;
42  
43  
44  /**
45   * Disapproves a document. This deactivates all requests on the document and sends
46   * acknowlegde requests to anybody who had already completed or approved the document.
47   *
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class DisapproveAction extends ActionTakenEvent {
51      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DisapproveAction.class);
52  
53      /**
54       * @param rh RouteHeader for the document upon which the action is taken.
55       * @param principal User taking the action.
56       */
57      public DisapproveAction(DocumentRouteHeaderValue rh, PrincipalContract principal) {
58          super(KewApiConstants.ACTION_TAKEN_DENIED_CD, rh, principal);
59      }
60  
61      /**
62       * @param rh RouteHeader for the document upon which the action is taken.
63       * @param principal User taking the action.
64       * @param annotation User comment on the action taken
65       */
66      public DisapproveAction(DocumentRouteHeaderValue rh, PrincipalContract principal, String annotation) {
67          super(KewApiConstants.ACTION_TAKEN_DENIED_CD, rh, principal, annotation);
68      }
69  
70      /* (non-Javadoc)
71       * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List)
72       */
73      @Override
74      public String validateActionRules() {
75      	return validateActionRules(getActionRequestService().findAllPendingRequests(routeHeader.getDocumentId()));
76      }
77  
78      public String validateActionRules(List<ActionRequestValue> actionRequests) {
79          if (!getRouteHeader().isValidActionToTake(getActionPerformedCode())) {
80              return "Document is not in a state to be disapproved";
81          }
82          List<ActionRequestValue> filteredActionRequests = filterActionRequestsByCode(actionRequests, KewApiConstants.ACTION_REQUEST_COMPLETE_REQ);
83          if (!isActionCompatibleRequest(filteredActionRequests)) {
84              return "No request for the user is compatible " + "with the DISAPPROVE or DENY action";
85          }
86          return "";
87      }
88  
89      /* (non-Javadoc)
90       * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List)
91       */
92      @Override
93      public boolean isActionCompatibleRequest(List requests) {
94          // can always cancel saved or initiated document
95          if (routeHeader.isStateInitiated() || routeHeader.isStateSaved()) {
96              return true;
97          }
98  
99          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         generateNotifications(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 
166     //generate notifications to all people that have approved the document including the initiator
167     private void generateNotifications(RouteNodeInstance notificationNodeInstance)
168     {
169         String groupName = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(
170                 KewApiConstants.KEW_NAMESPACE,
171                 KRADConstants.DetailTypes.WORKGROUP_DETAIL_TYPE,
172                 KewApiConstants.NOTIFICATION_EXCLUDED_USERS_WORKGROUP_NAME_IND);
173 
174         Set<String> systemPrincipalIds = new HashSet<String>();
175 
176         if( !StringUtils.isBlank(groupName))
177         {
178             Group systemUserWorkgroup = KimApiServiceLocator.getGroupService().
179                     getGroupByNameAndNamespaceCode(Utilities.parseGroupNamespaceCode(groupName),
180                             Utilities.parseGroupName(groupName));
181 
182             List<String> principalIds = KimApiServiceLocator.
183             getGroupService().getMemberPrincipalIds( systemUserWorkgroup.getId());
184 
185             if (systemUserWorkgroup != null)
186             {
187                 for( String id : principalIds)
188                 {
189                     systemPrincipalIds.add(id);
190                 }
191             }
192         }
193         ActionRequestFactory arFactory = new ActionRequestFactory(getRouteHeader(), notificationNodeInstance);
194         Collection<ActionTakenValue> actions = KEWServiceLocator.getActionTakenService().findByDocumentId(getDocumentId());
195         //one notification per person
196         Set<String> usersNotified = new HashSet<String>();
197         for (ActionTakenValue action : actions)
198         {
199             if ((action.isApproval() || action.isCompletion()) && !usersNotified.contains(action.getPrincipalId()))
200             {
201                 if (!systemPrincipalIds.contains(action.getPrincipalId()))
202                 {
203                     ActionRequestValue request = arFactory.createNotificationRequest(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, action.getPrincipal(), getActionTakenCode(), getPrincipal(), getActionTakenCode());
204                     KEWServiceLocator.getActionRequestService().activateRequest(request);
205                     usersNotified.add(request.getPrincipalId());
206                 }
207             }
208         }
209 
210     }
211 }