001    /**
002     * Copyright (c) 2000-2013 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.journal.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.Junction;
020    import com.liferay.portal.kernel.dao.orm.Property;
021    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
022    import com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;
023    import com.liferay.portal.kernel.exception.PortalException;
024    import com.liferay.portal.kernel.exception.SystemException;
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.DocumentImpl;
031    import com.liferay.portal.kernel.search.Field;
032    import com.liferay.portal.kernel.search.SearchContext;
033    import com.liferay.portal.kernel.search.SearchEngineUtil;
034    import com.liferay.portal.kernel.search.Summary;
035    import com.liferay.portal.kernel.util.GetterUtil;
036    import com.liferay.portal.kernel.util.HtmlUtil;
037    import com.liferay.portal.kernel.util.LocaleUtil;
038    import com.liferay.portal.kernel.util.LocalizationUtil;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.StringUtil;
041    import com.liferay.portal.kernel.util.Validator;
042    import com.liferay.portal.kernel.workflow.WorkflowConstants;
043    import com.liferay.portal.security.permission.ActionKeys;
044    import com.liferay.portal.security.permission.PermissionChecker;
045    import com.liferay.portal.util.PortalUtil;
046    import com.liferay.portal.util.PortletKeys;
047    import com.liferay.portlet.dynamicdatamapping.StructureFieldException;
048    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
049    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
050    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
051    import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
052    import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
053    import com.liferay.portlet.journal.asset.JournalArticleAssetRendererFactory;
054    import com.liferay.portlet.journal.model.JournalArticle;
055    import com.liferay.portlet.journal.model.JournalArticleConstants;
056    import com.liferay.portlet.journal.model.JournalFolder;
057    import com.liferay.portlet.journal.model.JournalFolderConstants;
058    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
059    import com.liferay.portlet.journal.service.JournalFolderServiceUtil;
060    import com.liferay.portlet.journal.service.permission.JournalArticlePermission;
061    import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
062    import com.liferay.portlet.trash.util.TrashUtil;
063    
064    import java.io.Serializable;
065    
066    import java.util.ArrayList;
067    import java.util.Collection;
068    import java.util.Date;
069    import java.util.HashSet;
070    import java.util.LinkedHashMap;
071    import java.util.List;
072    import java.util.Locale;
073    import java.util.Set;
074    
075    import javax.portlet.PortletURL;
076    
077    /**
078     * @author Brian Wing Shun Chan
079     * @author Harry Mark
080     * @author Bruno Farache
081     * @author Raymond Aug??
082     * @author Hugo Huijser
083     * @author Tibor Lipusz
084     */
085    public class JournalArticleIndexer extends BaseIndexer {
086    
087            public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
088    
089            public static final String PORTLET_ID = PortletKeys.JOURNAL;
090    
091            public JournalArticleIndexer() {
092                    setFilterSearch(true);
093                    setPermissionAware(true);
094            }
095    
096            @Override
097            public String[] getClassNames() {
098                    return CLASS_NAMES;
099            }
100    
101            @Override
102            public String getPortletId() {
103                    return PORTLET_ID;
104            }
105    
106            @Override
107            public boolean hasPermission(
108                            PermissionChecker permissionChecker, String entryClassName,
109                            long entryClassPK, String actionId)
110                    throws Exception {
111    
112                    return JournalArticlePermission.contains(
113                            permissionChecker, entryClassPK, ActionKeys.VIEW);
114            }
115    
116            @Override
117            public void postProcessContextQuery(
118                            BooleanQuery contextQuery, SearchContext searchContext)
119                    throws Exception {
120    
121                    Long classNameId = (Long)searchContext.getAttribute(
122                            Field.CLASS_NAME_ID);
123    
124                    if ((classNameId != null) && (classNameId.longValue() != 0)) {
125                            contextQuery.addRequiredTerm("classNameId", classNameId.toString());
126                    }
127    
128                    addStatus(contextQuery, searchContext);
129    
130                    addSearchClassTypeIds(contextQuery, searchContext);
131    
132                    String ddmStructureFieldName = (String)searchContext.getAttribute(
133                            "ddmStructureFieldName");
134                    Serializable ddmStructureFieldValue = searchContext.getAttribute(
135                            "ddmStructureFieldValue");
136    
137                    if (Validator.isNotNull(ddmStructureFieldName) &&
138                            Validator.isNotNull(ddmStructureFieldValue)) {
139    
140                            String[] ddmStructureFieldNameParts = StringUtil.split(
141                                    ddmStructureFieldName, StringPool.SLASH);
142    
143                            DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(
144                                    GetterUtil.getLong(ddmStructureFieldNameParts[1]));
145    
146                            String fieldName = StringUtil.replaceLast(
147                                    ddmStructureFieldNameParts[2],
148                                    StringPool.UNDERLINE.concat(
149                                            LocaleUtil.toLanguageId(searchContext.getLocale())),
150                                    StringPool.BLANK);
151    
152                            try {
153                                    ddmStructureFieldValue = DDMUtil.getIndexedFieldValue(
154                                            ddmStructureFieldValue, structure.getFieldType(fieldName));
155                            }
156                            catch (StructureFieldException sfe) {
157                            }
158    
159                            contextQuery.addRequiredTerm(
160                                    ddmStructureFieldName,
161                                    StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);
162                    }
163    
164                    long[] folderIds = searchContext.getFolderIds();
165    
166                    if ((folderIds != null) && (folderIds.length > 0) &&
167                            (folderIds[0] !=
168                                    JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID)) {
169    
170                            BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create(
171                                    searchContext);
172    
173                            for (long folderId : folderIds) {
174                                    try {
175                                            JournalFolderServiceUtil.getFolder(folderId);
176                                    }
177                                    catch (Exception e) {
178                                            continue;
179                                    }
180    
181                                    folderIdsQuery.addTerm(Field.FOLDER_ID, folderId);
182                            }
183    
184                            contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
185                    }
186    
187                    String articleType = (String)searchContext.getAttribute("articleType");
188    
189                    if (Validator.isNotNull(articleType)) {
190                            contextQuery.addRequiredTerm(Field.TYPE, articleType);
191                    }
192    
193                    String ddmStructureKey = (String)searchContext.getAttribute(
194                            "ddmStructureKey");
195    
196                    if (Validator.isNotNull(ddmStructureKey)) {
197                            contextQuery.addRequiredTerm("ddmStructureKey", ddmStructureKey);
198                    }
199    
200                    String ddmTemplateKey = (String)searchContext.getAttribute(
201                            "ddmTemplateKey");
202    
203                    if (Validator.isNotNull(ddmTemplateKey)) {
204                            contextQuery.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
205                    }
206            }
207    
208            @Override
209            public void postProcessSearchQuery(
210                            BooleanQuery searchQuery, SearchContext searchContext)
211                    throws Exception {
212    
213                    addSearchTerm(searchQuery, searchContext, Field.CLASS_PK, false);
214                    addSearchLocalizedTerm(
215                            searchQuery, searchContext, Field.CONTENT, false);
216                    addSearchLocalizedTerm(
217                            searchQuery, searchContext, Field.DESCRIPTION, false);
218                    addSearchTerm(searchQuery, searchContext, Field.ENTRY_CLASS_PK, false);
219                    addSearchLocalizedTerm(searchQuery, searchContext, Field.TITLE, false);
220                    addSearchTerm(searchQuery, searchContext, Field.TYPE, false);
221                    addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
222    
223                    addSearchTerm(searchQuery, searchContext, "articleId", false);
224    
225                    LinkedHashMap<String, Object> params =
226                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
227    
228                    if (params != null) {
229                            String expandoAttributes = (String)params.get("expandoAttributes");
230    
231                            if (Validator.isNotNull(expandoAttributes)) {
232                                    addSearchExpando(searchQuery, searchContext, expandoAttributes);
233                            }
234                    }
235            }
236    
237            protected void addDDMStructureAttributes(
238                            Document document, JournalArticle article)
239                    throws Exception {
240    
241                    if (Validator.isNull(article.getStructureId())) {
242                            return;
243                    }
244    
245                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.fetchStructure(
246                            article.getGroupId(),
247                            PortalUtil.getClassNameId(JournalArticle.class),
248                            article.getStructureId(), true);
249    
250                    if (ddmStructure == null) {
251                            return;
252                    }
253    
254                    document.addKeyword(Field.CLASS_TYPE_ID, ddmStructure.getStructureId());
255    
256                    Fields fields = null;
257    
258                    try {
259                            fields = JournalConverterUtil.getDDMFields(
260                                    ddmStructure, article.getContent());
261                    }
262                    catch (Exception e) {
263                            return;
264                    }
265    
266                    if (fields != null) {
267                            DDMIndexerUtil.addAttributes(document, ddmStructure, fields);
268                    }
269            }
270    
271            @Override
272            protected void addSearchLocalizedTerm(
273                            BooleanQuery searchQuery, SearchContext searchContext, String field,
274                            boolean like)
275                    throws Exception {
276    
277                    if (Validator.isNull(field)) {
278                            return;
279                    }
280    
281                    String value = String.valueOf(searchContext.getAttribute(field));
282    
283                    if (Validator.isNull(value)) {
284                            value = searchContext.getKeywords();
285                    }
286    
287                    if (Validator.isNull(value)) {
288                            return;
289                    }
290    
291                    field = DocumentImpl.getLocalizedName(searchContext.getLocale(), field);
292    
293                    if (searchContext.isAndSearch()) {
294                            searchQuery.addRequiredTerm(field, value, like);
295                    }
296                    else {
297                            searchQuery.addTerm(field, value, like);
298                    }
299            }
300    
301            @Override
302            protected void doDelete(Object obj) throws Exception {
303                    JournalArticle article = (JournalArticle)obj;
304    
305                    deleteDocument(
306                            article.getCompanyId(), article.getGroupId(),
307                            article.getArticleId());
308            }
309    
310            @Override
311            protected Document doGetDocument(Object obj) throws Exception {
312                    JournalArticle article = (JournalArticle)obj;
313    
314                    Document document = getBaseModelDocument(PORTLET_ID, article);
315    
316                    document.addUID(
317                            PORTLET_ID, article.getGroupId(), article.getArticleId());
318    
319                    String articleDefaultLanguageId = LocalizationUtil.getDefaultLanguageId(
320                            article.getContent());
321    
322                    Locale defaultLocale = LocaleUtil.getSiteDefault();
323    
324                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
325    
326                    String[] languageIds = getLanguageIds(
327                            defaultLanguageId, article.getContent());
328    
329                    for (String languageId : languageIds) {
330                            String content = extractContent(article, languageId);
331    
332                            String description = article.getDescription(languageId);
333    
334                            String title = article.getTitle(languageId);
335    
336                            if (languageId.equals(articleDefaultLanguageId)) {
337                                    document.addText(Field.CONTENT, content);
338                                    document.addText(Field.DESCRIPTION, description);
339                                    document.addText(Field.TITLE, title);
340                                    document.addText("defaultLanguageId", languageId);
341                            }
342    
343                            document.addText(
344                                    Field.CONTENT.concat(StringPool.UNDERLINE).concat(languageId),
345                                    content);
346                            document.addText(
347                                    Field.DESCRIPTION.concat(StringPool.UNDERLINE).concat(
348                                            languageId), description);
349                            document.addText(
350                                    Field.TITLE.concat(StringPool.UNDERLINE).concat(languageId),
351                                    title);
352                    }
353    
354                    document.addKeyword(Field.FOLDER_ID, article.getFolderId());
355                    document.addKeyword(Field.LAYOUT_UUID, article.getLayoutUuid());
356                    document.addKeyword(Field.TYPE, article.getType());
357                    document.addKeyword(Field.VERSION, article.getVersion());
358    
359                    String articleId = article.getArticleId();
360    
361                    if (article.isInTrash()) {
362                            articleId = TrashUtil.getOriginalTitle(articleId);
363                    }
364    
365                    document.addKeyword("articleId", articleId);
366                    document.addKeyword("ddmStructureKey", article.getStructureId());
367                    document.addKeyword("ddmTemplateKey", article.getTemplateId());
368                    document.addDate("displayDate", article.getDisplayDate());
369    
370                    addDDMStructureAttributes(document, article);
371    
372                    if (!article.isInTrash() && article.isInTrashContainer()) {
373                            JournalFolder folder = article.getTrashContainer();
374    
375                            addTrashFields(
376                                    document, JournalFolder.class.getName(), folder.getFolderId(),
377                                    null, null, JournalArticleAssetRendererFactory.TYPE);
378    
379                            document.addKeyword(
380                                    Field.ROOT_ENTRY_CLASS_NAME, JournalFolder.class.getName());
381                            document.addKeyword(
382                                    Field.ROOT_ENTRY_CLASS_PK, folder.getFolderId());
383                            document.addKeyword(
384                                    Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
385                    }
386    
387                    return document;
388            }
389    
390            @Override
391            protected String doGetSortField(String orderByCol) {
392                    if (orderByCol.equals("display-date")) {
393                            return "displayDate";
394                    }
395                    else if (orderByCol.equals("id")) {
396                            return Field.ENTRY_CLASS_PK;
397                    }
398                    else if (orderByCol.equals("modified-date")) {
399                            return Field.MODIFIED_DATE;
400                    }
401                    else if (orderByCol.equals("title")) {
402                            return Field.TITLE;
403                    }
404                    else {
405                            return orderByCol;
406                    }
407            }
408    
409            @Override
410            protected Summary doGetSummary(
411                    Document document, Locale locale, String snippet,
412                    PortletURL portletURL) {
413    
414                    Locale snippetLocale = getSnippetLocale(document, locale);
415    
416                    if (snippetLocale == null) {
417                            snippetLocale = LocaleUtil.fromLanguageId(
418                                    document.get("defaultLanguageId"));
419                    }
420    
421                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
422    
423                    String title = document.get(
424                            snippetLocale, prefix + Field.TITLE, Field.TITLE);
425    
426                    String content = document.get(
427                            snippetLocale, prefix + Field.DESCRIPTION, prefix + Field.CONTENT);
428    
429                    if (Validator.isBlank(content)) {
430                            content = document.get(
431                                    snippetLocale, Field.DESCRIPTION, Field.CONTENT);
432                    }
433    
434                    if (content.length() > 200) {
435                            content = StringUtil.shorten(content, 200);
436                    }
437    
438                    String groupId = document.get(Field.GROUP_ID);
439                    String articleId = document.get("articleId");
440                    String version = document.get(Field.VERSION);
441    
442                    portletURL.setParameter("struts_action", "/journal/edit_article");
443                    portletURL.setParameter("groupId", groupId);
444                    portletURL.setParameter("articleId", articleId);
445                    portletURL.setParameter("version", version);
446    
447                    return new Summary(snippetLocale, title, content, portletURL);
448            }
449    
450            @Override
451            protected void doReindex(Object obj) throws Exception {
452                    JournalArticle article = (JournalArticle)obj;
453    
454                    Document document = getDocument(article);
455    
456                    if (!article.isIndexable() ||
457                            (!article.isApproved() && !article.isInTrash() &&
458                             !article.isExpired() &&
459                             (article.getVersion() !=
460                                      JournalArticleConstants.VERSION_DEFAULT)) ||
461                            (PortalUtil.getClassNameId(DDMStructure.class) ==
462                                    article.getClassNameId())) {
463    
464                            SearchEngineUtil.deleteDocument(
465                                    getSearchEngineId(), article.getCompanyId(),
466                                    document.get(Field.UID));
467    
468                            return;
469                    }
470    
471                    SearchEngineUtil.updateDocument(
472                            getSearchEngineId(), article.getCompanyId(), document);
473            }
474    
475            @Override
476            protected void doReindex(String className, long classPK) throws Exception {
477                    JournalArticle article =
478                            JournalArticleLocalServiceUtil.getLatestArticle(
479                                    classPK, WorkflowConstants.STATUS_APPROVED);
480    
481                    doReindex(article);
482            }
483    
484            @Override
485            protected void doReindex(String[] ids) throws Exception {
486                    long companyId = GetterUtil.getLong(ids[0]);
487    
488                    reindexArticles(companyId);
489            }
490    
491            @Override
492            protected void doReindexDDMStructures(List<Long> ddmStructureIds)
493                    throws Exception {
494    
495                    String[] ddmStructureKeys = new String[ddmStructureIds.size()];
496    
497                    for (int i = 0; i < ddmStructureIds.size(); i++) {
498                            long structureId = ddmStructureIds.get(i);
499    
500                            DDMStructure ddmStructure =
501                                    DDMStructureLocalServiceUtil.getDDMStructure(structureId);
502    
503                            ddmStructureKeys[i] = ddmStructure.getStructureKey();
504                    }
505    
506                    List<JournalArticle> articles =
507                            JournalArticleLocalServiceUtil.getStructureArticles(
508                                    ddmStructureKeys);
509    
510                    for (JournalArticle article : articles) {
511                            doReindex(article);
512                    }
513            }
514    
515            protected String extractBasicContent(
516                    JournalArticle article, String languageId) {
517    
518                    String content = article.getContentByLocale(languageId);
519    
520                    content = StringUtil.replace(content, "<![CDATA[", StringPool.BLANK);
521                    content = StringUtil.replace(content, "]]>", StringPool.BLANK);
522                    content = StringUtil.replace(content, "&amp;", "&");
523                    content = StringUtil.replace(content, "&lt;", "<");
524                    content = StringUtil.replace(content, "&gt;", ">");
525    
526                    content = HtmlUtil.extractText(content);
527    
528                    return content;
529            }
530    
531            protected String extractContent(JournalArticle article, String languageId)
532                    throws Exception {
533    
534                    if (Validator.isNotNull(article.getStructureId())) {
535                            return extractDDMContent(article, languageId);
536                    }
537    
538                    return extractBasicContent(article, languageId);
539            }
540    
541            protected String extractDDMContent(
542                            JournalArticle article, String languageId)
543                    throws Exception {
544    
545                    if (Validator.isNull(article.getStructureId())) {
546                            return StringPool.BLANK;
547                    }
548    
549                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.fetchStructure(
550                            article.getGroupId(),
551                            PortalUtil.getClassNameId(JournalArticle.class),
552                            article.getStructureId(), true);
553    
554                    if (ddmStructure == null) {
555                            return StringPool.BLANK;
556                    }
557    
558                    Fields fields = null;
559    
560                    try {
561                            fields = JournalConverterUtil.getDDMFields(
562                                            ddmStructure, article.getContent());
563                    }
564                    catch (Exception e) {
565                            return StringPool.BLANK;
566                    }
567    
568                    if (fields == null) {
569                            return StringPool.BLANK;
570                    }
571    
572                    return DDMIndexerUtil.extractAttributes(
573                            ddmStructure, fields, LocaleUtil.fromLanguageId(languageId));
574            }
575    
576            protected String[] getLanguageIds(
577                    String defaultLanguageId, String content) {
578    
579                    String[] languageIds = LocalizationUtil.getAvailableLanguageIds(
580                            content);
581    
582                    if (languageIds.length == 0) {
583                            languageIds = new String[] {defaultLanguageId};
584                    }
585    
586                    return languageIds;
587            }
588    
589            @Override
590            protected String getPortletId(SearchContext searchContext) {
591                    return PORTLET_ID;
592            }
593    
594            protected void reindexArticles(long companyId)
595                    throws PortalException, SystemException {
596    
597                    final Collection<Document> documents = new ArrayList<Document>();
598    
599                    final Set<String> latestArticleIds = new HashSet<String>();
600    
601                    ActionableDynamicQuery actionableDynamicQuery =
602                            new JournalArticleActionableDynamicQuery() {
603    
604                            @Override
605                            protected void addCriteria(DynamicQuery dynamicQuery) {
606                                    Junction junction = RestrictionsFactoryUtil.disjunction();
607    
608                                    Junction approvedArticlesJunction =
609                                            RestrictionsFactoryUtil.conjunction();
610    
611                                    Property displayDateProperty = PropertyFactoryUtil.forName(
612                                            "displayDate");
613    
614                                    approvedArticlesJunction.add(
615                                            displayDateProperty.lt(new Date()));
616    
617                                    Property statusProperty = PropertyFactoryUtil.forName("status");
618    
619                                    approvedArticlesJunction.add(
620                                            statusProperty.eq(WorkflowConstants.STATUS_APPROVED));
621    
622                                    junction.add(approvedArticlesJunction);
623    
624                                    Junction draftArticlesJunction =
625                                            RestrictionsFactoryUtil.conjunction();
626    
627                                    Property versionProperty = PropertyFactoryUtil.forName(
628                                            "version");
629    
630                                    draftArticlesJunction.add(
631                                            versionProperty.eq(
632                                                    JournalArticleConstants.VERSION_DEFAULT));
633    
634                                    draftArticlesJunction.add(
635                                            statusProperty.eq(WorkflowConstants.STATUS_DRAFT));
636    
637                                    junction.add(draftArticlesJunction);
638    
639                                    Junction expiredArticlesJunction =
640                                            RestrictionsFactoryUtil.conjunction();
641    
642                                    expiredArticlesJunction.add(
643                                            statusProperty.eq(WorkflowConstants.STATUS_EXPIRED));
644    
645                                    junction.add(expiredArticlesJunction);
646    
647                                    dynamicQuery.add(junction);
648    
649                                    Property indexableProperty = PropertyFactoryUtil.forName(
650                                            "indexable");
651    
652                                    dynamicQuery.add(indexableProperty.eq(true));
653                            }
654    
655                            @Override
656                            protected void performAction(Object object)
657                                    throws PortalException, SystemException {
658    
659                                    JournalArticle article = (JournalArticle)object;
660    
661                                    if (article.isApproved() || article.isExpired()) {
662                                            JournalArticle latestArticle = null;
663    
664                                            if (article.isApproved()) {
665                                                    latestArticle =
666                                                            JournalArticleLocalServiceUtil.getLatestArticle(
667                                                                    article.getResourcePrimKey(),
668                                                                    WorkflowConstants.STATUS_APPROVED);
669                                            }
670                                            else if (article.isExpired()) {
671                                                    latestArticle =
672                                                            JournalArticleLocalServiceUtil.fetchLatestArticle(
673                                                                    article.getResourcePrimKey(),
674                                                                    WorkflowConstants.STATUS_APPROVED, true);
675    
676                                                    if (latestArticle != null) {
677                                                            return;
678                                                    }
679    
680                                                    latestArticle =
681                                                            JournalArticleLocalServiceUtil.getLatestArticle(
682                                                                    article.getResourcePrimKey(),
683                                                                    WorkflowConstants.STATUS_EXPIRED);
684                                            }
685    
686                                            String latestArticleId = latestArticle.getArticleId();
687    
688                                            if (latestArticleIds.contains(latestArticleId)) {
689                                                    return;
690                                            }
691    
692                                            latestArticleIds.add(latestArticleId);
693    
694                                            article = latestArticle;
695                                    }
696    
697                                    Document document = getDocument(article);
698    
699                                    documents.add(document);
700                            }
701    
702                    };
703    
704                    actionableDynamicQuery.setCompanyId(companyId);
705    
706                    actionableDynamicQuery.performActions();
707    
708                    SearchEngineUtil.updateDocuments(
709                            getSearchEngineId(), companyId, documents);
710            }
711    
712    }