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