001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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 void moveDiscussionToTrash(String className, long classPK)
1339                    throws SystemException {
1340    
1341                    List<MBMessage> messages = getMessages(
1342                            className, classPK, WorkflowConstants.STATUS_APPROVED);
1343    
1344                    for (MBMessage message : messages) {
1345                            message.setStatus(WorkflowConstants.STATUS_IN_TRASH);
1346    
1347                            mbMessageLocalService.updateMBMessage(message);
1348                    }
1349            }
1350    
1351            @Override
1352            public long moveMessageAttachmentToTrash(
1353                            long userId, long messageId, String fileName)
1354                    throws PortalException, SystemException {
1355    
1356                    MBMessage message = getMessage(messageId);
1357    
1358                    long folderId = message.getAttachmentsFolderId();
1359    
1360                    FileEntry fileEntry = PortletFileRepositoryUtil.getPortletFileEntry(
1361                            message.getGroupId(), folderId, fileName);
1362    
1363                    PortletFileRepositoryUtil.movePortletFileEntryToTrash(
1364                            userId, fileEntry.getFileEntryId());
1365    
1366                    return fileEntry.getFileEntryId();
1367            }
1368    
1369            @Override
1370            public void restoreDiscussionFromTrash(String className, long classPK)
1371                    throws SystemException {
1372    
1373                    List<MBMessage> messages = getMessages(
1374                            className, classPK, WorkflowConstants.STATUS_IN_TRASH);
1375    
1376                    for (MBMessage message : messages) {
1377                            message.setStatus(WorkflowConstants.STATUS_APPROVED);
1378    
1379                            mbMessageLocalService.updateMBMessage(message);
1380                    }
1381            }
1382    
1383            @Override
1384            public void restoreMessageAttachmentFromTrash(
1385                            long userId, long messageId, String deletedFileName)
1386                    throws PortalException, SystemException {
1387    
1388                    MBMessage message = getMessage(messageId);
1389    
1390                    Folder folder = message.addAttachmentsFolder();
1391    
1392                    PortletFileRepositoryUtil.restorePortletFileEntryFromTrash(
1393                            message.getGroupId(), userId, folder.getFolderId(),
1394                            deletedFileName);
1395            }
1396    
1397            @Override
1398            public void subscribeMessage(long userId, long messageId)
1399                    throws PortalException, SystemException {
1400    
1401                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1402    
1403                    subscriptionLocalService.addSubscription(
1404                            userId, message.getGroupId(), MBThread.class.getName(),
1405                            message.getThreadId());
1406            }
1407    
1408            @Override
1409            public void unsubscribeMessage(long userId, long messageId)
1410                    throws PortalException, SystemException {
1411    
1412                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1413    
1414                    subscriptionLocalService.deleteSubscription(
1415                            userId, MBThread.class.getName(), message.getThreadId());
1416            }
1417    
1418            @Override
1419            public void updateAnswer(long messageId, boolean answer, boolean cascade)
1420                    throws PortalException, SystemException {
1421    
1422                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1423    
1424                    updateAnswer(message, answer, cascade);
1425            }
1426    
1427            @Override
1428            public void updateAnswer(MBMessage message, boolean answer, boolean cascade)
1429                    throws PortalException, SystemException {
1430    
1431                    if (message.isAnswer() != answer) {
1432                            message.setAnswer(answer);
1433    
1434                            mbMessagePersistence.update(message);
1435                    }
1436    
1437                    if (cascade) {
1438                            List<MBMessage> messages = mbMessagePersistence.findByT_P(
1439                                    message.getThreadId(), message.getMessageId());
1440    
1441                            for (MBMessage curMessage : messages) {
1442                                    updateAnswer(curMessage, answer, cascade);
1443                            }
1444                    }
1445            }
1446    
1447            @Override
1448            public void updateAsset(
1449                            long userId, MBMessage message, long[] assetCategoryIds,
1450                            String[] assetTagNames, long[] assetLinkEntryIds)
1451                    throws PortalException, SystemException {
1452    
1453                    updateAsset(
1454                            userId, message, assetCategoryIds, assetTagNames, assetLinkEntryIds,
1455                            true);
1456            }
1457    
1458            @Override
1459            public MBMessage updateDiscussionMessage(
1460                            long userId, long messageId, String className, long classPK,
1461                            String subject, String body, ServiceContext serviceContext)
1462                    throws PortalException, SystemException {
1463    
1464                    if (Validator.isNull(subject)) {
1465                            if (Validator.isNotNull(body)) {
1466                                    int pos = Math.min(body.length(), 50);
1467    
1468                                    subject = body.substring(0, pos) + "...";
1469                            }
1470                            else {
1471                                    throw new MessageBodyException();
1472                            }
1473                    }
1474    
1475                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
1476                            Collections.emptyList();
1477                    List<String> existingFiles = new ArrayList<String>();
1478                    double priority = 0.0;
1479                    boolean allowPingbacks = false;
1480    
1481                    serviceContext.setAttribute("className", className);
1482                    serviceContext.setAttribute("classPK", String.valueOf(classPK));
1483    
1484                    return updateMessage(
1485                            userId, messageId, subject, body, inputStreamOVPs, existingFiles,
1486                            priority, allowPingbacks, serviceContext);
1487            }
1488    
1489            @Override
1490            public MBMessage updateMessage(
1491                            long userId, long messageId, String subject, String body,
1492                            List<ObjectValuePair<String, InputStream>> inputStreamOVPs,
1493                            List<String> existingFiles, double priority, boolean allowPingbacks,
1494                            ServiceContext serviceContext)
1495                    throws PortalException, SystemException {
1496    
1497                    // Message
1498    
1499                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1500    
1501                    int oldStatus = message.getStatus();
1502    
1503                    Date modifiedDate = serviceContext.getModifiedDate(new Date());
1504                    subject = ModelHintsUtil.trimString(
1505                            MBMessage.class.getName(), "subject", subject);
1506                    body = SanitizerUtil.sanitize(
1507                            message.getCompanyId(), message.getGroupId(), userId,
1508                            MBMessage.class.getName(), messageId, "text/" + message.getFormat(),
1509                            body);
1510    
1511                    validate(subject, body);
1512    
1513                    subject = getSubject(subject, body);
1514                    body = getBody(subject, body);
1515    
1516                    message.setModifiedDate(modifiedDate);
1517                    message.setSubject(subject);
1518                    message.setBody(body);
1519                    message.setAllowPingbacks(allowPingbacks);
1520    
1521                    if (priority != MBThreadConstants.PRIORITY_NOT_GIVEN) {
1522                            message.setPriority(priority);
1523                    }
1524    
1525                    MBThread thread = mbThreadPersistence.findByPrimaryKey(
1526                            message.getThreadId());
1527    
1528                    if (serviceContext.getWorkflowAction() ==
1529                                    WorkflowConstants.ACTION_SAVE_DRAFT) {
1530    
1531                            if (!message.isDraft() && !message.isPending()) {
1532                                    message.setStatus(WorkflowConstants.STATUS_DRAFT);
1533    
1534                                    // Thread
1535    
1536                                    User user = userPersistence.findByPrimaryKey(userId);
1537    
1538                                    updateThreadStatus(
1539                                            thread, message, user, oldStatus, modifiedDate);
1540    
1541                                    // Asset
1542    
1543                                    assetEntryLocalService.updateVisible(
1544                                            message.getWorkflowClassName(), message.getMessageId(),
1545                                            false);
1546    
1547                                    if (!message.isDiscussion()) {
1548    
1549                                            // Indexer
1550    
1551                                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1552                                                    MBMessage.class);
1553    
1554                                            indexer.delete(message);
1555                                    }
1556                            }
1557                    }
1558    
1559                    // Attachments
1560    
1561                    if (!inputStreamOVPs.isEmpty() || !existingFiles.isEmpty()) {
1562                            List<FileEntry> fileEntries = message.getAttachmentsFileEntries();
1563    
1564                            for (FileEntry fileEntry : fileEntries) {
1565                                    String fileEntryId = String.valueOf(fileEntry.getFileEntryId());
1566    
1567                                    if (!existingFiles.contains(fileEntryId)) {
1568                                            if (!TrashUtil.isTrashEnabled(message.getGroupId())) {
1569                                                    deleteMessageAttachment(
1570                                                            messageId, fileEntry.getTitle());
1571                                            }
1572                                            else {
1573                                                    moveMessageAttachmentToTrash(
1574                                                            userId, messageId, fileEntry.getTitle());
1575                                            }
1576                                    }
1577                            }
1578    
1579                            Folder folder = message.addAttachmentsFolder();
1580    
1581                            PortletFileRepositoryUtil.addPortletFileEntries(
1582                                    message.getGroupId(), userId, MBMessage.class.getName(),
1583                                    message.getMessageId(), PortletKeys.MESSAGE_BOARDS,
1584                                    folder.getFolderId(), inputStreamOVPs);
1585                    }
1586                    else {
1587                            if (TrashUtil.isTrashEnabled(message.getGroupId())) {
1588                                    List<FileEntry> fileEntries =
1589                                            message.getAttachmentsFileEntries();
1590    
1591                                    for (FileEntry fileEntry : fileEntries) {
1592                                            moveMessageAttachmentToTrash(
1593                                                    userId, messageId, fileEntry.getTitle());
1594                                    }
1595                            }
1596                    }
1597    
1598                    message.setExpandoBridgeAttributes(serviceContext);
1599    
1600                    mbMessagePersistence.update(message);
1601    
1602                    // Thread
1603    
1604                    if ((priority != MBThreadConstants.PRIORITY_NOT_GIVEN) &&
1605                            (thread.getPriority() != priority)) {
1606    
1607                            thread.setPriority(priority);
1608    
1609                            mbThreadPersistence.update(thread);
1610    
1611                            updatePriorities(thread.getThreadId(), priority);
1612                    }
1613    
1614                    // Asset
1615    
1616                    updateAsset(
1617                            userId, message, serviceContext.getAssetCategoryIds(),
1618                            serviceContext.getAssetTagNames(),
1619                            serviceContext.getAssetLinkEntryIds());
1620    
1621                    // Workflow
1622    
1623                    WorkflowHandlerRegistryUtil.startWorkflowInstance(
1624                            message.getCompanyId(), message.getGroupId(), userId,
1625                            message.getWorkflowClassName(), message.getMessageId(), message,
1626                            serviceContext);
1627    
1628                    return message;
1629            }
1630    
1631            @Override
1632            public MBMessage updateMessage(long messageId, String body)
1633                    throws PortalException, SystemException {
1634    
1635                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1636    
1637                    message.setBody(body);
1638    
1639                    mbMessagePersistence.update(message);
1640    
1641                    return message;
1642            }
1643    
1644            @Override
1645            public MBMessage updateStatus(
1646                            long userId, long messageId, int status,
1647                            ServiceContext serviceContext)
1648                    throws PortalException, SystemException {
1649    
1650                    // Message
1651    
1652                    MBMessage message = getMessage(messageId);
1653    
1654                    int oldStatus = message.getStatus();
1655    
1656                    User user = userPersistence.findByPrimaryKey(userId);
1657                    Date now = new Date();
1658    
1659                    Date modifiedDate = serviceContext.getModifiedDate(now);
1660    
1661                    message.setStatus(status);
1662                    message.setStatusByUserId(userId);
1663                    message.setStatusByUserName(user.getFullName());
1664                    message.setStatusDate(modifiedDate);
1665    
1666                    mbMessagePersistence.update(message);
1667    
1668                    // Thread
1669    
1670                    MBThread thread = mbThreadPersistence.findByPrimaryKey(
1671                            message.getThreadId());
1672    
1673                    updateThreadStatus(thread, message, user, oldStatus, modifiedDate);
1674    
1675                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1676                            MBMessage.class);
1677    
1678                    if (status == WorkflowConstants.STATUS_APPROVED) {
1679                            if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
1680    
1681                                    // Asset
1682    
1683                                    if (serviceContext.isAssetEntryVisible() &&
1684                                            ((message.getClassNameId() == 0) ||
1685                                             (message.getParentMessageId() != 0))) {
1686    
1687                                            Date publishDate = null;
1688    
1689                                            AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
1690                                                    message.getWorkflowClassName(), message.getMessageId());
1691    
1692                                            if ((assetEntry != null) &&
1693                                                    (assetEntry.getPublishDate() != null)) {
1694    
1695                                                    publishDate = assetEntry.getPublishDate();
1696                                            }
1697                                            else {
1698                                                    publishDate = now;
1699    
1700                                                    serviceContext.setCommand(Constants.ADD);
1701                                            }
1702    
1703                                            assetEntryLocalService.updateEntry(
1704                                                    message.getWorkflowClassName(), message.getMessageId(),
1705                                                    publishDate, true);
1706                                    }
1707    
1708                                    if (serviceContext.isCommandAdd()) {
1709    
1710                                            // Social
1711    
1712                                            JSONObject extraDataJSONObject =
1713                                                    JSONFactoryUtil.createJSONObject();
1714    
1715                                            extraDataJSONObject.put("title", message.getSubject());
1716    
1717                                            if (!message.isDiscussion() ) {
1718                                                    if (!message.isAnonymous() && !user.isDefaultUser()) {
1719                                                            long receiverUserId = 0;
1720    
1721                                                            MBMessage parentMessage =
1722                                                                    mbMessagePersistence.fetchByPrimaryKey(
1723                                                                            message.getParentMessageId());
1724    
1725                                                            if (parentMessage != null) {
1726                                                                    receiverUserId = parentMessage.getUserId();
1727                                                            }
1728    
1729                                                            socialActivityLocalService.addActivity(
1730                                                                    message.getUserId(), message.getGroupId(),
1731                                                                    MBMessage.class.getName(),
1732                                                                    message.getMessageId(),
1733                                                                    MBActivityKeys.ADD_MESSAGE,
1734                                                                    extraDataJSONObject.toString(), receiverUserId);
1735    
1736                                                            if ((parentMessage != null) &&
1737                                                                    (receiverUserId != message.getUserId())) {
1738    
1739                                                                    socialActivityLocalService.addActivity(
1740                                                                            message.getUserId(),
1741                                                                            parentMessage.getGroupId(),
1742                                                                            MBMessage.class.getName(),
1743                                                                            parentMessage.getMessageId(),
1744                                                                            MBActivityKeys.REPLY_MESSAGE,
1745                                                                            extraDataJSONObject.toString(), 0);
1746                                                            }
1747                                                    }
1748                                            }
1749                                            else {
1750                                                    String className = (String)serviceContext.getAttribute(
1751                                                            "className");
1752                                                    long classPK = ParamUtil.getLong(
1753                                                            serviceContext, "classPK");
1754                                                    long parentMessageId = message.getParentMessageId();
1755    
1756                                                    if (parentMessageId !=
1757                                                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
1758    
1759                                                            AssetEntry assetEntry =
1760                                                                    assetEntryLocalService.fetchEntry(
1761                                                                            className, classPK);
1762    
1763                                                            if (assetEntry != null) {
1764                                                                    extraDataJSONObject.put(
1765                                                                            "messageId", message.getMessageId());
1766    
1767                                                                    socialActivityLocalService.addActivity(
1768                                                                            message.getUserId(),
1769                                                                            assetEntry.getGroupId(), className, classPK,
1770                                                                            SocialActivityConstants.TYPE_ADD_COMMENT,
1771                                                                            extraDataJSONObject.toString(),
1772                                                                            assetEntry.getUserId());
1773                                                            }
1774                                                    }
1775                                            }
1776                                    }
1777                            }
1778    
1779                            // Subscriptions
1780    
1781                            notifySubscribers((MBMessage)message.clone(), serviceContext);
1782    
1783                            // Indexer
1784    
1785                            indexer.reindex(message);
1786    
1787                            // Ping
1788    
1789                            pingPingback(message, serviceContext);
1790                    }
1791                    else if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
1792    
1793                            // Asset
1794    
1795                            assetEntryLocalService.updateVisible(
1796                                    message.getWorkflowClassName(), message.getMessageId(), false);
1797    
1798                            // Indexer
1799    
1800                            indexer.delete(message);
1801                    }
1802    
1803                    // Statistics
1804    
1805                    if (!message.isDiscussion()) {
1806                            mbStatsUserLocalService.updateStatsUser(
1807                                    message.getGroupId(), userId,
1808                                    serviceContext.getModifiedDate(now));
1809                    }
1810    
1811                    return message;
1812            }
1813    
1814            @Override
1815            public void updateUserName(long userId, String userName)
1816                    throws SystemException {
1817    
1818                    List<MBMessage> messages = mbMessagePersistence.findByUserId(userId);
1819    
1820                    for (MBMessage message : messages) {
1821                            message.setUserName(userName);
1822    
1823                            mbMessagePersistence.update(message);
1824                    }
1825            }
1826    
1827            protected void deleteDiscussionSocialActivities(
1828                            String className, MBMessage message)
1829                    throws PortalException, SystemException {
1830    
1831                    MBDiscussion discussion = mbDiscussionPersistence.findByThreadId(
1832                            message.getThreadId());
1833    
1834                    long classNameId = PortalUtil.getClassNameId(className);
1835                    long classPK = discussion.getClassPK();
1836    
1837                    if (discussion.getClassNameId() != classNameId) {
1838                            return;
1839                    }
1840    
1841                    List<SocialActivity> socialActivities =
1842                            socialActivityLocalService.getActivities(
1843                                    0, className, classPK, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
1844    
1845                    for (SocialActivity socialActivity : socialActivities) {
1846                            if (Validator.isNull(socialActivity.getExtraData())) {
1847                                    continue;
1848                            }
1849    
1850                            JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject(
1851                                    socialActivity.getExtraData());
1852    
1853                            long extraDataMessageId = extraDataJSONObject.getLong("messageId");
1854    
1855                            if (message.getMessageId() == extraDataMessageId) {
1856                                    socialActivityLocalService.deleteActivity(
1857                                            socialActivity.getActivityId());
1858                            }
1859                    }
1860            }
1861    
1862            protected String getBody(String subject, String body) {
1863                    if (Validator.isNull(body)) {
1864                            return subject;
1865                    }
1866    
1867                    return body;
1868            }
1869    
1870            protected String getMessageURL(
1871                            MBMessage message, ServiceContext serviceContext)
1872                    throws PortalException, SystemException {
1873    
1874                    HttpServletRequest request = serviceContext.getRequest();
1875    
1876                    if (request == null) {
1877                            if (Validator.isNull(serviceContext.getLayoutFullURL())) {
1878                                    return StringPool.BLANK;
1879                            }
1880    
1881                            return serviceContext.getLayoutFullURL() +
1882                                    Portal.FRIENDLY_URL_SEPARATOR + "message_boards/view_message/" +
1883                                            message.getMessageId();
1884                    }
1885    
1886                    String layoutURL = getLayoutURL(
1887                            message.getGroupId(), PortletKeys.MESSAGE_BOARDS, serviceContext);
1888    
1889                    if (Validator.isNotNull(layoutURL)) {
1890                            return layoutURL + Portal.FRIENDLY_URL_SEPARATOR +
1891                                    "message_boards/view_message/" + message.getMessageId();
1892                    }
1893                    else {
1894                            long controlPanelPlid = PortalUtil.getControlPanelPlid(
1895                                    serviceContext.getCompanyId());
1896    
1897                            PortletURL portletURL = PortletURLFactoryUtil.create(
1898                                    request, PortletKeys.MESSAGE_BOARDS_ADMIN, controlPanelPlid,
1899                                    PortletRequest.RENDER_PHASE);
1900    
1901                            portletURL.setParameter(
1902                                    "struts_action", "/message_boards_admin/view_message");
1903                            portletURL.setParameter(
1904                                    "messageId", String.valueOf(message.getMessageId()));
1905    
1906                            return portletURL.toString();
1907                    }
1908            }
1909    
1910            protected String getSubject(String subject, String body) {
1911                    if (Validator.isNull(subject)) {
1912                            return StringUtil.shorten(body);
1913                    }
1914    
1915                    return subject;
1916            }
1917    
1918            protected void notifyDiscussionSubscribers(
1919                            MBMessage message, ServiceContext serviceContext)
1920                    throws SystemException {
1921    
1922                    if (!PrefsPropsUtil.getBoolean(
1923                                    message.getCompanyId(),
1924                                    PropsKeys.DISCUSSION_EMAIL_COMMENTS_ADDED_ENABLED)) {
1925    
1926                            return;
1927                    }
1928    
1929                    String contentURL = (String)serviceContext.getAttribute("contentURL");
1930    
1931                    String userAddress = StringPool.BLANK;
1932                    String userName = (String)serviceContext.getAttribute(
1933                            "pingbackUserName");
1934    
1935                    if (Validator.isNull(userName)) {
1936                            userAddress = PortalUtil.getUserEmailAddress(message.getUserId());
1937                            userName = PortalUtil.getUserName(
1938                                    message.getUserId(), StringPool.BLANK);
1939                    }
1940    
1941                    String fromName = PrefsPropsUtil.getString(
1942                            message.getCompanyId(), PropsKeys.ADMIN_EMAIL_FROM_NAME);
1943                    String fromAddress = PrefsPropsUtil.getString(
1944                            message.getCompanyId(), PropsKeys.ADMIN_EMAIL_FROM_ADDRESS);
1945    
1946                    String subject = PrefsPropsUtil.getContent(
1947                            message.getCompanyId(), PropsKeys.DISCUSSION_EMAIL_SUBJECT);
1948                    String body = PrefsPropsUtil.getContent(
1949                            message.getCompanyId(), PropsKeys.DISCUSSION_EMAIL_BODY);
1950    
1951                    SubscriptionSender subscriptionSender = new SubscriptionSender();
1952    
1953                    subscriptionSender.setBody(body);
1954                    subscriptionSender.setCompanyId(message.getCompanyId());
1955                    subscriptionSender.setContextAttribute(
1956                            "[$COMMENTS_BODY$]", message.getBody(true), false);
1957                    subscriptionSender.setContextAttributes(
1958                            "[$COMMENTS_USER_ADDRESS$]", userAddress, "[$COMMENTS_USER_NAME$]",
1959                            userName, "[$CONTENT_URL$]", contentURL);
1960                    subscriptionSender.setFrom(fromAddress, fromName);
1961                    subscriptionSender.setHtmlFormat(true);
1962                    subscriptionSender.setMailId(
1963                            "mb_discussion", message.getCategoryId(), message.getMessageId());
1964                    subscriptionSender.setScopeGroupId(message.getGroupId());
1965                    subscriptionSender.setServiceContext(serviceContext);
1966                    subscriptionSender.setSubject(subject);
1967                    subscriptionSender.setUserId(message.getUserId());
1968    
1969                    String className = (String)serviceContext.getAttribute("className");
1970                    long classPK = ParamUtil.getLong(serviceContext, "classPK");
1971    
1972                    subscriptionSender.addPersistedSubscribers(className, classPK);
1973    
1974                    subscriptionSender.flushNotificationsAsync();
1975            }
1976    
1977            protected void notifySubscribers(
1978                            MBMessage message, ServiceContext serviceContext)
1979                    throws PortalException, SystemException {
1980    
1981                    String layoutFullURL = serviceContext.getLayoutFullURL();
1982    
1983                    if (!message.isApproved() || Validator.isNull(layoutFullURL)) {
1984                            return;
1985                    }
1986    
1987                    if (message.isDiscussion()) {
1988                            try {
1989                                    notifyDiscussionSubscribers(message, serviceContext);
1990                            }
1991                            catch (Exception e) {
1992                                    _log.error(e, e);
1993                            }
1994    
1995                            return;
1996                    }
1997    
1998                    PortletPreferences preferences =
1999                            portletPreferencesLocalService.getPreferences(
2000                                    message.getCompanyId(), message.getGroupId(),
2001                                    PortletKeys.PREFS_OWNER_TYPE_GROUP,
2002                                    PortletKeys.PREFS_PLID_SHARED, PortletKeys.MESSAGE_BOARDS,
2003                                    null);
2004    
2005                    if (serviceContext.isCommandAdd() &&
2006                            MBUtil.getEmailMessageAddedEnabled(preferences)) {
2007                    }
2008                    else if (serviceContext.isCommandUpdate() &&
2009                                     MBUtil.getEmailMessageUpdatedEnabled(preferences)) {
2010                    }
2011                    else {
2012                            return;
2013                    }
2014    
2015                    Company company = companyPersistence.findByPrimaryKey(
2016                            message.getCompanyId());
2017    
2018                    Group group = groupPersistence.findByPrimaryKey(message.getGroupId());
2019    
2020                    String emailAddress = PortalUtil.getUserEmailAddress(
2021                            message.getUserId());
2022                    String fullName = PortalUtil.getUserName(
2023                            message.getUserId(), message.getUserName());
2024    
2025                    if (message.isAnonymous()) {
2026                            emailAddress = StringPool.BLANK;
2027                            fullName = serviceContext.translate("anonymous");
2028                    }
2029    
2030                    MBCategory category = message.getCategory();
2031    
2032                    String categoryName = category.getName();
2033    
2034                    if (category.getCategoryId() ==
2035                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2036    
2037                            categoryName = serviceContext.translate("message-boards-home");
2038    
2039                            categoryName += " - " + group.getDescriptiveName();
2040                    }
2041    
2042                    List<Long> categoryIds = new ArrayList<Long>();
2043    
2044                    categoryIds.add(message.getCategoryId());
2045    
2046                    if (message.getCategoryId() !=
2047                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2048    
2049                            categoryIds.addAll(category.getAncestorCategoryIds());
2050                    }
2051    
2052                    String fromName = MBUtil.getEmailFromName(
2053                            preferences, message.getCompanyId());
2054                    String fromAddress = MBUtil.getEmailFromAddress(
2055                            preferences, message.getCompanyId());
2056    
2057                    String replyToAddress = StringPool.BLANK;
2058    
2059                    if (PropsValues.POP_SERVER_NOTIFICATIONS_ENABLED) {
2060                            replyToAddress = MBUtil.getReplyToAddress(
2061                                    message.getCategoryId(), message.getMessageId(),
2062                                    company.getMx(), fromAddress);
2063                    }
2064    
2065                    String subject = null;
2066                    String body = null;
2067                    String signature = null;
2068    
2069                    if (serviceContext.isCommandUpdate()) {
2070                            subject = MBUtil.getEmailMessageUpdatedSubject(preferences);
2071                            body = MBUtil.getEmailMessageUpdatedBody(preferences);
2072                            signature = MBUtil.getEmailMessageUpdatedSignature(preferences);
2073                    }
2074                    else {
2075                            subject = MBUtil.getEmailMessageAddedSubject(preferences);
2076                            body = MBUtil.getEmailMessageAddedBody(preferences);
2077                            signature = MBUtil.getEmailMessageAddedSignature(preferences);
2078                    }
2079    
2080                    boolean htmlFormat = MBUtil.getEmailHtmlFormat(preferences);
2081    
2082                    if (Validator.isNotNull(signature)) {
2083                            String signatureSeparator = null;
2084    
2085                            if (htmlFormat) {
2086                                    signatureSeparator = "<br />--<br />";
2087                            }
2088                            else {
2089                                    signatureSeparator = "\n--\n";
2090                            }
2091    
2092                            body += signatureSeparator + signature;
2093                    }
2094    
2095                    String messageBody = message.getBody();
2096    
2097                    if (htmlFormat && message.isFormatBBCode()) {
2098                            try {
2099                                    messageBody = BBCodeTranslatorUtil.getHTML(messageBody);
2100    
2101                                    HttpServletRequest request = serviceContext.getRequest();
2102    
2103                                    if (request != null) {
2104                                            ThemeDisplay themeDisplay =
2105                                                    (ThemeDisplay)request.getAttribute(
2106                                                            WebKeys.THEME_DISPLAY);
2107    
2108                                            messageBody = MBUtil.replaceMessageBodyPaths(
2109                                                    themeDisplay, messageBody);
2110                                    }
2111                            }
2112                            catch (Exception e) {
2113                                    _log.error(
2114                                            "Could not parse message " + message.getMessageId() +
2115                                                    " " + e.getMessage());
2116                            }
2117                    }
2118    
2119                    String inReplyTo = null;
2120    
2121                    if (message.getParentMessageId() !=
2122                                    MBMessageConstants.DEFAULT_PARENT_MESSAGE_ID) {
2123    
2124                            inReplyTo = PortalUtil.getMailId(
2125                                    company.getMx(), MBUtil.MESSAGE_POP_PORTLET_PREFIX,
2126                                    message.getCategoryId(), message.getParentMessageId());
2127                    }
2128    
2129                    String entryURL = getMessageURL(message, serviceContext);
2130    
2131                    SubscriptionSender subscriptionSenderPrototype =
2132                            new MBSubscriptionSender();
2133    
2134                    subscriptionSenderPrototype.setBody(body);
2135                    subscriptionSenderPrototype.setBulk(
2136                            PropsValues.MESSAGE_BOARDS_EMAIL_BULK);
2137                    subscriptionSenderPrototype.setCompanyId(message.getCompanyId());
2138                    subscriptionSenderPrototype.setContextAttribute(
2139                            "[$MESSAGE_BODY$]", messageBody, false);
2140                    subscriptionSenderPrototype.setContextAttributes(
2141                            "[$CATEGORY_NAME$]", categoryName, "[$MAILING_LIST_ADDRESS$]",
2142                            replyToAddress, "[$MESSAGE_ID$]", message.getMessageId(),
2143                            "[$MESSAGE_SUBJECT$]", message.getSubject(), "[$MESSAGE_URL$]",
2144                            entryURL, "[$MESSAGE_USER_ADDRESS$]", emailAddress,
2145                            "[$MESSAGE_USER_NAME$]", fullName);
2146                    subscriptionSenderPrototype.setFrom(fromAddress, fromName);
2147                    subscriptionSenderPrototype.setHtmlFormat(htmlFormat);
2148                    subscriptionSenderPrototype.setInReplyTo(inReplyTo);
2149                    subscriptionSenderPrototype.setMailId(
2150                            MBUtil.MESSAGE_POP_PORTLET_PREFIX, message.getCategoryId(),
2151                            message.getMessageId());
2152                    subscriptionSenderPrototype.setPortletId(PortletKeys.MESSAGE_BOARDS);
2153                    subscriptionSenderPrototype.setReplyToAddress(replyToAddress);
2154                    subscriptionSenderPrototype.setScopeGroupId(message.getGroupId());
2155                    subscriptionSenderPrototype.setServiceContext(serviceContext);
2156                    subscriptionSenderPrototype.setSubject(subject);
2157                    subscriptionSenderPrototype.setUserId(message.getUserId());
2158    
2159                    serviceContext.setAttribute("entryURL", entryURL);
2160    
2161                    SubscriptionSender subscriptionSender =
2162                            (SubscriptionSender)SerializableUtil.clone(
2163                                    subscriptionSenderPrototype);
2164    
2165                    subscriptionSender.addPersistedSubscribers(
2166                            MBCategory.class.getName(), message.getGroupId());
2167    
2168                    for (long categoryId : categoryIds) {
2169                            if (categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
2170                                    subscriptionSender.addPersistedSubscribers(
2171                                            MBCategory.class.getName(), categoryId);
2172                            }
2173                    }
2174    
2175                    subscriptionSender.addPersistedSubscribers(
2176                            MBThread.class.getName(), message.getThreadId());
2177    
2178                    subscriptionSender.flushNotificationsAsync();
2179    
2180                    if (!MailingListThreadLocal.isSourceMailingList()) {
2181                            for (long categoryId : categoryIds) {
2182                                    MBSubscriptionSender sourceMailingListSubscriptionSender =
2183                                            (MBSubscriptionSender)SerializableUtil.clone(
2184                                                    subscriptionSenderPrototype);
2185    
2186                                    sourceMailingListSubscriptionSender.setBulk(false);
2187    
2188                                    sourceMailingListSubscriptionSender.addMailingListSubscriber(
2189                                            message.getGroupId(), categoryId);
2190    
2191                                    sourceMailingListSubscriptionSender.flushNotificationsAsync();
2192                            }
2193                    }
2194            }
2195    
2196            protected void pingPingback(
2197                    MBMessage message, ServiceContext serviceContext) {
2198    
2199                    if (!PropsValues.BLOGS_PINGBACK_ENABLED ||
2200                            !message.isAllowPingbacks() || !message.isApproved()) {
2201    
2202                            return;
2203                    }
2204    
2205                    String layoutFullURL = serviceContext.getLayoutFullURL();
2206    
2207                    if (Validator.isNull(layoutFullURL)) {
2208                            return;
2209                    }
2210    
2211                    String sourceUri =
2212                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR +
2213                                    "message_boards/view_message/" + message.getMessageId();
2214    
2215                    Source source = new Source(message.getBody(true));
2216    
2217                    List<StartTag> startTags = source.getAllStartTags("a");
2218    
2219                    for (StartTag startTag : startTags) {
2220                            String targetUri = startTag.getAttributeValue("href");
2221    
2222                            if (Validator.isNotNull(targetUri)) {
2223                                    try {
2224                                            LinkbackProducerUtil.sendPingback(sourceUri, targetUri);
2225                                    }
2226                                    catch (Exception e) {
2227                                            _log.error("Error while sending pingback " + targetUri, e);
2228                                    }
2229                            }
2230                    }
2231            }
2232    
2233            protected void updateAsset(
2234                            long userId, MBMessage message, long[] assetCategoryIds,
2235                            String[] assetTagNames, long[] assetLinkEntryIds,
2236                            boolean assetEntryVisible)
2237                    throws PortalException, SystemException {
2238    
2239                    boolean visible = false;
2240    
2241                    if (assetEntryVisible && message.isApproved() &&
2242                            ((message.getClassNameId() == 0) ||
2243                             (message.getParentMessageId() != 0))) {
2244    
2245                            visible = true;
2246                    }
2247    
2248                    AssetEntry assetEntry = assetEntryLocalService.updateEntry(
2249                            userId, message.getGroupId(), message.getCreateDate(),
2250                            message.getModifiedDate(), message.getWorkflowClassName(),
2251                            message.getMessageId(), message.getUuid(), 0, assetCategoryIds,
2252                            assetTagNames, visible, null, null, null, ContentTypes.TEXT_HTML,
2253                            message.getSubject(), null, null, null, null, 0, 0, null, false);
2254    
2255                    assetLinkLocalService.updateLinks(
2256                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
2257                            AssetLinkConstants.TYPE_RELATED);
2258            }
2259    
2260            protected void updatePriorities(long threadId, double priority)
2261                    throws SystemException {
2262    
2263                    List<MBMessage> messages = mbMessagePersistence.findByThreadId(
2264                            threadId);
2265    
2266                    for (MBMessage message : messages) {
2267                            if (message.getPriority() != priority) {
2268                                    message.setPriority(priority);
2269    
2270                                    mbMessagePersistence.update(message);
2271                            }
2272                    }
2273            }
2274    
2275            protected void updateThreadStatus(
2276                            MBThread thread, MBMessage message, User user, int oldStatus,
2277                            Date modifiedDate)
2278                    throws PortalException, SystemException {
2279    
2280                    MBCategory category = null;
2281    
2282                    int status = message.getStatus();
2283    
2284                    if ((thread.getCategoryId() !=
2285                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
2286                            (thread.getCategoryId() !=
2287                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
2288    
2289                            category = mbCategoryPersistence.findByPrimaryKey(
2290                                    thread.getCategoryId());
2291                    }
2292    
2293                    if ((thread.getRootMessageId() == message.getMessageId()) &&
2294                            (oldStatus != status)) {
2295    
2296                            thread.setModifiedDate(modifiedDate);
2297                            thread.setStatus(status);
2298                            thread.setStatusByUserId(user.getUserId());
2299                            thread.setStatusByUserName(user.getFullName());
2300                            thread.setStatusDate(modifiedDate);
2301                    }
2302    
2303                    if (status == oldStatus) {
2304                            return;
2305                    }
2306    
2307                    if (status == WorkflowConstants.STATUS_APPROVED) {
2308                            if (message.isAnonymous()) {
2309                                    thread.setLastPostByUserId(0);
2310                            }
2311                            else {
2312                                    thread.setLastPostByUserId(message.getUserId());
2313                            }
2314    
2315                            thread.setLastPostDate(modifiedDate);
2316    
2317                            if (category != null) {
2318                                    category.setLastPostDate(modifiedDate);
2319    
2320                                    category = mbCategoryPersistence.update(category);
2321                            }
2322                    }
2323    
2324                    if ((oldStatus == WorkflowConstants.STATUS_APPROVED) ||
2325                            (status == WorkflowConstants.STATUS_APPROVED)) {
2326    
2327                            // Thread
2328    
2329                            MBUtil.updateThreadMessageCount(
2330                                    thread.getCompanyId(), thread.getThreadId());
2331    
2332                            // Category
2333    
2334                            if ((category != null) &&
2335                                    (thread.getRootMessageId() == message.getMessageId())) {
2336    
2337                                    MBUtil.updateCategoryStatistics(
2338                                            category.getCompanyId(), category.getCategoryId());
2339                            }
2340    
2341                            if ((category != null) &&
2342                                    !(thread.getRootMessageId() == message.getMessageId())) {
2343    
2344                                    MBUtil.updateCategoryMessageCount(
2345                                            category.getCompanyId(), category.getCategoryId());
2346                            }
2347                    }
2348    
2349                    // Indexer
2350    
2351                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
2352                            MBThread.class);
2353    
2354                    indexer.reindex(thread);
2355    
2356                    mbThreadPersistence.update(thread);
2357            }
2358    
2359            protected void validate(String subject, String body)
2360                    throws PortalException {
2361    
2362                    if (Validator.isNull(subject) && Validator.isNull(body)) {
2363                            throw new MessageSubjectException();
2364                    }
2365            }
2366    
2367            private static Log _log = LogFactoryUtil.getLog(
2368                    MBMessageLocalServiceImpl.class);
2369    
2370    }