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