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