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