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