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