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