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