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.search.BaseIndexer;
024 import com.liferay.portal.kernel.search.BooleanClauseOccur;
025 import com.liferay.portal.kernel.search.BooleanQuery;
026 import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
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.GetterUtil;
034 import com.liferay.portal.kernel.util.HtmlUtil;
035 import com.liferay.portal.kernel.util.LocaleUtil;
036 import com.liferay.portal.kernel.util.LocalizationUtil;
037 import com.liferay.portal.kernel.util.StringPool;
038 import com.liferay.portal.kernel.util.StringUtil;
039 import com.liferay.portal.kernel.util.Validator;
040 import com.liferay.portal.kernel.workflow.WorkflowConstants;
041 import com.liferay.portal.security.permission.ActionKeys;
042 import com.liferay.portal.security.permission.PermissionChecker;
043 import com.liferay.portal.util.PortalUtil;
044 import com.liferay.portal.util.PortletKeys;
045 import com.liferay.portlet.dynamicdatamapping.StructureFieldException;
046 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
047 import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
048 import com.liferay.portlet.dynamicdatamapping.storage.Fields;
049 import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
050 import com.liferay.portlet.dynamicdatamapping.util.DDMUtil;
051 import com.liferay.portlet.journal.asset.JournalArticleAssetRendererFactory;
052 import com.liferay.portlet.journal.model.JournalArticle;
053 import com.liferay.portlet.journal.model.JournalFolder;
054 import com.liferay.portlet.journal.model.JournalFolderConstants;
055 import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
056 import com.liferay.portlet.journal.service.JournalFolderServiceUtil;
057 import com.liferay.portlet.journal.service.permission.JournalArticlePermission;
058 import com.liferay.portlet.journal.service.persistence.JournalArticleActionableDynamicQuery;
059 import com.liferay.portlet.trash.util.TrashUtil;
060
061 import java.io.Serializable;
062
063 import java.util.ArrayList;
064 import java.util.Collection;
065 import java.util.LinkedHashMap;
066 import java.util.List;
067 import java.util.Locale;
068
069 import javax.portlet.PortletURL;
070
071
079 public class JournalArticleIndexer extends BaseIndexer {
080
081 public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
082
083 public static final String PORTLET_ID = PortletKeys.JOURNAL;
084
085 public JournalArticleIndexer() {
086 setFilterSearch(true);
087 setPermissionAware(true);
088 }
089
090 @Override
091 public String[] getClassNames() {
092 return CLASS_NAMES;
093 }
094
095 @Override
096 public String getPortletId() {
097 return PORTLET_ID;
098 }
099
100 @Override
101 public boolean hasPermission(
102 PermissionChecker permissionChecker, String entryClassName,
103 long entryClassPK, String actionId)
104 throws Exception {
105
106 return JournalArticlePermission.contains(
107 permissionChecker, entryClassPK, ActionKeys.VIEW);
108 }
109
110 @Override
111 public void postProcessContextQuery(
112 BooleanQuery contextQuery, SearchContext searchContext)
113 throws Exception {
114
115 Long classNameId = (Long)searchContext.getAttribute(
116 Field.CLASS_NAME_ID);
117
118 if ((classNameId != null) && (classNameId.longValue() != 0)) {
119 contextQuery.addRequiredTerm("classNameId", classNameId.toString());
120 }
121
122 addStatus(contextQuery, searchContext);
123
124 addSearchClassTypeIds(contextQuery, searchContext);
125
126 String ddmStructureFieldName = (String)searchContext.getAttribute(
127 "ddmStructureFieldName");
128 Serializable ddmStructureFieldValue = searchContext.getAttribute(
129 "ddmStructureFieldValue");
130
131 if (Validator.isNotNull(ddmStructureFieldName) &&
132 Validator.isNotNull(ddmStructureFieldValue)) {
133
134 String[] ddmStructureFieldNameParts = StringUtil.split(
135 ddmStructureFieldName, StringPool.SLASH);
136
137 DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(
138 GetterUtil.getLong(ddmStructureFieldNameParts[1]));
139
140 String fieldName = StringUtil.replaceLast(
141 ddmStructureFieldNameParts[2],
142 StringPool.UNDERLINE.concat(
143 LocaleUtil.toLanguageId(searchContext.getLocale())),
144 StringPool.BLANK);
145
146 try {
147 ddmStructureFieldValue = DDMUtil.getIndexedFieldValue(
148 ddmStructureFieldValue, structure.getFieldType(fieldName));
149 }
150 catch (StructureFieldException sfe) {
151 }
152
153 contextQuery.addRequiredTerm(
154 ddmStructureFieldName,
155 StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);
156 }
157
158 long[] folderIds = searchContext.getFolderIds();
159
160 if ((folderIds != null) && (folderIds.length > 0) &&
161 (folderIds[0] !=
162 JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID)) {
163
164 BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create(
165 searchContext);
166
167 for (long folderId : folderIds) {
168 try {
169 JournalFolderServiceUtil.getFolder(folderId);
170 }
171 catch (Exception e) {
172 continue;
173 }
174
175 folderIdsQuery.addTerm(Field.FOLDER_ID, folderId);
176 }
177
178 contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
179 }
180
181 String articleType = (String)searchContext.getAttribute("articleType");
182
183 if (Validator.isNotNull(articleType)) {
184 contextQuery.addRequiredTerm(Field.TYPE, articleType);
185 }
186
187 String ddmStructureKey = (String)searchContext.getAttribute(
188 "ddmStructureKey");
189
190 if (Validator.isNotNull(ddmStructureKey)) {
191 contextQuery.addRequiredTerm("ddmStructureKey", ddmStructureKey);
192 }
193
194 String ddmTemplateKey = (String)searchContext.getAttribute(
195 "ddmTemplateKey");
196
197 if (Validator.isNotNull(ddmTemplateKey)) {
198 contextQuery.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
199 }
200
201 boolean head = GetterUtil.getBoolean(
202 searchContext.getAttribute("head"), Boolean.TRUE);
203 boolean relatedClassName = GetterUtil.getBoolean(
204 searchContext.getAttribute("relatedClassName"));
205
206 if (head && !relatedClassName) {
207 contextQuery.addRequiredTerm("head", Boolean.TRUE);
208 }
209 }
210
211 @Override
212 public void postProcessSearchQuery(
213 BooleanQuery searchQuery, SearchContext searchContext)
214 throws Exception {
215
216 addSearchTerm(searchQuery, searchContext, Field.CLASS_PK, false);
217 addSearchLocalizedTerm(
218 searchQuery, searchContext, Field.CONTENT, false);
219 addSearchLocalizedTerm(
220 searchQuery, searchContext, Field.DESCRIPTION, false);
221 addSearchTerm(searchQuery, searchContext, Field.ENTRY_CLASS_PK, false);
222 addSearchLocalizedTerm(searchQuery, searchContext, Field.TITLE, false);
223 addSearchTerm(searchQuery, searchContext, Field.TYPE, false);
224 addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
225
226 addSearchTerm(searchQuery, searchContext, "articleId", false);
227
228 LinkedHashMap<String, Object> params =
229 (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
230
231 if (params != null) {
232 String expandoAttributes = (String)params.get("expandoAttributes");
233
234 if (Validator.isNotNull(expandoAttributes)) {
235 addSearchExpando(searchQuery, searchContext, expandoAttributes);
236 }
237 }
238 }
239
240 protected void addDDMStructureAttributes(
241 Document document, JournalArticle article)
242 throws Exception {
243
244 if (Validator.isNull(article.getStructureId())) {
245 return;
246 }
247
248 DDMStructure ddmStructure = DDMStructureLocalServiceUtil.fetchStructure(
249 article.getGroupId(),
250 PortalUtil.getClassNameId(JournalArticle.class),
251 article.getStructureId(), true);
252
253 if (ddmStructure == null) {
254 return;
255 }
256
257 document.addKeyword(Field.CLASS_TYPE_ID, ddmStructure.getStructureId());
258
259 Fields fields = null;
260
261 try {
262 fields = JournalConverterUtil.getDDMFields(
263 ddmStructure, article.getContent());
264 }
265 catch (Exception e) {
266 return;
267 }
268
269 if (fields != null) {
270 DDMIndexerUtil.addAttributes(document, ddmStructure, fields);
271 }
272 }
273
274 @Override
275 protected void addSearchLocalizedTerm(
276 BooleanQuery searchQuery, SearchContext searchContext, String field,
277 boolean like)
278 throws Exception {
279
280 if (Validator.isNull(field)) {
281 return;
282 }
283
284 String value = String.valueOf(searchContext.getAttribute(field));
285
286 if (Validator.isNull(value)) {
287 value = searchContext.getKeywords();
288 }
289
290 if (Validator.isNull(value)) {
291 return;
292 }
293
294 field = DocumentImpl.getLocalizedName(searchContext.getLocale(), field);
295
296 if (searchContext.isAndSearch()) {
297 searchQuery.addRequiredTerm(field, value, like);
298 }
299 else {
300 searchQuery.addTerm(field, value, like);
301 }
302 }
303
304 @Override
305 protected void doDelete(Object obj) throws Exception {
306 JournalArticle article = (JournalArticle)obj;
307
308 deleteDocument(article.getCompanyId(), article.getId());
309
310 updateArticles(article);
311 }
312
313 @Override
314 protected Document doGetDocument(Object obj) throws Exception {
315 JournalArticle article = (JournalArticle)obj;
316
317 Document document = getBaseModelDocument(PORTLET_ID, article);
318
319 document.addUID(PORTLET_ID, article.getId());
320
321 String articleDefaultLanguageId = LocalizationUtil.getDefaultLanguageId(
322 article.getContent());
323
324 Locale defaultLocale = LocaleUtil.getSiteDefault();
325
326 String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
327
328 String[] languageIds = getLanguageIds(
329 defaultLanguageId, article.getContent());
330
331 for (String languageId : languageIds) {
332 String content = extractContent(article, languageId);
333
334 String description = article.getDescription(languageId);
335
336 String title = article.getTitle(languageId);
337
338 if (languageId.equals(articleDefaultLanguageId)) {
339 document.addText(Field.CONTENT, content);
340 document.addText(Field.DESCRIPTION, description);
341 document.addText(Field.TITLE, title);
342 document.addText("defaultLanguageId", languageId);
343 }
344
345 document.addText(
346 Field.CONTENT.concat(StringPool.UNDERLINE).concat(languageId),
347 content);
348 document.addText(
349 Field.DESCRIPTION.concat(StringPool.UNDERLINE).concat(
350 languageId), description);
351 document.addText(
352 Field.TITLE.concat(StringPool.UNDERLINE).concat(languageId),
353 title);
354 }
355
356 document.addKeyword(Field.FOLDER_ID, article.getFolderId());
357 document.addKeyword(Field.LAYOUT_UUID, article.getLayoutUuid());
358 document.addKeyword(Field.TYPE, article.getType());
359 document.addKeyword(Field.VERSION, article.getVersion());
360
361 String articleId = article.getArticleId();
362
363 if (article.isInTrash()) {
364 articleId = TrashUtil.getOriginalTitle(articleId);
365 }
366
367 document.addKeyword("articleId", articleId);
368 document.addKeyword("ddmStructureKey", article.getStructureId());
369 document.addKeyword("ddmTemplateKey", article.getTemplateId());
370 document.addDate("displayDate", article.getDisplayDate());
371 document.addKeyword("head", false);
372
373 addDDMStructureAttributes(document, article);
374
375 if (!article.isInTrash() && article.isInTrashContainer()) {
376 JournalFolder folder = article.getTrashContainer();
377
378 addTrashFields(
379 document, JournalFolder.class.getName(), folder.getFolderId(),
380 null, null, JournalArticleAssetRendererFactory.TYPE);
381
382 document.addKeyword(
383 Field.ROOT_ENTRY_CLASS_NAME, JournalFolder.class.getName());
384 document.addKeyword(
385 Field.ROOT_ENTRY_CLASS_PK, folder.getFolderId());
386 document.addKeyword(
387 Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
388 }
389
390 return document;
391 }
392
393 @Override
394 protected String doGetSortField(String orderByCol) {
395 if (orderByCol.equals("display-date")) {
396 return "displayDate";
397 }
398 else if (orderByCol.equals("id")) {
399 return Field.ENTRY_CLASS_PK;
400 }
401 else if (orderByCol.equals("modified-date")) {
402 return Field.MODIFIED_DATE;
403 }
404 else if (orderByCol.equals("title")) {
405 return Field.TITLE;
406 }
407 else {
408 return orderByCol;
409 }
410 }
411
412 @Override
413 protected Summary doGetSummary(
414 Document document, Locale locale, String snippet,
415 PortletURL portletURL) {
416
417 Locale snippetLocale = getSnippetLocale(document, locale);
418
419 if (snippetLocale == null) {
420 snippetLocale = LocaleUtil.fromLanguageId(
421 document.get("defaultLanguageId"));
422 }
423
424 String prefix = Field.SNIPPET + StringPool.UNDERLINE;
425
426 String title = document.get(
427 snippetLocale, prefix + Field.TITLE, Field.TITLE);
428
429 String content = document.get(
430 snippetLocale, prefix + Field.DESCRIPTION, prefix + Field.CONTENT);
431
432 if (Validator.isBlank(content)) {
433 content = document.get(
434 snippetLocale, Field.DESCRIPTION, Field.CONTENT);
435 }
436
437 if (content.length() > 200) {
438 content = StringUtil.shorten(content, 200);
439 }
440
441 String groupId = document.get(Field.GROUP_ID);
442 String articleId = document.get("articleId");
443 String version = document.get(Field.VERSION);
444
445 portletURL.setParameter("struts_action", "/journal/edit_article");
446 portletURL.setParameter("groupId", groupId);
447 portletURL.setParameter("articleId", articleId);
448 portletURL.setParameter("version", version);
449
450 return new Summary(snippetLocale, title, content, portletURL);
451 }
452
453 @Override
454 protected void doReindex(Object obj) throws Exception {
455 JournalArticle article = (JournalArticle)obj;
456
457 Document document = getDocument(article);
458
459 if (!article.isIndexable() ||
460 (PortalUtil.getClassNameId(DDMStructure.class) ==
461 article.getClassNameId())) {
462
463 SearchEngineUtil.deleteDocument(
464 getSearchEngineId(), article.getCompanyId(),
465 document.get(Field.UID));
466
467 return;
468 }
469
470 SearchEngineUtil.updateDocument(
471 getSearchEngineId(), article.getCompanyId(), document);
472
473 updateArticles(article);
474 }
475
476 @Override
477 protected void doReindex(String className, long classPK) throws Exception {
478 JournalArticle article = JournalArticleLocalServiceUtil.getArticle(
479 classPK);
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 ActionableDynamicQuery actionableDynamicQuery =
600 new JournalArticleActionableDynamicQuery() {
601
602 @Override
603 protected void addCriteria(DynamicQuery dynamicQuery) {
604 Property indexableProperty = PropertyFactoryUtil.forName(
605 "indexable");
606
607 dynamicQuery.add(indexableProperty.eq(true));
608 }
609
610 @Override
611 protected void performAction(Object object) throws PortalException {
612 JournalArticle article = (JournalArticle)object;
613
614 Document document = getDocument(article);
615
616 documents.add(document);
617 }
618
619 };
620
621 actionableDynamicQuery.setCompanyId(companyId);
622
623 actionableDynamicQuery.performActions();
624
625 SearchEngineUtil.updateDocuments(
626 getSearchEngineId(), companyId, documents);
627 }
628
629 protected void updateArticles(JournalArticle article) throws Exception {
630 JournalArticle latestIndexableArticle =
631 JournalArticleLocalServiceUtil.fetchLatestIndexableArticle(
632 article.getResourcePrimKey());
633
634 List<JournalArticle> articles =
635 JournalArticleLocalServiceUtil.getArticlesByResourcePrimKey(
636 article.getResourcePrimKey());
637
638 for (JournalArticle curArticle : articles) {
639 if (!curArticle.isIndexable() ||
640 ((latestIndexableArticle != null) &&
641 (curArticle.getArticleId() ==
642 latestIndexableArticle.getArticleId()))) {
643
644 continue;
645 }
646
647 Document document = getDocument(curArticle);
648
649 document.addKeyword("head", false);
650
651 SearchEngineUtil.updateDocument(
652 getSearchEngineId(), curArticle.getCompanyId(), document);
653 }
654
655 if (latestIndexableArticle != null) {
656 Document document = getDocument(latestIndexableArticle);
657
658 document.addKeyword("head", true);
659
660 SearchEngineUtil.updateDocument(
661 getSearchEngineId(), latestIndexableArticle.getCompanyId(),
662 document);
663 }
664 else if (article.getStatus() == WorkflowConstants.STATUS_IN_TRASH) {
665 Document document = getDocument(article);
666
667 document.addKeyword("head", true);
668
669 SearchEngineUtil.updateDocument(
670 getSearchEngineId(), article.getCompanyId(), document);
671 }
672 }
673
674 }