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