/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portlet.asset.service.impl;
import com.liferay.asset.kernel.exception.AssetTagException;
import com.liferay.asset.kernel.exception.DuplicateTagException;
import com.liferay.asset.kernel.model.AssetEntry;
import com.liferay.asset.kernel.model.AssetTag;
import com.liferay.portal.kernel.cache.thread.local.ThreadLocalCachable;
import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Group;
import com.liferay.portal.kernel.model.SystemEventConstants;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.search.BaseModelSearchResult;
import com.liferay.portal.kernel.search.Document;
import com.liferay.portal.kernel.search.Field;
import com.liferay.portal.kernel.search.Hits;
import com.liferay.portal.kernel.search.Indexable;
import com.liferay.portal.kernel.search.IndexableType;
import com.liferay.portal.kernel.search.Indexer;
import com.liferay.portal.kernel.search.IndexerRegistryUtil;
import com.liferay.portal.kernel.search.QueryConfig;
import com.liferay.portal.kernel.search.SearchContext;
import com.liferay.portal.kernel.search.SearchException;
import com.liferay.portal.kernel.search.Sort;
import com.liferay.portal.kernel.service.ServiceContext;
import com.liferay.portal.kernel.service.ServiceContextThreadLocal;
import com.liferay.portal.kernel.systemevent.SystemEvent;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portlet.asset.service.base.AssetTagLocalServiceBaseImpl;
import com.liferay.portlet.asset.util.AssetUtil;
import com.liferay.portlet.asset.util.comparator.AssetTagNameComparator;
import com.liferay.social.kernel.util.SocialCounterPeriodUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Provides the local service for accessing, adding, checking, deleting,
* merging, and updating asset tags.
*
* @author Brian Wing Shun Chan
* @author Alvaro del Castillo
* @author Jorge Ferrer
* @author Bruno Farache
*/
public class AssetTagLocalServiceImpl extends AssetTagLocalServiceBaseImpl {
/**
* Adds an asset tag.
*
* @param userId the primary key of the user adding the asset tag
* @param groupId the primary key of the group in which the asset tag is to
* be added
* @param name the asset tag's name
* @param serviceContext the service context to be applied
* @return the asset tag that was added
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public AssetTag addTag(
long userId, long groupId, String name,
ServiceContext serviceContext)
throws PortalException {
// Tag
User user = userPersistence.findByPrimaryKey(userId);
long tagId = counterLocalService.increment();
AssetTag tag = assetTagPersistence.create(tagId);
tag.setUuid(serviceContext.getUuid());
tag.setGroupId(groupId);
tag.setCompanyId(user.getCompanyId());
tag.setUserId(user.getUserId());
tag.setUserName(user.getFullName());
name = StringUtil.toLowerCase(StringUtil.trim(name));
if (hasTag(groupId, name)) {
throw new DuplicateTagException(
"A tag with the name " + name + " already exists");
}
validate(name);
tag.setName(name);
assetTagPersistence.update(tag);
// Resources
resourceLocalService.addModelResources(tag, serviceContext);
return tag;
}
/**
* Returns the asset tags matching the group and names, creating new asset
* tags matching the names if the group doesn't already have them.
*
* <p>
* For each name, if an asset tag with the name doesn't already exist in the
* group, this method creates a new asset tag with the name in the group.
* </p>
*
* @param userId the primary key of the user checking the asset tags
* @param group the group in which to check the asset tags
* @param names the asset tag names
* @return the asset tags matching the group and names and new asset tags
* matching the names that don't already exist in the group
*/
@Override
public List<AssetTag> checkTags(long userId, Group group, String[] names)
throws PortalException {
List<AssetTag> tags = new ArrayList<>();
for (String name : names) {
AssetTag tag = fetchTag(group.getGroupId(), name);
if (tag == null) {
ServiceContext serviceContext = new ServiceContext();
serviceContext.setAddGroupPermissions(true);
serviceContext.setAddGuestPermissions(true);
serviceContext.setScopeGroupId(group.getGroupId());
tag = addTag(userId, group.getGroupId(), name, serviceContext);
}
if (tag != null) {
tags.add(tag);
}
}
return tags;
}
/**
* Returns the asset tags matching the group and names, creating new asset
* tags matching the names if the group doesn't already have them.
*
* @param userId the primary key of the user checking the asset tags
* @param groupId the primary key of the group in which check the asset
* tags
* @param names the asset tag names
* @return the asset tags matching the group and names and new asset tags
* matching the names that don't already exist in the group
*/
@Override
public List<AssetTag> checkTags(long userId, long groupId, String[] names)
throws PortalException {
Group group = groupPersistence.findByPrimaryKey(groupId);
return checkTags(userId, group, names);
}
/**
* Decrements the number of assets to which the asset tag has been applied.
*
* @param tagId the primary key of the asset tag
* @param classNameId the class name ID of the entity to which the asset
* tag had been applied
* @return the asset tag
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public AssetTag decrementAssetCount(long tagId, long classNameId)
throws PortalException {
AssetTag tag = assetTagPersistence.findByPrimaryKey(tagId);
tag.setAssetCount(Math.max(0, tag.getAssetCount() - 1));
assetTagPersistence.update(tag);
assetTagStatsLocalService.updateTagStats(tagId, classNameId);
return tag;
}
/**
* Deletes all asset tags in the group.
*
* @param groupId the primary key of the group in which to delete all asset
* tags
*/
@Override
public void deleteGroupTags(long groupId) throws PortalException {
List<AssetTag> tags = getGroupTags(groupId);
for (AssetTag tag : tags) {
assetTagLocalService.deleteTag(tag);
}
}
/**
* Deletes the asset tag.
*
* @param tag the asset tag to be deleted
*/
@Override
@SystemEvent(type = SystemEventConstants.TYPE_DELETE)
public void deleteTag(AssetTag tag) throws PortalException {
// Entries
List<AssetEntry> entries = assetTagPersistence.getAssetEntries(
tag.getTagId());
// Tag
assetTagPersistence.remove(tag);
// Stats
assetTagStatsLocalService.deleteTagStatsByTagId(tag.getTagId());
// Indexer
assetEntryLocalService.reindex(entries);
Indexer<AssetTag> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
AssetTag.class);
indexer.delete(tag);
}
/**
* Deletes the asset tag.
*
* @param tagId the primary key of the asset tag
*/
@Override
public void deleteTag(long tagId) throws PortalException {
AssetTag tag = assetTagPersistence.findByPrimaryKey(tagId);
assetTagLocalService.deleteTag(tag);
}
/**
* Returns the asset tag with the name in the group.
*
* @param groupId the primary key of the group
* @param name the asset tag's name
* @return the asset tag with the name in the group or <code>null</code> if
* it could not be found
*/
@Override
public AssetTag fetchTag(long groupId, String name) {
return assetTagPersistence.fetchByG_N(groupId, name);
}
/**
* Returns the asset tags of the asset entry.
*
* @param entryId the primary key of the asset entry
* @return the asset tags of the asset entry
*/
@Override
public List<AssetTag> getEntryTags(long entryId) {
return assetEntryPersistence.getAssetTags(entryId);
}
/**
* Returns the asset tags in the groups.
*
* @param groupIds the primary keys of the groups
* @return the asset tags in the groups
*/
@Override
public List<AssetTag> getGroupsTags(long[] groupIds) {
List<AssetTag> groupsTags = new ArrayList<>();
for (long groupId : groupIds) {
List<AssetTag> groupTags = getGroupTags(groupId);
groupsTags.addAll(groupTags);
}
return groupsTags;
}
/**
* Returns the asset tags in the group.
*
* @param groupId the primary key of the group
* @return the asset tags in the group
*/
@Override
public List<AssetTag> getGroupTags(long groupId) {
return assetTagPersistence.findByGroupId(groupId);
}
/**
* Returns a range of all the asset tags in the group.
*
* @param groupId the primary key of the group
* @param start the lower bound of the range of asset tags
* @param end the upper bound of the range of asset tags (not inclusive)
* @return the range of matching asset tags
*/
@Override
public List<AssetTag> getGroupTags(long groupId, int start, int end) {
return assetTagPersistence.findByGroupId(groupId, start, end);
}
/**
* Returns the number of asset tags in the group.
*
* @param groupId the primary key of the group
* @return the number of asset tags in the group
*/
@Override
public int getGroupTagsCount(long groupId) {
return assetTagPersistence.countByGroupId(groupId);
}
@Override
public List<AssetTag> getSocialActivityCounterOffsetTags(
long groupId, String socialActivityCounterName, int startOffset,
int endOffset) {
int startPeriod = SocialCounterPeriodUtil.getStartPeriod(startOffset);
int endPeriod = SocialCounterPeriodUtil.getEndPeriod(endOffset);
return getSocialActivityCounterPeriodTags(
groupId, socialActivityCounterName, startPeriod, endPeriod);
}
@Override
public List<AssetTag> getSocialActivityCounterPeriodTags(
long groupId, String socialActivityCounterName, int startPeriod,
int endPeriod) {
int offset = SocialCounterPeriodUtil.getOffset(endPeriod);
int periodLength = SocialCounterPeriodUtil.getPeriodLength(offset);
return assetTagFinder.findByG_N_S_E(
groupId, socialActivityCounterName, startPeriod, endPeriod,
periodLength);
}
/**
* Returns the asset tag with the primary key.
*
* @param tagId the primary key of the asset tag
* @return the asset tag with the primary key
*/
@Override
public AssetTag getTag(long tagId) throws PortalException {
return assetTagPersistence.findByPrimaryKey(tagId);
}
/**
* Returns the asset tag with the name in the group.
*
* @param groupId the primary key of the group
* @param name the name of the asset tag
* @return the asset tag with the name in the group
*/
@Override
public AssetTag getTag(long groupId, String name) throws PortalException {
return assetTagPersistence.findByG_N(groupId, name);
}
/**
* Returns the primary keys of the asset tags with the names in the group.
*
* @param groupId the primary key of the group
* @param names the names of the asset tags
* @return the primary keys of the asset tags with the names in the group
*/
@Override
public long[] getTagIds(long groupId, String[] names) {
List<Long> tagIds = new ArrayList<>(names.length);
for (String name : names) {
AssetTag tag = fetchTag(groupId, name);
if (tag == null) {
continue;
}
tagIds.add(tag.getTagId());
}
return ArrayUtil.toArray(tagIds.toArray(new Long[tagIds.size()]));
}
/**
* Returns the primary keys of the asset tags with the name in the groups.
*
* @param groupIds the primary keys of the groups
* @param name the name of the asset tags
* @return the primary keys of the asset tags with the name in the groups
*/
@Override
public long[] getTagIds(long[] groupIds, String name) {
List<Long> tagIds = new ArrayList<>(groupIds.length);
for (long groupId : groupIds) {
AssetTag tag = fetchTag(groupId, name);
if (tag == null) {
continue;
}
tagIds.add(tag.getTagId());
}
return ArrayUtil.toArray(tagIds.toArray(new Long[tagIds.size()]));
}
/**
* Returns the primary keys of the asset tags with the names in the groups.
*
* @param groupIds the primary keys of the groups
* @param names the names of the asset tags
* @return the primary keys of the asset tags with the names in the groups
*/
@Override
public long[] getTagIds(long[] groupIds, String[] names) {
long[] tagsIds = new long[0];
for (long groupId : groupIds) {
tagsIds = ArrayUtil.append(tagsIds, getTagIds(groupId, names));
}
return tagsIds;
}
/**
* Returns the names of all the asset tags.
*
* @return the names of all the asset tags
*/
@Override
public String[] getTagNames() {
return getTagNames(getTags());
}
/**
* Returns the names of the asset tags of the entity.
*
* @param classNameId the class name ID of the entity
* @param classPK the primary key of the entity
* @return the names of the asset tags of the entity
*/
@Override
public String[] getTagNames(long classNameId, long classPK) {
return getTagNames(getTags(classNameId, classPK));
}
/**
* Returns the names of the asset tags of the entity
*
* @param className the class name of the entity
* @param classPK the primary key of the entity
* @return the names of the asset tags of the entity
*/
@Override
public String[] getTagNames(String className, long classPK) {
return getTagNames(getTags(className, classPK));
}
/**
* Returns all the asset tags.
*
* @return the asset tags
*/
@Override
public List<AssetTag> getTags() {
return assetTagPersistence.findAll();
}
/**
* Returns the asset tags of the entity.
*
* @param classNameId the class name ID of the entity
* @param classPK the primary key of the entity
* @return the asset tags of the entity
*/
@Override
public List<AssetTag> getTags(long classNameId, long classPK) {
AssetEntry entry = assetEntryPersistence.fetchByC_C(
classNameId, classPK);
if (entry == null) {
return Collections.emptyList();
}
return assetEntryPersistence.getAssetTags(entry.getEntryId());
}
@Override
public List<AssetTag> getTags(long groupId, long classNameId, String name) {
return assetTagFinder.findByG_C_N(
groupId, classNameId, name, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
null);
}
@Override
public List<AssetTag> getTags(
long groupId, long classNameId, String name, int start, int end) {
return assetTagFinder.findByG_C_N(
groupId, classNameId, name, start, end, null);
}
/**
* Returns the asset tags of the entity.
*
* @param className the class name of the entity
* @param classPK the primary key of the entity
* @return the asset tags of the entity
*/
@Override
@ThreadLocalCachable
public List<AssetTag> getTags(String className, long classPK) {
long classNameId = classNameLocalService.getClassNameId(className);
return getTags(classNameId, classPK);
}
@Override
public int getTagsSize(long groupId, long classNameId, String name) {
return assetTagFinder.countByG_C_N(groupId, classNameId, name);
}
/**
* Returns <code>true</code> if the group contains an asset tag with the
* name.
*
* @param groupId the primary key of the group
* @param name the name of the asset tag
* @return <code>true</code> if the group contains an asset tag with the
* name; <code>false</code> otherwise.
*/
@Override
public boolean hasTag(long groupId, String name) {
AssetTag tag = fetchTag(groupId, name);
if (tag != null) {
return true;
}
return false;
}
/**
* Increments the number of assets to which the asset tag has been applied.
*
* @param tagId the primary key of the asset tag
* @param classNameId the class name ID of the entity to which the asset
* tag is being applied
* @return the asset tag
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public AssetTag incrementAssetCount(long tagId, long classNameId)
throws PortalException {
AssetTag tag = assetTagPersistence.findByPrimaryKey(tagId);
tag.setAssetCount(tag.getAssetCount() + 1);
assetTagPersistence.update(tag);
assetTagStatsLocalService.updateTagStats(tagId, classNameId);
return tag;
}
/**
* Replaces all occurrences of the first asset tag with the second asset tag
* and deletes the first asset tag.
*
* @param fromTagId the primary key of the asset tag to be replaced
* @param toTagId the primary key of the asset tag to apply to the asset
* entries of the other asset tag
*/
@Override
public void mergeTags(long fromTagId, long toTagId) throws PortalException {
List<AssetEntry> entries = assetTagPersistence.getAssetEntries(
fromTagId);
assetTagPersistence.addAssetEntries(toTagId, entries);
deleteTag(fromTagId);
for (AssetEntry entry : entries) {
incrementAssetCount(toTagId, entry.getClassNameId());
}
}
/**
* Returns the asset tags in the group whose names match the pattern.
*
* @param groupId the primary key of the group
* @param name the pattern to match
* @param start the lower bound of the range of asset tags
* @param end the upper bound of the range of asset tags (not inclusive)
* @return the asset tags in the group whose names match the pattern
*/
@Override
public List<AssetTag> search(
long groupId, String name, int start, int end) {
return search(new long[] {groupId}, name, start, end);
}
/**
* Returns the asset tags in the groups whose names match the pattern.
*
* @param groupIds the primary keys of the groups
* @param name the pattern to match
* @param start the lower bound of the range of asset tags
* @param end the upper bound of the range of asset tags (not inclusive)
* @return the asset tags in the groups whose names match the pattern
*/
@Override
public List<AssetTag> search(
long[] groupIds, String name, int start, int end) {
return assetTagPersistence.findByG_LikeN(
groupIds, name, start, end, new AssetTagNameComparator());
}
@Override
public BaseModelSearchResult<AssetTag> searchTags(
long[] groupIds, String name, int start, int end, Sort sort)
throws PortalException {
ServiceContext serviceContext =
ServiceContextThreadLocal.getServiceContext();
SearchContext searchContext = buildSearchContext(
serviceContext.getCompanyId(), groupIds, name, start, end, sort);
return searchTags(searchContext);
}
@Indexable(type = IndexableType.REINDEX)
@Override
public AssetTag updateTag(
long userId, long tagId, String name, ServiceContext serviceContext)
throws PortalException {
// Tag
AssetTag tag = assetTagPersistence.findByPrimaryKey(tagId);
String oldName = tag.getName();
name = StringUtil.toLowerCase(StringUtil.trim(name));
if (!name.equals(tag.getName()) && hasTag(tag.getGroupId(), name)) {
throw new DuplicateTagException(
"A tag with the name " + name + " already exists");
}
if (!tag.getName().equals(name)) {
AssetTag existingAssetTag = fetchTag(tag.getGroupId(), name);
if ((existingAssetTag != null) &&
(existingAssetTag.getTagId() != tagId)) {
throw new DuplicateTagException(
"A tag with the name " + name + " already exists");
}
}
validate(name);
tag.setName(name);
assetTagPersistence.update(tag);
// Indexer
if (!oldName.equals(name)) {
List<AssetEntry> entries = assetTagPersistence.getAssetEntries(
tag.getTagId());
assetEntryLocalService.reindex(entries);
}
return tag;
}
protected SearchContext buildSearchContext(
long companyId, long[] groupIds, String name, int start, int end,
Sort sort) {
SearchContext searchContext = new SearchContext();
Map<String, Serializable> attributes = new HashMap<>();
attributes.put(Field.NAME, name);
searchContext.setAttributes(attributes);
searchContext.setCompanyId(companyId);
searchContext.setEnd(end);
searchContext.setGroupIds(groupIds);
searchContext.setKeywords(name);
searchContext.setStart(start);
if (sort != null) {
searchContext.setSorts(sort);
}
QueryConfig queryConfig = searchContext.getQueryConfig();
queryConfig.setHighlightEnabled(false);
queryConfig.setScoreEnabled(false);
return searchContext;
}
protected String[] getTagNames(List<AssetTag> tags) {
return ListUtil.toArray(tags, AssetTag.NAME_ACCESSOR);
}
protected List<AssetTag> getTags(Hits hits) throws PortalException {
List<Document> documents = hits.toList();
List<AssetTag> tags = new ArrayList<>(documents.size());
for (Document document : documents) {
long tagId = GetterUtil.getLong(document.get(Field.ENTRY_CLASS_PK));
AssetTag tag = fetchAssetTag(tagId);
if (tag == null) {
tags = null;
Indexer<AssetTag> indexer = IndexerRegistryUtil.getIndexer(
AssetTag.class);
long companyId = GetterUtil.getLong(
document.get(Field.COMPANY_ID));
indexer.delete(companyId, document.getUID());
}
else if (tags != null) {
tags.add(tag);
}
}
return tags;
}
protected BaseModelSearchResult<AssetTag> searchTags(
SearchContext searchContext)
throws PortalException {
Indexer<AssetTag> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
AssetTag.class);
for (int i = 0; i < 10; i++) {
Hits hits = indexer.search(searchContext);
List<AssetTag> tags = getTags(hits);
if (tags != null) {
return new BaseModelSearchResult<>(tags, hits.getLength());
}
}
throw new SearchException(
"Unable to fix the search index after 10 attempts");
}
protected void validate(String name) throws PortalException {
if (!AssetUtil.isValidWord(name)) {
throw new AssetTagException(
StringUtil.merge(
AssetUtil.INVALID_CHARACTERS, StringPool.SPACE),
AssetTagException.INVALID_CHARACTER);
}
}
private static final Log _log = LogFactoryUtil.getLog(
AssetTagLocalServiceImpl.class);
}