View Javadoc

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