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