001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.messageboards.service.impl;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.json.JSONFactoryUtil;
021    import com.liferay.portal.kernel.json.JSONObject;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.parsers.bbcode.BBCodeTranslatorUtil;
025    import com.liferay.portal.kernel.repository.model.FileEntry;
026    import com.liferay.portal.kernel.repository.model.Folder;
027    import com.liferay.portal.kernel.sanitizer.SanitizerUtil;
028    import com.liferay.portal.kernel.search.Indexable;
029    import com.liferay.portal.kernel.search.IndexableType;
030    import com.liferay.portal.kernel.search.Indexer;
031    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
032    import com.liferay.portal.kernel.util.Constants;
033    import com.liferay.portal.kernel.util.ContentTypes;
034    import com.liferay.portal.kernel.util.GetterUtil;
035    import com.liferay.portal.kernel.util.ListUtil;
036    import com.liferay.portal.kernel.util.ObjectValuePair;
037    import com.liferay.portal.kernel.util.OrderByComparator;
038    import com.liferay.portal.kernel.util.ParamUtil;
039    import com.liferay.portal.kernel.util.PropsKeys;
040    import com.liferay.portal.kernel.util.StringPool;
041    import com.liferay.portal.kernel.util.StringUtil;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.kernel.util.WebKeys;
044    import com.liferay.portal.kernel.workflow.WorkflowConstants;
045    import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
046    import com.liferay.portal.kernel.workflow.WorkflowThreadLocal;
047    import com.liferay.portal.model.Company;
048    import com.liferay.portal.model.Group;
049    import com.liferay.portal.model.ModelHintsUtil;
050    import com.liferay.portal.model.ResourceConstants;
051    import com.liferay.portal.model.User;
052    import com.liferay.portal.portletfilerepository.PortletFileRepositoryUtil;
053    import com.liferay.portal.security.auth.PrincipalException;
054    import com.liferay.portal.service.ServiceContext;
055    import com.liferay.portal.service.ServiceContextUtil;
056    import com.liferay.portal.theme.ThemeDisplay;
057    import com.liferay.portal.util.Portal;
058    import com.liferay.portal.util.PortalUtil;
059    import com.liferay.portal.util.PortletKeys;
060    import com.liferay.portal.util.PrefsPropsUtil;
061    import com.liferay.portal.util.PropsValues;
062    import com.liferay.portal.util.SubscriptionSender;
063    import com.liferay.portlet.PortletURLFactoryUtil;
064    import com.liferay.portlet.asset.model.AssetEntry;
065    import com.liferay.portlet.asset.model.AssetLinkConstants;
066    import com.liferay.portlet.blogs.model.BlogsEntry;
067    import com.liferay.portlet.blogs.util.LinkbackProducerUtil;
068    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
069    import com.liferay.portlet.messageboards.MessageBodyException;
070    import com.liferay.portlet.messageboards.MessageSubjectException;
071    import com.liferay.portlet.messageboards.NoSuchDiscussionException;
072    import com.liferay.portlet.messageboards.NoSuchThreadException;
073    import com.liferay.portlet.messageboards.RequiredMessageException;
074    import com.liferay.portlet.messageboards.model.MBCategory;
075    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
076    import com.liferay.portlet.messageboards.model.MBDiscussion;
077    import com.liferay.portlet.messageboards.model.MBMessage;
078    import com.liferay.portlet.messageboards.model.MBMessageConstants;
079    import com.liferay.portlet.messageboards.model.MBMessageDisplay;
080    import com.liferay.portlet.messageboards.model.MBThread;
081    import com.liferay.portlet.messageboards.model.MBThreadConstants;
082    import com.liferay.portlet.messageboards.model.impl.MBCategoryImpl;
083    import com.liferay.portlet.messageboards.model.impl.MBMessageDisplayImpl;
084    import com.liferay.portlet.messageboards.service.base.MBMessageLocalServiceBaseImpl;
085    import com.liferay.portlet.messageboards.social.MBActivityKeys;
086    import com.liferay.portlet.messageboards.util.MBSubscriptionSender;
087    import com.liferay.portlet.messageboards.util.MBUtil;
088    import com.liferay.portlet.messageboards.util.MailingListThreadLocal;
089    import com.liferay.portlet.messageboards.util.comparator.MessageCreateDateComparator;
090    import com.liferay.portlet.messageboards.util.comparator.MessageThreadComparator;
091    import com.liferay.portlet.messageboards.util.comparator.ThreadLastPostDateComparator;
092    import com.liferay.portlet.social.model.SocialActivity;
093    import com.liferay.portlet.social.model.SocialActivityConstants;
094    import com.liferay.portlet.trash.util.TrashUtil;
095    import com.liferay.util.SerializableUtil;
096    
097    import java.io.InputStream;
098    
099    import java.util.ArrayList;
100    import java.util.Collections;
101    import java.util.Comparator;
102    import java.util.Date;
103    import java.util.List;
104    
105    import javax.portlet.PortletPreferences;
106    import javax.portlet.PortletRequest;
107    import javax.portlet.PortletURL;
108    
109    import javax.servlet.http.HttpServletRequest;
110    
111    import net.htmlparser.jericho.Source;
112    import net.htmlparser.jericho.StartTag;
113    
114    /**
115     * @author Brian Wing Shun Chan
116     * @author Raymond Aug??
117     * @author Mika Koivisto
118     * @author Jorge Ferrer
119     * @author Juan Fern??ndez
120     * @author Shuyang Zhou
121     */
122    public class MBMessageLocalServiceImpl extends MBMessageLocalServiceBaseImpl {
123    
124            @Override
125            public MBMessage addDiscussionMessage(
126                            long userId, String userName, long groupId, String className,
127                            long classPK, int workflowAction)
128                    throws PortalException, SystemException {
129    
130                    long threadId = 0;
131                    long parentMessageId = MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID;
132                    String subject = String.valueOf(classPK);
133                    String body = subject;
134    
135                    ServiceContext serviceContext = new ServiceContext();
136    
137                    serviceContext.setWorkflowAction(workflowAction);
138    
139                    boolean workflowEnabled = WorkflowThreadLocal.isEnabled();
140    
141                    WorkflowThreadLocal.setEnabled(false);
142    
143                    try {
144                            return addDiscussionMessage(
145                                    userId, userName, groupId, className, classPK, threadId,
146                                    parentMessageId, subject, body, serviceContext);
147                    }
148                    finally {
149                            WorkflowThreadLocal.setEnabled(workflowEnabled);
150                    }
151            }
152    
153            @Override
154            public MBMessage addDiscussionMessage(
155                            long userId, String userName, long groupId, String className,
156                            long classPK, long threadId, long parentMessageId, String subject,
157                            String body, ServiceContext serviceContext)
158                    throws PortalException, SystemException {
159    
160                    // Message
161    
162                    long categoryId = MBCategoryConstants.DISCUSSION_CATEGORY_ID;
163    
164                    if (Validator.isNull(subject)) {
165                            if (Validator.isNotNull(body)) {
166                                    int pos = Math.min(body.length(), 50);
167    
168                                    subject = body.substring(0, pos) + "...";
169                            }
170                            else {
171                                    throw new MessageBodyException();
172                            }
173                    }
174    
175                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
176                            Collections.emptyList();
177                    boolean anonymous = false;
178                    double priority = 0.0;
179                    boolean allowPingbacks = false;
180    
181                    serviceContext.setAddGroupPermissions(true);
182                    serviceContext.setAddGuestPermissions(true);
183                    serviceContext.setAttribute("className", className);
184                    serviceContext.setAttribute("classPK", String.valueOf(classPK));
185    
186                    MBMessage message = addMessage(
187                            userId, userName, groupId, categoryId, threadId, parentMessageId,
188                            subject, body, MBMessageConstants.DEFAULT_FORMAT, inputStreamOVPs,
189                            anonymous, priority, allowPingbacks, serviceContext);
190    
191                    // Discussion
192    
193                    if (parentMessageId == MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
194                            long classNameId = PortalUtil.getClassNameId(className);
195    
196                            MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
197                                    classNameId, classPK);
198    
199                            if (discussion == null) {
200                                    mbDiscussionLocalService.addDiscussion(
201                                            userId, classNameId, classPK, message.getThreadId(),
202                                            serviceContext);
203                            }
204                    }
205    
206                    return message;
207            }
208    
209            @Override
210            public MBMessage addMessage(
211                            long userId, String userName, long groupId, long categoryId,
212                            long threadId, long parentMessageId, String subject, String body,
213                            String format,
214                            List<ObjectValuePair<String, InputStream>> inputStreamOVPs,
215                            boolean anonymous, double priority, boolean allowPingbacks,
216                            ServiceContext serviceContext)
217                    throws PortalException, SystemException {
218    
219                    // Message
220    
221                    User user = userPersistence.findByPrimaryKey(userId);
222                    userName = user.isDefaultUser() ? userName : user.getFullName();
223                    subject = ModelHintsUtil.trimString(
224                            MBMessage.class.getName(), "subject", subject);
225    
226                    PortletPreferences preferences =
227                            ServiceContextUtil.getPortletPreferences(serviceContext);
228    
229                    if (preferences != null) {
230                            if (!MBUtil.isAllowAnonymousPosting(preferences)) {
231                                    if (anonymous || user.isDefaultUser()) {
232                                            throw new PrincipalException();
233                                    }
234                            }
235                    }
236    
237                    if (user.isDefaultUser()) {
238                            anonymous = true;
239                    }
240    
241                    Date now = new Date();
242    
243                    long messageId = counterLocalService.increment();
244    
245                    body = SanitizerUtil.sanitize(
246                            user.getCompanyId(), groupId, userId, MBMessage.class.getName(),
247                            messageId, "text/" + format, body);
248    
249                    validate(subject, body);
250    
251                    subject = getSubject(subject, body);
252                    body = getBody(subject, body);
253    
254                    MBMessage message = mbMessagePersistence.create(messageId);
255    
256                    message.setUuid(serviceContext.getUuid());
257                    message.setGroupId(groupId);
258                    message.setCompanyId(user.getCompanyId());
259                    message.setUserId(user.getUserId());
260                    message.setUserName(userName);
261                    message.setCreateDate(serviceContext.getCreateDate(now));
262                    message.setModifiedDate(serviceContext.getModifiedDate(now));
263    
264                    if (threadId > 0) {
265                            message.setThreadId(threadId);
266                    }
267    
268                    if (priority != MBThreadConstants.PRIORITY_NOT_GIVEN) {
269                            message.setPriority(priority);
270                    }
271    
272                    message.setAllowPingbacks(allowPingbacks);
273                    message.setStatus(WorkflowConstants.STATUS_DRAFT);
274                    message.setStatusByUserId(user.getUserId());
275                    message.setStatusByUserName(userName);
276                    message.setStatusDate(serviceContext.getModifiedDate(now));
277    
278                    // Thread
279    
280                    if (parentMessageId != MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
281                            MBMessage parentMessage = mbMessagePersistence.fetchByPrimaryKey(
282                                    parentMessageId);
283    
284                            if (parentMessage == null) {
285                                    parentMessageId = MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID;
286                            }
287                    }
288    
289                    MBThread thread = null;
290    
291                    if (threadId > 0) {
292                            thread = mbThreadPersistence.fetchByPrimaryKey(threadId);
293                    }
294    
295                    if (thread == null) {
296                            if (parentMessageId ==
297                                            MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
298    
299                                    thread = mbThreadLocalService.addThread(
300                                            categoryId, message, serviceContext);
301                            }
302                            else {
303                                    throw new NoSuchThreadException("{threadId=" + threadId + "}");
304                            }
305                    }
306    
307                    if ((priority != MBThreadConstants.PRIORITY_NOT_GIVEN) &&
308                            (thread.getPriority() != priority)) {
309    
310                            thread.setPriority(priority);
311    
312                            mbThreadPersistence.update(thread);
313    
314                            updatePriorities(thread.getThreadId(), priority);
315                    }
316    
317                    // Message
318    
319                    message.setCategoryId(categoryId);
320                    message.setThreadId(thread.getThreadId());
321                    message.setRootMessageId(thread.getRootMessageId());
322                    message.setParentMessageId(parentMessageId);
323                    message.setSubject(subject);
324                    message.setBody(body);
325                    message.setFormat(format);
326                    message.setAnonymous(anonymous);
327    
328                    if (message.isDiscussion()) {
329                            long classNameId = PortalUtil.getClassNameId(
330                                    (String)serviceContext.getAttribute("className"));
331                            long classPK = ParamUtil.getLong(serviceContext, "classPK");
332    
333                            message.setClassNameId(classNameId);
334                            message.setClassPK(classPK);
335                    }
336    
337                    message.setExpandoBridgeAttributes(serviceContext);
338    
339                    mbMessagePersistence.update(message);
340    
341                    // Attachments
342    
343                    if (!inputStreamOVPs.isEmpty()) {
344                            Folder folder = message.addAttachmentsFolder();
345    
346                            PortletFileRepositoryUtil.addPortletFileEntries(
347                                    message.getGroupId(), userId, MBMessage.class.getName(),
348                                    message.getMessageId(), PortletKeys.MESSAGE_BOARDS,
349                                    folder.getFolderId(), inputStreamOVPs);
350                    }
351    
352                    // Resources
353    
354                    if ((parentMessageId !=
355                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) &&
356                            GetterUtil.getBoolean(
357                                    serviceContext.getAttribute("propagatePermissions"))) {
358    
359                            MBUtil.propagatePermissions(
360                                    message.getCompanyId(), groupId, parentMessageId,
361                                    serviceContext);
362                    }
363    
364                    if (!message.isDiscussion()) {
365                            if (user.isDefaultUser()) {
366                                    addMessageResources(message, true, true);
367                            }
368                            else if (serviceContext.isAddGroupPermissions() ||
369                                             serviceContext.isAddGuestPermissions()) {
370    
371                                    addMessageResources(
372                                            message, serviceContext.isAddGroupPermissions(),
373                                            serviceContext.isAddGuestPermissions());
374                            }
375                            else {
376                                    addMessageResources(
377                                            message, serviceContext.getGroupPermissions(),
378                                            serviceContext.getGuestPermissions());
379                            }
380                    }
381    
382                    // Asset
383    
384                    updateAsset(
385                            userId, message, serviceContext.getAssetCategoryIds(),
386                            serviceContext.getAssetTagNames(),
387                            serviceContext.getAssetLinkEntryIds(),
388                            serviceContext.isAssetEntryVisible());
389    
390                    // Workflow
391    
392                    WorkflowHandlerRegistryUtil.startWorkflowInstance(
393                            user.getCompanyId(), groupId, userId,
394                            message.getWorkflowClassName(), message.getMessageId(), message,
395                            serviceContext);
396    
397                    return message;
398            }
399    
400            @Override
401            public MBMessage addMessage(
402                            long userId, String userName, long groupId, long categoryId,
403                            String subject, String body, String format,
404                            List<ObjectValuePair<String, InputStream>> inputStreamOVPs,
405                            boolean anonymous, double priority, boolean allowPingbacks,
406                            ServiceContext serviceContext)
407                    throws PortalException, SystemException {
408    
409                    long threadId = 0;
410                    long parentMessageId = 0;
411    
412                    return addMessage(
413                            userId, userName, groupId, categoryId, threadId, parentMessageId,
414                            subject, body, format, inputStreamOVPs, anonymous, priority,
415                            allowPingbacks, serviceContext);
416            }
417    
418            @Override
419            public MBMessage addMessage(
420                            long userId, String userName, long categoryId, String subject,
421                            String body, ServiceContext serviceContext)
422                    throws PortalException, SystemException {
423    
424                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
425                            categoryId);
426    
427                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
428                            Collections.emptyList();
429    
430                    return addMessage(
431                            userId, userName, category.getGroupId(), categoryId, 0, 0, subject,
432                            body, MBMessageConstants.DEFAULT_FORMAT, inputStreamOVPs, false,
433                            0.0, false, serviceContext);
434            }
435    
436            @Override
437            public void addMessageResources(
438                            long messageId, boolean addGroupPermissions,
439                            boolean addGuestPermissions)
440                    throws PortalException, SystemException {
441    
442                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
443    
444                    addMessageResources(message, addGroupPermissions, addGuestPermissions);
445            }
446    
447            @Override
448            public void addMessageResources(
449                            long messageId, String[] groupPermissions,
450                            String[] guestPermissions)
451                    throws PortalException, SystemException {
452    
453                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
454    
455                    addMessageResources(message, groupPermissions, guestPermissions);
456            }
457    
458            @Override
459            public void addMessageResources(
460                            MBMessage message, boolean addGroupPermissions,
461                            boolean addGuestPermissions)
462                    throws PortalException, SystemException {
463    
464                    resourceLocalService.addResources(
465                            message.getCompanyId(), message.getGroupId(), message.getUserId(),
466                            MBMessage.class.getName(), message.getMessageId(), false,
467                            addGroupPermissions, addGuestPermissions);
468            }
469    
470            @Override
471            public void addMessageResources(
472                            MBMessage message, String[] groupPermissions,
473                            String[] guestPermissions)
474                    throws PortalException, SystemException {
475    
476                    resourceLocalService.addModelResources(
477                            message.getCompanyId(), message.getGroupId(), message.getUserId(),
478                            MBMessage.class.getName(), message.getMessageId(), groupPermissions,
479                            guestPermissions);
480            }
481    
482            @Indexable(type = IndexableType.DELETE)
483            @Override
484            public MBMessage deleteDiscussionMessage(long messageId)
485                    throws PortalException, SystemException {
486    
487                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
488    
489                    deleteDiscussionSocialActivities(BlogsEntry.class.getName(), message);
490    
491                    return deleteMessage(message);
492            }
493    
494            @Override
495            public void deleteDiscussionMessages(String className, long classPK)
496                    throws PortalException, SystemException {
497    
498                    try {
499                            long classNameId = PortalUtil.getClassNameId(className);
500    
501                            MBDiscussion discussion = mbDiscussionPersistence.findByC_C(
502                                    classNameId, classPK);
503    
504                            List<MBMessage> messages = mbMessagePersistence.findByT_P(
505                                    discussion.getThreadId(),
506                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID, 0, 1);
507    
508                            if (!messages.isEmpty()) {
509                                    MBMessage message = messages.get(0);
510    
511                                    deleteDiscussionSocialActivities(
512                                            BlogsEntry.class.getName(), message);
513    
514                                    mbThreadLocalService.deleteThread(message.getThreadId());
515                            }
516    
517                            mbDiscussionPersistence.remove(discussion);
518                    }
519                    catch (NoSuchDiscussionException nsde) {
520                            if (_log.isDebugEnabled()) {
521                                    _log.debug(nsde.getMessage());
522                            }
523                    }
524            }
525    
526            @Indexable(type = IndexableType.DELETE)
527            @Override
528            public MBMessage deleteMessage(long messageId)
529                    throws PortalException, SystemException {
530    
531                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
532    
533                    return deleteMessage(message);
534            }
535    
536            @Indexable(type = IndexableType.DELETE)
537            @Override
538            public MBMessage deleteMessage(MBMessage message)
539                    throws PortalException, SystemException {
540    
541                    // Attachments
542    
543                    long folderId = message.getAttachmentsFolderId();
544    
545                    if (folderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
546                            PortletFileRepositoryUtil.deleteFolder(folderId);
547                    }
548    
549                    // Thread
550    
551                    int count = mbMessagePersistence.countByThreadId(message.getThreadId());
552    
553                    if (count == 1) {
554    
555                            // Attachments
556    
557                            long threadAttachmentsFolderId =
558                                    message.getThreadAttachmentsFolderId();
559    
560                            if (threadAttachmentsFolderId !=
561                                            DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
562    
563                                    PortletFileRepositoryUtil.deleteFolder(
564                                            threadAttachmentsFolderId);
565                            }
566    
567                            // Subscriptions
568    
569                            subscriptionLocalService.deleteSubscriptions(
570                                    message.getCompanyId(), MBThread.class.getName(),
571                                    message.getThreadId());
572    
573                            // Thread
574    
575                            MBThread thread = mbThreadPersistence.findByPrimaryKey(
576                                    message.getThreadId());
577    
578                            mbThreadPersistence.remove(thread);
579    
580                            // Category
581    
582                            if ((message.getCategoryId() !=
583                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
584                                    (message.getCategoryId() !=
585                                            MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
586    
587                                    MBUtil.updateCategoryStatistics(
588                                            message.getCompanyId(), message.getCategoryId());
589                            }
590    
591                            // Indexer
592    
593                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
594                                    MBThread.class);
595    
596                            indexer.delete(thread);
597                    }
598                    else {
599                            MBThread thread = mbThreadPersistence.findByPrimaryKey(
600                                    message.getThreadId());
601    
602                            // Message is a root message
603    
604                            if (thread.getRootMessageId() == message.getMessageId()) {
605                                    List<MBMessage> childrenMessages =
606                                            mbMessagePersistence.findByT_P(
607                                                    message.getThreadId(), message.getMessageId());
608    
609                                    if (childrenMessages.size() > 1) {
610                                            throw new RequiredMessageException(
611                                                    String.valueOf(message.getMessageId()));
612                                    }
613                                    else if (childrenMessages.size() == 1) {
614                                            MBMessage childMessage = childrenMessages.get(0);
615    
616                                            childMessage.setRootMessageId(childMessage.getMessageId());
617                                            childMessage.setParentMessageId(
618                                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID);
619    
620                                            mbMessagePersistence.update(childMessage);
621    
622                                            List<MBMessage> repliesMessages =
623                                                    mbMessagePersistence.findByThreadReplies(
624                                                            message.getThreadId());
625    
626                                            for (MBMessage repliesMessage : repliesMessages) {
627                                                    repliesMessage.setRootMessageId(
628                                                            childMessage.getMessageId());
629    
630                                                    mbMessagePersistence.update(repliesMessage);
631                                            }
632    
633                                            thread.setRootMessageId(childMessage.getMessageId());
634                                            thread.setRootMessageUserId(childMessage.getUserId());
635    
636                                            mbThreadPersistence.update(thread);
637                                    }
638                            }
639    
640                            // Message is a child message
641    
642                            else {
643                                    List<MBMessage> childrenMessages =
644                                            mbMessagePersistence.findByT_P(
645                                                    message.getThreadId(), message.getMessageId());
646    
647                                    // Message has children messages
648    
649                                    if (!childrenMessages.isEmpty()) {
650                                            for (MBMessage childMessage : childrenMessages) {
651                                                    childMessage.setParentMessageId(
652                                                            message.getParentMessageId());
653    
654                                                    mbMessagePersistence.update(childMessage);
655                                            }
656                                    }
657                                    else {
658                                            MessageCreateDateComparator comparator =
659                                                    new MessageCreateDateComparator(true);
660    
661                                            MBMessage lastMessage =
662                                                    mbMessagePersistence.fetchByT_S_Last(
663                                                            thread.getThreadId(),
664                                                            WorkflowConstants.STATUS_APPROVED, comparator);
665    
666                                            if ((lastMessage != null) &&
667                                                    (message.getMessageId() ==
668                                                            lastMessage.getMessageId())) {
669    
670                                                    MBMessage parentMessage =
671                                                            mbMessagePersistence.findByPrimaryKey(
672                                                                    message.getParentMessageId());
673    
674                                                    thread.setLastPostByUserId(parentMessage.getUserId());
675                                                    thread.setLastPostDate(parentMessage.getModifiedDate());
676    
677                                                    mbThreadPersistence.update(thread);
678                                            }
679                                    }
680                            }
681    
682                            // Thread
683    
684                            if (message.isApproved()) {
685                                    MBUtil.updateThreadMessageCount(
686                                            thread.getCompanyId(), thread.getThreadId());
687                            }
688    
689                            // Category
690    
691                            if ((message.getCategoryId() !=
692                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
693                                    (message.getCategoryId() !=
694                                            MBCategoryConstants.DISCUSSION_CATEGORY_ID) &&
695                                    !message.isDraft()) {
696    
697                                    MBUtil.updateCategoryMessageCount(
698                                            message.getCompanyId(), message.getCategoryId());
699                            }
700    
701                            // Indexer
702    
703                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
704                                    MBThread.class);
705    
706                            indexer.reindex(thread);
707                    }
708    
709                    // Asset
710    
711                    assetEntryLocalService.deleteEntry(
712                            message.getWorkflowClassName(), message.getMessageId());
713    
714                    // Expando
715    
716                    expandoRowLocalService.deleteRows(message.getMessageId());
717    
718                    // Ratings
719    
720                    ratingsStatsLocalService.deleteStats(
721                            message.getWorkflowClassName(), message.getMessageId());
722    
723                    // Resources
724    
725                    if (!message.isDiscussion()) {
726                            resourceLocalService.deleteResource(
727                                    message.getCompanyId(), message.getWorkflowClassName(),
728                                    ResourceConstants.SCOPE_INDIVIDUAL, message.getMessageId());
729                    }
730    
731                    // Message
732    
733                    mbMessagePersistence.remove(message);
734    
735                    // Statistics
736    
737                    if (!message.isDiscussion()) {
738                            mbStatsUserLocalService.updateStatsUser(
739                                    message.getGroupId(), message.getUserId());
740                    }
741    
742                    // Workflow
743    
744                    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(
745                            message.getCompanyId(), message.getGroupId(),
746                            message.getWorkflowClassName(), message.getMessageId());
747    
748                    return message;
749            }
750    
751            @Override
752            public void deleteMessageAttachment(long messageId, String fileName)
753                    throws PortalException, SystemException {
754    
755                    MBMessage message = getMessage(messageId);
756    
757                    long folderId = message.getAttachmentsFolderId();
758    
759                    if (folderId == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
760                            return;
761                    }
762    
763                    PortletFileRepositoryUtil.deletePortletFileEntry(
764                            message.getGroupId(), folderId, fileName);
765            }
766    
767            @Override
768            public void deleteMessageAttachments(long messageId)
769                    throws PortalException, SystemException {
770    
771                    MBMessage message = getMessage(messageId);
772    
773                    long folderId = message.getAttachmentsFolderId();
774    
775                    if (folderId == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
776                            return;
777                    }
778    
779                    PortletFileRepositoryUtil.deletePortletFileEntries(
780                            message.getGroupId(), folderId);
781            }
782    
783            @Override
784            public List<MBMessage> getCategoryMessages(
785                            long groupId, long categoryId, int status, int start, int end)
786                    throws SystemException {
787    
788                    if (status == WorkflowConstants.STATUS_ANY) {
789                            return mbMessagePersistence.findByG_C(
790                                    groupId, categoryId, start, end);
791                    }
792                    else {
793                            return mbMessagePersistence.findByG_C_S(
794                                    groupId, categoryId, status, start, end);
795                    }
796            }
797    
798            @Override
799            public List<MBMessage> getCategoryMessages(
800                            long groupId, long categoryId, int status, int start, int end,
801                            OrderByComparator obc)
802                    throws SystemException {
803    
804                    if (status == WorkflowConstants.STATUS_ANY) {
805                            return mbMessagePersistence.findByG_C(
806                                    groupId, categoryId, start, end, obc);
807                    }
808                    else {
809                            return mbMessagePersistence.findByG_C_S(
810                                    groupId, categoryId, status, start, end, obc);
811                    }
812            }
813    
814            @Override
815            public int getCategoryMessagesCount(
816                            long groupId, long categoryId, int status)
817                    throws SystemException {
818    
819                    if (status == WorkflowConstants.STATUS_ANY) {
820                            return mbMessagePersistence.countByG_C(groupId, categoryId);
821                    }
822                    else {
823                            return mbMessagePersistence.countByG_C_S(
824                                    groupId, categoryId, status);
825                    }
826            }
827    
828            @Override
829            public List<MBMessage> getCompanyMessages(
830                            long companyId, int status, int start, int end)
831                    throws SystemException {
832    
833                    if (status == WorkflowConstants.STATUS_ANY) {
834                            return mbMessagePersistence.findByCompanyId(companyId, start, end);
835                    }
836                    else {
837                            return mbMessagePersistence.findByC_S(
838                                    companyId, status, start, end);
839                    }
840            }
841    
842            @Override
843            public List<MBMessage> getCompanyMessages(
844                            long companyId, int status, int start, int end,
845                            OrderByComparator obc)
846                    throws SystemException {
847    
848                    if (status == WorkflowConstants.STATUS_ANY) {
849                            return mbMessagePersistence.findByCompanyId(
850                                    companyId, start, end, obc);
851                    }
852                    else {
853                            return mbMessagePersistence.findByC_S(
854                                    companyId, status, start, end, obc);
855                    }
856            }
857    
858            @Override
859            public int getCompanyMessagesCount(long companyId, int status)
860                    throws SystemException {
861    
862                    if (status == WorkflowConstants.STATUS_ANY) {
863                            return mbMessagePersistence.countByCompanyId(companyId);
864                    }
865                    else {
866                            return mbMessagePersistence.countByC_S(companyId, status);
867                    }
868            }
869    
870            @Override
871            public MBMessageDisplay getDiscussionMessageDisplay(
872                            long userId, long groupId, String className, long classPK,
873                            int status)
874                    throws PortalException, SystemException {
875    
876                    return getDiscussionMessageDisplay(
877                            userId, groupId, className, classPK, status,
878                            MBThreadConstants.THREAD_VIEW_COMBINATION);
879            }
880    
881            @Override
882            public MBMessageDisplay getDiscussionMessageDisplay(
883                            long userId, long groupId, String className, long classPK,
884                            int status, String threadView)
885                    throws PortalException, SystemException {
886    
887                    long classNameId = PortalUtil.getClassNameId(className);
888    
889                    MBMessage message = null;
890    
891                    MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
892                            classNameId, classPK);
893    
894                    if (discussion != null) {
895                            List<MBMessage> messages = mbMessagePersistence.findByT_P(
896                                    discussion.getThreadId(),
897                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID);
898    
899                            message = messages.get(0);
900                    }
901                    else {
902                            boolean workflowEnabled = WorkflowThreadLocal.isEnabled();
903    
904                            WorkflowThreadLocal.setEnabled(false);
905    
906                            try {
907                                    String subject = String.valueOf(classPK);
908                                    //String body = subject;
909    
910                                    message = addDiscussionMessage(
911                                            userId, null, groupId, className, classPK, 0,
912                                            MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID, subject,
913                                            subject, new ServiceContext());
914                            }
915                            catch (SystemException se) {
916                                    if (_log.isWarnEnabled()) {
917                                            _log.warn(
918                                                    "Add failed, fetch {threadId=0, parentMessageId=" +
919                                                            MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID + "}");
920                                    }
921    
922                                    List<MBMessage> messages = mbMessagePersistence.findByT_P(
923                                            0, MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID);
924    
925                                    if (messages.isEmpty()) {
926                                            throw se;
927                                    }
928    
929                                    message = messages.get(0);
930                            }
931                            finally {
932                                    WorkflowThreadLocal.setEnabled(workflowEnabled);
933                            }
934                    }
935    
936                    return getMessageDisplay(userId, message, status, threadView, false);
937            }
938    
939            @Override
940            public int getDiscussionMessagesCount(
941                            long classNameId, long classPK, int status)
942                    throws SystemException {
943    
944                    MBDiscussion discussion = mbDiscussionPersistence.fetchByC_C(
945                            classNameId, classPK);
946    
947                    if (discussion == null) {
948                            return 0;
949                    }
950    
951                    int count = 0;
952    
953                    if (status == WorkflowConstants.STATUS_ANY) {
954                            count = mbMessagePersistence.countByThreadId(
955                                    discussion.getThreadId());
956                    }
957                    else {
958                            count = mbMessagePersistence.countByT_S(
959                                    discussion.getThreadId(), status);
960                    }
961    
962                    if (count >= 1) {
963                            return count - 1;
964                    }
965                    else {
966                            return 0;
967                    }
968            }
969    
970            @Override
971            public int getDiscussionMessagesCount(
972                            String className, long classPK, int status)
973                    throws SystemException {
974    
975                    long classNameId = PortalUtil.getClassNameId(className);
976    
977                    return getDiscussionMessagesCount(classNameId, classPK, status);
978            }
979    
980            @Override
981            public List<MBDiscussion> getDiscussions(String className)
982                    throws SystemException {
983    
984                    long classNameId = PortalUtil.getClassNameId(className);
985    
986                    return mbDiscussionPersistence.findByClassNameId(classNameId);
987            }
988    
989            @Override
990            public List<MBMessage> getGroupMessages(
991                            long groupId, int status, int start, int end)
992                    throws SystemException {
993    
994                    if (status == WorkflowConstants.STATUS_ANY) {
995                            return mbMessagePersistence.findByGroupId(groupId, start, end);
996                    }
997                    else {
998                            return mbMessagePersistence.findByG_S(groupId, status, start, end);
999                    }
1000            }
1001    
1002            @Override
1003            public List<MBMessage> getGroupMessages(
1004                            long groupId, int status, int start, int end, OrderByComparator obc)
1005                    throws SystemException {
1006    
1007                    if (status == WorkflowConstants.STATUS_ANY) {
1008                            return mbMessagePersistence.findByGroupId(groupId, start, end, obc);
1009                    }
1010                    else {
1011                            return mbMessagePersistence.findByG_S(
1012                                    groupId, status, start, end, obc);
1013                    }
1014            }
1015    
1016            @Override
1017            public List<MBMessage> getGroupMessages(
1018                            long groupId, long userId, int status, int start, int end)
1019                    throws SystemException {
1020    
1021                    if (status == WorkflowConstants.STATUS_ANY) {
1022                            return mbMessagePersistence.findByG_U(groupId, userId, start, end);
1023                    }
1024                    else {
1025                            return mbMessagePersistence.findByG_U_S(
1026                                    groupId, userId, status, start, end);
1027                    }
1028            }
1029    
1030            @Override
1031            public List<MBMessage> getGroupMessages(
1032                            long groupId, long userId, int status, int start, int end,
1033                            OrderByComparator obc)
1034                    throws SystemException {
1035    
1036                    if (status == WorkflowConstants.STATUS_ANY) {
1037                            return mbMessagePersistence.findByG_U(
1038                                    groupId, userId, start, end, obc);
1039                    }
1040                    else {
1041                            return mbMessagePersistence.findByG_U_S(
1042                                    groupId, userId, status, start, end, obc);
1043                    }
1044            }
1045    
1046            @Override
1047            public int getGroupMessagesCount(long groupId, int status)
1048                    throws SystemException {
1049    
1050                    if (status == WorkflowConstants.STATUS_ANY) {
1051                            return mbMessagePersistence.countByGroupId(groupId);
1052                    }
1053                    else {
1054                            return mbMessagePersistence.countByG_S(groupId, status);
1055                    }
1056            }
1057    
1058            @Override
1059            public int getGroupMessagesCount(long groupId, long userId, int status)
1060                    throws SystemException {
1061    
1062                    if (status == WorkflowConstants.STATUS_ANY) {
1063                            return mbMessagePersistence.countByG_U(groupId, userId);
1064                    }
1065                    else {
1066                            return mbMessagePersistence.countByG_U_S(groupId, userId, status);
1067                    }
1068            }
1069    
1070            @Override
1071            public MBMessage getMessage(long messageId)
1072                    throws PortalException, SystemException {
1073    
1074                    return mbMessagePersistence.findByPrimaryKey(messageId);
1075            }
1076    
1077            @Override
1078            public MBMessageDisplay getMessageDisplay(
1079                            long userId, long messageId, int status, String threadView,
1080                            boolean includePrevAndNext)
1081                    throws PortalException, SystemException {
1082    
1083                    MBMessage message = getMessage(messageId);
1084    
1085                    return getMessageDisplay(
1086                            userId, message, status, threadView, includePrevAndNext);
1087            }
1088    
1089            @Override
1090            public MBMessageDisplay getMessageDisplay(
1091                            long userId, MBMessage message, int status, String threadView,
1092                            boolean includePrevAndNext)
1093                    throws PortalException, SystemException {
1094    
1095                    MBCategory category = null;
1096    
1097                    if ((message.getCategoryId() !=
1098                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
1099                            (message.getCategoryId() !=
1100                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
1101    
1102                            category = mbCategoryPersistence.findByPrimaryKey(
1103                                    message.getCategoryId());
1104                    }
1105                    else {
1106                            category = new MBCategoryImpl();
1107    
1108                            category.setCategoryId(message.getCategoryId());
1109                            category.setDisplayStyle(MBCategoryConstants.DEFAULT_DISPLAY_STYLE);
1110                    }
1111    
1112                    MBMessage parentMessage = null;
1113    
1114                    if (message.isReply()) {
1115                            parentMessage = mbMessagePersistence.findByPrimaryKey(
1116                                    message.getParentMessageId());
1117                    }
1118    
1119                    MBThread thread = mbThreadPersistence.findByPrimaryKey(
1120                            message.getThreadId());
1121    
1122                    if (message.isApproved() && !message.isDiscussion()) {
1123                            mbThreadLocalService.incrementViewCounter(thread.getThreadId(), 1);
1124    
1125                            if (thread.getRootMessageUserId() != userId) {
1126                                    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(
1127                                            thread.getRootMessageId());
1128    
1129                                    socialActivityLocalService.addActivity(
1130                                            userId, rootMessage.getGroupId(), MBMessage.class.getName(),
1131                                            rootMessage.getMessageId(),
1132                                            SocialActivityConstants.TYPE_VIEW, StringPool.BLANK, 0);
1133                            }
1134                    }
1135    
1136                    MBThread previousThread = null;
1137                    MBThread nextThread = null;
1138    
1139                    if (message.isApproved() && includePrevAndNext) {
1140                            ThreadLastPostDateComparator comparator =
1141                                    new ThreadLastPostDateComparator(false);
1142    
1143                            MBThread[] prevAndNextThreads =
1144                                    mbThreadPersistence.findByG_C_PrevAndNext(
1145                                            message.getThreadId(), message.getGroupId(),
1146                                            message.getCategoryId(), comparator);
1147    
1148                            previousThread = prevAndNextThreads[0];
1149                            nextThread = prevAndNextThreads[2];
1150                    }
1151    
1152                    return new MBMessageDisplayImpl(
1153                            message, parentMessage, category, thread, previousThread,
1154                            nextThread, status, threadView, this);
1155            }
1156    
1157            @Override
1158            public List<MBMessage> getMessages(
1159                            String className, long classPK, int status)
1160                    throws SystemException {
1161    
1162                    long classNameId = PortalUtil.getClassNameId(className);
1163    
1164                    if (status == WorkflowConstants.STATUS_ANY) {
1165                            return mbMessagePersistence.findByC_C(classNameId, classPK);
1166                    }
1167                    else {
1168                            return mbMessagePersistence.findByC_C_S(
1169                                    classNameId, classPK, status);
1170                    }
1171            }
1172    
1173            @Override
1174            public List<MBMessage> getNoAssetMessages() throws SystemException {
1175                    return mbMessageFinder.findByNoAssets();
1176            }
1177    
1178            @Override
1179            public int getPositionInThread(long messageId)
1180                    throws PortalException, SystemException {
1181    
1182                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1183    
1184                    return mbMessageFinder.countByC_T(
1185                            message.getCreateDate(), message.getThreadId());
1186            }
1187    
1188            @Override
1189            public List<MBMessage> getThreadMessages(long threadId, int status)
1190                    throws SystemException {
1191    
1192                    return getThreadMessages(
1193                            threadId, status, new MessageThreadComparator());
1194            }
1195    
1196            @Override
1197            public List<MBMessage> getThreadMessages(
1198                            long threadId, int status, Comparator<MBMessage> comparator)
1199                    throws SystemException {
1200    
1201                    List<MBMessage> messages = null;
1202    
1203                    if (status == WorkflowConstants.STATUS_ANY) {
1204                            messages = mbMessagePersistence.findByThreadId(threadId);
1205                    }
1206                    else {
1207                            messages = mbMessagePersistence.findByT_S(threadId, status);
1208                    }
1209    
1210                    return ListUtil.sort(messages, comparator);
1211            }
1212    
1213            @Override
1214            public List<MBMessage> getThreadMessages(
1215                            long threadId, int status, int start, int end)
1216                    throws SystemException {
1217    
1218                    if (status == WorkflowConstants.STATUS_ANY) {
1219                            return mbMessagePersistence.findByThreadId(threadId, start, end);
1220                    }
1221                    else {
1222                            return mbMessagePersistence.findByT_S(threadId, status, start, end);
1223                    }
1224            }
1225    
1226            @Override
1227            public int getThreadMessagesCount(long threadId, int status)
1228                    throws SystemException {
1229    
1230                    if (status == WorkflowConstants.STATUS_ANY) {
1231                            return mbMessagePersistence.countByThreadId(threadId);
1232                    }
1233                    else {
1234                            return mbMessagePersistence.countByT_S(threadId, status);
1235                    }
1236            }
1237    
1238            @Override
1239            public List<MBMessage> getThreadRepliesMessages(
1240                            long threadId, int status, int start, int end)
1241                    throws SystemException {
1242    
1243                    if (status == WorkflowConstants.STATUS_ANY) {
1244                            return mbMessagePersistence.findByThreadReplies(
1245                                    threadId, start, end);
1246                    }
1247                    else {
1248                            return mbMessagePersistence.findByTR_S(
1249                                    threadId, status, start, end);
1250                    }
1251            }
1252    
1253            @Override
1254            public List<MBMessage> getUserDiscussionMessages(
1255                            long userId, long classNameId, long classPK, int status, int start,
1256                            int end, OrderByComparator obc)
1257                    throws SystemException {
1258    
1259                    if (status == WorkflowConstants.STATUS_ANY) {
1260                            return mbMessagePersistence.findByU_C_C(
1261                                    userId, classNameId, classPK, start, end, obc);
1262                    }
1263                    else {
1264                            return mbMessagePersistence.findByU_C_C_S(
1265                                    userId, classNameId, classPK, status, start, end, obc);
1266                    }
1267            }
1268    
1269            @Override
1270            public List<MBMessage> getUserDiscussionMessages(
1271                            long userId, long[] classNameIds, int status, int start, int end,
1272                            OrderByComparator obc)
1273                    throws SystemException {
1274    
1275                    if (status == WorkflowConstants.STATUS_ANY) {
1276                            return mbMessagePersistence.findByU_C(
1277                                    userId, classNameIds, start, end, obc);
1278                    }
1279                    else {
1280                            return mbMessagePersistence.findByU_C_S(
1281                                    userId, classNameIds, status, start, end, obc);
1282                    }
1283            }
1284    
1285            @Override
1286            public List<MBMessage> getUserDiscussionMessages(
1287                            long userId, String className, long classPK, int status, int start,
1288                            int end, OrderByComparator obc)
1289                    throws SystemException {
1290    
1291                    long classNameId = PortalUtil.getClassNameId(className);
1292    
1293                    return getUserDiscussionMessages(
1294                            userId, classNameId, classPK, status, start, end, obc);
1295            }
1296    
1297            @Override
1298            public int getUserDiscussionMessagesCount(
1299                            long userId, long classNameId, long classPK, int status)
1300                    throws SystemException {
1301    
1302                    if (status == WorkflowConstants.STATUS_ANY) {
1303                            return mbMessagePersistence.countByU_C_C(
1304                                    userId, classNameId, classPK);
1305                    }
1306                    else {
1307                            return mbMessagePersistence.countByU_C_C_S(
1308                                    userId, classNameId, classPK, status);
1309                    }
1310            }
1311    
1312            @Override
1313            public int getUserDiscussionMessagesCount(
1314                            long userId, long[] classNameIds, int status)
1315                    throws SystemException {
1316    
1317                    if (status == WorkflowConstants.STATUS_ANY) {
1318                            return mbMessagePersistence.countByU_C(userId, classNameIds);
1319                    }
1320                    else {
1321                            return mbMessagePersistence.countByU_C_S(
1322                                    userId, classNameIds, status);
1323                    }
1324            }
1325    
1326            @Override
1327            public int getUserDiscussionMessagesCount(
1328                            long userId, String className, long classPK, int status)
1329                    throws SystemException {
1330    
1331                    long classNameId = PortalUtil.getClassNameId(className);
1332    
1333                    return getUserDiscussionMessagesCount(
1334                            userId, classNameId, classPK, status);
1335            }
1336    
1337            @Override
1338            public long moveMessageAttachmentToTrash(
1339                            long userId, long messageId, String fileName)
1340                    throws PortalException, SystemException {
1341    
1342                    MBMessage message = getMessage(messageId);
1343    
1344                    long folderId = message.getAttachmentsFolderId();
1345    
1346                    FileEntry fileEntry = PortletFileRepositoryUtil.getPortletFileEntry(
1347                            message.getGroupId(), folderId, fileName);
1348    
1349                    PortletFileRepositoryUtil.movePortletFileEntryToTrash(
1350                            userId, fileEntry.getFileEntryId());
1351    
1352                    return fileEntry.getFileEntryId();
1353            }
1354    
1355            @Override
1356            public void restoreMessageAttachmentFromTrash(
1357                            long userId, long messageId, String deletedFileName)
1358                    throws PortalException, SystemException {
1359    
1360                    MBMessage message = getMessage(messageId);
1361    
1362                    Folder folder = message.addAttachmentsFolder();
1363    
1364                    PortletFileRepositoryUtil.restorePortletFileEntryFromTrash(
1365                            message.getGroupId(), userId, folder.getFolderId(),
1366                            deletedFileName);
1367            }
1368    
1369            @Override
1370            public void subscribeMessage(long userId, long messageId)
1371                    throws PortalException, SystemException {
1372    
1373                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1374    
1375                    subscriptionLocalService.addSubscription(
1376                            userId, message.getGroupId(), MBThread.class.getName(),
1377                            message.getThreadId());
1378            }
1379    
1380            @Override
1381            public void unsubscribeMessage(long userId, long messageId)
1382                    throws PortalException, SystemException {
1383    
1384                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1385    
1386                    subscriptionLocalService.deleteSubscription(
1387                            userId, MBThread.class.getName(), message.getThreadId());
1388            }
1389    
1390            @Override
1391            public void updateAnswer(long messageId, boolean answer, boolean cascade)
1392                    throws PortalException, SystemException {
1393    
1394                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1395    
1396                    updateAnswer(message, answer, cascade);
1397            }
1398    
1399            @Override
1400            public void updateAnswer(MBMessage message, boolean answer, boolean cascade)
1401                    throws PortalException, SystemException {
1402    
1403                    if (message.isAnswer() != answer) {
1404                            message.setAnswer(answer);
1405    
1406                            mbMessagePersistence.update(message);
1407                    }
1408    
1409                    if (cascade) {
1410                            List<MBMessage> messages = mbMessagePersistence.findByT_P(
1411                                    message.getThreadId(), message.getMessageId());
1412    
1413                            for (MBMessage curMessage : messages) {
1414                                    updateAnswer(curMessage, answer, cascade);
1415                            }
1416                    }
1417            }
1418    
1419            @Override
1420            public void updateAsset(
1421                            long userId, MBMessage message, long[] assetCategoryIds,
1422                            String[] assetTagNames, long[] assetLinkEntryIds)
1423                    throws PortalException, SystemException {
1424    
1425                    updateAsset(
1426                            userId, message, assetCategoryIds, assetTagNames, assetLinkEntryIds,
1427                            true);
1428            }
1429    
1430            @Override
1431            public MBMessage updateDiscussionMessage(
1432                            long userId, long messageId, String className, long classPK,
1433                            String subject, String body, ServiceContext serviceContext)
1434                    throws PortalException, SystemException {
1435    
1436                    if (Validator.isNull(subject)) {
1437                            if (Validator.isNotNull(body)) {
1438                                    int pos = Math.min(body.length(), 50);
1439    
1440                                    subject = body.substring(0, pos) + "...";
1441                            }
1442                            else {
1443                                    throw new MessageBodyException();
1444                            }
1445                    }
1446    
1447                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
1448                            Collections.emptyList();
1449                    List<String> existingFiles = new ArrayList<String>();
1450                    double priority = 0.0;
1451                    boolean allowPingbacks = false;
1452    
1453                    serviceContext.setAttribute("className", className);
1454                    serviceContext.setAttribute("classPK", String.valueOf(classPK));
1455    
1456                    return updateMessage(
1457                            userId, messageId, subject, body, inputStreamOVPs, existingFiles,
1458                            priority, allowPingbacks, serviceContext);
1459            }
1460    
1461            @Override
1462            public MBMessage updateMessage(
1463                            long userId, long messageId, String subject, String body,
1464                            List<ObjectValuePair<String, InputStream>> inputStreamOVPs,
1465                            List<String> existingFiles, double priority, boolean allowPingbacks,
1466                            ServiceContext serviceContext)
1467                    throws PortalException, SystemException {
1468    
1469                    // Message
1470    
1471                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1472    
1473                    int oldStatus = message.getStatus();
1474    
1475                    Date modifiedDate = serviceContext.getModifiedDate(new Date());
1476                    subject = ModelHintsUtil.trimString(
1477                            MBMessage.class.getName(), "subject", subject);
1478                    body = SanitizerUtil.sanitize(
1479                            message.getCompanyId(), message.getGroupId(), userId,
1480                            MBMessage.class.getName(), messageId, "text/" + message.getFormat(),
1481                            body);
1482    
1483                    validate(subject, body);
1484    
1485                    subject = getSubject(subject, body);
1486                    body = getBody(subject, body);
1487    
1488                    message.setModifiedDate(modifiedDate);
1489                    message.setSubject(subject);
1490                    message.setBody(body);
1491                    message.setAllowPingbacks(allowPingbacks);
1492    
1493                    if (priority != MBThreadConstants.PRIORITY_NOT_GIVEN) {
1494                            message.setPriority(priority);
1495                    }
1496    
1497                    MBThread thread = mbThreadPersistence.findByPrimaryKey(
1498                            message.getThreadId());
1499    
1500                    if (serviceContext.getWorkflowAction() ==
1501                                    WorkflowConstants.ACTION_SAVE_DRAFT) {
1502    
1503                            if (!message.isDraft() && !message.isPending()) {
1504                                    message.setStatus(WorkflowConstants.STATUS_DRAFT);
1505    
1506                                    // Thread
1507    
1508                                    User user = userPersistence.findByPrimaryKey(userId);
1509    
1510                                    updateThreadStatus(
1511                                            thread, message, user, oldStatus, modifiedDate);
1512    
1513                                    // Asset
1514    
1515                                    assetEntryLocalService.updateVisible(
1516                                            message.getWorkflowClassName(), message.getMessageId(),
1517                                            false);
1518    
1519                                    if (!message.isDiscussion()) {
1520    
1521                                            // Indexer
1522    
1523                                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1524                                                    MBMessage.class);
1525    
1526                                            indexer.delete(message);
1527                                    }
1528                            }
1529                    }
1530    
1531                    // Attachments
1532    
1533                    if (!inputStreamOVPs.isEmpty() || !existingFiles.isEmpty()) {
1534                            List<FileEntry> fileEntries = message.getAttachmentsFileEntries();
1535    
1536                            for (FileEntry fileEntry : fileEntries) {
1537                                    String fileEntryId = String.valueOf(fileEntry.getFileEntryId());
1538    
1539                                    if (!existingFiles.contains(fileEntryId)) {
1540                                            if (!TrashUtil.isTrashEnabled(message.getGroupId())) {
1541                                                    deleteMessageAttachment(
1542                                                            messageId, fileEntry.getTitle());
1543                                            }
1544                                            else {
1545                                                    moveMessageAttachmentToTrash(
1546                                                            userId, messageId, fileEntry.getTitle());
1547                                            }
1548                                    }
1549                            }
1550    
1551                            Folder folder = message.addAttachmentsFolder();
1552    
1553                            PortletFileRepositoryUtil.addPortletFileEntries(
1554                                    message.getGroupId(), userId, MBMessage.class.getName(),
1555                                    message.getMessageId(), PortletKeys.MESSAGE_BOARDS,
1556                                    folder.getFolderId(), inputStreamOVPs);
1557                    }
1558                    else {
1559                            if (TrashUtil.isTrashEnabled(message.getGroupId())) {
1560                                    List<FileEntry> fileEntries =
1561                                            message.getAttachmentsFileEntries();
1562    
1563                                    for (FileEntry fileEntry : fileEntries) {
1564                                            moveMessageAttachmentToTrash(
1565                                                    userId, messageId, fileEntry.getTitle());
1566                                    }
1567                            }
1568                    }
1569    
1570                    message.setExpandoBridgeAttributes(serviceContext);
1571    
1572                    mbMessagePersistence.update(message);
1573    
1574                    // Thread
1575    
1576                    if ((priority != MBThreadConstants.PRIORITY_NOT_GIVEN) &&
1577                            (thread.getPriority() != priority)) {
1578    
1579                            thread.setPriority(priority);
1580    
1581                            mbThreadPersistence.update(thread);
1582    
1583                            updatePriorities(thread.getThreadId(), priority);
1584                    }
1585    
1586                    // Asset
1587    
1588                    updateAsset(
1589                            userId, message, serviceContext.getAssetCategoryIds(),
1590                            serviceContext.getAssetTagNames(),
1591                            serviceContext.getAssetLinkEntryIds());
1592    
1593                    // Workflow
1594    
1595                    WorkflowHandlerRegistryUtil.startWorkflowInstance(
1596                            message.getCompanyId(), message.getGroupId(), userId,
1597                            message.getWorkflowClassName(), message.getMessageId(), message,
1598                            serviceContext);
1599    
1600                    return message;
1601            }
1602    
1603            @Override
1604            public MBMessage updateMessage(long messageId, String body)
1605                    throws PortalException, SystemException {
1606    
1607                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1608    
1609                    message.setBody(body);
1610    
1611                    mbMessagePersistence.update(message);
1612    
1613                    return message;
1614            }
1615    
1616            @Override
1617            public MBMessage updateStatus(
1618                            long userId, long messageId, int status,
1619                            ServiceContext serviceContext)
1620                    throws PortalException, SystemException {
1621    
1622                    // Message
1623    
1624                    MBMessage message = getMessage(messageId);
1625    
1626                    int oldStatus = message.getStatus();
1627    
1628                    User user = userPersistence.findByPrimaryKey(userId);
1629                    Date now = new Date();
1630    
1631                    Date modifiedDate = serviceContext.getModifiedDate(now);
1632    
1633                    message.setStatus(status);
1634                    message.setStatusByUserId(userId);
1635                    message.setStatusByUserName(user.getFullName());
1636                    message.setStatusDate(modifiedDate);
1637    
1638                    mbMessagePersistence.update(message);
1639    
1640                    // Thread
1641    
1642                    MBThread thread = mbThreadPersistence.findByPrimaryKey(
1643                            message.getThreadId());
1644    
1645                    updateThreadStatus(thread, message, user, oldStatus, modifiedDate);
1646    
1647                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1648                            MBMessage.class);
1649    
1650                    if (status == WorkflowConstants.STATUS_APPROVED) {
1651                            if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
1652    
1653                                    // Asset
1654    
1655                                    if (serviceContext.isAssetEntryVisible() &&
1656                                            ((message.getClassNameId() == 0) ||
1657                                             (message.getParentMessageId() != 0))) {
1658    
1659                                            Date publishDate = null;
1660    
1661                                            AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
1662                                                    message.getWorkflowClassName(), message.getMessageId());
1663    
1664                                            if ((assetEntry != null) &&
1665                                                    (assetEntry.getPublishDate() != null)) {
1666    
1667                                                    publishDate = assetEntry.getPublishDate();
1668                                            }
1669                                            else {
1670                                                    publishDate = now;
1671    
1672                                                    serviceContext.setCommand(Constants.ADD);
1673                                            }
1674    
1675                                            assetEntryLocalService.updateEntry(
1676                                                    message.getWorkflowClassName(), message.getMessageId(),
1677                                                    publishDate, true);
1678                                    }
1679    
1680                                    if (serviceContext.isCommandAdd()) {
1681    
1682                                            // Social
1683    
1684                                            JSONObject extraDataJSONObject =
1685                                                    JSONFactoryUtil.createJSONObject();
1686    
1687                                            extraDataJSONObject.put("title", message.getSubject());
1688    
1689                                            if (!message.isDiscussion() ) {
1690                                                    if (!message.isAnonymous() && !user.isDefaultUser()) {
1691                                                            long receiverUserId = 0;
1692    
1693                                                            MBMessage parentMessage =
1694                                                                    mbMessagePersistence.fetchByPrimaryKey(
1695                                                                            message.getParentMessageId());
1696    
1697                                                            if (parentMessage != null) {
1698                                                                    receiverUserId = parentMessage.getUserId();
1699                                                            }
1700    
1701                                                            socialActivityLocalService.addActivity(
1702                                                                    message.getUserId(), message.getGroupId(),
1703                                                                    MBMessage.class.getName(),
1704                                                                    message.getMessageId(),
1705                                                                    MBActivityKeys.ADD_MESSAGE,
1706                                                                    extraDataJSONObject.toString(), receiverUserId);
1707    
1708                                                            if ((parentMessage != null) &&
1709                                                                    (receiverUserId != message.getUserId())) {
1710    
1711                                                                    socialActivityLocalService.addActivity(
1712                                                                            message.getUserId(),
1713                                                                            parentMessage.getGroupId(),
1714                                                                            MBMessage.class.getName(),
1715                                                                            parentMessage.getMessageId(),
1716                                                                            MBActivityKeys.REPLY_MESSAGE,
1717                                                                            extraDataJSONObject.toString(), 0);
1718                                                            }
1719                                                    }
1720                                            }
1721                                            else {
1722                                                    String className = (String)serviceContext.getAttribute(
1723                                                            "className");
1724                                                    long classPK = ParamUtil.getLong(
1725                                                            serviceContext, "classPK");
1726                                                    long parentMessageId = message.getParentMessageId();
1727    
1728                                                    if (parentMessageId !=
1729                                                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
1730    
1731                                                            AssetEntry assetEntry =
1732                                                                    assetEntryLocalService.fetchEntry(
1733                                                                            className, classPK);
1734    
1735                                                            if (assetEntry != null) {
1736                                                                    extraDataJSONObject.put(
1737                                                                            "messageId", message.getMessageId());
1738    
1739                                                                    socialActivityLocalService.addActivity(
1740                                                                            message.getUserId(),
1741                                                                            assetEntry.getGroupId(), className, classPK,
1742                                                                            SocialActivityConstants.TYPE_ADD_COMMENT,
1743                                                                            extraDataJSONObject.toString(),
1744                                                                            assetEntry.getUserId());
1745                                                            }
1746                                                    }
1747                                            }
1748                                    }
1749                            }
1750    
1751                            // Subscriptions
1752    
1753                            notifySubscribers((MBMessage)message.clone(), serviceContext);
1754    
1755                            // Indexer
1756    
1757                            indexer.reindex(message);
1758    
1759                            // Ping
1760    
1761                            pingPingback(message, serviceContext);
1762                    }
1763                    else if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
1764    
1765                            // Asset
1766    
1767                            assetEntryLocalService.updateVisible(
1768                                    message.getWorkflowClassName(), message.getMessageId(), false);
1769    
1770                            // Indexer
1771    
1772                            indexer.delete(message);
1773                    }
1774    
1775                    // Statistics
1776    
1777                    if (!message.isDiscussion()) {
1778                            mbStatsUserLocalService.updateStatsUser(
1779                                    message.getGroupId(), userId,
1780                                    serviceContext.getModifiedDate(now));
1781                    }
1782    
1783                    return message;
1784            }
1785    
1786            @Override
1787            public void updateUserName(long userId, String userName)
1788                    throws SystemException {
1789    
1790                    List<MBMessage> messages = mbMessagePersistence.findByUserId(userId);
1791    
1792                    for (MBMessage message : messages) {
1793                            message.setUserName(userName);
1794    
1795                            mbMessagePersistence.update(message);
1796                    }
1797            }
1798    
1799            protected void deleteDiscussionSocialActivities(
1800                            String className, MBMessage message)
1801                    throws PortalException, SystemException {
1802    
1803                    MBDiscussion discussion = mbDiscussionPersistence.findByThreadId(
1804                            message.getThreadId());
1805    
1806                    long classNameId = PortalUtil.getClassNameId(className);
1807                    long classPK = discussion.getClassPK();
1808    
1809                    if (discussion.getClassNameId() != classNameId) {
1810                            return;
1811                    }
1812    
1813                    List<SocialActivity> socialActivities =
1814                            socialActivityLocalService.getActivities(
1815                                    0, className, classPK, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
1816    
1817                    for (SocialActivity socialActivity : socialActivities) {
1818                            if (Validator.isNull(socialActivity.getExtraData())) {
1819                                    continue;
1820                            }
1821    
1822                            JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject(
1823                                    socialActivity.getExtraData());
1824    
1825                            long extraDataMessageId = extraDataJSONObject.getLong("messageId");
1826    
1827                            if (message.getMessageId() == extraDataMessageId) {
1828                                    socialActivityLocalService.deleteActivity(
1829                                            socialActivity.getActivityId());
1830                            }
1831                    }
1832            }
1833    
1834            protected String getBody(String subject, String body) {
1835                    if (Validator.isNull(body)) {
1836                            return subject;
1837                    }
1838    
1839                    return body;
1840            }
1841    
1842            protected String getMessageURL(
1843                            MBMessage message, ServiceContext serviceContext)
1844                    throws PortalException, SystemException {
1845    
1846                    HttpServletRequest request = serviceContext.getRequest();
1847    
1848                    if (request == null) {
1849                            if (Validator.isNull(serviceContext.getLayoutFullURL())) {
1850                                    return StringPool.BLANK;
1851                            }
1852    
1853                            return serviceContext.getLayoutFullURL() +
1854                                    Portal.FRIENDLY_URL_SEPARATOR + "message_boards/view_message/" +
1855                                            message.getMessageId();
1856                    }
1857    
1858                    String layoutURL = getLayoutURL(
1859                            message.getGroupId(), PortletKeys.MESSAGE_BOARDS, serviceContext);
1860    
1861                    if (Validator.isNotNull(layoutURL)) {
1862                            return layoutURL + Portal.FRIENDLY_URL_SEPARATOR +
1863                                    "message_boards/view_message/" + message.getMessageId();
1864                    }
1865                    else {
1866                            long controlPanelPlid = PortalUtil.getControlPanelPlid(
1867                                    serviceContext.getCompanyId());
1868    
1869                            PortletURL portletURL = PortletURLFactoryUtil.create(
1870                                    request, PortletKeys.MESSAGE_BOARDS_ADMIN, controlPanelPlid,
1871                                    PortletRequest.RENDER_PHASE);
1872    
1873                            portletURL.setParameter(
1874                                    "struts_action", "/message_boards_admin/view_message");
1875                            portletURL.setParameter(
1876                                    "messageId", String.valueOf(message.getMessageId()));
1877    
1878                            return portletURL.toString();
1879                    }
1880            }
1881    
1882            protected String getSubject(String subject, String body) {
1883                    if (Validator.isNull(subject)) {
1884                            return StringUtil.shorten(body);
1885                    }
1886    
1887                    return subject;
1888            }
1889    
1890            protected void notifyDiscussionSubscribers(
1891                            MBMessage message, ServiceContext serviceContext)
1892                    throws SystemException {
1893    
1894                    if (!PrefsPropsUtil.getBoolean(
1895                                    message.getCompanyId(),
1896                                    PropsKeys.DISCUSSION_EMAIL_COMMENTS_ADDED_ENABLED)) {
1897    
1898                            return;
1899                    }
1900    
1901                    String contentURL = (String)serviceContext.getAttribute("contentURL");
1902    
1903                    String userAddress = StringPool.BLANK;
1904                    String userName = (String)serviceContext.getAttribute(
1905                            "pingbackUserName");
1906    
1907                    if (Validator.isNull(userName)) {
1908                            userAddress = PortalUtil.getUserEmailAddress(message.getUserId());
1909                            userName = PortalUtil.getUserName(
1910                                    message.getUserId(), StringPool.BLANK);
1911                    }
1912    
1913                    String fromName = PrefsPropsUtil.getString(
1914                            message.getCompanyId(), PropsKeys.ADMIN_EMAIL_FROM_NAME);
1915                    String fromAddress = PrefsPropsUtil.getString(
1916                            message.getCompanyId(), PropsKeys.ADMIN_EMAIL_FROM_ADDRESS);
1917    
1918                    String subject = PrefsPropsUtil.getContent(
1919                            message.getCompanyId(), PropsKeys.DISCUSSION_EMAIL_SUBJECT);
1920                    String body = PrefsPropsUtil.getContent(
1921                            message.getCompanyId(), PropsKeys.DISCUSSION_EMAIL_BODY);
1922    
1923                    SubscriptionSender subscriptionSender = new SubscriptionSender();
1924    
1925                    subscriptionSender.setBody(body);
1926                    subscriptionSender.setCompanyId(message.getCompanyId());
1927                    subscriptionSender.setContextAttribute(
1928                            "[$COMMENTS_BODY$]", message.getBody(true), false);
1929                    subscriptionSender.setContextAttributes(
1930                            "[$COMMENTS_USER_ADDRESS$]", userAddress, "[$COMMENTS_USER_NAME$]",
1931                            userName, "[$CONTENT_URL$]", contentURL);
1932                    subscriptionSender.setFrom(fromAddress, fromName);
1933                    subscriptionSender.setHtmlFormat(true);
1934                    subscriptionSender.setMailId(
1935                            "mb_discussion", message.getCategoryId(), message.getMessageId());
1936                    subscriptionSender.setScopeGroupId(message.getGroupId());
1937                    subscriptionSender.setServiceContext(serviceContext);
1938                    subscriptionSender.setSubject(subject);
1939                    subscriptionSender.setUserId(message.getUserId());
1940    
1941                    String className = (String)serviceContext.getAttribute("className");
1942                    long classPK = ParamUtil.getLong(serviceContext, "classPK");
1943    
1944                    subscriptionSender.addPersistedSubscribers(className, classPK);
1945    
1946                    subscriptionSender.flushNotificationsAsync();
1947            }
1948    
1949            protected void notifySubscribers(
1950                            MBMessage message, ServiceContext serviceContext)
1951                    throws PortalException, SystemException {
1952    
1953                    String layoutFullURL = serviceContext.getLayoutFullURL();
1954    
1955                    if (!message.isApproved() || Validator.isNull(layoutFullURL)) {
1956                            return;
1957                    }
1958    
1959                    if (message.isDiscussion()) {
1960                            try {
1961                                    notifyDiscussionSubscribers(message, serviceContext);
1962                            }
1963                            catch (Exception e) {
1964                                    _log.error(e, e);
1965                            }
1966    
1967                            return;
1968                    }
1969    
1970                    PortletPreferences preferences =
1971                            portletPreferencesLocalService.getPreferences(
1972                                    message.getCompanyId(), message.getGroupId(),
1973                                    PortletKeys.PREFS_OWNER_TYPE_GROUP,
1974                                    PortletKeys.PREFS_PLID_SHARED, PortletKeys.MESSAGE_BOARDS,
1975                                    null);
1976    
1977                    if (serviceContext.isCommandAdd() &&
1978                            MBUtil.getEmailMessageAddedEnabled(preferences)) {
1979                    }
1980                    else if (serviceContext.isCommandUpdate() &&
1981                                     MBUtil.getEmailMessageUpdatedEnabled(preferences)) {
1982                    }
1983                    else {
1984                            return;
1985                    }
1986    
1987                    Company company = companyPersistence.findByPrimaryKey(
1988                            message.getCompanyId());
1989    
1990                    Group group = groupPersistence.findByPrimaryKey(message.getGroupId());
1991    
1992                    String emailAddress = PortalUtil.getUserEmailAddress(
1993                            message.getUserId());
1994                    String fullName = PortalUtil.getUserName(
1995                            message.getUserId(), message.getUserName());
1996    
1997                    if (message.isAnonymous()) {
1998                            emailAddress = StringPool.BLANK;
1999                            fullName = serviceContext.translate("anonymous");
2000                    }
2001    
2002                    MBCategory category = message.getCategory();
2003    
2004                    String categoryName = category.getName();
2005    
2006                    if (category.getCategoryId() ==
2007                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2008    
2009                            categoryName = serviceContext.translate("message-boards-home");
2010    
2011                            categoryName += " - " + group.getDescriptiveName();
2012                    }
2013    
2014                    List<Long> categoryIds = new ArrayList<Long>();
2015    
2016                    categoryIds.add(message.getCategoryId());
2017    
2018                    if (message.getCategoryId() !=
2019                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2020    
2021                            categoryIds.addAll(category.getAncestorCategoryIds());
2022                    }
2023    
2024                    String fromName = MBUtil.getEmailFromName(
2025                            preferences, message.getCompanyId());
2026                    String fromAddress = MBUtil.getEmailFromAddress(
2027                            preferences, message.getCompanyId());
2028    
2029                    String replyToAddress = StringPool.BLANK;
2030    
2031                    if (PropsValues.POP_SERVER_NOTIFICATIONS_ENABLED) {
2032                            replyToAddress = MBUtil.getReplyToAddress(
2033                                    message.getCategoryId(), message.getMessageId(),
2034                                    company.getMx(), fromAddress);
2035                    }
2036    
2037                    String subject = null;
2038                    String body = null;
2039                    String signature = null;
2040    
2041                    if (serviceContext.isCommandUpdate()) {
2042                            subject = MBUtil.getEmailMessageUpdatedSubject(preferences);
2043                            body = MBUtil.getEmailMessageUpdatedBody(preferences);
2044                            signature = MBUtil.getEmailMessageUpdatedSignature(preferences);
2045                    }
2046                    else {
2047                            subject = MBUtil.getEmailMessageAddedSubject(preferences);
2048                            body = MBUtil.getEmailMessageAddedBody(preferences);
2049                            signature = MBUtil.getEmailMessageAddedSignature(preferences);
2050                    }
2051    
2052                    boolean htmlFormat = MBUtil.getEmailHtmlFormat(preferences);
2053    
2054                    if (Validator.isNotNull(signature)) {
2055                            String signatureSeparator = null;
2056    
2057                            if (htmlFormat) {
2058                                    signatureSeparator = "<br />--<br />";
2059                            }
2060                            else {
2061                                    signatureSeparator = "\n--\n";
2062                            }
2063    
2064                            body += signatureSeparator + signature;
2065                    }
2066    
2067                    String messageBody = message.getBody();
2068    
2069                    if (htmlFormat && message.isFormatBBCode()) {
2070                            try {
2071                                    messageBody = BBCodeTranslatorUtil.getHTML(messageBody);
2072    
2073                                    HttpServletRequest request = serviceContext.getRequest();
2074    
2075                                    if (request != null) {
2076                                            ThemeDisplay themeDisplay =
2077                                                    (ThemeDisplay)request.getAttribute(
2078                                                            WebKeys.THEME_DISPLAY);
2079    
2080                                            messageBody = MBUtil.replaceMessageBodyPaths(
2081                                                    themeDisplay, messageBody);
2082                                    }
2083                            }
2084                            catch (Exception e) {
2085                                    _log.error(
2086                                            "Could not parse message " + message.getMessageId() +
2087                                                    " " + e.getMessage());
2088                            }
2089                    }
2090    
2091                    String inReplyTo = null;
2092    
2093                    if (message.getParentMessageId() !=
2094                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
2095    
2096                            inReplyTo = PortalUtil.getMailId(
2097                                    company.getMx(), MBUtil.MESSAGE_POP_PORTLET_PREFIX,
2098                                    message.getCategoryId(), message.getParentMessageId());
2099                    }
2100    
2101                    String entryURL = getMessageURL(message, serviceContext);
2102    
2103                    SubscriptionSender subscriptionSenderPrototype =
2104                            new MBSubscriptionSender();
2105    
2106                    subscriptionSenderPrototype.setBody(body);
2107                    subscriptionSenderPrototype.setBulk(
2108                            PropsValues.MESSAGE_BOARDS_EMAIL_BULK);
2109                    subscriptionSenderPrototype.setCompanyId(message.getCompanyId());
2110                    subscriptionSenderPrototype.setContextAttribute(
2111                            "[$MESSAGE_BODY$]", messageBody, false);
2112                    subscriptionSenderPrototype.setContextAttributes(
2113                            "[$CATEGORY_NAME$]", categoryName, "[$MAILING_LIST_ADDRESS$]",
2114                            replyToAddress, "[$MESSAGE_ID$]", message.getMessageId(),
2115                            "[$MESSAGE_SUBJECT$]", message.getSubject(), "[$MESSAGE_URL$]",
2116                            entryURL, "[$MESSAGE_USER_ADDRESS$]", emailAddress,
2117                            "[$MESSAGE_USER_NAME$]", fullName);
2118                    subscriptionSenderPrototype.setFrom(fromAddress, fromName);
2119                    subscriptionSenderPrototype.setHtmlFormat(htmlFormat);
2120                    subscriptionSenderPrototype.setInReplyTo(inReplyTo);
2121                    subscriptionSenderPrototype.setMailId(
2122                            MBUtil.MESSAGE_POP_PORTLET_PREFIX, message.getCategoryId(),
2123                            message.getMessageId());
2124                    subscriptionSenderPrototype.setPortletId(PortletKeys.MESSAGE_BOARDS);
2125                    subscriptionSenderPrototype.setReplyToAddress(replyToAddress);
2126                    subscriptionSenderPrototype.setScopeGroupId(message.getGroupId());
2127                    subscriptionSenderPrototype.setServiceContext(serviceContext);
2128                    subscriptionSenderPrototype.setSubject(subject);
2129                    subscriptionSenderPrototype.setUserId(message.getUserId());
2130    
2131                    serviceContext.setAttribute("entryURL", entryURL);
2132    
2133                    SubscriptionSender subscriptionSender =
2134                            (SubscriptionSender)SerializableUtil.clone(
2135                                    subscriptionSenderPrototype);
2136    
2137                    subscriptionSender.addPersistedSubscribers(
2138                            MBCategory.class.getName(), message.getGroupId());
2139    
2140                    for (long categoryId : categoryIds) {
2141                            if (categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2142                                    subscriptionSender.addPersistedSubscribers(
2143                                            MBCategory.class.getName(), categoryId);
2144                            }
2145                    }
2146    
2147                    subscriptionSender.addPersistedSubscribers(
2148                            MBThread.class.getName(), message.getThreadId());
2149    
2150                    subscriptionSender.flushNotificationsAsync();
2151    
2152                    if (!MailingListThreadLocal.isSourceMailingList()) {
2153                            for (long categoryId : categoryIds) {
2154                                    MBSubscriptionSender sourceMailingListSubscriptionSender =
2155                                            (MBSubscriptionSender)SerializableUtil.clone(
2156                                                    subscriptionSenderPrototype);
2157    
2158                                    sourceMailingListSubscriptionSender.setBulk(false);
2159    
2160                                    sourceMailingListSubscriptionSender.addMailingListSubscriber(
2161                                            message.getGroupId(), categoryId);
2162    
2163                                    sourceMailingListSubscriptionSender.flushNotificationsAsync();
2164                            }
2165                    }
2166            }
2167    
2168            protected void pingPingback(
2169                    MBMessage message, ServiceContext serviceContext) {
2170    
2171                    if (!PropsValues.BLOGS_PINGBACK_ENABLED ||
2172                            !message.isAllowPingbacks() || !message.isApproved()) {
2173    
2174                            return;
2175                    }
2176    
2177                    String layoutFullURL = serviceContext.getLayoutFullURL();
2178    
2179                    if (Validator.isNull(layoutFullURL)) {
2180                            return;
2181                    }
2182    
2183                    String sourceUri =
2184                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR +
2185                                    "message_boards/view_message/" + message.getMessageId();
2186    
2187                    Source source = new Source(message.getBody(true));
2188    
2189                    List<StartTag> startTags = source.getAllStartTags("a");
2190    
2191                    for (StartTag startTag : startTags) {
2192                            String targetUri = startTag.getAttributeValue("href");
2193    
2194                            if (Validator.isNotNull(targetUri)) {
2195                                    try {
2196                                            LinkbackProducerUtil.sendPingback(sourceUri, targetUri);
2197                                    }
2198                                    catch (Exception e) {
2199                                            _log.error("Error while sending pingback " + targetUri, e);
2200                                    }
2201                            }
2202                    }
2203            }
2204    
2205            protected void updateAsset(
2206                            long userId, MBMessage message, long[] assetCategoryIds,
2207                            String[] assetTagNames, long[] assetLinkEntryIds,
2208                            boolean assetEntryVisible)
2209                    throws PortalException, SystemException {
2210    
2211                    boolean visible = false;
2212    
2213                    if (assetEntryVisible && message.isApproved() &&
2214                            ((message.getClassNameId() == 0) ||
2215                             (message.getParentMessageId() != 0))) {
2216    
2217                            visible = true;
2218                    }
2219    
2220                    AssetEntry assetEntry = assetEntryLocalService.updateEntry(
2221                            userId, message.getGroupId(), message.getCreateDate(),
2222                            message.getModifiedDate(), message.getWorkflowClassName(),
2223                            message.getMessageId(), message.getUuid(), 0, assetCategoryIds,
2224                            assetTagNames, visible, null, null, null, ContentTypes.TEXT_HTML,
2225                            message.getSubject(), null, null, null, null, 0, 0, null, false);
2226    
2227                    assetLinkLocalService.updateLinks(
2228                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
2229                            AssetLinkConstants.TYPE_RELATED);
2230            }
2231    
2232            protected void updatePriorities(long threadId, double priority)
2233                    throws SystemException {
2234    
2235                    List<MBMessage> messages = mbMessagePersistence.findByThreadId(
2236                            threadId);
2237    
2238                    for (MBMessage message : messages) {
2239                            if (message.getPriority() != priority) {
2240                                    message.setPriority(priority);
2241    
2242                                    mbMessagePersistence.update(message);
2243                            }
2244                    }
2245            }
2246    
2247            protected void updateThreadStatus(
2248                            MBThread thread, MBMessage message, User user, int oldStatus,
2249                            Date modifiedDate)
2250                    throws PortalException, SystemException {
2251    
2252                    MBCategory category = null;
2253    
2254                    int status = message.getStatus();
2255    
2256                    if ((thread.getCategoryId() !=
2257                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
2258                            (thread.getCategoryId() !=
2259                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
2260    
2261                            category = mbCategoryPersistence.findByPrimaryKey(
2262                                    thread.getCategoryId());
2263                    }
2264    
2265                    if ((thread.getRootMessageId() == message.getMessageId()) &&
2266                            (oldStatus != status)) {
2267    
2268                            thread.setModifiedDate(modifiedDate);
2269                            thread.setStatus(status);
2270                            thread.setStatusByUserId(user.getUserId());
2271                            thread.setStatusByUserName(user.getFullName());
2272                            thread.setStatusDate(modifiedDate);
2273                    }
2274    
2275                    if (status == oldStatus) {
2276                            return;
2277                    }
2278    
2279                    if (status == WorkflowConstants.STATUS_APPROVED) {
2280                            if (message.isAnonymous()) {
2281                                    thread.setLastPostByUserId(0);
2282                            }
2283                            else {
2284                                    thread.setLastPostByUserId(message.getUserId());
2285                            }
2286    
2287                            thread.setLastPostDate(modifiedDate);
2288    
2289                            if (category != null) {
2290                                    category.setLastPostDate(modifiedDate);
2291    
2292                                    category = mbCategoryPersistence.update(category);
2293                            }
2294                    }
2295    
2296                    if ((oldStatus == WorkflowConstants.STATUS_APPROVED) ||
2297                            (status == WorkflowConstants.STATUS_APPROVED)) {
2298    
2299                            // Thread
2300    
2301                            MBUtil.updateThreadMessageCount(
2302                                    thread.getCompanyId(), thread.getThreadId());
2303    
2304                            // Category
2305    
2306                            if ((category != null) &&
2307                                    (thread.getRootMessageId() == message.getMessageId())) {
2308    
2309                                    MBUtil.updateCategoryStatistics(
2310                                            category.getCompanyId(), category.getCategoryId());
2311                            }
2312    
2313                            if ((category != null) &&
2314                                    !(thread.getRootMessageId() == message.getMessageId())) {
2315    
2316                                    MBUtil.updateCategoryMessageCount(
2317                                            category.getCompanyId(), category.getCategoryId());
2318                            }
2319                    }
2320    
2321                    // Indexer
2322    
2323                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
2324                            MBThread.class);
2325    
2326                    indexer.reindex(thread);
2327    
2328                    mbThreadPersistence.update(thread);
2329            }
2330    
2331            protected void validate(String subject, String body)
2332                    throws PortalException {
2333    
2334                    if (Validator.isNull(subject) && Validator.isNull(body)) {
2335                            throw new MessageSubjectException();
2336                    }
2337            }
2338    
2339            private static Log _log = LogFactoryUtil.getLog(
2340                    MBMessageLocalServiceImpl.class);
2341    
2342    }