001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.messageboards.util;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.search.BaseIndexer;
021    import com.liferay.portal.kernel.search.BooleanQuery;
022    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
023    import com.liferay.portal.kernel.search.Document;
024    import com.liferay.portal.kernel.search.DocumentImpl;
025    import com.liferay.portal.kernel.search.Field;
026    import com.liferay.portal.kernel.search.Hits;
027    import com.liferay.portal.kernel.search.Indexer;
028    import com.liferay.portal.kernel.search.SearchContext;
029    import com.liferay.portal.kernel.search.SearchEngineUtil;
030    import com.liferay.portal.kernel.search.Summary;
031    import com.liferay.portal.kernel.util.GetterUtil;
032    import com.liferay.portal.kernel.util.HtmlUtil;
033    import com.liferay.portal.kernel.util.StringUtil;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.kernel.workflow.WorkflowConstants;
036    import com.liferay.portal.model.Group;
037    import com.liferay.portal.security.permission.ActionKeys;
038    import com.liferay.portal.security.permission.PermissionChecker;
039    import com.liferay.portal.service.GroupLocalServiceUtil;
040    import com.liferay.portal.util.PortalUtil;
041    import com.liferay.portal.util.PortletKeys;
042    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
043    import com.liferay.portlet.expando.model.ExpandoBridge;
044    import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
045    import com.liferay.portlet.messageboards.model.MBCategory;
046    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
047    import com.liferay.portlet.messageboards.model.MBMessage;
048    import com.liferay.portlet.messageboards.model.MBThread;
049    import com.liferay.portlet.messageboards.service.MBCategoryLocalServiceUtil;
050    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
051    import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
052    
053    import java.util.ArrayList;
054    import java.util.Collection;
055    import java.util.Date;
056    import java.util.List;
057    
058    import javax.portlet.PortletURL;
059    
060    /**
061     * @author Brian Wing Shun Chan
062     * @author Harry Mark
063     * @author Bruno Farache
064     * @author Raymond Augé
065     */
066    public class MBIndexer extends BaseIndexer {
067    
068            public static final String[] CLASS_NAMES = {MBMessage.class.getName()};
069    
070            public static final String PORTLET_ID = PortletKeys.MESSAGE_BOARDS;
071    
072            public String[] getClassNames() {
073                    return CLASS_NAMES;
074            }
075    
076            public Summary getSummary(
077                    Document document, String snippet, PortletURL portletURL) {
078    
079                    String title = document.get(Field.TITLE);
080    
081                    String content = snippet;
082    
083                    if (Validator.isNull(snippet)) {
084                            content = StringUtil.shorten(document.get(Field.CONTENT), 200);
085                    }
086    
087                    String messageId = document.get(Field.ENTRY_CLASS_PK);
088    
089                    portletURL.setParameter(
090                            "struts_action", "/message_boards/view_message");
091                    portletURL.setParameter("messageId", messageId);
092    
093                    return new Summary(title, content, portletURL);
094            }
095    
096            protected void doDelete(Object obj) throws Exception {
097                    if (obj instanceof MBCategory) {
098                            MBCategory category = (MBCategory)obj;
099    
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                    document.addKeyword(
199                            Field.ROOT_ENTRY_CLASS_PK, message.getRootMessageId());
200    
201                    ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
202    
203                    return document;
204            }
205    
206            protected void doReindex(Object obj) throws Exception {
207                    MBMessage message = (MBMessage)obj;
208    
209                    if (message.isDiscussion() ||
210                            (message.getStatus() != WorkflowConstants.STATUS_APPROVED)) {
211    
212                            return;
213                    }
214    
215                    Document document = getDocument(message);
216    
217                    SearchEngineUtil.updateDocument(message.getCompanyId(), document);
218            }
219    
220            protected void doReindex(String className, long classPK) throws Exception {
221                    MBMessage message = MBMessageLocalServiceUtil.getMessage(classPK);
222    
223                    doReindex(message);
224    
225                    if (message.isRoot()) {
226                            List<MBMessage> messages =
227                                    MBMessageLocalServiceUtil.getThreadMessages(
228                                            message.getThreadId(), WorkflowConstants.STATUS_APPROVED);
229    
230                            for (MBMessage curMessage : messages) {
231                                    reindex(curMessage);
232                            }
233                    }
234                    else {
235                            reindex(message);
236                    }
237            }
238    
239            protected void doReindex(String[] ids) throws Exception {
240                    long companyId = GetterUtil.getLong(ids[0]);
241    
242                    reindexCategories(companyId);
243                    reindexRoot(companyId);
244            }
245    
246            protected String getPortletId(SearchContext searchContext) {
247                    return PORTLET_ID;
248            }
249    
250            protected boolean hasPermission(
251                            PermissionChecker permissionChecker, long entryClassPK,
252                            String actionId)
253                    throws Exception {
254    
255                    return MBMessagePermission.contains(
256                            permissionChecker, entryClassPK, ActionKeys.VIEW);
257            }
258    
259            protected boolean isFilterSearch() {
260                    return _FILTER_SEARCH;
261            }
262    
263            protected void postProcessContextQuery(
264                            BooleanQuery contextQuery, SearchContext searchContext)
265                    throws Exception {
266    
267                    long threadId = GetterUtil.getLong(
268                            (String)searchContext.getAttribute("threadId"));
269    
270                    if (threadId > 0) {
271                            contextQuery.addTerm("threadId", threadId);
272                    }
273            }
274    
275            protected String processContent(long messageId, String content) {
276                    try {
277                            content = BBCodeUtil.getHTML(content);
278                    }
279                    catch (Exception e) {
280                            _log.error(
281                                    "Could not parse message " + messageId + ": " + e.getMessage());
282                    }
283    
284                    content = HtmlUtil.extractText(content);
285    
286                    return content;
287            }
288    
289            protected void reindexCategories(long companyId) throws Exception {
290                    int categoryCount =
291                            MBCategoryLocalServiceUtil.getCompanyCategoriesCount(companyId);
292    
293                    int categoryPages = categoryCount / Indexer.DEFAULT_INTERVAL;
294    
295                    for (int i = 0; i <= categoryPages; i++) {
296                            int categoryStart = (i * Indexer.DEFAULT_INTERVAL);
297                            int categoryEnd = categoryStart + Indexer.DEFAULT_INTERVAL;
298    
299                            reindexCategories(companyId, categoryStart, categoryEnd);
300                    }
301            }
302    
303            protected void reindexCategories(
304                            long companyId, int categoryStart, int categoryEnd)
305                    throws Exception {
306    
307                    List<MBCategory> categories =
308                            MBCategoryLocalServiceUtil.getCompanyCategories(
309                                    companyId, categoryStart, categoryEnd);
310    
311                    for (MBCategory category : categories) {
312                            long groupId = category.getGroupId();
313                            long categoryId = category.getCategoryId();
314    
315                            int messageCount =
316                                    MBMessageLocalServiceUtil.getCategoryMessagesCount(
317                                            groupId, categoryId, WorkflowConstants.STATUS_APPROVED);
318    
319                            int messagePages = messageCount / Indexer.DEFAULT_INTERVAL;
320    
321                            for (int i = 0; i <= messagePages; i++) {
322                                    int messageStart = (i * Indexer.DEFAULT_INTERVAL);
323                                    int messageEnd = messageStart + Indexer.DEFAULT_INTERVAL;
324    
325                                    reindexMessages(
326                                            companyId, groupId, categoryId, messageStart, messageEnd);
327                            }
328                    }
329            }
330    
331            protected void reindexMessages(
332                            long companyId, long groupId, long categoryId, int messageStart,
333                            int messageEnd)
334                    throws Exception {
335    
336                    List<MBMessage> messages =
337                            MBMessageLocalServiceUtil.getCategoryMessages(
338                                    groupId, categoryId, WorkflowConstants.STATUS_APPROVED,
339                                    messageStart, messageEnd);
340    
341                    if (messages.isEmpty()) {
342                            return;
343                    }
344    
345                    Collection<Document> documents = new ArrayList<Document>();
346    
347                    for (MBMessage message : messages) {
348                            Document document = getDocument(message);
349    
350                            documents.add(document);
351                    }
352    
353                    SearchEngineUtil.updateDocuments(companyId, documents);
354            }
355    
356            protected void reindexRoot(long companyId) throws Exception {
357                    int groupCount = GroupLocalServiceUtil.getCompanyGroupsCount(companyId);
358    
359                    int groupPages = groupCount / Indexer.DEFAULT_INTERVAL;
360    
361                    for (int i = 0; i <= groupPages; i++) {
362                            int groupStart = (i * Indexer.DEFAULT_INTERVAL);
363                            int groupEnd = groupStart + Indexer.DEFAULT_INTERVAL;
364    
365                            reindexRoot(companyId, groupStart, groupEnd);
366                    }
367            }
368    
369            protected void reindexRoot(long companyId, int groupStart, int groupEnd)
370                    throws Exception {
371    
372                    List<Group> groups = GroupLocalServiceUtil.getCompanyGroups(
373                            companyId, groupStart, groupEnd);
374    
375                    for (Group group : groups) {
376                            long groupId = group.getGroupId();
377                            long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
378    
379                            int entryCount = MBMessageLocalServiceUtil.getCategoryMessagesCount(
380                                    groupId, categoryId, WorkflowConstants.STATUS_APPROVED);
381    
382                            int entryPages = entryCount / Indexer.DEFAULT_INTERVAL;
383    
384                            for (int i = 0; i <= entryPages; i++) {
385                                    int entryStart = (i * Indexer.DEFAULT_INTERVAL);
386                                    int entryEnd = entryStart + Indexer.DEFAULT_INTERVAL;
387    
388                                    reindexMessages(
389                                            companyId, groupId, categoryId, entryStart, entryEnd);
390                            }
391                    }
392            }
393    
394            private static final boolean _FILTER_SEARCH = true;
395    
396            private static Log _log = LogFactoryUtil.getLog(MBIndexer.class);
397    
398    }