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