001    /**
002     * Copyright (c) 2000-present 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.FileAttachment;
018    import com.liferay.mail.service.MailServiceUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.json.JSONFactoryUtil;
021    import com.liferay.portal.kernel.json.JSONObject;
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.notifications.UserNotificationManagerUtil;
029    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackUtil;
030    import com.liferay.portal.kernel.util.ArrayUtil;
031    import com.liferay.portal.kernel.util.ClassLoaderPool;
032    import com.liferay.portal.kernel.util.EscapableObject;
033    import com.liferay.portal.kernel.util.GetterUtil;
034    import com.liferay.portal.kernel.util.HtmlEscapableObject;
035    import com.liferay.portal.kernel.util.HtmlUtil;
036    import com.liferay.portal.kernel.util.LocaleUtil;
037    import com.liferay.portal.kernel.util.ObjectValuePair;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
042    import com.liferay.portal.model.Company;
043    import com.liferay.portal.model.Group;
044    import com.liferay.portal.model.ResourceAction;
045    import com.liferay.portal.model.Subscription;
046    import com.liferay.portal.model.User;
047    import com.liferay.portal.model.UserNotificationDeliveryConstants;
048    import com.liferay.portal.security.permission.ActionKeys;
049    import com.liferay.portal.security.permission.BaseModelPermissionCheckerUtil;
050    import com.liferay.portal.security.permission.PermissionChecker;
051    import com.liferay.portal.security.permission.PermissionCheckerFactoryUtil;
052    import com.liferay.portal.service.CompanyLocalServiceUtil;
053    import com.liferay.portal.service.GroupLocalServiceUtil;
054    import com.liferay.portal.service.ResourceActionLocalServiceUtil;
055    import com.liferay.portal.service.ServiceContext;
056    import com.liferay.portal.service.SubscriptionLocalServiceUtil;
057    import com.liferay.portal.service.UserLocalServiceUtil;
058    import com.liferay.portal.service.UserNotificationEventLocalServiceUtil;
059    
060    import java.io.File;
061    import java.io.IOException;
062    import java.io.ObjectInputStream;
063    import java.io.ObjectOutputStream;
064    import java.io.Serializable;
065    
066    import java.util.ArrayList;
067    import java.util.HashMap;
068    import java.util.HashSet;
069    import java.util.List;
070    import java.util.Locale;
071    import java.util.Map;
072    import java.util.Set;
073    import java.util.concurrent.Callable;
074    
075    import javax.mail.internet.InternetAddress;
076    
077    /**
078     * @author Brian Wing Shun Chan
079     * @author Mate Thurzo
080     * @author Raymond Aug??
081     * @author Sergio Gonz??lez
082     * @author Roberto D??az
083     */
084    public class SubscriptionSender implements Serializable {
085    
086            public void addFileAttachment(File file) {
087                    addFileAttachment(file, null);
088            }
089    
090            public void addFileAttachment(File file, String fileName) {
091                    if (file == null) {
092                            return;
093                    }
094    
095                    if (fileAttachments == null) {
096                            fileAttachments = new ArrayList<>();
097                    }
098    
099                    FileAttachment attachment = new FileAttachment(file, fileName);
100    
101                    fileAttachments.add(attachment);
102            }
103    
104            public void addPersistedSubscribers(String className, long classPK) {
105                    ObjectValuePair<String, Long> ovp = new ObjectValuePair<>(
106                            className, classPK);
107    
108                    _persistestedSubscribersOVPs.add(ovp);
109            }
110    
111            public void addRuntimeSubscribers(String toAddress, String toName) {
112                    ObjectValuePair<String, String> ovp = new ObjectValuePair<>(
113                            toAddress, toName);
114    
115                    _runtimeSubscribersOVPs.add(ovp);
116            }
117    
118            public void flushNotifications() throws Exception {
119                    initialize();
120    
121                    Thread currentThread = Thread.currentThread();
122    
123                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
124    
125                    try {
126                            if ((_classLoader != null) &&
127                                    (contextClassLoader != _classLoader)) {
128    
129                                    currentThread.setContextClassLoader(_classLoader);
130                            }
131    
132                            for (ObjectValuePair<String, Long> ovp :
133                                            _persistestedSubscribersOVPs) {
134    
135                                    String className = ovp.getKey();
136                                    long classPK = ovp.getValue();
137    
138                                    List<Subscription> subscriptions =
139                                            SubscriptionLocalServiceUtil.getSubscriptions(
140                                                    companyId, className, classPK);
141    
142                                    for (Subscription subscription : subscriptions) {
143                                            try {
144                                                    notifyPersistedSubscriber(subscription);
145                                            }
146                                            catch (Exception e) {
147                                                    _log.error(
148                                                            "Unable to process subscription: " + subscription,
149                                                            e);
150                                            }
151                                    }
152                            }
153    
154                            _persistestedSubscribersOVPs.clear();
155    
156                            for (ObjectValuePair<String, String> ovp :
157                                            _runtimeSubscribersOVPs) {
158    
159                                    String toAddress = ovp.getKey();
160    
161                                    if (Validator.isNull(toAddress)) {
162                                            continue;
163                                    }
164    
165                                    if (_sentEmailAddresses.contains(toAddress)) {
166                                            if (_log.isDebugEnabled()) {
167                                                    _log.debug(
168                                                            "Do not send a duplicate email to " + toAddress);
169                                            }
170    
171                                            continue;
172                                    }
173    
174                                    if (_log.isDebugEnabled()) {
175                                            _log.debug(
176                                                    "Add " + toAddress + " to the list of users who " +
177                                                            "have received an email");
178                                    }
179    
180                                    _sentEmailAddresses.add(toAddress);
181    
182                                    String toName = ovp.getValue();
183    
184                                    InternetAddress to = new InternetAddress(toAddress, toName);
185    
186                                    notifyRuntimeSubscriber(to, LocaleUtil.getDefault());
187                            }
188    
189                            _runtimeSubscribersOVPs.clear();
190    
191                            if (bulk) {
192                                    Locale locale = LocaleUtil.getDefault();
193    
194                                    InternetAddress to = new InternetAddress(
195                                            replaceContent(replyToAddress, locale),
196                                            replaceContent(replyToAddress, locale));
197    
198                                    sendEmail(to, locale);
199                            }
200                    }
201                    finally {
202                            if ((_classLoader != null) &&
203                                    (contextClassLoader != _classLoader)) {
204    
205                                    currentThread.setContextClassLoader(contextClassLoader);
206                            }
207                    }
208            }
209    
210            public void flushNotificationsAsync() {
211                    TransactionCommitCallbackUtil.registerCallback(
212                            new Callable<Void>() {
213    
214                                    @Override
215                                    public Void call() throws Exception {
216                                            Thread currentThread = Thread.currentThread();
217    
218                                            _classLoader = currentThread.getContextClassLoader();
219    
220                                            MessageBusUtil.sendMessage(
221                                                    DestinationNames.SUBSCRIPTION_SENDER,
222                                                    SubscriptionSender.this);
223    
224                                            return null;
225                                    }
226    
227                            }
228                    );
229            }
230    
231            public Object getContextAttribute(String key) {
232                    return _context.get(key);
233            }
234    
235            public long getCurrentUserId() {
236                    return currentUserId;
237            }
238    
239            public String getMailId() {
240                    return this.mailId;
241            }
242    
243            /**
244             * @deprecated As of 7.0.0, replaced by {@link getCurrentUserId()}
245             */
246            @Deprecated
247            public long getUserId() {
248                    return getCurrentUserId();
249            }
250    
251            public void initialize() throws Exception {
252                    if (_initialized) {
253                            return;
254                    }
255    
256                    _initialized = true;
257    
258                    if ((groupId == 0) && (serviceContext != null)) {
259                            setScopeGroupId(serviceContext.getScopeGroupId());
260                    }
261    
262                    Company company = CompanyLocalServiceUtil.getCompany(companyId);
263    
264                    setContextAttribute("[$COMPANY_ID$]", company.getCompanyId());
265                    setContextAttribute("[$COMPANY_MX$]", company.getMx());
266                    setContextAttribute("[$COMPANY_NAME$]", company.getName());
267                    setContextAttribute("[$PORTAL_URL$]", company.getPortalURL(groupId));
268    
269                    if (groupId > 0) {
270                            Group group = GroupLocalServiceUtil.getGroup(groupId);
271    
272                            setContextAttribute("[$SITE_NAME$]", group.getDescriptiveName());
273                    }
274    
275                    if ((creatorUserId > 0) &&
276                            Validator.isNotNull(_contextCreatorUserPrefix)) {
277    
278                            setContextAttribute(
279                                    "[$" + _contextCreatorUserPrefix + "_USER_ADDRESS$]",
280                                    PortalUtil.getUserEmailAddress(creatorUserId));
281                            setContextAttribute(
282                                    "[$" + _contextCreatorUserPrefix + "_USER_NAME$]",
283                                    PortalUtil.getUserName(creatorUserId, StringPool.BLANK));
284                    }
285    
286                    if (uniqueMailId) {
287                            _mailIdIds = ArrayUtil.append(
288                                    _mailIdIds, PortalUUIDUtil.generate());
289                    }
290    
291                    mailId = PortalUtil.getMailId(
292                            company.getMx(), _mailIdPopPortletPrefix, _mailIdIds);
293            }
294    
295            public void setBody(String body) {
296                    this.body = body;
297            }
298    
299            public void setBulk(boolean bulk) {
300                    this.bulk = bulk;
301            }
302    
303            public void setClassName(String className) {
304                    _className = className;
305            }
306    
307            public void setClassPK(long classPK) {
308                    _classPK = classPK;
309            }
310    
311            public void setCompanyId(long companyId) {
312                    this.companyId = companyId;
313            }
314    
315            public void setContextAttribute(String key, EscapableObject<String> value) {
316                    _context.put(key, value);
317            }
318    
319            public void setContextAttribute(String key, Object value) {
320                    setContextAttribute(key, value, true);
321            }
322    
323            public void setContextAttribute(String key, Object value, boolean escape) {
324                    setContextAttribute(
325                            key,
326                            new HtmlEscapableObject<String>(String.valueOf(value), escape));
327            }
328    
329            public void setContextAttributes(Object... values) {
330                    for (int i = 0; i < values.length; i += 2) {
331                            setContextAttribute(String.valueOf(values[i]), values[i + 1]);
332                    }
333            }
334    
335            public void setContextCreatorUserPrefix(String contextCreatorUserPrefix) {
336                    _contextCreatorUserPrefix = contextCreatorUserPrefix;
337            }
338    
339            public void setCreatorUserId(long creatorUserId) {
340                    this.creatorUserId = creatorUserId;
341            }
342    
343            public void setCurrentUserId(long currentUserId) {
344                    this.currentUserId = currentUserId;
345            }
346    
347            public void setEntryTitle(String entryTitle) {
348                    this._entryTitle = entryTitle;
349            }
350    
351            public void setEntryURL(String entryURL) {
352                    _entryURL = entryURL;
353            }
354    
355            public void setFrom(String fromAddress, String fromName) {
356                    this.fromAddress = fromAddress;
357                    this.fromName = fromName;
358            }
359    
360            public void setGroupId(long groupId) {
361                    this.groupId = groupId;
362            }
363    
364            public void setHtmlFormat(boolean htmlFormat) {
365                    this.htmlFormat = htmlFormat;
366            }
367    
368            public void setInReplyTo(String inReplyTo) {
369                    this.inReplyTo = inReplyTo;
370            }
371    
372            public void setLocalizedBodyMap(Map<Locale, String> localizedBodyMap) {
373                    this.localizedBodyMap = localizedBodyMap;
374            }
375    
376            public void setLocalizedPortletTitleMap(
377                    Map<Locale, String> localizedPortletTitleMap) {
378    
379                    this.localizedPortletTitleMap = localizedPortletTitleMap;
380            }
381    
382            public void setLocalizedSubjectMap(
383                    Map<Locale, String> localizedSubjectMap) {
384    
385                    this.localizedSubjectMap = localizedSubjectMap;
386            }
387    
388            public void setMailId(String popPortletPrefix, Object... ids) {
389                    _mailIdPopPortletPrefix = popPortletPrefix;
390                    _mailIdIds = ids;
391            }
392    
393            public void setNotificationClassNameId(long notificationClassNameId) {
394                    _notificationClassNameId = notificationClassNameId;
395            }
396    
397            /**
398             * @see com.liferay.portal.kernel.notifications.UserNotificationDefinition
399             */
400            public void setNotificationType(int notificationType) {
401                    _notificationType = notificationType;
402            }
403    
404            public void setPortletId(String portletId) {
405                    this.portletId = portletId;
406            }
407    
408            public void setReplyToAddress(String replyToAddress) {
409                    this.replyToAddress = replyToAddress;
410            }
411    
412            /**
413             * @see com.liferay.portal.kernel.search.BaseIndexer#getSiteGroupId(long)
414             */
415            public void setScopeGroupId(long scopeGroupId) {
416                    try {
417                            Group group = GroupLocalServiceUtil.getGroup(scopeGroupId);
418    
419                            if (group.isLayout()) {
420                                    groupId = group.getParentGroupId();
421                            }
422                            else {
423                                    groupId = scopeGroupId;
424                            }
425                    }
426                    catch (Exception e) {
427                    }
428    
429                    this.scopeGroupId = scopeGroupId;
430            }
431    
432            public void setServiceContext(ServiceContext serviceContext) {
433                    this.serviceContext = serviceContext;
434            }
435    
436            public void setSMTPAccount(SMTPAccount smtpAccount) {
437                    this.smtpAccount = smtpAccount;
438            }
439    
440            public void setSubject(String subject) {
441                    this.subject = subject;
442            }
443    
444            public void setUniqueMailId(boolean uniqueMailId) {
445                    this.uniqueMailId = uniqueMailId;
446            }
447    
448            /**
449             * @deprecated As of 7.0.0, replaced by {@link #setCurrentUserId(long)}
450             */
451            @Deprecated
452            public void setUserId(long userId) {
453                    setCurrentUserId(userId);
454            }
455    
456            protected void deleteSubscription(Subscription subscription)
457                    throws Exception {
458    
459                    SubscriptionLocalServiceUtil.deleteSubscription(
460                            subscription.getSubscriptionId());
461            }
462    
463            protected boolean hasPermission(
464                            Subscription subscription, String className, long classPK,
465                            User user)
466                    throws Exception {
467    
468                    if (subscription.getClassName() == null) {
469                            return false;
470                    }
471    
472                    PermissionChecker permissionChecker =
473                            PermissionCheckerFactoryUtil.create(user);
474    
475                    Boolean hasPermission = null;
476    
477                    if (Validator.isNotNull(className)) {
478                            hasPermission =
479                                    BaseModelPermissionCheckerUtil.containsBaseModelPermission(
480                                            permissionChecker, groupId, className, classPK,
481                                            ActionKeys.VIEW);
482    
483                            if ((hasPermission == null) || !hasPermission) {
484                                    return false;
485                            }
486                    }
487    
488                    hasPermission = hasSubscribePermission(permissionChecker, subscription);
489    
490                    if ((hasPermission == null) || !hasPermission) {
491                            return false;
492                    }
493    
494                    return true;
495            }
496    
497            protected boolean hasPermission(Subscription subscription, User user)
498                    throws Exception {
499    
500                    return hasPermission(subscription, _className, _classPK, user);
501            }
502    
503            /**
504             * @throws PortalException
505             */
506            protected Boolean hasSubscribePermission(
507                            PermissionChecker permissionChecker, Subscription subscription)
508                    throws PortalException {
509    
510                    ResourceAction resourceAction =
511                            ResourceActionLocalServiceUtil.fetchResourceAction(
512                                    subscription.getClassName(), ActionKeys.SUBSCRIBE);
513    
514                    if (resourceAction != null) {
515                            return BaseModelPermissionCheckerUtil.containsBaseModelPermission(
516                                    permissionChecker, groupId, subscription.getClassName(),
517                                    subscription.getClassPK(), ActionKeys.SUBSCRIBE);
518                    }
519    
520                    return Boolean.TRUE;
521            }
522    
523            protected void notifyPersistedSubscriber(Subscription subscription)
524                    throws Exception {
525    
526                    notifyPersistedSubscriber(subscription, _className, _classPK);
527            }
528    
529            protected void notifyPersistedSubscriber(
530                            Subscription subscription, String className, long classPK)
531                    throws Exception {
532    
533                    User user = UserLocalServiceUtil.fetchUserById(
534                            subscription.getUserId());
535    
536                    if (user == null) {
537                            if (_log.isInfoEnabled()) {
538                                    _log.info(
539                                            "Subscription " + subscription.getSubscriptionId() +
540                                                    " is stale and will be deleted");
541                            }
542    
543                            deleteSubscription(subscription);
544    
545                            return;
546                    }
547    
548                    String emailAddress = user.getEmailAddress();
549    
550                    if (_sentEmailAddresses.contains(emailAddress)) {
551                            if (_log.isDebugEnabled()) {
552                                    _log.debug("Do not send a duplicate email to " + emailAddress);
553                            }
554    
555                            return;
556                    }
557                    else {
558                            if (_log.isDebugEnabled()) {
559                                    _log.debug(
560                                            "Add " + emailAddress +
561                                                    " to the list of users who have received an email");
562                            }
563    
564                            _sentEmailAddresses.add(emailAddress);
565                    }
566    
567                    if (!user.isActive()) {
568                            if (_log.isDebugEnabled()) {
569                                    _log.debug("Skip inactive user " + user.getUserId());
570                            }
571    
572                            return;
573                    }
574    
575                    try {
576                            if (!hasPermission(subscription, className, classPK, user)) {
577                                    if (_log.isDebugEnabled()) {
578                                            _log.debug("Skip unauthorized user " + user.getUserId());
579                                    }
580    
581                                    return;
582                            }
583                    }
584                    catch (Exception e) {
585                            _log.error(e, e);
586    
587                            return;
588                    }
589    
590                    sendNotification(user);
591            }
592    
593            protected void notifyRuntimeSubscriber(InternetAddress to, Locale locale)
594                    throws Exception {
595    
596                    String emailAddress = to.getAddress();
597    
598                    User user = UserLocalServiceUtil.fetchUserByEmailAddress(
599                            companyId, emailAddress);
600    
601                    if (user == null) {
602                            if (_log.isInfoEnabled()) {
603                                    _log.info(
604                                            "User with email address " + emailAddress +
605                                                    " does not exist for company " + companyId);
606                            }
607    
608                            if (bulk) {
609                                    if (_bulkAddresses == null) {
610                                            _bulkAddresses = new ArrayList<>();
611                                    }
612    
613                                    _bulkAddresses.add(to);
614    
615                                    return;
616                            }
617    
618                            sendEmail(to, locale);
619                    }
620                    else {
621                            sendNotification(user);
622                    }
623            }
624    
625            /**
626             * @deprecated As of 6.2.0, replaced by {@link
627             *             #notifyPersistedSubscriber(Subscription)}
628             */
629            @Deprecated
630            protected void notifySubscriber(Subscription subscription)
631                    throws Exception {
632    
633                    notifyPersistedSubscriber(subscription, null, 0);
634            }
635    
636            /**
637             * @deprecated As of 7.0.0, replaced by {@link
638             *             #notifyPersistedSubscriber(Subscription)}
639             */
640            @Deprecated
641            protected void notifySubscriber(
642                            Subscription subscription, String inferredClassName,
643                            long inferredClassPK)
644                    throws Exception {
645    
646                    notifyPersistedSubscriber(
647                            subscription, inferredClassName, inferredClassPK);
648            }
649    
650            protected void processMailMessage(MailMessage mailMessage, Locale locale)
651                    throws Exception {
652    
653                    InternetAddress from = mailMessage.getFrom();
654                    InternetAddress to = mailMessage.getTo()[0];
655    
656                    String processedSubject = StringUtil.replace(
657                            mailMessage.getSubject(),
658                            new String[] {
659                                    "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
660                                    "[$TO_NAME$]"
661                            },
662                            new String[] {
663                                    from.getAddress(),
664                                    GetterUtil.getString(from.getPersonal(), from.getAddress()),
665                                    HtmlUtil.escape(to.getAddress()),
666                                    HtmlUtil.escape(
667                                            GetterUtil.getString(to.getPersonal(), to.getAddress()))
668                            });
669    
670                    processedSubject = replaceContent(processedSubject, locale, false);
671    
672                    mailMessage.setSubject(processedSubject);
673    
674                    String processedBody = StringUtil.replace(
675                            mailMessage.getBody(),
676                            new String[] {
677                                    "[$FROM_ADDRESS$]", "[$FROM_NAME$]", "[$TO_ADDRESS$]",
678                                    "[$TO_NAME$]"
679                            },
680                            new String[] {
681                                    from.getAddress(),
682                                    GetterUtil.getString(from.getPersonal(), from.getAddress()),
683                                    HtmlUtil.escape(to.getAddress()),
684                                    HtmlUtil.escape(
685                                            GetterUtil.getString(to.getPersonal(), to.getAddress()))
686                            });
687    
688                    processedBody = replaceContent(processedBody, locale, htmlFormat);
689    
690                    mailMessage.setBody(processedBody);
691            }
692    
693            protected String replaceContent(String content, Locale locale)
694                    throws Exception {
695    
696                    return replaceContent(content, locale, true);
697            }
698    
699            protected String replaceContent(
700                            String content, Locale locale, boolean escape)
701                    throws Exception {
702    
703                    for (Map.Entry<String, EscapableObject<String>> entry :
704                                    _context.entrySet()) {
705    
706                            String key = entry.getKey();
707                            EscapableObject<String> value = entry.getValue();
708    
709                            String valueString = null;
710    
711                            if (escape) {
712                                    valueString = value.getEscapedValue();
713                            }
714                            else {
715                                    valueString = value.getOriginalValue();
716                            }
717    
718                            content = StringUtil.replace(content, key, valueString);
719                    }
720    
721                    String portletName = StringPool.BLANK;
722    
723                    if (Validator.isNotNull(portletId)) {
724                            portletName = PortalUtil.getPortletTitle(portletId, locale);
725    
726                            content = StringUtil.replace(
727                                    content, "[$PORTLET_NAME$]", portletName);
728                    }
729    
730                    String portletTitle = portletName;
731    
732                    if (localizedPortletTitleMap != null) {
733                            if (Validator.isNotNull(localizedPortletTitleMap.get(locale))) {
734                                    portletTitle = localizedPortletTitleMap.get(locale);
735                            }
736                            else {
737                                    Locale defaultLocale = LocaleUtil.getDefault();
738    
739                                    if (Validator.isNotNull(
740                                                    localizedPortletTitleMap.get(defaultLocale))) {
741    
742                                            portletTitle = localizedPortletTitleMap.get(defaultLocale);
743                                    }
744                            }
745                    }
746    
747                    if (Validator.isNotNull(portletTitle)) {
748                            content = StringUtil.replace(
749                                    content, "[$PORTLET_TITLE$]", portletTitle);
750                    }
751    
752                    Company company = CompanyLocalServiceUtil.getCompany(companyId);
753    
754                    content = StringUtil.replace(
755                            content, new String[] {"href=\"/", "src=\"/"},
756                            new String[] {
757                                    "href=\"" + company.getPortalURL(groupId) + "/",
758                                    "src=\"" + company.getPortalURL(groupId) + "/"
759                            });
760    
761                    return content;
762            }
763    
764            protected void sendEmail(InternetAddress to, Locale locale)
765                    throws Exception {
766    
767                    InternetAddress from = new InternetAddress(
768                            replaceContent(fromAddress, locale),
769                            replaceContent(fromName, locale));
770    
771                    String processedSubject = null;
772    
773                    if (localizedSubjectMap != null) {
774                            String localizedSubject = localizedSubjectMap.get(locale);
775    
776                            if (Validator.isNull(localizedSubject)) {
777                                    Locale defaultLocale = LocaleUtil.getDefault();
778    
779                                    processedSubject = localizedSubjectMap.get(defaultLocale);
780                            }
781                            else {
782                                    processedSubject = localizedSubject;
783                            }
784                    }
785                    else {
786                            processedSubject = this.subject;
787                    }
788    
789                    String processedBody = null;
790    
791                    if (localizedBodyMap != null) {
792                            String localizedBody = localizedBodyMap.get(locale);
793    
794                            if (Validator.isNull(localizedBody)) {
795                                    Locale defaultLocale = LocaleUtil.getDefault();
796    
797                                    processedBody = localizedBodyMap.get(defaultLocale);
798                            }
799                            else {
800                                    processedBody = localizedBody;
801                            }
802                    }
803                    else {
804                            processedBody = this.body;
805                    }
806    
807                    MailMessage mailMessage = new MailMessage(
808                            from, to, processedSubject, processedBody, htmlFormat);
809    
810                    if (fileAttachments != null) {
811                            for (FileAttachment fileAttachment : fileAttachments) {
812                                    mailMessage.addFileAttachment(
813                                            fileAttachment.getFile(), fileAttachment.getFileName());
814                            }
815                    }
816    
817                    if (bulk && (_bulkAddresses != null)) {
818                            mailMessage.setBulkAddresses(
819                                    _bulkAddresses.toArray(
820                                            new InternetAddress[_bulkAddresses.size()]));
821    
822                            _bulkAddresses.clear();
823                    }
824    
825                    if (inReplyTo != null) {
826                            mailMessage.setInReplyTo(inReplyTo);
827                    }
828    
829                    mailMessage.setMessageId(mailId);
830    
831                    if (replyToAddress != null) {
832                            InternetAddress replyTo = new InternetAddress(
833                                    replaceContent(replyToAddress, locale),
834                                    replaceContent(replyToAddress, locale));
835    
836                            mailMessage.setReplyTo(new InternetAddress[] {replyTo});
837                    }
838    
839                    if (smtpAccount != null) {
840                            mailMessage.setSMTPAccount(smtpAccount);
841                    }
842    
843                    processMailMessage(mailMessage, locale);
844    
845                    MailServiceUtil.sendEmail(mailMessage);
846            }
847    
848            protected void sendEmailNotification(User user) throws Exception {
849                    if (UserNotificationManagerUtil.isDeliver(
850                                    user.getUserId(), portletId, _notificationClassNameId,
851                                    _notificationType,
852                                    UserNotificationDeliveryConstants.TYPE_EMAIL)) {
853    
854                            InternetAddress to = new InternetAddress(
855                                    user.getEmailAddress(), user.getFullName());
856    
857                            if (bulk) {
858                                    if (_bulkAddresses == null) {
859                                            _bulkAddresses = new ArrayList<>();
860                                    }
861    
862                                    _bulkAddresses.add(to);
863    
864                                    return;
865                            }
866    
867                            sendEmail(to, user.getLocale());
868                    }
869            }
870    
871            protected void sendNotification(User user) throws Exception {
872                    if (currentUserId == user.getUserId() ) {
873                            if (_log.isDebugEnabled()) {
874                                    _log.debug("Skip user " + currentUserId);
875                            }
876    
877                            return;
878                    }
879    
880                    sendEmailNotification(user);
881                    sendUserNotification(user);
882            }
883    
884            protected void sendUserNotification(User user) throws Exception {
885                    JSONObject notificationEventJSONObject =
886                            JSONFactoryUtil.createJSONObject();
887    
888                    notificationEventJSONObject.put("className", _className);
889                    notificationEventJSONObject.put("classPK", _classPK);
890                    notificationEventJSONObject.put("entryTitle", _entryTitle);
891                    notificationEventJSONObject.put("entryURL", _entryURL);
892                    notificationEventJSONObject.put("notificationType", _notificationType);
893                    notificationEventJSONObject.put("userId", currentUserId);
894    
895                    if (UserNotificationManagerUtil.isDeliver(
896                                    user.getUserId(), portletId, _notificationClassNameId,
897                                    _notificationType,
898                                    UserNotificationDeliveryConstants.TYPE_PUSH)) {
899    
900                            UserNotificationEventLocalServiceUtil.sendUserNotificationEvents(
901                                    user.getUserId(), portletId,
902                                    UserNotificationDeliveryConstants.TYPE_PUSH,
903                                    notificationEventJSONObject);
904                    }
905    
906                    if (UserNotificationManagerUtil.isDeliver(
907                                    user.getUserId(), portletId, _notificationClassNameId,
908                                    _notificationType,
909                                    UserNotificationDeliveryConstants.TYPE_WEBSITE)) {
910    
911                            UserNotificationEventLocalServiceUtil.sendUserNotificationEvents(
912                                    user.getUserId(), portletId,
913                                    UserNotificationDeliveryConstants.TYPE_WEBSITE,
914                                    notificationEventJSONObject);
915                    }
916            }
917    
918            protected String body;
919            protected boolean bulk;
920            protected long companyId;
921            protected long creatorUserId;
922            protected long currentUserId;
923            protected List<FileAttachment> fileAttachments = new ArrayList<>();
924            protected String fromAddress;
925            protected String fromName;
926            protected long groupId;
927            protected boolean htmlFormat;
928            protected String inReplyTo;
929            protected Map<Locale, String> localizedBodyMap;
930            protected Map<Locale, String> localizedPortletTitleMap;
931            protected Map<Locale, String> localizedSubjectMap;
932            protected String mailId;
933            protected String portletId;
934            protected String replyToAddress;
935            protected long scopeGroupId;
936            protected ServiceContext serviceContext;
937            protected SMTPAccount smtpAccount;
938            protected String subject;
939            protected boolean uniqueMailId = true;
940    
941            private void readObject(ObjectInputStream objectInputStream)
942                    throws ClassNotFoundException, IOException {
943    
944                    objectInputStream.defaultReadObject();
945    
946                    String servletContextName = objectInputStream.readUTF();
947    
948                    if (!servletContextName.isEmpty()) {
949                            _classLoader = ClassLoaderPool.getClassLoader(servletContextName);
950                    }
951            }
952    
953            private void writeObject(ObjectOutputStream objectOutputStream)
954                    throws IOException {
955    
956                    objectOutputStream.defaultWriteObject();
957    
958                    String servletContextName = StringPool.BLANK;
959    
960                    if (_classLoader != null) {
961                            servletContextName = ClassLoaderPool.getContextName(_classLoader);
962                    }
963    
964                    objectOutputStream.writeUTF(servletContextName);
965            }
966    
967            private static final Log _log = LogFactoryUtil.getLog(
968                    SubscriptionSender.class);
969    
970            private List<InternetAddress> _bulkAddresses;
971            private transient ClassLoader _classLoader;
972            private String _className;
973            private long _classPK;
974            private final Map<String, EscapableObject<String>> _context =
975                    new HashMap<>();
976            private String _contextCreatorUserPrefix;
977            private String _entryTitle;
978            private String _entryURL;
979            private boolean _initialized;
980            private Object[] _mailIdIds;
981            private String _mailIdPopPortletPrefix;
982            private long _notificationClassNameId;
983            private int _notificationType;
984            private final List<ObjectValuePair<String, Long>>
985                    _persistestedSubscribersOVPs = new ArrayList<>();
986            private final List<ObjectValuePair<String, String>>
987                    _runtimeSubscribersOVPs = new ArrayList<>();
988            private final Set<String> _sentEmailAddresses = new HashSet<>();
989    
990    }