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