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 org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.kuali.rice.core.framework.persistence.dao.GenericDao;
21 import org.kuali.rice.ken.bo.NotificationBo;
22 import org.kuali.rice.ken.bo.NotificationChannelBo;
23 import org.kuali.rice.ken.bo.NotificationChannelReviewerBo;
24 import org.kuali.rice.ken.bo.NotificationContentTypeBo;
25 import org.kuali.rice.ken.bo.NotificationPriorityBo;
26 import org.kuali.rice.ken.bo.NotificationProducerBo;
27 import org.kuali.rice.ken.bo.NotificationRecipientBo;
28 import org.kuali.rice.ken.bo.NotificationSenderBo;
29 import org.kuali.rice.ken.document.kew.NotificationWorkflowDocument;
30 import org.kuali.rice.ken.exception.ErrorList;
31 import org.kuali.rice.ken.service.NotificationChannelService;
32 import org.kuali.rice.ken.service.NotificationMessageContentService;
33 import org.kuali.rice.ken.service.NotificationRecipientService;
34 import org.kuali.rice.ken.service.NotificationService;
35 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
36 import org.kuali.rice.ken.util.NotificationConstants;
37 import org.kuali.rice.ken.util.Util;
38 import org.kuali.rice.kew.api.WorkflowDocument;
39 import org.kuali.rice.kew.rule.GenericAttributeContent;
40 import org.kuali.rice.kim.api.KimConstants.KimGroupMemberTypes;
41 import org.springframework.web.servlet.ModelAndView;
42
43 import javax.servlet.ServletException;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46 import java.io.IOException;
47 import java.sql.Timestamp;
48 import java.text.ParseException;
49 import java.util.Date;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Map;
53
54
55
56
57
58
59 public class SendEventNotificationMessageController extends BaseSendNotificationController {
60
61 private static final Logger LOG = Logger
62 .getLogger(SendEventNotificationMessageController.class);
63
64 private static final String NONE_CHANNEL = "___NONE___";
65 private static final long REASONABLE_IMMEDIATE_TIME_THRESHOLD = 1000 * 60 * 5;
66
67
68
69
70
71
72 private boolean timeIsInTheFuture(long time) {
73 boolean future = (time - System.currentTimeMillis()) > REASONABLE_IMMEDIATE_TIME_THRESHOLD;
74 LOG.info("Time: " + new Date(time) + " is in the future? " + future);
75 return future;
76 }
77
78
79
80
81
82
83
84
85
86 private boolean hasPotentialRecipients(NotificationBo notification) {
87 LOG.info("notification channel " + notification.getChannel() + " is subscribable: " + notification.getChannel().isSubscribable());
88 return notification.getChannel().getRecipientLists().size() > 0 ||
89 notification.getChannel().getSubscriptions().size() > 0 ||
90 (notification.getChannel().isSubscribable() && timeIsInTheFuture(notification.getSendDateTimeValue().getTime()));
91 }
92
93 protected NotificationService notificationService;
94
95 protected NotificationWorkflowDocumentService notificationWorkflowDocService;
96
97 protected NotificationChannelService notificationChannelService;
98
99 protected NotificationRecipientService notificationRecipientService;
100
101 protected NotificationMessageContentService messageContentService;
102
103 protected GenericDao businessObjectDao;
104
105
106
107
108
109 public void setNotificationService(NotificationService notificationService) {
110 this.notificationService = notificationService;
111 }
112
113
114
115
116
117 public void setNotificationWorkflowDocumentService(
118 NotificationWorkflowDocumentService s) {
119 this.notificationWorkflowDocService = s;
120 }
121
122
123
124
125
126 public void setNotificationChannelService(
127 NotificationChannelService notificationChannelService) {
128 this.notificationChannelService = notificationChannelService;
129 }
130
131
132
133
134
135 public void setNotificationRecipientService(
136 NotificationRecipientService notificationRecipientService) {
137 this.notificationRecipientService = notificationRecipientService;
138 }
139
140
141
142
143
144 public void setMessageContentService(
145 NotificationMessageContentService notificationMessageContentService) {
146 this.messageContentService = notificationMessageContentService;
147 }
148
149
150
151
152
153 public void setBusinessObjectDao(GenericDao businessObjectDao) {
154 this.businessObjectDao = businessObjectDao;
155 }
156
157
158
159
160
161
162
163
164
165 public ModelAndView sendEventNotificationMessage(
166 HttpServletRequest request, HttpServletResponse response)
167 throws ServletException, IOException {
168 String view = "SendEventNotificationMessage";
169 LOG.debug("remoteUser: " + request.getRemoteUser());
170
171 Map<String, Object> model = setupModelForSendEventNotification(request);
172 model.put("errors", new ErrorList());
173
174 return new ModelAndView(view, model);
175 }
176
177
178
179
180
181
182 private Map<String, Object> setupModelForSendEventNotification(
183 HttpServletRequest request) {
184 Map<String, Object> model = new HashMap<String, Object>();
185 model.put("defaultSender", request.getRemoteUser());
186 model.put("channels", notificationChannelService
187 .getAllNotificationChannels());
188 model.put("priorities", businessObjectDao
189 .findAll(NotificationPriorityBo.class));
190
191 String sendDateTime = request.getParameter("sendDateTime");
192 String currentDateTime = Util.getCurrentDateTime();
193 if (StringUtils.isEmpty(sendDateTime)) {
194 sendDateTime = currentDateTime;
195 }
196 model.put("sendDateTime", sendDateTime);
197
198
199
200 if (request.getParameter("originalDateTime") == null) {
201 model.put("originalDateTime", currentDateTime);
202 } else {
203 model.put("originalDateTime", request.getParameter("originalDateTime"));
204 }
205 model.put("summary", request.getParameter("summary"));
206 model.put("description", request.getParameter("description"));
207 model.put("location", request.getParameter("location"));
208 model.put("startDateTime", request.getParameter("startDateTime"));
209 model.put("stopDateTime", request.getParameter("stopDateTime"));
210
211 model.put("userRecipients", request.getParameter("userRecipients"));
212 model.put("workgroupRecipients", request.getParameter("workgroupRecipients"));
213 model.put("workgroupNamespaceCodes", request.getParameter("workgroupNamespaceCodes"));
214
215 return model;
216 }
217
218
219
220
221
222
223
224
225
226 public ModelAndView submitEventNotificationMessage(
227 HttpServletRequest request, HttpServletResponse response)
228 throws ServletException, IOException {
229 LOG.debug("remoteUser: " + request.getRemoteUser());
230
231
232
233 String initiatorId = request.getRemoteUser();
234
235
236 WorkflowDocument document;
237 Map<String, Object> model = new HashMap<String, Object>();
238 String view;
239 try {
240 document = NotificationWorkflowDocument.createNotificationDocument(
241 initiatorId,
242 NotificationConstants.KEW_CONSTANTS.SEND_NOTIFICATION_REQ_DOC_TYPE);
243
244
245 NotificationBo 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<NotificationChannelReviewerBo> reviewers = notification.getChannel().getReviewers();
253 int ui = 0;
254 int gi = 0;
255 for (NotificationChannelReviewerBo 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.setApplicationContent(notificationAsXml);
274 document.setAttributeContent("<attributeContent>" + gac.generateContent(attrFields) + "</attributeContent>");
275
276 document.setTitle(notification.getTitle());
277
278 document.route("This message was submitted via the event notification message submission form by user "
279 + initiatorId);
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 NotificationBo populateNotificationInstance(
310 HttpServletRequest request, Map<String, Object> model)
311 throws IllegalArgumentException, ErrorList {
312 ErrorList errors = new ErrorList();
313
314 NotificationBo notification = new NotificationBo();
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 NotificationChannelBo channel = Util.retrieveFieldReference("channel",
480 "name", channelName, NotificationChannelBo.class,
481 businessObjectDao);
482 notification.setChannel(channel);
483
484 NotificationPriorityBo priority = Util.retrieveFieldReference("priority",
485 "name", priorityName, NotificationPriorityBo.class,
486 businessObjectDao);
487 notification.setPriority(priority);
488
489 NotificationContentTypeBo contentType = Util.retrieveFieldReference(
490 "contentType", "name",
491 NotificationConstants.CONTENT_TYPES.EVENT_CONTENT_TYPE,
492 NotificationContentTypeBo.class, businessObjectDao);
493 notification.setContentType(contentType);
494
495 NotificationProducerBo producer = Util
496 .retrieveFieldReference(
497 "producer",
498 "name",
499 NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_NAME,
500 NotificationProducerBo.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 NotificationSenderBo ns = new NotificationSenderBo();
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 NotificationRecipientBo recipient = new NotificationRecipientBo();
520 recipient.setRecipientType(KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode());
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 NotificationRecipientBo recipient = new NotificationRecipientBo();
534 recipient.setRecipientType(KimGroupMemberTypes.GROUP_MEMBER_TYPE.getCode());
535 recipient.setRecipientId(
536 getGroupService().getGroupByNamespaceCodeAndName(workgroupNamespaceCodes[i],
537 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.setSendDateTimeValue(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.setAutoRemoveDateTimeValue(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 }