001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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[] assetCategoryNames =
122                            AssetCategoryLocalServiceUtil.getCategoryNames(
123                                    JournalArticle.class.getName(), resourcePrimKey);
124                    String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
125                            JournalArticle.class.getName(), resourcePrimKey);
126    
127                    ExpandoBridge expandoBridge = article.getExpandoBridge();
128    
129                    Document document = new DocumentImpl();
130    
131                    document.addUID(PORTLET_ID, groupId, articleId);
132    
133                    document.addModifiedDate(displayDate);
134    
135                    document.addKeyword(Field.COMPANY_ID, companyId);
136                    document.addKeyword(Field.PORTLET_ID, PORTLET_ID);
137                    document.addKeyword(Field.GROUP_ID, groupId);
138                    document.addKeyword(Field.SCOPE_GROUP_ID, scopeGroupId);
139                    document.addKeyword(Field.USER_ID, userId);
140    
141                    document.addText(Field.TITLE, title);
142                    document.addText(Field.CONTENT, processContent(document, content));
143                    document.addText(Field.DESCRIPTION, description);
144                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
145                    document.addKeyword(Field.ASSET_CATEGORY_NAMES, assetCategoryNames);
146                    document.addKeyword(Field.ASSET_TAG_NAMES, assetTagNames);
147    
148                    document.addKeyword(
149                            Field.ENTRY_CLASS_NAME, JournalArticle.class.getName());
150                    document.addKeyword(Field.ENTRY_CLASS_PK, articleId);
151                    document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
152                    document.addKeyword(Field.VERSION, version);
153                    document.addKeyword(Field.TYPE, type);
154    
155                    ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
156    
157                    return document;
158            }
159    
160            protected void doReindex(Object obj) throws Exception {
161                    JournalArticle article = (JournalArticle)obj;
162    
163                    if (!article.isApproved() || !article.isIndexable()) {
164                            return;
165                    }
166    
167                    Document document = getDocument(article);
168    
169                    SearchEngineUtil.updateDocument(article.getCompanyId(), document);
170            }
171    
172            protected void doReindex(String className, long classPK) throws Exception {
173                    JournalArticle article =
174                            JournalArticleLocalServiceUtil.getLatestArticle(
175                                    classPK, WorkflowConstants.STATUS_APPROVED);
176    
177                    doReindex(article);
178            }
179    
180            protected void doReindex(String[] ids) throws Exception {
181                    long companyId = GetterUtil.getLong(ids[0]);
182    
183                    reindexArticles(companyId);
184            }
185    
186            protected String encodeFieldName(String name) {
187                    return _FIELD_NAMESPACE.concat(StringPool.FORWARD_SLASH).concat(name);
188            }
189    
190            protected String getIndexableContent(Document document, Element rootElement)
191                    throws Exception {
192    
193                    StringBundler sb = new StringBundler();
194    
195                    LinkedList<Element> queue = new LinkedList<Element>(
196                            rootElement.elements());
197    
198                    Element element = null;
199    
200                    while ((element = queue.poll()) != null) {
201                            String elType = element.attributeValue("type", StringPool.BLANK);
202                            String elIndexType = element.attributeValue(
203                                    "index-type", StringPool.BLANK);
204    
205                            indexField(document, element, elType, elIndexType);
206    
207                            if (elType.equals("text") || elType.equals("text_box") ||
208                                    elType.equals("text_area")) {
209    
210                                    for (Element dynamicContentElement :
211                                                    element.elements("dynamic-content")) {
212    
213                                            String text = dynamicContentElement.getText();
214    
215                                            sb.append(text);
216                                            sb.append(StringPool.SPACE);
217                                    }
218                            }
219                            else if (element.getName().equals("static-content")) {
220                                    String text = element.getText();
221    
222                                    sb.append(text);
223                                    sb.append(StringPool.SPACE);
224                            }
225    
226                            queue.addAll(element.elements());
227                    }
228    
229                    return sb.toString();
230            }
231    
232            protected String getIndexableContent(Document document, String content) {
233                    try {
234                            com.liferay.portal.kernel.xml.Document contentDocument =
235                                    SAXReaderUtil.read(content);
236    
237                            Element rootElement = contentDocument.getRootElement();
238    
239                            return getIndexableContent(document, rootElement);
240                    }
241                    catch (Exception e) {
242                            _log.error(e, e);
243    
244                            return content;
245                    }
246            }
247    
248            protected String getPortletId(SearchContext searchContext) {
249                    return PORTLET_ID;
250            }
251    
252            protected void indexField(
253                    Document document, Element element, String elType, String elIndexType) {
254    
255                    if (Validator.isNull(elIndexType)) {
256                            return;
257                    }
258    
259                    Element dynamicContentElement = element.element("dynamic-content");
260    
261                    String fieldName = encodeFieldName(
262                            element.attributeValue("name", StringPool.BLANK));
263                    String[] value = new String[] {dynamicContentElement.getText()};
264    
265                    if (elType.equals("multi-list")) {
266                            List<Element> optionElements = dynamicContentElement.elements();
267    
268                            value = new String[optionElements.size()];
269    
270                            for (int i = 0; i < optionElements.size(); i++) {
271                                    value[i] = optionElements.get(i).getText();
272                            }
273                    }
274    
275                    if (elIndexType.equals("keyword")) {
276                            document.addKeyword(fieldName, value);
277                    }
278                    else if (elIndexType.equals("text")) {
279                            document.addText(
280                                    fieldName, StringUtil.merge(value, StringPool.SPACE));
281                    }
282            }
283    
284            protected String processContent(Document document, String content) {
285                    if ((content != null) &&
286                            ((content.indexOf("<dynamic-content") != -1) ||
287                             (content.indexOf("<static-content") != -1))) {
288    
289                            content = getIndexableContent(document, content);
290    
291                            content = StringUtil.replace(
292                                    content, "<![CDATA[", StringPool.BLANK);
293                            content = StringUtil.replace(content, "]]>", StringPool.BLANK);
294                    }
295    
296                    content = StringUtil.replace(content, "&amp;", "&");
297                    content = StringUtil.replace(content, "&lt;", "<");
298                    content = StringUtil.replace(content, "&gt;", ">");
299    
300                    content = HtmlUtil.extractText(content);
301    
302                    return content;
303            }
304    
305            protected void reindexArticles(long companyId) throws Exception {
306                    int count = JournalArticleLocalServiceUtil.getCompanyArticlesCount(
307                            companyId, WorkflowConstants.STATUS_APPROVED);
308    
309                    int pages = count / Indexer.DEFAULT_INTERVAL;
310    
311                    for (int i = 0; i <= pages; i++) {
312                            int start = (i * Indexer.DEFAULT_INTERVAL);
313                            int end = start + Indexer.DEFAULT_INTERVAL;
314    
315                            reindexArticles(companyId, start, end);
316                    }
317            }
318    
319            protected void reindexArticles(long companyId, int start, int end)
320                    throws Exception {
321    
322                    List<JournalArticle> articles =
323                            JournalArticleLocalServiceUtil.getCompanyArticles(
324                                    companyId, WorkflowConstants.STATUS_APPROVED, start, end);
325    
326                    if (articles.isEmpty()) {
327                            return;
328                    }
329    
330                    Collection<Document> documents = new ArrayList<Document>();
331    
332                    for (JournalArticle article : articles) {
333                            Document document = getDocument(article);
334    
335                            documents.add(document);
336                    }
337    
338                    SearchEngineUtil.updateDocuments(companyId, documents);
339            }
340    
341            protected static final String _FIELD_NAMESPACE = "web_content";
342    
343            private static Log _log = LogFactoryUtil.getLog(JournalIndexer.class);
344    
345    }