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