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