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