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