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