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