001
014
015 package com.liferay.portlet.messageboards.util;
016
017 import com.liferay.portal.kernel.comment.Comment;
018 import com.liferay.portal.kernel.comment.CommentManagerUtil;
019 import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
020 import com.liferay.portal.kernel.dao.orm.DynamicQuery;
021 import com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;
022 import com.liferay.portal.kernel.dao.orm.Property;
023 import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
024 import com.liferay.portal.kernel.exception.PortalException;
025 import com.liferay.portal.kernel.log.Log;
026 import com.liferay.portal.kernel.log.LogFactoryUtil;
027 import com.liferay.portal.kernel.parsers.bbcode.BBCodeTranslatorUtil;
028 import com.liferay.portal.kernel.repository.model.FileEntry;
029 import com.liferay.portal.kernel.search.BaseIndexer;
030 import com.liferay.portal.kernel.search.BaseRelatedEntryIndexer;
031 import com.liferay.portal.kernel.search.BooleanClauseOccur;
032 import com.liferay.portal.kernel.search.Document;
033 import com.liferay.portal.kernel.search.Field;
034 import com.liferay.portal.kernel.search.IndexWriterHelperUtil;
035 import com.liferay.portal.kernel.search.Indexer;
036 import com.liferay.portal.kernel.search.IndexerRegistryUtil;
037 import com.liferay.portal.kernel.search.RelatedEntryIndexer;
038 import com.liferay.portal.kernel.search.SearchContext;
039 import com.liferay.portal.kernel.search.Summary;
040 import com.liferay.portal.kernel.search.filter.BooleanFilter;
041 import com.liferay.portal.kernel.search.filter.TermsFilter;
042 import com.liferay.portal.kernel.spring.osgi.OSGiBeanProperties;
043 import com.liferay.portal.kernel.util.GetterUtil;
044 import com.liferay.portal.kernel.util.HtmlUtil;
045 import com.liferay.portal.kernel.util.Validator;
046 import com.liferay.portal.kernel.workflow.WorkflowConstants;
047 import com.liferay.portal.model.Group;
048 import com.liferay.portal.security.permission.ActionKeys;
049 import com.liferay.portal.security.permission.PermissionChecker;
050 import com.liferay.portal.service.GroupLocalServiceUtil;
051 import com.liferay.portlet.messageboards.model.MBCategory;
052 import com.liferay.portlet.messageboards.model.MBCategoryConstants;
053 import com.liferay.portlet.messageboards.model.MBDiscussion;
054 import com.liferay.portlet.messageboards.model.MBMessage;
055 import com.liferay.portlet.messageboards.service.MBCategoryLocalServiceUtil;
056 import com.liferay.portlet.messageboards.service.MBCategoryServiceUtil;
057 import com.liferay.portlet.messageboards.service.MBDiscussionLocalServiceUtil;
058 import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
059 import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
060
061 import java.util.List;
062 import java.util.Locale;
063
064 import javax.portlet.PortletRequest;
065 import javax.portlet.PortletResponse;
066
067
073 @OSGiBeanProperties
074 public class MBMessageIndexer
075 extends BaseIndexer<MBMessage> implements RelatedEntryIndexer {
076
077 public static final String CLASS_NAME = MBMessage.class.getName();
078
079 public MBMessageIndexer() {
080 setDefaultSelectedFieldNames(
081 Field.ASSET_TAG_NAMES, Field.CLASS_NAME_ID, Field.CLASS_PK,
082 Field.COMPANY_ID, Field.CONTENT, Field.ENTRY_CLASS_NAME,
083 Field.ENTRY_CLASS_PK, Field.GROUP_ID, Field.MODIFIED_DATE,
084 Field.SCOPE_GROUP_ID, Field.TITLE, Field.UID);
085 setFilterSearch(true);
086 setPermissionAware(true);
087 }
088
089 @Override
090 public void addRelatedClassNames(
091 BooleanFilter contextFilter, SearchContext searchContext)
092 throws Exception {
093
094 _relatedEntryIndexer.addRelatedClassNames(contextFilter, searchContext);
095 }
096
097 @Override
098 public void addRelatedEntryFields(Document document, Object obj)
099 throws Exception {
100
101 FileEntry fileEntry = (FileEntry)obj;
102
103 MBMessage message = MBMessageAttachmentsUtil.fetchMessage(
104 fileEntry.getFileEntryId());
105
106 if (message == null) {
107 return;
108 }
109
110 document.addKeyword(Field.CATEGORY_ID, message.getCategoryId());
111
112 document.addKeyword("discussion", false);
113 document.addKeyword("threadId", message.getThreadId());
114 }
115
116 @Override
117 public String getClassName() {
118 return CLASS_NAME;
119 }
120
121 @Override
122 public boolean hasPermission(
123 PermissionChecker permissionChecker, String entryClassName,
124 long entryClassPK, String actionId)
125 throws Exception {
126
127 MBMessage message = MBMessageLocalServiceUtil.getMessage(entryClassPK);
128
129 if (message.isDiscussion()) {
130 Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
131 message.getClassName());
132
133 return indexer.hasPermission(
134 permissionChecker, message.getClassName(), message.getClassPK(),
135 ActionKeys.VIEW);
136 }
137
138 return MBMessagePermission.contains(
139 permissionChecker, entryClassPK, ActionKeys.VIEW);
140 }
141
142 @Override
143 public boolean isVisible(long classPK, int status) throws Exception {
144 MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
145
146 return isVisible(message.getStatus(), status);
147 }
148
149 @Override
150 public boolean isVisibleRelatedEntry(long classPK, int status)
151 throws Exception {
152
153 MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
154
155 if (message.isDiscussion()) {
156 Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
157 message.getClassName());
158
159 return indexer.isVisible(message.getClassPK(), status);
160 }
161
162 return true;
163 }
164
165 @Override
166 public void postProcessContextBooleanFilter(
167 BooleanFilter contextBooleanFilter, SearchContext searchContext)
168 throws Exception {
169
170 addStatus(contextBooleanFilter, searchContext);
171
172 boolean discussion = GetterUtil.getBoolean(
173 searchContext.getAttribute("discussion"), false);
174
175 contextBooleanFilter.addRequiredTerm("discussion", discussion);
176
177 if (searchContext.isIncludeDiscussions()) {
178 addRelatedClassNames(contextBooleanFilter, searchContext);
179 }
180
181 String classNameId = GetterUtil.getString(
182 searchContext.getAttribute(Field.CLASS_NAME_ID));
183
184 if (Validator.isNotNull(classNameId)) {
185 contextBooleanFilter.addRequiredTerm(
186 Field.CLASS_NAME_ID, classNameId);
187 }
188
189 long threadId = GetterUtil.getLong(
190 (String)searchContext.getAttribute("threadId"));
191
192 if (threadId > 0) {
193 contextBooleanFilter.addRequiredTerm("threadId", threadId);
194 }
195
196 long[] categoryIds = searchContext.getCategoryIds();
197
198 if ((categoryIds != null) && (categoryIds.length > 0) &&
199 (categoryIds[0] !=
200 MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID)) {
201
202 TermsFilter categoriesTermsFilter = new TermsFilter(
203 Field.CATEGORY_ID);
204
205 for (long categoryId : categoryIds) {
206 try {
207 MBCategoryServiceUtil.getCategory(categoryId);
208 }
209 catch (PortalException pe) {
210 if (_log.isDebugEnabled()) {
211 _log.debug(
212 "Unable to get message boards category " +
213 categoryId,
214 pe);
215 }
216
217 continue;
218 }
219
220 categoriesTermsFilter.addValue(String.valueOf(categoryId));
221 }
222
223 if (!categoriesTermsFilter.isEmpty()) {
224 contextBooleanFilter.add(
225 categoriesTermsFilter, BooleanClauseOccur.MUST);
226 }
227 }
228 }
229
230 @Override
231 public void updateFullQuery(SearchContext searchContext) {
232 if (searchContext.isIncludeDiscussions()) {
233 searchContext.addFullQueryEntryClassName(MBMessage.class.getName());
234
235 searchContext.setAttribute("discussion", Boolean.TRUE);
236 }
237 }
238
239 @Override
240 protected void doDelete(MBMessage mbMessage) throws Exception {
241 deleteDocument(mbMessage.getCompanyId(), mbMessage.getMessageId());
242 }
243
244 @Override
245 protected Document doGetDocument(MBMessage mbMessage) throws Exception {
246 Document document = getBaseModelDocument(CLASS_NAME, mbMessage);
247
248 document.addKeyword(Field.CATEGORY_ID, mbMessage.getCategoryId());
249 document.addText(Field.CONTENT, processContent(mbMessage));
250 document.addKeyword(
251 Field.ROOT_ENTRY_CLASS_PK, mbMessage.getRootMessageId());
252 document.addText(Field.TITLE, mbMessage.getSubject());
253
254 if (mbMessage.isAnonymous()) {
255 document.remove(Field.USER_NAME);
256 }
257
258 MBDiscussion discussion =
259 MBDiscussionLocalServiceUtil.fetchThreadDiscussion(
260 mbMessage.getThreadId());
261
262 if (discussion == null) {
263 document.addKeyword("discussion", false);
264 }
265 else {
266 document.addKeyword("discussion", true);
267 }
268
269 document.addKeyword("threadId", mbMessage.getThreadId());
270
271 if (mbMessage.isDiscussion()) {
272 Indexer<?> indexer = IndexerRegistryUtil.getIndexer(
273 mbMessage.getClassName());
274
275 if ((indexer != null) && (indexer instanceof RelatedEntryIndexer)) {
276 RelatedEntryIndexer relatedEntryIndexer =
277 (RelatedEntryIndexer)indexer;
278
279 Comment comment = CommentManagerUtil.fetchComment(
280 mbMessage.getMessageId());
281
282 if (comment != null) {
283 relatedEntryIndexer.addRelatedEntryFields(
284 document, comment);
285
286 document.addKeyword(Field.RELATED_ENTRY, true);
287 }
288 }
289 }
290
291 return document;
292 }
293
294 @Override
295 protected Summary doGetSummary(
296 Document document, Locale locale, String snippet,
297 PortletRequest portletRequest, PortletResponse portletResponse) {
298
299 Summary summary = createSummary(document, Field.TITLE, Field.CONTENT);
300
301 summary.setMaxContentLength(200);
302
303 return summary;
304 }
305
306 @Override
307 protected void doReindex(MBMessage mbMessage) throws Exception {
308 if ((!mbMessage.isApproved() && !mbMessage.isInTrash()) ||
309 (mbMessage.isDiscussion() && mbMessage.isRoot())) {
310
311 return;
312 }
313
314 Document document = getDocument(mbMessage);
315
316 IndexWriterHelperUtil.updateDocument(
317 getSearchEngineId(), mbMessage.getCompanyId(), document,
318 isCommitImmediately());
319 }
320
321 @Override
322 protected void doReindex(String className, long classPK) throws Exception {
323 MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
324
325 doReindex(message);
326
327 if (message.isRoot()) {
328 List<MBMessage> messages =
329 MBMessageLocalServiceUtil.getThreadMessages(
330 message.getThreadId(), WorkflowConstants.STATUS_APPROVED);
331
332 for (MBMessage curMessage : messages) {
333 reindex(curMessage);
334 }
335 }
336 else {
337 reindex(message);
338 }
339 }
340
341 @Override
342 protected void doReindex(String[] ids) throws Exception {
343 long companyId = GetterUtil.getLong(ids[0]);
344
345 reindexCategories(companyId);
346 reindexDiscussions(companyId);
347 reindexRoot(companyId);
348 }
349
350 protected String processContent(MBMessage message) {
351 String content = message.getBody();
352
353 try {
354 if (message.isFormatBBCode()) {
355 content = BBCodeTranslatorUtil.getHTML(content);
356 }
357 }
358 catch (Exception e) {
359 _log.error(
360 "Could not parse message " + message.getMessageId() + ": " +
361 e.getMessage());
362 }
363
364 content = HtmlUtil.extractText(content);
365
366 return content;
367 }
368
369 protected void reindexCategories(final long companyId)
370 throws PortalException {
371
372 ActionableDynamicQuery actionableDynamicQuery =
373 MBCategoryLocalServiceUtil.getActionableDynamicQuery();
374
375 actionableDynamicQuery.setCompanyId(companyId);
376 actionableDynamicQuery.setPerformActionMethod(
377 new ActionableDynamicQuery.PerformActionMethod<MBCategory>() {
378
379 @Override
380 public void performAction(MBCategory category)
381 throws PortalException {
382
383 reindexMessages(
384 companyId, category.getGroupId(),
385 category.getCategoryId());
386 }
387
388 });
389
390 actionableDynamicQuery.performActions();
391 }
392
393 protected void reindexDiscussions(final long companyId)
394 throws PortalException {
395
396 ActionableDynamicQuery actionableDynamicQuery =
397 GroupLocalServiceUtil.getActionableDynamicQuery();
398
399 actionableDynamicQuery.setCompanyId(companyId);
400 actionableDynamicQuery.setPerformActionMethod(
401 new ActionableDynamicQuery.PerformActionMethod<Group>() {
402
403 @Override
404 public void performAction(Group group) throws PortalException {
405 reindexMessages(
406 companyId, group.getGroupId(),
407 MBCategoryConstants.DISCUSSION_CATEGORY_ID);
408 }
409
410 });
411
412 actionableDynamicQuery.performActions();
413 }
414
415 protected void reindexMessages(
416 long companyId, long groupId, final long categoryId)
417 throws PortalException {
418
419 final IndexableActionableDynamicQuery indexableActionableDynamicQuery =
420 MBMessageLocalServiceUtil.getIndexableActionableDynamicQuery();
421
422 indexableActionableDynamicQuery.setAddCriteriaMethod(
423 new ActionableDynamicQuery.AddCriteriaMethod() {
424
425 @Override
426 public void addCriteria(DynamicQuery dynamicQuery) {
427 Property categoryIdProperty = PropertyFactoryUtil.forName(
428 "categoryId");
429
430 dynamicQuery.add(categoryIdProperty.eq(categoryId));
431
432 Property statusProperty = PropertyFactoryUtil.forName(
433 "status");
434
435 Integer[] statuses = {
436 WorkflowConstants.STATUS_APPROVED,
437 WorkflowConstants.STATUS_IN_TRASH
438 };
439
440 dynamicQuery.add(statusProperty.in(statuses));
441 }
442
443 });
444 indexableActionableDynamicQuery.setCompanyId(companyId);
445 indexableActionableDynamicQuery.setGroupId(groupId);
446 indexableActionableDynamicQuery.setPerformActionMethod(
447 new ActionableDynamicQuery.PerformActionMethod<MBMessage>() {
448
449 @Override
450 public void performAction(MBMessage message) {
451 if (message.isDiscussion() && message.isRoot()) {
452 return;
453 }
454
455 try {
456 Document document = getDocument(message);
457
458 indexableActionableDynamicQuery.addDocuments(document);
459 }
460 catch (PortalException pe) {
461 if (_log.isWarnEnabled()) {
462 _log.warn(
463 "Unable to index message boards message " +
464 message.getMessageId(),
465 pe);
466 }
467 }
468 }
469
470 });
471 indexableActionableDynamicQuery.setSearchEngineId(getSearchEngineId());
472
473 indexableActionableDynamicQuery.performActions();
474 }
475
476 protected void reindexRoot(final long companyId) throws PortalException {
477 ActionableDynamicQuery actionableDynamicQuery =
478 GroupLocalServiceUtil.getActionableDynamicQuery();
479
480 actionableDynamicQuery.setCompanyId(companyId);
481 actionableDynamicQuery.setPerformActionMethod(
482 new ActionableDynamicQuery.PerformActionMethod<Group>() {
483
484 @Override
485 public void performAction(Group group) throws PortalException {
486 reindexMessages(
487 companyId, group.getGroupId(),
488 MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID);
489 }
490
491 });
492
493 actionableDynamicQuery.performActions();
494 }
495
496 private static final Log _log = LogFactoryUtil.getLog(
497 MBMessageIndexer.class);
498
499 private final RelatedEntryIndexer _relatedEntryIndexer =
500 new BaseRelatedEntryIndexer();
501
502 }