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