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