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