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