1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.journal.util;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  import com.liferay.portal.kernel.search.BaseIndexer;
20  import com.liferay.portal.kernel.search.Document;
21  import com.liferay.portal.kernel.search.DocumentImpl;
22  import com.liferay.portal.kernel.search.Field;
23  import com.liferay.portal.kernel.search.Indexer;
24  import com.liferay.portal.kernel.search.SearchContext;
25  import com.liferay.portal.kernel.search.SearchEngineUtil;
26  import com.liferay.portal.kernel.search.Summary;
27  import com.liferay.portal.kernel.util.GetterUtil;
28  import com.liferay.portal.kernel.util.HtmlUtil;
29  import com.liferay.portal.kernel.util.StringBundler;
30  import com.liferay.portal.kernel.util.StringPool;
31  import com.liferay.portal.kernel.util.StringUtil;
32  import com.liferay.portal.kernel.util.Validator;
33  import com.liferay.portal.kernel.workflow.WorkflowConstants;
34  import com.liferay.portal.kernel.xml.Element;
35  import com.liferay.portal.kernel.xml.SAXReaderUtil;
36  import com.liferay.portal.util.PortletKeys;
37  import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
38  import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
39  import com.liferay.portlet.expando.model.ExpandoBridge;
40  import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
41  import com.liferay.portlet.journal.model.JournalArticle;
42  import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
43  
44  import java.util.ArrayList;
45  import java.util.Collection;
46  import java.util.Date;
47  import java.util.LinkedList;
48  import java.util.List;
49  
50  import javax.portlet.PortletURL;
51  
52  /**
53   * <a href="JournalIndexer.java.html"><b><i>View Source</i></b></a>
54   *
55   * @author Brian Wing Shun Chan
56   * @author Harry Mark
57   * @author Bruno Farache
58   * @author Raymond Augé
59   */
60  public class JournalIndexer extends BaseIndexer {
61  
62      public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
63  
64      public static final String PORTLET_ID = PortletKeys.JOURNAL;
65  
66      public String[] getClassNames() {
67          return CLASS_NAMES;
68      }
69  
70      public Summary getSummary(
71          Document document, String snippet, PortletURL portletURL) {
72  
73          String title = document.get(Field.TITLE);
74  
75          String content = snippet;
76  
77          if (Validator.isNull(snippet)) {
78              content = StringUtil.shorten(document.get(Field.CONTENT), 200);
79          }
80  
81          String groupId = document.get("groupId");
82          String articleId = document.get(Field.ENTRY_CLASS_PK);
83          String version = document.get("version");
84  
85          portletURL.setParameter("struts_action", "/journal/edit_article");
86          portletURL.setParameter("groupId", groupId);
87          portletURL.setParameter("articleId", articleId);
88          portletURL.setParameter("version", version);
89  
90          return new Summary(title, content, portletURL);
91      }
92  
93      protected void doDelete(Object obj) throws Exception {
94          JournalArticle article = (JournalArticle)obj;
95  
96          Document document = new DocumentImpl();
97  
98          document.addUID(
99              PORTLET_ID, article.getGroupId(), article.getArticleId());
100 
101         SearchEngineUtil.deleteDocument(
102             article.getCompanyId(), document.get(Field.UID));
103     }
104 
105     protected Document doGetDocument(Object obj) throws Exception {
106         JournalArticle article = (JournalArticle)obj;
107 
108         long companyId = article.getCompanyId();
109         long groupId = getParentGroupId(article.getGroupId());
110         long scopeGroupId = article.getGroupId();
111         long userId = article.getUserId();
112         long resourcePrimKey = article.getResourcePrimKey();
113         String articleId = article.getArticleId();
114         double version = article.getVersion();
115         String title = article.getTitle();
116         String description = article.getDescription();
117         String content = article.getContent();
118         String type = article.getType();
119         Date displayDate = article.getDisplayDate();
120 
121         long[] assetCategoryIds = AssetCategoryLocalServiceUtil.getCategoryIds(
122             JournalArticle.class.getName(), resourcePrimKey);
123         String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
124             JournalArticle.class.getName(), resourcePrimKey);
125 
126         ExpandoBridge expandoBridge = article.getExpandoBridge();
127 
128         Document document = new DocumentImpl();
129 
130         document.addUID(PORTLET_ID, groupId, articleId);
131 
132         document.addModifiedDate(displayDate);
133 
134         document.addKeyword(Field.COMPANY_ID, companyId);
135         document.addKeyword(Field.PORTLET_ID, PORTLET_ID);
136         document.addKeyword(Field.GROUP_ID, groupId);
137         document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);
138         document.addKeyword(Field.USER_ID, userId);
139 
140         document.addText(Field.TITLE, title);
141         document.addText(Field.CONTENT, processContent(document, content));
142         document.addText(Field.DESCRIPTION, description);
143         document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
144         document.addKeyword(Field.ASSET_TAG_NAMES, assetTagNames);
145 
146         document.addKeyword(
147             Field.ENTRY_CLASS_NAME, JournalArticle.class.getName());
148         document.addKeyword(Field.ENTRY_CLASS_PK, articleId);
149         document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
150         document.addKeyword(Field.VERSION, version);
151         document.addKeyword(Field.TYPE, type);
152 
153         ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
154 
155         return document;
156     }
157 
158     protected void doReindex(Object obj) throws Exception {
159         JournalArticle article = (JournalArticle)obj;
160 
161         if (!article.isApproved() || !article.isIndexable()) {
162             return;
163         }
164 
165         Document document = getDocument(article);
166 
167         SearchEngineUtil.updateDocument(article.getCompanyId(), document);
168     }
169 
170     protected void doReindex(String className, long classPK) throws Exception {
171         JournalArticle article =
172             JournalArticleLocalServiceUtil.getLatestArticle(
173                 classPK, WorkflowConstants.STATUS_APPROVED);
174 
175         doReindex(article);
176     }
177 
178     protected void doReindex(String[] ids) throws Exception {
179         long companyId = GetterUtil.getLong(ids[0]);
180 
181         reindexArticles(companyId);
182     }
183 
184     protected String encodeFieldName(String name) {
185         return _FIELD_NAMESPACE.concat(StringPool.FORWARD_SLASH).concat(name);
186     }
187 
188     protected String getIndexableContent(Document document, Element rootElement)
189         throws Exception {
190 
191         StringBundler sb = new StringBundler();
192 
193         LinkedList<Element> queue = new LinkedList<Element>(
194             rootElement.elements());
195 
196         Element element = null;
197 
198         while ((element = queue.poll()) != null) {
199             String elType = element.attributeValue("type", StringPool.BLANK);
200             String elIndexType = element.attributeValue(
201                 "index-type", StringPool.BLANK);
202 
203             indexField(document, element, elType, elIndexType);
204 
205             if (elType.equals("text") || elType.equals("text_box") ||
206                 elType.equals("text_area")) {
207 
208                 for (Element dynamicContentElement :
209                         element.elements("dynamic-content")) {
210 
211                     String text = dynamicContentElement.getText();
212 
213                     sb.append(text);
214                     sb.append(StringPool.SPACE);
215                 }
216             }
217             else if (element.getName().equals("static-content")) {
218                 String text = element.getText();
219 
220                 sb.append(text);
221                 sb.append(StringPool.SPACE);
222             }
223 
224             queue.addAll(element.elements());
225         }
226 
227         return sb.toString();
228     }
229 
230     protected String getIndexableContent(Document document, String content) {
231         try {
232             com.liferay.portal.kernel.xml.Document contentDocument =
233                 SAXReaderUtil.read(content);
234 
235             Element rootElement = contentDocument.getRootElement();
236 
237             return getIndexableContent(document, rootElement);
238         }
239         catch (Exception e) {
240             _log.error(e, e);
241 
242             return content;
243         }
244     }
245 
246     protected String getPortletId(SearchContext searchContext) {
247         return PORTLET_ID;
248     }
249 
250     protected void indexField(
251         Document document, Element element, String elType, String elIndexType) {
252 
253         if (Validator.isNull(elIndexType)) {
254             return;
255         }
256 
257         Element dynamicContentElement = element.element("dynamic-content");
258 
259         String fieldName = encodeFieldName(
260             element.attributeValue("name", StringPool.BLANK));
261         String[] value = new String[] {dynamicContentElement.getText()};
262 
263         if (elType.equals("multi-list")) {
264             List<Element> optionElements = dynamicContentElement.elements();
265 
266             value = new String[optionElements.size()];
267 
268             for (int i = 0; i < optionElements.size(); i++) {
269                 value[i] = optionElements.get(i).getText();
270             }
271         }
272 
273         if (elIndexType.equals("keyword")) {
274             document.addKeyword(fieldName, value);
275         }
276         else if (elIndexType.equals("text")) {
277             document.addText(
278                 fieldName, StringUtil.merge(value, StringPool.SPACE));
279         }
280     }
281 
282     protected String processContent(Document document, String content) {
283         if ((content != null) &&
284             ((content.indexOf("<dynamic-content") != -1) ||
285              (content.indexOf("<static-content") != -1))) {
286 
287             content = getIndexableContent(document, content);
288 
289             content = StringUtil.replace(
290                 content, "<![CDATA[", StringPool.BLANK);
291             content = StringUtil.replace(content, "]]>", StringPool.BLANK);
292         }
293 
294         content = StringUtil.replace(content, "&amp;", "&");
295         content = StringUtil.replace(content, "&lt;", "<");
296         content = StringUtil.replace(content, "&gt;", ">");
297 
298         content = HtmlUtil.extractText(content);
299 
300         return content;
301     }
302 
303     protected void reindexArticles(long companyId) throws Exception {
304         int count = JournalArticleLocalServiceUtil.getCompanyArticlesCount(
305             companyId, WorkflowConstants.STATUS_APPROVED);
306 
307         int pages = count / Indexer.DEFAULT_INTERVAL;
308 
309         for (int i = 0; i <= pages; i++) {
310             int start = (i * Indexer.DEFAULT_INTERVAL);
311             int end = start + Indexer.DEFAULT_INTERVAL;
312 
313             reindexArticles(companyId, start, end);
314         }
315     }
316 
317     protected void reindexArticles(long companyId, int start, int end)
318         throws Exception {
319 
320         List<JournalArticle> articles =
321             JournalArticleLocalServiceUtil.getCompanyArticles(
322                 companyId, WorkflowConstants.STATUS_APPROVED, start, end);
323 
324         if (articles.isEmpty()) {
325             return;
326         }
327 
328         Collection<Document> documents = new ArrayList<Document>();
329 
330         for (JournalArticle article : articles) {
331             Document document = getDocument(article);
332 
333             documents.add(document);
334         }
335 
336         SearchEngineUtil.updateDocuments(companyId, documents);
337     }
338 
339     protected static final String _FIELD_NAMESPACE = "web_content";
340 
341     private static Log _log = LogFactoryUtil.getLog(JournalIndexer.class);
342 
343 }