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