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