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