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