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