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