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