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