001    /**
002     * Copyright (c) 2000-2012 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.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
018    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.Property;
020    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
021    import com.liferay.portal.kernel.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
026    import com.liferay.portal.kernel.portlet.LiferayWindowState;
027    import com.liferay.portal.kernel.search.BaseIndexer;
028    import com.liferay.portal.kernel.search.BooleanClauseOccur;
029    import com.liferay.portal.kernel.search.BooleanQuery;
030    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
031    import com.liferay.portal.kernel.search.Document;
032    import com.liferay.portal.kernel.search.DocumentImpl;
033    import com.liferay.portal.kernel.search.Field;
034    import com.liferay.portal.kernel.search.SearchContext;
035    import com.liferay.portal.kernel.search.SearchEngineUtil;
036    import com.liferay.portal.kernel.search.SearchException;
037    import com.liferay.portal.kernel.search.Summary;
038    import com.liferay.portal.kernel.util.ArrayUtil;
039    import com.liferay.portal.kernel.util.GetterUtil;
040    import com.liferay.portal.kernel.util.PropsKeys;
041    import com.liferay.portal.kernel.util.StringPool;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.kernel.workflow.WorkflowConstants;
044    import com.liferay.portal.model.Group;
045    import com.liferay.portal.security.permission.ActionKeys;
046    import com.liferay.portal.security.permission.PermissionChecker;
047    import com.liferay.portal.service.GroupLocalServiceUtil;
048    import com.liferay.portal.service.persistence.GroupActionableDynamicQuery;
049    import com.liferay.portal.util.PortletKeys;
050    import com.liferay.portal.util.PrefsPropsUtil;
051    import com.liferay.portal.util.PropsValues;
052    import com.liferay.portlet.documentlibrary.asset.DLFileEntryAssetRendererFactory;
053    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
054    import com.liferay.portlet.documentlibrary.model.DLFileEntryMetadata;
055    import com.liferay.portlet.documentlibrary.model.DLFileEntryType;
056    import com.liferay.portlet.documentlibrary.model.DLFileVersion;
057    import com.liferay.portlet.documentlibrary.model.DLFolder;
058    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
059    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
060    import com.liferay.portlet.documentlibrary.service.DLFileEntryMetadataLocalServiceUtil;
061    import com.liferay.portlet.documentlibrary.service.DLFileEntryTypeLocalServiceUtil;
062    import com.liferay.portlet.documentlibrary.service.DLFolderServiceUtil;
063    import com.liferay.portlet.documentlibrary.service.permission.DLFileEntryPermission;
064    import com.liferay.portlet.documentlibrary.service.persistence.DLFileEntryActionableDynamicQuery;
065    import com.liferay.portlet.documentlibrary.service.persistence.DLFolderActionableDynamicQuery;
066    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
067    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
068    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
069    import com.liferay.portlet.dynamicdatamapping.storage.StorageEngineUtil;
070    import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
071    import com.liferay.portlet.expando.model.ExpandoBridge;
072    import com.liferay.portlet.expando.util.ExpandoBridgeFactoryUtil;
073    import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
074    
075    import java.io.IOException;
076    import java.io.InputStream;
077    
078    import java.util.ArrayList;
079    import java.util.Collection;
080    import java.util.LinkedHashMap;
081    import java.util.List;
082    import java.util.Locale;
083    import java.util.Set;
084    import java.util.TreeSet;
085    
086    import javax.portlet.PortletRequest;
087    import javax.portlet.PortletURL;
088    import javax.portlet.WindowStateException;
089    
090    /**
091     * @author Brian Wing Shun Chan
092     * @author Raymond Augé
093     * @author Alexander Chow
094     */
095    public class DLIndexer extends BaseIndexer {
096    
097            public static final String[] CLASS_NAMES = {DLFileEntry.class.getName()};
098    
099            public static final String PORTLET_ID = PortletKeys.DOCUMENT_LIBRARY;
100    
101            public DLIndexer() {
102                    setFilterSearch(true);
103                    setPermissionAware(true);
104            }
105    
106            public String[] getClassNames() {
107                    return CLASS_NAMES;
108            }
109    
110            public String getPortletId() {
111                    return PORTLET_ID;
112            }
113    
114            @Override
115            public boolean hasPermission(
116                            PermissionChecker permissionChecker, String entryClassName,
117                            long entryClassPK, String actionId)
118                    throws Exception {
119    
120                    return DLFileEntryPermission.contains(
121                            permissionChecker, entryClassPK, ActionKeys.VIEW);
122            }
123    
124            @Override
125            public void postProcessContextQuery(
126                            BooleanQuery contextQuery, SearchContext searchContext)
127                    throws Exception {
128    
129                    int status = GetterUtil.getInteger(
130                            searchContext.getAttribute(Field.STATUS),
131                            WorkflowConstants.STATUS_APPROVED);
132    
133                    if (status != WorkflowConstants.STATUS_ANY) {
134                            contextQuery.addRequiredTerm(Field.STATUS, status);
135                    }
136    
137                    long[] folderIds = searchContext.getFolderIds();
138    
139                    if ((folderIds != null) && (folderIds.length > 0)) {
140                            if (folderIds[0] == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
141                                    return;
142                            }
143    
144                            BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create(
145                                    searchContext);
146    
147                            for (long folderId : folderIds) {
148                                    try {
149                                            DLFolderServiceUtil.getFolder(folderId);
150                                    }
151                                    catch (Exception e) {
152                                            continue;
153                                    }
154    
155                                    folderIdsQuery.addTerm(Field.FOLDER_ID, folderId);
156                            }
157    
158                            contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
159                    }
160            }
161    
162            @Override
163            public void postProcessSearchQuery(
164                            BooleanQuery searchQuery, SearchContext searchContext)
165                    throws Exception {
166    
167                    Set<DDMStructure> ddmStructuresSet = new TreeSet<DDMStructure>();
168    
169                    long[] groupIds = searchContext.getGroupIds();
170    
171                    if ((groupIds != null) && (groupIds.length > 0)) {
172                            List<DLFileEntryType> dlFileEntryTypes =
173                                    DLFileEntryTypeLocalServiceUtil.getFileEntryTypes(groupIds);
174    
175                            for (DLFileEntryType dlFileEntryType : dlFileEntryTypes) {
176                                    ddmStructuresSet.addAll(dlFileEntryType.getDDMStructures());
177                            }
178                    }
179    
180                    Group group = GroupLocalServiceUtil.getCompanyGroup(
181                            searchContext.getCompanyId());
182    
183                    DDMStructure tikaRawMetadataStructure =
184                            DDMStructureLocalServiceUtil.fetchStructure(
185                                    group.getGroupId(), "TikaRawMetadata");
186    
187                    if (tikaRawMetadataStructure != null) {
188                            ddmStructuresSet.add(tikaRawMetadataStructure);
189                    }
190    
191                    for (DDMStructure ddmStructure : ddmStructuresSet) {
192                            addSearchDDMStruture(searchQuery, searchContext, ddmStructure);
193                    }
194    
195                    String keywords = searchContext.getKeywords();
196    
197                    if (Validator.isNull(keywords)) {
198                            addSearchTerm(searchQuery, searchContext, Field.DESCRIPTION, false);
199                            addSearchTerm(searchQuery, searchContext, Field.TITLE, false);
200                            addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
201                    }
202    
203                    addSearchTerm(searchQuery, searchContext, "extension", false);
204                    addSearchTerm(searchQuery, searchContext, "fileEntryTypeId", false);
205                    addSearchTerm(searchQuery, searchContext, "path", false);
206    
207                    LinkedHashMap<String, Object> params =
208                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
209    
210                    if (params != null) {
211                            String expandoAttributes = (String)params.get("expandoAttributes");
212    
213                            if (Validator.isNotNull(expandoAttributes)) {
214                                    addSearchExpando(searchQuery, searchContext, expandoAttributes);
215                            }
216                    }
217            }
218    
219            protected void addFileEntryTypeAttributes(
220                            Document document, DLFileVersion dlFileVersion)
221                    throws PortalException, SystemException {
222    
223                    List<DLFileEntryMetadata> dlFileEntryMetadatas =
224                            DLFileEntryMetadataLocalServiceUtil.
225                                    getFileVersionFileEntryMetadatas(
226                                            dlFileVersion.getFileVersionId());
227    
228                    for (DLFileEntryMetadata dlFileEntryMetadata : dlFileEntryMetadatas) {
229                            Fields fields = null;
230    
231                            try {
232                                    fields = StorageEngineUtil.getFields(
233                                            dlFileEntryMetadata.getDDMStorageId());
234                            }
235                            catch (Exception e) {
236                            }
237    
238                            if (fields != null) {
239                                    DDMStructure ddmStructure =
240                                            DDMStructureLocalServiceUtil.getStructure(
241                                                    dlFileEntryMetadata.getDDMStructureId());
242    
243                                    DDMIndexerUtil.addAttributes(document, ddmStructure, fields);
244                            }
245                    }
246            }
247    
248            @Override
249            protected void doDelete(Object obj) throws Exception {
250                    DLFileEntry dlFileEntry = (DLFileEntry)obj;
251    
252                    Document document = new DocumentImpl();
253    
254                    document.addUID(PORTLET_ID, dlFileEntry.getFileEntryId());
255    
256                    SearchEngineUtil.deleteDocument(
257                            getSearchEngineId(), dlFileEntry.getCompanyId(),
258                            document.get(Field.UID));
259            }
260    
261            @Override
262            protected Document doGetDocument(Object obj) throws Exception {
263                    DLFileEntry dlFileEntry = (DLFileEntry)obj;
264    
265                    if (_log.isDebugEnabled()) {
266                            _log.debug("Indexing document " + dlFileEntry);
267                    }
268    
269                    boolean indexContent = true;
270    
271                    InputStream is = null;
272    
273                    try {
274                            if (PropsValues.DL_FILE_INDEXING_MAX_SIZE == 0) {
275                                    indexContent = false;
276                            }
277                            else if (PropsValues.DL_FILE_INDEXING_MAX_SIZE != -1) {
278                                    if (dlFileEntry.getSize() >
279                                                    PropsValues.DL_FILE_INDEXING_MAX_SIZE) {
280    
281                                            indexContent = false;
282                                    }
283                            }
284    
285                            if (indexContent) {
286                                    String[] ignoreExtensions = PrefsPropsUtil.getStringArray(
287                                            PropsKeys.DL_FILE_INDEXING_IGNORE_EXTENSIONS,
288                                            StringPool.COMMA);
289    
290                                    if (ArrayUtil.contains(
291                                                    ignoreExtensions,
292                                                    StringPool.PERIOD + dlFileEntry.getExtension())) {
293    
294                                            indexContent = false;
295                                    }
296                            }
297    
298                            if (indexContent) {
299                                    is = dlFileEntry.getFileVersion().getContentStream(false);
300                            }
301                    }
302                    catch (Exception e) {
303                    }
304    
305                    if (indexContent && (is == null)) {
306                            if (_log.isDebugEnabled()) {
307                                    _log.debug(
308                                            "Document " + dlFileEntry + " does not have any content");
309                            }
310    
311                            return null;
312                    }
313    
314                    DLFileVersion dlFileVersion = dlFileEntry.getFileVersion();
315    
316                    try {
317                            Document document = getBaseModelDocument(
318                                    PORTLET_ID, dlFileEntry, dlFileVersion);
319    
320                            if (indexContent) {
321                                    try {
322                                            document.addFile(Field.CONTENT, is, dlFileEntry.getTitle());
323                                    }
324                                    catch (IOException ioe) {
325                                            throw new SearchException(
326                                                    "Cannot extract text from file" + dlFileEntry);
327                                    }
328                            }
329    
330                            document.addText(Field.DESCRIPTION, dlFileEntry.getDescription());
331                            document.addKeyword(Field.FOLDER_ID, dlFileEntry.getFolderId());
332                            document.addText(
333                                    Field.PROPERTIES, dlFileEntry.getLuceneProperties());
334                            document.addText(Field.TITLE, dlFileEntry.getTitle());
335    
336                            document.addKeyword(
337                                    "dataRepositoryId", dlFileEntry.getDataRepositoryId());
338                            document.addKeyword("extension", dlFileEntry.getExtension());
339                            document.addKeyword(
340                                    "fileEntryTypeId", dlFileEntry.getFileEntryTypeId());
341                            document.addKeyword("path", dlFileEntry.getTitle());
342    
343                            ExpandoBridge expandoBridge =
344                                    ExpandoBridgeFactoryUtil.getExpandoBridge(
345                                            dlFileEntry.getCompanyId(), DLFileEntry.class.getName(),
346                                            dlFileVersion.getFileVersionId());
347    
348                            ExpandoBridgeIndexerUtil.addAttributes(document, expandoBridge);
349    
350                            addFileEntryTypeAttributes(document, dlFileVersion);
351    
352                            if (!dlFileVersion.isInTrash() &&
353                                    dlFileVersion.isInTrashContainer()) {
354    
355                                    DLFolder trashedFolder = dlFileVersion.getTrashContainer();
356    
357                                    addTrashFields(
358                                            document, DLFolder.class.getName(),
359                                            trashedFolder.getFolderId(), null, null,
360                                            DLFileEntryAssetRendererFactory.TYPE);
361    
362                                    document.addKeyword(
363                                            Field.ROOT_ENTRY_CLASS_NAME, DLFolder.class.getName());
364                                    document.addKeyword(
365                                            Field.ROOT_ENTRY_CLASS_PK, trashedFolder.getFolderId());
366                                    document.addKeyword(
367                                            Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
368                            }
369    
370                            if (_log.isDebugEnabled()) {
371                                    _log.debug("Document " + dlFileEntry + " indexed successfully");
372                            }
373    
374                            return document;
375                    }
376                    finally {
377                            if (is != null) {
378                                    try {
379                                            is.close();
380                                    }
381                                    catch (IOException ioe) {
382                                    }
383                            }
384                    }
385            }
386    
387            @Override
388            protected Summary doGetSummary(
389                    Document document, Locale locale, String snippet,
390                    PortletURL portletURL) {
391    
392                    LiferayPortletURL liferayPortletURL = (LiferayPortletURL)portletURL;
393    
394                    liferayPortletURL.setLifecycle(PortletRequest.ACTION_PHASE);
395    
396                    try {
397                            liferayPortletURL.setWindowState(LiferayWindowState.EXCLUSIVE);
398                    }
399                    catch (WindowStateException wse) {
400                    }
401    
402                    String fileEntryId = document.get(Field.ENTRY_CLASS_PK);
403    
404                    portletURL.setParameter("struts_action", "/document_library/get_file");
405                    portletURL.setParameter("fileEntryId", fileEntryId);
406    
407                    Summary summary = createSummary(document, Field.TITLE, Field.CONTENT);
408    
409                    summary.setMaxContentLength(200);
410                    summary.setPortletURL(portletURL);
411    
412                    return summary;
413            }
414    
415            @Override
416            protected void doReindex(Object obj) throws Exception {
417                    DLFileEntry dlFileEntry = (DLFileEntry)obj;
418    
419                    DLFileVersion dlFileVersion = dlFileEntry.getFileVersion();
420    
421                    if (!dlFileVersion.isApproved() && !dlFileVersion.isInTrash()) {
422                            return;
423                    }
424    
425                    Document document = getDocument(dlFileEntry);
426    
427                    if (document != null) {
428                            SearchEngineUtil.updateDocument(
429                                    getSearchEngineId(), dlFileEntry.getCompanyId(), document);
430                    }
431            }
432    
433            @Override
434            protected void doReindex(String className, long classPK) throws Exception {
435                    DLFileEntry dlFileEntry = DLFileEntryLocalServiceUtil.getFileEntry(
436                            classPK);
437    
438                    doReindex(dlFileEntry);
439            }
440    
441            @Override
442            protected void doReindex(String[] ids) throws Exception {
443                    if (ids.length == 1) {
444                            long companyId = GetterUtil.getLong(ids[0]);
445    
446                            reindexFolders(companyId);
447                            reindexRoot(companyId);
448                    }
449                    else {
450                            long companyId = GetterUtil.getLong(ids[0]);
451                            long groupId = GetterUtil.getLong(ids[2]);
452                            long dataRepositoryId = GetterUtil.getLong(ids[3]);
453    
454                            reindexFileEntries(companyId, groupId, dataRepositoryId);
455                    }
456            }
457    
458            @Override
459            protected String getPortletId(SearchContext searchContext) {
460                    return PORTLET_ID;
461            }
462    
463            protected void reindexFileEntries(
464                            long companyId, final long groupId, final long dataRepositoryId)
465                    throws PortalException, SystemException {
466    
467                    final Collection<Document> documents = new ArrayList<Document>();
468    
469                    ActionableDynamicQuery actionableDynamicQuery =
470                            new DLFileEntryActionableDynamicQuery() {
471    
472                            @Override
473                            protected void addCriteria(DynamicQuery dynamicQuery) {
474                                    Property property = PropertyFactoryUtil.forName("folderId");
475    
476                                    long folderId = DLFolderConstants.getFolderId(
477                                            groupId, dataRepositoryId);
478    
479                                    dynamicQuery.add(property.eq(folderId));
480                            }
481    
482                            @Override
483                            protected void performAction(Object object) throws PortalException {
484                                    DLFileEntry dlFileEntry = (DLFileEntry)object;
485    
486                                    Document document = getDocument(dlFileEntry);
487    
488                                    if (document != null) {
489                                            documents.add(document);
490                                    }
491                            }
492    
493                    };
494    
495                    actionableDynamicQuery.setGroupId(groupId);
496    
497                    actionableDynamicQuery.performActions();
498    
499                    SearchEngineUtil.updateDocuments(
500                            getSearchEngineId(), companyId, documents);
501            }
502    
503            protected void reindexFolders(final long companyId)
504                    throws PortalException, SystemException {
505    
506                    ActionableDynamicQuery actionableDynamicQuery =
507                            new DLFolderActionableDynamicQuery() {
508    
509                            @Override
510                            protected void performAction(Object object) throws PortalException {
511                                    DLFolder dlFolder = (DLFolder)object;
512    
513                                    String portletId = PortletKeys.DOCUMENT_LIBRARY;
514                                    long groupId = dlFolder.getGroupId();
515                                    long folderId = dlFolder.getFolderId();
516    
517                                    String[] newIds = {
518                                            String.valueOf(companyId), portletId,
519                                            String.valueOf(groupId), String.valueOf(folderId)
520                                    };
521    
522                                    reindex(newIds);
523                            }
524    
525                    };
526    
527                    actionableDynamicQuery.setCompanyId(companyId);
528    
529                    actionableDynamicQuery.performActions();
530            }
531    
532            protected void reindexRoot(final long companyId)
533                    throws PortalException, SystemException {
534    
535                    ActionableDynamicQuery actionableDynamicQuery =
536                            new GroupActionableDynamicQuery() {
537    
538                            @Override
539                            protected void performAction(Object object) throws PortalException {
540                                    Group group = (Group)object;
541    
542                                    String portletId = PortletKeys.DOCUMENT_LIBRARY;
543                                    long groupId = group.getGroupId();
544                                    long folderId = groupId;
545    
546                                    String[] newIds = {
547                                            String.valueOf(companyId), portletId,
548                                            String.valueOf(groupId), String.valueOf(folderId)
549                                    };
550    
551                                    reindex(newIds);
552                            }
553    
554                    };
555    
556                    actionableDynamicQuery.setCompanyId(companyId);
557    
558                    actionableDynamicQuery.performActions();
559            }
560    
561            private static Log _log = LogFactoryUtil.getLog(DLIndexer.class);
562    
563    }