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 if ((groupId == 0) && (serviceContext != null)) {
232 setScopeGroupId(serviceContext.getScopeGroupId());
233 }
234
235 Company company = CompanyLocalServiceUtil.getCompany(companyId);
236
237 setContextAttribute("[$COMPANY_ID$]", company.getCompanyId());
238 setContextAttribute("[$COMPANY_MX$]", company.getMx());
239 setContextAttribute("[$COMPANY_NAME$]", company.getName());
240 setContextAttribute("[$PORTAL_URL$]", company.getPortalURL(groupId));
241
242 if (groupId > 0) {
243 Group group = GroupLocalServiceUtil.getGroup(groupId);
244
245 setContextAttribute("[$SITE_NAME$]", group.getDescriptiveName());
246 }
247
248 if ((userId > 0) && Validator.isNotNull(_contextUserPrefix)) {
249 setContextAttribute(
250 "[$" + _contextUserPrefix + "_USER_ADDRESS$]",
251 PortalUtil.getUserEmailAddress(userId));
252 setContextAttribute(
253 "[$" + _contextUserPrefix + "_USER_NAME$]",
254 PortalUtil.getUserName(userId, StringPool.BLANK));
255 }
256
257 mailId = PortalUtil.getMailId(
258 company.getMx(), _mailIdPopPortletPrefix, _mailIdIds);
259 }
260
261 public void setBody(String body) {
262 this.body = body;
263 }
264
265 public void setBulk(boolean bulk) {
266 this.bulk = bulk;
267 }
268
269 public void setCompanyId(long companyId) {
270 this.companyId = companyId;
271 }
272
273 public void setContextAttribute(String key, EscapableObject<String> value) {
274 _context.put(key, value);
275 }
276
277 public void setContextAttribute(String key, Object value) {
278 setContextAttribute(key, value, true);
279 }
280
281 public void setContextAttribute(String key, Object value, boolean escape) {
282 setContextAttribute(
283 key,
284 new HtmlEscapableObject<String>(String.valueOf(value), escape));
285 }
286
287 public void setContextAttributes(Object... values) {
288 for (int i = 0; i < values.length; i += 2) {
289 setContextAttribute(String.valueOf(values[i]), values[i + 1]);
290 }
291 }
292
293 public void setContextUserPrefix(String contextUserPrefix) {
294 _contextUserPrefix = contextUserPrefix;
295 }
296
297 public void setFrom(String fromAddress, String fromName) {
298 this.fromAddress = fromAddress;
299 this.fromName = fromName;
300 }
301
302 public void setGroupId(long groupId) {
303 this.groupId = groupId;
304 }
305
306 public void setHtmlFormat(boolean htmlFormat) {
307 this.htmlFormat = htmlFormat;
308 }
309
310 public void setInReplyTo(String inReplyTo) {
311 this.inReplyTo = inReplyTo;
312 }
313
314 public void setLocalizedBodyMap(Map<Locale, String> localizedBodyMap) {
315 this.localizedBodyMap = localizedBodyMap;
316 }
317
318 public void setLocalizedSubjectMap(
319 Map<Locale, String> localizedSubjectMap) {
320
321 this.localizedSubjectMap = localizedSubjectMap;
322 }
323
324 public void setMailId(String popPortletPrefix, Object... ids) {
325 _mailIdPopPortletPrefix = popPortletPrefix;
326 _mailIdIds = ids;
327 }
328
329 public void setPortletId(String portletId) {
330 this.portletId = portletId;
331 }
332
333 public void setReplyToAddress(String replyToAddress) {
334 this.replyToAddress = replyToAddress;
335 }
336
337
340 public void setScopeGroupId(long scopeGroupId) {
341 try {
342 Group group = GroupLocalServiceUtil.getGroup(scopeGroupId);
343
344 if (group.isLayout()) {
345 groupId = group.getParentGroupId();
346 }
347 else {
348 groupId = scopeGroupId;
349 }
350 }
351 catch (Exception e) {
352 }
353
354 this.scopeGroupId = scopeGroupId;
355 }
356
357 public void setServiceContext(ServiceContext serviceContext) {
358 this.serviceContext = serviceContext;
359 }
360
361 public void setSMTPAccount(SMTPAccount smtpAccount) {
362 this.smtpAccount = smtpAccount;
363 }
364
365 public void setSubject(String subject) {
366 this.subject = subject;
367 }
368
369 public void setUserId(long userId) {
370 this.userId = userId;
371 }
372
373 protected void deleteSubscription(Subscription subscription)
374 throws Exception {
375
376 SubscriptionLocalServiceUtil.deleteSubscription(
377 subscription.getSubscriptionId());
378 }
379
380 protected boolean hasPermission(
381 Subscription subscription, String inferredClassName,
382 long inferredClassPK, User user)
383 throws Exception {
384
385 PermissionChecker permissionChecker =
386 PermissionCheckerFactoryUtil.create(user);
387
388 return SubscriptionPermissionUtil.contains(
389 permissionChecker, subscription.getClassName(),
390 subscription.getClassPK(), inferredClassName, inferredClassPK);
391 }
392
393
397 protected boolean hasPermission(Subscription subscription, User user)
398 throws Exception {
399
400 return hasPermission(subscription, null, 0, user);
401 }
402
403
407 protected void notifySubscriber(Subscription subscription)
408 throws Exception {
409
410 notifySubscriber(subscription, null, 0);
411 }
412
413 protected void notifySubscriber(
414 Subscription subscription, String inferredClassName,
415 long inferredClassPK)
416 throws Exception {
417
418 User user = UserLocalServiceUtil.fetchUserById(
419 subscription.getUserId());
420
421 if (user == null) {
422 if (_log.isInfoEnabled()) {
423 _log.info(
424 "Subscription " + subscription.getSubscriptionId() +
425 " is stale and will be deleted");
426 }
427
428 deleteSubscription(subscription);
429
430 return;
431 }
432
433 String emailAddress = user.getEmailAddress();
434
435 if (_sentEmailAddresses.contains(emailAddress)) {
436 if (_log.isDebugEnabled()) {
437 _log.debug("Do not send a duplicate email to " + emailAddress);
438 }
439
440 return;
441 }
442 else {
443 if (_log.isDebugEnabled()) {
444 _log.debug(
445 "Add " + emailAddress +
446 " to the list of users who have received an email");
447 }
448
449 _sentEmailAddresses.add(emailAddress);
450 }
451
452 if (!user.isActive()) {
453 if (_log.isDebugEnabled()) {
454 _log.debug("Skip inactive user " + user.getUserId());
455 }
456
457 return;
458 }
459
460 try {
461 if (!hasPermission(
462 subscription, inferredClassName, inferredClassPK, user)) {
463
464 if (_log.isDebugEnabled()) {
465 _log.debug("Skip unauthorized user " + user.getUserId());
466 }
467
468 return;
469 }
470 }
471 catch (Exception e) {
472 _log.error(e, e);
473
474 return;
475 }
476
477 if (bulk) {
478 InternetAddress bulkAddress = new InternetAddress(
479 user.getEmailAddress(), user.getFullName());
480
481 if (_bulkAddresses == null) {
482 _bulkAddresses = new ArrayList<InternetAddress>();
483 }
484
485 _bulkAddresses.add(bulkAddress);
486 }
487 else {
488 try {
489 InternetAddress to = new InternetAddress(
490 user.getEmailAddress(), user.getFullName());
491
492 sendEmail(to, user.getLocale());
493 }
494 catch (Exception e) {
495 _log.error(e, e);
496 }
497 }
498 }
499
500 protected void processMailMessage(MailMessage mailMessage, Locale locale)
501 throws Exception {
502
503 InternetAddress from = mailMessage.getFrom();
504 InternetAddress to = mailMessage.getTo()[0];
505
506 String processedSubject = StringUtil.replace(
507 mailMessage.getSubject(),
508 new String[] {
509 "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
510 "[$TO_NAME$]"
511 },
512 new String[] {
513 from.getAddress(),
514 GetterUtil.getString(from.getPersonal(), from.getAddress()),
515 HtmlUtil.escape(to.getAddress()),
516 HtmlUtil.escape(
517 GetterUtil.getString(to.getPersonal(), to.getAddress()))
518 });
519
520 processedSubject = replaceContent(processedSubject, locale, false);
521
522 mailMessage.setSubject(processedSubject);
523
524 String processedBody = StringUtil.replace(
525 mailMessage.getBody(),
526 new String[] {
527 "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
528 "[$TO_NAME$]"
529 },
530 new String[] {
531 from.getAddress(),
532 GetterUtil.getString(from.getPersonal(), from.getAddress()),
533 HtmlUtil.escape(to.getAddress()),
534 HtmlUtil.escape(
535 GetterUtil.getString(to.getPersonal(), to.getAddress()))
536 });
537
538 processedBody = replaceContent(processedBody, locale, htmlFormat);
539
540 mailMessage.setBody(processedBody);
541 }
542
543 protected String replaceContent(String content, Locale locale)
544 throws Exception {
545
546 return replaceContent(content, locale, true);
547 }
548
549 protected String replaceContent(
550 String content, Locale locale, boolean escape)
551 throws Exception {
552
553 for (Map.Entry<String, EscapableObject<String>> entry :
554 _context.entrySet()) {
555
556 String key = entry.getKey();
557 EscapableObject<String> value = entry.getValue();
558
559 String valueString = null;
560
561 if (escape) {
562 valueString = value.getEscapedValue();
563 }
564 else {
565 valueString = value.getOriginalValue();
566 }
567
568 content = StringUtil.replace(content, key, valueString);
569 }
570
571 if (Validator.isNotNull(portletId)) {
572 String portletName = PortalUtil.getPortletTitle(portletId, locale);
573
574 content = StringUtil.replace(
575 content, "[$PORTLET_NAME$]", portletName);
576 }
577
578 Company company = CompanyLocalServiceUtil.getCompany(companyId);
579
580 content = StringUtil.replace(
581 content,
582 new String[] {
583 "href=\"/", "src=\"/"
584 },
585 new String[] {
586 "href=\"" + company.getPortalURL(groupId) + "/",
587 "src=\"" + company.getPortalURL(groupId) + "/"
588 });
589
590 return content;
591 }
592
593 protected void sendEmail(InternetAddress to, Locale locale)
594 throws Exception {
595
596 InternetAddress from = new InternetAddress(
597 replaceContent(fromAddress, locale),
598 replaceContent(fromName, locale));
599
600 String processedSubject = null;
601
602 if (localizedSubjectMap != null) {
603 String localizedSubject = localizedSubjectMap.get(locale);
604
605 if (Validator.isNull(localizedSubject)) {
606 Locale defaultLocale = LocaleUtil.getDefault();
607
608 processedSubject = localizedSubjectMap.get(defaultLocale);
609 }
610 else {
611 processedSubject = localizedSubject;
612 }
613 }
614 else {
615 processedSubject = this.subject;
616 }
617
618 String processedBody = null;
619
620 if (localizedBodyMap != null) {
621 String localizedBody = localizedBodyMap.get(locale);
622
623 if (Validator.isNull(localizedBody)) {
624 Locale defaultLocale = LocaleUtil.getDefault();
625
626 processedBody = localizedBodyMap.get(defaultLocale);
627 }
628 else {
629 processedBody = localizedBody;
630 }
631 }
632 else {
633 processedBody = this.body;
634 }
635
636 MailMessage mailMessage = new MailMessage(
637 from, to, processedSubject, processedBody, htmlFormat);
638
639 if (fileAttachments != null) {
640 for (FileAttachment fileAttachment : fileAttachments) {
641 mailMessage.addFileAttachment(
642 fileAttachment.getFile(), fileAttachment.getFileName());
643 }
644 }
645
646 if (bulk && (_bulkAddresses != null)) {
647 mailMessage.setBulkAddresses(
648 _bulkAddresses.toArray(
649 new InternetAddress[_bulkAddresses.size()]));
650
651 _bulkAddresses.clear();
652 }
653
654 if (inReplyTo != null) {
655 mailMessage.setInReplyTo(inReplyTo);
656 }
657
658 mailMessage.setMessageId(mailId);
659
660 if (replyToAddress != null) {
661 InternetAddress replyTo = new InternetAddress(
662 replaceContent(replyToAddress, locale),
663 replaceContent(replyToAddress, locale));
664
665 mailMessage.setReplyTo(new InternetAddress[] {replyTo});
666 }
667
668 if (smtpAccount != null) {
669 mailMessage.setSMTPAccount(smtpAccount);
670 }
671
672 processMailMessage(mailMessage, locale);
673
674 MailServiceUtil.sendEmail(mailMessage);
675 }
676
677 protected String body;
678 protected boolean bulk;
679 protected long companyId;
680 protected List<FileAttachment> fileAttachments =
681 new ArrayList<FileAttachment>();
682 protected String fromAddress;
683 protected String fromName;
684 protected long groupId;
685 protected boolean htmlFormat;
686 protected String inReplyTo;
687 protected Map<Locale, String> localizedBodyMap;
688 protected Map<Locale, String> localizedSubjectMap;
689 protected String mailId;
690 protected String portletId;
691 protected String replyToAddress;
692 protected long scopeGroupId;
693 protected ServiceContext serviceContext;
694 protected SMTPAccount smtpAccount;
695 protected String subject;
696 protected long userId;
697
698 private void readObject(ObjectInputStream objectInputStream)
699 throws ClassNotFoundException, IOException {
700
701 objectInputStream.defaultReadObject();
702
703 String servletContextName = objectInputStream.readUTF();
704
705 if (!servletContextName.isEmpty()) {
706 _classLoader = ClassLoaderPool.getClassLoader(servletContextName);
707 }
708 }
709
710 private void writeObject(ObjectOutputStream objectOutputStream)
711 throws IOException {
712
713 objectOutputStream.defaultWriteObject();
714
715 String servletContextName = StringPool.BLANK;
716
717 if (_classLoader != null) {
718 servletContextName = ClassLoaderPool.getContextName(_classLoader);
719 }
720
721 objectOutputStream.writeUTF(servletContextName);
722 }
723
724 private static Log _log = LogFactoryUtil.getLog(SubscriptionSender.class);
725
726 private List<InternetAddress> _bulkAddresses;
727 private transient ClassLoader _classLoader;
728 private Map<String, EscapableObject<String>> _context =
729 new HashMap<String, EscapableObject<String>>();
730 private String _contextUserPrefix;
731 private boolean _initialized;
732 private Object[] _mailIdIds;
733 private String _mailIdPopPortletPrefix;
734 private List<ObjectValuePair<String, Long>> _persistestedSubscribersOVPs =
735 new ArrayList<ObjectValuePair<String, Long>>();
736 private List<ObjectValuePair<String, String>> _runtimeSubscribersOVPs =
737 new ArrayList<ObjectValuePair<String, String>>();
738 private Set<String> _sentEmailAddresses = new HashSet<String>();
739
740 }