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