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