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.QueryDefinition;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.increment.BufferedIncrement;
020    import com.liferay.portal.kernel.increment.NumberIncrement;
021    import com.liferay.portal.kernel.json.JSONFactoryUtil;
022    import com.liferay.portal.kernel.json.JSONObject;
023    import com.liferay.portal.kernel.repository.model.FileEntry;
024    import com.liferay.portal.kernel.repository.model.Folder;
025    import com.liferay.portal.kernel.search.Field;
026    import com.liferay.portal.kernel.search.Hits;
027    import com.liferay.portal.kernel.search.Indexer;
028    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.search.Sort;
031    import com.liferay.portal.kernel.social.SocialActivityManagerUtil;
032    import com.liferay.portal.kernel.systemevent.SystemEvent;
033    import com.liferay.portal.kernel.util.StringUtil;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.kernel.workflow.WorkflowConstants;
036    import com.liferay.portal.model.Group;
037    import com.liferay.portal.model.ResourceConstants;
038    import com.liferay.portal.model.SystemEventConstants;
039    import com.liferay.portal.model.User;
040    import com.liferay.portal.portletfilerepository.PortletFileRepositoryUtil;
041    import com.liferay.portal.service.ServiceContext;
042    import com.liferay.portlet.asset.model.AssetEntry;
043    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
044    import com.liferay.portlet.exportimport.lar.ExportImportThreadLocal;
045    import com.liferay.portlet.messageboards.constants.MBConstants;
046    import com.liferay.portlet.messageboards.exception.NoSuchCategoryException;
047    import com.liferay.portlet.messageboards.exception.SplitThreadException;
048    import com.liferay.portlet.messageboards.model.MBCategory;
049    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
050    import com.liferay.portlet.messageboards.model.MBMessage;
051    import com.liferay.portlet.messageboards.model.MBMessageDisplay;
052    import com.liferay.portlet.messageboards.model.MBThread;
053    import com.liferay.portlet.messageboards.model.MBThreadConstants;
054    import com.liferay.portlet.messageboards.model.MBTreeWalker;
055    import com.liferay.portlet.messageboards.service.base.MBThreadLocalServiceBaseImpl;
056    import com.liferay.portlet.messageboards.util.MBUtil;
057    import com.liferay.portlet.social.model.SocialActivityConstants;
058    import com.liferay.portlet.trash.model.TrashEntry;
059    import com.liferay.portlet.trash.model.TrashVersion;
060    
061    import java.util.ArrayList;
062    import java.util.Date;
063    import java.util.HashSet;
064    import java.util.List;
065    import java.util.Set;
066    
067    /**
068     * @author Brian Wing Shun Chan
069     * @author Shuyang Zhou
070     */
071    public class MBThreadLocalServiceImpl extends MBThreadLocalServiceBaseImpl {
072    
073            @Override
074            public MBThread addThread(
075                            long categoryId, MBMessage message, ServiceContext serviceContext)
076                    throws PortalException {
077    
078                    // Thread
079    
080                    long threadId = message.getThreadId();
081    
082                    if (!message.isRoot() || (threadId <= 0)) {
083                            threadId = counterLocalService.increment();
084                    }
085    
086                    MBThread thread = mbThreadPersistence.create(threadId);
087    
088                    thread.setUuid(serviceContext.getUuid());
089                    thread.setGroupId(message.getGroupId());
090                    thread.setCompanyId(message.getCompanyId());
091                    thread.setUserId(message.getUserId());
092                    thread.setUserName(message.getUserName());
093                    thread.setCategoryId(categoryId);
094                    thread.setRootMessageId(message.getMessageId());
095                    thread.setRootMessageUserId(message.getUserId());
096    
097                    if (message.isAnonymous()) {
098                            thread.setLastPostByUserId(0);
099                    }
100                    else {
101                            thread.setLastPostByUserId(message.getUserId());
102                    }
103    
104                    thread.setLastPostDate(message.getModifiedDate());
105    
106                    if (message.getPriority() != MBThreadConstants.PRIORITY_NOT_GIVEN) {
107                            thread.setPriority(message.getPriority());
108                    }
109    
110                    thread.setStatus(message.getStatus());
111                    thread.setStatusByUserId(message.getStatusByUserId());
112                    thread.setStatusByUserName(message.getStatusByUserName());
113                    thread.setStatusDate(message.getStatusDate());
114    
115                    mbThreadPersistence.update(thread);
116    
117                    // Asset
118    
119                    if (categoryId >= 0) {
120                            assetEntryLocalService.updateEntry(
121                                    message.getUserId(), message.getGroupId(),
122                                    thread.getStatusDate(), thread.getLastPostDate(),
123                                    MBThread.class.getName(), thread.getThreadId(),
124                                    thread.getUuid(), 0, new long[0], new String[0], false, null,
125                                    null, null, null, String.valueOf(thread.getRootMessageId()),
126                                    null, null, null, null, 0, 0,
127                                    serviceContext.getAssetPriority());
128                    }
129    
130                    return thread;
131            }
132    
133            @Override
134            public void deleteThread(long threadId) throws PortalException {
135                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
136    
137                    mbThreadLocalService.deleteThread(thread);
138            }
139    
140            @Override
141            @SystemEvent(
142                    action = SystemEventConstants.ACTION_SKIP,
143                    type = SystemEventConstants.TYPE_DELETE
144            )
145            public void deleteThread(MBThread thread) throws PortalException {
146                    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(
147                            thread.getRootMessageId());
148    
149                    // Indexer
150    
151                    Indexer<MBMessage> messageIndexer =
152                            IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);
153    
154                    // Attachments
155    
156                    long folderId = thread.getAttachmentsFolderId();
157    
158                    if (folderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
159                            PortletFileRepositoryUtil.deletePortletFolder(folderId);
160                    }
161    
162                    // Subscriptions
163    
164                    subscriptionLocalService.deleteSubscriptions(
165                            thread.getCompanyId(), MBThread.class.getName(),
166                            thread.getThreadId());
167    
168                    // Thread flags
169    
170                    mbThreadFlagLocalService.deleteThreadFlagsByThreadId(
171                            thread.getThreadId());
172    
173                    // Messages
174    
175                    List<MBMessage> messages = mbMessagePersistence.findByThreadId(
176                            thread.getThreadId());
177    
178                    for (MBMessage message : messages) {
179    
180                            // Ratings
181    
182                            ratingsStatsLocalService.deleteStats(
183                                    message.getWorkflowClassName(), message.getMessageId());
184    
185                            // Asset
186    
187                            assetEntryLocalService.deleteEntry(
188                                    message.getWorkflowClassName(), message.getMessageId());
189    
190                            // Resources
191    
192                            if (!message.isDiscussion()) {
193                                    resourceLocalService.deleteResource(
194                                            message.getCompanyId(), message.getWorkflowClassName(),
195                                            ResourceConstants.SCOPE_INDIVIDUAL, message.getMessageId());
196                            }
197    
198                            // Message
199    
200                            mbMessagePersistence.remove(message);
201    
202                            // Indexer
203    
204                            messageIndexer.delete(message);
205    
206                            // Statistics
207    
208                            if (!message.isDiscussion()) {
209                                    mbStatsUserLocalService.updateStatsUser(
210                                            message.getGroupId(), message.getUserId());
211                            }
212    
213                            // Workflow
214    
215                            workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
216                                    message.getCompanyId(), message.getGroupId(),
217                                    message.getWorkflowClassName(), message.getMessageId());
218                    }
219    
220                    // Category
221    
222                    if ((rootMessage.getCategoryId() !=
223                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
224                            (rootMessage.getCategoryId() !=
225                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
226    
227                            try {
228                                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
229                                            thread.getCategoryId());
230    
231                                    MBUtil.updateCategoryStatistics(category.getCategoryId());
232                            }
233                            catch (NoSuchCategoryException nsce) {
234                                    if (!thread.isInTrash()) {
235                                            throw nsce;
236                                    }
237                            }
238                    }
239    
240                    // Thread Asset
241    
242                    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(
243                            MBThread.class.getName(), thread.getThreadId());
244    
245                    if (assetEntry != null) {
246                            assetEntry.setTitle(rootMessage.getSubject());
247    
248                            assetEntryLocalService.updateAssetEntry(assetEntry);
249                    }
250    
251                    assetEntryLocalService.deleteEntry(
252                            MBThread.class.getName(), thread.getThreadId());
253    
254                    // Trash
255    
256                    if (thread.isInTrashExplicitly()) {
257                            trashEntryLocalService.deleteEntry(
258                                    MBThread.class.getName(), thread.getThreadId());
259                    }
260                    else {
261                            trashVersionLocalService.deleteTrashVersion(
262                                    MBThread.class.getName(), thread.getThreadId());
263                    }
264    
265                    // Indexer
266    
267                    Indexer<MBThread> threadIndexer =
268                            IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);
269    
270                    threadIndexer.delete(thread);
271    
272                    // Thread
273    
274                    mbThreadPersistence.remove(thread);
275            }
276    
277            @Override
278            public void deleteThreads(long groupId, long categoryId)
279                    throws PortalException {
280    
281                    deleteThreads(groupId, categoryId, true);
282            }
283    
284            @Override
285            public void deleteThreads(
286                            long groupId, long categoryId, boolean includeTrashedEntries)
287                    throws PortalException {
288    
289                    List<MBThread> threads = mbThreadPersistence.findByG_C(
290                            groupId, categoryId);
291    
292                    for (MBThread thread : threads) {
293                            if (includeTrashedEntries || !thread.isInTrashExplicitly()) {
294                                    mbThreadLocalService.deleteThread(thread);
295                            }
296                    }
297    
298                    if (mbThreadPersistence.countByGroupId(groupId) == 0) {
299                            PortletFileRepositoryUtil.deletePortletRepository(
300                                    groupId, MBConstants.SERVICE_NAME);
301                    }
302            }
303    
304            @Override
305            public MBThread fetchThread(long threadId) {
306                    return mbThreadPersistence.fetchByPrimaryKey(threadId);
307            }
308    
309            @Override
310            public int getCategoryThreadsCount(
311                    long groupId, long categoryId, int status) {
312    
313                    if (status == WorkflowConstants.STATUS_ANY) {
314                            return mbThreadPersistence.countByG_C(groupId, categoryId);
315                    }
316                    else {
317                            return mbThreadPersistence.countByG_C_S(
318                                    groupId, categoryId, status);
319                    }
320            }
321    
322            /**
323             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreads(long,
324             *             QueryDefinition)}
325             */
326            @Deprecated
327            @Override
328            public List<MBThread> getGroupThreads(
329                    long groupId, int status, int start, int end) {
330    
331                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
332                            status, start, end, null);
333    
334                    return getGroupThreads(groupId, queryDefinition);
335            }
336    
337            @Override
338            public List<MBThread> getGroupThreads(
339                    long groupId, long userId, boolean subscribed, boolean includeAnonymous,
340                    QueryDefinition<MBThread> queryDefinition) {
341    
342                    if (userId <= 0) {
343                            return getGroupThreads(groupId, queryDefinition);
344                    }
345    
346                    if (subscribed) {
347                            return mbThreadFinder.findByS_G_U_C(
348                                    groupId, userId, null, queryDefinition);
349                    }
350                    else {
351                            if (includeAnonymous) {
352                                    return mbThreadFinder.findByG_U_C(
353                                            groupId, userId, null, queryDefinition);
354                            }
355                            else {
356                                    return mbThreadFinder.findByG_U_C_A(
357                                            groupId, userId, null, false, queryDefinition);
358                            }
359                    }
360            }
361    
362            @Override
363            public List<MBThread> getGroupThreads(
364                    long groupId, long userId, boolean subscribed,
365                    QueryDefinition<MBThread> queryDefinition) {
366    
367                    return getGroupThreads(
368                            groupId, userId, subscribed, true, queryDefinition);
369            }
370    
371            /**
372             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreads(long, long,
373             *             boolean, boolean, QueryDefinition)}
374             */
375            @Deprecated
376            @Override
377            public List<MBThread> getGroupThreads(
378                    long groupId, long userId, int status, boolean subscribed,
379                    boolean includeAnonymous, int start, int end) {
380    
381                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
382                            status, start, end, null);
383    
384                    return getGroupThreads(
385                            groupId, userId, subscribed, includeAnonymous, queryDefinition);
386            }
387    
388            /**
389             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreads(long, long,
390             *             boolean, QueryDefinition)}
391             */
392            @Deprecated
393            @Override
394            public List<MBThread> getGroupThreads(
395                    long groupId, long userId, int status, boolean subscribed, int start,
396                    int end) {
397    
398                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
399                            status, start, end, null);
400    
401                    return getGroupThreads(groupId, userId, subscribed, queryDefinition);
402            }
403    
404            /**
405             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreads(long, long,
406             *             QueryDefinition)}
407             */
408            @Deprecated
409            @Override
410            public List<MBThread> getGroupThreads(
411                    long groupId, long userId, int status, int start, int end) {
412    
413                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
414                            status, start, end, null);
415    
416                    return getGroupThreads(groupId, userId, false, queryDefinition);
417            }
418    
419            @Override
420            public List<MBThread> getGroupThreads(
421                    long groupId, long userId, QueryDefinition<MBThread> queryDefinition) {
422    
423                    return getGroupThreads(groupId, userId, false, queryDefinition);
424            }
425    
426            @Override
427            public List<MBThread> getGroupThreads(
428                    long groupId, QueryDefinition<MBThread> queryDefinition) {
429    
430                    if (queryDefinition.isExcludeStatus()) {
431                            return mbThreadPersistence.findByG_NotC_NotS(
432                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
433                                    queryDefinition.getStatus(), queryDefinition.getStart(),
434                                    queryDefinition.getEnd());
435                    }
436                    else {
437                            return mbThreadPersistence.findByG_NotC_S(
438                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
439                                    queryDefinition.getStatus(), queryDefinition.getStart(),
440                                    queryDefinition.getEnd());
441                    }
442            }
443    
444            /**
445             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreadsCount(long,
446             *             QueryDefinition)}
447             */
448            @Deprecated
449            @Override
450            public int getGroupThreadsCount(long groupId, int status) {
451                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
452                            status);
453    
454                    return getGroupThreadsCount(groupId, queryDefinition);
455            }
456    
457            @Override
458            public int getGroupThreadsCount(
459                    long groupId, long userId, boolean subscribed, boolean includeAnonymous,
460                    QueryDefinition<MBThread> queryDefinition) {
461    
462                    if (userId <= 0) {
463                            return getGroupThreadsCount(groupId, queryDefinition);
464                    }
465    
466                    if (subscribed) {
467                            return mbThreadFinder.countByS_G_U_C(
468                                    groupId, userId, null, queryDefinition);
469                    }
470                    else {
471                            if (includeAnonymous) {
472                                    return mbThreadFinder.countByG_U_C(
473                                            groupId, userId, null, queryDefinition);
474                            }
475                            else {
476                                    return mbThreadFinder.countByG_U_C_A(
477                                            groupId, userId, null, false, queryDefinition);
478                            }
479                    }
480            }
481    
482            @Override
483            public int getGroupThreadsCount(
484                    long groupId, long userId, boolean subscribed,
485                    QueryDefinition<MBThread> queryDefinition) {
486    
487                    return getGroupThreadsCount(
488                            groupId, userId, subscribed, true, queryDefinition);
489            }
490    
491            /**
492             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreadsCount(long,
493             *             long, QueryDefinition)}
494             */
495            @Deprecated
496            @Override
497            public int getGroupThreadsCount(long groupId, long userId, int status) {
498                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
499                            status);
500    
501                    return getGroupThreadsCount(groupId, userId, false, queryDefinition);
502            }
503    
504            /**
505             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreadsCount(long,
506             *             long, boolean, QueryDefinition)}
507             */
508            @Deprecated
509            @Override
510            public int getGroupThreadsCount(
511                    long groupId, long userId, int status, boolean subscribed) {
512    
513                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
514                            status);
515    
516                    return getGroupThreadsCount(
517                            groupId, userId, subscribed, true, queryDefinition);
518            }
519    
520            /**
521             * @deprecated As of 6.2.0, replaced by {@link #getGroupThreadsCount(long,
522             *             long, boolean, boolean, QueryDefinition)}
523             */
524            @Deprecated
525            @Override
526            public int getGroupThreadsCount(
527                    long groupId, long userId, int status, boolean subscribed,
528                    boolean includeAnonymous) {
529    
530                    QueryDefinition<MBThread> queryDefinition = new QueryDefinition<>(
531                            status);
532    
533                    return getGroupThreadsCount(
534                            groupId, userId, subscribed, includeAnonymous, queryDefinition);
535            }
536    
537            @Override
538            public int getGroupThreadsCount(
539                    long groupId, long userId, QueryDefinition<MBThread> queryDefinition) {
540    
541                    return getGroupThreadsCount(groupId, userId, false, queryDefinition);
542            }
543    
544            @Override
545            public int getGroupThreadsCount(
546                    long groupId, QueryDefinition<MBThread> queryDefinition) {
547    
548                    if (queryDefinition.isExcludeStatus()) {
549                            return mbThreadPersistence.countByG_NotC_NotS(
550                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
551                                    queryDefinition.getStatus());
552                    }
553                    else {
554                            return mbThreadPersistence.countByG_NotC_S(
555                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
556                                    queryDefinition.getStatus());
557                    }
558            }
559    
560            @Override
561            public List<MBThread> getNoAssetThreads() {
562                    return mbThreadFinder.findByNoAssets();
563            }
564    
565            @Override
566            public List<MBThread> getPriorityThreads(long categoryId, double priority)
567                    throws PortalException {
568    
569                    return getPriorityThreads(categoryId, priority, false);
570            }
571    
572            @Override
573            public List<MBThread> getPriorityThreads(
574                            long categoryId, double priority, boolean inherit)
575                    throws PortalException {
576    
577                    if (!inherit) {
578                            return mbThreadPersistence.findByC_P(categoryId, priority);
579                    }
580    
581                    List<MBThread> threads = new ArrayList<>();
582    
583                    while ((categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
584                               (categoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
585    
586                            threads.addAll(
587                                    0, mbThreadPersistence.findByC_P(categoryId, priority));
588    
589                            MBCategory category = mbCategoryPersistence.findByPrimaryKey(
590                                    categoryId);
591    
592                            categoryId = category.getParentCategoryId();
593                    }
594    
595                    return threads;
596            }
597    
598            @Override
599            public MBThread getThread(long threadId) throws PortalException {
600                    return mbThreadPersistence.findByPrimaryKey(threadId);
601            }
602    
603            @Override
604            public List<MBThread> getThreads(
605                    long groupId, long categoryId, int status, int start, int end) {
606    
607                    if (status == WorkflowConstants.STATUS_ANY) {
608                            return mbThreadPersistence.findByG_C(
609                                    groupId, categoryId, start, end);
610                    }
611                    else {
612                            return mbThreadPersistence.findByG_C_S(
613                                    groupId, categoryId, status, start, end);
614                    }
615            }
616    
617            @Override
618            public int getThreadsCount(long groupId, long categoryId, int status) {
619                    if (status == WorkflowConstants.STATUS_ANY) {
620                            return mbThreadPersistence.countByG_C(groupId, categoryId);
621                    }
622                    else {
623                            return mbThreadPersistence.countByG_C_S(
624                                    groupId, categoryId, status);
625                    }
626            }
627    
628            @Override
629            public boolean hasAnswerMessage(long threadId) {
630                    int count = mbMessagePersistence.countByT_A(threadId, true);
631    
632                    if (count > 0) {
633                            return true;
634                    }
635                    else {
636                            return false;
637                    }
638            }
639    
640            @BufferedIncrement(
641                    configuration = "MBThread", incrementClass = NumberIncrement.class
642            )
643            @Override
644            public void incrementViewCounter(long threadId, int increment)
645                    throws PortalException {
646    
647                    if (ExportImportThreadLocal.isImportInProcess()) {
648                            return;
649                    }
650    
651                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
652    
653                    thread.setModifiedDate(thread.getModifiedDate());
654                    thread.setViewCount(thread.getViewCount() + increment);
655    
656                    mbThreadPersistence.update(thread);
657            }
658    
659            @Override
660            public void moveDependentsToTrash(
661                            long groupId, long threadId, long trashEntryId)
662                    throws PortalException {
663    
664                    Set<Long> userIds = new HashSet<>();
665    
666                    MBThread thread = mbThreadLocalService.getThread(threadId);
667    
668                    List<MBMessage> messages = mbMessageLocalService.getThreadMessages(
669                            threadId, WorkflowConstants.STATUS_ANY);
670    
671                    for (MBMessage message : messages) {
672    
673                            // Message
674    
675                            if (message.isDiscussion()) {
676                                    continue;
677                            }
678    
679                            int oldStatus = message.getStatus();
680    
681                            message.setStatus(WorkflowConstants.STATUS_IN_TRASH);
682    
683                            mbMessagePersistence.update(message);
684    
685                            userIds.add(message.getUserId());
686    
687                            // Trash
688    
689                            int status = oldStatus;
690    
691                            if (oldStatus == WorkflowConstants.STATUS_PENDING) {
692                                    status = WorkflowConstants.STATUS_DRAFT;
693                            }
694    
695                            if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
696                                    trashVersionLocalService.addTrashVersion(
697                                            trashEntryId, MBMessage.class.getName(),
698                                            message.getMessageId(), status, null);
699                            }
700    
701                            // Asset
702    
703                            if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
704                                    assetEntryLocalService.updateVisible(
705                                            MBMessage.class.getName(), message.getMessageId(), false);
706                            }
707    
708                            // Attachments
709    
710                            for (FileEntry fileEntry : message.getAttachmentsFileEntries()) {
711                                    PortletFileRepositoryUtil.movePortletFileEntryToTrash(
712                                            thread.getStatusByUserId(), fileEntry.getFileEntryId());
713                            }
714    
715                            // Indexer
716    
717                            Indexer<MBMessage> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
718                                    MBMessage.class);
719    
720                            indexer.reindex(message);
721    
722                            // Workflow
723    
724                            if (oldStatus == WorkflowConstants.STATUS_PENDING) {
725                                    workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
726                                            message.getCompanyId(), message.getGroupId(),
727                                            MBMessage.class.getName(), message.getMessageId());
728                            }
729                    }
730    
731                    // Statistics
732    
733                    for (long userId : userIds) {
734                            mbStatsUserLocalService.updateStatsUser(groupId, userId);
735                    }
736            }
737    
738            @Override
739            public MBThread moveThread(long groupId, long categoryId, long threadId)
740                    throws PortalException {
741    
742                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
743    
744                    long oldCategoryId = thread.getCategoryId();
745    
746                    MBCategory oldCategory = null;
747    
748                    if (oldCategoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
749                            oldCategory = mbCategoryPersistence.fetchByPrimaryKey(
750                                    oldCategoryId);
751                    }
752    
753                    MBCategory category = null;
754    
755                    if (categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
756                            category = mbCategoryPersistence.fetchByPrimaryKey(categoryId);
757                    }
758    
759                    // Thread
760    
761                    thread.setCategoryId(categoryId);
762    
763                    mbThreadPersistence.update(thread);
764    
765                    // Messages
766    
767                    List<MBMessage> messages = mbMessagePersistence.findByG_C_T(
768                            groupId, oldCategoryId, thread.getThreadId());
769    
770                    for (MBMessage message : messages) {
771                            message.setCategoryId(categoryId);
772    
773                            mbMessagePersistence.update(message);
774    
775                            // Indexer
776    
777                            if (!message.isDiscussion()) {
778                                    Indexer<MBMessage> indexer =
779                                            IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);
780    
781                                    indexer.reindex(message);
782                            }
783                    }
784    
785                    // Category
786    
787                    if ((oldCategory != null) && (categoryId != oldCategoryId)) {
788                            MBUtil.updateCategoryStatistics(oldCategory.getCategoryId());
789                    }
790    
791                    if ((category != null) && (categoryId != oldCategoryId)) {
792                            MBUtil.updateCategoryStatistics(category.getCategoryId());
793                    }
794    
795                    // Indexer
796    
797                    Indexer<MBThread> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
798                            MBThread.class);
799    
800                    indexer.reindex(thread);
801    
802                    return thread;
803            }
804    
805            @Override
806            public MBThread moveThreadFromTrash(
807                            long userId, long categoryId, long threadId)
808                    throws PortalException {
809    
810                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
811    
812                    if (thread.isInTrashExplicitly()) {
813                            restoreThreadFromTrash(userId, threadId);
814                    }
815                    else {
816    
817                            // Thread
818    
819                            TrashVersion trashVersion = trashVersionLocalService.fetchVersion(
820                                    MBThread.class.getName(), thread.getThreadId());
821    
822                            int status = WorkflowConstants.STATUS_APPROVED;
823    
824                            if (trashVersion != null) {
825                                    status = trashVersion.getStatus();
826                            }
827    
828                            updateStatus(userId, threadId, status);
829    
830                            // Trash
831    
832                            if (trashVersion != null) {
833                                    trashVersionLocalService.deleteTrashVersion(trashVersion);
834                            }
835    
836                            // Messages
837    
838                            restoreDependentsFromTrash(thread.getGroupId(), threadId);
839                    }
840    
841                    return moveThread(thread.getGroupId(), categoryId, threadId);
842            }
843    
844            @Override
845            public void moveThreadsToTrash(long groupId, long userId)
846                    throws PortalException {
847    
848                    List<MBThread> threads = mbThreadPersistence.findByGroupId(groupId);
849    
850                    for (MBThread thread : threads) {
851                            moveThreadToTrash(userId, thread);
852                    }
853            }
854    
855            @Override
856            public MBThread moveThreadToTrash(long userId, long threadId)
857                    throws PortalException {
858    
859                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
860    
861                    return moveThreadToTrash(userId, thread);
862            }
863    
864            @Override
865            public MBThread moveThreadToTrash(long userId, MBThread thread)
866                    throws PortalException {
867    
868                    // Thread
869    
870                    if (thread.getCategoryId() ==
871                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
872    
873                            return thread;
874                    }
875    
876                    int oldStatus = thread.getStatus();
877    
878                    if (oldStatus == WorkflowConstants.STATUS_PENDING) {
879                            thread.setStatus(WorkflowConstants.STATUS_DRAFT);
880    
881                            mbThreadPersistence.update(thread);
882                    }
883    
884                    thread = updateStatus(
885                            userId, thread.getThreadId(), WorkflowConstants.STATUS_IN_TRASH);
886    
887                    // Trash
888    
889                    TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
890                            userId, thread.getGroupId(), MBThread.class.getName(),
891                            thread.getThreadId(), thread.getUuid(), null, oldStatus, null,
892                            null);
893    
894                    // Messages
895    
896                    moveDependentsToTrash(
897                            thread.getGroupId(), thread.getThreadId(), trashEntry.getEntryId());
898    
899                    // Social
900    
901                    MBMessage message = mbMessageLocalService.getMBMessage(
902                            thread.getRootMessageId());
903    
904                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
905    
906                    extraDataJSONObject.put("rootMessageId", thread.getRootMessageId());
907                    extraDataJSONObject.put("title", message.getSubject());
908    
909                    SocialActivityManagerUtil.addActivity(
910                            userId, thread, SocialActivityConstants.TYPE_MOVE_TO_TRASH,
911                            extraDataJSONObject.toString(), 0);
912    
913                    return thread;
914            }
915    
916            @Override
917            public void restoreDependentsFromTrash(long groupId, long threadId)
918                    throws PortalException {
919    
920                    Set<Long> userIds = new HashSet<>();
921    
922                    MBThread thread = mbThreadLocalService.getThread(threadId);
923    
924                    List<MBMessage> messages = mbMessageLocalService.getThreadMessages(
925                            threadId, WorkflowConstants.STATUS_ANY);
926    
927                    for (MBMessage message : messages) {
928    
929                            // Message
930    
931                            if (message.isDiscussion()) {
932                                    continue;
933                            }
934    
935                            TrashVersion trashVersion = trashVersionLocalService.fetchVersion(
936                                    MBMessage.class.getName(), message.getMessageId());
937    
938                            int oldStatus = WorkflowConstants.STATUS_APPROVED;
939    
940                            if (trashVersion != null) {
941                                    oldStatus = trashVersion.getStatus();
942                            }
943    
944                            message.setStatus(oldStatus);
945    
946                            mbMessagePersistence.update(message);
947    
948                            userIds.add(message.getUserId());
949    
950                            // Trash
951    
952                            if (trashVersion != null) {
953                                    trashVersionLocalService.deleteTrashVersion(trashVersion);
954                            }
955    
956                            // Asset
957    
958                            if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
959                                    assetEntryLocalService.updateVisible(
960                                            MBMessage.class.getName(), message.getMessageId(), true);
961                            }
962    
963                            // Attachments
964    
965                            for (FileEntry fileEntry : message.getAttachmentsFileEntries()) {
966                                    PortletFileRepositoryUtil.restorePortletFileEntryFromTrash(
967                                            thread.getStatusByUserId(), fileEntry.getFileEntryId());
968                            }
969    
970                            // Indexer
971    
972                            Indexer<MBMessage> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
973                                    MBMessage.class);
974    
975                            indexer.reindex(message);
976                    }
977    
978                    // Statistics
979    
980                    for (long userId : userIds) {
981                            mbStatsUserLocalService.updateStatsUser(groupId, userId);
982                    }
983            }
984    
985            /**
986             * @deprecated As of 7.0.0, replaced by {@link
987             *             #restoreDependentsFromTrash(long, long)}
988             */
989            @Deprecated
990            @Override
991            public void restoreDependentsFromTrash(
992                            long groupId, long threadId, long trashEntryId)
993                    throws PortalException {
994    
995                    restoreDependentsFromTrash(groupId, threadId);
996            }
997    
998            @Override
999            public void restoreThreadFromTrash(long userId, long threadId)
1000                    throws PortalException {
1001    
1002                    // Thread
1003    
1004                    MBThread thread = getThread(threadId);
1005    
1006                    if (thread.getCategoryId() ==
1007                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
1008    
1009                            return;
1010                    }
1011    
1012                    TrashEntry trashEntry = trashEntryLocalService.getEntry(
1013                            MBThread.class.getName(), threadId);
1014    
1015                    updateStatus(userId, threadId, trashEntry.getStatus());
1016    
1017                    // Messages
1018    
1019                    restoreDependentsFromTrash(thread.getGroupId(), threadId);
1020    
1021                    // Trash
1022    
1023                    trashEntryLocalService.deleteEntry(trashEntry.getEntryId());
1024    
1025                    // Social
1026    
1027                    MBMessage message = mbMessageLocalService.getMBMessage(
1028                            thread.getRootMessageId());
1029    
1030                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
1031    
1032                    extraDataJSONObject.put("rootMessageId", thread.getRootMessageId());
1033                    extraDataJSONObject.put("title", message.getSubject());
1034    
1035                    SocialActivityManagerUtil.addActivity(
1036                            userId, thread, SocialActivityConstants.TYPE_RESTORE_FROM_TRASH,
1037                            extraDataJSONObject.toString(), 0);
1038            }
1039    
1040            @Override
1041            public Hits search(
1042                            long groupId, long userId, long creatorUserId, int status,
1043                            int start, int end)
1044                    throws PortalException {
1045    
1046                    return search(groupId, userId, creatorUserId, 0, 0, status, start, end);
1047            }
1048    
1049            @Override
1050            public Hits search(
1051                            long groupId, long userId, long creatorUserId, long startDate,
1052                            long endDate, int status, int start, int end)
1053                    throws PortalException {
1054    
1055                    Indexer<MBThread> indexer = IndexerRegistryUtil.getIndexer(
1056                            MBThread.class.getName());
1057    
1058                    SearchContext searchContext = new SearchContext();
1059    
1060                    searchContext.setAttribute(Field.STATUS, status);
1061    
1062                    if (endDate > 0) {
1063                            searchContext.setAttribute("endDate", endDate);
1064                    }
1065    
1066                    searchContext.setAttribute("paginationType", "none");
1067    
1068                    if (creatorUserId > 0) {
1069                            searchContext.setAttribute(
1070                                    "participantUserId", String.valueOf(creatorUserId));
1071                    }
1072    
1073                    if (startDate > 0) {
1074                            searchContext.setAttribute("startDate", startDate);
1075                    }
1076    
1077                    Group group = groupLocalService.getGroup(groupId);
1078    
1079                    searchContext.setCompanyId(group.getCompanyId());
1080    
1081                    searchContext.setEnd(end);
1082                    searchContext.setGroupIds(new long[] {groupId});
1083                    searchContext.setSorts(new Sort("lastPostDate", true));
1084                    searchContext.setStart(start);
1085                    searchContext.setUserId(userId);
1086    
1087                    return indexer.search(searchContext);
1088            }
1089    
1090            @Override
1091            public MBThread splitThread(
1092                            long userId, long messageId, String subject,
1093                            ServiceContext serviceContext)
1094                    throws PortalException {
1095    
1096                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
1097    
1098                    if (message.isRoot()) {
1099                            throw new SplitThreadException(
1100                                    "Unable to split message " + messageId +
1101                                            " because it is a root message");
1102                    }
1103    
1104                    MBCategory category = message.getCategory();
1105                    MBThread oldThread = message.getThread();
1106                    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(
1107                            oldThread.getRootMessageId());
1108                    long oldAttachmentsFolderId = message.getAttachmentsFolderId();
1109    
1110                    // Message flags
1111    
1112                    mbMessageLocalService.updateAnswer(message, false, true);
1113    
1114                    // Create new thread
1115    
1116                    MBThread thread = addThread(
1117                            message.getCategoryId(), message, serviceContext);
1118    
1119                    mbThreadPersistence.update(oldThread);
1120    
1121                    // Update messages
1122    
1123                    if (Validator.isNotNull(subject)) {
1124                            MBMessageDisplay messageDisplay =
1125                                    mbMessageLocalService.getMessageDisplay(
1126                                            userId, messageId, WorkflowConstants.STATUS_ANY,
1127                                            MBThreadConstants.THREAD_VIEW_TREE, false);
1128    
1129                            MBTreeWalker treeWalker = messageDisplay.getTreeWalker();
1130    
1131                            List<MBMessage> messages = treeWalker.getMessages();
1132    
1133                            int[] range = treeWalker.getChildrenRange(message);
1134    
1135                            for (int i = range[0]; i < range[1]; i++) {
1136                                    MBMessage curMessage = messages.get(i);
1137    
1138                                    String oldSubject = message.getSubject();
1139                                    String curSubject = curMessage.getSubject();
1140    
1141                                    if (oldSubject.startsWith("RE: ")) {
1142                                            curSubject = StringUtil.replace(
1143                                                    curSubject, rootMessage.getSubject(), subject);
1144                                    }
1145                                    else {
1146                                            curSubject = StringUtil.replace(
1147                                                    curSubject, oldSubject, subject);
1148                                    }
1149    
1150                                    curMessage.setSubject(curSubject);
1151    
1152                                    mbMessagePersistence.update(curMessage);
1153                            }
1154    
1155                            message.setSubject(subject);
1156                    }
1157    
1158                    message.setThreadId(thread.getThreadId());
1159                    message.setRootMessageId(thread.getRootMessageId());
1160                    message.setParentMessageId(0);
1161    
1162                    mbMessagePersistence.update(message);
1163    
1164                    // Attachments
1165    
1166                    moveAttachmentsFolders(
1167                            message, oldAttachmentsFolderId, oldThread, thread, serviceContext);
1168    
1169                    // Indexer
1170    
1171                    if (!message.isDiscussion()) {
1172                            Indexer<MBMessage> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1173                                    MBMessage.class);
1174    
1175                            indexer.reindex(message);
1176                    }
1177    
1178                    // Update children
1179    
1180                    moveChildrenMessages(message, category, oldThread.getThreadId());
1181    
1182                    // Update new thread
1183    
1184                    MBUtil.updateThreadMessageCount(thread.getThreadId());
1185    
1186                    // Update old thread
1187    
1188                    MBUtil.updateThreadMessageCount(oldThread.getThreadId());
1189    
1190                    // Category
1191    
1192                    if ((message.getCategoryId() !=
1193                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
1194                            (message.getCategoryId() !=
1195                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
1196    
1197                            MBUtil.updateCategoryThreadCount(category.getCategoryId());
1198                    }
1199    
1200                    // Indexer
1201    
1202                    Indexer<MBThread> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1203                            MBThread.class);
1204    
1205                    indexer.reindex(oldThread);
1206    
1207                    indexer.reindex(message.getThread());
1208    
1209                    return thread;
1210            }
1211    
1212            @Override
1213            public MBThread updateMessageCount(long threadId) {
1214                    MBThread mbThread = mbThreadPersistence.fetchByPrimaryKey(threadId);
1215    
1216                    if (mbThread == null) {
1217                            return null;
1218                    }
1219    
1220                    int messageCount = mbMessageLocalService.getThreadMessagesCount(
1221                            threadId, WorkflowConstants.STATUS_APPROVED);
1222    
1223                    mbThread.setMessageCount(messageCount);
1224    
1225                    return mbThreadPersistence.update(mbThread);
1226            }
1227    
1228            @Override
1229            public void updateQuestion(long threadId, boolean question)
1230                    throws PortalException {
1231    
1232                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
1233    
1234                    if (thread.isQuestion() == question) {
1235                            return;
1236                    }
1237    
1238                    thread.setQuestion(question);
1239    
1240                    mbThreadPersistence.update(thread);
1241    
1242                    if (!question) {
1243                            MBMessage message = mbMessagePersistence.findByPrimaryKey(
1244                                    thread.getRootMessageId());
1245    
1246                            mbMessageLocalService.updateAnswer(message, false, true);
1247                    }
1248            }
1249    
1250            @Override
1251            public MBThread updateStatus(long userId, long threadId, int status)
1252                    throws PortalException {
1253    
1254                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
1255    
1256                    // Thread
1257    
1258                    User user = userPersistence.findByPrimaryKey(userId);
1259    
1260                    thread.setStatus(status);
1261                    thread.setStatusByUserId(user.getUserId());
1262                    thread.setStatusByUserName(user.getFullName());
1263                    thread.setStatusDate(new Date());
1264    
1265                    mbThreadPersistence.update(thread);
1266    
1267                    // Messages
1268    
1269                    if (thread.getCategoryId() !=
1270                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
1271    
1272                            // Category
1273    
1274                            MBCategory category = mbCategoryPersistence.fetchByPrimaryKey(
1275                                    thread.getCategoryId());
1276    
1277                            if (category != null) {
1278                                    MBUtil.updateCategoryStatistics(category.getCategoryId());
1279                            }
1280                    }
1281    
1282                    // Indexer
1283    
1284                    Indexer<MBThread> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1285                            MBThread.class);
1286    
1287                    indexer.reindex(thread);
1288    
1289                    return thread;
1290            }
1291    
1292            /**
1293             * @deprecated As of 6.2.0, replaced by {@link #incrementViewCounter(long,
1294             *             int)}
1295             */
1296            @Deprecated
1297            @Override
1298            public MBThread updateThread(long threadId, int viewCount)
1299                    throws PortalException {
1300    
1301                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
1302    
1303                    thread.setViewCount(viewCount);
1304    
1305                    mbThreadPersistence.update(thread);
1306    
1307                    return thread;
1308            }
1309    
1310            protected void moveAttachmentsFolders(
1311                            MBMessage message, long oldAttachmentsFolderId, MBThread oldThread,
1312                            MBThread newThread, ServiceContext serviceContext)
1313                    throws PortalException {
1314    
1315                    if (oldAttachmentsFolderId !=
1316                                    DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
1317    
1318                            Folder newThreadFolder = newThread.addAttachmentsFolder();
1319    
1320                            PortletFileRepositoryUtil.movePortletFolder(
1321                                    message.getGroupId(), message.getUserId(),
1322                                    oldAttachmentsFolderId, newThreadFolder.getFolderId(),
1323                                    serviceContext);
1324                    }
1325    
1326                    List<MBMessage> childMessages = mbMessagePersistence.findByT_P(
1327                            oldThread.getThreadId(), message.getMessageId());
1328    
1329                    for (MBMessage childMessage : childMessages) {
1330                            moveAttachmentsFolders(
1331                                    childMessage, childMessage.getAttachmentsFolderId(), oldThread,
1332                                    newThread, serviceContext);
1333                    }
1334            }
1335    
1336            protected void moveChildrenMessages(
1337                            MBMessage parentMessage, MBCategory category, long oldThreadId)
1338                    throws PortalException {
1339    
1340                    List<MBMessage> messages = mbMessagePersistence.findByT_P(
1341                            oldThreadId, parentMessage.getMessageId());
1342    
1343                    for (MBMessage message : messages) {
1344                            message.setCategoryId(parentMessage.getCategoryId());
1345                            message.setThreadId(parentMessage.getThreadId());
1346                            message.setRootMessageId(parentMessage.getRootMessageId());
1347    
1348                            mbMessagePersistence.update(message);
1349    
1350                            if (!message.isDiscussion()) {
1351                                    Indexer<MBMessage> indexer =
1352                                            IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);
1353    
1354                                    indexer.reindex(message);
1355                            }
1356    
1357                            moveChildrenMessages(message, category, oldThreadId);
1358                    }
1359            }
1360    
1361    }