001
014
015 package com.liferay.portlet.messageboards.util;
016
017 import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
018 import com.liferay.portal.kernel.dao.orm.DynamicQuery;
019 import com.liferay.portal.kernel.dao.orm.Property;
020 import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
021 import com.liferay.portal.kernel.dao.orm.QueryUtil;
022 import com.liferay.portal.kernel.exception.PortalException;
023 import com.liferay.portal.kernel.exception.SystemException;
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.search.BaseIndexer;
028 import com.liferay.portal.kernel.search.BooleanClauseOccur;
029 import com.liferay.portal.kernel.search.BooleanQuery;
030 import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
031 import com.liferay.portal.kernel.search.Document;
032 import com.liferay.portal.kernel.search.DocumentImpl;
033 import com.liferay.portal.kernel.search.Field;
034 import com.liferay.portal.kernel.search.Hits;
035 import com.liferay.portal.kernel.search.Indexer;
036 import com.liferay.portal.kernel.search.IndexerRegistryUtil;
037 import com.liferay.portal.kernel.search.SearchContext;
038 import com.liferay.portal.kernel.search.SearchEngineUtil;
039 import com.liferay.portal.kernel.search.Summary;
040 import com.liferay.portal.kernel.util.GetterUtil;
041 import com.liferay.portal.kernel.util.HtmlUtil;
042 import com.liferay.portal.kernel.workflow.WorkflowConstants;
043 import com.liferay.portal.model.Group;
044 import com.liferay.portal.security.permission.ActionKeys;
045 import com.liferay.portal.security.permission.PermissionChecker;
046 import com.liferay.portal.service.persistence.GroupActionableDynamicQuery;
047 import com.liferay.portal.util.PortalUtil;
048 import com.liferay.portal.util.PortletKeys;
049 import com.liferay.portlet.documentlibrary.model.DLFileEntry;
050 import com.liferay.portlet.messageboards.NoSuchDiscussionException;
051 import com.liferay.portlet.messageboards.asset.MBMessageAssetRendererFactory;
052 import com.liferay.portlet.messageboards.model.MBCategory;
053 import com.liferay.portlet.messageboards.model.MBCategoryConstants;
054 import com.liferay.portlet.messageboards.model.MBMessage;
055 import com.liferay.portlet.messageboards.model.MBThread;
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 import com.liferay.portlet.messageboards.service.persistence.MBCategoryActionableDynamicQuery;
061 import com.liferay.portlet.messageboards.service.persistence.MBMessageActionableDynamicQuery;
062
063 import java.util.ArrayList;
064 import java.util.Collection;
065 import java.util.List;
066 import java.util.Locale;
067
068 import javax.portlet.PortletURL;
069
070
076 public class MBMessageIndexer extends BaseIndexer {
077
078 public static final String[] CLASS_NAMES = {MBMessage.class.getName()};
079
080 public static final String PORTLET_ID = PortletKeys.MESSAGE_BOARDS;
081
082 public MBMessageIndexer() {
083 setFilterSearch(true);
084 setPermissionAware(true);
085 }
086
087 @Override
088 public void addRelatedEntryFields(Document document, Object obj)
089 throws Exception {
090
091 DLFileEntry dlFileEntry = (DLFileEntry)obj;
092
093 MBMessage message = MBMessageAttachmentsUtil.getMessage(
094 dlFileEntry.getFileEntryId());
095
096 document.addKeyword(Field.CATEGORY_ID, message.getCategoryId());
097 document.addKeyword(
098 Field.CLASS_NAME_ID,
099 PortalUtil.getClassNameId(MBMessage.class.getName()));
100 document.addKeyword(Field.CLASS_PK, message.getMessageId());
101 document.addKeyword(Field.RELATED_ENTRY, true);
102
103 document.addKeyword("discussion", false);
104 document.addKeyword("threadId", message.getThreadId());
105 }
106
107 public String[] getClassNames() {
108 return CLASS_NAMES;
109 }
110
111 public String getPortletId() {
112 return PORTLET_ID;
113 }
114
115 @Override
116 public boolean hasPermission(
117 PermissionChecker permissionChecker, String entryClassName,
118 long entryClassPK, String actionId)
119 throws Exception {
120
121 return MBMessagePermission.contains(
122 permissionChecker, entryClassPK, ActionKeys.VIEW);
123 }
124
125 @Override
126 public void postProcessContextQuery(
127 BooleanQuery contextQuery, SearchContext searchContext)
128 throws Exception {
129
130 int status = GetterUtil.getInteger(
131 searchContext.getAttribute(Field.STATUS),
132 WorkflowConstants.STATUS_APPROVED);
133
134 if (status != WorkflowConstants.STATUS_ANY) {
135 contextQuery.addRequiredTerm(Field.STATUS, status);
136 }
137
138 boolean discussion = GetterUtil.getBoolean(
139 searchContext.getAttribute("discussion"), false);
140
141 contextQuery.addRequiredTerm("discussion", discussion);
142
143 if (searchContext.isIncludeDiscussions()) {
144 addRelatedClassNames(contextQuery, searchContext);
145 }
146
147 long threadId = GetterUtil.getLong(
148 (String)searchContext.getAttribute("threadId"));
149
150 if (threadId > 0) {
151 contextQuery.addRequiredTerm("threadId", threadId);
152 }
153
154 long[] categoryIds = searchContext.getCategoryIds();
155
156 if ((categoryIds != null) && (categoryIds.length > 0)) {
157 if (categoryIds[0] ==
158 MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
159
160 return;
161 }
162
163 BooleanQuery categoriesQuery = BooleanQueryFactoryUtil.create(
164 searchContext);
165
166 for (long categoryId : categoryIds) {
167 try {
168 MBCategoryServiceUtil.getCategory(categoryId);
169 }
170 catch (Exception e) {
171 continue;
172 }
173
174 categoriesQuery.addTerm(Field.CATEGORY_ID, categoryId);
175 }
176
177 contextQuery.add(categoriesQuery, BooleanClauseOccur.MUST);
178 }
179 }
180
181 @Override
182 protected void doDelete(Object obj) throws Exception {
183 SearchContext searchContext = new SearchContext();
184
185 searchContext.setSearchEngineId(getSearchEngineId());
186
187 if (obj instanceof MBCategory) {
188 MBCategory category = (MBCategory)obj;
189
190 BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create(
191 searchContext);
192
193 booleanQuery.addRequiredTerm(Field.PORTLET_ID, PORTLET_ID);
194
195 booleanQuery.addRequiredTerm(
196 "categoryId", category.getCategoryId());
197
198 Hits hits = SearchEngineUtil.search(
199 getSearchEngineId(), category.getCompanyId(), booleanQuery,
200 QueryUtil.ALL_POS, QueryUtil.ALL_POS);
201
202 for (int i = 0; i < hits.getLength(); i++) {
203 Document document = hits.doc(i);
204
205 SearchEngineUtil.deleteDocument(
206 getSearchEngineId(), category.getCompanyId(),
207 document.get(Field.UID));
208 }
209 }
210 else if (obj instanceof MBMessage) {
211 MBMessage message = (MBMessage)obj;
212
213 Document document = new DocumentImpl();
214
215 document.addUID(PORTLET_ID, message.getMessageId());
216
217 SearchEngineUtil.deleteDocument(
218 getSearchEngineId(), message.getCompanyId(),
219 document.get(Field.UID));
220 }
221 else if (obj instanceof MBThread) {
222 MBThread thread = (MBThread)obj;
223
224 MBMessage message = MBMessageLocalServiceUtil.getMessage(
225 thread.getRootMessageId());
226
227 BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create(
228 searchContext);
229
230 booleanQuery.addRequiredTerm(Field.PORTLET_ID, PORTLET_ID);
231
232 booleanQuery.addRequiredTerm("threadId", thread.getThreadId());
233
234 Hits hits = SearchEngineUtil.search(
235 getSearchEngineId(), message.getCompanyId(), booleanQuery,
236 QueryUtil.ALL_POS, QueryUtil.ALL_POS);
237
238 for (int i = 0; i < hits.getLength(); i++) {
239 Document document = hits.doc(i);
240
241 SearchEngineUtil.deleteDocument(
242 getSearchEngineId(), message.getCompanyId(),
243 document.get(Field.UID));
244 }
245 }
246 }
247
248 @Override
249 protected Document doGetDocument(Object obj) throws Exception {
250 MBMessage message = (MBMessage)obj;
251
252 Document document = getBaseModelDocument(PORTLET_ID, message);
253
254 document.addKeyword(Field.CATEGORY_ID, message.getCategoryId());
255 document.addText(Field.CONTENT, processContent(message));
256 document.addKeyword(
257 Field.ROOT_ENTRY_CLASS_PK, message.getRootMessageId());
258 document.addText(Field.TITLE, message.getSubject());
259
260 if (message.isAnonymous()) {
261 document.remove(Field.USER_NAME);
262 }
263
264 try {
265 MBDiscussionLocalServiceUtil.getThreadDiscussion(
266 message.getThreadId());
267
268 document.addKeyword("discussion", true);
269 }
270 catch (NoSuchDiscussionException nsde) {
271 document.addKeyword("discussion", false);
272 }
273
274 document.addKeyword("threadId", message.getThreadId());
275
276 if (message.isDiscussion()) {
277 Indexer indexer = IndexerRegistryUtil.getIndexer(
278 message.getClassName());
279
280 if (indexer != null) {
281 indexer.addRelatedEntryFields(document, obj);
282 }
283 }
284
285 if (!message.isInTrash() && message.isInTrashThread()) {
286 addTrashFields(
287 document, MBThread.class.getName(), message.getThreadId(), null,
288 null, MBMessageAssetRendererFactory.TYPE);
289
290 String className = MBThread.class.getName();
291 long classPK = message.getThreadId();
292
293 MBThread thread = message.getThread();
294
295 if (thread.isInTrashContainer()) {
296 MBCategory category = thread.getTrashContainer();
297
298 className = MBCategory.class.getName();
299 classPK = category.getCategoryId();
300 }
301
302 document.addKeyword(Field.ROOT_ENTRY_CLASS_NAME, className);
303 document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, classPK);
304
305 document.addKeyword(
306 Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
307 }
308
309 return document;
310 }
311
312 @Override
313 protected Summary doGetSummary(
314 Document document, Locale locale, String snippet,
315 PortletURL portletURL) {
316
317 String messageId = document.get(Field.ENTRY_CLASS_PK);
318
319 portletURL.setParameter(
320 "struts_action", "/message_boards/view_message");
321 portletURL.setParameter("messageId", messageId);
322
323 Summary summary = createSummary(document, Field.TITLE, Field.CONTENT);
324
325 summary.setMaxContentLength(200);
326 summary.setPortletURL(portletURL);
327
328 return summary;
329 }
330
331 @Override
332 protected void doReindex(Object obj) throws Exception {
333 MBMessage message = (MBMessage)obj;
334
335 if (message.getStatus() != WorkflowConstants.STATUS_APPROVED) {
336 return;
337 }
338
339 if (message.isDiscussion() && message.isRoot()) {
340 return;
341 }
342
343 Document document = getDocument(message);
344
345 SearchEngineUtil.updateDocument(
346 getSearchEngineId(), message.getCompanyId(), document);
347 }
348
349 @Override
350 protected void doReindex(String className, long classPK) throws Exception {
351 MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
352
353 doReindex(message);
354
355 if (message.isRoot()) {
356 List<MBMessage> messages =
357 MBMessageLocalServiceUtil.getThreadMessages(
358 message.getThreadId(), WorkflowConstants.STATUS_APPROVED);
359
360 for (MBMessage curMessage : messages) {
361 reindex(curMessage);
362 }
363 }
364 else {
365 reindex(message);
366 }
367 }
368
369 @Override
370 protected void doReindex(String[] ids) throws Exception {
371 long companyId = GetterUtil.getLong(ids[0]);
372
373 reindexCategories(companyId);
374 reindexDiscussions(companyId);
375 reindexRoot(companyId);
376 }
377
378 @Override
379 protected String getPortletId(SearchContext searchContext) {
380 return PORTLET_ID;
381 }
382
383 protected String processContent(MBMessage message) {
384 String content = message.getBody();
385
386 try {
387 if (message.isFormatBBCode()) {
388 content = BBCodeTranslatorUtil.getHTML(content);
389 }
390 }
391 catch (Exception e) {
392 _log.error(
393 "Could not parse message " + message.getMessageId() + ": " +
394 e.getMessage());
395 }
396
397 content = HtmlUtil.extractText(content);
398
399 return content;
400 }
401
402 protected void reindexCategories(final long companyId)
403 throws PortalException, SystemException {
404
405 ActionableDynamicQuery actionableDynamicQuery =
406 new MBCategoryActionableDynamicQuery() {
407
408 @Override
409 protected void performAction(Object object)
410 throws PortalException, SystemException {
411
412 MBCategory category = (MBCategory)object;
413
414 reindexMessages(
415 companyId, category.getGroupId(), category.getCategoryId());
416 }
417
418 };
419
420 actionableDynamicQuery.setCompanyId(companyId);
421
422 actionableDynamicQuery.performActions();
423 }
424
425 protected void reindexDiscussions(final long companyId)
426 throws PortalException, SystemException {
427
428 ActionableDynamicQuery actionableDynamicQuery =
429 new GroupActionableDynamicQuery() {
430
431 @Override
432 protected void performAction(Object object)
433 throws PortalException, SystemException {
434
435 Group group = (Group)object;
436
437 reindexMessages(
438 companyId, group.getGroupId(),
439 MBCategoryConstants.DISCUSSION_CATEGORY_ID);
440 }
441
442 };
443
444 actionableDynamicQuery.setCompanyId(companyId);
445
446 actionableDynamicQuery.performActions();
447 }
448
449 protected void reindexMessages(
450 long companyId, long groupId, final long categoryId)
451 throws PortalException, SystemException {
452
453 final Collection<Document> documents = new ArrayList<Document>();
454
455 ActionableDynamicQuery actionableDynamicQuery =
456 new MBMessageActionableDynamicQuery() {
457
458 @Override
459 protected void addCriteria(DynamicQuery dynamicQuery) {
460 Property categoryIdProperty = PropertyFactoryUtil.forName(
461 "categoryId");
462
463 dynamicQuery.add(categoryIdProperty.eq(categoryId));
464
465 Property statusProperty = PropertyFactoryUtil.forName("status");
466
467 dynamicQuery.add(
468 statusProperty.eq(WorkflowConstants.STATUS_APPROVED));
469 }
470
471 @Override
472 protected void performAction(Object object) throws PortalException {
473 MBMessage message = (MBMessage)object;
474
475 Document document = getDocument(message);
476
477 documents.add(document);
478 }
479
480 };
481
482 actionableDynamicQuery.setGroupId(groupId);
483
484 actionableDynamicQuery.performActions();
485
486 SearchEngineUtil.updateDocuments(
487 getSearchEngineId(), companyId, documents);
488 }
489
490 protected void reindexRoot(final long companyId)
491 throws PortalException, SystemException {
492
493 ActionableDynamicQuery actionableDynamicQuery =
494 new GroupActionableDynamicQuery() {
495
496 @Override
497 protected void performAction(Object object)
498 throws PortalException, SystemException {
499
500 Group group = (Group)object;
501
502 reindexMessages(
503 companyId, group.getGroupId(),
504 MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID);
505 }
506
507 };
508
509 actionableDynamicQuery.setCompanyId(companyId);
510
511 actionableDynamicQuery.performActions();
512 }
513
514 private static Log _log = LogFactoryUtil.getLog(MBMessageIndexer.class);
515
516 }