1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.messageboards.service.impl;
24  
25  import com.liferay.documentlibrary.DuplicateDirectoryException;
26  import com.liferay.documentlibrary.DuplicateFileException;
27  import com.liferay.documentlibrary.NoSuchDirectoryException;
28  import com.liferay.portal.PortalException;
29  import com.liferay.portal.SystemException;
30  import com.liferay.portal.kernel.dao.orm.QueryUtil;
31  import com.liferay.portal.kernel.json.JSONFactoryUtil;
32  import com.liferay.portal.kernel.json.JSONObject;
33  import com.liferay.portal.kernel.language.LanguageUtil;
34  import com.liferay.portal.kernel.log.Log;
35  import com.liferay.portal.kernel.log.LogFactoryUtil;
36  import com.liferay.portal.kernel.mail.MailMessage;
37  import com.liferay.portal.kernel.messaging.DestinationNames;
38  import com.liferay.portal.kernel.messaging.MessageBusUtil;
39  import com.liferay.portal.kernel.search.SearchEngineUtil;
40  import com.liferay.portal.kernel.search.SearchException;
41  import com.liferay.portal.kernel.util.ContentTypes;
42  import com.liferay.portal.kernel.util.ListUtil;
43  import com.liferay.portal.kernel.util.ObjectValuePair;
44  import com.liferay.portal.kernel.util.OrderByComparator;
45  import com.liferay.portal.kernel.util.StringPool;
46  import com.liferay.portal.kernel.util.StringUtil;
47  import com.liferay.portal.kernel.util.Validator;
48  import com.liferay.portal.model.Company;
49  import com.liferay.portal.model.CompanyConstants;
50  import com.liferay.portal.model.Group;
51  import com.liferay.portal.model.GroupConstants;
52  import com.liferay.portal.model.ModelHintsUtil;
53  import com.liferay.portal.model.ResourceConstants;
54  import com.liferay.portal.model.User;
55  import com.liferay.portal.security.auth.PrincipalException;
56  import com.liferay.portal.service.ServiceContext;
57  import com.liferay.portal.service.ServiceContextUtil;
58  import com.liferay.portal.util.PortalUtil;
59  import com.liferay.portal.util.PortletKeys;
60  import com.liferay.portal.util.PrefsPropsUtil;
61  import com.liferay.portal.util.PropsKeys;
62  import com.liferay.portal.util.PropsValues;
63  import com.liferay.portlet.blogs.model.BlogsEntry;
64  import com.liferay.portlet.blogs.social.BlogsActivityKeys;
65  import com.liferay.portlet.messageboards.MessageBodyException;
66  import com.liferay.portlet.messageboards.MessageSubjectException;
67  import com.liferay.portlet.messageboards.NoSuchDiscussionException;
68  import com.liferay.portlet.messageboards.NoSuchThreadException;
69  import com.liferay.portlet.messageboards.RequiredMessageException;
70  import com.liferay.portlet.messageboards.model.MBCategory;
71  import com.liferay.portlet.messageboards.model.MBDiscussion;
72  import com.liferay.portlet.messageboards.model.MBMessage;
73  import com.liferay.portlet.messageboards.model.MBMessageDisplay;
74  import com.liferay.portlet.messageboards.model.MBStatsUser;
75  import com.liferay.portlet.messageboards.model.MBThread;
76  import com.liferay.portlet.messageboards.model.MBTreeWalker;
77  import com.liferay.portlet.messageboards.model.impl.MBMessageDisplayImpl;
78  import com.liferay.portlet.messageboards.model.impl.MBMessageImpl;
79  import com.liferay.portlet.messageboards.model.impl.MBThreadImpl;
80  import com.liferay.portlet.messageboards.model.impl.MBTreeWalkerImpl;
81  import com.liferay.portlet.messageboards.service.base.MBMessageLocalServiceBaseImpl;
82  import com.liferay.portlet.messageboards.social.MBActivityKeys;
83  import com.liferay.portlet.messageboards.util.Indexer;
84  import com.liferay.portlet.messageboards.util.MBUtil;
85  import com.liferay.portlet.messageboards.util.MailingListThreadLocal;
86  import com.liferay.portlet.messageboards.util.comparator.MessageThreadComparator;
87  import com.liferay.portlet.messageboards.util.comparator.ThreadLastPostDateComparator;
88  import com.liferay.portlet.social.model.SocialActivity;
89  
90  import java.io.IOException;
91  
92  import java.util.ArrayList;
93  import java.util.Comparator;
94  import java.util.Date;
95  import java.util.HashSet;
96  import java.util.Iterator;
97  import java.util.List;
98  import java.util.Set;
99  
100 import javax.mail.internet.InternetAddress;
101 
102 import javax.portlet.PortletPreferences;
103 
104 import org.apache.commons.lang.time.StopWatch;
105 
106 /**
107  * <a href="MBMessageLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
108  *
109  * @author Brian Wing Shun Chan
110  * @author Raymond Augé
111  *
112  */
113 public class MBMessageLocalServiceImpl extends MBMessageLocalServiceBaseImpl {
114 
115     public MBMessage addDiscussionMessage(
116             long userId, String userName, String subject, String body)
117         throws PortalException, SystemException {
118 
119         String className = StringPool.BLANK;
120         long classPK = 0;
121         long threadId = 0;
122         long parentMessageId = 0;
123         ServiceContext serviceContext = new ServiceContext();
124 
125         return addDiscussionMessage(
126             userId, userName, className, classPK, threadId, parentMessageId,
127             subject, body, serviceContext);
128     }
129 
130     public MBMessage addDiscussionMessage(
131             long userId, String userName, String className, long classPK,
132             long threadId, long parentMessageId, String subject, String body,
133             ServiceContext serviceContext)
134         throws PortalException, SystemException {
135 
136         long categoryId = CompanyConstants.SYSTEM;
137 
138         if (Validator.isNull(subject)) {
139             subject = "N/A";
140         }
141 
142         List<ObjectValuePair<String, byte[]>> files =
143             new ArrayList<ObjectValuePair<String, byte[]>>();
144         boolean anonymous = false;
145         double priority = 0.0;
146 
147         serviceContext.setAddCommunityPermissions(true);
148         serviceContext.setAddGuestPermissions(true);
149 
150         mbCategoryLocalService.getSystemCategory();
151 
152         MBMessage message = addMessage(
153             userId, userName, categoryId, threadId, parentMessageId, subject,
154             body, files, anonymous, priority, serviceContext);
155 
156         if (className.equals(BlogsEntry.class.getName()) &&
157             parentMessageId != MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID) {
158 
159             // Social
160 
161             BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(classPK);
162 
163             JSONObject extraData = JSONFactoryUtil.createJSONObject();
164 
165             extraData.put("messageId", message.getMessageId());
166 
167             socialActivityLocalService.addActivity(
168                 userId, entry.getGroupId(), BlogsEntry.class.getName(),
169                 classPK, BlogsActivityKeys.ADD_COMMENT, extraData.toString(),
170                 entry.getUserId());
171 
172             // Email
173 
174             try {
175                 sendBlogsCommentsEmail(userId, entry, message, serviceContext);
176             }
177             catch (Exception e) {
178                 _log.error(e, e);
179             }
180         }
181 
182         if (parentMessageId == MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID) {
183             long classNameId = PortalUtil.getClassNameId(className);
184 
185             MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
186                 classNameId, classPK);
187 
188             if (discussion == null) {
189                 long discussionId = counterLocalService.increment();
190 
191                 discussion = mbDiscussionPersistence.create(discussionId);
192 
193                 discussion.setClassNameId(classNameId);
194                 discussion.setClassPK(classPK);
195             }
196 
197             discussion.setThreadId(message.getThreadId());
198 
199             mbDiscussionPersistence.update(discussion, false);
200         }
201 
202         return message;
203     }
204 
205     public MBMessage addMessage(
206             long userId, String userName, long categoryId, String subject,
207             String body, List<ObjectValuePair<String, byte[]>> files,
208             boolean anonymous, double priority, ServiceContext serviceContext)
209         throws PortalException, SystemException {
210 
211         long threadId = 0;
212         long parentMessageId = 0;
213 
214         return addMessage(
215             null, userId, userName, categoryId, threadId, parentMessageId,
216             subject, body, files, anonymous, priority, serviceContext);
217     }
218 
219     public MBMessage addMessage(
220             long userId, String userName, long categoryId, long threadId,
221             long parentMessageId, String subject, String body,
222             List<ObjectValuePair<String, byte[]>> files, boolean anonymous,
223             double priority, ServiceContext serviceContext)
224         throws PortalException, SystemException {
225 
226         return addMessage(
227             null, userId, userName, categoryId, threadId, parentMessageId,
228             subject, body, files, anonymous, priority, serviceContext);
229     }
230 
231     public MBMessage addMessage(
232             String uuid, long userId, String userName, long categoryId,
233             long threadId, long parentMessageId, String subject, String body,
234             List<ObjectValuePair<String, byte[]>> files, boolean anonymous,
235             double priority, ServiceContext serviceContext)
236         throws PortalException, SystemException {
237 
238         StopWatch stopWatch = null;
239 
240         if (_log.isDebugEnabled()) {
241             stopWatch = new StopWatch();
242 
243             stopWatch.start();
244         }
245 
246         // Message
247 
248         User user = userPersistence.findByPrimaryKey(userId);
249         userName = user.isDefaultUser() ? userName : user.getFullName();
250         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
251             categoryId);
252         subject = ModelHintsUtil.trimString(
253             MBMessage.class.getName(), "subject", subject);
254 
255         PortletPreferences preferences =
256             ServiceContextUtil.getPortletPreferences(serviceContext);
257 
258         if (preferences != null) {
259             if (!MBUtil.isAllowAnonymousPosting(preferences)) {
260                 if (anonymous || user.isDefaultUser()) {
261                     throw new PrincipalException();
262                 }
263             }
264         }
265 
266         if (user.isDefaultUser()) {
267             anonymous = true;
268         }
269 
270         Date now = new Date();
271 
272         validate(subject, body);
273 
274         long messageId = counterLocalService.increment();
275 
276         logAddMessage(messageId, stopWatch, 1);
277 
278         MBMessage message = mbMessagePersistence.create(messageId);
279 
280         message.setUuid(uuid);
281         message.setCompanyId(user.getCompanyId());
282         message.setUserId(user.getUserId());
283         message.setUserName(userName);
284         message.setCreateDate(now);
285         message.setModifiedDate(now);
286 
287         // Thread
288 
289         MBMessage parentMessage = mbMessagePersistence.fetchByPrimaryKey(
290             parentMessageId);
291 
292         if (parentMessage == null) {
293             parentMessageId = MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID;
294         }
295 
296         MBThread thread = null;
297 
298         if (threadId > 0) {
299             thread = mbThreadPersistence.fetchByPrimaryKey(threadId);
300         }
301 
302         if ((thread == null) ||
303             (parentMessageId == MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID)) {
304 
305             threadId = counterLocalService.increment();
306 
307             thread = mbThreadPersistence.create(threadId);
308 
309             thread.setCategoryId(categoryId);
310             thread.setRootMessageId(messageId);
311         }
312 
313         thread.setMessageCount(thread.getMessageCount() + 1);
314 
315         if (anonymous) {
316             thread.setLastPostByUserId(0);
317         }
318         else {
319             thread.setLastPostByUserId(userId);
320         }
321 
322         thread.setLastPostDate(now);
323 
324         if (priority != MBThreadImpl.PRIORITY_NOT_GIVEN) {
325             thread.setPriority(priority);
326         }
327 
328         logAddMessage(messageId, stopWatch, 2);
329 
330         // Message
331 
332         message.setCategoryId(categoryId);
333         message.setThreadId(threadId);
334         message.setParentMessageId(parentMessageId);
335         message.setSubject(subject);
336         message.setBody(body);
337         message.setAttachments(!files.isEmpty());
338         message.setAnonymous(anonymous);
339 
340         // Attachments
341 
342         if (files.size() > 0) {
343             long companyId = message.getCompanyId();
344             String portletId = CompanyConstants.SYSTEM_STRING;
345             long groupId = GroupConstants.DEFAULT_PARENT_GROUP_ID;
346             long repositoryId = CompanyConstants.SYSTEM;
347             String dirName = message.getAttachmentsDir();
348 
349             try {
350                 dlService.deleteDirectory(
351                     companyId, portletId, repositoryId, dirName);
352             }
353             catch (NoSuchDirectoryException nsde) {
354                 if (_log.isDebugEnabled()) {
355                     _log.debug(nsde.getMessage());
356                 }
357             }
358 
359             dlService.addDirectory(companyId, repositoryId, dirName);
360 
361             for (int i = 0; i < files.size(); i++) {
362                 ObjectValuePair<String, byte[]> ovp = files.get(i);
363 
364                 String fileName = ovp.getKey();
365                 byte[] bytes = ovp.getValue();
366 
367                 try {
368                     dlService.addFile(
369                         companyId, portletId, groupId, repositoryId,
370                         dirName + "/" + fileName, 0, StringPool.BLANK,
371                         message.getModifiedDate(), new String[0], bytes);
372                 }
373                 catch (DuplicateFileException dfe) {
374                     if (_log.isDebugEnabled()) {
375                         _log.debug(dfe.getMessage());
376                     }
377                 }
378             }
379         }
380 
381         logAddMessage(messageId, stopWatch, 3);
382 
383         // Commit
384 
385         mbThreadPersistence.update(thread, false);
386         mbMessagePersistence.update(message, false);
387 
388         logAddMessage(messageId, stopWatch, 4);
389 
390         // Resources
391 
392         if (!category.isDiscussion()) {
393             if (user.isDefaultUser()) {
394                 addMessageResources(category, message, true, true);
395             }
396             if (serviceContext.getAddCommunityPermissions() ||
397                 serviceContext.getAddGuestPermissions()) {
398 
399                 addMessageResources(
400                     category, message,
401                     serviceContext.getAddCommunityPermissions(),
402                     serviceContext.getAddGuestPermissions());
403             }
404             else {
405                 addMessageResources(
406                     category, message, serviceContext.getCommunityPermissions(),
407                     serviceContext.getGuestPermissions());
408             }
409         }
410 
411         logAddMessage(messageId, stopWatch, 5);
412 
413         // Statistics
414 
415         if (!category.isDiscussion()) {
416             mbStatsUserLocalService.updateStatsUser(
417                 category.getGroupId(), userId);
418         }
419 
420         logAddMessage(messageId, stopWatch, 6);
421 
422         // Category
423 
424         category.setLastPostDate(now);
425 
426         mbCategoryPersistence.update(category, false);
427 
428         logAddMessage(messageId, stopWatch, 7);
429 
430         // Subscriptions
431 
432         notifySubscribers(category, message, serviceContext, false);
433 
434         logAddMessage(messageId, stopWatch, 8);
435 
436         // Social
437 
438         if (!message.isDiscussion() && !message.isAnonymous() &&
439             !user.isDefaultUser()) {
440 
441             int activityType = MBActivityKeys.ADD_MESSAGE;
442             long receiverUserId = 0;
443 
444             if (parentMessage != null) {
445                 activityType = MBActivityKeys.REPLY_MESSAGE;
446                 receiverUserId = parentMessage.getUserId();
447             }
448 
449             socialActivityLocalService.addActivity(
450                 userId, category.getGroupId(), MBMessage.class.getName(),
451                 messageId, activityType, StringPool.BLANK, receiverUserId);
452         }
453 
454         logAddMessage(messageId, stopWatch, 9);
455 
456         // Tags
457 
458         updateTagsAsset(userId, message, serviceContext.getTagsEntries());
459 
460         logAddMessage(messageId, stopWatch, 10);
461 
462         // Testing roll back
463 
464         /*if (true) {
465             throw new SystemException("Testing roll back");
466         }*/
467 
468         // Indexer
469 
470         try {
471             if (!category.isDiscussion()) {
472                 Indexer.addMessage(
473                     message.getCompanyId(), category.getGroupId(),
474                     message.getUserId(), message.getUserName(),
475                     category.getCategoryId(), threadId, messageId, subject,
476                     body, anonymous, message.getModifiedDate(),
477                     serviceContext.getTagsEntries(),
478                     message.getExpandoBridge());
479             }
480         }
481         catch (SearchException se) {
482             _log.error("Indexing " + messageId, se);
483         }
484 
485         logAddMessage(messageId, stopWatch, 11);
486 
487         return message;
488     }
489 
490     public void addMessageResources(
491             long categoryId, long messageId, boolean addCommunityPermissions,
492             boolean addGuestPermissions)
493         throws PortalException, SystemException {
494 
495         addMessageResources(
496             categoryId, null, messageId, addCommunityPermissions,
497             addGuestPermissions);
498     }
499 
500     public void addMessageResources(
501             long categoryId, String topicId, long messageId,
502             boolean addCommunityPermissions, boolean addGuestPermissions)
503         throws PortalException, SystemException {
504 
505         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
506             categoryId);
507         MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
508 
509         addMessageResources(
510             category, message, addCommunityPermissions, addGuestPermissions);
511     }
512 
513     public void addMessageResources(
514             MBCategory category, MBMessage message,
515             boolean addCommunityPermissions, boolean addGuestPermissions)
516         throws PortalException, SystemException {
517 
518         resourceLocalService.addResources(
519             message.getCompanyId(), category.getGroupId(), message.getUserId(),
520             MBMessage.class.getName(), message.getMessageId(),
521             false, addCommunityPermissions, addGuestPermissions);
522     }
523 
524     public void addMessageResources(
525             long categoryId, long messageId, String[] communityPermissions,
526             String[] guestPermissions)
527         throws PortalException, SystemException {
528 
529         addMessageResources(
530             categoryId, null, messageId, communityPermissions,
531             guestPermissions);
532     }
533 
534     public void addMessageResources(
535             long categoryId, String topicId, long messageId,
536             String[] communityPermissions, String[] guestPermissions)
537         throws PortalException, SystemException {
538 
539         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
540             categoryId);
541         MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
542 
543         addMessageResources(
544             category, message, communityPermissions, guestPermissions);
545     }
546 
547     public void addMessageResources(
548             MBCategory category, MBMessage message,
549             String[] communityPermissions, String[] guestPermissions)
550         throws PortalException, SystemException {
551 
552         resourceLocalService.addModelResources(
553             message.getCompanyId(), category.getGroupId(), message.getUserId(),
554             MBMessage.class.getName(), message.getMessageId(),
555             communityPermissions, guestPermissions);
556     }
557 
558     public void deleteDiscussionMessage(long messageId)
559         throws PortalException, SystemException {
560 
561         MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
562 
563         List<MBMessage> messages = new ArrayList<MBMessage>();
564 
565         messages.add(message);
566 
567         deleteDiscussionSocialActivities(BlogsEntry.class.getName(), messages);
568 
569         deleteMessage(message);
570     }
571 
572     public void deleteDiscussionMessages(String className, long classPK)
573         throws PortalException, SystemException {
574 
575         try {
576             long classNameId = PortalUtil.getClassNameId(className);
577 
578             MBDiscussion discussion = mbDiscussionPersistence.findByC_C(
579                 classNameId, classPK);
580 
581             List<MBMessage> messages = mbMessagePersistence.findByT_P(
582                 discussion.getThreadId(),
583                 MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID, 0, 1);
584 
585             deleteDiscussionSocialActivities(
586                 BlogsEntry.class.getName(), messages);
587 
588             if (messages.size() > 0) {
589                 MBMessage message = messages.get(0);
590 
591                 mbThreadLocalService.deleteThread(message.getThreadId());
592             }
593 
594             mbDiscussionPersistence.remove(discussion);
595         }
596         catch (NoSuchDiscussionException nsde) {
597             if (_log.isDebugEnabled()) {
598                 _log.debug(nsde.getMessage());
599             }
600         }
601     }
602 
603     public void deleteMessage(long messageId)
604         throws PortalException, SystemException {
605 
606         MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
607 
608         deleteMessage(message);
609     }
610 
611     public void deleteMessage(MBMessage message)
612         throws PortalException, SystemException {
613 
614         // Indexer
615 
616         try {
617             Indexer.deleteMessage(
618                 message.getCompanyId(), message.getMessageId());
619         }
620         catch (SearchException se) {
621             _log.error("Deleting index " + message.getMessageId(), se);
622         }
623 
624         // Attachments
625 
626         if (message.isAttachments()) {
627             long companyId = message.getCompanyId();
628             String portletId = CompanyConstants.SYSTEM_STRING;
629             long repositoryId = CompanyConstants.SYSTEM;
630             String dirName = message.getAttachmentsDir();
631 
632             try {
633                 dlService.deleteDirectory(
634                     companyId, portletId, repositoryId, dirName);
635             }
636             catch (NoSuchDirectoryException nsde) {
637                 if (_log.isDebugEnabled()) {
638                     _log.debug(nsde.getMessage());
639                 }
640             }
641         }
642 
643         // Thread
644 
645         int count = mbMessagePersistence.countByThreadId(message.getThreadId());
646 
647         // Message flags
648 
649         if (message.isRoot()) {
650             mbMessageFlagLocalService.deleteQuestionAndAnswerFlags(
651                 message.getThreadId());
652         }
653 
654         if (count == 1) {
655 
656             // Attachments
657 
658             long companyId = message.getCompanyId();
659             String portletId = CompanyConstants.SYSTEM_STRING;
660             long repositoryId = CompanyConstants.SYSTEM;
661             String dirName = message.getThreadAttachmentsDir();
662 
663             try {
664                 dlService.deleteDirectory(
665                     companyId, portletId, repositoryId, dirName);
666             }
667             catch (NoSuchDirectoryException nsde) {
668                 if (_log.isDebugEnabled()) {
669                     _log.debug(nsde.getMessage());
670                 }
671             }
672 
673             // Subscriptions
674 
675             subscriptionLocalService.deleteSubscriptions(
676                 message.getCompanyId(), MBThread.class.getName(),
677                 message.getThreadId());
678 
679             // Thread
680 
681             mbThreadPersistence.remove(message.getThreadId());
682         }
683         else if (count > 1) {
684             MBThread thread = mbThreadPersistence.findByPrimaryKey(
685                 message.getThreadId());
686 
687             // Message is a root message
688 
689             if (thread.getRootMessageId() == message.getMessageId()) {
690                 List<MBMessage> childrenMessages =
691                     mbMessagePersistence.findByT_P(
692                         message.getThreadId(), message.getMessageId());
693 
694                 if (childrenMessages.size() > 1) {
695                     throw new RequiredMessageException(
696                         String.valueOf(message.getMessageId()));
697                 }
698                 else if (childrenMessages.size() == 1) {
699                     MBMessage childMessage = childrenMessages.get(0);
700 
701                     childMessage.setParentMessageId(
702                         MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID);
703 
704                     mbMessagePersistence.update(childMessage, false);
705 
706                     thread.setRootMessageId(childMessage.getMessageId());
707 
708                     mbThreadPersistence.update(thread, false);
709                 }
710             }
711 
712             // Message is a child message
713 
714             else {
715                 List<MBMessage> childrenMessages =
716                     mbMessagePersistence.findByT_P(
717                         message.getThreadId(), message.getMessageId());
718 
719                 // Message has children messages
720 
721                 if (childrenMessages.size() > 0) {
722                     Iterator<MBMessage> itr = childrenMessages.iterator();
723 
724                     while (itr.hasNext()) {
725                         MBMessage childMessage = itr.next();
726 
727                         childMessage.setParentMessageId(
728                             message.getParentMessageId());
729 
730                         mbMessagePersistence.update(childMessage, false);
731                     }
732                 }
733             }
734 
735             // Thread
736 
737             thread.setMessageCount(count - 1);
738 
739             mbThreadPersistence.update(thread, false);
740         }
741 
742         // Tags
743 
744         tagsAssetLocalService.deleteAsset(
745             MBMessage.class.getName(), message.getMessageId());
746 
747         // Social
748 
749         socialActivityLocalService.deleteActivities(
750             MBMessage.class.getName(), message.getMessageId());
751 
752         // Ratings
753 
754         ratingsStatsLocalService.deleteStats(
755             MBMessage.class.getName(), message.getMessageId());
756 
757         // Message flags
758 
759         mbMessageFlagPersistence.removeByMessageId(message.getMessageId());
760 
761         // Resources
762 
763         if (!message.isDiscussion()) {
764             resourceLocalService.deleteResource(
765                 message.getCompanyId(), MBMessage.class.getName(),
766                 ResourceConstants.SCOPE_INDIVIDUAL, message.getMessageId());
767         }
768 
769         // Message
770 
771         mbMessagePersistence.remove(message);
772     }
773 
774     public List<MBMessage> getCategoryMessages(
775             long categoryId, int start, int end)
776         throws SystemException {
777 
778         return mbMessagePersistence.findByCategoryId(categoryId, start, end);
779     }
780 
781     public List<MBMessage> getCategoryMessages(
782             long categoryId, int start, int end, OrderByComparator obc)
783         throws SystemException {
784 
785         return mbMessagePersistence.findByCategoryId(
786             categoryId, start, end, obc);
787     }
788 
789     public int getCategoryMessagesCount(long categoryId)
790         throws SystemException {
791 
792         return mbMessagePersistence.countByCategoryId(categoryId);
793     }
794 
795     public int getCategoriesMessagesCount(List<Long> categoryIds)
796         throws SystemException {
797 
798         return mbMessageFinder.countByCategoryIds(categoryIds);
799     }
800 
801     public List<MBMessage> getCompanyMessages(
802             long companyId, int start, int end)
803         throws SystemException {
804 
805         return mbMessagePersistence.findByCompanyId(companyId, start, end);
806     }
807 
808     public List<MBMessage> getCompanyMessages(
809             long companyId, int start, int end, OrderByComparator obc)
810         throws SystemException {
811 
812         return mbMessagePersistence.findByCompanyId(companyId, start, end, obc);
813     }
814 
815     public int getCompanyMessagesCount(long companyId)
816         throws SystemException {
817 
818         return mbMessagePersistence.countByCompanyId(companyId);
819     }
820 
821     public MBMessageDisplay getDiscussionMessageDisplay(
822             long userId, String className, long classPK)
823         throws PortalException, SystemException {
824 
825         long classNameId = PortalUtil.getClassNameId(className);
826 
827         MBMessage message = null;
828 
829         MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
830             classNameId, classPK);
831 
832         if (discussion != null) {
833             List<MBMessage> messages = mbMessagePersistence.findByT_P(
834                 discussion.getThreadId(),
835                 MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID);
836 
837             message = messages.get(0);
838         }
839         else {
840             String subject = String.valueOf(classPK);
841             //String body = subject;
842 
843             message = addDiscussionMessage(
844                 userId, null, className, classPK, 0,
845                 MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID,
846                 subject, subject, new ServiceContext());
847 
848             long discussionId = counterLocalService.increment();
849 
850             discussion = mbDiscussionPersistence.create(
851                 discussionId);
852 
853             discussion.setClassNameId(classNameId);
854             discussion.setClassPK(classPK);
855             discussion.setThreadId(message.getThreadId());
856 
857             mbDiscussionPersistence.update(discussion, false);
858         }
859 
860         return getMessageDisplay(message);
861     }
862 
863     public int getDiscussionMessagesCount(long classNameId, long classPK)
864         throws SystemException {
865 
866         MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
867             classNameId, classPK);
868 
869         if (discussion == null) {
870             return 0;
871         }
872 
873         int count = mbMessagePersistence.countByThreadId(
874             discussion.getThreadId());
875 
876         if (count >= 1) {
877             return count - 1;
878         }
879         else {
880             return 0;
881         }
882     }
883 
884     public List<MBDiscussion> getDiscussions(String className)
885         throws SystemException {
886 
887         long classNameId = PortalUtil.getClassNameId(className);
888 
889         return mbDiscussionPersistence.findByClassNameId(classNameId);
890     }
891 
892     public List<MBMessage> getGroupMessages(long groupId, int start, int end)
893         throws SystemException {
894 
895         return mbMessageFinder.findByGroupId(groupId, start, end);
896     }
897 
898     public List<MBMessage> getGroupMessages(
899             long groupId, int start, int end, OrderByComparator obc)
900         throws SystemException {
901 
902         return mbMessageFinder.findByGroupId(groupId, start, end, obc);
903     }
904 
905     public List<MBMessage> getGroupMessages(
906             long groupId, long userId, int start, int end)
907         throws SystemException {
908 
909         return mbMessageFinder.findByG_U(groupId, userId, start, end);
910     }
911 
912     public List<MBMessage> getGroupMessages(
913             long groupId, long userId, int start, int end,
914             OrderByComparator obc)
915         throws SystemException {
916 
917         return mbMessageFinder.findByG_U(groupId, userId, start, end, obc);
918     }
919 
920     public int getGroupMessagesCount(long groupId) throws SystemException {
921         return mbMessageFinder.countByGroupId(groupId);
922     }
923 
924     public int getGroupMessagesCount(long groupId, long userId)
925         throws SystemException {
926 
927         return mbMessageFinder.countByG_U(groupId, userId);
928     }
929 
930     public MBMessage getMessage(long messageId)
931         throws PortalException, SystemException {
932 
933         return mbMessagePersistence.findByPrimaryKey(messageId);
934     }
935 
936     public List<MBMessage> getMessages(String className, long classPK)
937         throws SystemException {
938 
939         long classNameId = PortalUtil.getClassNameId(className);
940 
941         return mbMessageFinder.findByC_C(classNameId, classPK);
942     }
943 
944     public MBMessageDisplay getMessageDisplay(long messageId)
945         throws PortalException, SystemException {
946 
947         MBMessage message = getMessage(messageId);
948 
949         return getMessageDisplay(message);
950     }
951 
952     public MBMessageDisplay getMessageDisplay(MBMessage message)
953         throws PortalException, SystemException {
954 
955         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
956             message.getCategoryId());
957 
958         MBMessage parentMessage = null;
959 
960         if (message.isReply()) {
961             parentMessage = mbMessagePersistence.findByPrimaryKey(
962                 message.getParentMessageId());
963         }
964 
965         MBThread thread = mbThreadPersistence.findByPrimaryKey(
966             message.getThreadId());
967 
968         thread.setViewCount(thread.getViewCount() + 1);
969 
970         mbThreadPersistence.update(thread, false);
971 
972         MBTreeWalker treeWalker = new MBTreeWalkerImpl(message);
973 
974         ThreadLastPostDateComparator comparator =
975             new ThreadLastPostDateComparator(false);
976 
977         MBThread[] prevAndNextThreads =
978             mbThreadPersistence.findByCategoryId_PrevAndNext(
979                 message.getThreadId(), message.getCategoryId(), comparator);
980 
981         MBThread previousThread = prevAndNextThreads[0];
982         MBThread nextThread = prevAndNextThreads[2];
983 
984         MBThread firstThread = null;
985 
986         try {
987             firstThread = mbThreadPersistence.findByCategoryId_First(
988                 message.getCategoryId(), comparator);
989         }
990         catch (NoSuchThreadException nste) {
991         }
992 
993         MBThread lastThread = null;
994 
995         try {
996             lastThread = mbThreadPersistence.findByCategoryId_Last(
997                 message.getCategoryId(), comparator);
998         }
999         catch (NoSuchThreadException nste) {
1000        }
1001
1002        return new MBMessageDisplayImpl(
1003            message, parentMessage, category, thread, treeWalker,
1004            previousThread, nextThread, firstThread, lastThread);
1005    }
1006
1007    public List<MBMessage> getNoAssetMessages() throws SystemException {
1008        return mbMessageFinder.findByNoAssets();
1009    }
1010
1011    public List<MBMessage> getThreadMessages(long threadId)
1012        throws SystemException {
1013
1014        return getThreadMessages(threadId, new MessageThreadComparator());
1015    }
1016
1017    public List<MBMessage> getThreadMessages(
1018            long threadId, Comparator<MBMessage> comparator)
1019        throws SystemException {
1020
1021        List<MBMessage> messages = mbMessagePersistence.findByThreadId(
1022            threadId);
1023
1024        return ListUtil.sort(messages, comparator);
1025    }
1026
1027    public int getThreadMessagesCount(long threadId) throws SystemException {
1028        return mbMessagePersistence.countByThreadId(threadId);
1029    }
1030
1031    public void reIndex(long messageId) throws SystemException {
1032        if (SearchEngineUtil.isIndexReadOnly()) {
1033            return;
1034        }
1035
1036        MBMessage message = mbMessagePersistence.fetchByPrimaryKey(messageId);
1037
1038        if (message == null) {
1039            return;
1040        }
1041
1042        MBCategory category = mbCategoryPersistence.fetchByPrimaryKey(
1043            message.getCategoryId());
1044
1045        long companyId = category.getCompanyId();
1046        long groupId = category.getGroupId();
1047        long userId = message.getUserId();
1048        String userName = message.getUserName();
1049        long categoryId = category.getCategoryId();
1050        long threadId = message.getThreadId();
1051        String title = message.getSubject();
1052        String content = message.getBody();
1053        boolean anonymous = message.isAnonymous();
1054        Date modifiedDate = message.getModifiedDate();
1055
1056        String[] tagsEntries = tagsEntryLocalService.getEntryNames(
1057            MBMessage.class.getName(), messageId);
1058
1059        try {
1060            Indexer.updateMessage(
1061                companyId, groupId, userId, userName, categoryId, threadId,
1062                messageId, title, content, anonymous, modifiedDate, tagsEntries,
1063                message.getExpandoBridge());
1064        }
1065        catch (SearchException se) {
1066            _log.error("Reindexing " + messageId, se);
1067        }
1068    }
1069
1070    public void subscribeMessage(long userId, long messageId)
1071        throws PortalException, SystemException {
1072
1073        MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1074
1075        subscriptionLocalService.addSubscription(
1076            userId, MBThread.class.getName(), message.getThreadId());
1077    }
1078
1079    public void unsubscribeMessage(long userId, long messageId)
1080        throws PortalException, SystemException {
1081
1082        MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1083
1084        subscriptionLocalService.deleteSubscription(
1085            userId, MBThread.class.getName(), message.getThreadId());
1086    }
1087
1088    public MBMessage updateDiscussionMessage(
1089            long userId, long messageId, String subject, String body)
1090        throws PortalException, SystemException {
1091
1092        if (Validator.isNull(subject)) {
1093            subject = "N/A";
1094        }
1095
1096        List<ObjectValuePair<String, byte[]>> files =
1097            new ArrayList<ObjectValuePair<String, byte[]>>();
1098        List<String> existingFiles = new ArrayList<String>();
1099        double priority = 0.0;
1100        ServiceContext serviceContext = new ServiceContext();
1101
1102        return updateMessage(
1103            userId, messageId, subject, body, files, existingFiles, priority,
1104            serviceContext);
1105    }
1106
1107    public MBMessage updateMessage(
1108            long userId, long messageId, String subject, String body,
1109            List<ObjectValuePair<String, byte[]>> files,
1110            List<String> existingFiles, double priority,
1111            ServiceContext serviceContext)
1112        throws PortalException, SystemException {
1113
1114        // Message
1115
1116        MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1117
1118        MBCategory category = message.getCategory();
1119        subject = ModelHintsUtil.trimString(
1120            MBMessage.class.getName(), "subject", subject);
1121        Date now = new Date();
1122
1123        validate(subject, body);
1124
1125        // Attachments
1126
1127        long companyId = message.getCompanyId();
1128        String portletId = CompanyConstants.SYSTEM_STRING;
1129        long groupId = GroupConstants.DEFAULT_PARENT_GROUP_ID;
1130        long repositoryId = CompanyConstants.SYSTEM;
1131        String dirName = message.getAttachmentsDir();
1132
1133        if (!files.isEmpty() || !existingFiles.isEmpty()) {
1134            try {
1135                dlService.addDirectory(companyId, repositoryId, dirName);
1136            }
1137            catch (DuplicateDirectoryException dde) {
1138            }
1139
1140            String[] fileNames = dlService.getFileNames(
1141                companyId, repositoryId, dirName);
1142
1143            for (String fileName: fileNames) {
1144                if (!existingFiles.contains(fileName)) {
1145                    dlService.deleteFile(
1146                        companyId, portletId, repositoryId, fileName);
1147                }
1148            }
1149
1150            for (int i = 0; i < files.size(); i++) {
1151                ObjectValuePair<String, byte[]> ovp = files.get(i);
1152
1153                String fileName = ovp.getKey();
1154                byte[] bytes = ovp.getValue();
1155
1156                try {
1157                    dlService.addFile(
1158                        companyId, portletId, groupId, repositoryId,
1159                        dirName + "/" + fileName, 0, StringPool.BLANK,
1160                        message.getModifiedDate(), new String[0], bytes);
1161                }
1162                catch (DuplicateFileException dfe) {
1163                }
1164            }
1165        }
1166        else {
1167            try {
1168                dlService.deleteDirectory(
1169                    companyId, portletId, repositoryId, dirName);
1170            }
1171            catch (NoSuchDirectoryException nsde) {
1172            }
1173        }
1174
1175        // Message
1176
1177        message.setModifiedDate(now);
1178        message.setSubject(subject);
1179        message.setBody(body);
1180        message.setAttachments(!files.isEmpty() || !existingFiles.isEmpty());
1181
1182        mbMessagePersistence.update(message, false);
1183
1184        // Thread
1185
1186        MBThread thread = mbThreadPersistence.findByPrimaryKey(
1187            message.getThreadId());
1188
1189        if (priority != MBThreadImpl.PRIORITY_NOT_GIVEN) {
1190            thread.setPriority(priority);
1191        }
1192
1193        mbThreadPersistence.update(thread, false);
1194
1195        // Category
1196
1197        category.setLastPostDate(now);
1198
1199        mbCategoryPersistence.update(category, false);
1200
1201        // Subscriptions
1202
1203        notifySubscribers(category, message, serviceContext, true);
1204
1205        // Tags
1206
1207        updateTagsAsset(userId, message, serviceContext.getTagsEntries());
1208
1209        // Indexer
1210
1211        try {
1212            if (!category.isDiscussion()) {
1213                Indexer.updateMessage(
1214                    message.getCompanyId(), category.getGroupId(),
1215                    message.getUserId(), message.getUserName(),
1216                    category.getCategoryId(), message.getThreadId(), messageId,
1217                    subject, body, message.isAnonymous(),
1218                    message.getModifiedDate(), serviceContext.getTagsEntries(),
1219                    message.getExpandoBridge());
1220            }
1221        }
1222        catch (SearchException se) {
1223            _log.error("Indexing " + messageId, se);
1224        }
1225
1226        return message;
1227    }
1228
1229    public MBMessage updateMessage(
1230            long messageId, Date createDate, Date modifiedDate)
1231        throws PortalException, SystemException {
1232
1233        // Message
1234
1235        MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1236
1237        message.setCreateDate(createDate);
1238        message.setModifiedDate(modifiedDate);
1239
1240        mbMessagePersistence.update(message, false);
1241
1242        // Thread
1243
1244        MBThread thread = mbThreadPersistence.findByPrimaryKey(
1245            message.getThreadId());
1246
1247        if (message.isAnonymous()) {
1248            thread.setLastPostByUserId(0);
1249        }
1250        else {
1251            thread.setLastPostByUserId(message.getUserId());
1252        }
1253
1254        thread.setLastPostDate(modifiedDate);
1255
1256        mbThreadPersistence.update(thread, false);
1257
1258        // Category
1259
1260        MBCategory category = mbCategoryPersistence.findByPrimaryKey(
1261            message.getCategoryId());
1262
1263        category.setLastPostDate(modifiedDate);
1264
1265        mbCategoryPersistence.update(category, false);
1266
1267        // Statistics
1268
1269        MBStatsUser statsUser = mbStatsUserPersistence.fetchByG_U(
1270            category.getGroupId(), message.getUserId());
1271
1272        if (statsUser != null) {
1273            statsUser.setLastPostDate(modifiedDate);
1274
1275            mbStatsUserPersistence.update(statsUser, false);
1276        }
1277
1278        return message;
1279    }
1280
1281    public MBMessage updateMessage(long messageId, String body)
1282        throws PortalException, SystemException {
1283
1284        MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1285
1286        message.setBody(body);
1287
1288        mbMessagePersistence.update(message, false);
1289
1290        return message;
1291    }
1292
1293    public void updateTagsAsset(
1294            long userId, MBMessage message, String[] tagsEntries)
1295        throws PortalException, SystemException {
1296
1297        if (message.isDiscussion()) {
1298            return;
1299        }
1300
1301        tagsAssetLocalService.updateAsset(
1302            userId, message.getCategory().getGroupId(),
1303            MBMessage.class.getName(), message.getMessageId(), null,
1304            tagsEntries, true, null, null, null, null, ContentTypes.TEXT_HTML,
1305            message.getSubject(), null, null, null, 0, 0, null, false);
1306    }
1307
1308    protected void deleteDiscussionSocialActivities(
1309            String className, List<MBMessage> messages)
1310        throws PortalException, SystemException {
1311
1312        if (messages.size() == 0) {
1313            return;
1314        }
1315
1316        MBMessage message = messages.get(0);
1317
1318        MBDiscussion discussion = mbDiscussionPersistence.findByThreadId(
1319            message.getThreadId());
1320
1321        long classNameId = PortalUtil.getClassNameId(className);
1322        long classPK = discussion.getClassPK();
1323
1324        if (discussion.getClassNameId() != classNameId) {
1325            return;
1326        }
1327
1328        Set<Long> messageIds = new HashSet<Long>();
1329
1330        for (MBMessage curMessage : messages) {
1331            messageIds.add(curMessage.getMessageId());
1332        }
1333
1334        List<SocialActivity> socialActivities =
1335            socialActivityLocalService.getActivities(
1336                0, className, classPK, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
1337
1338        for (SocialActivity socialActivity : socialActivities) {
1339            if (Validator.isNull(socialActivity.getExtraData())) {
1340                continue;
1341            }
1342
1343            JSONObject extraData = JSONFactoryUtil.createJSONObject(
1344                socialActivity.getExtraData());
1345
1346            long extraDataMessageId = extraData.getLong("messageId");
1347
1348            if (messageIds.contains(extraDataMessageId)) {
1349                socialActivityLocalService.deleteActivity(
1350                    socialActivity.getActivityId());
1351            }
1352        }
1353    }
1354
1355    protected void logAddMessage(
1356        long messageId, StopWatch stopWatch, int block) {
1357
1358        if (_log.isDebugEnabled()) {
1359            if ((messageId != 1) && ((messageId % 10) != 0)) {
1360                return;
1361            }
1362
1363            _log.debug(
1364                "Adding message block " + block + " for " + messageId +
1365                    " takes " + stopWatch.getTime() + " ms");
1366        }
1367    }
1368
1369    protected void notifySubscribers(
1370            MBCategory category, MBMessage message,
1371            ServiceContext serviceContext, boolean update)
1372        throws PortalException, SystemException {
1373
1374        String portalURL = serviceContext.getPortalURL();
1375        String layoutURL = serviceContext.getLayoutURL();
1376
1377        if (Validator.isNull(portalURL) || Validator.isNull(layoutURL) ||
1378            category.isDiscussion()) {
1379
1380            return;
1381        }
1382
1383        PortletPreferences preferences =
1384            ServiceContextUtil.getPortletPreferences(serviceContext);
1385
1386        if (preferences == null) {
1387            long ownerId = category.getGroupId();
1388            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1389            long plid = PortletKeys.PREFS_PLID_SHARED;
1390            String portletId = PortletKeys.MESSAGE_BOARDS;
1391            String defaultPreferences = null;
1392
1393            preferences = portletPreferencesLocalService.getPreferences(
1394                category.getCompanyId(), ownerId, ownerType, plid, portletId,
1395                defaultPreferences);
1396        }
1397
1398        if (!update && MBUtil.getEmailMessageAddedEnabled(preferences)) {
1399        }
1400        else if (update && MBUtil.getEmailMessageUpdatedEnabled(preferences)) {
1401        }
1402        else {
1403            return;
1404        }
1405
1406        Company company = companyPersistence.findByPrimaryKey(
1407            message.getCompanyId());
1408
1409        Group group = groupPersistence.findByPrimaryKey(category.getGroupId());
1410
1411        User user = userPersistence.findByPrimaryKey(message.getUserId());
1412
1413        String emailAddress = user.getEmailAddress();
1414        String fullName = user.getFullName();
1415
1416        if (message.isAnonymous()) {
1417            emailAddress = StringPool.BLANK;
1418            fullName = LanguageUtil.get(
1419                ServiceContextUtil.getLocale(serviceContext), "anonymous");
1420        }
1421
1422        List<Long> categoryIds = new ArrayList<Long>();
1423
1424        categoryIds.add(category.getCategoryId());
1425        categoryIds.addAll(category.getAncestorCategoryIds());
1426
1427        String messageURL = StringPool.BLANK;
1428
1429        messageURL =
1430            portalURL + layoutURL + "/-/message_boards/message/" +
1431                message.getMessageId();
1432
1433        String portletName = PortalUtil.getPortletTitle(
1434            PortletKeys.MESSAGE_BOARDS, user);
1435
1436        String fromName = MBUtil.getEmailFromName(preferences);
1437        String fromAddress = MBUtil.getEmailFromAddress(preferences);
1438
1439        String mailingListAddress = StringPool.BLANK;
1440
1441        if (PropsValues.POP_SERVER_NOTIFICATIONS_ENABLED) {
1442            mailingListAddress = MBUtil.getMailingListAddress(
1443                message.getCategoryId(), message.getMessageId(),
1444                company.getMx(), fromAddress);
1445        }
1446
1447        String replyToAddress = mailingListAddress;
1448        String mailId = MBUtil.getMailId(
1449            company.getMx(), message.getCategoryId(), message.getMessageId());
1450
1451        fromName = StringUtil.replace(
1452            fromName,
1453            new String[] {
1454                "[$COMPANY_ID$]",
1455                "[$COMPANY_MX$]",
1456                "[$COMPANY_NAME$]",
1457                "[$COMMUNITY_NAME$]",
1458                "[$MAILING_LIST_ADDRESS$]",
1459                "[$MESSAGE_USER_ADDRESS$]",
1460                "[$MESSAGE_USER_NAME$]",
1461                "[$PORTLET_NAME$]"
1462            },
1463            new String[] {
1464                String.valueOf(company.getCompanyId()),
1465                company.getMx(),
1466                company.getName(),
1467                group.getName(),
1468                mailingListAddress,
1469                emailAddress,
1470                fullName,
1471                portletName
1472            });
1473
1474        fromAddress = StringUtil.replace(
1475            fromAddress,
1476            new String[] {
1477                "[$COMPANY_ID$]",
1478                "[$COMPANY_MX$]",
1479                "[$COMPANY_NAME$]",
1480                "[$COMMUNITY_NAME$]",
1481                "[$MAILING_LIST_ADDRESS$]",
1482                "[$MESSAGE_USER_ADDRESS$]",
1483                "[$MESSAGE_USER_NAME$]",
1484                "[$PORTLET_NAME$]"
1485            },
1486            new String[] {
1487                String.valueOf(company.getCompanyId()),
1488                company.getMx(),
1489                company.getName(),
1490                group.getName(),
1491                mailingListAddress,
1492                emailAddress,
1493                fullName,
1494                portletName
1495            });
1496
1497        String subjectPrefix = null;
1498        String body = null;
1499        String signature = null;
1500        boolean htmlFormat = MBUtil.getEmailHtmlFormat(preferences);
1501
1502        if (update) {
1503            subjectPrefix = MBUtil.getEmailMessageUpdatedSubjectPrefix(
1504                preferences);
1505            body = MBUtil.getEmailMessageUpdatedBody(preferences);
1506            signature = MBUtil.getEmailMessageUpdatedSignature(preferences);
1507        }
1508        else {
1509            subjectPrefix = MBUtil.getEmailMessageAddedSubjectPrefix(
1510                preferences);
1511            body = MBUtil.getEmailMessageAddedBody(preferences);
1512            signature = MBUtil.getEmailMessageAddedSignature(preferences);
1513        }
1514
1515        if (Validator.isNotNull(signature)) {
1516            body +=  "\n--\n" + signature;
1517        }
1518
1519        subjectPrefix = StringUtil.replace(
1520            subjectPrefix,
1521            new String[] {
1522                "[$CATEGORY_NAME$]",
1523                "[$COMPANY_ID$]",
1524                "[$COMPANY_MX$]",
1525                "[$COMPANY_NAME$]",
1526                "[$COMMUNITY_NAME$]",
1527                "[$FROM_ADDRESS$]",
1528                "[$FROM_NAME$]",
1529                "[$MAILING_LIST_ADDRESS$]",
1530                "[$MESSAGE_BODY$]",
1531                "[$MESSAGE_ID$]",
1532                "[$MESSAGE_SUBJECT$]",
1533                "[$MESSAGE_USER_ADDRESS$]",
1534                "[$MESSAGE_USER_NAME$]",
1535                "[$PORTAL_URL$]",
1536                "[$PORTLET_NAME$]"
1537            },
1538            new String[] {
1539                category.getName(),
1540                String.valueOf(company.getCompanyId()),
1541                company.getMx(),
1542                company.getName(),
1543                group.getName(),
1544                fromAddress,
1545                fromName,
1546                mailingListAddress,
1547                message.getBody(),
1548                String.valueOf(message.getMessageId()),
1549                message.getSubject(),
1550                emailAddress,
1551                fullName,
1552                company.getVirtualHost(),
1553                portletName
1554            });
1555
1556        body = StringUtil.replace(
1557            body,
1558            new String[] {
1559                "[$CATEGORY_NAME$]",
1560                "[$COMPANY_ID$]",
1561                "[$COMPANY_MX$]",
1562                "[$COMPANY_NAME$]",
1563                "[$COMMUNITY_NAME$]",
1564                "[$FROM_ADDRESS$]",
1565                "[$FROM_NAME$]",
1566                "[$MAILING_LIST_ADDRESS$]",
1567                "[$MESSAGE_BODY$]",
1568                "[$MESSAGE_ID$]",
1569                "[$MESSAGE_SUBJECT$]",
1570                "[$MESSAGE_URL$]",
1571                "[$MESSAGE_USER_ADDRESS$]",
1572                "[$MESSAGE_USER_NAME$]",
1573                "[$PORTAL_URL$]",
1574                "[$PORTLET_NAME$]"
1575            },
1576            new String[] {
1577                category.getName(),
1578                String.valueOf(company.getCompanyId()),
1579                company.getMx(),
1580                company.getName(),
1581                group.getName(),
1582                fromAddress,
1583                fromName,
1584                mailingListAddress,
1585                message.getBody(),
1586                String.valueOf(message.getMessageId()),
1587                message.getSubject(),
1588                messageURL,
1589                emailAddress,
1590                fullName,
1591                company.getVirtualHost(),
1592                portletName
1593            });
1594
1595        String subject = message.getSubject();
1596
1597        if (subject.indexOf(subjectPrefix) == -1) {
1598            subject = subjectPrefix.trim() + " " + subject.trim();
1599        }
1600
1601        String inReplyTo = null;
1602
1603        if (message.getParentMessageId() !=
1604                MBMessageImpl.DEFAULT_PARENT_MESSAGE_ID) {
1605
1606            inReplyTo = MBUtil.getMailId(
1607                company.getMx(), message.getCategoryId(),
1608                message.getParentMessageId());
1609        }
1610
1611        com.liferay.portal.kernel.messaging.Message messagingObj =
1612            new com.liferay.portal.kernel.messaging.Message();
1613
1614        messagingObj.put("companyId", message.getCompanyId());
1615        messagingObj.put("userId", message.getUserId());
1616        messagingObj.put("categoryIds", StringUtil.merge(categoryIds));
1617        messagingObj.put("threadId", message.getThreadId());
1618        messagingObj.put("fromName", fromName);
1619        messagingObj.put("fromAddress", fromAddress);
1620        messagingObj.put("subject", subject);
1621        messagingObj.put("body", body);
1622        messagingObj.put("replyToAddress", replyToAddress);
1623        messagingObj.put("mailId", mailId);
1624        messagingObj.put("inReplyTo", inReplyTo);
1625        messagingObj.put("htmlFormat", htmlFormat);
1626        messagingObj.put(
1627            "sourceMailingList", MailingListThreadLocal.isSourceMailingList());
1628
1629        MessageBusUtil.sendMessage(
1630            DestinationNames.MESSAGE_BOARDS, messagingObj);
1631    }
1632
1633    protected void sendBlogsCommentsEmail(
1634            long userId, BlogsEntry entry, MBMessage message,
1635            ServiceContext serviceContext)
1636        throws IOException, PortalException, SystemException {
1637
1638        long companyId = message.getCompanyId();
1639
1640        if (!PrefsPropsUtil.getBoolean(
1641                companyId, PropsKeys.BLOGS_EMAIL_COMMENTS_ADDED_ENABLED)) {
1642
1643            return;
1644        }
1645
1646        String portalURL = serviceContext.getPortalURL();
1647        String layoutURL = serviceContext.getLayoutURL();
1648
1649        String blogsEntryURL =
1650            portalURL + layoutURL + "/-/blogs/" + entry.getUrlTitle();
1651
1652        User blogsUser = userPersistence.findByPrimaryKey(entry.getUserId());
1653        User commentsUser = userPersistence.findByPrimaryKey(userId);
1654
1655        String fromName = PrefsPropsUtil.getString(
1656            companyId, PropsKeys.ADMIN_EMAIL_FROM_NAME);
1657        String fromAddress = PrefsPropsUtil.getString(
1658            companyId, PropsKeys.ADMIN_EMAIL_FROM_ADDRESS);
1659
1660        String toName = blogsUser.getFullName();
1661        String toAddress = blogsUser.getEmailAddress();
1662
1663        String subject = PrefsPropsUtil.getContent(
1664            companyId, PropsKeys.BLOGS_EMAIL_COMMENTS_ADDED_SUBJECT);
1665        String body = PrefsPropsUtil.getContent(
1666            companyId, PropsKeys.BLOGS_EMAIL_COMMENTS_ADDED_BODY);
1667
1668        subject = StringUtil.replace(
1669            subject,
1670            new String[] {
1671                "[$BLOGS_COMMENTS_USER_ADDRESS$]",
1672                "[$BLOGS_COMMENTS_USER_NAME$]",
1673                "[$BLOGS_ENTRY_URL$]",
1674                "[$FROM_ADDRESS$]",
1675                "[$FROM_NAME$]",
1676                "[$TO_ADDRESS$]",
1677                "[$TO_NAME$]"
1678            },
1679            new String[] {
1680                commentsUser.getEmailAddress(),
1681                commentsUser.getFullName(),
1682                blogsEntryURL,
1683                fromAddress,
1684                fromName,
1685                toAddress,
1686                toName
1687            });
1688
1689        body = StringUtil.replace(
1690            body,
1691            new String[] {
1692                "[$BLOGS_COMMENTS_USER_ADDRESS$]",
1693                "[$BLOGS_COMMENTS_USER_NAME$]",
1694                "[$BLOGS_ENTRY_URL$]",
1695                "[$FROM_ADDRESS$]",
1696                "[$FROM_NAME$]",
1697                "[$TO_ADDRESS$]",
1698                "[$TO_NAME$]"
1699            },
1700            new String[] {
1701                commentsUser.getEmailAddress(),
1702                commentsUser.getFullName(),
1703                blogsEntryURL,
1704                fromAddress,
1705                fromName,
1706                toAddress,
1707                toName
1708            });
1709
1710        InternetAddress from = new InternetAddress(fromAddress, fromName);
1711
1712        InternetAddress to = new InternetAddress(toAddress, toName);
1713
1714        MailMessage mailMessage = new MailMessage(
1715            from, to, subject, body, true);
1716
1717        mailService.sendEmail(mailMessage);
1718    }
1719
1720    protected void validate(String subject, String body)
1721        throws PortalException {
1722
1723        if (Validator.isNull(subject)) {
1724            throw new MessageSubjectException();
1725        }
1726
1727        if (Validator.isNull(body)) {
1728            throw new MessageBodyException();
1729        }
1730    }
1731
1732    private static Log _log =
1733        LogFactoryUtil.getLog(MBMessageLocalServiceImpl.class);
1734
1735}