001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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.Document;
021    import com.liferay.portal.kernel.search.DocumentImpl;
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.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.kernel.workflow.WorkflowConstants;
034    import com.liferay.portal.kernel.xml.Element;
035    import com.liferay.portal.kernel.xml.SAXReaderUtil;
036    import com.liferay.portal.util.PortletKeys;
037    import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
038    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
039    import com.liferay.portlet.expando.model.ExpandoBridge;
040    import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
041    import com.liferay.portlet.journal.model.JournalArticle;
042    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
043    
044    import java.util.ArrayList;
045    import java.util.Collection;
046    import java.util.Date;
047    import java.util.LinkedList;
048    import java.util.List;
049    
050    import javax.portlet.PortletURL;
051    
052    /**
053     * @author Brian Wing Shun Chan
054     * @author Harry Mark
055     * @author Bruno Farache
056     * @author Raymond Augé
057     */
058    public class JournalIndexer extends BaseIndexer {
059    
060            public static final String[] CLASS_NAMES = {JournalArticle.class.getName()};
061    
062            public static final String PORTLET_ID = PortletKeys.JOURNAL;
063    
064            public String[] getClassNames() {
065                    return CLASS_NAMES;
066            }
067    
068            public Summary getSummary(
069                    Document document, String snippet, PortletURL portletURL) {
070    
071                    String title = document.get(Field.TITLE);
072    
073                    String content = snippet;
074    
075                    if (Validator.isNull(snippet)) {
076                            content = StringUtil.shorten(document.get(Field.CONTENT), 200);
077                    }
078    
079                    String groupId = document.get("groupId");
080                    String articleId = document.get(Field.ENTRY_CLASS_PK);
081                    String version = document.get("version");
082    
083                    portletURL.setParameter("struts_action", "/journal/edit_article");
084                    portletURL.setParameter("groupId", groupId);
085                    portletURL.setParameter("articleId", articleId);
086                    portletURL.setParameter("version", version);
087    
088                    return new Summary(title, content, portletURL);
089            }
090    
091            protected void doDelete(Object obj) throws Exception {
092                    JournalArticle article = (JournalArticle)obj;
093    
094                    Document document = new DocumentImpl();
095    
096                    document.addUID(
097                            PORTLET_ID, article.getGroupId(), article.getArticleId());
098    
099                    SearchEngineUtil.deleteDocument(
100                            article.getCompanyId(), document.get(Field.UID));
101            }
102    
103            protected Document doGetDocument(Object obj) throws Exception {
104                    JournalArticle article = (JournalArticle)obj;
105    
106                    long companyId = article.getCompanyId();
107                    long groupId = getParentGroupId(article.getGroupId());
108                    long scopeGroupId = article.getGroupId();
109                    long userId = article.getUserId();
110                    long resourcePrimKey = article.getResourcePrimKey();
111                    String articleId = article.getArticleId();
112                    double version = article.getVersion();
113                    String title = article.getTitle();
114                    String description = article.getDescription();
115                    String content = article.getContent();
116                    String type = article.getType();
117                    Date displayDate = article.getDisplayDate();
118    
119                    long[] assetCategoryIds = AssetCategoryLocalServiceUtil.getCategoryIds(
120                            JournalArticle.class.getName(), resourcePrimKey);
121                    String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
122                            JournalArticle.class.getName(), resourcePrimKey);
123    
124                    ExpandoBridge expandoBridge = article.getExpandoBridge();
125    
126                    Document document = new DocumentImpl();
127    
128                    document.addUID(PORTLET_ID, groupId, articleId);
129    
130                    document.addModifiedDate(displayDate);
131    
132                    document.addKeyword(Field.COMPANY_ID, companyId);
133                    document.addKeyword(Field.PORTLET_ID, PORTLET_ID);
134                    document.addKeyword(Field.GROUP_ID, groupId);
135                    document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);
136                    document.addKeyword(Field.USER_ID, userId);
137    
138                    document.addText(Field.TITLE, title);
139                    document.addText(Field.CONTENT, processContent(document, content));
140                    document.addText(Field.DESCRIPTION, description);
141                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
142                    document.addKeyword(Field.ASSET_TAG_NAMES, assetTagNames);
143    
144                    document.addKeyword(
145                            Field.ENTRY_CLASS_NAME, JournalArticle.class.getName());
146                    document.addKeyword(Field.ENTRY_CLASS_PK, articleId);
147                    document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
148                    document.addKeyword(Field.VERSION, version);
149                    document.addKeyword(Field.TYPE, type);
150    
151                    ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
152    
153                    return document;
154            }
155    
156            protected void doReindex(Object obj) throws Exception {
157                    JournalArticle article = (JournalArticle)obj;
158    
159                    if (!article.isApproved() || !article.isIndexable()) {
160                            return;
161                    }
162    
163                    Document document = getDocument(article);
164    
165                    SearchEngineUtil.updateDocument(article.getCompanyId(), document);
166            }
167    
168            protected void doReindex(String className, long classPK) throws Exception {
169                    JournalArticle article =
170                            JournalArticleLocalServiceUtil.getLatestArticle(
171                                    classPK, WorkflowConstants.STATUS_APPROVED);
172    
173                    doReindex(article);
174            }
175    
176            protected void doReindex(String[] ids) throws Exception {
177                    long companyId = GetterUtil.getLong(ids[0]);
178    
179                    reindexArticles(companyId);
180            }
181    
182            protected String encodeFieldName(String name) {
183                    return _FIELD_NAMESPACE.concat(StringPool.FORWARD_SLASH).concat(name);
184            }
185    
186            protected String getIndexableContent(Document document, Element rootElement)
187                    throws Exception {
188    
189                    StringBundler sb = new StringBundler();
190    
191                    LinkedList<Element> queue = new LinkedList<Element>(
192                            rootElement.elements());
193    
194                    Element element = null;
195    
196                    while ((element = queue.poll()) != null) {
197                            String elType = element.attributeValue("type", StringPool.BLANK);
198                            String elIndexType = element.attributeValue(
199                                    "index-type", StringPool.BLANK);
200    
201                            indexField(document, element, elType, elIndexType);
202    
203                            if (elType.equals("text") || elType.equals("text_box") ||
204                                    elType.equals("text_area")) {
205    
206                                    for (Element dynamicContentElement :
207                                                    element.elements("dynamic-content")) {
208    
209                                            String text = dynamicContentElement.getText();
210    
211                                            sb.append(text);
212                                            sb.append(StringPool.SPACE);
213                                    }
214                            }
215                            else if (element.getName().equals("static-content")) {
216                                    String text = element.getText();
217    
218                                    sb.append(text);
219                                    sb.append(StringPool.SPACE);
220                            }
221    
222                            queue.addAll(element.elements());
223                    }
224    
225                    return sb.toString();
226            }
227    
228            protected String getIndexableContent(Document document, String content) {
229                    try {
230                            com.liferay.portal.kernel.xml.Document contentDocument =
231                                    SAXReaderUtil.read(content);
232    
233                            Element rootElement = contentDocument.getRootElement();
234    
235                            return getIndexableContent(document, rootElement);
236                    }
237                    catch (Exception e) {
238                            _log.error(e, e);
239    
240                            return content;
241                    }
242            }
243    
244            protected String getPortletId(SearchContext searchContext) {
245                    return PORTLET_ID;
246            }
247    
248            protected void indexField(
249                    Document document, Element element, String elType, String elIndexType) {
250    
251                    if (Validator.isNull(elIndexType)) {
252                            return;
253                    }
254    
255                    Element dynamicContentElement = element.element("dynamic-content");
256    
257                    String fieldName = encodeFieldName(
258                            element.attributeValue("name", StringPool.BLANK));
259                    String[] value = new String[] {dynamicContentElement.getText()};
260    
261                    if (elType.equals("multi-list")) {
262                            List<Element> optionElements = dynamicContentElement.elements();
263    
264                            value = new String[optionElements.size()];
265    
266                            for (int i = 0; i < optionElements.size(); i++) {
267                                    value[i] = optionElements.get(i).getText();
268                            }
269                    }
270    
271                    if (elIndexType.equals("keyword")) {
272                            document.addKeyword(fieldName, value);
273                    }
274                    else if (elIndexType.equals("text")) {
275                            document.addText(
276                                    fieldName, StringUtil.merge(value, StringPool.SPACE));
277                    }
278            }
279    
280            protected String processContent(Document document, String content) {
281                    if ((content != null) &&
282                            ((content.indexOf("<dynamic-content") != -1) ||
283                             (content.indexOf("<static-content") != -1))) {
284    
285                            content = getIndexableContent(document, content);
286    
287                            content = StringUtil.replace(
288                                    content, "<![CDATA[", StringPool.BLANK);
289                            content = StringUtil.replace(content, "]]>", StringPool.BLANK);
290                    }
291    
292                    content = StringUtil.replace(content, "&amp;", "&");
293                    content = StringUtil.replace(content, "&lt;", "<");
294                    content = StringUtil.replace(content, "&gt;", ">");
295    
296                    content = HtmlUtil.extractText(content);
297    
298                    return content;
299            }
300    
301            protected void reindexArticles(long companyId) throws Exception {
302                    int count = JournalArticleLocalServiceUtil.getCompanyArticlesCount(
303                            companyId, WorkflowConstants.STATUS_APPROVED);
304    
305                    int pages = count / Indexer.DEFAULT_INTERVAL;
306    
307                    for (int i = 0; i <= pages; i++) {
308                            int start = (i * Indexer.DEFAULT_INTERVAL);
309                            int end = start + Indexer.DEFAULT_INTERVAL;
310    
311                            reindexArticles(companyId, start, end);
312                    }
313            }
314    
315            protected void reindexArticles(long companyId, int start, int end)
316                    throws Exception {
317    
318                    List<JournalArticle> articles =
319                            JournalArticleLocalServiceUtil.getCompanyArticles(
320                                    companyId, WorkflowConstants.STATUS_APPROVED, start, end);
321    
322                    if (articles.isEmpty()) {
323                            return;
324                    }
325    
326                    Collection<Document> documents = new ArrayList<Document>();
327    
328                    for (JournalArticle article : articles) {
329                            Document document = getDocument(article);
330    
331                            documents.add(document);
332                    }
333    
334                    SearchEngineUtil.updateDocuments(companyId, documents);
335            }
336    
337            protected static final String _FIELD_NAMESPACE = "web_content";
338    
339            private static Log _log = LogFactoryUtil.getLog(JournalIndexer.class);
340    
341    }