001    /**
002     * Copyright (c) 2000-2012 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.search.Indexer;
023    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.StringUtil;
026    import com.liferay.portal.kernel.util.Validator;
027    import com.liferay.portal.kernel.workflow.WorkflowConstants;
028    import com.liferay.portal.model.ResourceConstants;
029    import com.liferay.portal.model.User;
030    import com.liferay.portal.portletfilerepository.PortletFileRepositoryUtil;
031    import com.liferay.portal.service.ServiceContext;
032    import com.liferay.portal.util.PortletKeys;
033    import com.liferay.portlet.messageboards.NoSuchCategoryException;
034    import com.liferay.portlet.messageboards.SplitThreadException;
035    import com.liferay.portlet.messageboards.model.MBCategory;
036    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
037    import com.liferay.portlet.messageboards.model.MBMessage;
038    import com.liferay.portlet.messageboards.model.MBMessageDisplay;
039    import com.liferay.portlet.messageboards.model.MBThread;
040    import com.liferay.portlet.messageboards.model.MBThreadConstants;
041    import com.liferay.portlet.messageboards.model.MBTreeWalker;
042    import com.liferay.portlet.messageboards.service.base.MBThreadLocalServiceBaseImpl;
043    import com.liferay.portlet.messageboards.util.MBUtil;
044    import com.liferay.portlet.social.model.SocialActivityConstants;
045    import com.liferay.portlet.trash.model.TrashEntry;
046    
047    import java.util.ArrayList;
048    import java.util.Date;
049    import java.util.List;
050    
051    /**
052     * @author Brian Wing Shun Chan
053     * @author Shuyang Zhou
054     */
055    public class MBThreadLocalServiceImpl extends MBThreadLocalServiceBaseImpl {
056    
057            public MBThread addThread(long categoryId, MBMessage message)
058                    throws PortalException, SystemException {
059    
060                    // Thread
061    
062                    long threadId = message.getThreadId();
063    
064                    if (!message.isRoot() || (threadId <= 0)) {
065                            threadId = counterLocalService.increment();
066                    }
067    
068                    MBThread thread = mbThreadPersistence.create(threadId);
069    
070                    thread.setGroupId(message.getGroupId());
071                    thread.setCompanyId(message.getCompanyId());
072                    thread.setCategoryId(categoryId);
073                    thread.setRootMessageId(message.getMessageId());
074                    thread.setRootMessageUserId(message.getUserId());
075    
076                    if (message.isAnonymous()) {
077                            thread.setLastPostByUserId(0);
078                    }
079                    else {
080                            thread.setLastPostByUserId(message.getUserId());
081                    }
082    
083                    thread.setLastPostDate(message.getCreateDate());
084    
085                    if (message.getPriority() != MBThreadConstants.PRIORITY_NOT_GIVEN) {
086                            thread.setPriority(message.getPriority());
087                    }
088    
089                    thread.setStatus(message.getStatus());
090                    thread.setStatusByUserId(message.getStatusByUserId());
091                    thread.setStatusByUserName(message.getStatusByUserName());
092                    thread.setStatusDate(message.getStatusDate());
093    
094                    mbThreadPersistence.update(thread);
095    
096                    // Asset
097    
098                    if (categoryId >= 0) {
099                            assetEntryLocalService.updateEntry(
100                                    message.getUserId(), message.getGroupId(),
101                                    thread.getStatusDate(), thread.getLastPostDate(),
102                                    MBThread.class.getName(), thread.getThreadId(), null, 0,
103                                    new long[0], new String[0], false, null, null, null, null,
104                                    String.valueOf(thread.getRootMessageId()), null, null, null,
105                                    null, 0, 0, null, false);
106                    }
107    
108                    return thread;
109            }
110    
111            public void deleteThread(long threadId)
112                    throws PortalException, SystemException {
113    
114                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
115    
116                    deleteThread(thread);
117            }
118    
119            public void deleteThread(MBThread thread)
120                    throws PortalException, SystemException {
121    
122                    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(
123                            thread.getRootMessageId());
124    
125                    // Indexer
126    
127                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
128                            MBMessage.class);
129    
130                    indexer.delete(thread);
131    
132                    // Attachments
133    
134                    PortletFileRepositoryUtil.deleteFolder(thread.getAttachmentsFolderId());
135    
136                    // Subscriptions
137    
138                    subscriptionLocalService.deleteSubscriptions(
139                            thread.getCompanyId(), MBThread.class.getName(),
140                            thread.getThreadId());
141    
142                    // Thread flags
143    
144                    mbThreadFlagPersistence.removeByThreadId(thread.getThreadId());
145    
146                    // Messages
147    
148                    List<MBMessage> messages = mbMessagePersistence.findByThreadId(
149                            thread.getThreadId());
150    
151                    for (MBMessage message : messages) {
152    
153                            // Ratings
154    
155                            ratingsStatsLocalService.deleteStats(
156                                    message.getWorkflowClassName(), message.getMessageId());
157    
158                            // Asset
159    
160                            assetEntryLocalService.deleteEntry(
161                                    message.getWorkflowClassName(), message.getMessageId());
162    
163                            // Resources
164    
165                            if (!message.isDiscussion()) {
166                                    resourceLocalService.deleteResource(
167                                            message.getCompanyId(), message.getWorkflowClassName(),
168                                            ResourceConstants.SCOPE_INDIVIDUAL, message.getMessageId());
169                            }
170    
171                            // Message
172    
173                            mbMessagePersistence.remove(message);
174    
175                            // Statistics
176    
177                            if (!message.isDiscussion()) {
178                                    mbStatsUserLocalService.updateStatsUser(
179                                            message.getGroupId(), message.getUserId());
180                            }
181    
182                            // Workflow
183    
184                            workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
185                                    message.getCompanyId(), message.getGroupId(),
186                                    message.getWorkflowClassName(), message.getMessageId());
187                    }
188    
189                    // Category
190    
191                    if ((rootMessage.getCategoryId() !=
192                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
193                            (rootMessage.getCategoryId() !=
194                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
195    
196                            try {
197                                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
198                                            thread.getCategoryId());
199    
200                                    MBUtil.updateCategoryStatistics(category);
201                            }
202                            catch (NoSuchCategoryException nsce) {
203                                    if (!thread.isInTrash()) {
204                                            throw nsce;
205                                    }
206                            }
207                    }
208    
209                    // Thread Asset
210    
211                    assetEntryLocalService.deleteEntry(
212                            MBThread.class.getName(), thread.getThreadId());
213    
214                    // Trash
215    
216                    trashEntryLocalService.deleteEntry(
217                            MBThread.class.getName(), thread.getThreadId());
218    
219                    // Thread
220    
221                    mbThreadPersistence.remove(thread);
222            }
223    
224            public void deleteThreads(long groupId, long categoryId)
225                    throws PortalException, SystemException {
226    
227                    deleteThreads(groupId, categoryId, true);
228            }
229    
230            public void deleteThreads(
231                            long groupId, long categoryId, boolean includeTrashedEntries)
232                    throws PortalException, SystemException {
233    
234                    List<MBThread> threads = mbThreadPersistence.findByG_C(
235                            groupId, categoryId);
236    
237                    for (MBThread thread : threads) {
238                            if (includeTrashedEntries || !thread.isInTrash()) {
239                                    deleteThread(thread);
240                            }
241                    }
242    
243                    if (mbThreadPersistence.countByGroupId(groupId) == 0) {
244                            PortletFileRepositoryUtil.deletePortletRepository(
245                                    groupId, PortletKeys.MESSAGE_BOARDS);
246                    }
247            }
248    
249            public MBThread fetchThread(long threadId) throws SystemException {
250                    return mbThreadPersistence.fetchByPrimaryKey(threadId);
251            }
252    
253            public int getCategoryThreadsCount(
254                            long groupId, long categoryId, int status)
255                    throws SystemException {
256    
257                    if (status == WorkflowConstants.STATUS_ANY) {
258                            return mbThreadPersistence.countByG_C(groupId, categoryId);
259                    }
260                    else {
261                            return mbThreadPersistence.countByG_C_S(
262                                    groupId, categoryId, status);
263                    }
264            }
265    
266            /**
267             * @deprecated {@link #getGroupThreads(long, QueryDefinition)}
268             */
269            public List<MBThread> getGroupThreads(
270                            long groupId, int status, int start, int end)
271                    throws SystemException {
272    
273                    QueryDefinition queryDefinition = new QueryDefinition(
274                            status, start, end, null);
275    
276                    return getGroupThreads(groupId, queryDefinition);
277            }
278    
279            public List<MBThread> getGroupThreads(
280                            long groupId, long userId, boolean subscribed,
281                            boolean includeAnonymous, QueryDefinition queryDefinition)
282                    throws SystemException {
283    
284                    if (userId <= 0) {
285                            return getGroupThreads(groupId, queryDefinition);
286                    }
287                    else {
288                            if (subscribed) {
289                                    return mbThreadFinder.findByS_G_U_C(
290                                            groupId, userId, null, queryDefinition);
291                            }
292                            else {
293                                    if (includeAnonymous) {
294                                            return mbThreadFinder.findByG_U_C(
295                                                    groupId, userId, null, queryDefinition);
296                                    }
297                                    else {
298                                            return mbThreadFinder.findByG_U_C_A(
299                                                    groupId, userId, null, false, queryDefinition);
300                                    }
301                            }
302                    }
303            }
304    
305            public List<MBThread> getGroupThreads(
306                            long groupId, long userId, boolean subscribed,
307                            QueryDefinition queryDefinition)
308                    throws SystemException {
309    
310                    return getGroupThreads(
311                            groupId, userId, subscribed, true, queryDefinition);
312            }
313    
314            /**
315             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreads( long, long,
316             *             boolean, boolean, QueryDefinition)}
317             */
318            public List<MBThread> getGroupThreads(
319                            long groupId, long userId, int status, boolean subscribed,
320                            boolean includeAnonymous, int start, int end)
321                    throws SystemException {
322    
323                    QueryDefinition queryDefinition = new QueryDefinition(
324                            status, start, end, null);
325    
326                    return getGroupThreads(
327                            groupId, userId, subscribed, includeAnonymous, queryDefinition);
328            }
329    
330            /**
331             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreads( long, long,
332             *             boolean, QueryDefinition)}
333             */
334            public List<MBThread> getGroupThreads(
335                            long groupId, long userId, int status, boolean subscribed,
336                            int start, int end)
337                    throws SystemException {
338    
339                    QueryDefinition queryDefinition = new QueryDefinition(
340                            status, start, end, null);
341    
342                    return getGroupThreads(groupId, userId, subscribed, queryDefinition);
343            }
344    
345            /**
346             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreads( long, long,
347             *             QueryDefinition)}
348             */
349            public List<MBThread> getGroupThreads(
350                            long groupId, long userId, int status, int start, int end)
351                    throws SystemException {
352    
353                    QueryDefinition queryDefinition = new QueryDefinition(
354                            status, start, end, null);
355    
356                    return getGroupThreads(groupId, userId, false, queryDefinition);
357            }
358    
359            public List<MBThread> getGroupThreads(
360                            long groupId, long userId, QueryDefinition queryDefinition)
361                    throws SystemException {
362    
363                    return getGroupThreads(groupId, userId, false, queryDefinition);
364            }
365    
366            public List<MBThread> getGroupThreads(
367                            long groupId, QueryDefinition queryDefinition)
368                    throws SystemException {
369    
370                    if (queryDefinition.isExcludeStatus()) {
371                            return mbThreadPersistence.findByG_NotC_NotS(
372                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
373                                    queryDefinition.getStatus(), queryDefinition.getStart(),
374                                    queryDefinition.getEnd());
375                    }
376                    else {
377                            return mbThreadPersistence.findByG_NotC_S(
378                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
379                                    queryDefinition.getStatus(), queryDefinition.getStart(),
380                                    queryDefinition.getEnd());
381                    }
382            }
383    
384            /**
385             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreadsCount( long,
386             *             QueryDefinition)}
387             */
388            public int getGroupThreadsCount(long groupId, int status)
389                            throws SystemException {
390    
391                    QueryDefinition queryDefinition = new QueryDefinition(status);
392    
393                    return getGroupThreadsCount(groupId, queryDefinition);
394            }
395    
396            public int getGroupThreadsCount(
397                            long groupId, long userId, boolean subscribed,
398                            boolean includeAnonymous, QueryDefinition queryDefinition)
399                    throws SystemException {
400    
401                    if (userId <= 0) {
402                            return getGroupThreadsCount(groupId, queryDefinition);
403                    }
404                    else {
405                            if (subscribed) {
406                                    return mbThreadFinder.countByS_G_U_C(
407                                            groupId, userId, null, queryDefinition);
408                            }
409                            else {
410                                    if (includeAnonymous) {
411                                            return mbThreadFinder.countByG_U_C(
412                                                    groupId, userId, null, queryDefinition);
413                                    }
414                                    else {
415                                            return mbThreadFinder.countByG_U_C_A(
416                                                    groupId, userId, null, false, queryDefinition);
417                                    }
418                            }
419                    }
420            }
421    
422            public int getGroupThreadsCount(
423                            long groupId, long userId, boolean subscribed,
424                            QueryDefinition queryDefinition)
425                    throws SystemException {
426    
427                    return getGroupThreadsCount(
428                            groupId, userId, subscribed, true, queryDefinition);
429            }
430    
431            /**
432             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreadsCount( long,
433             *             long, QueryDefinition)}
434             */
435            public int getGroupThreadsCount(long groupId, long userId, int status)
436                    throws SystemException {
437    
438                    QueryDefinition queryDefinition = new QueryDefinition(status);
439    
440                    return getGroupThreadsCount(groupId, userId, false, queryDefinition);
441            }
442    
443            /**
444             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreadsCount( long,
445             *             long, boolean, QueryDefinition)}
446             */
447            public int getGroupThreadsCount(
448                            long groupId, long userId, int status, boolean subscribed)
449                    throws SystemException {
450    
451                    QueryDefinition queryDefinition = new QueryDefinition(status);
452    
453                    return getGroupThreadsCount(
454                            groupId, userId, subscribed, true, queryDefinition);
455            }
456    
457            /**
458             * @deprecated {As of 6.2.0, replaced by @link #getGroupThreadsCount( long,
459             *             long, boolean, boolean, QueryDefinition)}
460             */
461            public int getGroupThreadsCount(
462                            long groupId, long userId, int status, boolean subscribed,
463                            boolean includeAnonymous)
464                    throws SystemException {
465    
466                    QueryDefinition queryDefinition = new QueryDefinition(status);
467    
468                    return getGroupThreadsCount(
469                            groupId, userId, subscribed, includeAnonymous, queryDefinition);
470            }
471    
472            public int getGroupThreadsCount(
473                            long groupId, long userId, QueryDefinition queryDefinition)
474                    throws SystemException {
475    
476                    return getGroupThreadsCount(groupId, userId, false, queryDefinition);
477            }
478    
479            public int getGroupThreadsCount(
480                            long groupId, QueryDefinition queryDefinition)
481                    throws SystemException {
482    
483                    if (queryDefinition.isExcludeStatus()) {
484                            return mbThreadPersistence.countByG_NotC_NotS(
485                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
486                                    queryDefinition.getStatus());
487                    }
488                    else {
489                            return mbThreadPersistence.countByG_NotC_S(
490                                    groupId, MBCategoryConstants.DISCUSSION_CATEGORY_ID,
491                                    queryDefinition.getStatus());
492                    }
493            }
494    
495            public List<MBThread> getNoAssetThreads() throws SystemException {
496                    return mbThreadFinder.findByNoAssets();
497            }
498    
499            public List<MBThread> getPriorityThreads(long categoryId, double priority)
500                    throws PortalException, SystemException {
501    
502                    return getPriorityThreads(categoryId, priority, false);
503            }
504    
505            public List<MBThread> getPriorityThreads(
506                            long categoryId, double priority, boolean inherit)
507                    throws PortalException, SystemException {
508    
509                    if (!inherit) {
510                            return mbThreadPersistence.findByC_P(categoryId, priority);
511                    }
512    
513                    List<MBThread> threads = new ArrayList<MBThread>();
514    
515                    while ((categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
516                               (categoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
517    
518                            threads.addAll(
519                                    0, mbThreadPersistence.findByC_P(categoryId, priority));
520    
521                            MBCategory category = mbCategoryPersistence.findByPrimaryKey(
522                                    categoryId);
523    
524                            categoryId = category.getParentCategoryId();
525                    }
526    
527                    return threads;
528            }
529    
530            public MBThread getThread(long threadId)
531                    throws PortalException, SystemException {
532    
533                    return mbThreadPersistence.findByPrimaryKey(threadId);
534            }
535    
536            public List<MBThread> getThreads(
537                            long groupId, long categoryId, int status, int start, int end)
538                    throws SystemException {
539    
540                    if (status == WorkflowConstants.STATUS_ANY) {
541                            return mbThreadPersistence.findByG_C(
542                                    groupId, categoryId, start, end);
543                    }
544                    else {
545                            return mbThreadPersistence.findByG_C_S(
546                                    groupId, categoryId, status, start, end);
547                    }
548            }
549    
550            public int getThreadsCount(long groupId, long categoryId, int status)
551                    throws SystemException {
552    
553                    if (status == WorkflowConstants.STATUS_ANY) {
554                            return mbThreadPersistence.countByG_C(groupId, categoryId);
555                    }
556                    else {
557                            return mbThreadPersistence.countByG_C_S(
558                                    groupId, categoryId, status);
559                    }
560            }
561    
562            public boolean hasAnswerMessage(long threadId) throws SystemException {
563                    int count = mbMessagePersistence.countByT_A(threadId, true);
564    
565                    if (count > 0) {
566                            return true;
567                    }
568                    else {
569                            return false;
570                    }
571            }
572    
573            @BufferedIncrement(
574                    configuration = "MBThread", incrementClass = NumberIncrement.class)
575            public MBThread incrementViewCounter(long threadId, int increment)
576                    throws PortalException, SystemException {
577    
578                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
579    
580                    thread.setViewCount(thread.getViewCount() + increment);
581    
582                    mbThreadPersistence.update(thread);
583    
584                    return thread;
585            }
586    
587            public MBThread moveThread(long groupId, long categoryId, long threadId)
588                    throws PortalException, SystemException {
589    
590                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
591    
592                    long oldCategoryId = thread.getCategoryId();
593    
594                    MBCategory oldCategory = null;
595    
596                    if (oldCategoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
597                            oldCategory = mbCategoryPersistence.findByPrimaryKey(oldCategoryId);
598                    }
599    
600                    MBCategory category = null;
601    
602                    if (categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
603                            category = mbCategoryPersistence.findByPrimaryKey(categoryId);
604                    }
605    
606                    // Messages
607    
608                    List<MBMessage> messages = mbMessagePersistence.findByG_C_T(
609                            groupId, oldCategoryId, thread.getThreadId());
610    
611                    for (MBMessage message : messages) {
612                            message.setCategoryId(categoryId);
613    
614                            mbMessagePersistence.update(message);
615    
616                            // Indexer
617    
618                            if (!message.isDiscussion()) {
619                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
620                                            MBMessage.class);
621    
622                                    indexer.reindex(message);
623                            }
624                    }
625    
626                    // Thread
627    
628                    thread.setCategoryId(categoryId);
629    
630                    mbThreadPersistence.update(thread);
631    
632                    // Category
633    
634                    if ((oldCategory != null) && (categoryId != oldCategoryId)) {
635                            MBUtil.updateCategoryStatistics(oldCategory);
636                    }
637    
638                    if ((category != null) && (categoryId != oldCategoryId)) {
639                            MBUtil.updateCategoryStatistics(category);
640                    }
641    
642                    return thread;
643            }
644    
645            public MBThread moveThreadFromTrash(
646                            long userId, long categoryId, long threadId)
647                    throws PortalException, SystemException {
648    
649                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
650    
651                    if (thread.isInTrash()) {
652                            restoreThreadFromTrash(userId, threadId);
653                    }
654                    else {
655                            updateStatus(
656                                    userId, threadId, thread.getStatus(),
657                                    WorkflowConstants.STATUS_ANY);
658                    }
659    
660                    return moveThread(thread.getGroupId(), categoryId, threadId);
661            }
662    
663            public void moveThreadsToTrash(long groupId, long userId)
664                    throws PortalException, SystemException {
665    
666                    List<MBThread> threads = mbThreadPersistence.findByGroupId(groupId);
667    
668                    for (MBThread thread : threads) {
669                            moveThreadToTrash(userId, thread);
670                    }
671            }
672    
673            public MBThread moveThreadToTrash(long userId, long entryId)
674                    throws PortalException, SystemException {
675    
676                    MBThread thread = mbThreadPersistence.findByPrimaryKey(entryId);
677    
678                    return moveThreadToTrash(userId, thread);
679            }
680    
681            public MBThread moveThreadToTrash(long userId, MBThread thread)
682                    throws PortalException, SystemException {
683    
684                    if (thread.getCategoryId() ==
685                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
686    
687                            return thread;
688                    }
689    
690                    return updateStatus(
691                            userId, thread.getThreadId(), WorkflowConstants.STATUS_IN_TRASH,
692                            WorkflowConstants.STATUS_ANY);
693            }
694    
695            public void restoreThreadFromTrash(long userId, long threadId)
696                    throws PortalException, SystemException {
697    
698                    MBThread thread = getThread(threadId);
699    
700                    if (thread.getCategoryId() ==
701                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
702    
703                            return;
704                    }
705    
706                    TrashEntry trashEntry = trashEntryLocalService.getEntry(
707                            MBThread.class.getName(), threadId);
708    
709                    updateStatus(
710                            userId, threadId, trashEntry.getStatus(),
711                            WorkflowConstants.STATUS_ANY);
712            }
713    
714            public MBThread splitThread(
715                            long messageId, String subject, ServiceContext serviceContext)
716                    throws PortalException, SystemException {
717    
718                    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);
719    
720                    if (message.isRoot()) {
721                            throw new SplitThreadException();
722                    }
723    
724                    MBCategory category = message.getCategory();
725                    MBThread oldThread = message.getThread();
726                    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(
727                            oldThread.getRootMessageId());
728    
729                    // Message flags
730    
731                    mbMessageLocalService.updateAnswer(message, false, true);
732    
733                    // Create new thread
734    
735                    MBThread thread = addThread(message.getCategoryId(), message);
736    
737                    // Update messages
738    
739                    if (Validator.isNotNull(subject)) {
740                            MBMessageDisplay messageDisplay =
741                                    mbMessageService.getMessageDisplay(
742                                            messageId, WorkflowConstants.STATUS_ANY,
743                                            MBThreadConstants.THREAD_VIEW_TREE, false);
744    
745                            MBTreeWalker treeWalker = messageDisplay.getTreeWalker();
746    
747                            List<MBMessage> messages = treeWalker.getMessages();
748    
749                            int[] range = treeWalker.getChildrenRange(message);
750    
751                            for (int i = range[0]; i < range[1]; i++) {
752                                    MBMessage curMessage = messages.get(i);
753    
754                                    String oldSubject = message.getSubject();
755                                    String curSubject = curMessage.getSubject();
756    
757                                    if (oldSubject.startsWith("RE: ")) {
758                                            curSubject = StringUtil.replace(
759                                                    curSubject, rootMessage.getSubject(), subject);
760                                    }
761                                    else {
762                                            curSubject = StringUtil.replace(
763                                                    curSubject, oldSubject, subject);
764                                    }
765    
766                                    curMessage.setSubject(curSubject);
767    
768                                    mbMessagePersistence.update(curMessage);
769                            }
770    
771                            message.setSubject(subject);
772                    }
773    
774                    message.setThreadId(thread.getThreadId());
775                    message.setRootMessageId(thread.getRootMessageId());
776                    message.setParentMessageId(0);
777    
778                    mbMessagePersistence.update(message);
779    
780                    // Indexer
781    
782                    if (!message.isDiscussion()) {
783                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
784                                    MBMessage.class);
785    
786                            indexer.reindex(message);
787                    }
788    
789                    // Update children
790    
791                    moveChildrenMessages(message, category, oldThread.getThreadId());
792    
793                    // Update new thread
794    
795                    MBUtil.updateThreadMessageCount(thread);
796    
797                    // Update old thread
798    
799                    MBUtil.updateThreadMessageCount(oldThread);
800    
801                    // Category
802    
803                    if ((message.getCategoryId() !=
804                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
805                            (message.getCategoryId() !=
806                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
807    
808                            MBUtil.updateCategoryThreadCount(category);
809                    }
810    
811                    return thread;
812            }
813    
814            public void updateQuestion(long threadId, boolean question)
815                    throws PortalException, SystemException {
816    
817                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
818    
819                    if (thread.isQuestion() == question) {
820                            return;
821                    }
822    
823                    thread.setQuestion(question);
824    
825                    mbThreadPersistence.update(thread);
826    
827                    if (!question) {
828                            MBMessage message = mbMessagePersistence.findByPrimaryKey(
829                                    thread.getRootMessageId());
830    
831                            mbMessageLocalService.updateAnswer(message, false, true);
832                    }
833            }
834    
835            public MBThread updateStatus(
836                            long userId, long threadId, int status, int categoryStatus)
837                    throws PortalException, SystemException {
838    
839                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
840    
841                    if (categoryStatus != WorkflowConstants.STATUS_IN_TRASH) {
842    
843                            // Thread
844    
845                            User user = userPersistence.findByPrimaryKey(userId);
846    
847                            Date now = new Date();
848    
849                            int oldStatus = thread.getStatus();
850    
851                            if (oldStatus == WorkflowConstants.STATUS_PENDING) {
852                                    MBMessage rootMessage = mbMessageLocalService.getMBMessage(
853                                            thread.getRootMessageId());
854    
855                                    rootMessage.setStatus(WorkflowConstants.STATUS_DRAFT);
856    
857                                    mbMessagePersistence.update(rootMessage);
858                            }
859    
860                            thread.setStatus(status);
861                            thread.setStatusByUserId(user.getUserId());
862                            thread.setStatusByUserName(user.getFullName());
863                            thread.setStatusDate(now);
864    
865                            mbThreadPersistence.update(thread);
866    
867                            // Messages
868    
869                            updateDependentStatus(threadId, status);
870    
871                            if (thread.getCategoryId() !=
872                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
873    
874                                    // Category
875    
876                                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
877                                            thread.getCategoryId());
878    
879                                    MBUtil.updateCategoryStatistics(category);
880                            }
881    
882                            // Stats
883    
884                            mbStatsUserLocalService.updateStatsUser(
885                                    thread.getGroupId(), userId);
886    
887                            if (status == WorkflowConstants.STATUS_IN_TRASH) {
888    
889                                    // Social
890    
891                                    socialActivityLocalService.addActivity(
892                                            userId, thread.getGroupId(), MBThread.class.getName(),
893                                            thread.getThreadId(),
894                                            SocialActivityConstants.TYPE_MOVE_TO_TRASH,
895                                            StringPool.BLANK, 0);
896    
897                                    // Trash
898    
899                                    trashEntryLocalService.addTrashEntry(
900                                            userId, thread.getGroupId(), MBThread.class.getName(),
901                                            thread.getThreadId(), oldStatus, null, null);
902                            }
903                            else {
904    
905                                    // Social
906    
907                                    socialActivityLocalService.addActivity(
908                                            userId, thread.getGroupId(), MBThread.class.getName(),
909                                            thread.getThreadId(),
910                                            SocialActivityConstants.TYPE_RESTORE_FROM_TRASH,
911                                            StringPool.BLANK, 0);
912    
913                                    // Trash
914    
915                                    trashEntryLocalService.deleteEntry(
916                                            MBThread.class.getName(), thread.getThreadId());
917                            }
918                    }
919                    else {
920                            updateDependentStatus(threadId, status);
921                    }
922    
923                    return thread;
924            }
925    
926            /**
927             * @deprecated {@link #incrementViewCounter(long, int)}
928             */
929            public MBThread updateThread(long threadId, int viewCount)
930                    throws PortalException, SystemException {
931    
932                    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);
933    
934                    thread.setViewCount(viewCount);
935    
936                    mbThreadPersistence.update(thread);
937    
938                    return thread;
939            }
940    
941            protected void moveChildrenMessages(
942                            MBMessage parentMessage, MBCategory category, long oldThreadId)
943                    throws PortalException, SystemException {
944    
945                    List<MBMessage> messages = mbMessagePersistence.findByT_P(
946                            oldThreadId, parentMessage.getMessageId());
947    
948                    for (MBMessage message : messages) {
949                            message.setCategoryId(parentMessage.getCategoryId());
950                            message.setThreadId(parentMessage.getThreadId());
951                            message.setRootMessageId(parentMessage.getRootMessageId());
952    
953                            mbMessagePersistence.update(message);
954    
955                            if (!message.isDiscussion()) {
956                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
957                                            MBMessage.class);
958    
959                                    indexer.reindex(message);
960                            }
961    
962                            moveChildrenMessages(message, category, oldThreadId);
963                    }
964            }
965    
966            protected void updateDependentStatus(long threadId, int status)
967                    throws PortalException, SystemException {
968    
969                    List<MBMessage> messages = mbMessageLocalService.getThreadMessages(
970                            threadId, WorkflowConstants.STATUS_ANY);
971    
972                    for (MBMessage message : messages) {
973                            if (message.isDiscussion()) {
974                                    continue;
975                            }
976    
977                            if (status == WorkflowConstants.STATUS_IN_TRASH) {
978    
979                                    // Asset
980    
981                                    if (message.getStatus() == WorkflowConstants.STATUS_APPROVED) {
982                                            assetEntryLocalService.updateVisible(
983                                                    MBMessage.class.getName(), message.getMessageId(),
984                                                    false);
985                                    }
986    
987                                    // Social
988    
989                                    socialActivityCounterLocalService.disableActivityCounters(
990                                            MBMessage.class.getName(), message.getMessageId());
991    
992                                    // Index
993    
994                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
995                                            MBMessage.class);
996    
997                                    indexer.reindex(message);
998    
999                                    // Workflow
1000    
1001                                    if (message.getStatus() == WorkflowConstants.STATUS_PENDING) {
1002                                            message.setStatus(WorkflowConstants.STATUS_DRAFT);
1003    
1004                                            mbMessagePersistence.update(message);
1005    
1006                                            workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
1007                                                    message.getCompanyId(), message.getGroupId(),
1008                                                    MBMessage.class.getName(), message.getMessageId());
1009                                    }
1010                            }
1011                            else {
1012    
1013                                    // Asset
1014    
1015                                    if (message.getStatus() == WorkflowConstants.STATUS_APPROVED) {
1016                                            assetEntryLocalService.updateVisible(
1017                                                    MBMessage.class.getName(), message.getMessageId(),
1018                                                    true);
1019                                    }
1020    
1021                                    // Social
1022    
1023                                    socialActivityCounterLocalService.enableActivityCounters(
1024                                            MBMessage.class.getName(), message.getMessageId());
1025    
1026                                    // Index
1027    
1028                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
1029                                            MBMessage.class);
1030    
1031                                    indexer.reindex(message);
1032                            }
1033                    }
1034            }
1035    
1036    }