1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.ken.web.spring;
18
19 import java.io.IOException;
20 import java.sql.Timestamp;
21 import java.text.ParseException;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.log4j.Logger;
33 import org.kuali.rice.core.framework.persistence.dao.GenericDao;
34 import org.kuali.rice.ken.bo.Notification;
35 import org.kuali.rice.ken.bo.NotificationChannel;
36 import org.kuali.rice.ken.bo.NotificationChannelReviewer;
37 import org.kuali.rice.ken.bo.NotificationContentType;
38 import org.kuali.rice.ken.bo.NotificationPriority;
39 import org.kuali.rice.ken.bo.NotificationProducer;
40 import org.kuali.rice.ken.bo.NotificationRecipient;
41 import org.kuali.rice.ken.bo.NotificationSender;
42 import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument;
43 import org.kuali.rice.ken.exception.ErrorList;
44 import org.kuali.rice.ken.service.NotificationChannelService;
45 import org.kuali.rice.ken.service.NotificationMessageContentService;
46 import org.kuali.rice.ken.service.NotificationRecipientService;
47 import org.kuali.rice.ken.service.NotificationService;
48 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
49 import org.kuali.rice.ken.util.NotificationConstants;
50 import org.kuali.rice.ken.util.Util;
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
234 String initiatorId = request.getRemoteUser();
235
236
237 NotificationWorkflowDocument document;
238 Map<String, Object> model = new HashMap<String, Object>();
239 String view;
240 try {
241 document = NotificationWorkflowDocument.createNotificationDocument(
242 initiatorId,
243 NotificationConstants.KEW_CONSTANTS.SEND_NOTIFICATION_REQ_DOC_TYPE);
244
245
246 Notification notification = populateNotificationInstance(request, model);
247
248
249 String notificationAsXml = messageContentService
250 .generateNotificationMessage(notification);
251
252 Map<String, String> attrFields = new HashMap<String,String>();
253 List<NotificationChannelReviewer> reviewers = notification.getChannel().getReviewers();
254 int ui = 0;
255 int gi = 0;
256 for (NotificationChannelReviewer reviewer: reviewers) {
257 String prefix;
258 int index;
259 if (KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
260 prefix = "user";
261 index = ui;
262 ui++;
263 } else if (KimGroupMemberTypes.GROUP_MEMBER_TYPE.equals(reviewer.getReviewerType())) {
264 prefix = "group";
265 index = gi;
266 gi++;
267 } else {
268 LOG.error("Invalid type for reviewer " + reviewer.getReviewerId() + ": " + reviewer.getReviewerType());
269 continue;
270 }
271 attrFields.put(prefix + index, reviewer.getReviewerId());
272 }
273 GenericAttributeContent gac = new GenericAttributeContent("channelReviewers");
274 document.getDocumentContent().setApplicationContent(notificationAsXml);
275 document.getDocumentContent().setAttributeContent("<attributeContent>" + gac.generateContent(attrFields) + "</attributeContent>");
276
277 document.setTitle(notification.getTitle());
278
279 document.routeDocument("This message was submitted via the event notification message submission form by user "
280 + initiatorId);
281
282 view = "SendEventNotificationMessage";
283
284
285 ErrorList el = new ErrorList();
286 el.addError("Notification(s) sent.");
287 model.put("errors", el);
288
289 } catch (ErrorList el) {
290
291 Map<String, Object> model2 = setupModelForSendEventNotification(request);
292 model.putAll(model2);
293 model.put("errors", el);
294
295 view = "SendEventNotificationMessage";
296 } catch (Exception e) {
297 throw new RuntimeException(e);
298 }
299
300 return new ModelAndView(view, model);
301 }
302
303
304
305
306
307
308
309
310 private Notification populateNotificationInstance(
311 HttpServletRequest request, Map<String, Object> model)
312 throws IllegalArgumentException, ErrorList {
313 ErrorList errors = new ErrorList();
314
315 Notification notification = new Notification();
316
317
318
319 String channelName = request.getParameter("channelName");
320 if (StringUtils.isEmpty(channelName) || StringUtils.equals(channelName, NONE_CHANNEL)) {
321 errors.addError("You must choose a channel.");
322 } else {
323 model.put("channelName", channelName);
324 }
325
326
327 String priorityName = request.getParameter("priorityName");
328 if (StringUtils.isEmpty(priorityName)) {
329 errors.addError("You must choose a priority.");
330 } else {
331 model.put("priorityName", priorityName);
332 }
333
334
335 String senderNames = request.getParameter("senderNames");
336 String[] senders = null;
337 if (StringUtils.isEmpty(senderNames)) {
338 errors.addError("You must enter at least one sender.");
339 } else {
340 senders = StringUtils.split(senderNames, ",");
341
342 model.put("senderNames", senderNames);
343 }
344
345
346 String deliveryType = request.getParameter("deliveryType");
347 if (StringUtils.isEmpty(deliveryType)) {
348 errors.addError("You must choose a type.");
349 } else {
350 if (deliveryType
351 .equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.FYI)) {
352 deliveryType = NotificationConstants.DELIVERY_TYPES.FYI;
353 } else {
354 deliveryType = NotificationConstants.DELIVERY_TYPES.ACK;
355 }
356 model.put("deliveryType", deliveryType);
357 }
358
359
360 String originalDateTime = request.getParameter("originalDateTime");
361 Date origdate = null;
362 Date senddate = null;
363 Date removedate = null;
364 try {
365 origdate = Util.parseUIDateTime(originalDateTime);
366 } catch (ParseException pe) {
367 errors.addError("Original date is invalid.");
368 }
369
370 String sendDateTime = request.getParameter("sendDateTime");
371 if (StringUtils.isBlank(sendDateTime)) {
372 sendDateTime = Util.getCurrentDateTime();
373 }
374
375 try {
376 senddate = Util.parseUIDateTime(sendDateTime);
377 } catch (ParseException pe) {
378 errors.addError("You specified an invalid Send Date/Time. Please use the calendar picker.");
379 }
380
381 if(senddate != null && senddate.before(origdate)) {
382 errors.addError("Send Date/Time cannot be in the past.");
383 }
384
385 model.put("sendDateTime", sendDateTime);
386
387
388 String autoRemoveDateTime = request.getParameter("autoRemoveDateTime");
389 if (StringUtils.isNotBlank(autoRemoveDateTime)) {
390 try {
391 removedate = Util.parseUIDateTime(autoRemoveDateTime);
392 } catch (ParseException pe) {
393 errors.addError("You specified an invalid Auto-Remove Date/Time. Please use the calendar picker.");
394 }
395
396 if(removedate != null) {
397 if(removedate.before(origdate)) {
398 errors.addError("Auto-Remove Date/Time cannot be in the past.");
399 } else if (senddate != null && removedate.before(senddate)){
400 errors.addError("Auto-Remove Date/Time cannot be before the Send Date/Time.");
401 }
402 }
403 }
404
405 model.put("autoRemoveDateTime", autoRemoveDateTime);
406
407
408 String[] userRecipients = parseUserRecipients(request);
409
410
411 String[] workgroupRecipients = parseWorkgroupRecipients(request);
412
413
414 String[] workgroupNamespaceCodes = parseWorkgroupNamespaceCodes(request);
415
416
417 String title = request.getParameter("title");
418 if (!StringUtils.isEmpty(title)) {
419 model.put("title", title);
420 } else {
421 errors.addError("You must fill in a title");
422 }
423
424
425 String message = request.getParameter("message");
426 if (StringUtils.isEmpty(message)) {
427 errors.addError("You must fill in a message.");
428 } else {
429 model.put("message", message);
430 }
431
432
433
434
435 String startDateTime = request.getParameter("startDateTime");
436 if (StringUtils.isEmpty(startDateTime)) {
437 errors.addError("You must fill in a start date/time.");
438 } else {
439 model.put("startDateTime", startDateTime);
440 }
441
442
443 String stopDateTime = request.getParameter("stopDateTime");
444 if (StringUtils.isEmpty(stopDateTime)) {
445 errors.addError("You must fill in a stop date/time.");
446 } else {
447 model.put("stopDateTime", stopDateTime);
448 }
449
450
451 String summary = request.getParameter("summary");
452 if (StringUtils.isEmpty(summary)) {
453 errors.addError("You must fill in a summary.");
454 } else {
455 model.put("summary", summary);
456 }
457
458
459 String description = request.getParameter("description");
460 if (StringUtils.isEmpty(description)) {
461 errors.addError("You must fill in a description.");
462 } else {
463 model.put("description", description);
464 }
465
466
467 String location = request.getParameter("location");
468 if (StringUtils.isEmpty(location)) {
469 errors.addError("You must fill in a location.");
470 } else {
471 model.put("location", location);
472 }
473
474
475 if (errors.getErrors().size() > 0) {
476 throw errors;
477 }
478
479
480 NotificationChannel channel = Util.retrieveFieldReference("channel",
481 "name", channelName, NotificationChannel.class,
482 businessObjectDao);
483 notification.setChannel(channel);
484
485 NotificationPriority priority = Util.retrieveFieldReference("priority",
486 "name", priorityName, NotificationPriority.class,
487 businessObjectDao);
488 notification.setPriority(priority);
489
490 NotificationContentType contentType = Util.retrieveFieldReference(
491 "contentType", "name",
492 NotificationConstants.CONTENT_TYPES.EVENT_CONTENT_TYPE,
493 NotificationContentType.class, businessObjectDao);
494 notification.setContentType(contentType);
495
496 NotificationProducer producer = Util
497 .retrieveFieldReference(
498 "producer",
499 "name",
500 NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_NAME,
501 NotificationProducer.class, businessObjectDao);
502 notification.setProducer(producer);
503
504 for (String senderName : senders) {
505 if (StringUtils.isEmpty(senderName)) {
506 errors.addError("A sender's name cannot be blank.");
507 } else {
508 NotificationSender ns = new NotificationSender();
509 ns.setSenderName(senderName.trim());
510 notification.addSender(ns);
511 }
512 }
513
514 boolean recipientsExist = false;
515
516 if (userRecipients != null && userRecipients.length > 0) {
517 recipientsExist = true;
518 for (String userRecipientId : userRecipients) {
519 if (isUserRecipientValid(userRecipientId, errors)) {
520 NotificationRecipient recipient = new NotificationRecipient();
521 recipient.setRecipientType(KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE);
522 recipient.setRecipientId(userRecipientId);
523 notification.addRecipient(recipient);
524 }
525 }
526 }
527
528 if (workgroupRecipients != null && workgroupRecipients.length > 0) {
529 recipientsExist = true;
530 if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) {
531 if (workgroupNamespaceCodes.length == workgroupRecipients.length) {
532 for (int i = 0; i < workgroupRecipients.length; i++) {
533 if (isWorkgroupRecipientValid(workgroupRecipients[i], workgroupNamespaceCodes[i], errors)) {
534 NotificationRecipient recipient = new NotificationRecipient();
535 recipient.setRecipientType(KimGroupMemberTypes.GROUP_MEMBER_TYPE);
536 recipient.setRecipientId(
537 getIdentityManagementService().getGroupByName(workgroupNamespaceCodes[i], workgroupRecipients[i]).getId());
538 notification.addRecipient(recipient);
539 }
540 }
541 } else {
542 errors.addError("The number of groups must match the number of namespace codes");
543 }
544 } else {
545 errors.addError("You must specify a namespace code for every group name");
546 }
547 } else if (workgroupNamespaceCodes != null && workgroupNamespaceCodes.length > 0) {
548 errors.addError("You must specify a group name for every namespace code");
549 }
550
551
552 if (errors.getErrors().size() > 0) {
553 throw errors;
554 }
555
556 notification.setTitle(title);
557
558 notification.setDeliveryType(deliveryType);
559
560 Date startDate = null;
561 Date stopDate = null;
562
563 Date d = null;
564 if (StringUtils.isNotBlank(sendDateTime)) {
565 try {
566 d = Util.parseUIDateTime(sendDateTime);
567 } catch (ParseException pe) {
568 errors.addError("You specified an invalid send date and time. Please use the calendar picker.");
569 }
570 notification.setSendDateTime(new Timestamp(d.getTime()));
571 }
572
573 Date d2 = null;
574 if (StringUtils.isNotBlank(autoRemoveDateTime)) {
575 try {
576 d2 = Util.parseUIDateTime(autoRemoveDateTime);
577 if (d2.before(d)) {
578 errors.addError("Auto Remove Date/Time cannot be before Send Date/Time.");
579 }
580 } catch (ParseException pe) {
581 errors.addError("You specified an invalid auto-remove date and time. Please use the calendar picker.");
582 }
583 notification.setAutoRemoveDateTime(new Timestamp(d2.getTime()));
584 }
585
586 if (StringUtils.isNotBlank(startDateTime)) {
587 try {
588 startDate = Util.parseUIDateTime(startDateTime);
589 } catch (ParseException pe) {
590 errors.addError("You specified an invalid start date and time. Please use the calendar picker.");
591 }
592 }
593
594 if (StringUtils.isNotBlank(stopDateTime)) {
595 try {
596 stopDate = Util.parseUIDateTime(stopDateTime);
597 } catch (ParseException pe) {
598 errors.addError("You specified an invalid stop date and time. Please use the calendar picker.");
599 }
600 }
601
602 if(stopDate != null && startDate != null) {
603 if (stopDate.before(startDate)) {
604 errors.addError("Event Stop Date/Time cannot be before Event Start Date/Time.");
605 }
606 }
607
608 if (!recipientsExist && !hasPotentialRecipients(notification)) {
609 errors.addError("You must specify at least one user or group recipient.");
610 }
611
612
613 if (errors.getErrors().size() > 0) {
614 throw errors;
615 }
616
617 notification
618 .setContent(NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_EVENT_OPEN
619 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_OPEN
620 + message
621 + NotificationConstants.XML_MESSAGE_CONSTANTS.MESSAGE_CLOSE
622 + "<event>\n"
623 + " <summary>" + summary + "</summary>\n"
624 + " <description>" + description + "</description>\n"
625 + " <location>" + location + "</location>\n"
626 + " <startDateTime>" + Util.toUIDateTimeString(startDate) + "</startDateTime>\n"
627 + " <stopDateTime>" + Util.toUIDateTimeString(stopDate) + "</stopDateTime>\n"
628 + "</event>"
629 + NotificationConstants.XML_MESSAGE_CONSTANTS.CONTENT_CLOSE);
630
631 return notification;
632 }
633 }