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