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