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