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.ken.web.spring;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.ken.bo.Notification;
21  import org.kuali.rice.ken.bo.NotificationChannelReviewer;
22  import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument;
23  import org.kuali.rice.ken.service.NotificationMessageContentService;
24  import org.kuali.rice.ken.service.NotificationRecipientService;
25  import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
26  import org.kuali.rice.ken.util.NotificationConstants;
27  import org.kuali.rice.ken.util.Util;
28  import org.kuali.rice.kew.api.WorkflowDocument;
29  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
30  import org.kuali.rice.kim.api.KimConstants.KimGroupMemberTypes;
31  import org.kuali.rice.kim.api.identity.Person;
32  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
33  import org.springframework.validation.BindException;
34  import org.springframework.validation.ValidationUtils;
35  import org.springframework.web.bind.ServletRequestBindingException;
36  import org.springframework.web.servlet.ModelAndView;
37  import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
38  
39  import javax.servlet.ServletException;
40  import javax.servlet.http.HttpServletRequest;
41  import javax.servlet.http.HttpServletResponse;
42  import java.util.Date;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Map;
46  
47  
48  /**
49   * Implements reviewer Approve/Disapprove and initiator Acknowledge of a Notification requests
50   * sent to channels configured with reviewers
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public class AdministerNotificationRequestController extends MultiActionController {
54      private static final Logger LOG = Logger.getLogger(AdministerNotificationRequestController.class);
55  
56      /**
57       * Command object for this controller
58       */
59      public static class AdministerNotificationRequestCommand {
60          // incoming
61          private String docId;
62  
63          // outgoing
64          private WorkflowDocument document;
65          private Notification notification;
66          private String renderedContent;
67          private boolean valid = true;
68          private String message;
69  
70          public String getDocId() {
71              return docId;
72          }
73          public void setDocId(String docId) {
74              this.docId = docId;
75          }
76          public WorkflowDocument getDocument() {
77              return document;
78          }
79          public void setDocument(WorkflowDocument document) {
80              this.document = document;
81          }
82          public Notification getNotification() {
83              return notification;
84          }
85          public void setNotification(Notification notification) {
86              this.notification = notification;
87          }
88          public String getRenderedContent() {
89              return renderedContent;
90          }
91          public void setRenderedContent(String renderedContent) {
92              this.renderedContent = renderedContent;
93          }
94          public boolean isValid() {
95              return valid;
96          }
97          public void setValid(boolean valid) {
98              this.valid = valid;
99          }
100         public String getMessage() {
101             return message;
102         }
103         public void setMessage(String message) {
104             this.message = message;
105         }
106     }
107 
108     protected NotificationMessageContentService messageContentService;
109     protected NotificationWorkflowDocumentService workflowDocumentService;
110     protected NotificationRecipientService recipientService;
111 
112     /**
113      * Sets the messageContentService attribute value.
114      * @param messageContentService the NotificationMessageContentService impl
115      */
116     public void setMessageContentService(
117             NotificationMessageContentService notificationMessageContentService) {
118         this.messageContentService = notificationMessageContentService;
119     }
120 
121     /**
122      * Sets the workflowDocumentService attribute value.
123      * @param workflowDocumentService the NotificationWorkflowDocumentService impl
124      */
125     public void setWorkflowDocumentService(
126             NotificationWorkflowDocumentService notificationWorkflowDocumentService) {
127         this.workflowDocumentService = notificationWorkflowDocumentService;
128     }
129 
130     /**
131      * Sets the recipientService attribute value.
132      * @param recipientService the NotificationRecipientService impl
133      */
134     public void setRecipientService(
135             NotificationRecipientService notificationRecipientService) {
136         this.recipientService = notificationRecipientService;
137     }
138 
139     /**
140      * Parses the serialized Notification xml from the workflow document application content into a reconstituted
141      * Notification BO
142      * @param document the WorkflowDocument
143      * @return a Notification BO reconstituted from the serialized XML form in the workflow document
144      * @throws Exception if parsing fails
145      */
146     private Notification retrieveNotificationForWorkflowDocument(WorkflowDocument document) throws Exception {
147         String notificationAsXml = document.getApplicationContent();
148 
149         //parse out the application content into a Notification BO
150         Notification notification = messageContentService.parseSerializedNotificationXml(notificationAsXml.getBytes());
151 
152         return notification;
153     }
154 
155     /**
156      * View action that displays an approve/disapprove/acknowledge view
157      * @param request the HttpServletRequest
158      * @param response the HttpServletResponse
159      * @param command the command object bound for this MultiActionController
160      * @return a view ModelAndView
161      */
162     public ModelAndView view(HttpServletRequest request, HttpServletResponse response, AdministerNotificationRequestCommand command) {
163         // obtain a workflow user object first
164         String initiatorId = request.getRemoteUser();
165 
166         // now construct the workflow document, which will interact with workflow
167         if (command.getDocId() == null) {
168             throw new RuntimeException("An invalid document ID was recieved from KEW's action list.");
169         }
170 
171         //check to see which view is being passed to us from the notification list - pop up or inline
172         String view = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.COMMAND);
173         String standaloneWindow = "true";
174         if(view != null && view.equals(NotificationConstants.NOTIFICATION_DETAIL_VIEWS.INLINE)) {
175             standaloneWindow = "false";
176         }
177 
178         WorkflowDocument document;
179         Map<String, Object> model = new HashMap<String, Object>();
180         // set into model whether we are dealing with a pop up or an inline window
181         model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, standaloneWindow);
182         try {
183             document = NotificationWorkflowDocument.loadNotificationDocument(initiatorId, command.getDocId());
184 
185             Notification notification = retrieveNotificationForWorkflowDocument(document);
186 
187             // set up model
188             command.setDocument(document);
189             command.setNotification(notification);
190             // render the event content according to registered XSLT stylesheet
191             command.setRenderedContent(Util.transformContent(notification));
192 
193             LOG.info("notification auto remove date time: " + notification.getAutoRemoveDateTime());
194             if (document.isApproved()) {
195                 command.setValid(false);
196                 command.setMessage("This notification request has been approved.");
197             } else if (document.isDisapproved()) {
198                 command.setMessage("This notification request has been disapproved.");
199             } else if (notification.getAutoRemoveDateTime() != null && notification.getAutoRemoveDateTime().before(new Date(System.currentTimeMillis()))) {
200                 /*if (!document.stateIsCanceled()) {
201                 workflowDocumentService.terminateWorkflowDocument(new WorkflowDocument(new NetworkIdVO("notsys"), new Long(command.getDocId())));
202                 }*/
203                 // the autoremove date time has already passed...this notification request is null and void at this time
204                 boolean disapproved = document.isDisapproved();
205                 if (!document.isDisapproved()) {
206                     List<NotificationChannelReviewer> reviewers = notification.getChannel().getReviewers();
207                     String user = null;
208                     for (NotificationChannelReviewer reviewer: reviewers) {
209                         if (KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
210                             if (reviewer.getReviewerId().equals(request.getRemoteUser())) {
211                                 user = request.getRemoteUser();
212                             }
213                         } else if (KimGroupMemberTypes.GROUP_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
214                             // if it's a group
215                             String[] members = recipientService.getGroupMembers(reviewer.getReviewerId());
216                             for (String member: members) {
217                                 if (StringUtils.equals(member, request.getRemoteUser())) {
218                                     user = request.getRemoteUser();
219                                     break;
220                                 }
221                             }
222                         }
223                     }
224                     // if the current user is a reviewer, then disapprove as that user
225                     if (user != null) {
226 	                    WorkflowDocumentFactory.loadDocument(user, command.getDocId()).disapprove("Disapproving notification request.  Auto-remove datetime has already passed.");
227                         disapproved = true;
228                     }
229                 }
230                 command.setValid(false);
231                 if (disapproved) {
232                     command.setMessage("This notification request is no longer valid because the Auto-Remove date has already passed.  It has been disapproved.  Please refresh your action list.");
233                 } else {
234                     command.setMessage("This notification request is no longer valid because the Auto-Remove date has already passed.");
235                 }
236             }
237 
238             model.put(getCommandName(command), command);
239         } catch (Exception e) {
240             throw new RuntimeException(e);
241         }
242 
243         return new ModelAndView("ViewNotificationRequestDetails", model);
244     }
245 
246     /**
247      * Approve action that approves a notification request
248      * @param request the HttpServletRequest
249      * @param response the HttpServletResponse
250      * @param command the command object bound for this MultiActionController
251      * @return a view ModelAndView
252      * @throws ServletException if an error occurs during approval
253      */
254     public ModelAndView approve(HttpServletRequest request, HttpServletResponse response, AdministerNotificationRequestCommand command) throws ServletException {
255         administerEventNotificationMessage(request, response, command, "approve");
256         Map<String, Object> model = new HashMap<String, Object>();
257         model.put("workflowActionTaken", "Approved");
258         model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW));
259         return new ModelAndView("SendNotificationRequestActionTakenWindow", model);
260     }
261 
262     /**
263      * Disapprove action that disapproves a notification request
264      * @param request the HttpServletRequest
265      * @param response the HttpServletResponse
266      * @param command the command object bound for this MultiActionController
267      * @return a view ModelAndView
268      * @throws ServletException if an error occurs during disapproval
269      */
270     public ModelAndView disapprove(HttpServletRequest request, HttpServletResponse response, AdministerNotificationRequestCommand command) throws ServletException {
271         administerEventNotificationMessage(request, response, command, "disapprove");
272         Map<String, Object> model = new HashMap<String, Object>();
273         model.put("workflowActionTaken", "Disapproved");
274         model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW));
275         return new ModelAndView("SendNotificationRequestActionTakenWindow", model);
276     }
277 
278     /**
279      * Acknowledge action that acknowledges a notification request disapproval
280      * @param request the HttpServletRequest
281      * @param response the HttpServletResponse
282      * @param command the command object bound for this MultiActionController
283      * @return a view ModelAndView
284      * @throws ServletException if an error occurs during acknowledgement
285      */
286     public ModelAndView acknowledge(HttpServletRequest request, HttpServletResponse response, AdministerNotificationRequestCommand command) throws ServletException {
287         administerEventNotificationMessage(request, response, command, "acknowledge");
288         Map<String, Object> model = new HashMap<String, Object>();
289         model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW));
290         model.put("workflowActionTaken", "Acknowledged");
291         return new ModelAndView("SendNotificationRequestActionTakenWindow", model);
292     }
293 
294     /**
295      * This method handles approval/disapproval/acknowledgement of the notification request
296      * @param request the HttpServletRequest
297      * @param response the HttpServletResponse
298      * @param command the command object bound for this MultiActionController
299      * @throws ServletException
300      */
301     private void administerEventNotificationMessage(HttpServletRequest request, HttpServletResponse response, AdministerNotificationRequestCommand command, String action) throws ServletException {
302         LOG.debug("remoteUser: " + request.getRemoteUser());
303 
304         BindException bindException = new BindException(command, "command");
305         ValidationUtils.rejectIfEmpty(bindException, "docId", "Document id must be specified");
306         if (bindException.hasErrors()) {
307             throw new ServletRequestBindingException("Document id must be specified", bindException);
308         }
309 
310         // obtain a workflow user object first
311         //WorkflowIdDTO user = new WorkflowIdDTO(request.getRemoteUser());
312         String userId = request.getRemoteUser();
313 
314         try {
315             // now construct the workflow document, which will interact with workflow
316             WorkflowDocument document = NotificationWorkflowDocument.loadNotificationDocument(userId, command.getDocId());
317 
318             Notification notification = retrieveNotificationForWorkflowDocument(document);
319 
320             String initiatorPrincipalId = document.getInitiatorPrincipalId();
321             Person initiator = KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
322             String notificationBlurb =  notification.getContentType().getName() + " notification submitted by " + initiator.getName() + " for channel " + notification.getChannel().getName();
323             if ("disapprove".equals(action)) {
324                 document.disapprove("User " + userId + " disapproving " + notificationBlurb);
325             } else if ("approve".equals(action)) {
326                 document.approve("User " + userId + " approving " + notificationBlurb);
327             } else if ("acknowledge".equals(action)) {
328                 document.acknowledge("User " + userId + " acknowledging " + notificationBlurb);
329             }
330         } catch (Exception e) {
331             LOG.error("Exception occurred taking action on notification request", e);
332             throw new ServletException("Exception occurred taking action on notification request", e);
333         }
334     }
335 }