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