001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
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    /**
063     * @author Brian Wing Shun Chan
064     * @author Mate Thurzo
065     */
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            /**
316             * @see {@link
317             *      com.liferay.portal.kernel.search.BaseIndexer#getParentGroupId(long)}
318             */
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    }