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