View Javadoc

1   /*
2    * Copyright 2005-2008 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.kew.actions;
18  
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Set;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.log4j.MDC;
27  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
28  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
29  import org.kuali.rice.kew.actionrequest.Recipient;
30  import org.kuali.rice.kew.actiontaken.ActionTakenValue;
31  import org.kuali.rice.kew.engine.node.RouteNodeInstance;
32  import org.kuali.rice.kew.exception.InvalidActionTakenException;
33  import org.kuali.rice.kew.exception.WorkflowException;
34  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
35  import org.kuali.rice.kew.service.KEWServiceLocator;
36  import org.kuali.rice.kew.util.KEWConstants;
37  import org.kuali.rice.kew.util.Utilities;
38  import org.kuali.rice.kim.bo.Group;
39  import org.kuali.rice.kim.bo.entity.KimPrincipal;
40  import org.kuali.rice.kim.service.KIMServiceLocator;
41  import org.kuali.rice.kns.util.KNSConstants;
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, KimPrincipal principal) {
58          super(KEWConstants.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, KimPrincipal principal, String annotation) {
67          super(KEWConstants.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.getRouteHeaderId()));
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, KEWConstants.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 ( (KEWConstants.ACTION_REQUEST_APPROVE_REQ.equals(request)) ||
109                  (KEWConstants.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 InvalidActionTakenException
122      */
123     public void recordAction() throws InvalidActionTakenException {
124         MDC.put("docId", getRouteHeader().getRouteHeaderId());
125         updateSearchableAttributesIfPossible();
126 
127         LOG.debug("Disapproving document : " + annotation);
128 
129         List actionRequests = getActionRequestService().findAllValidRequests(getPrincipal().getPrincipalId(), getRouteHeaderId(), KEWConstants.ACTION_REQUEST_COMPLETE_REQ);
130         LOG.debug("Checking to see if the action is legal");
131         String errorMessage = validateActionRules(actionRequests);
132         if (!Utilities.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(getRouteHeaderId());
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 = Utilities.getKNSParameterValue(
170                                 KEWConstants.KEW_NAMESPACE,
171                                 KNSConstants.DetailTypes.WORKGROUP_DETAIL_TYPE,
172                                 KEWConstants.NOTIFICATION_EXCLUDED_USERS_WORKGROUP_NAME_IND);
173 
174         Set<String> systemPrincipalIds = new HashSet<String>();
175 
176         if( !StringUtils.isBlank(groupName))
177         {
178             Group systemUserWorkgroup = KIMServiceLocator.getIdentityManagementService().
179                 getGroupByName(Utilities.parseGroupNamespaceCode(groupName), Utilities.parseGroupName(groupName));
180 
181             List<String> principalIds = KIMServiceLocator.
182             getIdentityManagementService().getGroupMemberPrincipalIds( systemUserWorkgroup.getGroupId());
183 
184             if (systemUserWorkgroup != null)
185             {
186                 for( String id : principalIds)
187                 {
188                     systemPrincipalIds.add(id);
189                 }
190             }
191         }
192         ActionRequestFactory arFactory = new ActionRequestFactory(getRouteHeader(), notificationNodeInstance);
193         Collection<ActionTakenValue> actions = KEWServiceLocator.getActionTakenService().findByRouteHeaderId(getRouteHeaderId());
194         //one notification per person
195         Set<String> usersNotified = new HashSet<String>();
196         for (ActionTakenValue action : actions)
197         {
198             if ((action.isApproval() || action.isCompletion()) && !usersNotified.contains(action.getPrincipalId()))
199             {
200                 if (!systemPrincipalIds.contains(action.getPrincipalId()))
201                 {
202                     ActionRequestValue request = arFactory.createNotificationRequest(KEWConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, action.getPrincipal(), getActionTakenCode(), getPrincipal(), getActionTakenCode());
203                     KEWServiceLocator.getActionRequestService().activateRequest(request);
204                     usersNotified.add(request.getPrincipalId());
205                 }
206             }
207         }
208 
209     }
210 }