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