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