001 /** 002 * Copyright 2005-2014 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.ken.web.spring; 017 018 import java.io.IOException; 019 import java.util.HashMap; 020 import java.util.List; 021 import java.util.Map; 022 023 import javax.servlet.ServletException; 024 import javax.servlet.http.HttpServletRequest; 025 import javax.servlet.http.HttpServletResponse; 026 027 import org.apache.log4j.Logger; 028 import org.kuali.rice.ken.bo.NotificationBo; 029 import org.kuali.rice.ken.bo.NotificationMessageDelivery; 030 import org.kuali.rice.ken.bo.NotificationRecipientBo; 031 import org.kuali.rice.ken.bo.NotificationSenderBo; 032 import org.kuali.rice.ken.service.NotificationMessageDeliveryService; 033 import org.kuali.rice.ken.service.NotificationService; 034 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService; 035 import org.kuali.rice.ken.util.NotificationConstants; 036 import org.kuali.rice.ken.util.Util; 037 import org.kuali.rice.kew.api.KewApiConstants; 038 import org.kuali.rice.kim.api.identity.principal.Principal; 039 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 040 import org.springframework.web.servlet.ModelAndView; 041 import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 042 043 044 /** 045 * This class is the controller for the basic notification related actions - viewing, etc. 046 * @author Kuali Rice Team (rice.collab@kuali.org) 047 */ 048 public class NotificationController extends MultiActionController { 049 /** Logger for this class and subclasses */ 050 private static final Logger LOG = Logger.getLogger(NotificationController.class); 051 052 protected NotificationService notificationService; 053 protected NotificationWorkflowDocumentService notificationWorkflowDocService; 054 protected NotificationMessageDeliveryService messageDeliveryService; 055 056 /** 057 * Set the NotificationService 058 * @param notificationService 059 */ 060 public void setNotificationService(NotificationService notificationService) { 061 this.notificationService = notificationService; 062 } 063 064 /** 065 * This method sets the NotificationWorkflowDocumentService 066 * @param s 067 */ 068 public void setNotificationWorkflowDocumentService(NotificationWorkflowDocumentService s) { 069 this.notificationWorkflowDocService = s; 070 } 071 072 /** 073 * Sets the messageDeliveryService attribute value. 074 * @param messageDeliveryService The messageDeliveryService to set. 075 */ 076 public void setMessageDeliveryService(NotificationMessageDeliveryService messageDeliveryService) { 077 this.messageDeliveryService = messageDeliveryService; 078 } 079 080 /** 081 * Handles the display of the main home page in the system. 082 * @param request : a servlet request 083 * @param response : a servlet response 084 * @throws ServletException : an exception 085 * @throws IOException : an exception 086 * @return a ModelAndView object 087 */ 088 public ModelAndView displayHome(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 089 String view = "HomePage"; 090 LOG.debug("remoteUser: "+request.getRemoteUser()); 091 Map<String, Object> model = new HashMap<String, Object>(); 092 return new ModelAndView(view, model); 093 } 094 095 /** 096 * This method handles displaying the notifications that an individual sent. 097 * @param request 098 * @param response 099 * @return ModelAndView 100 * @throws ServletException 101 * @throws IOException 102 */ 103 public ModelAndView displayNotificationsSent(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 104 String view = "NotificationsSent"; 105 LOG.debug("remoteUser: "+request.getRemoteUser()); 106 Map<String, Object> model = new HashMap<String, Object>(); 107 model.put("userId", request.getRemoteUser()); 108 return new ModelAndView(view, model); 109 } 110 111 /** 112 * This method handles displaying the search screen. 113 * @param request 114 * @param response 115 * @return ModelAndView 116 * @throws ServletException 117 * @throws IOException 118 */ 119 public ModelAndView displaySearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 120 String view = "Search"; 121 LOG.debug("remoteUser: "+request.getRemoteUser()); 122 Map<String, Object> model = new HashMap<String, Object>(); 123 return new ModelAndView(view, model); 124 } 125 126 /** 127 * This method displays the user lookup screen. 128 * @param request 129 * @param response 130 * @return 131 * @throws ServletException 132 * @throws IOException 133 */ 134 public ModelAndView displayLookupUsers(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 135 String view = "LookupUsers"; 136 LOG.debug("remoteUser: "+request.getRemoteUser()); 137 Map<String, Object> model = new HashMap<String, Object>(); 138 return new ModelAndView(view, model); 139 } 140 141 /** 142 * This method displays the workgroup lookup screen. 143 * @param request 144 * @param response 145 * @return 146 * @throws ServletException 147 * @throws IOException 148 */ 149 public ModelAndView displayLookupWorkgroups(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 150 String view = "LookupWorkgroups"; 151 LOG.debug("remoteUser: "+request.getRemoteUser()); 152 Map<String, Object> model = new HashMap<String, Object>(); 153 return new ModelAndView(view, model); 154 } 155 156 157 /** 158 * This method retrieves the NotificationMessageDelivery given an HttpServletRequest which 159 * may contain EITHER a message delivery id or a workflow doc id. Therefore, this is a 160 * "special case" for handling the workflow deliverer. 161 * @param request the incoming {@link HttpServletRequest} 162 * @return the {@link NotificationMessageDelivery} or null if not found 163 */ 164 protected NotificationMessageDelivery determineMessageFromRequest(HttpServletRequest request) { 165 /** 166 * We can get the NotificationMessageDelivery object given a workflow ID or a NotificationMessageDelivery 167 * Id. This method might be called either from a workflow action list or 168 * as a link from a message deliverer endpoint such as an email message. 169 */ 170 String messageDeliveryId = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.MSG_DELIVERY_ID); 171 String delivererId = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.DELIVERER_ID); 172 if (delivererId == null) { 173 delivererId = request.getParameter(KewApiConstants.DOCUMENT_ID_PARAMETER); 174 } 175 176 NotificationMessageDelivery messageDelivery; 177 if (messageDeliveryId != null) { // this means that the request came in not from the action list, but rather from a delivery end point 178 LOG.debug("Looking up notification with messageDeliveryId: "+messageDeliveryId); 179 try { 180 messageDelivery = messageDeliveryService.getNotificationMessageDelivery(new Long(messageDeliveryId)); 181 } catch (Exception e) { 182 throw new RuntimeException("Error getting message with id: " + messageDeliveryId, e); 183 } 184 } else if (delivererId != null) { // this means that the request was triggered via the action list 185 LOG.debug("Looking up notification with workflowId: "+delivererId); 186 try { 187 messageDelivery = messageDeliveryService.getNotificationMessageDeliveryByDelivererId(delivererId); 188 } catch (Exception e) { 189 LOG.error("Error getting message with from deliverer id: " + delivererId, e); 190 throw new RuntimeException("Error getting message with deliverer id: " + delivererId, e); 191 } 192 } else { 193 throw new RuntimeException("Neither message ('" + NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.MSG_DELIVERY_ID 194 + "') nor deliverer id ('" + NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.DELIVERER_ID + "') were specified in the request"); 195 } 196 197 return messageDelivery; 198 } 199 200 /** 201 * @param req the {@link HttpServletRequest} 202 * @return whether the incoming request was from the action list 203 */ 204 protected boolean requestIsFromKEW(HttpServletRequest req) { 205 return req.getParameter(KewApiConstants.DOCUMENT_ID_PARAMETER) != null; 206 } 207 208 /** 209 * This controller handles displaying the appropriate notification details for a specific record. 210 * @param request : a servlet request 211 * @param response : a servlet response 212 * @throws ServletException : an exception 213 * @throws IOException : an exception 214 * @return a ModelAndView object 215 */ 216 public ModelAndView displayNotificationDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 217 String view = "NotificationDetail"; // default to full view 218 219 String principalNm = request.getRemoteUser(); 220 String command = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.COMMAND); 221 String standaloneWindow = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW); 222 223 NotificationMessageDelivery messageDelivery = determineMessageFromRequest(request); 224 // now get the notification from the message delivery object 225 NotificationBo notification = messageDelivery.getNotification(); 226 boolean actionable = false; 227 228 if (requestIsFromKEW(request)) { 229 // check to see if this was a standalone window by examining the command from KEW before setting it to INLINE to force an inline view 230 if(command != null && 231 (command.equals(NotificationConstants.NOTIFICATION_DETAIL_VIEWS.NORMAL_VIEW) || 232 command.equals(NotificationConstants.NOTIFICATION_DETAIL_VIEWS.DOC_SEARCH_VIEW))) { 233 standaloneWindow = "true"; 234 } 235 236 // we want all messages from the action list in line 237 command = NotificationConstants.NOTIFICATION_DETAIL_VIEWS.INLINE; 238 } 239 240 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalNm); 241 if (principal != null) { 242 actionable = (principal.getPrincipalId()).equals(messageDelivery.getUserRecipientId()) && NotificationConstants.MESSAGE_DELIVERY_STATUS.DELIVERED.equals(messageDelivery.getMessageDeliveryStatus()); 243 } else { 244 throw new RuntimeException("There is no principal for principalNm " + principalNm); 245 } 246 247 List<NotificationSenderBo> senders = notification.getSenders(); 248 List<NotificationRecipientBo> recipients = notification.getRecipients(); 249 250 String contenthtml = Util.transformContent(notification); 251 252 // check to see if the details need to be rendered in line (no stuff around them) 253 if (command != null && command.equals(NotificationConstants.NOTIFICATION_DETAIL_VIEWS.INLINE)) { 254 view = "NotificationDetailInline"; 255 } 256 257 Map<String, Object> model = new HashMap<String, Object>(); 258 model.put("notification", notification); 259 model.put("senders", senders); 260 model.put("recipients", recipients); 261 model.put("contenthtml", contenthtml); 262 model.put("messageDeliveryId", messageDelivery.getId()); 263 model.put("command", command); 264 model.put("actionable", actionable); 265 model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, standaloneWindow); 266 return new ModelAndView(view, model); 267 } 268 269 /** 270 * This method handles user dismissal of a message 271 * @param request : a servlet request 272 * @param response : a servlet response 273 * @return a ModelAndView object 274 */ 275 public ModelAndView dismissMessage(HttpServletRequest request, HttpServletResponse response) { 276 String command = request.getParameter("action"); 277 if (command == null) throw new RuntimeException("Dismissal command not specified"); 278 279 if (NotificationConstants.ACK_CAUSE.equals(command)) { 280 return dismissMessage(command, "Notificaton acknowledged. Please refresh your action list.", request, response); 281 } else if (NotificationConstants.FYI_CAUSE.equals(command)) { 282 return dismissMessage(command, "Action Taken. Please refresh your action list.", request, response); 283 } else { 284 throw new RuntimeException("Unknown dismissal command: " + command); 285 } 286 } 287 288 /** 289 * This method takes an action on the message delivery - dismisses it with the action/cause that comes from the 290 * UI layer 291 * @param action the action or cause of the dismissal 292 * @param message the message to display to the user 293 * @param request the HttpServletRequest 294 * @param response the HttpServletResponse 295 * @return an appropriate ModelAndView 296 */ 297 private ModelAndView dismissMessage(String action, String message, HttpServletRequest request, HttpServletResponse response) { 298 String view = "NotificationDetail"; 299 300 String principalNm = request.getRemoteUser(); 301 String messageDeliveryId = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.MSG_DELIVERY_ID); 302 String command = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.COMMAND); 303 String standaloneWindow = request.getParameter(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW); 304 305 if (messageDeliveryId == null) { 306 throw new RuntimeException("A null messageDeliveryId was provided."); 307 } 308 309 LOG.debug("messageDeliveryId: "+messageDeliveryId); 310 LOG.debug("command: "+command); 311 312 /** 313 * We can get the notification object given a workflow ID or a notification 314 * Id. This method might be called either from a workflow action list or 315 * as a link from a message deliverer endpoint such as an email message. 316 */ 317 NotificationMessageDelivery delivery = messageDeliveryService.getNotificationMessageDelivery(Long.decode(messageDeliveryId)); 318 if (delivery == null) { 319 throw new RuntimeException("Could not find message delivery with id " + messageDeliveryId); 320 } 321 NotificationBo notification = delivery.getNotification(); 322 323 /* 324 * dismiss the message delivery 325 */ 326 327 Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalNm); 328 notificationService.dismissNotificationMessageDelivery(delivery.getId(), principal.getPrincipalId(), action); 329 330 List<NotificationSenderBo> senders = notification.getSenders(); 331 List<NotificationRecipientBo> recipients = notification.getRecipients(); 332 333 String contenthtml = Util.transformContent(notification); 334 335 // first check to see if this is a standalone window, b/c if it is, we'll want to close 336 if(standaloneWindow != null && standaloneWindow.equals("true")) { 337 view = "NotificationActionTakenCloseWindow"; 338 } else { // otherwise check to see if the details need to be rendered in line (no stuff around them) 339 if (command != null && command.equals(NotificationConstants.NOTIFICATION_DETAIL_VIEWS.INLINE)) { 340 view = "NotificationDetailInline"; 341 } 342 } 343 344 Map<String, Object> model = new HashMap<String, Object>(); 345 model.put("notification", notification); 346 model.put("message", message); 347 model.put("senders", senders); 348 model.put("recipients", recipients); 349 model.put("contenthtml", contenthtml); 350 model.put("messageDeliveryId", messageDeliveryId); 351 model.put("command", command); 352 model.put(NotificationConstants.NOTIFICATION_CONTROLLER_CONSTANTS.STANDALONE_WINDOW, standaloneWindow); 353 return new ModelAndView(view, model); 354 } 355 }