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