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