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