001
014
015 package com.liferay.portal.util;
016
017 import com.liferay.mail.model.FileAttachment;
018 import com.liferay.mail.service.MailServiceUtil;
019 import com.liferay.portal.kernel.exception.PortalException;
020 import com.liferay.portal.kernel.log.Log;
021 import com.liferay.portal.kernel.log.LogFactoryUtil;
022 import com.liferay.portal.kernel.mail.MailMessage;
023 import com.liferay.portal.kernel.mail.SMTPAccount;
024 import com.liferay.portal.kernel.messaging.DestinationNames;
025 import com.liferay.portal.kernel.messaging.MessageBusUtil;
026 import com.liferay.portal.kernel.util.ClassLoaderPool;
027 import com.liferay.portal.kernel.util.EscapableObject;
028 import com.liferay.portal.kernel.util.GetterUtil;
029 import com.liferay.portal.kernel.util.HtmlEscapableObject;
030 import com.liferay.portal.kernel.util.HtmlUtil;
031 import com.liferay.portal.kernel.util.LocaleUtil;
032 import com.liferay.portal.kernel.util.ObjectValuePair;
033 import com.liferay.portal.kernel.util.StringPool;
034 import com.liferay.portal.kernel.util.StringUtil;
035 import com.liferay.portal.kernel.util.Validator;
036 import com.liferay.portal.model.Company;
037 import com.liferay.portal.model.Group;
038 import com.liferay.portal.model.Subscription;
039 import com.liferay.portal.model.User;
040 import com.liferay.portal.security.permission.PermissionChecker;
041 import com.liferay.portal.security.permission.PermissionCheckerFactoryUtil;
042 import com.liferay.portal.service.CompanyLocalServiceUtil;
043 import com.liferay.portal.service.GroupLocalServiceUtil;
044 import com.liferay.portal.service.ServiceContext;
045 import com.liferay.portal.service.SubscriptionLocalServiceUtil;
046 import com.liferay.portal.service.UserLocalServiceUtil;
047 import com.liferay.portal.service.permission.SubscriptionPermissionUtil;
048
049 import java.io.File;
050 import java.io.IOException;
051 import java.io.ObjectInputStream;
052 import java.io.ObjectOutputStream;
053 import java.io.Serializable;
054
055 import java.util.ArrayList;
056 import java.util.HashMap;
057 import java.util.HashSet;
058 import java.util.List;
059 import java.util.Locale;
060 import java.util.Map;
061 import java.util.Set;
062
063 import javax.mail.internet.InternetAddress;
064
065
070 public class SubscriptionSender implements Serializable {
071
072 public void addFileAttachment(File file) {
073 addFileAttachment(file, null);
074 }
075
076 public void addFileAttachment(File file, String fileName) {
077 if (file == null) {
078 return;
079 }
080
081 if (fileAttachments == null) {
082 fileAttachments = new ArrayList<FileAttachment>();
083 }
084
085 FileAttachment attachment = new FileAttachment(file, fileName);
086
087 fileAttachments.add(attachment);
088 }
089
090 public void addPersistedSubscribers(String className, long classPK) {
091 ObjectValuePair<String, Long> ovp = new ObjectValuePair<String, Long>(
092 className, classPK);
093
094 _persistestedSubscribersOVPs.add(ovp);
095 }
096
097 public void addRuntimeSubscribers(String toAddress, String toName) {
098 ObjectValuePair<String, String> ovp =
099 new ObjectValuePair<String, String>(toAddress, toName);
100
101 _runtimeSubscribersOVPs.add(ovp);
102 }
103
104 public void flushNotifications() throws Exception {
105 initialize();
106
107 Thread currentThread = Thread.currentThread();
108
109 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
110
111 try {
112 if ((_classLoader != null) &&
113 (contextClassLoader != _classLoader)) {
114
115 currentThread.setContextClassLoader(_classLoader);
116 }
117
118 String inferredClassName = null;
119 long inferredClassPK = 0;
120
121 if (_persistestedSubscribersOVPs.size() > 1) {
122 ObjectValuePair<String, Long> objectValuePair =
123 _persistestedSubscribersOVPs.get(
124 _persistestedSubscribersOVPs.size() - 1);
125
126 inferredClassName = objectValuePair.getKey();
127 inferredClassPK = objectValuePair.getValue();
128 }
129
130 for (ObjectValuePair<String, Long> ovp :
131 _persistestedSubscribersOVPs) {
132
133 String className = ovp.getKey();
134 long classPK = ovp.getValue();
135
136 List<Subscription> subscriptions =
137 SubscriptionLocalServiceUtil.getSubscriptions(
138 companyId, className, classPK);
139
140 for (Subscription subscription : subscriptions) {
141 try {
142 notifySubscriber(
143 subscription, inferredClassName, inferredClassPK);
144 }
145 catch (PortalException pe) {
146 _log.error(
147 "Unable to process subscription: " + subscription);
148 }
149 }
150
151 if (bulk) {
152 Locale locale = LocaleUtil.getDefault();
153
154 InternetAddress to = new InternetAddress(
155 replaceContent(replyToAddress, locale),
156 replaceContent(replyToAddress, locale));
157
158 sendEmail(to, locale);
159 }
160 }
161
162 _persistestedSubscribersOVPs.clear();
163
164 for (ObjectValuePair<String, String> ovp :
165 _runtimeSubscribersOVPs) {
166
167 String toAddress = ovp.getKey();
168
169 if (Validator.isNull(toAddress)) {
170 continue;
171 }
172
173 if (_sentEmailAddresses.contains(toAddress)) {
174 if (_log.isDebugEnabled()) {
175 _log.debug(
176 "Do not send a duplicate email to " + toAddress);
177 }
178
179 continue;
180 }
181
182 if (_log.isDebugEnabled()) {
183 _log.debug(
184 "Add " + toAddress + " to the list of users who " +
185 "have received an email");
186 }
187
188 _sentEmailAddresses.add(toAddress);
189
190 String toName = ovp.getValue();
191
192 InternetAddress to = new InternetAddress(toAddress, toName);
193
194 sendEmail(to, LocaleUtil.getDefault());
195 }
196
197 _runtimeSubscribersOVPs.clear();
198 }
199 finally {
200 if ((_classLoader != null) &&
201 (contextClassLoader != _classLoader)) {
202
203 currentThread.setContextClassLoader(contextClassLoader);
204 }
205 }
206 }
207
208 public void flushNotificationsAsync() {
209 Thread currentThread = Thread.currentThread();
210
211 _classLoader = currentThread.getContextClassLoader();
212
213 MessageBusUtil.sendMessage(DestinationNames.SUBSCRIPTION_SENDER, this);
214 }
215
216 public Object getContextAttribute(String key) {
217 return _context.get(key);
218 }
219
220 public String getMailId() {
221 return this.mailId;
222 }
223
224 public void initialize() throws Exception {
225 if (_initialized) {
226 return;
227 }
228
229 _initialized = true;
230
231 Company company = CompanyLocalServiceUtil.getCompany(companyId);
232
233 setContextAttribute("[$COMPANY_ID$]", company.getCompanyId());
234 setContextAttribute("[$COMPANY_MX$]", company.getMx());
235 setContextAttribute("[$COMPANY_NAME$]", company.getName());
236 setContextAttribute("[$PORTAL_URL$]", company.getPortalURL(groupId));
237
238 if (groupId > 0) {
239 Group group = GroupLocalServiceUtil.getGroup(groupId);
240
241 setContextAttribute("[$SITE_NAME$]", group.getDescriptiveName());
242 }
243
244 if ((userId > 0) && Validator.isNotNull(_contextUserPrefix)) {
245 setContextAttribute(
246 "[$" + _contextUserPrefix + "_USER_ADDRESS$]",
247 PortalUtil.getUserEmailAddress(userId));
248 setContextAttribute(
249 "[$" + _contextUserPrefix + "_USER_NAME$]",
250 PortalUtil.getUserName(userId, StringPool.BLANK));
251 }
252
253 mailId = PortalUtil.getMailId(
254 company.getMx(), _mailIdPopPortletPrefix, _mailIdIds);
255 }
256
257 public void setBody(String body) {
258 this.body = body;
259 }
260
261 public void setBulk(boolean bulk) {
262 this.bulk = bulk;
263 }
264
265 public void setCompanyId(long companyId) {
266 this.companyId = companyId;
267 }
268
269 public void setContextAttribute(String key, EscapableObject<String> value) {
270 _context.put(key, value);
271 }
272
273 public void setContextAttribute(String key, Object value) {
274 setContextAttribute(key, value, true);
275 }
276
277 public void setContextAttribute(String key, Object value, boolean escape) {
278 setContextAttribute(
279 key,
280 new HtmlEscapableObject<String>(String.valueOf(value), escape));
281 }
282
283 public void setContextAttributes(Object... values) {
284 for (int i = 0; i < values.length; i += 2) {
285 setContextAttribute(String.valueOf(values[i]), values[i + 1]);
286 }
287 }
288
289 public void setContextUserPrefix(String contextUserPrefix) {
290 _contextUserPrefix = contextUserPrefix;
291 }
292
293 public void setFrom(String fromAddress, String fromName) {
294 this.fromAddress = fromAddress;
295 this.fromName = fromName;
296 }
297
298 public void setGroupId(long groupId) {
299 this.groupId = groupId;
300 }
301
302 public void setHtmlFormat(boolean htmlFormat) {
303 this.htmlFormat = htmlFormat;
304 }
305
306 public void setInReplyTo(String inReplyTo) {
307 this.inReplyTo = inReplyTo;
308 }
309
310 public void setLocalizedBodyMap(Map<Locale, String> localizedBodyMap) {
311 this.localizedBodyMap = localizedBodyMap;
312 }
313
314 public void setLocalizedSubjectMap(
315 Map<Locale, String> localizedSubjectMap) {
316
317 this.localizedSubjectMap = localizedSubjectMap;
318 }
319
320 public void setMailId(String popPortletPrefix, Object... ids) {
321 _mailIdPopPortletPrefix = popPortletPrefix;
322 _mailIdIds = ids;
323 }
324
325 public void setPortletId(String portletId) {
326 this.portletId = portletId;
327 }
328
329 public void setReplyToAddress(String replyToAddress) {
330 this.replyToAddress = replyToAddress;
331 }
332
333
336 public void setScopeGroupId(long scopeGroupId) {
337 try {
338 Group group = GroupLocalServiceUtil.getGroup(scopeGroupId);
339
340 if (group.isLayout()) {
341 groupId = group.getParentGroupId();
342 }
343 else {
344 groupId = scopeGroupId;
345 }
346 }
347 catch (Exception e) {
348 }
349
350 this.scopeGroupId = scopeGroupId;
351 }
352
353 public void setServiceContext(ServiceContext serviceContext) {
354 this.serviceContext = serviceContext;
355 }
356
357 public void setSMTPAccount(SMTPAccount smtpAccount) {
358 this.smtpAccount = smtpAccount;
359 }
360
361 public void setSubject(String subject) {
362 this.subject = subject;
363 }
364
365 public void setUserId(long userId) {
366 this.userId = userId;
367 }
368
369 protected void deleteSubscription(Subscription subscription)
370 throws Exception {
371
372 SubscriptionLocalServiceUtil.deleteSubscription(
373 subscription.getSubscriptionId());
374 }
375
376 protected boolean hasPermission(
377 Subscription subscription, String inferredClassName,
378 long inferredClassPK, User user)
379 throws Exception {
380
381 PermissionChecker permissionChecker =
382 PermissionCheckerFactoryUtil.create(user);
383
384 return SubscriptionPermissionUtil.contains(
385 permissionChecker, subscription.getClassName(),
386 subscription.getClassPK(), inferredClassName, inferredClassPK);
387 }
388
389
393 protected boolean hasPermission(Subscription subscription, User user)
394 throws Exception {
395
396 return hasPermission(subscription, null, 0, user);
397 }
398
399
403 protected void notifySubscriber(Subscription subscription)
404 throws Exception {
405
406 notifySubscriber(subscription, null, 0);
407 }
408
409 protected void notifySubscriber(
410 Subscription subscription, String inferredClassName,
411 long inferredClassPK)
412 throws Exception {
413
414 User user = UserLocalServiceUtil.fetchUserById(
415 subscription.getUserId());
416
417 if (user == null) {
418 if (_log.isInfoEnabled()) {
419 _log.info(
420 "Subscription " + subscription.getSubscriptionId() +
421 " is stale and will be deleted");
422 }
423
424 deleteSubscription(subscription);
425
426 return;
427 }
428
429 String emailAddress = user.getEmailAddress();
430
431 if (_sentEmailAddresses.contains(emailAddress)) {
432 if (_log.isDebugEnabled()) {
433 _log.debug("Do not send a duplicate email to " + emailAddress);
434 }
435
436 return;
437 }
438 else {
439 if (_log.isDebugEnabled()) {
440 _log.debug(
441 "Add " + emailAddress +
442 " to the list of users who have received an email");
443 }
444
445 _sentEmailAddresses.add(emailAddress);
446 }
447
448 if (!user.isActive()) {
449 if (_log.isDebugEnabled()) {
450 _log.debug("Skip inactive user " + user.getUserId());
451 }
452
453 return;
454 }
455
456 try {
457 if (!hasPermission(
458 subscription, inferredClassName, inferredClassPK, user)) {
459
460 if (_log.isDebugEnabled()) {
461 _log.debug("Skip unauthorized user " + user.getUserId());
462 }
463
464 return;
465 }
466 }
467 catch (Exception e) {
468 _log.error(e, e);
469
470 return;
471 }
472
473 if (bulk) {
474 InternetAddress bulkAddress = new InternetAddress(
475 user.getEmailAddress(), user.getFullName());
476
477 if (_bulkAddresses == null) {
478 _bulkAddresses = new ArrayList<InternetAddress>();
479 }
480
481 _bulkAddresses.add(bulkAddress);
482 }
483 else {
484 try {
485 InternetAddress to = new InternetAddress(
486 user.getEmailAddress(), user.getFullName());
487
488 sendEmail(to, user.getLocale());
489 }
490 catch (Exception e) {
491 _log.error(e, e);
492 }
493 }
494 }
495
496 protected void processMailMessage(MailMessage mailMessage, Locale locale)
497 throws Exception {
498
499 InternetAddress from = mailMessage.getFrom();
500 InternetAddress to = mailMessage.getTo()[0];
501
502 String processedSubject = StringUtil.replace(
503 mailMessage.getSubject(),
504 new String[] {
505 "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
506 "[$TO_NAME$]"
507 },
508 new String[] {
509 from.getAddress(),
510 GetterUtil.getString(from.getPersonal(), from.getAddress()),
511 HtmlUtil.escape(to.getAddress()),
512 HtmlUtil.escape(
513 GetterUtil.getString(to.getPersonal(), to.getAddress()))
514 });
515
516 processedSubject = replaceContent(processedSubject, locale, false);
517
518 mailMessage.setSubject(processedSubject);
519
520 String processedBody = StringUtil.replace(
521 mailMessage.getBody(),
522 new String[] {
523 "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
524 "[$TO_NAME$]"
525 },
526 new String[] {
527 from.getAddress(),
528 GetterUtil.getString(from.getPersonal(), from.getAddress()),
529 HtmlUtil.escape(to.getAddress()),
530 HtmlUtil.escape(
531 GetterUtil.getString(to.getPersonal(), to.getAddress()))
532 });
533
534 processedBody = replaceContent(processedBody, locale, htmlFormat);
535
536 mailMessage.setBody(processedBody);
537 }
538
539 protected String replaceContent(String content, Locale locale)
540 throws Exception {
541
542 return replaceContent(content, locale, true);
543 }
544
545 protected String replaceContent(
546 String content, Locale locale, boolean escape)
547 throws Exception {
548
549 for (Map.Entry<String, EscapableObject<String>> entry :
550 _context.entrySet()) {
551
552 String key = entry.getKey();
553 EscapableObject<String> value = entry.getValue();
554
555 String valueString = null;
556
557 if (escape) {
558 valueString = value.getEscapedValue();
559 }
560 else {
561 valueString = value.getOriginalValue();
562 }
563
564 content = StringUtil.replace(content, key, valueString);
565 }
566
567 if (Validator.isNotNull(portletId)) {
568 String portletName = PortalUtil.getPortletTitle(portletId, locale);
569
570 content = StringUtil.replace(
571 content, "[$PORTLET_NAME$]", portletName);
572 }
573
574 Company company = CompanyLocalServiceUtil.getCompany(companyId);
575
576 content = StringUtil.replace(
577 content,
578 new String[] {
579 "href=\"/", "src=\"/"
580 },
581 new String[] {
582 "href=\"" + company.getPortalURL(groupId) + "/",
583 "src=\"" + company.getPortalURL(groupId) + "/"
584 });
585
586 return content;
587 }
588
589 protected void sendEmail(InternetAddress to, Locale locale)
590 throws Exception {
591
592 InternetAddress from = new InternetAddress(
593 replaceContent(fromAddress, locale),
594 replaceContent(fromName, locale));
595
596 String processedSubject = null;
597
598 if (localizedSubjectMap != null) {
599 String localizedSubject = localizedSubjectMap.get(locale);
600
601 if (Validator.isNull(localizedSubject)) {
602 Locale defaultLocale = LocaleUtil.getDefault();
603
604 processedSubject = localizedSubjectMap.get(defaultLocale);
605 }
606 else {
607 processedSubject = localizedSubject;
608 }
609 }
610 else {
611 processedSubject = this.subject;
612 }
613
614 String processedBody = null;
615
616 if (localizedBodyMap != null) {
617 String localizedBody = localizedBodyMap.get(locale);
618
619 if (Validator.isNull(localizedBody)) {
620 Locale defaultLocale = LocaleUtil.getDefault();
621
622 processedBody = localizedBodyMap.get(defaultLocale);
623 }
624 else {
625 processedBody = localizedBody;
626 }
627 }
628 else {
629 processedBody = this.body;
630 }
631
632 MailMessage mailMessage = new MailMessage(
633 from, to, processedSubject, processedBody, htmlFormat);
634
635 if (fileAttachments != null) {
636 for (FileAttachment fileAttachment : fileAttachments) {
637 mailMessage.addFileAttachment(
638 fileAttachment.getFile(), fileAttachment.getFileName());
639 }
640 }
641
642 if (bulk && (_bulkAddresses != null)) {
643 mailMessage.setBulkAddresses(
644 _bulkAddresses.toArray(
645 new InternetAddress[_bulkAddresses.size()]));
646
647 _bulkAddresses.clear();
648 }
649
650 if (inReplyTo != null) {
651 mailMessage.setInReplyTo(inReplyTo);
652 }
653
654 mailMessage.setMessageId(mailId);
655
656 if (replyToAddress != null) {
657 InternetAddress replyTo = new InternetAddress(
658 replaceContent(replyToAddress, locale),
659 replaceContent(replyToAddress, locale));
660
661 mailMessage.setReplyTo(new InternetAddress[] {replyTo});
662 }
663
664 if (smtpAccount != null) {
665 mailMessage.setSMTPAccount(smtpAccount);
666 }
667
668 processMailMessage(mailMessage, locale);
669
670 MailServiceUtil.sendEmail(mailMessage);
671 }
672
673 protected String body;
674 protected boolean bulk;
675 protected long companyId;
676 protected List<FileAttachment> fileAttachments =
677 new ArrayList<FileAttachment>();
678 protected String fromAddress;
679 protected String fromName;
680 protected long groupId;
681 protected boolean htmlFormat;
682 protected String inReplyTo;
683 protected Map<Locale, String> localizedBodyMap;
684 protected Map<Locale, String> localizedSubjectMap;
685 protected String mailId;
686 protected String portletId;
687 protected String replyToAddress;
688 protected long scopeGroupId;
689 protected ServiceContext serviceContext;
690 protected SMTPAccount smtpAccount;
691 protected String subject;
692 protected long userId;
693
694 private void readObject(ObjectInputStream objectInputStream)
695 throws ClassNotFoundException, IOException {
696
697 objectInputStream.defaultReadObject();
698
699 String servletContextName = objectInputStream.readUTF();
700
701 if (!servletContextName.isEmpty()) {
702 _classLoader = ClassLoaderPool.getClassLoader(servletContextName);
703 }
704 }
705
706 private void writeObject(ObjectOutputStream objectOutputStream)
707 throws IOException {
708
709 objectOutputStream.defaultWriteObject();
710
711 String servletContextName = StringPool.BLANK;
712
713 if (_classLoader != null) {
714 servletContextName = ClassLoaderPool.getContextName(_classLoader);
715 }
716
717 objectOutputStream.writeUTF(servletContextName);
718 }
719
720 private static Log _log = LogFactoryUtil.getLog(SubscriptionSender.class);
721
722 private List<InternetAddress> _bulkAddresses;
723 private transient ClassLoader _classLoader;
724 private Map<String, EscapableObject<String>> _context =
725 new HashMap<String, EscapableObject<String>>();
726 private String _contextUserPrefix;
727 private boolean _initialized;
728 private Object[] _mailIdIds;
729 private String _mailIdPopPortletPrefix;
730 private List<ObjectValuePair<String, Long>> _persistestedSubscribersOVPs =
731 new ArrayList<ObjectValuePair<String, Long>>();
732 private List<ObjectValuePair<String, String>> _runtimeSubscribersOVPs =
733 new ArrayList<ObjectValuePair<String, String>>();
734 private Set<String> _sentEmailAddresses = new HashSet<String>();
735
736 }