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