1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.messageboards.util;
16  
17  import com.liferay.portal.kernel.dao.orm.QueryUtil;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.search.BaseIndexer;
21  import com.liferay.portal.kernel.search.BooleanQuery;
22  import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
23  import com.liferay.portal.kernel.search.Document;
24  import com.liferay.portal.kernel.search.DocumentImpl;
25  import com.liferay.portal.kernel.search.Field;
26  import com.liferay.portal.kernel.search.Hits;
27  import com.liferay.portal.kernel.search.Indexer;
28  import com.liferay.portal.kernel.search.SearchContext;
29  import com.liferay.portal.kernel.search.SearchEngineUtil;
30  import com.liferay.portal.kernel.search.Summary;
31  import com.liferay.portal.kernel.util.GetterUtil;
32  import com.liferay.portal.kernel.util.HtmlUtil;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.util.Validator;
35  import com.liferay.portal.kernel.workflow.WorkflowConstants;
36  import com.liferay.portal.model.Group;
37  import com.liferay.portal.service.GroupLocalServiceUtil;
38  import com.liferay.portal.util.PortalUtil;
39  import com.liferay.portal.util.PortletKeys;
40  import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
41  import com.liferay.portlet.expando.model.ExpandoBridge;
42  import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
43  import com.liferay.portlet.messageboards.model.MBCategory;
44  import com.liferay.portlet.messageboards.model.MBCategoryConstants;
45  import com.liferay.portlet.messageboards.model.MBMessage;
46  import com.liferay.portlet.messageboards.model.MBThread;
47  import com.liferay.portlet.messageboards.service.MBCategoryLocalServiceUtil;
48  import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
49  import com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil;
50  
51  import java.util.ArrayList;
52  import java.util.Collection;
53  import java.util.Date;
54  import java.util.List;
55  
56  import javax.portlet.PortletURL;
57  
58  /**
59   * <a href="MBIndexer.java.html"><b><i>View Source</i></b></a>
60   *
61   * @author Brian Wing Shun Chan
62   * @author Harry Mark
63   * @author Bruno Farache
64   * @author Raymond Augé
65   */
66  public class MBIndexer extends BaseIndexer {
67  
68      public static final String[] CLASS_NAMES = {MBMessage.class.getName()};
69  
70      public static final String PORTLET_ID = PortletKeys.MESSAGE_BOARDS;
71  
72      public String[] getClassNames() {
73          return CLASS_NAMES;
74      }
75  
76      public Summary getSummary(
77          Document document, String snippet, PortletURL portletURL) {
78  
79          String title = document.get(Field.TITLE);
80  
81          String content = snippet;
82  
83          if (Validator.isNull(snippet)) {
84              content = StringUtil.shorten(document.get(Field.CONTENT), 200);
85          }
86  
87          String messageId = document.get(Field.ENTRY_CLASS_PK);
88  
89          portletURL.setParameter(
90              "struts_action", "/message_boards/view_message");
91          portletURL.setParameter("messageId", messageId);
92  
93          return new Summary(title, content, portletURL);
94      }
95  
96      protected void doDelete(Object obj) throws Exception {
97          if (obj instanceof MBCategory) {
98              MBCategory category = (MBCategory)obj;
99  
100             BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create();
101 
102             booleanQuery.addRequiredTerm(Field.PORTLET_ID, PORTLET_ID);
103 
104             booleanQuery.addRequiredTerm(
105                 "categoryId", category.getCategoryId());
106 
107             Hits hits = SearchEngineUtil.search(
108                 category.getCompanyId(), booleanQuery, QueryUtil.ALL_POS,
109                 QueryUtil.ALL_POS);
110 
111             for (int i = 0; i < hits.getLength(); i++) {
112                 Document document = hits.doc(i);
113 
114                 SearchEngineUtil.deleteDocument(
115                     category.getCompanyId(), document.get(Field.UID));
116             }
117         }
118         else if (obj instanceof MBMessage) {
119             MBMessage message = (MBMessage)obj;
120 
121             Document document = new DocumentImpl();
122 
123             document.addUID(PORTLET_ID, message.getMessageId());
124 
125             SearchEngineUtil.deleteDocument(
126                 message.getCompanyId(), document.get(Field.UID));
127         }
128         else if (obj instanceof MBThread) {
129             MBThread thread = (MBThread)obj;
130 
131             MBMessage message = MBMessageLocalServiceUtil.getMessage(
132                 thread.getRootMessageId());
133 
134             BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create();
135 
136             booleanQuery.addRequiredTerm(Field.PORTLET_ID, PORTLET_ID);
137 
138             booleanQuery.addRequiredTerm("threadId", thread.getThreadId());
139 
140             Hits hits = SearchEngineUtil.search(
141                 message.getCompanyId(), booleanQuery, QueryUtil.ALL_POS,
142                 QueryUtil.ALL_POS);
143 
144             for (int i = 0; i < hits.getLength(); i++) {
145                 Document document = hits.doc(i);
146 
147                 SearchEngineUtil.deleteDocument(
148                     message.getCompanyId(), document.get(Field.UID));
149             }
150         }
151     }
152 
153     protected Document doGetDocument(Object obj) throws Exception {
154         MBMessage message = (MBMessage)obj;
155 
156         long companyId = message.getCompanyId();
157         long groupId = getParentGroupId(message.getGroupId());
158         long scopeGroupId = message.getGroupId();
159         long userId = message.getUserId();
160         String userName = PortalUtil.getUserName(userId, message.getUserName());
161         long categoryId = message.getCategoryId();
162         long threadId = message.getThreadId();
163         long messageId = message.getMessageId();
164         String title = message.getSubject();
165         String content = processContent(messageId, message.getBody());
166         boolean anonymous = message.isAnonymous();
167         Date modifiedDate = message.getModifiedDate();
168 
169         String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
170             MBMessage.class.getName(), messageId);
171 
172         ExpandoBridge expandoBridge = message.getExpandoBridge();
173 
174         Document document = new DocumentImpl();
175 
176         document.addUID(PORTLET_ID, messageId);
177 
178         document.addModifiedDate(modifiedDate);
179 
180         document.addKeyword(Field.COMPANY_ID, companyId);
181         document.addKeyword(Field.PORTLET_ID, PORTLET_ID);
182         document.addKeyword(Field.GROUP_ID, groupId);
183         document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);
184         document.addKeyword(Field.USER_ID, userId);
185 
186         if (!anonymous) {
187             document.addText(Field.USER_NAME, userName);
188         }
189 
190         document.addText(Field.TITLE, title);
191         document.addText(Field.CONTENT, content);
192         document.addKeyword(Field.ASSET_TAG_NAMES, assetTagNames);
193 
194         document.addKeyword(Field.CATEGORY_ID, categoryId);
195         document.addKeyword("threadId", threadId);
196         document.addKeyword(Field.ENTRY_CLASS_NAME, MBMessage.class.getName());
197         document.addKeyword(Field.ENTRY_CLASS_PK, messageId);
198 
199         try {
200             MBThread thread = MBThreadLocalServiceUtil.getMBThread(threadId);
201 
202             document.addKeyword(
203                 Field.ROOT_ENTRY_CLASS_PK, thread.getRootMessageId());
204         }
205         catch (Exception e) {
206         }
207 
208         ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
209 
210         return document;
211     }
212 
213     protected void doReindex(Object obj) throws Exception {
214         MBMessage message = (MBMessage)obj;
215 
216         if (message.isDiscussion() ||
217             (message.getStatus() != WorkflowConstants.STATUS_APPROVED)) {
218 
219             return;
220         }
221 
222         Document document = getDocument(message);
223 
224         SearchEngineUtil.updateDocument(message.getCompanyId(), document);
225     }
226 
227     protected void doReindex(String className, long classPK) throws Exception {
228         MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
229 
230         doReindex(message);
231 
232         if (message.isRoot()) {
233             List<MBMessage> messages =
234                 MBMessageLocalServiceUtil.getThreadMessages(
235                     message.getThreadId(), WorkflowConstants.STATUS_APPROVED);
236 
237             for (MBMessage curMessage : messages) {
238                 reindex(curMessage);
239             }
240         }
241         else {
242             reindex(message);
243         }
244     }
245 
246     protected void doReindex(String[] ids) throws Exception {
247         long companyId = GetterUtil.getLong(ids[0]);
248 
249         reindexCategories(companyId);
250         reindexRoot(companyId);
251     }
252 
253     protected String getPortletId(SearchContext searchContext) {
254         return PORTLET_ID;
255     }
256 
257     protected void postProcessContextQuery(
258             BooleanQuery contextQuery, SearchContext searchContext)
259         throws Exception {
260 
261         long threadId = GetterUtil.getLong(
262             (String)searchContext.getAttribute("threadId"));
263 
264         if (threadId > 0) {
265             contextQuery.addTerm("threadId", threadId);
266         }
267     }
268 
269     protected String processContent(long messageId, String content) {
270         try {
271             content = BBCodeUtil.getHTML(content);
272         }
273         catch (Exception e) {
274             _log.error(
275                 "Could not parse message " + messageId + ": " + e.getMessage());
276         }
277 
278         content = HtmlUtil.extractText(content);
279 
280         return content;
281     }
282 
283     protected void reindexCategories(long companyId) throws Exception {
284         int categoryCount =
285             MBCategoryLocalServiceUtil.getCompanyCategoriesCount(companyId);
286 
287         int categoryPages = categoryCount / Indexer.DEFAULT_INTERVAL;
288 
289         for (int i = 0; i <= categoryPages; i++) {
290             int categoryStart = (i * Indexer.DEFAULT_INTERVAL);
291             int categoryEnd = categoryStart + Indexer.DEFAULT_INTERVAL;
292 
293             reindexCategories(companyId, categoryStart, categoryEnd);
294         }
295     }
296 
297     protected void reindexCategories(
298             long companyId, int categoryStart, int categoryEnd)
299         throws Exception {
300 
301         List<MBCategory> categories =
302             MBCategoryLocalServiceUtil.getCompanyCategories(
303                 companyId, categoryStart, categoryEnd);
304 
305         for (MBCategory category : categories) {
306             long groupId = category.getGroupId();
307             long categoryId = category.getCategoryId();
308 
309             int messageCount =
310                 MBMessageLocalServiceUtil.getCategoryMessagesCount(
311                     groupId, categoryId, WorkflowConstants.STATUS_APPROVED);
312 
313             int messagePages = messageCount / Indexer.DEFAULT_INTERVAL;
314 
315             for (int i = 0; i <= messagePages; i++) {
316                 int messageStart = (i * Indexer.DEFAULT_INTERVAL);
317                 int messageEnd = messageStart + Indexer.DEFAULT_INTERVAL;
318 
319                 reindexMessages(
320                     companyId, groupId, categoryId, messageStart, messageEnd);
321             }
322         }
323     }
324 
325     protected void reindexMessages(
326             long companyId, long groupId, long categoryId, int messageStart,
327             int messageEnd)
328         throws Exception {
329 
330         List<MBMessage> messages =
331             MBMessageLocalServiceUtil.getCategoryMessages(
332                 groupId, categoryId, WorkflowConstants.STATUS_APPROVED,
333                 messageStart, messageEnd);
334 
335         if (messages.isEmpty()) {
336             return;
337         }
338 
339         Collection<Document> documents = new ArrayList<Document>();
340 
341         for (MBMessage message : messages) {
342             Document document = getDocument(message);
343 
344             documents.add(document);
345         }
346 
347         SearchEngineUtil.updateDocuments(companyId, documents);
348     }
349 
350     protected void reindexRoot(long companyId) throws Exception {
351         int groupCount = GroupLocalServiceUtil.getCompanyGroupsCount(companyId);
352 
353         int groupPages = groupCount / Indexer.DEFAULT_INTERVAL;
354 
355         for (int i = 0; i <= groupPages; i++) {
356             int groupStart = (i * Indexer.DEFAULT_INTERVAL);
357             int groupEnd = groupStart + Indexer.DEFAULT_INTERVAL;
358 
359             reindexRoot(companyId, groupStart, groupEnd);
360         }
361     }
362 
363     protected void reindexRoot(long companyId, int groupStart, int groupEnd)
364         throws Exception {
365 
366         List<Group> groups = GroupLocalServiceUtil.getCompanyGroups(
367             companyId, groupStart, groupEnd);
368 
369         for (Group group : groups) {
370             long groupId = group.getGroupId();
371             long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
372 
373             int entryCount = MBMessageLocalServiceUtil.getCategoryMessagesCount(
374                 groupId, categoryId, WorkflowConstants.STATUS_APPROVED);
375 
376             int entryPages = entryCount / Indexer.DEFAULT_INTERVAL;
377 
378             for (int i = 0; i <= entryPages; i++) {
379                 int entryStart = (i * Indexer.DEFAULT_INTERVAL);
380                 int entryEnd = entryStart + Indexer.DEFAULT_INTERVAL;
381 
382                 reindexMessages(
383                     companyId, groupId, categoryId, entryStart, entryEnd);
384             }
385         }
386     }
387 
388     private static Log _log = LogFactoryUtil.getLog(MBIndexer.class);
389 
390 }