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.exception.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                    QueryDefinition<?> queryDefinition = new QueryDefinition<>(
369                            WorkflowConstants.STATUS_ANY);
370    
371                    return mbCategoryFinder.findC_T_ByG_C(
372                            groupId, categoryId, queryDefinition);
373            }
374    
375            @Override
376            public List<Object> getCategoriesAndThreads(
377                    long groupId, long categoryId, int status) {
378    
379                    QueryDefinition<?> queryDefinition = new QueryDefinition<>(status);
380    
381                    return mbCategoryFinder.findC_T_ByG_C(
382                            groupId, categoryId, queryDefinition);
383            }
384    
385            @Override
386            public List<Object> getCategoriesAndThreads(
387                    long groupId, long categoryId, int status, int start, int end) {
388    
389                    QueryDefinition<?> queryDefinition = new QueryDefinition<>(
390                            status, start, end, null);
391    
392                    return mbCategoryFinder.findC_T_ByG_C(
393                            groupId, categoryId, queryDefinition);
394            }
395    
396            @Override
397            public int getCategoriesAndThreadsCount(long groupId, long categoryId) {
398                    QueryDefinition<?> queryDefinition = new QueryDefinition<>(
399                            WorkflowConstants.STATUS_ANY);
400    
401                    return mbCategoryFinder.countC_T_ByG_C(
402                            groupId, categoryId, queryDefinition);
403            }
404    
405            @Override
406            public int getCategoriesAndThreadsCount(
407                    long groupId, long categoryId, int status) {
408    
409                    QueryDefinition<?> queryDefinition = new QueryDefinition<>(status);
410    
411                    return mbCategoryFinder.countC_T_ByG_C(
412                            groupId, categoryId, queryDefinition);
413            }
414    
415            @Override
416            public int getCategoriesCount(long groupId) {
417                    return mbCategoryPersistence.countByGroupId(groupId);
418            }
419    
420            @Override
421            public int getCategoriesCount(long groupId, int status) {
422                    return mbCategoryPersistence.countByG_S(groupId, status);
423            }
424    
425            @Override
426            public int getCategoriesCount(long groupId, long parentCategoryId) {
427                    return mbCategoryPersistence.countByG_P(groupId, parentCategoryId);
428            }
429    
430            @Override
431            public int getCategoriesCount(
432                    long groupId, long parentCategoryId, int status) {
433    
434                    if (status == WorkflowConstants.STATUS_ANY) {
435                            return mbCategoryPersistence.countByG_P(groupId, parentCategoryId);
436                    }
437    
438                    return mbCategoryPersistence.countByG_P_S(
439                            groupId, parentCategoryId, status);
440            }
441    
442            @Override
443            public int getCategoriesCount(
444                    long groupId, long excludedCategoryId, long parentCategoryId,
445                    int status) {
446    
447                    if (status == WorkflowConstants.STATUS_ANY) {
448                            return mbCategoryPersistence.countByNotC_G_P(
449                                    excludedCategoryId, groupId, parentCategoryId);
450                    }
451    
452                    return mbCategoryPersistence.countByNotC_G_P_S(
453                            excludedCategoryId, groupId, parentCategoryId, status);
454            }
455    
456            @Override
457            public int getCategoriesCount(long groupId, long[] parentCategoryIds) {
458                    return mbCategoryPersistence.countByG_P(groupId, parentCategoryIds);
459            }
460    
461            @Override
462            public int getCategoriesCount(
463                    long groupId, long[] parentCategoryIds, int status) {
464    
465                    if (status == WorkflowConstants.STATUS_ANY) {
466                            return mbCategoryPersistence.countByG_P(groupId, parentCategoryIds);
467                    }
468    
469                    return mbCategoryPersistence.countByG_P_S(
470                            groupId, parentCategoryIds, status);
471            }
472    
473            @Override
474            public int getCategoriesCount(
475                    long groupId, long[] excludedCategoryIds, long[] parentCategoryIds,
476                    int status) {
477    
478                    if (status == WorkflowConstants.STATUS_ANY) {
479                            return mbCategoryPersistence.countByNotC_G_P(
480                                    excludedCategoryIds, groupId, parentCategoryIds);
481                    }
482    
483                    return mbCategoryPersistence.countByNotC_G_P_S(
484                            excludedCategoryIds, groupId, parentCategoryIds, status);
485            }
486    
487            @Override
488            public MBCategory getCategory(long categoryId) throws PortalException {
489                    MBCategory category = null;
490    
491                    if ((categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
492                            (categoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
493    
494                            category = mbCategoryPersistence.findByPrimaryKey(categoryId);
495                    }
496                    else {
497                            category = new MBCategoryImpl();
498    
499                            category.setCategoryId(categoryId);
500                            category.setParentCategoryId(categoryId);
501                    }
502    
503                    return category;
504            }
505    
506            @Override
507            public List<MBCategory> getCompanyCategories(
508                    long companyId, int start, int end) {
509    
510                    return mbCategoryPersistence.findByCompanyId(companyId, start, end);
511            }
512    
513            @Override
514            public int getCompanyCategoriesCount(long companyId) {
515                    return mbCategoryPersistence.countByCompanyId(companyId);
516            }
517    
518            @Override
519            public List<Long> getSubcategoryIds(
520                    List<Long> categoryIds, long groupId, long categoryId) {
521    
522                    List<MBCategory> categories = mbCategoryPersistence.findByG_P(
523                            groupId, categoryId);
524    
525                    for (MBCategory category : categories) {
526                            categoryIds.add(category.getCategoryId());
527    
528                            getSubcategoryIds(
529                                    categoryIds, category.getGroupId(), category.getCategoryId());
530                    }
531    
532                    return categoryIds;
533            }
534    
535            @Override
536            public List<MBCategory> getSubscribedCategories(
537                    long groupId, long userId, int start, int end) {
538    
539                    QueryDefinition<MBCategory> queryDefinition = new QueryDefinition<>(
540                            WorkflowConstants.STATUS_ANY, start, end, null);
541    
542                    return mbCategoryFinder.findC_ByS_G_U_P(
543                            groupId, userId, null, queryDefinition);
544            }
545    
546            @Override
547            public int getSubscribedCategoriesCount(long groupId, long userId) {
548                    QueryDefinition<MBCategory> queryDefinition = new QueryDefinition<>(
549                            WorkflowConstants.STATUS_ANY);
550    
551                    return mbCategoryFinder.countC_ByS_G_U_P(
552                            groupId, userId, null, queryDefinition);
553            }
554    
555            @Override
556            public void moveCategoriesToTrash(long groupId, long userId)
557                    throws PortalException {
558    
559                    List<MBCategory> categories = mbCategoryPersistence.findByGroupId(
560                            groupId);
561    
562                    for (MBCategory category : categories) {
563                            moveCategoryToTrash(userId, category.getCategoryId());
564                    }
565            }
566    
567            @Override
568            public MBCategory moveCategory(
569                            long categoryId, long parentCategoryId,
570                            boolean mergeWithParentCategory)
571                    throws PortalException {
572    
573                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
574                            categoryId);
575    
576                    parentCategoryId = getParentCategoryId(category, parentCategoryId);
577    
578                    if (mergeWithParentCategory && (categoryId != parentCategoryId) &&
579                            (parentCategoryId !=
580                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
581                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
582    
583                            mergeCategories(category, parentCategoryId);
584    
585                            return category;
586                    }
587    
588                    category.setParentCategoryId(parentCategoryId);
589    
590                    return mbCategoryPersistence.update(category);
591            }
592    
593            @Override
594            public MBCategory moveCategoryFromTrash(
595                            long userId, long categoryId, long newCategoryId)
596                    throws PortalException {
597    
598                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
599                            categoryId);
600    
601                    if (category.isInTrashExplicitly()) {
602                            restoreCategoryFromTrash(userId, categoryId);
603                    }
604                    else {
605    
606                            // Category
607    
608                            TrashVersion trashVersion = trashVersionLocalService.fetchVersion(
609                                    MBCategory.class.getName(), category.getCategoryId());
610    
611                            int status = WorkflowConstants.STATUS_APPROVED;
612    
613                            if (trashVersion != null) {
614                                    status = trashVersion.getStatus();
615                            }
616    
617                            updateStatus(userId, categoryId, status);
618    
619                            // Trash
620    
621                            if (trashVersion != null) {
622                                    trashVersionLocalService.deleteTrashVersion(trashVersion);
623                            }
624    
625                            // Categories and threads
626    
627                            User user = userPersistence.findByPrimaryKey(userId);
628    
629                            List<Object> categoriesAndThreads = getCategoriesAndThreads(
630                                    category.getGroupId(), categoryId,
631                                    WorkflowConstants.STATUS_IN_TRASH);
632    
633                            restoreDependentsFromTrash(user, categoriesAndThreads);
634                    }
635    
636                    return moveCategory(categoryId, newCategoryId, false);
637            }
638    
639            @Override
640            public MBCategory moveCategoryToTrash(long userId, long categoryId)
641                    throws PortalException {
642    
643                    // Category
644    
645                    MBCategory category = updateStatus(
646                            userId, categoryId, WorkflowConstants.STATUS_IN_TRASH);
647    
648                    // Trash
649    
650                    TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
651                            userId, category.getGroupId(), MBCategory.class.getName(),
652                            categoryId, category.getUuid(), null,
653                            WorkflowConstants.STATUS_APPROVED, null, null);
654    
655                    // Categories and threads
656    
657                    User user = userPersistence.findByPrimaryKey(userId);
658    
659                    List<Object> categoriesAndThreads = getCategoriesAndThreads(
660                            category.getGroupId(), categoryId);
661    
662                    moveDependentsToTrash(
663                            user, categoriesAndThreads, trashEntry.getEntryId());
664    
665                    return category;
666            }
667    
668            @Override
669            public void restoreCategoryFromTrash(long userId, long categoryId)
670                    throws PortalException {
671    
672                    // Category
673    
674                    TrashEntry trashEntry = trashEntryLocalService.getEntry(
675                            MBCategory.class.getName(), categoryId);
676    
677                    MBCategory category = updateStatus(
678                            userId, categoryId, WorkflowConstants.STATUS_APPROVED);
679    
680                    // Categories and threads
681    
682                    User user = userPersistence.findByPrimaryKey(userId);
683    
684                    List<Object> categoriesAndThreads = getCategoriesAndThreads(
685                            category.getGroupId(), categoryId,
686                            WorkflowConstants.STATUS_IN_TRASH);
687    
688                    restoreDependentsFromTrash(user, categoriesAndThreads);
689    
690                    // Trash
691    
692                    trashEntryLocalService.deleteEntry(trashEntry.getEntryId());
693            }
694    
695            @Override
696            public void subscribeCategory(long userId, long groupId, long categoryId)
697                    throws PortalException {
698    
699                    if (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
700                            categoryId = groupId;
701                    }
702    
703                    subscriptionLocalService.addSubscription(
704                            userId, groupId, MBCategory.class.getName(), categoryId);
705            }
706    
707            @Override
708            public void unsubscribeCategory(long userId, long groupId, long categoryId)
709                    throws PortalException {
710    
711                    if (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
712                            categoryId = groupId;
713                    }
714    
715                    subscriptionLocalService.deleteSubscription(
716                            userId, MBCategory.class.getName(), categoryId);
717            }
718    
719            @Override
720            public MBCategory updateCategory(
721                            long categoryId, long parentCategoryId, String name,
722                            String description, String displayStyle, String emailAddress,
723                            String inProtocol, String inServerName, int inServerPort,
724                            boolean inUseSSL, String inUserName, String inPassword,
725                            int inReadInterval, String outEmailAddress, boolean outCustom,
726                            String outServerName, int outServerPort, boolean outUseSSL,
727                            String outUserName, String outPassword, boolean allowAnonymous,
728                            boolean mailingListActive, boolean mergeWithParentCategory,
729                            ServiceContext serviceContext)
730                    throws PortalException {
731    
732                    // Merge categories
733    
734                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
735                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
736    
737                            return null;
738                    }
739    
740                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
741                            categoryId);
742    
743                    parentCategoryId = getParentCategoryId(category, parentCategoryId);
744    
745                    if (mergeWithParentCategory && (categoryId != parentCategoryId) &&
746                            (parentCategoryId !=
747                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
748                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
749    
750                            mergeCategories(category, parentCategoryId);
751    
752                            return category;
753                    }
754    
755                    // Category
756    
757                    validate(name);
758    
759                    category.setParentCategoryId(parentCategoryId);
760                    category.setName(name);
761                    category.setDescription(description);
762    
763                    if (!displayStyle.equals(category.getDisplayStyle())) {
764                            category.setDisplayStyle(displayStyle);
765    
766                            updateChildCategoriesDisplayStyle(category, displayStyle);
767                    }
768    
769                    category.setExpandoBridgeAttributes(serviceContext);
770    
771                    mbCategoryPersistence.update(category);
772    
773                    // Mailing list
774    
775                    MBMailingList mailingList = mbMailingListPersistence.fetchByG_C(
776                            category.getGroupId(), category.getCategoryId());
777    
778                    if (mailingList != null) {
779                            mbMailingListLocalService.updateMailingList(
780                                    mailingList.getMailingListId(), emailAddress, inProtocol,
781                                    inServerName, inServerPort, inUseSSL, inUserName, inPassword,
782                                    inReadInterval, outEmailAddress, outCustom, outServerName,
783                                    outServerPort, outUseSSL, outUserName, outPassword,
784                                    allowAnonymous, mailingListActive, serviceContext);
785                    }
786                    else {
787                            mbMailingListLocalService.addMailingList(
788                                    category.getUserId(), category.getGroupId(),
789                                    category.getCategoryId(), emailAddress, inProtocol,
790                                    inServerName, inServerPort, inUseSSL, inUserName, inPassword,
791                                    inReadInterval, outEmailAddress, outCustom, outServerName,
792                                    outServerPort, outUseSSL, outUserName, outPassword,
793                                    allowAnonymous, mailingListActive, serviceContext);
794                    }
795    
796                    return category;
797            }
798    
799            @Override
800            public MBCategory updateMessageCount(long categoryId) {
801                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
802                            categoryId);
803    
804                    if (mbCategory == null) {
805                            return null;
806                    }
807    
808                    int messageCount = mbMessageLocalService.getCategoryMessagesCount(
809                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
810                            WorkflowConstants.STATUS_APPROVED);
811    
812                    mbCategory.setMessageCount(messageCount);
813    
814                    return mbCategoryPersistence.update(mbCategory);
815            }
816    
817            @Override
818            public MBCategory updateStatistics(long categoryId) {
819                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
820                            categoryId);
821    
822                    if (mbCategory == null) {
823                            return null;
824                    }
825    
826                    int messageCount = mbMessageLocalService.getCategoryMessagesCount(
827                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
828                            WorkflowConstants.STATUS_APPROVED);
829    
830                    mbCategory.setMessageCount(messageCount);
831    
832                    int threadCount = mbThreadLocalService.getCategoryThreadsCount(
833                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
834                            WorkflowConstants.STATUS_APPROVED);
835    
836                    mbCategory.setThreadCount(threadCount);
837    
838                    return mbCategoryPersistence.update(mbCategory);
839            }
840    
841            @Override
842            public MBCategory updateStatus(long userId, long categoryId, int status)
843                    throws PortalException {
844    
845                    // Category
846    
847                    User user = userPersistence.findByPrimaryKey(userId);
848    
849                    MBCategory category = mbCategoryPersistence.findByPrimaryKey(
850                            categoryId);
851    
852                    category.setStatus(status);
853                    category.setStatusByUserId(user.getUserId());
854                    category.setStatusByUserName(user.getFullName());
855                    category.setStatusDate(new Date());
856    
857                    mbCategoryPersistence.update(category);
858    
859                    return category;
860            }
861    
862            @Override
863            public MBCategory updateThreadCount(long categoryId) {
864                    MBCategory mbCategory = mbCategoryPersistence.fetchByPrimaryKey(
865                            categoryId);
866    
867                    if (mbCategory == null) {
868                            return null;
869                    }
870    
871                    int threadCount = mbThreadLocalService.getCategoryThreadsCount(
872                            mbCategory.getGroupId(), mbCategory.getCategoryId(),
873                            WorkflowConstants.STATUS_APPROVED);
874    
875                    mbCategory.setThreadCount(threadCount);
876    
877                    return mbCategoryPersistence.update(mbCategory);
878            }
879    
880            protected long getParentCategoryId(long groupId, long parentCategoryId) {
881                    if ((parentCategoryId !=
882                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) &&
883                            (parentCategoryId != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
884    
885                            MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
886                                    parentCategoryId);
887    
888                            if ((parentCategory == null) ||
889                                    (groupId != parentCategory.getGroupId())) {
890    
891                                    parentCategoryId =
892                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
893                            }
894                    }
895    
896                    return parentCategoryId;
897            }
898    
899            protected long getParentCategoryId(
900                    MBCategory category, long parentCategoryId) {
901    
902                    if ((parentCategoryId ==
903                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
904                            (parentCategoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
905    
906                            return parentCategoryId;
907                    }
908    
909                    if (category.getCategoryId() == parentCategoryId) {
910                            return category.getParentCategoryId();
911                    }
912    
913                    MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
914                            parentCategoryId);
915    
916                    if ((parentCategory == null) ||
917                            (category.getGroupId() != parentCategory.getGroupId())) {
918    
919                            return category.getParentCategoryId();
920                    }
921    
922                    List<Long> subcategoryIds = new ArrayList<>();
923    
924                    getSubcategoryIds(
925                            subcategoryIds, category.getGroupId(), category.getCategoryId());
926    
927                    if (subcategoryIds.contains(parentCategoryId)) {
928                            return category.getParentCategoryId();
929                    }
930    
931                    return parentCategoryId;
932            }
933    
934            protected void mergeCategories(MBCategory fromCategory, long toCategoryId)
935                    throws PortalException {
936    
937                    if ((toCategoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
938                            (toCategoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
939    
940                            return;
941                    }
942    
943                    List<MBCategory> categories = mbCategoryPersistence.findByG_P(
944                            fromCategory.getGroupId(), fromCategory.getCategoryId());
945    
946                    for (MBCategory category : categories) {
947                            mergeCategories(category, toCategoryId);
948                    }
949    
950                    List<MBThread> threads = mbThreadPersistence.findByG_C(
951                            fromCategory.getGroupId(), fromCategory.getCategoryId());
952    
953                    for (MBThread thread : threads) {
954    
955                            // Thread
956    
957                            thread.setCategoryId(toCategoryId);
958    
959                            mbThreadPersistence.update(thread);
960    
961                            List<MBMessage> messages = mbMessagePersistence.findByThreadId(
962                                    thread.getThreadId());
963    
964                            for (MBMessage message : messages) {
965    
966                                    // Message
967    
968                                    message.setCategoryId(toCategoryId);
969    
970                                    mbMessagePersistence.update(message);
971    
972                                    // Indexer
973    
974                                    Indexer<MBMessage> indexer =
975                                            IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);
976    
977                                    indexer.reindex(message);
978                            }
979                    }
980    
981                    MBCategory toCategory = mbCategoryPersistence.findByPrimaryKey(
982                            toCategoryId);
983    
984                    toCategory.setThreadCount(
985                            fromCategory.getThreadCount() + toCategory.getThreadCount());
986                    toCategory.setMessageCount(
987                            fromCategory.getMessageCount() + toCategory.getMessageCount());
988    
989                    mbCategoryPersistence.update(toCategory);
990    
991                    mbCategoryLocalService.deleteCategory(fromCategory);
992            }
993    
994            protected void moveDependentsToTrash(
995                            User user, List<Object> categoriesAndThreads, long trashEntryId)
996                    throws PortalException {
997    
998                    for (Object object : categoriesAndThreads) {
999                            if (object instanceof MBThread) {
1000    
1001                                    // Thread
1002    
1003                                    MBThread thread = (MBThread)object;
1004    
1005                                    int oldStatus = thread.getStatus();
1006    
1007                                    if (oldStatus == WorkflowConstants.STATUS_IN_TRASH) {
1008                                            continue;
1009                                    }
1010    
1011                                    thread.setStatus(WorkflowConstants.STATUS_IN_TRASH);
1012    
1013                                    mbThreadPersistence.update(thread);
1014    
1015                                    // Trash
1016    
1017                                    if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
1018                                            trashVersionLocalService.addTrashVersion(
1019                                                    trashEntryId, MBThread.class.getName(),
1020                                                    thread.getThreadId(), oldStatus, null);
1021                                    }
1022    
1023                                    // Threads
1024    
1025                                    mbThreadLocalService.moveDependentsToTrash(
1026                                            thread.getGroupId(), thread.getThreadId(), trashEntryId);
1027    
1028                                    // Indexer
1029    
1030                                    Indexer<MBThread> indexer =
1031                                            IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);
1032    
1033                                    indexer.reindex(thread);
1034                            }
1035                            else if (object instanceof MBCategory) {
1036    
1037                                    // Category
1038    
1039                                    MBCategory category = (MBCategory)object;
1040    
1041                                    if (category.isInTrash()) {
1042                                            continue;
1043                                    }
1044    
1045                                    int oldStatus = category.getStatus();
1046    
1047                                    category.setStatus(WorkflowConstants.STATUS_IN_TRASH);
1048    
1049                                    mbCategoryPersistence.update(category);
1050    
1051                                    // Trash
1052    
1053                                    if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
1054                                            trashVersionLocalService.addTrashVersion(
1055                                                    trashEntryId, MBCategory.class.getName(),
1056                                                    category.getCategoryId(), oldStatus, null);
1057                                    }
1058    
1059                                    // Categories and threads
1060    
1061                                    moveDependentsToTrash(
1062                                            user,
1063                                            getCategoriesAndThreads(
1064                                                    category.getGroupId(), category.getCategoryId()),
1065                                            trashEntryId);
1066                            }
1067                    }
1068            }
1069    
1070            protected void restoreDependentsFromTrash(
1071                            User user, List<Object> categoriesAndThreads)
1072                    throws PortalException {
1073    
1074                    for (Object object : categoriesAndThreads) {
1075                            if (object instanceof MBThread) {
1076    
1077                                    // Thread
1078    
1079                                    MBThread thread = (MBThread)object;
1080    
1081                                    if (!thread.isInTrashImplicitly()) {
1082                                            continue;
1083                                    }
1084    
1085                                    TrashVersion trashVersion =
1086                                            trashVersionLocalService.fetchVersion(
1087                                                    MBThread.class.getName(), thread.getThreadId());
1088    
1089                                    int oldStatus = WorkflowConstants.STATUS_APPROVED;
1090    
1091                                    if (trashVersion != null) {
1092                                            oldStatus = trashVersion.getStatus();
1093                                    }
1094    
1095                                    thread.setStatus(oldStatus);
1096    
1097                                    mbThreadPersistence.update(thread);
1098    
1099                                    // Threads
1100    
1101                                    mbThreadLocalService.restoreDependentsFromTrash(
1102                                            thread.getGroupId(), thread.getThreadId());
1103    
1104                                    // Trash
1105    
1106                                    if (trashVersion != null) {
1107                                            trashVersionLocalService.deleteTrashVersion(trashVersion);
1108                                    }
1109    
1110                                    // Indexer
1111    
1112                                    Indexer<MBThread> indexer =
1113                                            IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);
1114    
1115                                    indexer.reindex(thread);
1116                            }
1117                            else if (object instanceof MBCategory) {
1118    
1119                                    // Category
1120    
1121                                    MBCategory category = (MBCategory)object;
1122    
1123                                    if (!category.isInTrashImplicitly()) {
1124                                            continue;
1125                                    }
1126    
1127                                    TrashVersion trashVersion =
1128                                            trashVersionLocalService.fetchVersion(
1129                                                    MBCategory.class.getName(), category.getCategoryId());
1130    
1131                                    int oldStatus = WorkflowConstants.STATUS_APPROVED;
1132    
1133                                    if (trashVersion != null) {
1134                                            oldStatus = trashVersion.getStatus();
1135                                    }
1136    
1137                                    category.setStatus(oldStatus);
1138    
1139                                    mbCategoryPersistence.update(category);
1140    
1141                                    // Categories and threads
1142    
1143                                    restoreDependentsFromTrash(
1144                                            user,
1145                                            getCategoriesAndThreads(
1146                                                    category.getGroupId(), category.getCategoryId(),
1147                                                    WorkflowConstants.STATUS_IN_TRASH));
1148    
1149                                    // Trash
1150    
1151                                    if (trashVersion != null) {
1152                                            trashVersionLocalService.deleteTrashVersion(trashVersion);
1153                                    }
1154                            }
1155                    }
1156            }
1157    
1158            protected void updateChildCategoriesDisplayStyle(
1159                            MBCategory category, String displayStyle)
1160                    throws PortalException {
1161    
1162                    List<MBCategory> categories = getCategories(
1163                            category.getGroupId(), category.getCategoryId(), QueryUtil.ALL_POS,
1164                            QueryUtil.ALL_POS);
1165    
1166                    for (MBCategory curCategory : categories) {
1167                            updateChildCategoriesDisplayStyle(curCategory, displayStyle);
1168    
1169                            curCategory.setDisplayStyle(displayStyle);
1170    
1171                            mbCategoryPersistence.update(curCategory);
1172                    }
1173            }
1174    
1175            protected void validate(String name) throws PortalException {
1176                    if (Validator.isNull(name)) {
1177                            throw new CategoryNameException("Name is null");
1178                    }
1179            }
1180    
1181    }