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.log.Log;
026 import com.liferay.portal.kernel.log.LogFactoryUtil;
027 import com.liferay.portal.kernel.search.BaseIndexer;
028 import com.liferay.portal.kernel.search.BooleanClauseOccur;
029 import com.liferay.portal.kernel.search.BooleanQuery;
030 import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
031 import com.liferay.portal.kernel.search.Document;
032 import com.liferay.portal.kernel.search.DocumentImpl;
033 import com.liferay.portal.kernel.search.Field;
034 import com.liferay.portal.kernel.search.SearchContext;
035 import com.liferay.portal.kernel.search.SearchEngineUtil;
036 import com.liferay.portal.kernel.search.Summary;
037 import com.liferay.portal.kernel.util.GetterUtil;
038 import com.liferay.portal.kernel.util.HtmlUtil;
039 import com.liferay.portal.kernel.util.LocaleUtil;
040 import com.liferay.portal.kernel.util.LocalizationUtil;
041 import com.liferay.portal.kernel.util.StringBundler;
042 import com.liferay.portal.kernel.util.StringPool;
043 import com.liferay.portal.kernel.util.StringUtil;
044 import com.liferay.portal.kernel.util.Validator;
045 import com.liferay.portal.kernel.workflow.WorkflowConstants;
046 import com.liferay.portal.kernel.xml.DocumentException;
047 import com.liferay.portal.kernel.xml.Element;
048 import com.liferay.portal.kernel.xml.Node;
049 import com.liferay.portal.kernel.xml.SAXReaderUtil;
050 import com.liferay.portal.util.PortalUtil;
051 import com.liferay.portal.util.PortletKeys;
052 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
053 import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
054 import com.liferay.portlet.journal.NoSuchStructureException;
055 import com.liferay.portlet.journal.model.JournalArticle;
056 import com.liferay.portlet.journal.model.JournalArticleConstants;
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.persistence.JournalArticleActionableDynamicQuery;
061 import com.liferay.portlet.trash.util.TrashUtil;
062
063 import java.util.ArrayList;
064 import java.util.Collection;
065 import java.util.LinkedHashMap;
066 import java.util.LinkedList;
067 import java.util.List;
068 import java.util.Locale;
069
070 import javax.portlet.PortletURL;
071
072
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 setPermissionAware(true);
087 }
088
089 public String[] getClassNames() {
090 return CLASS_NAMES;
091 }
092
093 public String getPortletId() {
094 return PORTLET_ID;
095 }
096
097 @Override
098 public void postProcessContextQuery(
099 BooleanQuery contextQuery, SearchContext searchContext)
100 throws Exception {
101
102 Long classNameId = (Long)searchContext.getAttribute(
103 Field.CLASS_NAME_ID);
104
105 if ((classNameId != null) && (classNameId.longValue() != 0)) {
106 contextQuery.addRequiredTerm("classNameId", classNameId.toString());
107 }
108
109 int status = GetterUtil.getInteger(
110 searchContext.getAttribute(Field.STATUS),
111 WorkflowConstants.STATUS_APPROVED);
112
113 if (status != WorkflowConstants.STATUS_ANY) {
114 contextQuery.addRequiredTerm(Field.STATUS, status);
115 }
116
117 long[] folderIds = searchContext.getFolderIds();
118
119 if ((folderIds != null) && (folderIds.length > 0)) {
120 if (folderIds[0] ==
121 JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
122
123 return;
124 }
125
126 BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create(
127 searchContext);
128
129 for (long folderId : folderIds) {
130 try {
131 JournalFolderServiceUtil.getFolder(folderId);
132 }
133 catch (Exception e) {
134 continue;
135 }
136
137 folderIdsQuery.addTerm(Field.FOLDER_ID, folderId);
138 }
139
140 contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
141 }
142
143 String articleType = (String)searchContext.getAttribute("articleType");
144
145 if (Validator.isNotNull(articleType)) {
146 contextQuery.addRequiredTerm(Field.TYPE, articleType);
147 }
148
149 String ddmStructureKey = (String)searchContext.getAttribute(
150 "ddmStructureKey");
151
152 if (Validator.isNotNull(ddmStructureKey)) {
153 contextQuery.addRequiredTerm("ddmStructureKey", ddmStructureKey);
154 }
155
156 String ddmTemplateKey = (String)searchContext.getAttribute(
157 "ddmTemplateKey");
158
159 if (Validator.isNotNull(ddmTemplateKey)) {
160 contextQuery.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
161 }
162 }
163
164 @Override
165 public void postProcessSearchQuery(
166 BooleanQuery searchQuery, SearchContext searchContext)
167 throws Exception {
168
169 addSearchTerm(searchQuery, searchContext, Field.CLASS_PK, false);
170 addSearchLocalizedTerm(
171 searchQuery, searchContext, Field.CONTENT, false);
172 addSearchLocalizedTerm(
173 searchQuery, searchContext, Field.DESCRIPTION, false);
174 addSearchTerm(searchQuery, searchContext, Field.ENTRY_CLASS_PK, false);
175 addSearchLocalizedTerm(searchQuery, searchContext, Field.TITLE, false);
176 addSearchTerm(searchQuery, searchContext, Field.TYPE, false);
177 addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
178
179 addSearchTerm(searchQuery, searchContext, "articleId", false);
180
181 LinkedHashMap<String, Object> params =
182 (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
183
184 if (params != null) {
185 String expandoAttributes = (String)params.get("expandoAttributes");
186
187 if (Validator.isNotNull(expandoAttributes)) {
188 addSearchExpando(searchQuery, searchContext, expandoAttributes);
189 }
190 }
191 }
192
193 @Override
194 protected void addSearchLocalizedTerm(
195 BooleanQuery searchQuery, SearchContext searchContext, String field,
196 boolean like)
197 throws Exception {
198
199 if (Validator.isNull(field)) {
200 return;
201 }
202
203 String value = String.valueOf(searchContext.getAttribute(field));
204
205 if (Validator.isNull(value)) {
206 value = searchContext.getKeywords();
207 }
208
209 if (Validator.isNull(value)) {
210 return;
211 }
212
213 field = DocumentImpl.getLocalizedName(searchContext.getLocale(), field);
214
215 if (searchContext.isAndSearch()) {
216 searchQuery.addRequiredTerm(field, value, like);
217 }
218 else {
219 searchQuery.addTerm(field, value, like);
220 }
221 }
222
223 @Override
224 protected void doDelete(Object obj) throws Exception {
225 JournalArticle article = (JournalArticle)obj;
226
227 deleteDocument(
228 article.getCompanyId(), article.getGroupId(),
229 article.getArticleId());
230 }
231
232 @Override
233 protected Document doGetDocument(Object obj) throws Exception {
234 JournalArticle article = (JournalArticle)obj;
235
236 Document document = getBaseModelDocument(PORTLET_ID, article);
237
238 document.addUID(
239 PORTLET_ID, article.getGroupId(), article.getArticleId());
240
241 Locale defaultLocale = LocaleUtil.getDefault();
242
243 String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale);
244
245 String[] languageIds = getLanguageIds(
246 defaultLanguageId, article.getContent());
247
248 for (String languageId : languageIds) {
249 String content = extractContent(article, languageId);
250
251 if (languageId.equals(defaultLanguageId)) {
252 document.addText(Field.CONTENT, content);
253 }
254
255 document.addText(
256 Field.CONTENT.concat(StringPool.UNDERLINE).concat(languageId),
257 content);
258 }
259
260 document.addLocalizedText(
261 Field.DESCRIPTION, article.getDescriptionMap());
262 document.addKeyword(Field.FOLDER_ID, article.getFolderId());
263 document.addLocalizedText(Field.TITLE, article.getTitleMap());
264 document.addKeyword(Field.TYPE, article.getType());
265 document.addKeyword(Field.VERSION, article.getVersion());
266
267 String articleId = article.getArticleId();
268
269 if (article.isInTrash()) {
270 articleId = TrashUtil.getOriginalTitle(articleId);
271 }
272
273 document.addKeyword("articleId", articleId);
274 document.addKeyword("ddmStructureKey", article.getStructureId());
275 document.addKeyword("ddmTemplateKey", article.getTemplateId());
276 document.addDate("displayDate", article.getDisplayDate());
277 document.addKeyword("layoutUuid", article.getLayoutUuid());
278
279 DDMStructure ddmStructure = null;
280
281 if (Validator.isNotNull(article.getStructureId())) {
282 try {
283 ddmStructure = DDMStructureLocalServiceUtil.getStructure(
284 article.getGroupId(),
285 PortalUtil.getClassNameId(JournalArticle.class),
286 article.getStructureId(), true);
287 }
288 catch (NoSuchStructureException nsse) {
289 }
290 }
291
292 processDDMStructure(ddmStructure, document, article.getContent());
293
294 return document;
295 }
296
297 @Override
298 protected String doGetSortField(String orderByCol) {
299 if (orderByCol.equals("display-date")) {
300 return "displayDate";
301 }
302 else if (orderByCol.equals("id")) {
303 return Field.ENTRY_CLASS_PK;
304 }
305 else if (orderByCol.equals("modified-date")) {
306 return Field.MODIFIED_DATE;
307 }
308 else if (orderByCol.equals("title")) {
309 return Field.TITLE;
310 }
311 else {
312 return orderByCol;
313 }
314 }
315
316 @Override
317 protected Summary doGetSummary(
318 Document document, Locale locale, String snippet,
319 PortletURL portletURL) {
320
321 Locale snippetLocale = getSnippetLocale(document, locale);
322
323 String prefix = Field.SNIPPET + StringPool.UNDERLINE;
324
325 String title = document.get(
326 snippetLocale, prefix + Field.TITLE, Field.TITLE);
327
328 String content = document.get(
329 snippetLocale, prefix + Field.DESCRIPTION, prefix + Field.CONTENT);
330
331 if (Validator.isBlank(content)) {
332 content = document.get(locale, Field.DESCRIPTION, Field.CONTENT);
333
334 if (Validator.isBlank(content)) {
335 content = document.get(Field.DESCRIPTION, Field.CONTENT);
336 }
337 }
338
339 if (content.length() > 200) {
340 content = StringUtil.shorten(content, 200);
341 }
342
343 String groupId = document.get(Field.GROUP_ID);
344 String articleId = document.get("articleId");
345 String version = document.get(Field.VERSION);
346
347 portletURL.setParameter("struts_action", "/journal/edit_article");
348 portletURL.setParameter("groupId", groupId);
349 portletURL.setParameter("articleId", articleId);
350 portletURL.setParameter("version", version);
351
352 return new Summary(snippetLocale, title, content, portletURL);
353 }
354
355 @Override
356 protected void doReindex(Object obj) throws Exception {
357 JournalArticle article = (JournalArticle)obj;
358
359 Document document = getDocument(article);
360
361 if (!article.isIndexable() ||
362 (!article.isApproved() && !article.isInTrash() &&
363 (article.getVersion() !=
364 JournalArticleConstants.VERSION_DEFAULT))) {
365
366 SearchEngineUtil.deleteDocument(
367 getSearchEngineId(), article.getCompanyId(),
368 document.get(Field.UID));
369
370 return;
371 }
372
373 SearchEngineUtil.updateDocument(
374 getSearchEngineId(), article.getCompanyId(), document);
375 }
376
377 @Override
378 protected void doReindex(String className, long classPK) throws Exception {
379 JournalArticle article =
380 JournalArticleLocalServiceUtil.getLatestArticle(
381 classPK, WorkflowConstants.STATUS_APPROVED);
382
383 doReindex(article);
384 }
385
386 @Override
387 protected void doReindex(String[] ids) throws Exception {
388 long companyId = GetterUtil.getLong(ids[0]);
389
390 reindexArticles(companyId);
391 }
392
393 protected String encodeFieldName(String name) {
394 return _FIELD_NAMESPACE.concat(StringPool.FORWARD_SLASH).concat(name);
395 }
396
397 protected String extractContent(JournalArticle article, String languageId) {
398 String content = article.getContentByLocale(languageId);
399
400 if (Validator.isNotNull(article.getStructureId())) {
401 content = extractDynamicContent(content);
402 }
403 else {
404 content = extractStaticContent(content);
405 }
406
407 content = HtmlUtil.extractText(content);
408
409 return content;
410 }
411
412 protected String extractDynamicContent(Element rootElement) {
413 StringBundler sb = new StringBundler();
414
415 List<Element> dynamicElementElements = rootElement.elements(
416 "dynamic-element");
417
418 for (Element dynamicElementElement : dynamicElementElements) {
419 String type = dynamicElementElement.attributeValue(
420 "type", StringPool.BLANK);
421
422 if (!type.equals("boolean") && !type.equals("document_library") &&
423 !type.equals("image") && !type.equals("list") &&
424 !type.equals("link_to_layout") && !type.equals("multi-list") &&
425 !type.equals("selection_break")) {
426
427 Element dynamicContentElement = dynamicElementElement.element(
428 "dynamic-content");
429
430 if (dynamicContentElement != null) {
431 String dynamicContent = dynamicContentElement.getText();
432
433 sb.append(dynamicContent);
434 sb.append(StringPool.SPACE);
435 }
436 }
437
438 sb.append(extractDynamicContent(dynamicElementElement));
439 }
440
441 return sb.toString();
442 }
443
444 protected String extractDynamicContent(String content) {
445 try {
446 com.liferay.portal.kernel.xml.Document document =
447 SAXReaderUtil.read(content);
448
449 Element rootElement = document.getRootElement();
450
451 return extractDynamicContent(rootElement);
452 }
453 catch (DocumentException de) {
454 _log.error(de);
455 }
456
457 return StringPool.BLANK;
458 }
459
460 protected String extractStaticContent(String content) {
461 content = StringUtil.replace(content, "<![CDATA[", StringPool.BLANK);
462 content = StringUtil.replace(content, "]]>", StringPool.BLANK);
463 content = StringUtil.replace(content, "&", "&");
464 content = StringUtil.replace(content, "<", "<");
465 content = StringUtil.replace(content, ">", ">");
466
467 return content;
468 }
469
470 protected String[] getLanguageIds(
471 String defaultLanguageId, String content) {
472
473 String[] languageIds = LocalizationUtil.getAvailableLocales(content);
474
475 if (languageIds.length == 0) {
476 languageIds = new String[] {defaultLanguageId};
477 }
478
479 return languageIds;
480 }
481
482 @Override
483 protected String getPortletId(SearchContext searchContext) {
484 return PORTLET_ID;
485 }
486
487 protected void indexField(
488 Document document, Element element, String elType, String elIndexType) {
489
490 if (Validator.isNull(elIndexType)) {
491 return;
492 }
493
494 com.liferay.portal.kernel.xml.Document contentDocument =
495 element.getDocument();
496
497 Element rootElement = contentDocument.getRootElement();
498
499 String defaultLocale = GetterUtil.getString(
500 rootElement.attributeValue("default-locale"));
501
502 String name = encodeFieldName(element.attributeValue("name"));
503
504 List<Element> dynamicContentElements = element.elements(
505 "dynamic-content");
506
507 for (Element dynamicContentElement : dynamicContentElements) {
508 String contentLocale = GetterUtil.getString(
509 dynamicContentElement.attributeValue("language-id"));
510
511 String[] value = new String[] {dynamicContentElement.getText()};
512
513 if (elType.equals("multi-list")) {
514 List<Element> optionElements = dynamicContentElement.elements(
515 "option");
516
517 value = new String[optionElements.size()];
518
519 for (int i = 0; i < optionElements.size(); i++) {
520 value[i] = optionElements.get(i).getText();
521 }
522 }
523
524 if (elIndexType.equals("keyword")) {
525 if (Validator.isNull(contentLocale)) {
526 document.addKeyword(name, value);
527 }
528 else {
529 if (defaultLocale.equals(contentLocale)) {
530 document.addKeyword(name, value);
531 }
532
533 document.addKeyword(
534 name.concat(StringPool.UNDERLINE).concat(contentLocale),
535 value);
536 }
537 }
538 else if (elIndexType.equals("text")) {
539 if (Validator.isNull(contentLocale)) {
540 document.addText(
541 name, StringUtil.merge(value, StringPool.SPACE));
542 }
543 else {
544 if (defaultLocale.equals(contentLocale)) {
545 document.addText(
546 name, StringUtil.merge(value, StringPool.SPACE));
547 }
548
549 document.addText(
550 name.concat(StringPool.UNDERLINE).concat(contentLocale),
551 StringUtil.merge(value, StringPool.SPACE));
552 }
553 }
554 }
555 }
556
557 protected void processDDMStructure(
558 com.liferay.portal.kernel.xml.Document ddmStructureDocument,
559 Document document, Element rootElement)
560 throws Exception {
561
562 LinkedList<Element> queue = new LinkedList<Element>(
563 rootElement.elements());
564
565 Element element = null;
566
567 while ((element = queue.poll()) != null) {
568 String elName = element.attributeValue("name", StringPool.BLANK);
569 String elType = element.attributeValue("type", StringPool.BLANK);
570 String elIndexType = element.attributeValue(
571 "index-type", StringPool.BLANK);
572
573 if (ddmStructureDocument != null) {
574 String path = element.getPath();
575
576 path = path.concat("[@name=").concat(
577 HtmlUtil.escapeXPathAttribute(elName)).concat("]");
578
579 Node ddmStructureNode = ddmStructureDocument.selectSingleNode(
580 path);
581
582 if (ddmStructureNode != null) {
583 Element ddmStructureElement = (Element)ddmStructureNode;
584
585 elType = ddmStructureElement.attributeValue(
586 "type", StringPool.BLANK);
587 elIndexType = ddmStructureElement.attributeValue(
588 "indexType", StringPool.BLANK);
589 }
590 }
591
592 if (Validator.isNotNull(elType)) {
593 indexField(document, element, elType, elIndexType);
594 }
595
596 queue.addAll(element.elements());
597 }
598 }
599
600 protected void processDDMStructure(
601 DDMStructure ddmStructure, Document document, String content) {
602
603 try {
604 com.liferay.portal.kernel.xml.Document ddmStructureDocument = null;
605
606 if (ddmStructure != null) {
607 ddmStructureDocument = SAXReaderUtil.read(
608 ddmStructure.getXsd());
609 }
610
611 com.liferay.portal.kernel.xml.Document contentDocument =
612 SAXReaderUtil.read(content);
613
614 Element rootElement = contentDocument.getRootElement();
615
616 processDDMStructure(ddmStructureDocument, document, rootElement);
617 }
618 catch (Exception e) {
619 _log.error(e, e);
620 }
621 }
622
623 protected void reindexArticles(long companyId)
624 throws PortalException, SystemException {
625
626 final Collection<Document> documents = new ArrayList<Document>();
627
628 ActionableDynamicQuery actionableDynamicQuery =
629 new JournalArticleActionableDynamicQuery() {
630
631 @Override
632 protected void addCriteria(DynamicQuery dynamicQuery) {
633 Junction junction = RestrictionsFactoryUtil.disjunction();
634
635 Junction approvedArticlesJunction =
636 RestrictionsFactoryUtil.conjunction();
637
638 Property statusProperty = PropertyFactoryUtil.forName("status");
639
640 approvedArticlesJunction.add(
641 statusProperty.eq(WorkflowConstants.STATUS_APPROVED));
642
643 junction.add(approvedArticlesJunction);
644
645 Junction draftArticlesJunction =
646 RestrictionsFactoryUtil.conjunction();
647
648 Property versionProperty = PropertyFactoryUtil.forName(
649 "version");
650
651 draftArticlesJunction.add(
652 versionProperty.eq(
653 JournalArticleConstants.VERSION_DEFAULT));
654
655 draftArticlesJunction.add(
656 statusProperty.eq(WorkflowConstants.STATUS_DRAFT));
657
658 junction.add(draftArticlesJunction);
659
660 dynamicQuery.add(junction);
661
662 Property indexableProperty = PropertyFactoryUtil.forName(
663 "indexable");
664
665 dynamicQuery.add(indexableProperty.eq(true));
666 }
667
668 @Override
669 protected void performAction(Object object)
670 throws PortalException, SystemException {
671
672 JournalArticle article = (JournalArticle)object;
673
674 if (article.isApproved()) {
675 JournalArticle latestArticle =
676 JournalArticleLocalServiceUtil.getLatestArticle(
677 article.getResourcePrimKey(),
678 WorkflowConstants.STATUS_APPROVED);
679
680 if (!latestArticle.isIndexable()) {
681 return;
682 }
683 }
684
685 Document document = getDocument(article);
686
687 documents.add(document);
688 }
689
690 };
691
692 actionableDynamicQuery.setCompanyId(companyId);
693
694 actionableDynamicQuery.performActions();
695
696 SearchEngineUtil.updateDocuments(
697 getSearchEngineId(), companyId, documents);
698 }
699
700 private static final String _FIELD_NAMESPACE = "web_content";
701
702 private static Log _log = LogFactoryUtil.getLog(
703 JournalArticleIndexer.class);
704
705 }