/**
* 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.messageboards.util;
import com.liferay.document.library.kernel.model.DLFileEntry;
import com.liferay.message.boards.kernel.model.MBCategory;
import com.liferay.message.boards.kernel.model.MBCategoryConstants;
import com.liferay.message.boards.kernel.model.MBDiscussion;
import com.liferay.message.boards.kernel.model.MBMessage;
import com.liferay.message.boards.kernel.service.MBCategoryLocalServiceUtil;
import com.liferay.message.boards.kernel.service.MBCategoryServiceUtil;
import com.liferay.message.boards.kernel.service.MBDiscussionLocalServiceUtil;
import com.liferay.message.boards.kernel.service.MBMessageLocalServiceUtil;
import com.liferay.portal.kernel.comment.Comment;
import com.liferay.portal.kernel.comment.CommentManagerUtil;
import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
import com.liferay.portal.kernel.dao.orm.DynamicQuery;
import com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;
import com.liferay.portal.kernel.dao.orm.Property;
import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
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.parsers.bbcode.BBCodeTranslatorUtil;
import com.liferay.portal.kernel.repository.model.FileEntry;
import com.liferay.portal.kernel.search.BaseIndexer;
import com.liferay.portal.kernel.search.BaseRelatedEntryIndexer;
import com.liferay.portal.kernel.search.BooleanClauseOccur;
import com.liferay.portal.kernel.search.Document;
import com.liferay.portal.kernel.search.Field;
import com.liferay.portal.kernel.search.IndexWriterHelperUtil;
import com.liferay.portal.kernel.search.Indexer;
import com.liferay.portal.kernel.search.IndexerRegistryUtil;
import com.liferay.portal.kernel.search.RelatedEntryIndexer;
import com.liferay.portal.kernel.search.SearchContext;
import com.liferay.portal.kernel.search.Summary;
import com.liferay.portal.kernel.search.filter.BooleanFilter;
import com.liferay.portal.kernel.search.filter.TermsFilter;
import com.liferay.portal.kernel.security.permission.ActionKeys;
import com.liferay.portal.kernel.security.permission.PermissionChecker;
import com.liferay.portal.kernel.service.GroupLocalServiceUtil;
import com.liferay.portal.kernel.spring.osgi.OSGiBeanProperties;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.HtmlUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
import java.util.List;
import java.util.Locale;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
/**
* @author Brian Wing Shun Chan
* @author Harry Mark
* @author Bruno Farache
* @author Raymond Augé
*/
@OSGiBeanProperties
public class MBMessageIndexer
extends BaseIndexer<MBMessage> implements RelatedEntryIndexer {
public static final String CLASS_NAME = MBMessage.class.getName();
public MBMessageIndexer() {
setDefaultSelectedFieldNames(
Field.ASSET_TAG_NAMES, Field.CLASS_NAME_ID, Field.CLASS_PK,
Field.COMPANY_ID, Field.CONTENT, Field.ENTRY_CLASS_NAME,
Field.ENTRY_CLASS_PK, Field.GROUP_ID, Field.MODIFIED_DATE,
Field.SCOPE_GROUP_ID, Field.TITLE, Field.UID);
setFilterSearch(true);
setPermissionAware(true);
}
@Override
public void addRelatedClassNames(
BooleanFilter contextFilter, SearchContext searchContext)
throws Exception {
_relatedEntryIndexer.addRelatedClassNames(contextFilter, searchContext);
}
@Override
public void addRelatedEntryFields(Document document, Object obj)
throws Exception {
FileEntry fileEntry = (FileEntry)obj;
MBMessage message = MBMessageAttachmentsUtil.fetchMessage(
fileEntry.getFileEntryId());
if (message == null) {
return;
}
document.addKeyword(Field.CATEGORY_ID, message.getCategoryId());
document.addKeyword("discussion", false);
document.addKeyword("threadId", message.getThreadId());
}
@Override
public String getClassName() {
return CLASS_NAME;
}
@Override
public boolean hasPermission(
PermissionChecker permissionChecker, String entryClassName,
long entryClassPK, String actionId)
throws Exception {
MBMessage message = MBMessageLocalServiceUtil.getMessage(entryClassPK);
if (message.isDiscussion()) {
Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
message.getClassName());
return indexer.hasPermission(
permissionChecker, message.getClassName(), message.getClassPK(),
ActionKeys.VIEW);
}
return MBMessagePermission.contains(
permissionChecker, entryClassPK, ActionKeys.VIEW);
}
@Override
public boolean isVisible(long classPK, int status) throws Exception {
MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
return isVisible(message.getStatus(), status);
}
@Override
public boolean isVisibleRelatedEntry(long classPK, int status)
throws Exception {
MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
if (message.isDiscussion()) {
Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
message.getClassName());
return indexer.isVisible(message.getClassPK(), status);
}
return true;
}
@Override
public void postProcessContextBooleanFilter(
BooleanFilter contextBooleanFilter, SearchContext searchContext)
throws Exception {
addStatus(contextBooleanFilter, searchContext);
boolean discussion = GetterUtil.getBoolean(
searchContext.getAttribute("discussion"));
contextBooleanFilter.addRequiredTerm("discussion", discussion);
if (searchContext.isIncludeDiscussions()) {
addRelatedClassNames(contextBooleanFilter, searchContext);
}
String classNameId = GetterUtil.getString(
searchContext.getAttribute(Field.CLASS_NAME_ID));
if (Validator.isNotNull(classNameId)) {
contextBooleanFilter.addRequiredTerm(
Field.CLASS_NAME_ID, classNameId);
}
long threadId = GetterUtil.getLong(
(String)searchContext.getAttribute("threadId"));
if (threadId > 0) {
contextBooleanFilter.addRequiredTerm("threadId", threadId);
}
long[] categoryIds = searchContext.getCategoryIds();
if ((categoryIds != null) && (categoryIds.length > 0) &&
(categoryIds[0] !=
MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID)) {
TermsFilter categoriesTermsFilter = new TermsFilter(
Field.CATEGORY_ID);
for (long categoryId : categoryIds) {
try {
MBCategoryServiceUtil.getCategory(categoryId);
}
catch (PortalException pe) {
if (_log.isDebugEnabled()) {
_log.debug(
"Unable to get message boards category " +
categoryId,
pe);
}
continue;
}
categoriesTermsFilter.addValue(String.valueOf(categoryId));
}
if (!categoriesTermsFilter.isEmpty()) {
contextBooleanFilter.add(
categoriesTermsFilter, BooleanClauseOccur.MUST);
}
}
}
@Override
public void updateFullQuery(SearchContext searchContext) {
if (searchContext.isIncludeDiscussions()) {
searchContext.addFullQueryEntryClassName(MBMessage.class.getName());
searchContext.setAttribute("discussion", Boolean.TRUE);
}
}
@Override
protected void doDelete(MBMessage mbMessage) throws Exception {
deleteDocument(mbMessage.getCompanyId(), mbMessage.getMessageId());
}
@Override
protected Document doGetDocument(MBMessage mbMessage) throws Exception {
Document document = getBaseModelDocument(CLASS_NAME, mbMessage);
document.addKeyword(Field.CATEGORY_ID, mbMessage.getCategoryId());
document.addText(Field.CONTENT, processContent(mbMessage));
document.addKeyword(
Field.ROOT_ENTRY_CLASS_PK, mbMessage.getRootMessageId());
document.addText(Field.TITLE, mbMessage.getSubject());
if (mbMessage.isAnonymous()) {
document.remove(Field.USER_NAME);
}
MBDiscussion discussion =
MBDiscussionLocalServiceUtil.fetchThreadDiscussion(
mbMessage.getThreadId());
if (discussion == null) {
document.addKeyword("discussion", false);
}
else {
document.addKeyword("discussion", true);
}
document.addKeyword("threadId", mbMessage.getThreadId());
if (mbMessage.isDiscussion()) {
Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
mbMessage.getClassName());
if ((indexer != null) && (indexer instanceof RelatedEntryIndexer)) {
RelatedEntryIndexer relatedEntryIndexer =
(RelatedEntryIndexer)indexer;
Comment comment = CommentManagerUtil.fetchComment(
mbMessage.getMessageId());
if (comment != null) {
relatedEntryIndexer.addRelatedEntryFields(
document, comment);
document.addKeyword(Field.RELATED_ENTRY, true);
}
}
}
return document;
}
@Override
protected Summary doGetSummary(
Document document, Locale locale, String snippet,
PortletRequest portletRequest, PortletResponse portletResponse) {
Summary summary = createSummary(document, Field.TITLE, Field.CONTENT);
summary.setMaxContentLength(200);
return summary;
}
@Override
protected void doReindex(MBMessage mbMessage) throws Exception {
if ((!mbMessage.isApproved() && !mbMessage.isInTrash()) ||
(mbMessage.isDiscussion() && mbMessage.isRoot())) {
return;
}
Document document = getDocument(mbMessage);
IndexWriterHelperUtil.updateDocument(
getSearchEngineId(), mbMessage.getCompanyId(), document,
isCommitImmediately());
reindexAttachments(mbMessage);
}
@Override
protected void doReindex(String className, long classPK) throws Exception {
MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
doReindex(message);
if (message.isRoot()) {
List<MBMessage> messages =
MBMessageLocalServiceUtil.getThreadMessages(
message.getThreadId(), WorkflowConstants.STATUS_APPROVED);
for (MBMessage curMessage : messages) {
reindex(curMessage);
}
}
else {
reindex(message);
}
}
@Override
protected void doReindex(String[] ids) throws Exception {
long companyId = GetterUtil.getLong(ids[0]);
reindexCategories(companyId);
reindexDiscussions(companyId);
reindexRoot(companyId);
}
protected String processContent(MBMessage message) {
String content = message.getBody();
try {
if (message.isFormatBBCode()) {
content = BBCodeTranslatorUtil.getHTML(content);
}
}
catch (Exception e) {
_log.error(
"Could not parse message " + message.getMessageId() + ": " +
e.getMessage());
}
content = HtmlUtil.extractText(content);
return content;
}
protected void reindexAttachments(MBMessage mbMessage)
throws PortalException {
Indexer<DLFileEntry> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
DLFileEntry.class);
for (FileEntry attachmentsFileEntry :
mbMessage.getAttachmentsFileEntries()) {
indexer.reindex((DLFileEntry)attachmentsFileEntry.getModel());
}
}
protected void reindexCategories(final long companyId)
throws PortalException {
ActionableDynamicQuery actionableDynamicQuery =
MBCategoryLocalServiceUtil.getActionableDynamicQuery();
actionableDynamicQuery.setCompanyId(companyId);
actionableDynamicQuery.setPerformActionMethod(
new ActionableDynamicQuery.PerformActionMethod<MBCategory>() {
@Override
public void performAction(MBCategory category)
throws PortalException {
reindexMessages(
companyId, category.getGroupId(),
category.getCategoryId());
}
});
actionableDynamicQuery.performActions();
}
protected void reindexDiscussions(final long companyId)
throws PortalException {
ActionableDynamicQuery actionableDynamicQuery =
GroupLocalServiceUtil.getActionableDynamicQuery();
actionableDynamicQuery.setCompanyId(companyId);
actionableDynamicQuery.setPerformActionMethod(
new ActionableDynamicQuery.PerformActionMethod<Group>() {
@Override
public void performAction(Group group) throws PortalException {
reindexMessages(
companyId, group.getGroupId(),
MBCategoryConstants.DISCUSSION_CATEGORY_ID);
}
});
actionableDynamicQuery.performActions();
}
protected void reindexMessages(
long companyId, long groupId, final long categoryId)
throws PortalException {
final IndexableActionableDynamicQuery indexableActionableDynamicQuery =
MBMessageLocalServiceUtil.getIndexableActionableDynamicQuery();
indexableActionableDynamicQuery.setAddCriteriaMethod(
new ActionableDynamicQuery.AddCriteriaMethod() {
@Override
public void addCriteria(DynamicQuery dynamicQuery) {
Property categoryIdProperty = PropertyFactoryUtil.forName(
"categoryId");
dynamicQuery.add(categoryIdProperty.eq(categoryId));
Property statusProperty = PropertyFactoryUtil.forName(
"status");
Integer[] statuses = {
WorkflowConstants.STATUS_APPROVED,
WorkflowConstants.STATUS_IN_TRASH
};
dynamicQuery.add(statusProperty.in(statuses));
}
});
indexableActionableDynamicQuery.setCompanyId(companyId);
indexableActionableDynamicQuery.setGroupId(groupId);
indexableActionableDynamicQuery.setPerformActionMethod(
new ActionableDynamicQuery.PerformActionMethod<MBMessage>() {
@Override
public void performAction(MBMessage message) {
if (message.isDiscussion() && message.isRoot()) {
return;
}
try {
Document document = getDocument(message);
indexableActionableDynamicQuery.addDocuments(document);
}
catch (PortalException pe) {
if (_log.isWarnEnabled()) {
_log.warn(
"Unable to index message boards message " +
message.getMessageId(),
pe);
}
}
}
});
indexableActionableDynamicQuery.setSearchEngineId(getSearchEngineId());
indexableActionableDynamicQuery.performActions();
}
protected void reindexRoot(final long companyId) throws PortalException {
ActionableDynamicQuery actionableDynamicQuery =
GroupLocalServiceUtil.getActionableDynamicQuery();
actionableDynamicQuery.setCompanyId(companyId);
actionableDynamicQuery.setPerformActionMethod(
new ActionableDynamicQuery.PerformActionMethod<Group>() {
@Override
public void performAction(Group group) throws PortalException {
reindexMessages(
companyId, group.getGroupId(),
MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID);
}
});
actionableDynamicQuery.performActions();
}
private static final Log _log = LogFactoryUtil.getLog(
MBMessageIndexer.class);
private final RelatedEntryIndexer _relatedEntryIndexer =
new BaseRelatedEntryIndexer();
}