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