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.asset.service.impl;
016    
017    import com.liferay.portal.kernel.cache.thread.local.ThreadLocalCachable;
018    import com.liferay.portal.kernel.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.search.BaseModelSearchResult;
021    import com.liferay.portal.kernel.search.Document;
022    import com.liferay.portal.kernel.search.Field;
023    import com.liferay.portal.kernel.search.Hits;
024    import com.liferay.portal.kernel.search.Indexable;
025    import com.liferay.portal.kernel.search.IndexableType;
026    import com.liferay.portal.kernel.search.Indexer;
027    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028    import com.liferay.portal.kernel.search.QueryConfig;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.search.SearchException;
031    import com.liferay.portal.kernel.search.Sort;
032    import com.liferay.portal.kernel.systemevent.SystemEvent;
033    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackUtil;
034    import com.liferay.portal.kernel.util.CharPool;
035    import com.liferay.portal.kernel.util.GetterUtil;
036    import com.liferay.portal.kernel.util.ListUtil;
037    import com.liferay.portal.kernel.util.LocaleUtil;
038    import com.liferay.portal.kernel.util.OrderByComparator;
039    import com.liferay.portal.kernel.util.StringBundler;
040    import com.liferay.portal.kernel.util.StringPool;
041    import com.liferay.portal.kernel.util.StringUtil;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.model.ModelHintsUtil;
044    import com.liferay.portal.model.ResourceConstants;
045    import com.liferay.portal.model.SystemEventConstants;
046    import com.liferay.portal.model.User;
047    import com.liferay.portal.service.ServiceContext;
048    import com.liferay.portal.service.permission.ModelPermissions;
049    import com.liferay.portlet.asset.exception.AssetCategoryNameException;
050    import com.liferay.portlet.asset.exception.DuplicateCategoryException;
051    import com.liferay.portlet.asset.model.AssetCategory;
052    import com.liferay.portlet.asset.model.AssetCategoryConstants;
053    import com.liferay.portlet.asset.model.AssetCategoryProperty;
054    import com.liferay.portlet.asset.model.AssetEntry;
055    import com.liferay.portlet.asset.service.base.AssetCategoryLocalServiceBaseImpl;
056    import com.liferay.portlet.asset.util.comparator.AssetCategoryLeftCategoryIdComparator;
057    
058    import java.io.Serializable;
059    
060    import java.util.ArrayList;
061    import java.util.Collections;
062    import java.util.HashMap;
063    import java.util.Iterator;
064    import java.util.List;
065    import java.util.Locale;
066    import java.util.Map;
067    import java.util.concurrent.Callable;
068    
069    /**
070     * Provides the local service for accessing, adding, deleting, merging, moving,
071     * and updating asset categories.
072     *
073     * @author Brian Wing Shun Chan
074     * @author Alvaro del Castillo
075     * @author Jorge Ferrer
076     * @author Bruno Farache
077     */
078    public class AssetCategoryLocalServiceImpl
079            extends AssetCategoryLocalServiceBaseImpl {
080    
081            @Indexable(type = IndexableType.REINDEX)
082            @Override
083            public AssetCategory addCategory(
084                            long userId, long groupId, long parentCategoryId,
085                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
086                            long vocabularyId, String[] categoryProperties,
087                            ServiceContext serviceContext)
088                    throws PortalException {
089    
090                    // Category
091    
092                    User user = userPersistence.findByPrimaryKey(userId);
093    
094                    String name = titleMap.get(LocaleUtil.getSiteDefault());
095    
096                    name = ModelHintsUtil.trimString(
097                            AssetCategory.class.getName(), "name", name);
098    
099                    if (categoryProperties == null) {
100                            categoryProperties = new String[0];
101                    }
102    
103                    validate(0, parentCategoryId, name, vocabularyId);
104    
105                    if (parentCategoryId > 0) {
106                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
107                    }
108    
109                    assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
110    
111                    long categoryId = counterLocalService.increment();
112    
113                    AssetCategory category = assetCategoryPersistence.create(categoryId);
114    
115                    category.setUuid(serviceContext.getUuid());
116                    category.setGroupId(groupId);
117                    category.setCompanyId(user.getCompanyId());
118                    category.setUserId(user.getUserId());
119                    category.setUserName(user.getFullName());
120                    category.setParentCategoryId(parentCategoryId);
121                    category.setName(name);
122                    category.setTitleMap(titleMap);
123                    category.setDescriptionMap(descriptionMap);
124                    category.setVocabularyId(vocabularyId);
125    
126                    assetCategoryPersistence.update(category);
127    
128                    // Resources
129    
130                    if (serviceContext.isAddGroupPermissions() ||
131                            serviceContext.isAddGuestPermissions()) {
132    
133                            addCategoryResources(
134                                    category, serviceContext.isAddGroupPermissions(),
135                                    serviceContext.isAddGuestPermissions());
136                    }
137                    else {
138                            addCategoryResources(
139                                    category, serviceContext.getModelPermissions());
140                    }
141    
142                    // Properties
143    
144                    for (int i = 0; i < categoryProperties.length; i++) {
145                            String[] categoryProperty = StringUtil.split(
146                                    categoryProperties[i],
147                                    AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);
148    
149                            if (categoryProperty.length <= 1) {
150                                    categoryProperty = StringUtil.split(
151                                            categoryProperties[i], CharPool.COLON);
152                            }
153    
154                            String key = StringPool.BLANK;
155                            String value = StringPool.BLANK;
156    
157                            if (categoryProperty.length > 1) {
158                                    key = GetterUtil.getString(categoryProperty[0]);
159                                    value = GetterUtil.getString(categoryProperty[1]);
160                            }
161    
162                            if (Validator.isNotNull(key)) {
163                                    assetCategoryPropertyLocalService.addCategoryProperty(
164                                            userId, categoryId, key, value);
165                            }
166                    }
167    
168                    return category;
169            }
170    
171            @Override
172            public AssetCategory addCategory(
173                            long userId, long groupId, String title, long vocabularyId,
174                            ServiceContext serviceContext)
175                    throws PortalException {
176    
177                    Map<Locale, String> titleMap = new HashMap<>();
178    
179                    Locale locale = LocaleUtil.getSiteDefault();
180    
181                    titleMap.put(locale, title);
182    
183                    Map<Locale, String> descriptionMap = new HashMap<>();
184    
185                    descriptionMap.put(locale, StringPool.BLANK);
186    
187                    return assetCategoryLocalService.addCategory(
188                            userId, groupId, AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID,
189                            titleMap, descriptionMap, vocabularyId, null, serviceContext);
190            }
191    
192            @Override
193            public void addCategoryResources(
194                            AssetCategory category, boolean addGroupPermissions,
195                            boolean addGuestPermissions)
196                    throws PortalException {
197    
198                    resourceLocalService.addResources(
199                            category.getCompanyId(), category.getGroupId(),
200                            category.getUserId(), AssetCategory.class.getName(),
201                            category.getCategoryId(), false, addGroupPermissions,
202                            addGuestPermissions);
203            }
204    
205            @Override
206            public void addCategoryResources(
207                            AssetCategory category, ModelPermissions modelPermissions)
208                    throws PortalException {
209    
210                    resourceLocalService.addModelResources(
211                            category.getCompanyId(), category.getGroupId(),
212                            category.getUserId(), AssetCategory.class.getName(),
213                            category.getCategoryId(), modelPermissions);
214            }
215    
216            @Override
217            public void deleteCategories(List<AssetCategory> categories)
218                    throws PortalException {
219    
220                    List<Long> rebuildTreeGroupIds = new ArrayList<>();
221    
222                    for (AssetCategory category : categories) {
223                            if (!rebuildTreeGroupIds.contains(category.getGroupId()) &&
224                                    (getChildCategoriesCount(category.getCategoryId()) > 0)) {
225    
226                                    final long groupId = category.getGroupId();
227    
228                                    TransactionCommitCallbackUtil.registerCallback(
229                                            new Callable<Void>() {
230    
231                                                    @Override
232                                                    public Void call() throws Exception {
233                                                            assetCategoryLocalService.rebuildTree(
234                                                                    groupId, true);
235    
236                                                            return null;
237                                                    }
238    
239                                            });
240    
241                                    rebuildTreeGroupIds.add(groupId);
242                            }
243    
244                            assetCategoryLocalService.deleteCategory(category, true);
245                    }
246            }
247    
248            @Override
249            public void deleteCategories(long[] categoryIds) throws PortalException {
250                    List<AssetCategory> categories = new ArrayList<>();
251    
252                    for (long categoryId : categoryIds) {
253                            AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
254                                    categoryId);
255    
256                            categories.add(category);
257                    }
258    
259                    deleteCategories(categories);
260            }
261    
262            @Override
263            public AssetCategory deleteCategory(AssetCategory category)
264                    throws PortalException {
265    
266                    return assetCategoryLocalService.deleteCategory(category, false);
267            }
268    
269            @Indexable(type = IndexableType.DELETE)
270            @Override
271            @SystemEvent(type = SystemEventConstants.TYPE_DELETE)
272            public AssetCategory deleteCategory(
273                            AssetCategory category, boolean skipRebuildTree)
274                    throws PortalException {
275    
276                    // Categories
277    
278                    List<AssetCategory> categories =
279                            assetCategoryPersistence.findByParentCategoryId(
280                                    category.getCategoryId());
281    
282                    for (AssetCategory curCategory : categories) {
283                            assetCategoryLocalService.deleteCategory(curCategory, true);
284                    }
285    
286                    if (!categories.isEmpty() && !skipRebuildTree) {
287                            final long groupId = category.getGroupId();
288    
289                            TransactionCommitCallbackUtil.registerCallback(
290                                    new Callable<Void>() {
291    
292                                            @Override
293                                            public Void call() throws Exception {
294                                                    assetCategoryLocalService.rebuildTree(groupId, true);
295    
296                                                    return null;
297                                            }
298    
299                                    });
300                    }
301    
302                    // Entries
303    
304                    List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
305                            category.getCategoryId());
306    
307                    // Category
308    
309                    assetCategoryPersistence.remove(category);
310    
311                    // Resources
312    
313                    resourceLocalService.deleteResource(
314                            category.getCompanyId(), AssetCategory.class.getName(),
315                            ResourceConstants.SCOPE_INDIVIDUAL, category.getCategoryId());
316    
317                    // Properties
318    
319                    assetCategoryPropertyLocalService.deleteCategoryProperties(
320                            category.getCategoryId());
321    
322                    // Indexer
323    
324                    assetEntryLocalService.reindex(entries);
325    
326                    return category;
327            }
328    
329            @Override
330            public AssetCategory deleteCategory(long categoryId)
331                    throws PortalException {
332    
333                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
334                            categoryId);
335    
336                    return assetCategoryLocalService.deleteCategory(category);
337            }
338    
339            @Override
340            public void deleteVocabularyCategories(long vocabularyId)
341                    throws PortalException {
342    
343                    List<AssetCategory> categories = assetCategoryPersistence.findByP_V(
344                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId,
345                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
346                            new AssetCategoryLeftCategoryIdComparator(false));
347    
348                    assetCategoryLocalService.deleteCategories(categories);
349            }
350    
351            @Override
352            public AssetCategory fetchCategory(long categoryId) {
353                    return assetCategoryPersistence.fetchByPrimaryKey(categoryId);
354            }
355    
356            @Override
357            public List<AssetCategory> getCategories() {
358                    return assetCategoryPersistence.findAll();
359            }
360    
361            @Override
362            public List<AssetCategory> getCategories(Hits hits) throws PortalException {
363                    List<Document> documents = hits.toList();
364    
365                    List<AssetCategory> categories = new ArrayList<>(documents.size());
366    
367                    for (Document document : documents) {
368                            long categoryId = GetterUtil.getLong(
369                                    document.get(Field.ASSET_CATEGORY_ID));
370    
371                            AssetCategory category = fetchCategory(categoryId);
372    
373                            if (category == null) {
374                                    categories = null;
375    
376                                    Indexer<AssetCategory> indexer = IndexerRegistryUtil.getIndexer(
377                                            AssetCategory.class);
378    
379                                    long companyId = GetterUtil.getLong(
380                                            document.get(Field.COMPANY_ID));
381    
382                                    indexer.delete(companyId, document.getUID());
383                            }
384                            else if (categories != null) {
385                                    categories.add(category);
386                            }
387                    }
388    
389                    return categories;
390            }
391    
392            @Override
393            @ThreadLocalCachable
394            public List<AssetCategory> getCategories(long classNameId, long classPK) {
395                    AssetEntry entry = assetEntryPersistence.fetchByC_C(
396                            classNameId, classPK);
397    
398                    if (entry == null) {
399                            return Collections.emptyList();
400                    }
401    
402                    return assetEntryPersistence.getAssetCategories(entry.getEntryId());
403            }
404    
405            @Override
406            public List<AssetCategory> getCategories(String className, long classPK) {
407                    long classNameId = classNameLocalService.getClassNameId(className);
408    
409                    return getCategories(classNameId, classPK);
410            }
411    
412            @Override
413            public AssetCategory getCategory(long categoryId) throws PortalException {
414                    return assetCategoryPersistence.findByPrimaryKey(categoryId);
415            }
416    
417            @Override
418            public AssetCategory getCategory(String uuid, long groupId)
419                    throws PortalException {
420    
421                    return assetCategoryPersistence.findByUUID_G(uuid, groupId);
422            }
423    
424            @Override
425            public long[] getCategoryIds(String className, long classPK) {
426                    return getCategoryIds(getCategories(className, classPK));
427            }
428    
429            @Override
430            public String[] getCategoryNames() {
431                    return getCategoryNames(getCategories());
432            }
433    
434            @Override
435            public String[] getCategoryNames(long classNameId, long classPK) {
436                    return getCategoryNames(getCategories(classNameId, classPK));
437            }
438    
439            @Override
440            public String[] getCategoryNames(String className, long classPK) {
441                    return getCategoryNames(getCategories(className, classPK));
442            }
443    
444            @Override
445            public List<AssetCategory> getChildCategories(long parentCategoryId) {
446                    return assetCategoryPersistence.findByParentCategoryId(
447                            parentCategoryId);
448            }
449    
450            @Override
451            public List<AssetCategory> getChildCategories(
452                    long parentCategoryId, int start, int end,
453                    OrderByComparator<AssetCategory> obc) {
454    
455                    return assetCategoryPersistence.findByParentCategoryId(
456                            parentCategoryId, start, end, obc);
457            }
458    
459            @Override
460            public int getChildCategoriesCount(long parentCategoryId) {
461                    return assetCategoryPersistence.countByParentCategoryId(
462                            parentCategoryId);
463            }
464    
465            @Override
466            public List<AssetCategory> getEntryCategories(long entryId) {
467                    return assetEntryPersistence.getAssetCategories(entryId);
468            }
469    
470            @Override
471            public List<Long> getSubcategoryIds(long parentCategoryId) {
472                    AssetCategory parentAssetCategory =
473                            assetCategoryPersistence.fetchByPrimaryKey(parentCategoryId);
474    
475                    if (parentAssetCategory == null) {
476                            return Collections.emptyList();
477                    }
478    
479                    return ListUtil.toList(
480                            assetCategoryPersistence.getDescendants(parentAssetCategory),
481                            AssetCategory.CATEGORY_ID_ACCESSOR);
482            }
483    
484            @Override
485            public List<AssetCategory> getVocabularyCategories(
486                    long vocabularyId, int start, int end,
487                    OrderByComparator<AssetCategory> obc) {
488    
489                    return assetCategoryPersistence.findByVocabularyId(
490                            vocabularyId, start, end, obc);
491            }
492    
493            @Override
494            public List<AssetCategory> getVocabularyCategories(
495                    long parentCategoryId, long vocabularyId, int start, int end,
496                    OrderByComparator<AssetCategory> obc) {
497    
498                    return assetCategoryPersistence.findByP_V(
499                            parentCategoryId, vocabularyId, start, end, obc);
500            }
501    
502            @Override
503            public int getVocabularyCategoriesCount(long vocabularyId) {
504                    return assetCategoryPersistence.countByVocabularyId(vocabularyId);
505            }
506    
507            @Override
508            public List<AssetCategory> getVocabularyRootCategories(
509                    long vocabularyId, int start, int end,
510                    OrderByComparator<AssetCategory> obc) {
511    
512                    return getVocabularyCategories(
513                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId,
514                            start, end, obc);
515            }
516    
517            @Override
518            public int getVocabularyRootCategoriesCount(long vocabularyId) {
519                    return assetCategoryPersistence.countByP_V(
520                            AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID, vocabularyId);
521            }
522    
523            @Indexable(type = IndexableType.REINDEX)
524            @Override
525            public AssetCategory mergeCategories(long fromCategoryId, long toCategoryId)
526                    throws PortalException {
527    
528                    List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
529                            fromCategoryId);
530    
531                    assetCategoryPersistence.addAssetEntries(toCategoryId, entries);
532    
533                    List<AssetCategoryProperty> categoryProperties =
534                            assetCategoryPropertyPersistence.findByCategoryId(fromCategoryId);
535    
536                    for (AssetCategoryProperty fromCategoryProperty : categoryProperties) {
537                            AssetCategoryProperty toCategoryProperty =
538                                    assetCategoryPropertyPersistence.fetchByCA_K(
539                                            toCategoryId, fromCategoryProperty.getKey());
540    
541                            if (toCategoryProperty == null) {
542                                    fromCategoryProperty.setCategoryId(toCategoryId);
543    
544                                    assetCategoryPropertyPersistence.update(fromCategoryProperty);
545                            }
546                    }
547    
548                    assetCategoryLocalService.deleteCategory(fromCategoryId);
549    
550                    return getCategory(toCategoryId);
551            }
552    
553            @Indexable(type = IndexableType.REINDEX)
554            @Override
555            public AssetCategory moveCategory(
556                            long categoryId, long parentCategoryId, long vocabularyId,
557                            ServiceContext serviceContext)
558                    throws PortalException {
559    
560                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
561                            categoryId);
562    
563                    validate(
564                            categoryId, parentCategoryId, category.getName(), vocabularyId);
565    
566                    if (parentCategoryId > 0) {
567                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
568                    }
569    
570                    if (vocabularyId != category.getVocabularyId()) {
571                            assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
572    
573                            category.setVocabularyId(vocabularyId);
574    
575                            updateChildrenVocabularyId(category, vocabularyId);
576                    }
577    
578                    category.setParentCategoryId(parentCategoryId);
579    
580                    assetCategoryPersistence.update(category);
581    
582                    return category;
583            }
584    
585            @Override
586            public void rebuildTree(long groupId, boolean force) {
587                    assetCategoryPersistence.rebuildTree(groupId, force);
588            }
589    
590            @Override
591            public List<AssetCategory> search(
592                    long groupId, String name, String[] categoryProperties, int start,
593                    int end) {
594    
595                    return assetCategoryFinder.findByG_N_P(
596                            groupId, name, categoryProperties, start, end);
597            }
598    
599            @Override
600            public BaseModelSearchResult<AssetCategory> searchCategories(
601                            long companyId, long groupIds, String title, long vocabularyId,
602                            int start, int end)
603                    throws PortalException {
604    
605                    return searchCategories(
606                            companyId, new long[] {groupIds}, title, new long[] {vocabularyId},
607                            start, end);
608            }
609    
610            @Override
611            public BaseModelSearchResult<AssetCategory> searchCategories(
612                            long companyId, long[] groupIds, String title, long[] vocabularyIds,
613                            int start, int end)
614                    throws PortalException {
615    
616                    SearchContext searchContext = buildSearchContext(
617                            companyId, groupIds, title, new long[0], vocabularyIds, start, end,
618                            null);
619    
620                    return searchCategories(searchContext);
621            }
622    
623            @Override
624            public BaseModelSearchResult<AssetCategory> searchCategories(
625                            long companyId, long[] groupIds, String title,
626                            long[] parentCategoryIds, long[] vocabularyIds, int start, int end)
627                    throws PortalException {
628    
629                    SearchContext searchContext = buildSearchContext(
630                            companyId, groupIds, title, parentCategoryIds, vocabularyIds, start,
631                            end, null);
632    
633                    return searchCategories(searchContext);
634            }
635    
636            @Override
637            public BaseModelSearchResult<AssetCategory> searchCategories(
638                            long companyId, long[] groupIds, String title, long[] vocabularyIds,
639                            long[] parentCategoryIds, int start, int end, Sort sort)
640                    throws PortalException {
641    
642                    SearchContext searchContext = buildSearchContext(
643                            companyId, groupIds, title, parentCategoryIds, vocabularyIds, start,
644                            end, sort);
645    
646                    return searchCategories(searchContext);
647            }
648    
649            @Indexable(type = IndexableType.REINDEX)
650            @Override
651            public AssetCategory updateCategory(
652                            long userId, long categoryId, long parentCategoryId,
653                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
654                            long vocabularyId, String[] categoryProperties,
655                            ServiceContext serviceContext)
656                    throws PortalException {
657    
658                    // Category
659    
660                    String name = titleMap.get(LocaleUtil.getSiteDefault());
661    
662                    name = ModelHintsUtil.trimString(
663                            AssetCategory.class.getName(), "name", name);
664    
665                    if (categoryProperties == null) {
666                            categoryProperties = new String[0];
667                    }
668    
669                    validate(categoryId, parentCategoryId, name, vocabularyId);
670    
671                    if (parentCategoryId > 0) {
672                            assetCategoryPersistence.findByPrimaryKey(parentCategoryId);
673                    }
674    
675                    AssetCategory category = assetCategoryPersistence.findByPrimaryKey(
676                            categoryId);
677    
678                    String oldName = category.getName();
679    
680                    if (vocabularyId != category.getVocabularyId()) {
681                            assetVocabularyPersistence.findByPrimaryKey(vocabularyId);
682    
683                            parentCategoryId =
684                                    AssetCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
685    
686                            category.setVocabularyId(vocabularyId);
687    
688                            updateChildrenVocabularyId(category, vocabularyId);
689                    }
690    
691                    category.setParentCategoryId(parentCategoryId);
692                    category.setName(name);
693                    category.setTitleMap(titleMap);
694                    category.setDescriptionMap(descriptionMap);
695    
696                    assetCategoryPersistence.update(category);
697    
698                    // Properties
699    
700                    List<AssetCategoryProperty> oldCategoryProperties =
701                            assetCategoryPropertyPersistence.findByCategoryId(categoryId);
702    
703                    oldCategoryProperties = ListUtil.copy(oldCategoryProperties);
704    
705                    for (int i = 0; i < categoryProperties.length; i++) {
706                            String[] categoryProperty = StringUtil.split(
707                                    categoryProperties[i],
708                                    AssetCategoryConstants.PROPERTY_KEY_VALUE_SEPARATOR);
709    
710                            if (categoryProperty.length <= 1) {
711                                    categoryProperty = StringUtil.split(
712                                            categoryProperties[i], CharPool.COLON);
713                            }
714    
715                            String key = StringPool.BLANK;
716    
717                            if (categoryProperty.length > 0) {
718                                    key = GetterUtil.getString(categoryProperty[0]);
719                            }
720    
721                            String value = StringPool.BLANK;
722    
723                            if (categoryProperty.length > 1) {
724                                    value = GetterUtil.getString(categoryProperty[1]);
725                            }
726    
727                            if (Validator.isNotNull(key)) {
728                                    boolean addCategoryProperty = true;
729    
730                                    AssetCategoryProperty oldCategoryProperty = null;
731    
732                                    Iterator<AssetCategoryProperty> iterator =
733                                            oldCategoryProperties.iterator();
734    
735                                    while (iterator.hasNext()) {
736                                            oldCategoryProperty = iterator.next();
737    
738                                            if ((categoryId == oldCategoryProperty.getCategoryId()) &&
739                                                    key.equals(oldCategoryProperty.getKey())) {
740    
741                                                    addCategoryProperty = false;
742    
743                                                    if (!value.equals(oldCategoryProperty.getValue())) {
744                                                            assetCategoryPropertyLocalService.
745                                                                    updateCategoryProperty(
746                                                                            userId,
747                                                                            oldCategoryProperty.getCategoryPropertyId(),
748                                                                            key, value);
749                                                    }
750    
751                                                    iterator.remove();
752    
753                                                    break;
754                                            }
755                                    }
756    
757                                    if (addCategoryProperty) {
758                                            assetCategoryPropertyLocalService.addCategoryProperty(
759                                                    userId, categoryId, key, value);
760                                    }
761                            }
762                    }
763    
764                    for (AssetCategoryProperty categoryProperty : oldCategoryProperties) {
765                            assetCategoryPropertyLocalService.deleteAssetCategoryProperty(
766                                    categoryProperty);
767                    }
768    
769                    // Indexer
770    
771                    if (!oldName.equals(name)) {
772                            List<AssetEntry> entries = assetCategoryPersistence.getAssetEntries(
773                                    category.getCategoryId());
774    
775                            assetEntryLocalService.reindex(entries);
776                    }
777    
778                    return category;
779            }
780    
781            protected SearchContext buildSearchContext(
782                    long companyId, long[] groupIds, String title, long[] parentCategoryIds,
783                    long[] vocabularyIds, int start, int end, Sort sort) {
784    
785                    SearchContext searchContext = new SearchContext();
786    
787                    Map<String, Serializable> attributes = new HashMap<>();
788    
789                    attributes.put(Field.ASSET_PARENT_CATEGORY_IDS, parentCategoryIds);
790                    attributes.put(Field.ASSET_VOCABULARY_IDS, vocabularyIds);
791                    attributes.put(Field.TITLE, title);
792    
793                    searchContext.setAttributes(attributes);
794    
795                    searchContext.setCompanyId(companyId);
796                    searchContext.setEnd(end);
797                    searchContext.setGroupIds(groupIds);
798                    searchContext.setKeywords(title);
799                    searchContext.setSorts(sort);
800                    searchContext.setStart(start);
801    
802                    QueryConfig queryConfig = searchContext.getQueryConfig();
803    
804                    queryConfig.setHighlightEnabled(false);
805                    queryConfig.setScoreEnabled(false);
806    
807                    return searchContext;
808            }
809    
810            protected long[] getCategoryIds(List<AssetCategory> categories) {
811                    return ListUtil.toLongArray(
812                            categories, AssetCategory.CATEGORY_ID_ACCESSOR);
813            }
814    
815            protected String[] getCategoryNames(List<AssetCategory> categories) {
816                    return ListUtil.toArray(categories, AssetCategory.NAME_ACCESSOR);
817            }
818    
819            protected BaseModelSearchResult<AssetCategory> searchCategories(
820                            SearchContext searchContext)
821                    throws PortalException {
822    
823                    Indexer<AssetCategory> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
824                            AssetCategory.class);
825    
826                    for (int i = 0; i < 10; i++) {
827                            Hits hits = indexer.search(searchContext);
828    
829                            List<AssetCategory> categories = getCategories(hits);
830    
831                            if (categories != null) {
832                                    return new BaseModelSearchResult<>(
833                                            categories, hits.getLength());
834                            }
835                    }
836    
837                    throw new SearchException(
838                            "Unable to fix the search index after 10 attempts");
839            }
840    
841            protected void updateChildrenVocabularyId(
842                    AssetCategory category, long vocabularyId) {
843    
844                    List<AssetCategory> childrenCategories =
845                            assetCategoryPersistence.findByParentCategoryId(
846                                    category.getCategoryId());
847    
848                    if (!childrenCategories.isEmpty()) {
849                            for (AssetCategory childCategory : childrenCategories) {
850                                    childCategory.setVocabularyId(vocabularyId);
851    
852                                    assetCategoryPersistence.update(childCategory);
853    
854                                    updateChildrenVocabularyId(childCategory, vocabularyId);
855                            }
856                    }
857            }
858    
859            protected void validate(
860                            long categoryId, long parentCategoryId, String name,
861                            long vocabularyId)
862                    throws PortalException {
863    
864                    if (Validator.isNull(name)) {
865                            StringBundler sb = new StringBundler(5);
866    
867                            sb.append(
868                                    "Asset category name cannot be null for key {categoryId=");
869                            sb.append(categoryId);
870                            sb.append(", vocabularyId=");
871                            sb.append(vocabularyId);
872                            sb.append("}");
873    
874                            throw new AssetCategoryNameException(
875                                    "Category name cannot be null for category " + categoryId +
876                                            " and vocabulary " + vocabularyId);
877                    }
878    
879                    AssetCategory category = assetCategoryPersistence.fetchByP_N_V(
880                            parentCategoryId, name, vocabularyId);
881    
882                    if ((category != null) && (category.getCategoryId() != categoryId)) {
883                            StringBundler sb = new StringBundler(4);
884    
885                            sb.append("There is another category named ");
886                            sb.append(name);
887                            sb.append(" as a child of category ");
888                            sb.append(parentCategoryId);
889    
890                            throw new DuplicateCategoryException(sb.toString());
891                    }
892            }
893    
894    }