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.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.search.Indexer;
021    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
022    import com.liferay.portal.kernel.systemevent.SystemEvent;
023    import com.liferay.portal.kernel.util.Validator;
024    import com.liferay.portal.kernel.workflow.WorkflowConstants;
025    import com.liferay.portal.model.ResourceConstants;
026    import com.liferay.portal.model.SystemEventConstants;
027    import com.liferay.portal.model.User;
028    import com.liferay.portal.service.ServiceContext;
029    import com.liferay.portal.service.permission.ModelPermissions;
030    import com.liferay.portlet.messageboards.CategoryNameException;
031    import com.liferay.portlet.messageboards.model.MBCategory;
032    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
033    import com.liferay.portlet.messageboards.model.MBMailingList;
034    import com.liferay.portlet.messageboards.model.MBMessage;
035    import com.liferay.portlet.messageboards.model.MBThread;
036    import com.liferay.portlet.messageboards.model.impl.MBCategoryImpl;
037    import com.liferay.portlet.messageboards.service.base.MBCategoryLocalServiceBaseImpl;
038    import com.liferay.portlet.trash.model.TrashEntry;
039    import com.liferay.portlet.trash.model.TrashVersion;
040    
041    import java.util.ArrayList;
042    import java.util.Date;
043    import java.util.List;
044    
045    /**
046     * @author Brian Wing Shun Chan
047     * @author Wesley Gong
048     */
049    public class MBCategoryLocalServiceImpl extends MBCategoryLocalServiceBaseImpl {
050    
051            @Override
052            public MBCategory addCategory(
053                            long userId, long parentCategoryId, String name, String description,
054                            ServiceContext serviceContext)
055                    throws PortalException {
056    
057                    return addCategory(
058                            userId, parentCategoryId, name, description,
059                            MBCategoryConstants.DEFAULT_DISPLAY_STYLE, null, null, null, 0,
060                            false, null, null, 0, null, false, null, 0, false, null, null,
061                            false, false, serviceContext);
062            }
063    
064            @Override
065            public MBCategory addCategory(
066                            long userId, long parentCategoryId, String name, String description,
067                            String displayStyle, String emailAddress, String inProtocol,
068                            String inServerName, int inServerPort, boolean inUseSSL,
069                            String inUserName, String inPassword, int inReadInterval,
070                            String outEmailAddress, boolean outCustom, String outServerName,
071                            int outServerPort, boolean outUseSSL, String outUserName,
072                            String outPassword, boolean allowAnonymous,
073                            boolean mailingListActive, ServiceContext serviceContext)
074                    throws PortalException {
075    
076                    // Category
077    
078                    User user = userPersistence.findByPrimaryKey(userId);
079                    long groupId = serviceContext.getScopeGroupId();
080                    parentCategoryId = getParentCategoryId(groupId, parentCategoryId);
081    
082                    validate(name);
083    
084                    long categoryId = counterLocalService.increment();
085    
086                    MBCategory category = mbCategoryPersistence.create(categoryId);
087    
088                    category.setUuid(serviceContext.getUuid());
089                    category.setGroupId(groupId);
090                    category.setCompanyId(user.getCompanyId());
091                    category.setUserId(user.getUserId());
092                    category.setUserName(user.getFullName());
093                    category.setParentCategoryId(parentCategoryId);
094                    category.setName(name);
095                    category.setDescription(description);
096                    category.setDisplayStyle(displayStyle);
097                    category.setExpandoBridgeAttributes(serviceContext);
098    
099                    mbCategoryPersistence.update(category);
100    
101                    // Resources
102    
103                    if (serviceContext.isAddGroupPermissions() ||
104                            serviceContext.isAddGuestPermissions()) {
105    
106                            addCategoryResources(
107                                    category, serviceContext.isAddGroupPermissions(),
108                                    serviceContext.isAddGuestPermissions());
109                    }
110                    else {
111                            addCategoryResources(
112                                    category, serviceContext.getModelPermissions());
113                    }
114    
115                    // Mailing list
116    
117                    mbMailingListLocalService.addMailingList(
118                            userId, groupId, category.getCategoryId(), emailAddress, inProtocol,
119                            inServerName, inServerPort, inUseSSL, inUserName, inPassword,
120                            inReadInterval, outEmailAddress, outCustom, outServerName,
121                            outServerPort, outUseSSL, outUserName, outPassword, allowAnonymous,
122                            mailingListActive, serviceContext);
123    
124                    return category;
125            }
126    
127            @Override
128            public void addCategoryResources(
129                            long categoryId, boolean addGroupPermissions,
130                            boolean addGuestPermissions)
131                    throws PortalException {
132    
133                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
134                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
135    
136                            return;
137                    }
138    
139                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
140                            categoryId);
141    
142                    addCategoryResources(
143                            category, addGroupPermissions, addGuestPermissions);
144            }
145    
146            @Override
147            public void addCategoryResources(
148                            long categoryId, ModelPermissions modelPermissions)
149                    throws PortalException {
150    
151                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
152                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
153    
154                            return;
155                    }
156    
157                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
158                            categoryId);
159    
160                    addCategoryResources(category, modelPermissions);
161            }
162    
163            @Override
164            public void addCategoryResources(
165                            MBCategory category, boolean addGroupPermissions,
166                            boolean addGuestPermissions)
167                    throws PortalException {
168    
169                    resourceLocalService.addResources(
170                            category.getCompanyId(), category.getGroupId(),
171                            category.getUserId(), MBCategory.class.getName(),
172                            category.getCategoryId(), false, addGroupPermissions,
173                            addGuestPermissions);
174            }
175    
176            @Override
177            public void addCategoryResources(
178                            MBCategory category, ModelPermissions modelPermissions)
179                    throws PortalException {
180    
181                    resourceLocalService.addModelResources(
182                            category.getCompanyId(), category.getGroupId(),
183                            category.getUserId(), MBCategory.class.getName(),
184                            category.getCategoryId(), modelPermissions);
185            }
186    
187            @Override
188            public void deleteCategories(long groupId) throws PortalException {
189                    List<MBCategory> categories = mbCategoryPersistence.findByGroupId(
190                            groupId);
191    
192                    for (MBCategory category : categories) {
193                            mbCategoryLocalService.deleteCategory(category);
194                    }
195            }
196    
197            @Override
198            public void deleteCategory(long categoryId) throws PortalException {
199                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
200                            categoryId);
201    
202                    mbCategoryLocalService.deleteCategory(category);
203            }
204    
205            @Override
206            public void deleteCategory(MBCategory category) throws PortalException {
207                    mbCategoryLocalService.deleteCategory(category, true);
208            }
209    
210            @Override
211            @SystemEvent(
212                    action = SystemEventConstants.ACTION_SKIP,
213                    type = SystemEventConstants.TYPE_DELETE
214            )
215            public void deleteCategory(
216                            MBCategory category, boolean includeTrashedEntries)
217                    throws PortalException {
218    
219                    // Categories
220    
221                    List<MBCategory> categories = mbCategoryPersistence.findByG_P(
222                            category.getGroupId(), category.getCategoryId());
223    
224                    for (MBCategory curCategory : categories) {
225                            if (includeTrashedEntries || !curCategory.isInTrashExplicitly()) {
226                                    mbCategoryLocalService.deleteCategory(
227                                            curCategory, includeTrashedEntries);
228                            }
229                    }
230    
231                    // Threads
232    
233                    mbThreadLocalService.deleteThreads(
234                            category.getGroupId(), category.getCategoryId(),
235                            includeTrashedEntries);
236    
237                    // Mailing list
238    
239                    MBMailingList mbMailingList =
240                            mbMailingListLocalService.fetchCategoryMailingList(
241                                    category.getGroupId(), category.getCategoryId());
242    
243                    if (mbMailingList != null) {
244                            mbMailingListLocalService.deleteMailingList(mbMailingList);
245                    }
246    
247                    // Subscriptions
248    
249                    subscriptionLocalService.deleteSubscriptions(
250                            category.getCompanyId(), MBCategory.class.getName(),
251                            category.getCategoryId());
252    
253                    // Expando
254    
255                    expandoRowLocalService.deleteRows(category.getCategoryId());
256    
257                    // Ratings
258    
259                    ratingsStatsLocalService.deleteStats(
260                            MBCategory.class.getName(), category.getCategoryId());
261    
262                    // Resources
263    
264                    resourceLocalService.deleteResource(
265                            category.getCompanyId(), MBCategory.class.getName(),
266                            ResourceConstants.SCOPE_INDIVIDUAL, category.getCategoryId());
267    
268                    // Trash
269    
270                    if (category.isInTrashExplicitly()) {
271                            trashEntryLocalService.deleteEntry(
272                                    MBCategory.class.getName(), category.getCategoryId());
273                    }
274                    else {
275                            trashVersionLocalService.deleteTrashVersion(
276                                    MBCategory.class.getName(), category.getCategoryId());
277                    }
278    
279                    // Category
280    
281                    mbCategoryPersistence.remove(category);
282            }
283    
284            @Override
285            public List<MBCategory> getCategories(long groupId) {
286                    return mbCategoryPersistence.findByGroupId(groupId);
287            }
288    
289            @Override
290            public List<MBCategory> getCategories(long groupId, int status) {
291                    return mbCategoryPersistence.findByG_S(groupId, status);
292            }
293    
294            @Override
295            public List<MBCategory> getCategories(
296                    long groupId, long parentCategoryId, int start, int end) {
297    
298                    return mbCategoryPersistence.findByG_P(
299                            groupId, parentCategoryId, start, end);
300            }
301    
302            @Override
303            public List<MBCategory> getCategories(
304                    long groupId, long parentCategoryId, int status, int start, int end) {
305    
306                    if (status == WorkflowConstants.STATUS_ANY) {
307                            return mbCategoryPersistence.findByG_P(
308                                    groupId, parentCategoryId, start, end);
309                    }
310    
311                    return mbCategoryPersistence.findByG_P_S(
312                            groupId, parentCategoryId, status, start, end);
313            }
314    
315            @Override
316            public List<MBCategory> getCategories(
317                    long groupId, long excludedCategoryId, long parentCategoryId,
318                    int status, int start, int end) {
319    
320                    if (status == WorkflowConstants.STATUS_ANY) {
321                            return mbCategoryPersistence.findByNotC_G_P(
322                                    excludedCategoryId, groupId, parentCategoryId, start, end);
323                    }
324    
325                    return mbCategoryPersistence.findByNotC_G_P_S(
326                            excludedCategoryId, groupId, parentCategoryId, status, start, end);
327            }
328    
329            @Override
330            public List<MBCategory> getCategories(
331                    long groupId, long[] parentCategoryIds, int start, int end) {
332    
333                    return mbCategoryPersistence.findByG_P(
334                            groupId, parentCategoryIds, start, end);
335            }
336    
337            @Override
338            public List<MBCategory> getCategories(
339                    long groupId, long[] parentCategoryIds, int status, int start,
340                    int end) {
341    
342                    if (status == WorkflowConstants.STATUS_ANY) {
343                            return mbCategoryPersistence.findByG_P(
344                                    groupId, parentCategoryIds, start, end);
345                    }
346    
347                    return mbCategoryPersistence.findByG_P_S(
348                            groupId, parentCategoryIds, status, start, end);
349            }
350    
351            @Override
352            public List<MBCategory> getCategories(
353                    long groupId, long[] excludedCategoryIds, long[] parentCategoryIds,
354                    int status, int start, int end) {
355    
356                    if (status == WorkflowConstants.STATUS_ANY) {
357                            return mbCategoryPersistence.findByNotC_G_P(
358                                    excludedCategoryIds, groupId, parentCategoryIds, start, end);
359                    }
360    
361                    return mbCategoryPersistence.findByNotC_G_P_S(
362                            excludedCategoryIds, groupId, parentCategoryIds, status, start,
363                            end);
364            }
365    
366            @Override
367            public List<Object> getCategoriesAndThreads(long groupId, long categoryId) {
368                    List<Object> categoriesAndThreads = new ArrayList<>();
369    
370                    List<MBCategory> categories = getCategories(
371                            groupId, categoryId, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
372    
373                    categoriesAndThreads.addAll(categories);
374    
375                    List<MBThread> threads = mbThreadLocalService.getThreads(
376                            groupId, categoryId, WorkflowConstants.STATUS_ANY,
377                            QueryUtil.ALL_POS, QueryUtil.ALL_POS);
378    
379                    categoriesAndThreads.addAll(threads);
380    
381                    return categoriesAndThreads;
382            }
383    
384            @Override
385            public int getCategoriesCount(long groupId) {
386                    return mbCategoryPersistence.countByGroupId(groupId);
387            }
388    
389            @Override
390            public int getCategoriesCount(long groupId, int status) {
391                    return mbCategoryPersistence.countByG_S(groupId, status);
392            }
393    
394            @Override
395            public int getCategoriesCount(long groupId, long parentCategoryId) {
396                    return mbCategoryPersistence.countByG_P(groupId, parentCategoryId);
397            }
398    
399            @Override
400            public int getCategoriesCount(
401                    long groupId, long parentCategoryId, int status) {
402    
403                    if (status == WorkflowConstants.STATUS_ANY) {
404                            return mbCategoryPersistence.countByG_P(groupId, parentCategoryId);
405                    }
406    
407                    return mbCategoryPersistence.countByG_P_S(
408                            groupId, parentCategoryId, status);
409            }
410    
411            @Override
412            public int getCategoriesCount(
413                    long groupId, long excludedCategoryId, long parentCategoryId,
414                    int status) {
415    
416                    if (status == WorkflowConstants.STATUS_ANY) {
417                            return mbCategoryPersistence.countByNotC_G_P(
418                                    excludedCategoryId, groupId, parentCategoryId);
419                    }
420    
421                    return mbCategoryPersistence.countByNotC_G_P_S(
422                            excludedCategoryId, groupId, parentCategoryId, status);
423            }
424    
425            @Override
426            public int getCategoriesCount(long groupId, long[] parentCategoryIds) {
427                    return mbCategoryPersistence.countByG_P(groupId, parentCategoryIds);
428            }
429    
430            @Override
431            public int getCategoriesCount(
432                    long groupId, long[] parentCategoryIds, int status) {
433    
434                    if (status == WorkflowConstants.STATUS_ANY) {
435                            return mbCategoryPersistence.countByG_P(groupId, parentCategoryIds);
436                    }
437    
438                    return mbCategoryPersistence.countByG_P_S(
439                            groupId, parentCategoryIds, status);
440            }
441    
442            @Override
443            public int getCategoriesCount(
444                    long groupId, long[] excludedCategoryIds, long[] parentCategoryIds,
445                    int status) {
446    
447                    if (status == WorkflowConstants.STATUS_ANY) {
448                            return mbCategoryPersistence.countByNotC_G_P(
449                                    excludedCategoryIds, groupId, parentCategoryIds);
450                    }
451    
452                    return mbCategoryPersistence.countByNotC_G_P_S(
453                            excludedCategoryIds, groupId, parentCategoryIds, status);
454            }
455    
456            @Override
457            public MBCategory getCategory(long categoryId) throws PortalException {
458                    MBCategory category = null;
459    
460                    if ((categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
461                            (categoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
462    
463                            category = mbCategoryPersistence.findByPrimaryKey(categoryId);
464                    }
465                    else {
466                            category = new MBCategoryImpl();
467    
468                            category.setCategoryId(categoryId);
469                            category.setParentCategoryId(categoryId);
470                    }
471    
472                    return category;
473            }
474    
475            @Override
476            public List<MBCategory> getCompanyCategories(
477                    long companyId, int start, int end) {
478    
479                    return mbCategoryPersistence.findByCompanyId(companyId, start, end);
480            }
481    
482            @Override
483            public int getCompanyCategoriesCount(long companyId) {
484                    return mbCategoryPersistence.countByCompanyId(companyId);
485            }
486    
487            @Override
488            public List<Long> getSubcategoryIds(
489                    List<Long> categoryIds, long groupId, long categoryId) {
490    
491                    List<MBCategory> categories = mbCategoryPersistence.findByG_P(
492                            groupId, categoryId);
493    
494                    for (MBCategory category : categories) {
495                            categoryIds.add(category.getCategoryId());
496    
497                            getSubcategoryIds(
498                                    categoryIds, category.getGroupId(), category.getCategoryId());
499                    }
500    
501                    return categoryIds;
502            }
503    
504            @Override
505            public List<MBCategory> getSubscribedCategories(
506                    long groupId, long userId, int start, int end) {
507    
508                    QueryDefinition<MBCategory> queryDefinition = new QueryDefinition<>(
509                            WorkflowConstants.STATUS_ANY, start, end, null);
510    
511                    return mbCategoryFinder.findByS_G_U_P(
512                            groupId, userId, null, queryDefinition);
513            }
514    
515            @Override
516            public int getSubscribedCategoriesCount(long groupId, long userId) {
517                    QueryDefinition<MBCategory> queryDefinition = new QueryDefinition<>(
518                            WorkflowConstants.STATUS_ANY);
519    
520                    return mbCategoryFinder.countByS_G_U_P(
521                            groupId, userId, null, queryDefinition);
522            }
523    
524            @Override
525            public void moveCategoriesToTrash(long groupId, long userId)
526                    throws PortalException {
527    
528                    List<MBCategory> categories = mbCategoryPersistence.findByGroupId(
529                            groupId);
530    
531                    for (MBCategory category : categories) {
532                            moveCategoryToTrash(userId, category.getCategoryId());
533                    }
534            }
535    
536            @Override
537            public MBCategory moveCategory(
538                            long categoryId, long parentCategoryId,
539                            boolean mergeWithParentCategory)
540                    throws PortalException {
541    
542                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
543                            categoryId);
544    
545                    parentCategoryId = getParentCategoryId(category, parentCategoryId);
546    
547                    if (mergeWithParentCategory && (categoryId != parentCategoryId) &&
548                            (parentCategoryId !=
549                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
550                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
551    
552                            mergeCategories(category, parentCategoryId);
553    
554                            return category;
555                    }
556    
557                    category.setParentCategoryId(parentCategoryId);
558    
559                    return mbCategoryPersistence.update(category);
560            }
561    
562            @Override
563            public MBCategory moveCategoryFromTrash(
564                            long userId, long categoryId, long newCategoryId)
565                    throws PortalException {
566    
567                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
568                            categoryId);
569    
570                    if (category.isInTrashExplicitly()) {
571                            restoreCategoryFromTrash(userId, categoryId);
572                    }
573                    else {
574    
575                            // Category
576    
577                            TrashVersion trashVersion = trashVersionLocalService.fetchVersion(
578                                    MBCategory.class.getName(), category.getCategoryId());
579    
580                            int status = WorkflowConstants.STATUS_APPROVED;
581    
582                            if (trashVersion != null) {
583                                    status = trashVersion.getStatus();
584                            }
585    
586                            updateStatus(userId, categoryId, status);
587    
588                            // Trash
589    
590                            if (trashVersion != null) {
591                                    trashVersionLocalService.deleteTrashVersion(trashVersion);
592                            }
593    
594                            // Categories and threads
595    
596                            User user = userPersistence.findByPrimaryKey(userId);
597    
598                            List<Object> categoriesAndThreads = getCategoriesAndThreads(
599                                    category.getGroupId(), categoryId);
600    
601                            restoreDependentsFromTrash(user, categoriesAndThreads);
602                    }
603    
604                    return moveCategory(categoryId, newCategoryId, false);
605            }
606    
607            @Override
608            public MBCategory moveCategoryToTrash(long userId, long categoryId)
609                    throws PortalException {
610    
611                    // Category
612    
613                    MBCategory category = updateStatus(
614                            userId, categoryId, WorkflowConstants.STATUS_IN_TRASH);
615    
616                    // Trash
617    
618                    TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
619                            userId, category.getGroupId(), MBCategory.class.getName(),
620                            categoryId, category.getUuid(), null,
621                            WorkflowConstants.STATUS_APPROVED, null, null);
622    
623                    // Categories and threads
624    
625                    User user = userPersistence.findByPrimaryKey(userId);
626    
627                    List<Object> categoriesAndThreads = getCategoriesAndThreads(
628                            category.getGroupId(), categoryId);
629    
630                    moveDependentsToTrash(
631                            user, categoriesAndThreads, trashEntry.getEntryId());
632    
633                    return category;
634            }
635    
636            @Override
637            public void restoreCategoryFromTrash(long userId, long categoryId)
638                    throws PortalException {
639    
640                    // Category
641    
642                    TrashEntry trashEntry = trashEntryLocalService.getEntry(
643                            MBCategory.class.getName(), categoryId);
644    
645                    MBCategory category = updateStatus(
646                            userId, categoryId, WorkflowConstants.STATUS_APPROVED);
647    
648                    // Categories and threads
649    
650                    User user = userPersistence.findByPrimaryKey(userId);
651    
652                    List<Object> categoriesAndThreads = getCategoriesAndThreads(
653                            category.getGroupId(), categoryId);
654    
655                    restoreDependentsFromTrash(user, categoriesAndThreads);
656    
657                    // Trash
658    
659                    trashEntryLocalService.deleteEntry(trashEntry.getEntryId());
660            }
661    
662            @Override
663            public void subscribeCategory(long userId, long groupId, long categoryId)
664                    throws PortalException {
665    
666                    if (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
667                            categoryId = groupId;
668                    }
669    
670                    subscriptionLocalService.addSubscription(
671                            userId, groupId, MBCategory.class.getName(), categoryId);
672            }
673    
674            @Override
675            public void unsubscribeCategory(long userId, long groupId, long categoryId)
676                    throws PortalException {
677    
678                    if (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
679                            categoryId = groupId;
680                    }
681    
682                    subscriptionLocalService.deleteSubscription(
683                            userId, MBCategory.class.getName(), categoryId);
684            }
685    
686            @Override
687            public MBCategory updateCategory(
688                            long categoryId, long parentCategoryId, String name,
689                            String description, String displayStyle, String emailAddress,
690                            String inProtocol, String inServerName, int inServerPort,
691                            boolean inUseSSL, String inUserName, String inPassword,
692                            int inReadInterval, String outEmailAddress, boolean outCustom,
693                            String outServerName, int outServerPort, boolean outUseSSL,
694                            String outUserName, String outPassword, boolean allowAnonymous,
695                            boolean mailingListActive, boolean mergeWithParentCategory,
696                            ServiceContext serviceContext)
697                    throws PortalException {
698    
699                    // Merge categories
700    
701                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
702                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
703    
704                            return null;
705                    }
706    
707                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
708                            categoryId);
709    
710                    parentCategoryId = getParentCategoryId(category, parentCategoryId);
711    
712                    if (mergeWithParentCategory && (categoryId != parentCategoryId) &&
713                            (parentCategoryId !=
714                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
715                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
716    
717                            mergeCategories(category, parentCategoryId);
718    
719                            return category;
720                    }
721    
722                    // Category
723    
724                    validate(name);
725    
726                    category.setParentCategoryId(parentCategoryId);
727                    category.setName(name);
728                    category.setDescription(description);
729    
730                    if (!displayStyle.equals(category.getDisplayStyle())) {
731                            category.setDisplayStyle(displayStyle);
732    
733                            updateChildCategoriesDisplayStyle(category, displayStyle);
734                    }
735    
736                    category.setExpandoBridgeAttributes(serviceContext);
737    
738                    mbCategoryPersistence.update(category);
739    
740                    // Mailing list
741    
742                    MBMailingList mailingList = mbMailingListPersistence.fetchByG_C(
743                            category.getGroupId(), category.getCategoryId());
744    
745                    if (mailingList != null) {
746                            mbMailingListLocalService.updateMailingList(
747                                    mailingList.getMailingListId(), emailAddress, inProtocol,
748                                    inServerName, inServerPort, inUseSSL, inUserName, inPassword,
749                                    inReadInterval, outEmailAddress, outCustom, outServerName,
750                                    outServerPort, outUseSSL, outUserName, outPassword,
751                                    allowAnonymous, mailingListActive, serviceContext);
752                    }
753                    else {
754                            mbMailingListLocalService.addMailingList(
755                                    category.getUserId(), category.getGroupId(),
756                                    category.getCategoryId(), emailAddress, inProtocol,
757                                    inServerName, inServerPort, inUseSSL, inUserName, inPassword,
758                                    inReadInterval, outEmailAddress, outCustom, outServerName,
759                                    outServerPort, outUseSSL, outUserName, outPassword,
760                                    allowAnonymous, mailingListActive, serviceContext);
761                    }
762    
763                    return category;
764            }
765    
766            @Override
767            public MBCategory updateMessageCount(long categoryId) {
768                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
769                            categoryId);
770    
771                    if (mbCategory == null) {
772                            return null;
773                    }
774    
775                    int messageCount = mbMessageLocalService.getCategoryMessagesCount(
776                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
777                            WorkflowConstants.STATUS_APPROVED);
778    
779                    mbCategory.setMessageCount(messageCount);
780    
781                    return mbCategoryPersistence.update(mbCategory);
782            }
783    
784            @Override
785            public MBCategory updateStatistics(long categoryId) {
786                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
787                            categoryId);
788    
789                    if (mbCategory == null) {
790                            return null;
791                    }
792    
793                    int messageCount = mbMessageLocalService.getCategoryMessagesCount(
794                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
795                            WorkflowConstants.STATUS_APPROVED);
796    
797                    mbCategory.setMessageCount(messageCount);
798    
799                    int threadCount = mbThreadLocalService.getCategoryThreadsCount(
800                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
801                            WorkflowConstants.STATUS_APPROVED);
802    
803                    mbCategory.setThreadCount(threadCount);
804    
805                    return mbCategoryPersistence.update(mbCategory);
806            }
807    
808            @Override
809            public MBCategory updateStatus(long userId, long categoryId, int status)
810                    throws PortalException {
811    
812                    // Category
813    
814                    User user = userPersistence.findByPrimaryKey(userId);
815    
816                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
817                            categoryId);
818    
819                    category.setStatus(status);
820                    category.setStatusByUserId(user.getUserId());
821                    category.setStatusByUserName(user.getFullName());
822                    category.setStatusDate(new Date());
823    
824                    mbCategoryPersistence.update(category);
825    
826                    return category;
827            }
828    
829            @Override
830            public MBCategory updateThreadCount(long categoryId) {
831                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
832                            categoryId);
833    
834                    if (mbCategory == null) {
835                            return null;
836                    }
837    
838                    int threadCount = mbThreadLocalService.getCategoryThreadsCount(
839                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
840                            WorkflowConstants.STATUS_APPROVED);
841    
842                    mbCategory.setThreadCount(threadCount);
843    
844                    return mbCategoryPersistence.update(mbCategory);
845            }
846    
847            protected long getParentCategoryId(long groupId, long parentCategoryId) {
848                    if ((parentCategoryId !=
849                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
850                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
851    
852                            MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
853                                    parentCategoryId);
854    
855                            if ((parentCategory == null) ||
856                                    (groupId != parentCategory.getGroupId())) {
857    
858                                    parentCategoryId =
859                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
860                            }
861                    }
862    
863                    return parentCategoryId;
864            }
865    
866            protected long getParentCategoryId(
867                    MBCategory category, long parentCategoryId) {
868    
869                    if ((parentCategoryId ==
870                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
871                            (parentCategoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
872    
873                            return parentCategoryId;
874                    }
875    
876                    if (category.getCategoryId() == parentCategoryId) {
877                            return category.getParentCategoryId();
878                    }
879    
880                    MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
881                            parentCategoryId);
882    
883                    if ((parentCategory == null) ||
884                            (category.getGroupId() != parentCategory.getGroupId())) {
885    
886                            return category.getParentCategoryId();
887                    }
888    
889                    List<Long> subcategoryIds = new ArrayList<>();
890    
891                    getSubcategoryIds(
892                            subcategoryIds, category.getGroupId(), category.getCategoryId());
893    
894                    if (subcategoryIds.contains(parentCategoryId)) {
895                            return category.getParentCategoryId();
896                    }
897    
898                    return parentCategoryId;
899            }
900    
901            protected void mergeCategories(MBCategory fromCategory, long toCategoryId)
902                    throws PortalException {
903    
904                    if ((toCategoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
905                            (toCategoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
906    
907                            return;
908                    }
909    
910                    List<MBCategory> categories = mbCategoryPersistence.findByG_P(
911                            fromCategory.getGroupId(), fromCategory.getCategoryId());
912    
913                    for (MBCategory category : categories) {
914                            mergeCategories(category, toCategoryId);
915                    }
916    
917                    List<MBThread> threads = mbThreadPersistence.findByG_C(
918                            fromCategory.getGroupId(), fromCategory.getCategoryId());
919    
920                    for (MBThread thread : threads) {
921    
922                            // Thread
923    
924                            thread.setCategoryId(toCategoryId);
925    
926                            mbThreadPersistence.update(thread);
927    
928                            List<MBMessage> messages = mbMessagePersistence.findByThreadId(
929                                    thread.getThreadId());
930    
931                            for (MBMessage message : messages) {
932    
933                                    // Message
934    
935                                    message.setCategoryId(toCategoryId);
936    
937                                    mbMessagePersistence.update(message);
938    
939                                    // Indexer
940    
941                                    Indexer<MBMessage> indexer =
942                                            IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);
943    
944                                    indexer.reindex(message);
945                            }
946                    }
947    
948                    MBCategory toCategory = mbCategoryPersistence.findByPrimaryKey(
949                            toCategoryId);
950    
951                    toCategory.setThreadCount(
952                            fromCategory.getThreadCount() + toCategory.getThreadCount());
953                    toCategory.setMessageCount(
954                            fromCategory.getMessageCount() + toCategory.getMessageCount());
955    
956                    mbCategoryPersistence.update(toCategory);
957    
958                    mbCategoryLocalService.deleteCategory(fromCategory);
959            }
960    
961            protected void moveDependentsToTrash(
962                            User user, List<Object> categoriesAndThreads, long trashEntryId)
963                    throws PortalException {
964    
965                    for (Object object : categoriesAndThreads) {
966                            if (object instanceof MBThread) {
967    
968                                    // Thread
969    
970                                    MBThread thread = (MBThread)object;
971    
972                                    int oldStatus = thread.getStatus();
973    
974                                    if (oldStatus == WorkflowConstants.STATUS_IN_TRASH) {
975                                            continue;
976                                    }
977    
978                                    thread.setStatus(WorkflowConstants.STATUS_IN_TRASH);
979    
980                                    mbThreadPersistence.update(thread);
981    
982                                    // Trash
983    
984                                    if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
985                                            trashVersionLocalService.addTrashVersion(
986                                                    trashEntryId, MBThread.class.getName(),
987                                                    thread.getThreadId(), oldStatus, null);
988                                    }
989    
990                                    // Threads
991    
992                                    mbThreadLocalService.moveDependentsToTrash(
993                                            thread.getGroupId(), thread.getThreadId(), trashEntryId);
994    
995                                    // Indexer
996    
997                                    Indexer<MBThread> indexer =
998                                            IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);
999    
1000                                    indexer.reindex(thread);
1001                            }
1002                            else if (object instanceof MBCategory) {
1003    
1004                                    // Category
1005    
1006                                    MBCategory category = (MBCategory)object;
1007    
1008                                    if (category.isInTrash()) {
1009                                            continue;
1010                                    }
1011    
1012                                    int oldStatus = category.getStatus();
1013    
1014                                    category.setStatus(WorkflowConstants.STATUS_IN_TRASH);
1015    
1016                                    mbCategoryPersistence.update(category);
1017    
1018                                    // Trash
1019    
1020                                    if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
1021                                            trashVersionLocalService.addTrashVersion(
1022                                                    trashEntryId, MBCategory.class.getName(),
1023                                                    category.getCategoryId(), oldStatus, null);
1024                                    }
1025    
1026                                    // Categories and threads
1027    
1028                                    moveDependentsToTrash(
1029                                            user,
1030                                            getCategoriesAndThreads(
1031                                                    category.getGroupId(), category.getCategoryId()),
1032                                            trashEntryId);
1033                            }
1034                    }
1035            }
1036    
1037            protected void restoreDependentsFromTrash(
1038                            User user, List<Object> categoriesAndThreads)
1039                    throws PortalException {
1040    
1041                    for (Object object : categoriesAndThreads) {
1042                            if (object instanceof MBThread) {
1043    
1044                                    // Thread
1045    
1046                                    MBThread thread = (MBThread)object;
1047    
1048                                    if (!thread.isInTrashImplicitly()) {
1049                                            continue;
1050                                    }
1051    
1052                                    TrashVersion trashVersion =
1053                                            trashVersionLocalService.fetchVersion(
1054                                                    MBThread.class.getName(), thread.getThreadId());
1055    
1056                                    int oldStatus = WorkflowConstants.STATUS_APPROVED;
1057    
1058                                    if (trashVersion != null) {
1059                                            oldStatus = trashVersion.getStatus();
1060                                    }
1061    
1062                                    thread.setStatus(oldStatus);
1063    
1064                                    mbThreadPersistence.update(thread);
1065    
1066                                    // Threads
1067    
1068                                    mbThreadLocalService.restoreDependentsFromTrash(
1069                                            thread.getGroupId(), thread.getThreadId());
1070    
1071                                    // Trash
1072    
1073                                    if (trashVersion != null) {
1074                                            trashVersionLocalService.deleteTrashVersion(trashVersion);
1075                                    }
1076    
1077                                    // Indexer
1078    
1079                                    Indexer<MBThread> indexer =
1080                                            IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);
1081    
1082                                    indexer.reindex(thread);
1083                            }
1084                            else if (object instanceof MBCategory) {
1085    
1086                                    // Category
1087    
1088                                    MBCategory category = (MBCategory)object;
1089    
1090                                    if (!category.isInTrashImplicitly()) {
1091                                            continue;
1092                                    }
1093    
1094                                    TrashVersion trashVersion =
1095                                            trashVersionLocalService.fetchVersion(
1096                                                    MBCategory.class.getName(), category.getCategoryId());
1097    
1098                                    int oldStatus = WorkflowConstants.STATUS_APPROVED;
1099    
1100                                    if (trashVersion != null) {
1101                                            oldStatus = trashVersion.getStatus();
1102                                    }
1103    
1104                                    category.setStatus(oldStatus);
1105    
1106                                    mbCategoryPersistence.update(category);
1107    
1108                                    // Categories and threads
1109    
1110                                    restoreDependentsFromTrash(
1111                                            user,
1112                                            getCategoriesAndThreads(
1113                                                    category.getGroupId(), category.getCategoryId()));
1114    
1115                                    // Trash
1116    
1117                                    if (trashVersion != null) {
1118                                            trashVersionLocalService.deleteTrashVersion(trashVersion);
1119                                    }
1120                            }
1121                    }
1122            }
1123    
1124            protected void updateChildCategoriesDisplayStyle(
1125                            MBCategory category, String displayStyle)
1126                    throws PortalException {
1127    
1128                    List<MBCategory> categories = getCategories(
1129                            category.getGroupId(), category.getCategoryId(), QueryUtil.ALL_POS,
1130                            QueryUtil.ALL_POS);
1131    
1132                    for (MBCategory curCategory : categories) {
1133                            updateChildCategoriesDisplayStyle(curCategory, displayStyle);
1134    
1135                            curCategory.setDisplayStyle(displayStyle);
1136    
1137                            mbCategoryPersistence.update(curCategory);
1138                    }
1139            }
1140    
1141            protected void validate(String name) throws PortalException {
1142                    if (Validator.isNull(name)) {
1143                            throw new CategoryNameException();
1144                    }
1145            }
1146    
1147    }