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