1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.ken.web.spring;
17
18 import java.io.IOException;
19 import java.sql.Timestamp;
20 import java.text.ParseException;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import javax.servlet.ServletException;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.log4j.Logger;
32 import org.kuali.rice.core.dao.GenericDao;
33 import org.kuali.rice.ken.bo.Notification;
34 import org.kuali.rice.ken.bo.NotificationChannel;
35 import org.kuali.rice.ken.bo.NotificationChannelReviewer;
36 import org.kuali.rice.ken.bo.NotificationContentType;
37 import org.kuali.rice.ken.bo.NotificationPriority;
38 import org.kuali.rice.ken.bo.NotificationProducer;
39 import org.kuali.rice.ken.bo.NotificationRecipient;
40 import org.kuali.rice.ken.bo.NotificationSender;
41 import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument;
42 import org.kuali.rice.ken.exception.ErrorList;
43 import org.kuali.rice.ken.service.NotificationChannelService;
44 import org.kuali.rice.ken.service.NotificationMessageContentService;
45 import org.kuali.rice.ken.service.NotificationRecipientService;
46 import org.kuali.rice.ken.service.NotificationService;
47 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
48 import org.kuali.rice.ken.util.NotificationConstants;
49 import org.kuali.rice.ken.util.Util;
50 import org.kuali.rice.kew.dto.WorkflowIdDTO;
51 import org.kuali.rice.kew.rule.GenericAttributeContent;
52 import org.kuali.rice.kim.util.KimConstants.KimGroupMemberTypes;
53 import org.springframework.web.servlet.ModelAndView;
54
55
56
57
58
59
60 public class SendEventNotificationMessageController extends BaseSendNotificationController {
61
62 private static final Logger LOG = Logger
63 .getLogger(SendEventNotificationMessageController.class);
64
65 private static final String NONE_CHANNEL = "___NONE___";
66 private static final long REASONABLE_IMMEDIATE_TIME_THRESHOLD = 1000 * 60 * 5;
67
68
69
70
71
72
73 private boolean timeIsInTheFuture(long time) {
74 boolean future = (time - System.currentTimeMillis()) > REASONABLE_IMMEDIATE_TIME_THRESHOLD;
75 LOG.info("Time: " + new Date(time) + " is in the future? " + future);
76 return future;
77 }
78
79
80
81
82
83
84
85
86
87 private boolean hasPotentialRecipients(Notification notification) {
88 LOG.info("notification channel " + notification.getChannel() + " is subscribable: " + notification.getChannel().isSubscribable());
89 return notification.getChannel().getRecipientLists().size() > 0 ||
90 notification.getChannel().getSubscriptions().size() > 0 ||
91 (notification.getChannel().isSubscribable() && timeIsInTheFuture(notification.getSendDateTime().getTime()));
92 }
93
94 protected NotificationService notificationService;
95
96 protected NotificationWorkflowDocumentService notificationWorkflowDocService;
97
98 protected NotificationChannelService notificationChannelService;
99
100 protected NotificationRecipientService notificationRecipientService;
101
102 protected NotificationMessageContentService messageContentService;
103
104 protected GenericDao businessObjectDao;
105
106
107
108
109
110 public void setNotificationService(NotificationService notificationService) {
111 this.notificationService = notificationService;
112 }
113
114
115
116
117
118 public void setNotificationWorkflowDocumentService(
119 NotificationWorkflowDocumentService s) {
120 this.notificationWorkflowDocService = s;
121 }
122
123
124
125
126
127 public void setNotificationChannelService(
128 NotificationChannelService notificationChannelService) {
129 this.notificationChannelService = notificationChannelService;
130 }
131
132
133
134
135
136 public void setNotificationRecipientService(
137 NotificationRecipientService notificationRecipientService) {
138 this.notificationRecipientService = notificationRecipientService;
139 }
140
141
142
143
144
145 public void setMessageContentService(
146 NotificationMessageContentService notificationMessageContentService) {
147 this.messageContentService = notificationMessageContentService;
148 }
149
150
151
152
153
154 public void setBusinessObjectDao(GenericDao businessObjectDao) {
155 this.businessObjectDao = businessObjectDao;
156 }
157
158
159
160
161
162
163
164
165
166 public ModelAndView sendEventNotificationMessage(
167 HttpServletRequest request, HttpServletResponse response)
168 throws ServletException, IOException {
169 String view = "SendEventNotificationMessage";
170 LOG.debug("remoteUser: " + request.getRemoteUser());
171
172 Map<String, Object> model = setupModelForSendEventNotification(request);
173 model.put("errors", new ErrorList());
174
175 return new ModelAndView(view, model);
176 }
177
178
179
180
181
182
183 private Map<String, Object> setupModelForSendEventNotification(
184 HttpServletRequest request) {
185 Map<String, Object> model = new HashMap<String, Object>();
186 model.put("defaultSender", request.getRemoteUser());
187 model.put("channels", notificationChannelService
188 .getAllNotificationChannels());
189 model.put("priorities", businessObjectDao
190 .findAll(NotificationPriority.class));
191
192 String sendDateTime = request.getParameter("sendDateTime");
193 String currentDateTime = Util.getCurrentDateTime();
194 if (StringUtils.isEmpty(sendDateTime)) {
195 sendDateTime = currentDateTime;
196 }
197 model.put("sendDateTime", sendDateTime);
198
199
200
201 if (request.getParameter("originalDateTime") == null) {
202 model.put("originalDateTime", currentDateTime);
203 } else {
204 model.put("originalDateTime", request.getParameter("originalDateTime"));
205 }
206 model.put("summary", request.getParameter("summary"));
207 model.put("description", request.getParameter("description"));
208 model.put("location", request.getParameter("location"));
209 model.put("startDateTime", request.getParameter("startDateTime"));
210 model.put("stopDateTime", request.getParameter("stopDateTime"));
211
212 model.put("userRecipients", request.getParameter("userRecipients"));
213 model.put("workgroupRecipients", request.getParameter("workgroupRecipients"));
214 model.put("workgroupNamespaceCodes", request.getParameter("workgroupNamespaceCodes"));
215
216 return model;
217 }
218
219
220
221
222
223
224
225
226
227 public ModelAndView submitEventNotificationMessage(
228 HttpServletRequest request, HttpServletResponse response)
229 throws ServletException, IOException {
230 LOG.debug("remoteUser: " + request.getRemoteUser());
231
232
233 WorkflowIdDTO initiator = new WorkflowIdDTO(request.getRemoteUser());
234
235
236 NotificationWorkflowDocument document;
237 Map<String, Object> model = new HashMap<String, Object>();
238 String view;
239 try {
240 document = new NotificationWorkflowDocument(
241 initiator,
242 NotificationConstants.KEW_CONSTANTS.SEND_NOTIFICATION_REQ_DOC_TYPE);
243
244
245 Notification notification = populateNotificationInstance(request, model);
246
247
248 String notificationAsXml = messageContentService
249 .generateNotificationMessage(notification);
250
251 Map<String, String> attrFields = new HashMap<String,String>();
252 List<NotificationChannelReviewer> reviewers = notification.getChannel().getReviewers();
253 int ui = 0;
254 int gi = 0;
255 for (NotificationChannelReviewer reviewer: reviewers) {
256 String prefix;
257 int index;
258 if (KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
259 prefix = "user";
260 index = ui;
261 ui++;
262 } else if (KimGroupMemberTypes.GROUP_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
263 prefix = "group";
264 index = gi;
265 gi++;
266 } else {
267 LOG.error("Invalid type for reviewer " + reviewer.getReviewerId() + ": " + reviewer.getReviewerType());
268 continue;
269 }
270 attrFields.put(prefix + index, reviewer.getReviewerId());
271 }
272 GenericAttributeContent gac = new GenericAttributeContent("channelReviewers");
273 document.getDocumentContent().setApplicationContent(notificationAsXml);
274 document.getDocumentContent().setAttributeContent("<attributeContent>" + gac.generateContent(attrFields) + "</attributeContent>");
275
276 document.setTitle(notification.getTitle());
277
278 document.routeDocument("This message was submitted via the event notification message submission form by user "
279 + initiator.getWorkflowId());
280
281 view = "SendEventNotificationMessage";
282
283
284 ErrorList el = new ErrorList();
285 el.addError("Notification(s) sent.");
286 model.put("errors", el);
287
288 } catch (ErrorList el) {
289
290 Map<String, Object> model2 = setupModelForSendEventNotification(request);
291 model.putAll(model2);
292 model.put("errors", el);
293
294 view = "SendEventNotificationMessage";
295 } catch (Exception e) {
296 throw new RuntimeException(e);
297 }
298
299 return new ModelAndView(view, model);
300 }
301
302
303
304
305
306
307
308
309 private Notification populateNotificationInstance(
310 HttpServletRequest request, Map<String, Object> model)
311 throws IllegalArgumentException, ErrorList {
312 ErrorList errors = new ErrorList();
313
314 Notification notification = new Notification();
315
316
317
318 String channelName = request.getParameter("channelName");
319 if (StringUtils.isEmpty(channelName) || StringUtils.equals(channelName, NONE_CHANNEL)) {
320 errors.addError("You must choose a channel.");
321 } else {
322 model.put("channelName", channelName);
323 }
324
325
326 String priorityName = request.getParameter("priorityName");
327 if (StringUtils.isEmpty(priorityName)) {
328 errors.addError("You must choose a priority.");
329 } else {
330 model.put("priorityName", priorityName);
331 }
332
333
334 String senderNames = request.getParameter("senderNames");
335 String[] senders = null;
336 if (StringUtils.isEmpty(senderNames)) {
337 errors.addError("You must enter at least one sender.");
338 } else {
339 senders = StringUtils.split(senderNames, ",");
340
341 model.put("senderNames", senderNames);
342 }
343
344
345 String deliveryType = request.getParameter("deliveryType");
346 if (StringUtils.isEmpty(deliveryType)) {
347 errors.addError("You must choose a type.");
348 } else {
349 if (deliveryType
350 .equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.FYI)) {
351 deliveryType = NotificationConstants.DELIVERY_TYPES.FYI;
352 } else {
353 deliveryType = NotificationConstants.DELIVERY_TYPES.ACK;
354 }
355 model.put("deliveryType", deliveryType);
356 }
357
358
359 String originalDateTime = request.getParameter("originalDateTime");
360 Date origdate = null;
361 Date senddate = null;
362 Date removedate = null;
363 try {
364 origdate = Util.parseUIDateTime(originalDateTime);
365 } catch (ParseException pe) {
366 errors.addError("Original date is invalid.");
367 }
368
369 String sendDateTime = request.getParameter("sendDateTime");
370 if (StringUtils.isBlank(sendDateTime)) {
371 sendDateTime = Util.getCurrentDateTime();
372 }
373
374 try {
375 senddate = Util.parseUIDateTime(sendDateTime);
376 } catch (ParseException pe) {
377 errors.addError("You specified an invalid Send Date/Time. Please use the calendar picker.");
378 }
379
380 if(senddate != null && senddate.before(origdate)) {
381 errors.addError("Send Date/Time cannot be in the past.");
382 }
383
384 model.put("sendDateTime", sendDateTime);
385
386
387 String autoRemoveDateTime = request.getParameter("autoRemoveDateTime");
388 if (StringUtils.isNotBlank(autoRemoveDateTime)) {
389 try {
390 removedate = Util.parseUIDateTime(autoRemoveDateTime);
391 } catch (ParseException pe) {
392 errors.addError("You specified an invalid Auto-Remove Date/Time. Please use the calendar picker.");
393 }
394
395 if(removedate != null) {
396 if(removedate.before(origdate)) {
397 errors.addError("Auto-Remove Date/Time cannot be in the past.");
398 } else if (senddate != null && removedate.before(senddate)){
399 errors.addError("Auto-Remove Date/Time cannot be before the Send Date/Time.");
400 }
401 }
402 }
403
404 model.put("autoRemoveDateTime", autoRemoveDateTime);
405
406
407 String[] userRecipients = parseUserRecipients(request);
408
409
410 String[] workgroupRecipients = parseWorkgroupRecipients(request);
411
412
413 String[] workgroupNamespaceCodes = parseWorkgroupNamespaceCodes(request);
414
415
416 String title = request.getParameter("title");
417 if (!StringUtils.isEmpty(title)) {
418 model.put("title", title);
419 } else {
420 errors.addError("You must fill in a title");
421 }
422
423
424 String message = request.getParameter("message");
425 if (StringUtils.isEmpty(message)) {
426 errors.addError("You must fill in a message.");
427 } else {
428 model.put("message", message);
429 }
430
431
432
433
434 String startDateTime = request.getParameter("startDateTime");
435 if (StringUtils.isEmpty(startDateTime)) {
436 errors.addError("You must fill in a start date/time.");
437 } else {
438 model.put("startDateTime", startDateTime);
439 }
440
441
442 String stopDateTime = request.getParameter("stopDateTime");
443 if (StringUtils.isEmpty(stopDateTime)) {
444 errors.addError("You must fill in a stop date/time.");
445 } else {
446 model.put("stopDateTime", stopDateTime);
447 }
448
449
450 String summary = request.getParameter("summary");
451 if (StringUtils.isEmpty(summary)) {
452 errors.addError("You must fill in a summary.");
453 } else {
454 model.put("summary", summary);
455 }
456
457
458 String description = request.getParameter("description");
459 if (StringUtils.isEmpty(description)) {
460 errors.addError("You must fill in a description.");
461 } else {
462 model.put("description", description);
463 }
464
465
466 String location = request.getParameter("location");
467 if (StringUtils.isEmpty(location)) {
468 errors.addError("You must fill in a location.");
469 } else {
470 model.put("location", location);
471 }
472
473
474 if (errors.getErrors().size() > 0) {
475 throw errors;
476 }
477
478
479 NotificationChannel channel = Util.retrieveFieldReference("channel",
480 "name", channelName, NotificationChannel.class,
481 businessObjectDao);
482 notification.setChannel(channel);
483
484 NotificationPriority priority = Util.retrieveFieldReference("priority",
485 "name", priorityName, NotificationPriority.class,
486 businessObjectDao);
487 notification.setPriority(priority);
488
489 NotificationContentType contentType = Util.retrieveFieldReference(
490 "contentType", "name",
491 NotificationConstants.CONTENT_TYPES.EVENT_CONTENT_TYPE,
492 NotificationContentType.class, businessObjectDao);
493 notification.setContentType(contentType);
494
495 NotificationProducer producer = Util
496 .retrieveFieldReference(
497 "producer",
498 "name",
499 NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_NAME,
500 NotificationProducer.class, businessObjectDao);
501 notification.setProducer(producer);
502
503 for (String senderName : senders) {
504 if (StringUtils.isEmpty(senderName)) {
505 errors.addError("A sender's name cannot be blank.");
506 } else {
507 NotificationSender ns = new NotificationSender();
508 ns.setSenderName(senderName.trim());
509 notification.addSender(ns);
510 }
511 }
512
513 boolean recipientsExist = false;
514
515 if (userRecipients != null && userRecipients.length > 0) {
516 recipientsExist = true;
517 for (String userRecipientId : userRecipients) {
518 if (isUserRecipientValid(userRecipientId, errors)) {
519 NotificationRecipient recipient = new NotificationRecipient();
520 recipient.setRecipientType(KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE);
521 recipient.setRecipientId(userRecipientId);
522 notification.addRecipient(recipient);
523 }
524 }
525 }
526
527 if (workgroupRecipients != null && workgroupRecipients.length > 0) {
528 recipientsExist = true;
529 if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) {
530 if (workgroupNamespaceCodes.length == workgroupRecipients.length) {
531 for (int i = 0; i < workgroupRecipients.length; i++) {
532 if (isWorkgroupRecipientValid(workgroupRecipients[i], workgroupNamespaceCodes[i], errors)) {
533 NotificationRecipient recipient = new NotificationRecipient();
534 recipient.setRecipientType(KimGroupMemberTypes.GROUP_MEMBER_TYPE);
535 recipient.setRecipientId(
536 getIdentityManagementService().getGroupByName(workgroupNamespaceCodes[i], workgroupRecipients[i]).getGroupId());
537 notification.addRecipient(recipient);
538 }
539 }
540 } else {
541 errors.addError("The number of groups must match the number of namespace codes");
542 }
543 } else {
544 errors.addError("You must specify a namespace code for every group name");
545 }
546 } else if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) {
547 errors.addError("You must specify a group name for every namespace code");
548 }
549
550
551 if (errors.getErrors().size() > 0) {
552 throw errors;
553 }
554
555 notification.setTitle(title);
556
557 notification.setDeliveryType(deliveryType);
558
559 Date startDate = null;
560 Date stopDate = null;
561
562 Date d = null;
563 if (StringUtils.isNotBlank(sendDateTime)) {
564 try {
565 d = Util.parseUIDateTime(sendDateTime);
566 } catch (ParseException pe) {
567 errors.addError("You specified an invalid send date and time. Please use the calendar picker.");
568 }
569 notification.setSendDateTime(new Timestamp(d.getTime()));
570 }
571
572 Date d2 = null;
573 if (StringUtils.isNotBlank(autoRemoveDateTime)) {
574 try {
575 d2 = Util.parseUIDateTime(autoRemoveDateTime);
576 if (d2.before(d)) {
577 errors.addError("Auto Remove Date/Time cannot be before Send Date/Time.");
578 }
579 } catch (ParseException pe) {
580 errors.addError("You specified an invalid auto-remove date and time. Please use the calendar picker.");
581 }
582 notification.setAutoRemoveDateTime(new Timestamp(d2.getTime()));
583 }
584
585 if (StringUtils.isNotBlank(startDateTime)) {
586 try {
587 startDate = Util.parseUIDateTime(startDateTime);
588 } catch (ParseException pe) {
589 errors.addError("You specified an invalid start date and time. Please use the calendar picker.");
590 }
591 }
592
593 if (StringUtils.isNotBlank(stopDateTime)) {
594 try {
595 stopDate = Util.parseUIDateTime(stopDateTime);
596 } catch (ParseException pe) {
597 errors.addError("You specified an invalid stop date and time. Please use the calendar picker.");
598 }
599 }
600
601 if(stopDate != null && startDate != null) {
602 if (stopDate.before(startDate)) {
603 errors.addError("Event Stop Date/Time cannot be before Event Start Date/Time.");
604 }
605 }
606
607 if (!recipientsExist && !hasPotentialRecipients(notification)) {
608 errors.addError("You must specify at least one user or group recipient.");
609 }
610
611
612 if (errors.getErrors().size() > 0) {
613 throw errors;
614 }
615
616 notification
617 .setContent(NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_EVENT_OPEN
618 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_OPEN
619 + message
620 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_CLOSE
621 + "<event>\n"
622 + " <summary>" + summary + "</summary>\n"
623 + " <description>" + description + "</description>\n"
624 + " <location>" + location + "</location>\n"
625 + " <startDateTime>" + Util.toUIDateTimeString(startDate) + "</startDateTime>\n"
626 + " <stopDateTime>" + Util.toUIDateTimeString(stopDate) + "</stopDateTime>\n"
627 + "</event>"
628 + NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_CLOSE);
629
630 return notification;
631 }
632 }