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