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    }