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