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