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