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