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