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