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.journal.util;
016    
017    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.search.BaseIndexer;
023    import com.liferay.portal.kernel.search.BooleanClauseOccur;
024    import com.liferay.portal.kernel.search.BooleanQuery;
025    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
026    import com.liferay.portal.kernel.search.Document;
027    import com.liferay.portal.kernel.search.DocumentImpl;
028    import com.liferay.portal.kernel.search.Field;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.search.SearchEngineUtil;
031    import com.liferay.portal.kernel.search.Summary;
032    import com.liferay.portal.kernel.util.CharPool;
033    import com.liferay.portal.kernel.util.Constants;
034    import com.liferay.portal.kernel.util.GetterUtil;
035    import com.liferay.portal.kernel.util.HtmlUtil;
036    import com.liferay.portal.kernel.util.LocaleUtil;
037    import com.liferay.portal.kernel.util.LocalizationUtil;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.kernel.workflow.WorkflowConstants;
042    import com.liferay.portal.security.permission.ActionKeys;
043    import com.liferay.portal.security.permission.PermissionChecker;
044    import com.liferay.portal.util.PortalUtil;
045    import com.liferay.portal.util.PortletKeys;
046    import com.liferay.portal.util.PropsUtil;
047    import com.liferay.portal.util.PropsValues;
048    import com.liferay.portlet.dynamicdatamapping.StructureFieldException;
049    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
050    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
051    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
052    import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
053    import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
054    import com.liferay.portlet.journal.model.JournalArticle;
055    import com.liferay.portlet.journal.model.JournalArticleDisplay;
056    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
057    import com.liferay.portlet.journal.service.permission.JournalArticlePermission;
058    import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
059    import com.liferay.portlet.trash.util.TrashUtil;
060    
061    import java.io.Serializable;
062    
063    import java.util.ArrayList;
064    import java.util.Collection;
065    import java.util.LinkedHashMap;
066    import java.util.List;
067    import java.util.Locale;
068    
069    import javax.portlet.PortletURL;
070    
071    /**
072     * @author Brian Wing Shun Chan
073     * @author Harry Mark
074     * @author Bruno Farache
075     * @author Raymond Aug??
076     * @author Hugo Huijser
077     * @author Tibor Lipusz
078     */
079    public class JournalArticleIndexer extends BaseIndexer {
080    
081            public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
082    
083            public static boolean JOURNAL_ARTICLE_INDEX_ALL_VERSIONS =
084                    GetterUtil.getBoolean(
085                            PropsUtil.get("journal.articles.index.all.versions"));
086    
087            public static final String PORTLET_ID = PortletKeys.JOURNAL;
088    
089            public JournalArticleIndexer() {
090                    setFilterSearch(true);
091                    setPermissionAware(true);
092            }
093    
094            @Override
095            public String[] getClassNames() {
096                    return CLASS_NAMES;
097            }
098    
099            @Override
100            public String getPortletId() {
101                    return PORTLET_ID;
102            }
103    
104            @Override
105            public boolean hasPermission(
106                            PermissionChecker permissionChecker, String entryClassName,
107                            long entryClassPK, String actionId)
108                    throws Exception {
109    
110                    return JournalArticlePermission.contains(
111                            permissionChecker, entryClassPK, ActionKeys.VIEW);
112            }
113    
114            @Override
115            public boolean isVisible(long classPK, int status) throws Exception {
116                    List<JournalArticle> articles =
117                            JournalArticleLocalServiceUtil.getArticlesByResourcePrimKey(
118                                    classPK);
119    
120                    for (JournalArticle article : articles) {
121                            if (isVisible(article.getStatus(), status)) {
122                                    return true;
123                            }
124                    }
125    
126                    return false;
127            }
128    
129            @Override
130            public void postProcessContextQuery(
131                            BooleanQuery contextQuery, SearchContext searchContext)
132                    throws Exception {
133    
134                    Long classNameId = (Long)searchContext.getAttribute(
135                            Field.CLASS_NAME_ID);
136    
137                    if ((classNameId != null) && (classNameId.longValue() != 0)) {
138                            contextQuery.addRequiredTerm("classNameId", classNameId.toString());
139                    }
140    
141                    addStatus(contextQuery, searchContext);
142    
143                    addSearchClassTypeIds(contextQuery, searchContext);
144    
145                    String ddmStructureFieldName = (String)searchContext.getAttribute(
146                            "ddmStructureFieldName");
147                    Serializable ddmStructureFieldValue = searchContext.getAttribute(
148                            "ddmStructureFieldValue");
149    
150                    if (Validator.isNotNull(ddmStructureFieldName) &&
151                            Validator.isNotNull(ddmStructureFieldValue)) {
152    
153                            String[] ddmStructureFieldNameParts = StringUtil.split(
154                                    ddmStructureFieldName, StringPool.SLASH);
155    
156                            DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(
157                                    GetterUtil.getLong(ddmStructureFieldNameParts[1]));
158    
159                            String fieldName = StringUtil.replaceLast(
160                                    ddmStructureFieldNameParts[2],
161                                    StringPool.UNDERLINE.concat(
162                                            LocaleUtil.toLanguageId(searchContext.getLocale())),
163                                    StringPool.BLANK);
164    
165                            try {
166                                    ddmStructureFieldValue = DDMUtil.getIndexedFieldValue(
167                                            ddmStructureFieldValue, structure.getFieldType(fieldName));
168                            }
169                            catch (StructureFieldException sfe) {
170                            }
171    
172                            contextQuery.addRequiredTerm(
173                                    ddmStructureFieldName,
174                                    StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);
175                    }
176    
177                    String articleType = (String)searchContext.getAttribute("articleType");
178    
179                    if (Validator.isNotNull(articleType)) {
180                            contextQuery.addRequiredTerm(Field.TYPE, articleType);
181                    }
182    
183                    String ddmStructureKey = (String)searchContext.getAttribute(
184                            "ddmStructureKey");
185    
186                    if (Validator.isNotNull(ddmStructureKey)) {
187                            contextQuery.addRequiredTerm("ddmStructureKey", ddmStructureKey);
188                    }
189    
190                    String ddmTemplateKey = (String)searchContext.getAttribute(
191                            "ddmTemplateKey");
192    
193                    if (Validator.isNotNull(ddmTemplateKey)) {
194                            contextQuery.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
195                    }
196            }
197    
198            @Override
199            public void postProcessSearchQuery(
200                            BooleanQuery searchQuery, SearchContext searchContext)
201                    throws Exception {
202    
203                    addSearchTerm(searchQuery, searchContext, Field.CLASS_PK, false);
204                    addSearchLocalizedTerm(
205                            searchQuery, searchContext, Field.CONTENT, false);
206                    addSearchLocalizedTerm(
207                            searchQuery, searchContext, Field.DESCRIPTION, false);
208                    addSearchTerm(searchQuery, searchContext, Field.ENTRY_CLASS_PK, false);
209                    addSearchLocalizedTerm(searchQuery, searchContext, Field.TITLE, false);
210                    addSearchTerm(searchQuery, searchContext, Field.TYPE, false);
211                    addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
212    
213                    addSearchTerm(searchQuery, searchContext, "articleId", false);
214    
215                    LinkedHashMap<String, Object> params =
216                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
217    
218                    if (params != null) {
219                            String expandoAttributes = (String)params.get("expandoAttributes");
220    
221                            if (Validator.isNotNull(expandoAttributes)) {
222                                    addSearchExpando(searchQuery, searchContext, expandoAttributes);
223                            }
224                    }
225            }
226    
227            protected void addDDMStructureAttributes(
228                            Document document, JournalArticle article)
229                    throws Exception {
230    
231                    if (Validator.isNull(article.getStructureId())) {
232                            return;
233                    }
234    
235                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.fetchStructure(
236                            article.getGroupId(),
237                            PortalUtil.getClassNameId(JournalArticle.class),
238                            article.getStructureId(), true);
239    
240                    if (ddmStructure == null) {
241                            return;
242                    }
243    
244                    document.addKeyword(Field.CLASS_TYPE_ID, ddmStructure.getStructureId());
245    
246                    Fields fields = null;
247    
248                    try {
249                            fields = JournalConverterUtil.getDDMFields(
250                                    ddmStructure, article.getContent());
251                    }
252                    catch (Exception e) {
253                            return;
254                    }
255    
256                    if (fields != null) {
257                            DDMIndexerUtil.addAttributes(document, ddmStructure, fields);
258                    }
259            }
260    
261            @Override
262            protected void addSearchLocalizedTerm(
263                            BooleanQuery searchQuery, SearchContext searchContext, String field,
264                            boolean like)
265                    throws Exception {
266    
267                    if (Validator.isNull(field)) {
268                            return;
269                    }
270    
271                    String value = String.valueOf(searchContext.getAttribute(field));
272    
273                    if (Validator.isNull(value)) {
274                            value = searchContext.getKeywords();
275                    }
276    
277                    if (Validator.isNull(value)) {
278                            return;
279                    }
280    
281                    String localizedField = DocumentImpl.getLocalizedName(
282                            searchContext.getLocale(), field);
283    
284                    if (Validator.isNull(searchContext.getKeywords())) {
285                            BooleanQuery localizedQuery = BooleanQueryFactoryUtil.create(
286                                    searchContext);
287    
288                            localizedQuery.addTerm(field, value, like);
289                            localizedQuery.addTerm(localizedField, value, like);
290    
291                            BooleanClauseOccur booleanClauseOccur = BooleanClauseOccur.SHOULD;
292    
293                            if (searchContext.isAndSearch()) {
294                                    booleanClauseOccur = BooleanClauseOccur.MUST;
295                            }
296    
297                            searchQuery.add(localizedQuery, booleanClauseOccur);
298                    }
299                    else {
300                            searchQuery.addTerm(localizedField, value, like);
301                    }
302            }
303    
304            @Override
305            protected void addStatus(
306                            BooleanQuery contextQuery, SearchContext searchContext)
307                    throws Exception {
308    
309                    LinkedHashMap<String, Object> params =
310                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
311    
312                    boolean includeScheduledArticles = false;
313    
314                    if (params != null) {
315                            includeScheduledArticles = GetterUtil.getBoolean(
316                                    params.get("includeScheduledArticles"));
317                    }
318    
319                    if (includeScheduledArticles) {
320                            BooleanQuery statusQuery = BooleanQueryFactoryUtil.create(
321                                    searchContext);
322    
323                            BooleanQuery statusHeadQuery = BooleanQueryFactoryUtil.create(
324                                    searchContext);
325    
326                            statusHeadQuery.addRequiredTerm("head", Boolean.TRUE);
327                            statusHeadQuery.addRequiredTerm(
328                                    Field.STATUS, WorkflowConstants.STATUS_APPROVED);
329    
330                            statusQuery.add(statusHeadQuery, BooleanClauseOccur.SHOULD);
331    
332                            BooleanQuery statusScheduledHeadQuery =
333                                    BooleanQueryFactoryUtil.create(searchContext);
334    
335                            statusScheduledHeadQuery.addRequiredTerm(
336                                    "scheduledHead", Boolean.TRUE);
337                            statusScheduledHeadQuery.addRequiredTerm(
338                                    Field.STATUS, WorkflowConstants.STATUS_SCHEDULED);
339    
340                            statusQuery.add(
341                                    statusScheduledHeadQuery, BooleanClauseOccur.SHOULD);
342    
343                            contextQuery.add(statusQuery, BooleanClauseOccur.MUST);
344                    }
345                    else {
346                            super.addStatus(contextQuery, searchContext);
347    
348                            boolean head = GetterUtil.getBoolean(
349                                    searchContext.getAttribute("head"), Boolean.TRUE);
350                            boolean relatedClassName = GetterUtil.getBoolean(
351                                    searchContext.getAttribute("relatedClassName"));
352    
353                            if (head && !relatedClassName) {
354                                    contextQuery.addRequiredTerm("head", Boolean.TRUE);
355                            }
356                    }
357            }
358    
359            protected void addStatusHeads(Document document, JournalArticle article)
360                    throws SystemException {
361    
362                    boolean head = false;
363                    boolean scheduledHead = false;
364    
365                    int[] statuses = new int[] {
366                            WorkflowConstants.STATUS_APPROVED, WorkflowConstants.STATUS_IN_TRASH
367                    };
368    
369                    JournalArticle latestArticle =
370                            JournalArticleLocalServiceUtil.fetchLatestArticle(
371                                    article.getResourcePrimKey(), statuses);
372    
373                    if (latestArticle == null) {
374                            statuses = new int[] {WorkflowConstants.STATUS_SCHEDULED};
375    
376                            latestArticle = JournalArticleLocalServiceUtil.fetchLatestArticle(
377                                    article.getResourcePrimKey(), statuses);
378                    }
379    
380                    if ((latestArticle != null) && latestArticle.isIndexable() &&
381                            (article.getId() == latestArticle.getId())) {
382    
383                            if (latestArticle.getStatus() ==
384                                            WorkflowConstants.STATUS_SCHEDULED) {
385    
386                                    scheduledHead = true;
387                            }
388                            else {
389                                    head = true;
390                            }
391                    }
392    
393                    document.addKeyword("head", head);
394                    document.addKeyword("scheduledHead", scheduledHead);
395            }
396    
397            @Override
398            protected void doDelete(Object obj) throws Exception {
399                    JournalArticle article = (JournalArticle)obj;
400    
401                    long classPK = article.getId();
402    
403                    if (!PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
404                            if (JournalArticleLocalServiceUtil.getArticlesCount(
405                                            article.getGroupId(), article.getArticleId()) > 0) {
406    
407                                    doReindex(obj);
408    
409                                    return;
410                            }
411                            else {
412                                    classPK = article.getResourcePrimKey();
413                            }
414                    }
415    
416                    deleteDocument(article.getCompanyId(), classPK);
417    
418                    if (!article.isApproved()) {
419                            return;
420                    }
421    
422                    JournalArticle latestIndexableArticle =
423                            JournalArticleLocalServiceUtil.fetchLatestIndexableArticle(
424                                    article.getResourcePrimKey());
425    
426                    if ((latestIndexableArticle == null) ||
427                            (PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS &&
428                            (latestIndexableArticle.getVersion() > article.getVersion()))) {
429    
430                            return;
431                    }
432    
433                    SearchEngineUtil.updateDocument(
434                            getSearchEngineId(), article.getCompanyId(),
435                            getDocument(latestIndexableArticle), isCommitImmediately());
436            }
437    
438            @Override
439            protected Document doGetDocument(Object obj) throws Exception {
440                    JournalArticle article = (JournalArticle)obj;
441    
442                    Document document = getBaseModelDocument(PORTLET_ID, article);
443    
444                    long classPK = article.getId();
445    
446                    if (!PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
447                            classPK = article.getResourcePrimKey();
448                    }
449    
450                    document.addUID(PORTLET_ID, classPK);
451    
452                    String articleDefaultLanguageId = LocalizationUtil.getDefaultLanguageId(
453                            article.getContent());
454    
455                    Locale defaultLocale = LocaleUtil.getSiteDefault();
456    
457                    String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
458    
459                    String[] languageIds = getLanguageIds(
460                            defaultLanguageId, article.getContent());
461    
462                    for (String languageId : languageIds) {
463                            String content = extractContent(article, languageId);
464    
465                            String description = article.getDescription(languageId);
466    
467                            String title = article.getTitle(languageId);
468    
469                            if (languageId.equals(articleDefaultLanguageId)) {
470                                    document.addText(Field.CONTENT, content);
471                                    document.addText(Field.DESCRIPTION, description);
472                                    document.addText(Field.TITLE, title);
473                                    document.addText("defaultLanguageId", languageId);
474                            }
475    
476                            document.addText(
477                                    Field.CONTENT.concat(StringPool.UNDERLINE).concat(languageId),
478                                    content);
479                            document.addText(
480                                    Field.DESCRIPTION.concat(StringPool.UNDERLINE).concat(
481                                            languageId), description);
482                            document.addText(
483                                    Field.TITLE.concat(StringPool.UNDERLINE).concat(languageId),
484                                    title);
485                    }
486    
487                    document.addKeyword(Field.FOLDER_ID, article.getFolderId());
488                    document.addKeyword(Field.LAYOUT_UUID, article.getLayoutUuid());
489                    document.addKeyword(
490                            Field.TREE_PATH,
491                            StringUtil.split(article.getTreePath(), CharPool.SLASH));
492                    document.addKeyword(Field.TYPE, article.getType());
493                    document.addKeyword(Field.VERSION, article.getVersion());
494    
495                    String articleId = article.getArticleId();
496    
497                    if (article.isInTrash()) {
498                            articleId = TrashUtil.getOriginalTitle(articleId);
499                    }
500    
501                    document.addKeyword("articleId", articleId);
502                    document.addKeyword("ddmStructureKey", article.getStructureId());
503                    document.addKeyword("ddmTemplateKey", article.getTemplateId());
504                    document.addDate("displayDate", article.getDisplayDate());
505    
506                    addDDMStructureAttributes(document, article);
507    
508                    addStatusHeads(document, article);
509    
510                    return document;
511            }
512    
513            @Override
514            protected String doGetSortField(String orderByCol) {
515                    if (orderByCol.equals("display-date")) {
516                            return "displayDate";
517                    }
518                    else if (orderByCol.equals("id")) {
519                            return Field.ENTRY_CLASS_PK;
520                    }
521                    else if (orderByCol.equals("modified-date")) {
522                            return Field.MODIFIED_DATE;
523                    }
524                    else if (orderByCol.equals("title")) {
525                            return Field.TITLE;
526                    }
527                    else {
528                            return orderByCol;
529                    }
530            }
531    
532            @Override
533            protected Summary doGetSummary(
534                    Document document, Locale locale, String snippet,
535                    PortletURL portletURL) {
536    
537                    Locale snippetLocale = getSnippetLocale(document, locale);
538    
539                    String localizedTitleName = DocumentImpl.getLocalizedName(
540                            locale, Field.TITLE);
541    
542                    if ((snippetLocale == null) ||
543                            (document.getField(localizedTitleName) == null)) {
544    
545                            snippetLocale = LocaleUtil.fromLanguageId(
546                                    document.get("defaultLanguageId"));
547                    }
548    
549                    String title = document.get(
550                            snippetLocale, Field.SNIPPET + StringPool.UNDERLINE + Field.TITLE,
551                            Field.TITLE);
552    
553                    String content = StringPool.BLANK;
554    
555                    String ddmStructureKey = document.get("ddmStructureKey");
556    
557                    if (Validator.isNotNull(ddmStructureKey)) {
558                            content = getDDMContentSummary(document, snippetLocale);
559                    }
560                    else {
561                            content = getBasicContentSummary(document, snippetLocale);
562                    }
563    
564                    String groupId = document.get(Field.GROUP_ID);
565                    String articleId = document.get("articleId");
566                    String version = document.get(Field.VERSION);
567    
568                    portletURL.setParameter("struts_action", "/journal/edit_article");
569                    portletURL.setParameter("groupId", groupId);
570                    portletURL.setParameter("articleId", articleId);
571                    portletURL.setParameter("version", version);
572    
573                    return new Summary(snippetLocale, title, content, portletURL);
574            }
575    
576            public void doReindex(JournalArticle article, boolean allVersions)
577                    throws Exception {
578    
579                    if (PortalUtil.getClassNameId(DDMStructure.class) ==
580                                    article.getClassNameId()) {
581    
582                            Document document = getDocument(article);
583    
584                            SearchEngineUtil.deleteDocument(
585                                    getSearchEngineId(), article.getCompanyId(),
586                                    document.get(Field.UID), isCommitImmediately());
587    
588                            return;
589                    }
590    
591                    if (allVersions || !PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
592                            reindexArticleVersions(article);
593                    }
594                    else {
595                            SearchEngineUtil.updateDocument(
596                                    getSearchEngineId(), article.getCompanyId(),
597                                    getDocument(article), isCommitImmediately());
598                    }
599            }
600    
601            @Override
602            protected void doReindex(Object obj) throws Exception {
603                    JournalArticle article = (JournalArticle)obj;
604    
605                    doReindex(article, true);
606            }
607    
608            @Override
609            protected void doReindex(String className, long classPK) throws Exception {
610                    JournalArticle article =
611                            JournalArticleLocalServiceUtil.fetchJournalArticle(classPK);
612    
613                    if (article == null) {
614                            article = JournalArticleLocalServiceUtil.fetchLatestArticle(
615                                    classPK);
616                    }
617    
618                    if (article != null) {
619                            doReindex(article);
620                    }
621            }
622    
623            @Override
624            protected void doReindex(String[] ids) throws Exception {
625                    long companyId = GetterUtil.getLong(ids[0]);
626    
627                    reindexArticles(companyId);
628            }
629    
630            @Override
631            protected void doReindexDDMStructures(List<Long> ddmStructureIds)
632                    throws Exception {
633    
634                    String[] ddmStructureKeys = new String[ddmStructureIds.size()];
635    
636                    for (int i = 0; i < ddmStructureIds.size(); i++) {
637                            long structureId = ddmStructureIds.get(i);
638    
639                            DDMStructure ddmStructure =
640                                    DDMStructureLocalServiceUtil.getDDMStructure(structureId);
641    
642                            ddmStructureKeys[i] = ddmStructure.getStructureKey();
643                    }
644    
645                    List<JournalArticle> articles =
646                            JournalArticleLocalServiceUtil.
647                                    getIndexableArticlesByDDMStructureKey(ddmStructureKeys);
648    
649                    for (JournalArticle article : articles) {
650                            doReindex(article, false);
651                    }
652            }
653    
654            protected String extractBasicContent(
655                    JournalArticle article, String languageId) {
656    
657                    String content = article.getContentByLocale(languageId);
658    
659                    content = StringUtil.replace(content, "<![CDATA[", StringPool.BLANK);
660                    content = StringUtil.replace(content, "]]>", StringPool.BLANK);
661                    content = StringUtil.replace(content, "&amp;", "&");
662                    content = StringUtil.replace(content, "&lt;", "<");
663                    content = StringUtil.replace(content, "&gt;", ">");
664    
665                    content = HtmlUtil.extractText(content);
666    
667                    return content;
668            }
669    
670            protected String extractContent(JournalArticle article, String languageId)
671                    throws Exception {
672    
673                    if (Validator.isNotNull(article.getStructureId())) {
674                            return extractDDMContent(article, languageId);
675                    }
676    
677                    return extractBasicContent(article, languageId);
678            }
679    
680            protected String extractDDMContent(
681                            JournalArticle article, String languageId)
682                    throws Exception {
683    
684                    if (Validator.isNull(article.getStructureId())) {
685                            return StringPool.BLANK;
686                    }
687    
688                    DDMStructure ddmStructure = DDMStructureLocalServiceUtil.fetchStructure(
689                            article.getGroupId(),
690                            PortalUtil.getClassNameId(JournalArticle.class),
691                            article.getStructureId(), true);
692    
693                    if (ddmStructure == null) {
694                            return StringPool.BLANK;
695                    }
696    
697                    Fields fields = null;
698    
699                    try {
700                            fields = JournalConverterUtil.getDDMFields(
701                                    ddmStructure, article.getContent());
702                    }
703                    catch (Exception e) {
704                            return StringPool.BLANK;
705                    }
706    
707                    if (fields == null) {
708                            return StringPool.BLANK;
709                    }
710    
711                    return DDMIndexerUtil.extractAttributes(
712                            ddmStructure, fields, LocaleUtil.fromLanguageId(languageId));
713            }
714    
715            protected JournalArticle fetchLatestIndexableArticleVersion(
716                            long resourcePrimKey)
717                    throws SystemException {
718    
719                    JournalArticle latestIndexableArticle =
720                            JournalArticleLocalServiceUtil.fetchLatestArticle(
721                                    resourcePrimKey,
722                                    new int[] {
723                                            WorkflowConstants.STATUS_APPROVED,
724                                            WorkflowConstants.STATUS_IN_TRASH
725                                    });
726    
727                    if (latestIndexableArticle == null) {
728                            latestIndexableArticle =
729                                    JournalArticleLocalServiceUtil.fetchLatestArticle(
730                                            resourcePrimKey);
731                    }
732    
733                    return latestIndexableArticle;
734            }
735    
736            protected Collection<Document> getArticleVersions(JournalArticle article)
737                    throws PortalException, SystemException {
738    
739                    Collection<Document> documents = new ArrayList<Document>();
740    
741                    List<JournalArticle> articles = null;
742    
743                    if (PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
744                            articles =
745                                    JournalArticleLocalServiceUtil.
746                                            getIndexableArticlesByResourcePrimKey(
747                                                    article.getResourcePrimKey());
748                    }
749                    else {
750                            articles = new ArrayList<JournalArticle>();
751    
752                            JournalArticle latestIndexableArticle =
753                                    fetchLatestIndexableArticleVersion(
754                                            article.getResourcePrimKey());
755    
756                            if (latestIndexableArticle != null) {
757                                    articles.add(latestIndexableArticle);
758                            }
759                    }
760    
761                    for (JournalArticle curArticle : articles) {
762                            Document document = getDocument(curArticle);
763    
764                            documents.add(document);
765                    }
766    
767                    return documents;
768            }
769    
770            protected String getBasicContentSummary(
771                    Document document, Locale snippetLocale) {
772    
773                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
774    
775                    String content = document.get(
776                            snippetLocale, prefix + Field.DESCRIPTION, prefix + Field.CONTENT);
777    
778                    if (Validator.isBlank(content)) {
779                            content = document.get(
780                                    snippetLocale, Field.DESCRIPTION, Field.CONTENT);
781                    }
782    
783                    if (content.length() > 200) {
784                            content = StringUtil.shorten(content, 200);
785                    }
786    
787                    return content;
788            }
789    
790            protected String getDDMContentSummary(
791                    Document document, Locale snippetLocale) {
792    
793                    String content = StringPool.BLANK;
794    
795                    try {
796                            long groupId = GetterUtil.getLong(document.get(Field.GROUP_ID));
797                            String articleId = document.get("articleId");
798                            double version = GetterUtil.getDouble(document.get(Field.VERSION));
799    
800                            JournalArticle article =
801                                    JournalArticleLocalServiceUtil.fetchArticle(
802                                            groupId, articleId, version);
803    
804                            if (article == null) {
805                                    return content;
806                            }
807    
808                            JournalArticleDisplay articleDisplay =
809                                    JournalArticleLocalServiceUtil.getArticleDisplay(
810                                            article, null, Constants.VIEW,
811                                            LocaleUtil.toLanguageId(snippetLocale), 1, null, null);
812    
813                            content = HtmlUtil.escape(articleDisplay.getDescription());
814                            content = HtmlUtil.replaceNewLine(content);
815    
816                            if (Validator.isNull(content)) {
817                                    content = HtmlUtil.extractText(articleDisplay.getContent());
818                            }
819                    }
820                    catch (Exception e) {
821                            if (_log.isDebugEnabled()) {
822                                    _log.debug(e, e);
823                            }
824                    }
825    
826                    return content;
827            }
828    
829            protected String[] getLanguageIds(
830                    String defaultLanguageId, String content) {
831    
832                    String[] languageIds = LocalizationUtil.getAvailableLanguageIds(
833                            content);
834    
835                    if (languageIds.length == 0) {
836                            languageIds = new String[] {defaultLanguageId};
837                    }
838    
839                    return languageIds;
840            }
841    
842            @Override
843            protected String getPortletId(SearchContext searchContext) {
844                    return PORTLET_ID;
845            }
846    
847            protected void reindexArticles(long companyId)
848                    throws PortalException, SystemException {
849    
850                    ActionableDynamicQuery actionableDynamicQuery =
851                            new JournalArticleActionableDynamicQuery() {
852    
853                            @Override
854                            protected void performAction(Object object)
855                                    throws PortalException, SystemException {
856    
857                                    JournalArticle article = (JournalArticle)object;
858    
859                                    if (!PropsValues.JOURNAL_ARTICLE_INDEX_ALL_VERSIONS) {
860                                            JournalArticle latestIndexableArticle =
861                                                    fetchLatestIndexableArticleVersion(
862                                                            article.getResourcePrimKey());
863    
864                                            if (latestIndexableArticle == null) {
865                                                    return;
866                                            }
867    
868                                            article = latestIndexableArticle;
869                                    }
870    
871                                    Document document = getDocument(article);
872    
873                                    addDocument(document);
874                            }
875    
876                    };
877    
878                    actionableDynamicQuery.setCompanyId(companyId);
879                    actionableDynamicQuery.setSearchEngineId(getSearchEngineId());
880    
881                    actionableDynamicQuery.performActions();
882            }
883    
884            protected void reindexArticleVersions(JournalArticle article)
885                    throws PortalException, SystemException {
886    
887                    SearchEngineUtil.updateDocuments(
888                            getSearchEngineId(), article.getCompanyId(),
889                            getArticleVersions(article), isCommitImmediately());
890            }
891    
892            private static Log _log = LogFactoryUtil.getLog(
893                    JournalArticleIndexer.class);
894    
895    }