001    /**
002     * Copyright (c) 2000-2011 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.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                    DLFileVersion dlFileVersion = dlFileEntry.getLatestFileVersion(true);
436    
437                    if (!dlFileVersion.isApproved()) {
438                            return;
439                    }
440    
441                    Document document = getDocument(dlFileEntry);
442    
443                    if (document != null) {
444                            SearchEngineUtil.updateDocument(
445                                    dlFileEntry.getCompanyId(), document);
446                    }
447            }
448    
449            @Override
450            protected void doReindex(String className, long classPK) throws Exception {
451                    DLFileEntry dlFileEntry = DLFileEntryLocalServiceUtil.getFileEntry(
452                            classPK);
453    
454                    doReindex(dlFileEntry);
455            }
456    
457            @Override
458            protected void doReindex(String[] ids) throws Exception {
459                    if (ids.length == 1) {
460                            long companyId = GetterUtil.getLong(ids[0]);
461    
462                            reindexFolders(companyId);
463                            reindexRoot(companyId);
464                    }
465                    else {
466                            long companyId = GetterUtil.getLong(ids[0]);
467                            long groupId = GetterUtil.getLong(ids[2]);
468                            long dataRepositoryId = GetterUtil.getLong(ids[3]);
469    
470                            reindexFileEntries(companyId, groupId, dataRepositoryId);
471                    }
472            }
473    
474            @Override
475            protected String getPortletId(SearchContext searchContext) {
476                    return PORTLET_ID;
477            }
478    
479            protected void reindexFileEntries(
480                            long companyId, long groupId, long dataRepositoryId)
481                    throws Exception {
482    
483                    long folderId = DLFolderConstants.getFolderId(
484                            groupId, dataRepositoryId);
485    
486                    int fileEntriesCount = DLFileEntryLocalServiceUtil.getFileEntriesCount(
487                            companyId, folderId);
488    
489                    int fileEntriesPages = fileEntriesCount / Indexer.DEFAULT_INTERVAL;
490    
491                    for (int i = 0; i <= fileEntriesPages; i++) {
492                            int fileEntriesStart = (i * Indexer.DEFAULT_INTERVAL);
493                            int fileEntriesEnd = fileEntriesStart + Indexer.DEFAULT_INTERVAL;
494    
495                            reindexFileEntries(
496                                    companyId, groupId, folderId, fileEntriesStart, fileEntriesEnd);
497                    }
498            }
499    
500            protected void reindexFileEntries(
501                            long companyId, long groupId, long folderId, int fileEntriesStart,
502                            int fileEntriesEnd)
503                    throws Exception {
504    
505                    Collection<Document> documents = new ArrayList<Document>();
506    
507                    List<DLFileEntry> dlFileEntries =
508                            DLFileEntryLocalServiceUtil.getFileEntries(
509                                    groupId, folderId, fileEntriesStart, fileEntriesEnd, null);
510    
511                    for (DLFileEntry dlFileEntry : dlFileEntries) {
512                            Document document = getDocument(dlFileEntry);
513    
514                            if (document != null) {
515                                    documents.add(document);
516                            }
517                    }
518    
519                    SearchEngineUtil.updateDocuments(companyId, documents);
520            }
521    
522            protected void reindexFolders(long companyId) throws Exception {
523                    int folderCount = DLFolderLocalServiceUtil.getCompanyFoldersCount(
524                            companyId);
525    
526                    int folderPages = folderCount / Indexer.DEFAULT_INTERVAL;
527    
528                    for (int i = 0; i <= folderPages; i++) {
529                            int folderStart = (i * Indexer.DEFAULT_INTERVAL);
530                            int folderEnd = folderStart + Indexer.DEFAULT_INTERVAL;
531    
532                            reindexFolders(companyId, folderStart, folderEnd);
533                    }
534            }
535    
536            protected void reindexFolders(
537                            long companyId, int folderStart, int folderEnd)
538                    throws Exception {
539    
540                    List<DLFolder> dlFolders = DLFolderLocalServiceUtil.getCompanyFolders(
541                            companyId, folderStart, folderEnd);
542    
543                    for (DLFolder dlFolder : dlFolders) {
544                            String portletId = PortletKeys.DOCUMENT_LIBRARY;
545                            long groupId = dlFolder.getGroupId();
546                            long folderId = dlFolder.getFolderId();
547    
548                            String[] newIds = {
549                                    String.valueOf(companyId), portletId,
550                                    String.valueOf(groupId), String.valueOf(folderId)
551                            };
552    
553                            reindex(newIds);
554                    }
555            }
556    
557            protected void reindexRoot(long companyId) throws Exception {
558                    int groupCount = GroupLocalServiceUtil.getCompanyGroupsCount(companyId);
559    
560                    int groupPages = groupCount / Indexer.DEFAULT_INTERVAL;
561    
562                    for (int i = 0; i <= groupPages; i++) {
563                            int groupStart = (i * Indexer.DEFAULT_INTERVAL);
564                            int groupEnd = groupStart + Indexer.DEFAULT_INTERVAL;
565    
566                            reindexRoot(companyId, groupStart, groupEnd);
567                    }
568            }
569    
570            protected void reindexRoot(long companyId, int groupStart, int groupEnd)
571                    throws Exception {
572    
573                    List<Group> groups = GroupLocalServiceUtil.getCompanyGroups(
574                            companyId, groupStart, groupEnd);
575    
576                    for (Group group : groups) {
577                            String portletId = PortletKeys.DOCUMENT_LIBRARY;
578                            long groupId = group.getGroupId();
579                            long folderId = groupId;
580    
581                            String[] newIds = {
582                                    String.valueOf(companyId), portletId,
583                                    String.valueOf(groupId), String.valueOf(folderId)
584                            };
585    
586                            reindex(newIds);
587                    }
588            }
589    
590            private static final boolean _FILTER_SEARCH = true;
591    
592            private static Log _log = LogFactoryUtil.getLog(DLIndexer.class);
593    
594    }