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