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