001    /**
002     * Copyright (c) 2000-2011 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.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    /**
061     * @author Brian Wing Shun Chan
062     * @author Mate Thurzo
063     */
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            /**
305             * @see {@link
306             *      com.liferay.portal.kernel.search.BaseIndexer#getParentGroupId(long)}
307             */
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    }