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