001
014
015 package com.liferay.portlet.journal.util;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.search.BaseIndexer;
020 import com.liferay.portal.kernel.search.BooleanQuery;
021 import com.liferay.portal.kernel.search.Document;
022 import com.liferay.portal.kernel.search.Field;
023 import com.liferay.portal.kernel.search.Indexer;
024 import com.liferay.portal.kernel.search.SearchContext;
025 import com.liferay.portal.kernel.search.SearchEngineUtil;
026 import com.liferay.portal.kernel.search.Summary;
027 import com.liferay.portal.kernel.util.GetterUtil;
028 import com.liferay.portal.kernel.util.HtmlUtil;
029 import com.liferay.portal.kernel.util.LocaleUtil;
030 import com.liferay.portal.kernel.util.LocalizationUtil;
031 import com.liferay.portal.kernel.util.StringPool;
032 import com.liferay.portal.kernel.util.StringUtil;
033 import com.liferay.portal.kernel.util.Validator;
034 import com.liferay.portal.kernel.workflow.WorkflowConstants;
035 import com.liferay.portal.kernel.xml.Element;
036 import com.liferay.portal.kernel.xml.Node;
037 import com.liferay.portal.kernel.xml.SAXReaderUtil;
038 import com.liferay.portal.model.Group;
039 import com.liferay.portal.service.GroupLocalServiceUtil;
040 import com.liferay.portal.util.PortletKeys;
041 import com.liferay.portlet.journal.NoSuchStructureException;
042 import com.liferay.portlet.journal.model.JournalArticle;
043 import com.liferay.portlet.journal.model.JournalArticleConstants;
044 import com.liferay.portlet.journal.model.JournalStructure;
045 import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
046 import com.liferay.portlet.journal.service.JournalStructureLocalServiceUtil;
047
048 import java.util.ArrayList;
049 import java.util.Collection;
050 import java.util.LinkedHashMap;
051 import java.util.LinkedList;
052 import java.util.List;
053 import java.util.Locale;
054
055 import javax.portlet.PortletURL;
056
057
064 public class JournalIndexer extends BaseIndexer {
065
066 public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
067
068 public static final String PORTLET_ID = PortletKeys.JOURNAL;
069
070 public String[] getClassNames() {
071 return CLASS_NAMES;
072 }
073
074 public String getPortletId() {
075 return PORTLET_ID;
076 }
077
078 @Override
079 public void postProcessContextQuery(
080 BooleanQuery contextQuery, SearchContext searchContext)
081 throws Exception {
082
083 Long classNameId = (Long)searchContext.getAttribute(
084 Field.CLASS_NAME_ID);
085
086 if (classNameId != null) {
087 contextQuery.addRequiredTerm("classNameId", classNameId.toString());
088 }
089
090 int status = GetterUtil.getInteger(
091 searchContext.getAttribute(Field.STATUS),
092 WorkflowConstants.STATUS_APPROVED);
093
094 if (status != WorkflowConstants.STATUS_ANY) {
095 contextQuery.addRequiredTerm(Field.STATUS, status);
096 }
097
098 String articleType = (String)searchContext.getAttribute("articleType");
099
100 if (Validator.isNotNull(articleType)) {
101 contextQuery.addRequiredTerm(Field.TYPE, articleType);
102 }
103
104 String structureId = (String)searchContext.getAttribute("structureId");
105
106 if (Validator.isNotNull(structureId)) {
107 contextQuery.addRequiredTerm("structureId", structureId);
108 }
109
110 String templateId = (String)searchContext.getAttribute("templateId");
111
112 if (Validator.isNotNull(templateId)) {
113 contextQuery.addRequiredTerm("templateId", templateId);
114 }
115 }
116
117 @Override
118 public void postProcessSearchQuery(
119 BooleanQuery searchQuery, SearchContext searchContext)
120 throws Exception {
121
122 addSearchTerm(searchQuery, searchContext, Field.CLASS_PK, false);
123 addLocalizedSearchTerm(
124 searchQuery, searchContext, Field.CONTENT, false);
125 addLocalizedSearchTerm(
126 searchQuery, searchContext, Field.DESCRIPTION, false);
127 addSearchTerm(searchQuery, searchContext, Field.ENTRY_CLASS_PK, false);
128 addLocalizedSearchTerm(searchQuery, searchContext, Field.TITLE, false);
129 addSearchTerm(searchQuery, searchContext, Field.TYPE, false);
130 addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
131
132 LinkedHashMap<String, Object> params =
133 (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
134
135 if (params != null) {
136 String expandoAttributes = (String)params.get("expandoAttributes");
137
138 if (Validator.isNotNull(expandoAttributes)) {
139 addSearchExpando(searchQuery, searchContext, expandoAttributes);
140 }
141 }
142 }
143
144 @Override
145 protected void doDelete(Object obj) throws Exception {
146 JournalArticle article = (JournalArticle)obj;
147
148 deleteDocument(
149 article.getCompanyId(), article.getGroupId(),
150 article.getArticleId());
151 }
152
153 @Override
154 protected Document doGetDocument(Object obj) throws Exception {
155 JournalArticle article = (JournalArticle)obj;
156
157 Document document = getBaseModelDocument(PORTLET_ID, article);
158
159 document.addUID(
160 PORTLET_ID, article.getGroupId(), article.getArticleId());
161
162 Locale defaultLocale = LocaleUtil.getDefault();
163
164 String defaultLangaugeId = LocaleUtil.toLanguageId(defaultLocale);
165
166 String[] languageIds = getLanguageIds(
167 defaultLangaugeId, article.getContent());
168
169 for (String languageId : languageIds) {
170 String content = extractContent(
171 article.getContentByLocale(languageId));
172
173 if (languageId.equals(defaultLangaugeId)) {
174 document.addText(Field.CONTENT, content);
175 }
176
177 document.addText(
178 Field.CONTENT.concat(StringPool.UNDERLINE).concat(languageId),
179 content);
180 }
181
182 document.addLocalizedText(
183 Field.DESCRIPTION, article.getDescriptionMap());
184 document.addLocalizedText(Field.TITLE, article.getTitleMap());
185 document.addKeyword(Field.TYPE, article.getType());
186 document.addKeyword(Field.VERSION, article.getVersion());
187
188 document.addKeyword("articleId", article.getArticleId());
189 document.addDate("displayDate", article.getDisplayDate());
190 document.addKeyword("layoutUuid", article.getLayoutUuid());
191 document.addKeyword("structureId", article.getStructureId());
192 document.addKeyword("templateId", article.getTemplateId());
193
194 JournalStructure structure = null;
195
196 if (Validator.isNotNull(article.getStructureId())) {
197 try {
198 structure = JournalStructureLocalServiceUtil.getStructure(
199 article.getGroupId(), article.getStructureId());
200 }
201 catch (NoSuchStructureException nsse1) {
202 Group group = GroupLocalServiceUtil.getCompanyGroup(
203 article.getCompanyId());
204
205 try {
206 structure = JournalStructureLocalServiceUtil.getStructure(
207 group.getGroupId(), article.getStructureId());
208 }
209 catch (NoSuchStructureException nsse2) {
210 }
211 }
212 }
213
214 processStructure(structure, document, article.getContent());
215
216 return document;
217 }
218
219 @Override
220 protected String doGetSortField(String orderByCol) {
221 if (orderByCol.equals("display-date")) {
222 return "displayDate";
223 }
224 else if (orderByCol.equals("id")) {
225 return Field.ENTRY_CLASS_PK;
226 }
227 else if (orderByCol.equals("modified-date")) {
228 return Field.MODIFIED_DATE;
229 }
230 else if (orderByCol.equals("title")) {
231 return Field.TITLE;
232 }
233 else {
234 return orderByCol;
235 }
236 }
237
238 @Override
239 protected Summary doGetSummary(
240 Document document, Locale locale, String snippet,
241 PortletURL portletURL) {
242
243 String title = document.get(locale, Field.TITLE);
244
245 String content = snippet;
246
247 if (Validator.isNull(snippet)) {
248 content = StringUtil.shorten(
249 document.get(locale, Field.CONTENT), 200);
250 }
251
252 String groupId = document.get(Field.GROUP_ID);
253 String articleId = document.get("articleId");
254 String version = document.get(Field.VERSION);
255
256 portletURL.setParameter("struts_action", "/journal/edit_article");
257 portletURL.setParameter("groupId", groupId);
258 portletURL.setParameter("articleId", articleId);
259 portletURL.setParameter("version", version);
260
261 return new Summary(title, content, portletURL);
262 }
263
264 @Override
265 protected void doReindex(Object obj) throws Exception {
266 JournalArticle article = (JournalArticle)obj;
267
268 Document document = getDocument(article);
269
270 if (!article.isIndexable() ||
271 (!article.isApproved() &&
272 (article.getVersion() !=
273 JournalArticleConstants.VERSION_DEFAULT))) {
274
275 SearchEngineUtil.deleteDocument(
276 article.getCompanyId(), document.get(Field.UID));
277
278 return;
279 }
280
281 SearchEngineUtil.updateDocument(article.getCompanyId(), document);
282 }
283
284 @Override
285 protected void doReindex(String className, long classPK) throws Exception {
286 JournalArticle article =
287 JournalArticleLocalServiceUtil.getLatestArticle(
288 classPK, WorkflowConstants.STATUS_APPROVED);
289
290 doReindex(article);
291 }
292
293 @Override
294 protected void doReindex(String[] ids) throws Exception {
295 long companyId = GetterUtil.getLong(ids[0]);
296
297 reindexArticles(companyId);
298 }
299
300 protected String encodeFieldName(String name) {
301 return _FIELD_NAMESPACE.concat(StringPool.FORWARD_SLASH).concat(name);
302 }
303
304 protected String extractContent(String content) {
305 content = StringUtil.replace(content, "<![CDATA[", StringPool.BLANK);
306 content = StringUtil.replace(content, "]]>", StringPool.BLANK);
307 content = StringUtil.replace(content, "&", "&");
308 content = StringUtil.replace(content, "<", "<");
309 content = StringUtil.replace(content, ">", ">");
310
311 content = HtmlUtil.extractText(content);
312
313 return content;
314 }
315
316 protected String[] getLanguageIds(
317 String defaultLangaugeId, String content) {
318
319 String[] languageIds = LocalizationUtil.getAvailableLocales(content);
320
321 if (languageIds.length == 0) {
322 languageIds = new String[] {defaultLangaugeId};
323 }
324
325 return languageIds;
326 }
327
328 @Override
329 protected String getPortletId(SearchContext searchContext) {
330 return PORTLET_ID;
331 }
332
333 protected void indexField(
334 Document document, Element element, String elType, String elIndexType) {
335
336 if (Validator.isNull(elIndexType)) {
337 return;
338 }
339
340 com.liferay.portal.kernel.xml.Document structureDocument =
341 element.getDocument();
342
343 Element rootElement = structureDocument.getRootElement();
344
345 String defaultLocale = GetterUtil.getString(
346 rootElement.attributeValue("default-locale"));
347
348 String name = encodeFieldName(element.attributeValue("name"));
349
350 List<Element> dynamicContentElements = element.elements(
351 "dynamic-content");
352
353 for (Element dynamicContentElement : dynamicContentElements) {
354 String contentLocale = GetterUtil.getString(
355 dynamicContentElement.attributeValue("language-id"));
356
357 String[] value = new String[] {dynamicContentElement.getText()};
358
359 if (elType.equals("multi-list")) {
360 List<Element> optionElements = dynamicContentElement.elements(
361 "option");
362
363 value = new String[optionElements.size()];
364
365 for (int i = 0; i < optionElements.size(); i++) {
366 value[i] = optionElements.get(i).getText();
367 }
368 }
369
370 if (elIndexType.equals("keyword")) {
371 if (Validator.isNull(contentLocale)) {
372 document.addKeyword(name, value);
373 }
374 else {
375 if (defaultLocale.equals(contentLocale)) {
376 document.addKeyword(name, value);
377 }
378
379 document.addKeyword(
380 name.concat(StringPool.UNDERLINE).concat(contentLocale),
381 value);
382 }
383 }
384 else if (elIndexType.equals("text")) {
385 if (Validator.isNull(contentLocale)) {
386 document.addText(
387 name, StringUtil.merge(value, StringPool.SPACE));
388 }
389 else {
390 if (defaultLocale.equals(contentLocale)) {
391 document.addText(
392 name, StringUtil.merge(value, StringPool.SPACE));
393 }
394
395 document.addText(
396 name.concat(StringPool.UNDERLINE).concat(contentLocale),
397 StringUtil.merge(value, StringPool.SPACE));
398 }
399 }
400 }
401 }
402
403 protected void processStructure(
404 com.liferay.portal.kernel.xml.Document structureDocument,
405 Document document, Element rootElement)
406 throws Exception {
407
408 LinkedList<Element> queue = new LinkedList<Element>(
409 rootElement.elements());
410
411 Element element = null;
412
413 while ((element = queue.poll()) != null) {
414 String elName = element.attributeValue("name", StringPool.BLANK);
415 String elType = element.attributeValue("type", StringPool.BLANK);
416 String elIndexType = element.attributeValue(
417 "index-type", StringPool.BLANK);
418
419 if (structureDocument != null) {
420 String path = element.getPath().concat(
421 "[@name='").concat(elName).concat("']");
422
423 Node structureNode = structureDocument.selectSingleNode(path);
424
425 if (structureNode != null) {
426 Element structureElement = (Element)structureNode;
427
428 elType = structureElement.attributeValue(
429 "type", StringPool.BLANK);
430 elIndexType = structureElement.attributeValue(
431 "index-type", StringPool.BLANK);
432 }
433 }
434
435 if (Validator.isNotNull(elType)) {
436 indexField(document, element, elType, elIndexType);
437 }
438
439 queue.addAll(element.elements());
440 }
441 }
442
443 protected void processStructure(
444 JournalStructure structure, Document document, String content) {
445
446 try {
447 com.liferay.portal.kernel.xml.Document structureDocument = null;
448
449 if (structure != null) {
450 structureDocument = SAXReaderUtil.read(structure.getXsd());
451 }
452
453 com.liferay.portal.kernel.xml.Document contentDocument =
454 SAXReaderUtil.read(content);
455
456 Element rootElement = contentDocument.getRootElement();
457
458 processStructure(structureDocument, document, rootElement);
459 }
460 catch (Exception e) {
461 _log.error(e, e);
462 }
463 }
464
465 protected void reindexArticles(long companyId) throws Exception {
466 int count = JournalArticleLocalServiceUtil.getCompanyArticlesCount(
467 companyId, WorkflowConstants.STATUS_APPROVED);
468
469 int pages = count / Indexer.DEFAULT_INTERVAL;
470
471 for (int i = 0; i <= pages; i++) {
472 int start = (i * Indexer.DEFAULT_INTERVAL);
473 int end = start + Indexer.DEFAULT_INTERVAL;
474
475 reindexArticles(companyId, start, end);
476 }
477 }
478
479 protected void reindexArticles(long companyId, int start, int end)
480 throws Exception {
481
482 List<JournalArticle> articles = new ArrayList<JournalArticle>();
483
484 List<JournalArticle> approvedArticles =
485 JournalArticleLocalServiceUtil.getCompanyArticles(
486 companyId, WorkflowConstants.STATUS_APPROVED, start, end);
487
488 articles.addAll(approvedArticles);
489
490 List<JournalArticle> draftArticles =
491 JournalArticleLocalServiceUtil.getCompanyArticles(
492 companyId, JournalArticleConstants.VERSION_DEFAULT,
493 WorkflowConstants.STATUS_DRAFT, start, end);
494
495 articles.addAll(draftArticles);
496
497 if (articles.isEmpty()) {
498 return;
499 }
500
501 Collection<Document> documents = new ArrayList<Document>();
502
503 for (JournalArticle article : articles) {
504 if (!article.isIndexable()) {
505 continue;
506 }
507
508 if (article.isApproved()) {
509 JournalArticle latestArticle =
510 JournalArticleLocalServiceUtil.getLatestArticle(
511 article.getResourcePrimKey(),
512 WorkflowConstants.STATUS_APPROVED);
513
514 if (!latestArticle.isIndexable()) {
515 continue;
516 }
517 }
518
519 Document document = getDocument(article);
520
521 documents.add(document);
522 }
523
524 SearchEngineUtil.updateDocuments(companyId, documents);
525 }
526
527 private static final String _FIELD_NAMESPACE = "web_content";
528
529 private static Log _log = LogFactoryUtil.getLog(JournalIndexer.class);
530
531 }