001    /**
002     * Copyright (c) 2000-2012 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.wiki.util;
016    
017    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
018    import com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;
019    import com.liferay.portal.kernel.dao.orm.Projection;
020    import com.liferay.portal.kernel.dao.orm.ProjectionFactoryUtil;
021    import com.liferay.portal.kernel.dao.orm.ProjectionList;
022    import com.liferay.portal.kernel.dao.orm.Property;
023    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
024    import com.liferay.portal.kernel.dao.orm.QueryUtil;
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.Hits;
033    import com.liferay.portal.kernel.search.SearchContext;
034    import com.liferay.portal.kernel.search.SearchEngineUtil;
035    import com.liferay.portal.kernel.search.Summary;
036    import com.liferay.portal.kernel.util.GetterUtil;
037    import com.liferay.portal.kernel.util.HtmlUtil;
038    import com.liferay.portal.kernel.util.StringUtil;
039    import com.liferay.portal.kernel.util.Validator;
040    import com.liferay.portal.kernel.workflow.WorkflowConstants;
041    import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
042    import com.liferay.portal.util.PortletKeys;
043    import com.liferay.portlet.wiki.model.WikiNode;
044    import com.liferay.portlet.wiki.model.WikiPage;
045    import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
046    import com.liferay.portlet.wiki.service.WikiNodeServiceUtil;
047    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
048    
049    import java.util.ArrayList;
050    import java.util.Collection;
051    import java.util.List;
052    import java.util.Locale;
053    
054    import javax.portlet.PortletURL;
055    
056    /**
057     * @author Brian Wing Shun Chan
058     * @author Harry Mark
059     * @author Bruno Farache
060     * @author Raymond Augé
061     */
062    public class WikiIndexer extends BaseIndexer {
063    
064            public static final String[] CLASS_NAMES = {WikiPage.class.getName()};
065    
066            public static final String PORTLET_ID = PortletKeys.WIKI;
067    
068            public String[] getClassNames() {
069                    return CLASS_NAMES;
070            }
071    
072            public String getPortletId() {
073                    return PORTLET_ID;
074            }
075    
076            @Override
077            public boolean isPermissionAware() {
078                    return _PERMISSION_AWARE;
079            }
080    
081            @Override
082            public void postProcessContextQuery(
083                            BooleanQuery contextQuery, SearchContext searchContext)
084                    throws Exception {
085    
086                    int status = GetterUtil.getInteger(
087                            searchContext.getAttribute(Field.STATUS),
088                            WorkflowConstants.STATUS_ANY);
089    
090                    if (status != WorkflowConstants.STATUS_ANY) {
091                            contextQuery.addRequiredTerm(Field.STATUS, status);
092                    }
093    
094                    long[] nodeIds = searchContext.getNodeIds();
095    
096                    if ((nodeIds != null) && (nodeIds.length > 0)) {
097                            BooleanQuery nodeIdsQuery = BooleanQueryFactoryUtil.create(
098                                    searchContext);
099    
100                            for (long nodeId : nodeIds) {
101                                    try {
102                                            WikiNodeServiceUtil.getNode(nodeId);
103                                    }
104                                    catch (Exception e) {
105                                            continue;
106                                    }
107    
108                                    nodeIdsQuery.addTerm(Field.NODE_ID, nodeId);
109                            }
110    
111                            contextQuery.add(nodeIdsQuery, BooleanClauseOccur.MUST);
112                    }
113            }
114    
115            protected void addReindexCriteria(
116                    DynamicQuery dynamicQuery, long companyId) {
117    
118                    Property property = PropertyFactoryUtil.forName("companyId");
119    
120                    dynamicQuery.add(property.eq(companyId));
121            }
122    
123            protected void addReindexCriteria(
124                    DynamicQuery dynamicQuery, long groupId, long nodeId) {
125    
126                    Property groupIdProperty = PropertyFactoryUtil.forName("groupId");
127    
128                    dynamicQuery.add(groupIdProperty.eq(groupId));
129    
130                    Property nodeIdProperty = PropertyFactoryUtil.forName("nodeId");
131    
132                    dynamicQuery.add(nodeIdProperty.eq(nodeId));
133    
134                    Property headProperty = PropertyFactoryUtil.forName("head");
135    
136                    dynamicQuery.add(headProperty.eq(true));
137            }
138    
139            @Override
140            protected void doDelete(Object obj) throws Exception {
141                    SearchContext searchContext = new SearchContext();
142    
143                    searchContext.setSearchEngineId(getSearchEngineId());
144    
145                    if (obj instanceof Object[]) {
146                            Object[] array = (Object[])obj;
147    
148                            long companyId = (Long)array[0];
149                            long nodeId = (Long)array[1];
150                            String title = (String)array[2];
151    
152                            Document document = new DocumentImpl();
153    
154                            document.addUID(PORTLET_ID, nodeId, title);
155    
156                            SearchEngineUtil.deleteDocument(
157                                    getSearchEngineId(), companyId, document.get(Field.UID));
158    
159                    }
160                    else if (obj instanceof WikiNode) {
161                            WikiNode node = (WikiNode)obj;
162    
163                            BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create(
164                                    searchContext);
165    
166                            booleanQuery.addRequiredTerm(Field.PORTLET_ID, PORTLET_ID);
167    
168                            booleanQuery.addRequiredTerm("nodeId", node.getNodeId());
169    
170                            Hits hits = SearchEngineUtil.search(
171                                    getSearchEngineId(), node.getCompanyId(), booleanQuery,
172                                    QueryUtil.ALL_POS, QueryUtil.ALL_POS);
173    
174                            for (int i = 0; i < hits.getLength(); i++) {
175                                    Document document = hits.doc(i);
176    
177                                    SearchEngineUtil.deleteDocument(
178                                            getSearchEngineId(), node.getCompanyId(),
179                                            document.get(Field.UID));
180                            }
181                    }
182                    else if (obj instanceof WikiPage) {
183                            WikiPage page = (WikiPage)obj;
184    
185                            Document document = new DocumentImpl();
186    
187                            document.addUID(PORTLET_ID, page.getNodeId(), page.getTitle());
188    
189                            SearchEngineUtil.deleteDocument(
190                                    getSearchEngineId(), page.getCompanyId(),
191                                    document.get(Field.UID));
192                    }
193            }
194    
195            @Override
196            protected Document doGetDocument(Object obj) throws Exception {
197                    WikiPage page = (WikiPage)obj;
198    
199                    Document document = getBaseModelDocument(PORTLET_ID, page);
200    
201                    document.addUID(PORTLET_ID, page.getNodeId(), page.getTitle());
202    
203                    String content = HtmlUtil.extractText(
204                            WikiUtil.convert(page, null, null, null));
205    
206                    document.addText(Field.CONTENT, content);
207    
208                    document.addKeyword(Field.NODE_ID, page.getNodeId());
209                    document.addText(Field.TITLE, page.getTitle());
210    
211                    return document;
212            }
213    
214            @Override
215            protected Summary doGetSummary(
216                    Document document, Locale locale, String snippet,
217                    PortletURL portletURL) {
218    
219                    String title = document.get(Field.TITLE);
220    
221                    String content = snippet;
222    
223                    if (Validator.isNull(snippet)) {
224                            content = StringUtil.shorten(document.get(Field.CONTENT), 200);
225                    }
226    
227                    String nodeId = document.get("nodeId");
228    
229                    portletURL.setParameter("struts_action", "/wiki/view");
230                    portletURL.setParameter("nodeId", nodeId);
231                    portletURL.setParameter("title", title);
232    
233                    return new Summary(title, content, portletURL);
234            }
235    
236            @Override
237            protected void doReindex(Object obj) throws Exception {
238                    WikiPage page = (WikiPage)obj;
239    
240                    if (Validator.isNotNull(page.getRedirectTitle())) {
241                            return;
242                    }
243    
244                    Document document = getDocument(page);
245    
246                    SearchEngineUtil.updateDocument(
247                            getSearchEngineId(), page.getCompanyId(), document);
248            }
249    
250            @Override
251            protected void doReindex(String className, long classPK) throws Exception {
252                    WikiPage page = WikiPageLocalServiceUtil.getPage(classPK);
253    
254                    doReindex(page);
255            }
256    
257            @Override
258            protected void doReindex(String[] ids) throws Exception {
259                    long companyId = GetterUtil.getLong(ids[0]);
260    
261                    reindexNodes(companyId);
262            }
263    
264            @Override
265            protected String getPortletId(SearchContext searchContext) {
266                    return PORTLET_ID;
267            }
268    
269            protected void reindexNodes(long companyId) throws Exception {
270                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
271                            WikiNode.class, PACLClassLoaderUtil.getPortalClassLoader());
272    
273                    Projection minNodeIdProjection = ProjectionFactoryUtil.min("nodeId");
274                    Projection maxNodeIdProjection = ProjectionFactoryUtil.max("nodeId");
275    
276                    ProjectionList projectionList = ProjectionFactoryUtil.projectionList();
277    
278                    projectionList.add(minNodeIdProjection);
279                    projectionList.add(maxNodeIdProjection);
280    
281                    dynamicQuery.setProjection(projectionList);
282    
283                    addReindexCriteria(dynamicQuery, companyId);
284    
285                    List<Object[]> results = WikiNodeLocalServiceUtil.dynamicQuery(
286                            dynamicQuery);
287    
288                    Object[] minAndMaxNodeIds = results.get(0);
289    
290                    if ((minAndMaxNodeIds[0] == null) || (minAndMaxNodeIds[1] == null)) {
291                            return;
292                    }
293    
294                    long minNodeId = (Long)minAndMaxNodeIds[0];
295                    long maxNodeId = (Long)minAndMaxNodeIds[1];
296    
297                    long startNodeId = minNodeId;
298                    long endNodeId = startNodeId + DEFAULT_INTERVAL;
299    
300                    while (startNodeId <= maxNodeId) {
301                            reindexNodes(companyId, startNodeId, endNodeId);
302    
303                            startNodeId = endNodeId;
304                            endNodeId += DEFAULT_INTERVAL;
305                    }
306            }
307    
308            protected void reindexNodes(
309                            long companyId, long startNodeId, long endNodeId)
310                    throws Exception {
311    
312                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
313                            WikiNode.class, PACLClassLoaderUtil.getPortalClassLoader());
314    
315                    Property property = PropertyFactoryUtil.forName("nodeId");
316    
317                    dynamicQuery.add(property.ge(startNodeId));
318                    dynamicQuery.add(property.lt(endNodeId));
319    
320                    addReindexCriteria(dynamicQuery, companyId);
321    
322                    List<WikiNode> nodes = WikiNodeLocalServiceUtil.dynamicQuery(
323                            dynamicQuery);
324    
325                    for (WikiNode node : nodes) {
326                            long groupId = node.getGroupId();
327                            long nodeId = node.getNodeId();
328    
329                            reindexPages(companyId, groupId, nodeId);
330                    }
331            }
332    
333            protected void reindexPages(long companyId, long groupId, long nodeId)
334                    throws Exception {
335    
336                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
337                            WikiPage.class, PACLClassLoaderUtil.getPortalClassLoader());
338    
339                    Projection minPageIdProjection = ProjectionFactoryUtil.min("pageId");
340                    Projection maxPageIdProjection = ProjectionFactoryUtil.max("pageId");
341    
342                    ProjectionList projectionList = ProjectionFactoryUtil.projectionList();
343    
344                    projectionList.add(minPageIdProjection);
345                    projectionList.add(maxPageIdProjection);
346    
347                    dynamicQuery.setProjection(projectionList);
348    
349                    addReindexCriteria(dynamicQuery, groupId, nodeId);
350    
351                    List<Object[]> results = WikiPageLocalServiceUtil.dynamicQuery(
352                            dynamicQuery);
353    
354                    Object[] minAndMaxPageIds = results.get(0);
355    
356                    if ((minAndMaxPageIds[0] == null) || (minAndMaxPageIds[1] == null)) {
357                            return;
358                    }
359    
360                    long minPageId = (Long)minAndMaxPageIds[0];
361                    long maxPageId = (Long)minAndMaxPageIds[1];
362    
363                    long startPageId = minPageId;
364                    long endPageId = startPageId + DEFAULT_INTERVAL;
365    
366                    while (startPageId <= maxPageId) {
367                            reindexPages(companyId, groupId, nodeId, startPageId, endPageId);
368    
369                            startPageId = endPageId;
370                            endPageId += DEFAULT_INTERVAL;
371                    }
372            }
373    
374            protected void reindexPages(
375                            long companyId, long groupId, long nodeId, long startPageId,
376                            long endPageId)
377                    throws Exception {
378    
379                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
380                            WikiPage.class, PACLClassLoaderUtil.getPortalClassLoader());
381    
382                    Property property = PropertyFactoryUtil.forName("pageId");
383    
384                    dynamicQuery.add(property.ge(startPageId));
385                    dynamicQuery.add(property.lt(endPageId));
386    
387                    addReindexCriteria(dynamicQuery, groupId, nodeId);
388    
389                    List<WikiPage> pages = WikiPageLocalServiceUtil.dynamicQuery(
390                            dynamicQuery);
391    
392                    if (pages.isEmpty()) {
393                            return;
394                    }
395    
396                    Collection<Document> documents = new ArrayList<Document>(pages.size());
397    
398                    for (WikiPage page : pages) {
399                            Document document = getDocument(page);
400    
401                            documents.add(document);
402                    }
403    
404                    SearchEngineUtil.updateDocuments(
405                            getSearchEngineId(), companyId, documents);
406            }
407    
408            private static final boolean _PERMISSION_AWARE = true;
409    
410    }