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