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