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