mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	127605 jkaabimofrad: Merged API-STRIKES-BACK (5.2.0) to HEAD (5.2)
      127418 jvonka: FileFolderService / GetChildrenCannedQuery - optional support for filtering by assocType(s)
      - eg. as required by new V1 REST API
      - RA-1053, RA-1092
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@127712 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			1835 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1835 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * Alfresco is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  * 
 | |
|  * Alfresco is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU Lesser General Public License for more details.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU Lesser General Public License
 | |
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| package org.alfresco.repo.model.filefolder;
 | |
| 
 | |
| import java.io.Serializable;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Collection;
 | |
| import java.util.Collections;
 | |
| import java.util.HashMap;
 | |
| import java.util.HashSet;
 | |
| import java.util.Iterator;
 | |
| import java.util.LinkedList;
 | |
| import java.util.List;
 | |
| import java.util.Locale;
 | |
| import java.util.Map;
 | |
| import java.util.ResourceBundle.Control;
 | |
| import java.util.Set;
 | |
| import java.util.Stack;
 | |
| 
 | |
| import org.alfresco.model.ContentModel;
 | |
| import org.alfresco.query.CannedQueryFactory;
 | |
| import org.alfresco.query.CannedQueryResults;
 | |
| import org.alfresco.query.PagingRequest;
 | |
| import org.alfresco.query.PagingResults;
 | |
| import org.alfresco.repo.copy.AbstractBaseCopyService;
 | |
| import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility;
 | |
| import org.alfresco.repo.model.filefolder.traitextender.FileFolderServiceExtension;
 | |
| import org.alfresco.repo.model.filefolder.traitextender.FileFolderServiceTrait;
 | |
| import org.alfresco.repo.node.getchildren.FilterProp;
 | |
| import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
 | |
| import org.alfresco.repo.policy.BehaviourFilter;
 | |
| import org.alfresco.repo.search.QueryParameterDefImpl;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationUtil;
 | |
| import org.alfresco.repo.security.permissions.PermissionCheckedCollection.PermissionCheckedCollectionMixin;
 | |
| import org.alfresco.repo.security.permissions.PermissionCheckedValue.PermissionCheckedValueMixin;
 | |
| import org.alfresco.service.Auditable;
 | |
| import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
 | |
| import org.alfresco.service.cmr.dictionary.DictionaryService;
 | |
| import org.alfresco.service.cmr.model.FileExistsException;
 | |
| import org.alfresco.service.cmr.model.FileFolderService;
 | |
| import org.alfresco.service.cmr.model.FileFolderServiceType;
 | |
| import org.alfresco.service.cmr.model.FileFolderUtil;
 | |
| import org.alfresco.service.cmr.model.FileInfo;
 | |
| import org.alfresco.service.cmr.model.FileNotFoundException;
 | |
| import org.alfresco.service.cmr.model.SubFolderFilter;
 | |
| import org.alfresco.service.cmr.repository.ChildAssociationRef;
 | |
| import org.alfresco.service.cmr.repository.ContentData;
 | |
| import org.alfresco.service.cmr.repository.ContentReader;
 | |
| import org.alfresco.service.cmr.repository.ContentService;
 | |
| import org.alfresco.service.cmr.repository.ContentWriter;
 | |
| import org.alfresco.service.cmr.repository.CopyService;
 | |
| import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
 | |
| import org.alfresco.service.cmr.repository.InvalidNodeRefException;
 | |
| import org.alfresco.service.cmr.repository.MimetypeService;
 | |
| import org.alfresco.service.cmr.repository.NodeRef;
 | |
| import org.alfresco.service.cmr.repository.NodeService;
 | |
| import org.alfresco.service.cmr.repository.Path;
 | |
| import org.alfresco.service.cmr.search.QueryParameterDefinition;
 | |
| import org.alfresco.service.cmr.search.SearchService;
 | |
| import org.alfresco.service.namespace.NamespaceService;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.traitextender.AJProxyTrait;
 | |
| import org.alfresco.traitextender.Extend;
 | |
| import org.alfresco.traitextender.ExtendedTrait;
 | |
| import org.alfresco.traitextender.Extensible;
 | |
| import org.alfresco.traitextender.Trait;
 | |
| import org.alfresco.util.FileFilterMode;
 | |
| import org.alfresco.util.FileFilterMode.Client;
 | |
| import org.alfresco.util.GUID;
 | |
| import org.alfresco.util.Pair;
 | |
| import org.alfresco.util.ParameterCheck;
 | |
| import org.alfresco.util.SearchLanguageConversion;
 | |
| import org.alfresco.util.registry.NamedObjectRegistry;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.springframework.extensions.surf.util.I18NUtil;
 | |
| 
 | |
| /**
 | |
|  * Implementation of the file/folder-specific service.
 | |
|  * 
 | |
|  * @author Derek Hulley
 | |
|  */
 | |
| public class FileFolderServiceImpl extends AbstractBaseCopyService implements FileFolderService, Extensible
 | |
| {
 | |
|     private static final String CANNED_QUERY_FILEFOLDER_LIST = "fileFolderGetChildrenCannedQueryFactory";
 | |
|     
 | |
|     /** Shallow search for files and folders with a name pattern */
 | |
|     private static final String XPATH_QUERY_SHALLOW_ALL =
 | |
|         "./*" +
 | |
|         "[like(@cm:name, $cm:name, false)" +
 | |
|         " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
 | |
|         " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" +
 | |
|         " or subtypeOf('" + ContentModel.TYPE_LINK + "'))]";
 | |
|     
 | |
|     /** Deep search for files and folders with a name pattern */
 | |
|     private static final String XPATH_QUERY_DEEP_ALL =
 | |
|         ".//*" +
 | |
|         "[like(@cm:name, $cm:name, false)" +
 | |
|         " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
 | |
|         " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" +
 | |
|         " or subtypeOf('" + ContentModel.TYPE_LINK + "'))]";
 | |
|     
 | |
|     /** Deep search for folders with a name pattern */
 | |
|     private static final String XPATH_QUERY_DEEP_FOLDERS =
 | |
|         ".//*" +
 | |
|         "[like(@cm:name, $cm:name, false)" +
 | |
|         " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
 | |
|         " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "'))]";
 | |
|     
 | |
|     /** Deep search for files with a name pattern */
 | |
|     private static final String XPATH_QUERY_DEEP_FILES =
 | |
|         ".//*" +
 | |
|         "[like(@cm:name, $cm:name, false)" +
 | |
|         " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
 | |
|         " and (subtypeOf('" + ContentModel.TYPE_CONTENT + "')" +
 | |
|         " or subtypeOf('" + ContentModel.TYPE_LINK + "'))]";
 | |
|        
 | |
|     private static Log logger = LogFactory.getLog(FileFolderServiceImpl.class);
 | |
|     
 | |
|     private HiddenAspect hiddenAspect;
 | |
|     private NamespaceService namespaceService;
 | |
|     private DictionaryService dictionaryService;
 | |
|     private NodeService nodeService;
 | |
|     private CopyService copyService;
 | |
|     private SearchService searchService;
 | |
|     private ContentService contentService;
 | |
|     private MimetypeService mimetypeService;
 | |
|     private BehaviourFilter behaviourFilter;
 | |
|     private NamedObjectRegistry<CannedQueryFactory<NodeRef>> cannedQueryRegistry;
 | |
| 
 | |
|     private boolean preserveAuditableData = true;
 | |
| 
 | |
|     // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
 | |
|     private List<String> systemPaths;
 | |
|     
 | |
|     // default cutoff - applies to list "all" methods
 | |
|     private int defaultListMaxResults = 5000;
 | |
| 
 | |
|     private final ExtendedTrait<FileFolderServiceTrait> fileFolderTrait;
 | |
|     
 | |
|     /**
 | |
|      * Default constructor
 | |
|      */
 | |
|     public FileFolderServiceImpl()
 | |
|     {
 | |
|         super();
 | |
|         
 | |
|         fileFolderTrait=new ExtendedTrait<FileFolderServiceTrait>(AJProxyTrait.create(createFileFolderTrait(),FileFolderServiceTrait.class));
 | |
|     }
 | |
| 
 | |
|     public void setNamespaceService(NamespaceService namespaceService)
 | |
|     {
 | |
|         this.namespaceService = namespaceService;
 | |
|     }
 | |
| 
 | |
|     public void setDictionaryService(DictionaryService dictionaryService)
 | |
|     {
 | |
|         this.dictionaryService = dictionaryService;
 | |
|     }
 | |
|     
 | |
|     public void setNodeService(NodeService nodeService)
 | |
|     {
 | |
|         this.nodeService = nodeService;
 | |
|     }
 | |
| 
 | |
|     public void setCopyService(CopyService copyService)
 | |
|     {
 | |
|         this.copyService = copyService;
 | |
|     }
 | |
| 
 | |
|     public void setSearchService(SearchService searchService)
 | |
|     {
 | |
|         this.searchService = searchService;
 | |
|     }
 | |
|     
 | |
|     public void setContentService(ContentService contentService)
 | |
|     {
 | |
|         this.contentService = contentService;
 | |
|     }
 | |
| 
 | |
|     public void setMimetypeService(MimetypeService mimetypeService)
 | |
|     {
 | |
|         this.mimetypeService = mimetypeService;
 | |
|     }
 | |
| 
 | |
|     public void setHiddenAspect(HiddenAspect hiddenAspect)
 | |
|     {
 | |
|         this.hiddenAspect = hiddenAspect;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set the registry of {@link CannedQueryFactory canned queries}
 | |
|      */
 | |
|     public void setCannedQueryRegistry(NamedObjectRegistry<CannedQueryFactory<NodeRef>> cannedQueryRegistry)
 | |
|     {
 | |
|         this.cannedQueryRegistry = cannedQueryRegistry;
 | |
|     }
 | |
| 
 | |
|     // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
 | |
|     public void setSystemPaths(List<String> systemPaths)
 | |
|     {
 | |
|         this.systemPaths = systemPaths;
 | |
|     }
 | |
|     
 | |
|     public void setDefaultListMaxResults(int defaultListMaxResults)
 | |
|     {
 | |
|         this.defaultListMaxResults = defaultListMaxResults;
 | |
|     }
 | |
| 
 | |
|     public void setBehaviourFilter(BehaviourFilter behaviourFilter)
 | |
|     {
 | |
|         this.behaviourFilter = behaviourFilter;
 | |
|     }
 | |
| 
 | |
|     public void setPreserveAuditableData(boolean preserveAuditableData)
 | |
|     {
 | |
|         this.preserveAuditableData = preserveAuditableData;
 | |
|     }
 | |
| 
 | |
|     public boolean isPreserveAuditableData()
 | |
|     {
 | |
|         return preserveAuditableData;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public void init()
 | |
|     {
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Helper method to convert node reference instances to file info
 | |
|      * 
 | |
|      * @param nodeRefs the node references
 | |
|      * @return Return a list of file info
 | |
|      * @throws InvalidTypeException if the node is not a valid type
 | |
|      */
 | |
|     private List<FileInfo> toFileInfo(List<NodeRef> nodeRefs) throws InvalidTypeException
 | |
|     {
 | |
|         List<FileInfo> results = new ArrayList<FileInfo>(nodeRefs.size());
 | |
|         for (NodeRef nodeRef : nodeRefs)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 FileInfo fileInfo = toFileInfo(nodeRef, true);
 | |
|                 results.add(fileInfo);
 | |
|             }
 | |
|             catch (InvalidNodeRefException inre)
 | |
|             {
 | |
|                 logger.warn("toFileInfo: "+inre);
 | |
|                 // skip
 | |
|             }
 | |
|         }
 | |
|         return results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Helper method to convert a node reference instance to a file info
 | |
|      */
 | |
|     private FileInfo toFileInfo(NodeRef nodeRef, boolean addTranslations) throws InvalidTypeException
 | |
|     {
 | |
|         // Get the file attributes
 | |
|         Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
 | |
|         // Is it a folder
 | |
|         QName typeQName = nodeService.getType(nodeRef);
 | |
|         
 | |
|         FileFolderServiceType type = getType(typeQName);
 | |
|        
 | |
|         boolean isFolder = type.equals(FileFolderServiceType.FOLDER);
 | |
|         boolean isHidden = false;
 | |
| 
 | |
|         Client client = FileFilterMode.getClient();
 | |
|         if(hiddenAspect.getVisibility(client, nodeRef) == Visibility.HiddenAttribute)
 | |
|         {
 | |
|             isHidden = true;
 | |
|         }
 | |
| 
 | |
|         // Construct the file info and add to the results
 | |
|         FileInfo fileInfo = new FileInfoImpl(nodeRef, typeQName, isFolder, isHidden, properties);
 | |
| 
 | |
|         // Done
 | |
|         return fileInfo;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public List<FileInfo> toFileInfoList(List<NodeRef> nodeRefs)
 | |
|     {
 | |
|         List<FileInfo> fileInfos = new LinkedList<FileInfo>();
 | |
|         for (NodeRef nodeRef : nodeRefs)
 | |
|         {
 | |
|             // Use service proxy
 | |
|             FileInfo fileInfo = this.getFileInfo(nodeRef);
 | |
|             if (fileInfo != null)
 | |
|             {
 | |
|                 fileInfos.add(fileInfo);
 | |
|             }
 | |
|         }
 | |
|         return fileInfos;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Exception when the type is not a valid File or Folder type
 | |
|      * 
 | |
|      * @see ContentModel#TYPE_CONTENT
 | |
|      * @see ContentModel#TYPE_FOLDER
 | |
|      * 
 | |
|      * @author Derek Hulley
 | |
|      */
 | |
|     public static class InvalidTypeException extends RuntimeException
 | |
|     {
 | |
|         private static final long serialVersionUID = -310101369475434280L;
 | |
|         
 | |
|         public InvalidTypeException(String msg)
 | |
|         {
 | |
|             super(msg);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public boolean exists(NodeRef nodeRef)
 | |
|     {
 | |
|         return nodeService.exists(nodeRef);
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public FileFolderServiceType getType(QName typeQName)
 | |
|     {
 | |
|         if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_FOLDER))
 | |
|         {
 | |
|             if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_SYSTEM_FOLDER))
 | |
|             {
 | |
|                 return FileFolderServiceType.SYSTEM_FOLDER;
 | |
|             }
 | |
|             return FileFolderServiceType.FOLDER;
 | |
|         }
 | |
|         else if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT) ||
 | |
|                 dictionaryService.isSubClass(typeQName, ContentModel.TYPE_LINK))
 | |
|         {
 | |
|             // it is a regular file
 | |
|             return FileFolderServiceType.FILE;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // unhandled type
 | |
|             return FileFolderServiceType.INVALID;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public List<FileInfo> list(NodeRef contextNodeRef)
 | |
|     {
 | |
|         // execute the query
 | |
|         List<FileInfo> results = listSimple(contextNodeRef, true, true);
 | |
|         // done
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace("List files and folders: \n" +
 | |
|                     "   context: " + contextNodeRef + "\n" +
 | |
|                     "   results: " + results);
 | |
|         }
 | |
|         return results;
 | |
|     }
 | |
|     
 | |
|     private PagingResults<FileInfo> getPagingResults(PagingRequest pagingRequest, final CannedQueryResults<NodeRef> results)
 | |
|     {
 | |
|         List<NodeRef> nodeRefs = null;
 | |
|         if (results.getPageCount() > 0)
 | |
|         {
 | |
|             nodeRefs = results.getPages().get(0);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             nodeRefs = Collections.emptyList();
 | |
|         }
 | |
|         
 | |
|         // set total count
 | |
|         final Pair<Integer, Integer> totalCount;
 | |
|         if (pagingRequest.getRequestTotalCountMax() > 0)
 | |
|         {
 | |
|             totalCount = results.getTotalResultCount();
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             totalCount = null;
 | |
|         }
 | |
|         
 | |
|         final List<FileInfo> nodeInfos = new ArrayList<FileInfo>(nodeRefs.size());
 | |
|         for (NodeRef nodeRef : nodeRefs)
 | |
|         {
 | |
|             nodeInfos.add(toFileInfo(nodeRef, true));
 | |
|         }
 | |
|         PermissionCheckedCollectionMixin.create(nodeInfos, nodeRefs);
 | |
|         
 | |
|         return new PagingResults<FileInfo>()
 | |
|         {
 | |
|             @Override
 | |
|             public String getQueryExecutionId()
 | |
|             {
 | |
|                 return results.getQueryExecutionId();
 | |
|             }
 | |
|             @Override
 | |
|             public List<FileInfo> getPage()
 | |
|             {
 | |
|                 return nodeInfos;
 | |
|             }
 | |
|             @Override
 | |
|             public boolean hasMoreItems()
 | |
|             {
 | |
|                 return results.hasMoreItems();
 | |
|             }
 | |
|             @Override
 | |
|             public Pair<Integer, Integer> getTotalResultCount()
 | |
|             {
 | |
|                 return totalCount;
 | |
|             }
 | |
|         };        
 | |
|     }
 | |
| 
 | |
|     /* (non-Javadoc)
 | |
|      * @see org.alfresco.service.cmr.model.FileFolderService#list(org.alfresco.service.cmr.repository.NodeRef, boolean, boolean, java.util.Set, org.alfresco.service.cmr.model.PagingSortRequest)
 | |
|      */
 | |
|     @Auditable(parameters = {"contextNodeRef", "files", "folders", "ignoreQNames", "sortProps", "pagingRequest"})
 | |
|     @Override
 | |
|     @Extend(traitAPI = FileFolderServiceTrait.class, extensionAPI = FileFolderServiceExtension.class)
 | |
|     public PagingResults<FileInfo> list(NodeRef contextNodeRef,
 | |
|                                       boolean files,
 | |
|                                       boolean folders,
 | |
|                                       Set<QName> ignoreQNames,
 | |
|                                       List<Pair<QName, Boolean>> sortProps,
 | |
|                                       PagingRequest pagingRequest)
 | |
|     {
 | |
|         ParameterCheck.mandatory("contextNodeRef", contextNodeRef);
 | |
|         ParameterCheck.mandatory("pagingRequest", pagingRequest);
 | |
|         
 | |
|         Pair<Set<QName>,Set<QName>> pair = buildSearchTypesAndIgnoreAspects(files, folders, ignoreQNames);
 | |
|         Set<QName> searchTypeQNames = pair.getFirst();
 | |
|         Set<QName> ignoreAspectQNames = pair.getSecond();
 | |
|         
 | |
|         // execute query
 | |
|         final CannedQueryResults<NodeRef> results = listImpl(contextNodeRef, null, searchTypeQNames, ignoreAspectQNames, sortProps, pagingRequest);
 | |
|         return getPagingResults(pagingRequest, results);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public PagingResults<FileInfo> list(NodeRef contextNodeRef, boolean files, boolean folders, String pattern, Set<QName> ignoreQNames, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
 | |
|     {
 | |
|         ParameterCheck.mandatory("contextNodeRef", contextNodeRef);
 | |
|         ParameterCheck.mandatory("pagingRequest", pagingRequest);
 | |
|         
 | |
|         Pair<Set<QName>,Set<QName>> pair = buildSearchTypesAndIgnoreAspects(files, folders, ignoreQNames);
 | |
|         Set<QName> searchTypeQNames = pair.getFirst();
 | |
|         Set<QName> ignoreAspectQNames = pair.getSecond();
 | |
|         
 | |
|         // execute query
 | |
|         final CannedQueryResults<NodeRef> results = listImpl(contextNodeRef, pattern, searchTypeQNames, ignoreAspectQNames, sortProps, pagingRequest);
 | |
|         return getPagingResults(pagingRequest, results);
 | |
|     }
 | |
|     
 | |
|    
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public PagingResults<FileInfo> list(NodeRef rootNodeRef, Set<QName> searchTypeQNames, Set<QName> ignoreAspectQNames, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
 | |
|     {
 | |
|         CannedQueryResults<NodeRef> results = listImpl(rootNodeRef, null,  searchTypeQNames, ignoreAspectQNames, sortProps, pagingRequest);
 | |
|         return getPagingResults(pagingRequest, results);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public PagingResults<FileInfo> list(NodeRef rootNodeRef,
 | |
|                                         Set<QName> assocTypeQNames,
 | |
|                                         Set<QName> searchTypeQNames,
 | |
|                                         Set<QName> ignoreAspectQNames,
 | |
|                                         List<Pair<QName, Boolean>> sortProps,
 | |
|                                         List<FilterProp> filterProps,
 | |
|                                         PagingRequest pagingRequest)
 | |
|     {
 | |
|         CannedQueryResults<NodeRef> results = listImpl(rootNodeRef, null,  assocTypeQNames, searchTypeQNames, ignoreAspectQNames, sortProps, filterProps, pagingRequest);
 | |
|         return getPagingResults(pagingRequest, results);
 | |
|     }
 | |
|     
 | |
|     private CannedQueryResults<NodeRef> listImpl(NodeRef contextNodeRef, boolean files, boolean folders)
 | |
|     {
 | |
|         Set<QName> searchTypeQNames = buildSearchTypesAndIgnoreAspects(files, folders, null).getFirst();
 | |
|         return listImpl(contextNodeRef, searchTypeQNames);
 | |
|     }
 | |
|     
 | |
|     private CannedQueryResults<NodeRef> listImpl(NodeRef contextNodeRef, Set<QName> searchTypeQNames)
 | |
|     {
 | |
|         return listImpl(contextNodeRef, null, searchTypeQNames, null, null, new PagingRequest(defaultListMaxResults, null));
 | |
|     }
 | |
|     
 | |
|     // note: similar to getChildAssocs(contextNodeRef, searchTypeQNames) but enables paging features, including max items, sorting etc (with permissions per-applied)
 | |
| 
 | |
|     /**
 | |
|      *
 | |
|      * @param contextNodeRef
 | |
|      * @param pattern
 | |
|      * @param searchTypeQNames
 | |
|      * @param ignoreAspectQNames
 | |
|      * @param sortProps
 | |
|      * @param pagingRequest
 | |
|      * @return
 | |
|      */
 | |
|     private CannedQueryResults<NodeRef> listImpl(NodeRef contextNodeRef, String pattern,  Set<QName> searchTypeQNames, Set<QName> ignoreAspectQNames, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
 | |
|     {
 | |
|         return listImpl(contextNodeRef, pattern, Collections.singleton(ContentModel.ASSOC_CONTAINS), searchTypeQNames, ignoreAspectQNames, sortProps, null, pagingRequest);
 | |
|     }
 | |
| 
 | |
|     private CannedQueryResults<NodeRef> listImpl(NodeRef contextNodeRef, String pattern, Set<QName> assocTypeQNames, Set<QName> searchTypeQNames, Set<QName> ignoreAspectQNames,
 | |
|                                                  List<Pair<QName, Boolean>> sortProps, List<FilterProp> filterProps, PagingRequest pagingRequest)
 | |
|     {
 | |
|         Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
 | |
|         
 | |
|         // get canned query
 | |
|         GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_FILEFOLDER_LIST);
 | |
| 
 | |
|         GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, pattern, assocTypeQNames, searchTypeQNames, ignoreAspectQNames, filterProps, sortProps, pagingRequest);
 | |
| 
 | |
|         // execute canned query
 | |
|         CannedQueryResults<NodeRef> results = cq.execute();
 | |
|         
 | |
|         if (start != null)
 | |
|         {
 | |
|             int cnt = results.getPagedResultCount();
 | |
|             int skipCount = pagingRequest.getSkipCount();
 | |
|             int maxItems = pagingRequest.getMaxItems();
 | |
|             boolean hasMoreItems = results.hasMoreItems();
 | |
|             Pair<Integer, Integer> totalCount = (pagingRequest.getRequestTotalCountMax() > 0 ? results.getTotalResultCount() : null);
 | |
|             int pageNum = (skipCount / maxItems) + 1;
 | |
|             
 | |
|             logger.debug("List: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs [pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+",totalCount="+totalCount+",parentNodeRef="+contextNodeRef+"]");
 | |
|         }
 | |
|         
 | |
|         return results;
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public List<FileInfo> listFiles(NodeRef contextNodeRef)
 | |
|     {
 | |
|         // execute the query
 | |
|         List<FileInfo> results = listSimple(contextNodeRef, true, false);
 | |
|         // done
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace("List files: \n" +
 | |
|                     "   context: " + contextNodeRef + "\n" +
 | |
|                     "   results: " + results);
 | |
|         }
 | |
|         return results;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public List<FileInfo> listFolders(NodeRef contextNodeRef)
 | |
|     {
 | |
|         // execute the query
 | |
|         List<FileInfo> results = listSimple(contextNodeRef, false, true);
 | |
|         // done
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace("List for folders: \n" +
 | |
|                     "   context: " + contextNodeRef + "\n" +
 | |
|                     "   results: " + results);
 | |
|         }
 | |
|         return results;
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public List<FileInfo> listDeepFolders(NodeRef contextNodeRef, SubFolderFilter filter)
 | |
|     {
 | |
|         List<NodeRef> nodeRefs = listSimpleDeep(contextNodeRef, false, true, filter);
 | |
|         
 | |
|         List<FileInfo> results = toFileInfo(nodeRefs);
 | |
|         
 | |
|         // done
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace("Deep search for files: \n" +
 | |
|                     "   context: " + contextNodeRef + "\n" +
 | |
|                     "   results: " + results.size());
 | |
|         }
 | |
|         return results;
 | |
|         
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public NodeRef getLocalizedSibling(NodeRef nodeRef)
 | |
|     {
 | |
|         Locale userLocale = I18NUtil.getLocale();
 | |
|         
 | |
|         String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
 | |
|         NodeRef parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef();
 | |
|         // Work out the base name we are working with
 | |
|         Pair<String, String> split = getExtension(name, false);
 | |
|         String base = split.getFirst();
 | |
|         String ext = split.getSecond();
 | |
|         
 | |
|         NodeRef resultNodeRef = nodeRef;
 | |
|         // Search for siblings with the same name
 | |
|         Control resourceHelper = Control.getControl(Control.FORMAT_DEFAULT);
 | |
|         List<Locale> candidateLocales = resourceHelper.getCandidateLocales(base, userLocale);
 | |
|         for (Locale candidateLocale : candidateLocales)
 | |
|         {
 | |
|             String filename = resourceHelper.toBundleName(base, candidateLocale) + "." + ext;
 | |
|             // Attempt to find the file
 | |
|             NodeRef foundNodeRef = searchSimple(parentNodeRef, filename);
 | |
|             if (foundNodeRef != null)   // TODO: Check for read permissions
 | |
|             {
 | |
|                 resultNodeRef = foundNodeRef;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         // Done
 | |
|         return resultNodeRef;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public NodeRef searchSimple(NodeRef contextNodeRef, String name)
 | |
|     {
 | |
|         ParameterCheck.mandatory("name", name);
 | |
|         ParameterCheck.mandatory("contextNodeRef", contextNodeRef);
 | |
|         
 | |
|         NodeRef childNodeRef = nodeService.getChildByName(contextNodeRef, ContentModel.ASSOC_CONTAINS, name);
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace(
 | |
|                     "Simple name search results: \n" +
 | |
|                     "   parent: " + contextNodeRef + "\n" +
 | |
|                     "   name: " + name + "\n" +
 | |
|                     "   result: " + childNodeRef);
 | |
|         }
 | |
|         return childNodeRef;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * @see #search(NodeRef, String, boolean, boolean, boolean)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public List<FileInfo> search(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders)
 | |
|     {
 | |
|         return search(contextNodeRef, namePattern, true, true, includeSubFolders);
 | |
|     }
 | |
| 
 | |
|     private static final String LUCENE_MULTI_CHAR_WILDCARD = "*";
 | |
|     
 | |
|     /**
 | |
|      * Full search with all options
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public List<FileInfo> search(
 | |
|             NodeRef contextNodeRef,
 | |
|             String namePattern,
 | |
|             boolean fileSearch,
 | |
|             boolean folderSearch,
 | |
|             boolean includeSubFolders)
 | |
|     {
 | |
|         // get the raw nodeRefs
 | |
|         List<NodeRef> nodeRefs = searchInternal(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders);
 | |
|         
 | |
|         List<FileInfo> results = toFileInfo(nodeRefs);
 | |
|         
 | |
|         // eliminate unwanted files/folders
 | |
|         Iterator<FileInfo> iterator = results.iterator(); 
 | |
|         while (iterator.hasNext())
 | |
|         {
 | |
|             FileInfo file = iterator.next();
 | |
|             if (file.isFolder() && !folderSearch)
 | |
|             {
 | |
|                 iterator.remove();
 | |
|             }
 | |
|             else if (!file.isFolder() && !fileSearch)
 | |
|             {
 | |
|                 iterator.remove();
 | |
|             }
 | |
|         }
 | |
|         // done
 | |
|         if (logger.isTraceEnabled())
 | |
|         {
 | |
|             logger.trace("Deep search: \n" +
 | |
|                     "   context: " + contextNodeRef + "\n" +
 | |
|                     "   pattern: " + namePattern + "\n" +
 | |
|                     "   files: " + fileSearch + "\n" +
 | |
|                     "   folders: " + folderSearch + "\n" +
 | |
|                     "   deep: " + includeSubFolders + "\n" +
 | |
|                     "   results: " + results);
 | |
|         }
 | |
|         return results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Performs a full search, but doesn't translate the node references into
 | |
|      * file info objects.  This allows {@link #checkExists(NodeRef, String)} to
 | |
|      * bypass the retrieval of node properties.
 | |
|      */
 | |
|     private List<NodeRef> searchInternal(
 | |
|             NodeRef contextNodeRef,
 | |
|             String namePattern,
 | |
|             boolean fileSearch,
 | |
|             boolean folderSearch,
 | |
|             boolean includeSubFolders)
 | |
|     {
 | |
|         // shortcut if the search is requesting nothing
 | |
|         if (!fileSearch && !folderSearch)
 | |
|         {
 | |
|             return Collections.emptyList();
 | |
|         }
 | |
|         
 | |
|         if (namePattern == null)
 | |
|         {
 | |
|             namePattern = LUCENE_MULTI_CHAR_WILDCARD;      // default to wildcard
 | |
|         }
 | |
|         // now check if we can use Lucene to handle this query
 | |
|         boolean anyName = namePattern.equals(LUCENE_MULTI_CHAR_WILDCARD);
 | |
|         
 | |
|         List<NodeRef> nodeRefs = null;
 | |
|         if (anyName)
 | |
|         {
 | |
|             // This is search for any name
 | |
|             if(includeSubFolders)
 | |
|             {
 | |
|                nodeRefs = listSimpleDeep(contextNodeRef, fileSearch, folderSearch, null);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 nodeRefs = listImpl(contextNodeRef, fileSearch, folderSearch).getPage();
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // TODO - we need to get rid of this xpath stuff
 | |
|             // if the name pattern is null, then we use the ANY pattern
 | |
|             QueryParameterDefinition[] params = null;
 | |
|             if (namePattern != null)
 | |
|             {
 | |
|                 // the interface specifies the Lucene syntax, so perform a conversion
 | |
|                 namePattern = SearchLanguageConversion.convert(
 | |
|                         SearchLanguageConversion.DEF_LUCENE,
 | |
|                         SearchLanguageConversion.DEF_XPATH_LIKE,
 | |
|                         namePattern);
 | |
|                 
 | |
|                 params = new QueryParameterDefinition[1];
 | |
|                 params[0] = new QueryParameterDefImpl(
 | |
|                         ContentModel.PROP_NAME,
 | |
|                         dictionaryService.getDataType(DataTypeDefinition.TEXT),
 | |
|                         true,
 | |
|                         namePattern);
 | |
|             }
 | |
|             // determine the correct query to use
 | |
|             String query = null;
 | |
|             if (includeSubFolders)
 | |
|             {
 | |
|                 // this is a deep search
 | |
|                 if(!fileSearch && folderSearch)
 | |
|                 {
 | |
|                     // This is a folder search only;
 | |
|                     query = XPATH_QUERY_DEEP_FOLDERS;
 | |
|                 }
 | |
|                 else if(fileSearch && !folderSearch)
 | |
|                 {
 | |
|                     // This is a folder search only;
 | |
|                     query = XPATH_QUERY_DEEP_FILES;
 | |
|                 }
 | |
|                 else
 | |
|                 {        
 | |
|                     query = XPATH_QUERY_DEEP_ALL;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // this is a shallow search
 | |
|                 query = XPATH_QUERY_SHALLOW_ALL;
 | |
|             }
 | |
|             // execute the query
 | |
|             nodeRefs = searchService.selectNodes(
 | |
|                     contextNodeRef,
 | |
|                     query,
 | |
|                     params,
 | |
|                     namespaceService,
 | |
|                     false);
 | |
|         }
 | |
|         // done
 | |
|         return nodeRefs;
 | |
|     }
 | |
|     
 | |
|     private List<FileInfo> listSimple(NodeRef contextNodeRef, boolean files, boolean folders) throws InvalidTypeException
 | |
|     {
 | |
|         CannedQueryResults<NodeRef> cq = listImpl(contextNodeRef, files, folders);
 | |
|         List<NodeRef> nodeRefs = cq.getPage();
 | |
|         
 | |
|         List<FileInfo> results = toFileInfo(nodeRefs);
 | |
|         
 | |
|         // avoid re-applying permissions (for "list" canned queries)
 | |
|         return PermissionCheckedValueMixin.create(results);
 | |
|     }
 | |
|     
 | |
|     private Pair<Set<QName>, Set<QName>> buildSearchTypesAndIgnoreAspects(boolean files, boolean folders, Set<QName> ignoreQNameTypes)
 | |
|     {
 | |
|         Set<QName> searchTypeQNames = new HashSet<QName>(100);
 | |
|         Set<QName> ignoreAspectQNames = null;
 | |
|         
 | |
|         // Build a list of file and folder types
 | |
|         if (folders)
 | |
|         {
 | |
|             searchTypeQNames.addAll(buildFolderTypes());
 | |
|         }
 | |
|         if (files)
 | |
|         {
 | |
|             searchTypeQNames.addAll(buildFileTypes());
 | |
|         }
 | |
|         
 | |
|         if (ignoreQNameTypes != null)
 | |
|         {
 | |
|             Set<QName> ignoreQNamesNotSearchTypes = new HashSet<QName>(ignoreQNameTypes);
 | |
|             ignoreQNamesNotSearchTypes.removeAll(searchTypeQNames);
 | |
|             ignoreQNamesNotSearchTypes.remove(ContentModel.TYPE_SYSTEM_FOLDER); // note: not included in buildFolderTypes()
 | |
|             
 | |
|             if (ignoreQNamesNotSearchTypes.size() > 0)
 | |
|             {
 | |
|                 ignoreAspectQNames = getAspectsToIgnore(ignoreQNamesNotSearchTypes);
 | |
|             }
 | |
|             
 | |
|             searchTypeQNames.removeAll(ignoreQNameTypes);
 | |
|         }
 | |
|         
 | |
|         return new Pair<Set<QName>, Set<QName>>(searchTypeQNames, ignoreAspectQNames);
 | |
|     }
 | |
|     
 | |
|     private Set<QName> getAspectsToIgnore(Set<QName> ignoreQNames)
 | |
|     {
 | |
|         Set<QName> ignoreQNameAspects = new HashSet<QName>(ignoreQNames.size());
 | |
|         for (QName qname : ignoreQNames)
 | |
|         {
 | |
|             if (dictionaryService.getAspect(qname) != null)
 | |
|             {
 | |
|                 ignoreQNameAspects.add(qname);
 | |
|             }
 | |
|         }
 | |
|         return ignoreQNameAspects;
 | |
|     }
 | |
|     
 | |
|     private Set<QName> buildFolderTypes()
 | |
|     {
 | |
|         Set<QName> folderTypeQNames = new HashSet<QName>(50);
 | |
|         
 | |
|         // Build a list of folder types
 | |
|         Collection<QName> qnames = dictionaryService.getSubTypes(ContentModel.TYPE_FOLDER, true);
 | |
|         folderTypeQNames.addAll(qnames);
 | |
|         folderTypeQNames.add(ContentModel.TYPE_FOLDER);
 | |
|         
 | |
|         // Remove 'system' folders
 | |
|         qnames = dictionaryService.getSubTypes(ContentModel.TYPE_SYSTEM_FOLDER, true);
 | |
|         folderTypeQNames.removeAll(qnames);
 | |
|         folderTypeQNames.remove(ContentModel.TYPE_SYSTEM_FOLDER);
 | |
|         
 | |
|         return folderTypeQNames;
 | |
|     }
 | |
|     
 | |
|     private Set<QName> buildFileTypes()
 | |
|     {
 | |
|         Set<QName> fileTypeQNames = new HashSet<QName>(50);
 | |
|         
 | |
|         // Build a list of file types
 | |
|         Collection<QName> qnames = dictionaryService.getSubTypes(ContentModel.TYPE_CONTENT, true);
 | |
|         fileTypeQNames.addAll(qnames);
 | |
|         fileTypeQNames.add(ContentModel.TYPE_CONTENT);
 | |
|         qnames = dictionaryService.getSubTypes(ContentModel.TYPE_LINK, true);
 | |
|         fileTypeQNames.addAll(qnames);
 | |
|         fileTypeQNames.add(ContentModel.TYPE_LINK);
 | |
|         
 | |
|         return fileTypeQNames;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * A deep version of listSimple.   Which recursively walks down the tree from a given starting point, returning 
 | |
|      * the node refs of files or folders found along the way.
 | |
|      * <p>
 | |
|      * The folder filter is called for each sub-folder to determine whether to search in that sub-folder, should a subfolder be excluded 
 | |
|      * then all its chidren are excluded as well.
 | |
|      * 
 | |
|      * @param contextNodeRef the starting point.
 | |
|      * @param files return nodes of type files.
 | |
|      * @param folders return nodes of type folders.
 | |
|      * @param folderFilter filter controls which folders to search.  If null then all subfolders are searched.
 | |
|      * @return list of node references
 | |
|      */
 | |
|    /* <p>
 | |
|     * MER: I've added this rather than changing listSimple to minimise the risk of breaking 
 | |
|     * the existing code.   This is a quick performance improvement between using 
 | |
|     * XPath which is awful or adding new methods to the NodeService/DB   This is also a dangerous method in that it can return a 
 | |
|     * lot of data and take a long time.
 | |
|     */  
 | |
|     private List<NodeRef> listSimpleDeep(NodeRef contextNodeRef, boolean files, boolean folders, SubFolderFilter folderFilter)
 | |
|     {
 | |
|         if(logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("searchSimpleDeep contextNodeRef:" + contextNodeRef);
 | |
|         }
 | |
| 
 | |
|         // To hold the results.
 | |
|         List<NodeRef> result = new ArrayList<NodeRef>();
 | |
|         
 | |
|         // Build a list of folder types
 | |
|         Set<QName> folderTypeQNames = buildFolderTypes();
 | |
|         Set<QName> fileTypeQNames = (files ? buildFileTypes() : new HashSet<QName>(0));
 | |
|         
 | |
|         if(!folders && !files)
 | |
|         {
 | |
|             return Collections.emptyList();
 | |
|             
 | |
|         }
 | |
|         
 | |
|         // Shortcut
 | |
|         if (folderTypeQNames.size() == 0)
 | |
|         {
 | |
|             return Collections.emptyList();
 | |
|         }
 | |
|         
 | |
|         Stack<NodeRef> toSearch = new Stack<NodeRef>();
 | |
|         toSearch.push(contextNodeRef);
 | |
|         
 | |
|         // Now we need to walk down the folders.
 | |
|         while(!toSearch.empty())
 | |
|         {
 | |
|             NodeRef currentDir = toSearch.pop();
 | |
|             
 | |
|             List<ChildAssociationRef> folderAssocRefs = nodeService.getChildAssocs(currentDir, folderTypeQNames);
 | |
|             
 | |
|             for (ChildAssociationRef folderRef : folderAssocRefs)
 | |
|             {
 | |
|                 // We have some child folders
 | |
|                 boolean include = true;
 | |
|                 if(folderFilter != null)
 | |
|                 {
 | |
|                     include = folderFilter.isEnterSubfolder(folderRef);
 | |
|                     if(include)
 | |
|                     {
 | |
|                         // yes search in these subfolders
 | |
|                         toSearch.push(folderRef.getChildRef());
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // No filter - Add the folders in the currentDir
 | |
|                     toSearch.push(folderRef.getChildRef());
 | |
|                 }
 | |
|                 
 | |
|                 if(folders && include)
 | |
|                 {
 | |
|                     result.add(folderRef.getChildRef());
 | |
|                 }
 | |
|             }
 | |
|                 
 | |
|             if(files)
 | |
|             {
 | |
|                 // Add the files in the current dir
 | |
|                 List<ChildAssociationRef> fileAssocRefs = nodeService.getChildAssocs(currentDir, fileTypeQNames);
 | |
|                 for (ChildAssociationRef fileRef : fileAssocRefs)
 | |
|                 {
 | |
|                     result.add(fileRef.getChildRef());
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
| 
 | |
|         if(logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("searchSimpleDeep finished size:" + result.size());
 | |
|         }
 | |
|  
 | |
|         // Done
 | |
|         return result;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * @see #move(NodeRef, NodeRef, String)
 | |
|      */
 | |
|     @Override
 | |
|     @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
 | |
|     public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         return moveOrCopy(sourceNodeRef, null, null, newName, true);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
 | |
|      */
 | |
|     @Override
 | |
|     public FileInfo move(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, true);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
 | |
|      */
 | |
|     @Override
 | |
|     public FileInfo moveFrom(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         return moveOrCopy(sourceNodeRef, sourceParentRef, targetParentRef, newName, true);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * @deprecated
 | |
|      */
 | |
|     @Override
 | |
|     public FileInfo move(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         return moveOrCopy(sourceNodeRef, sourceParentRef, targetParentRef, newName, true);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
 | |
|      */
 | |
|     @Override
 | |
|     public FileInfo copy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, false);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Implements both move and copy behaviour
 | |
|      * 
 | |
|      * @param move true to move, otherwise false to copy
 | |
|      */
 | |
|     private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
 | |
|     {
 | |
|         // get file/folder in its current state
 | |
|         FileInfo beforeFileInfo = toFileInfo(sourceNodeRef, true);
 | |
|         // check the name - null means keep the existing name
 | |
|         if (newName == null)
 | |
|         {
 | |
|             newName = beforeFileInfo.getName();
 | |
|         }
 | |
| 
 | |
|         boolean nameChanged = (newName.equals(beforeFileInfo.getName()) == false);
 | |
| 
 | |
|         AssociationCopyInfo targetInfo = getAssociationCopyInfo(nodeService, sourceNodeRef, sourceParentRef, newName, nameChanged);
 | |
|         QName qname = targetInfo.getTargetAssocQName();
 | |
|         boolean isPrimaryParent = targetInfo.getSourceParentAssoc().isPrimary();
 | |
|         ChildAssociationRef assocRef = targetInfo.getSourceParentAssoc();
 | |
| 
 | |
|         if (targetParentRef == null)
 | |
|         {
 | |
|             targetParentRef = assocRef.getParentRef();
 | |
|         }
 | |
|         
 | |
|         boolean changedParent = !targetParentRef.equals(assocRef.getParentRef());
 | |
|         // there is nothing to do if both the name and parent folder haven't changed
 | |
|         if (!nameChanged && !changedParent)
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|             {
 | |
|                 logger.debug("Doing nothing - neither filename or parent has changed: \n" +
 | |
|                         "   parent: " + targetParentRef + "\n" +
 | |
|                         "   before: " + beforeFileInfo + "\n" +
 | |
|                         "   new name: " + newName);
 | |
|             }
 | |
|             return beforeFileInfo;
 | |
|         }
 | |
|         
 | |
|         QName targetParentType = nodeService.getType(targetParentRef);
 | |
|         
 | |
|         // Fix AWC-1517 & ALF-5569
 | |
|         QName assocTypeQname = null;
 | |
|         if (nameChanged && move)
 | |
|         {
 | |
|             // if it's a rename use the existing assoc type
 | |
|             assocTypeQname = assocRef.getTypeQName();
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_FOLDER))
 | |
|             {
 | |
|                 assocTypeQname = ContentModel.ASSOC_CONTAINS; // cm:folder -> cm:contains
 | |
|             }
 | |
|             else if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_CONTAINER))
 | |
|             {
 | |
|                 assocTypeQname = ContentModel.ASSOC_CHILDREN; // sys:container -> sys:children
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 throw new InvalidTypeException("Unexpected type (" + targetParentType + ") for target parent: " + targetParentRef);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // move or copy
 | |
|         NodeRef targetNodeRef = null;
 | |
|         if (move)
 | |
|         {
 | |
|             // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
 | |
|             if (!isSystemPath(sourceNodeRef))
 | |
|             {
 | |
|                 // The cm:name might clash with another node in the target location.
 | |
|                 if (nameChanged)
 | |
|                 {
 | |
|                     // The name will be changing, so we really need to set the node's name to the new
 | |
|                     // name.  This can't be done at the same time as the move - to avoid incorrect violations
 | |
|                     // of the name constraints, the cm:name is set to something random and will be reset
 | |
|                     // to the correct name later.
 | |
|                     nodeService.setProperty(sourceNodeRef, ContentModel.PROP_NAME, GUID.generate());
 | |
|                 }
 | |
|                 try
 | |
|                 {
 | |
|                     ChildAssociationRef newAssocRef = null;
 | |
| 
 | |
|                     if (isPrimaryParent)
 | |
|                     {
 | |
|                         // move the node so that the association moves as well
 | |
|                         boolean auditableBehaviorWasDisabled = preserveAuditableData && behaviourFilter.isEnabled(ContentModel.ASPECT_AUDITABLE);
 | |
|                         if (auditableBehaviorWasDisabled)
 | |
|                         {
 | |
|                             behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
 | |
|                         }
 | |
| 
 | |
|                         try
 | |
|                         {
 | |
|                             newAssocRef = nodeService.moveNode(sourceNodeRef, targetParentRef, assocTypeQname, qname);
 | |
|                         }
 | |
|                         finally
 | |
|                         {
 | |
|                             if (auditableBehaviorWasDisabled)
 | |
|                             {
 | |
|                                 behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         nodeService.removeChild(sourceParentRef, sourceNodeRef);
 | |
|                         newAssocRef = nodeService.addChild(targetParentRef, sourceNodeRef, assocRef.getTypeQName(), assocRef.getQName());
 | |
|                     }
 | |
| 
 | |
|                     targetNodeRef = newAssocRef.getChildRef();
 | |
|                 }
 | |
|                 catch (DuplicateChildNodeNameException e)
 | |
|                 {
 | |
|                     throw new FileExistsException(targetParentRef, newName);
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // system path folders do not need to be moved
 | |
|                 targetNodeRef = sourceNodeRef;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // Check if during copy top level name will be changed to some new
 | |
|             String newNameAfterCopy = copyService.getTopLevelNodeNewName(sourceNodeRef, targetParentRef, assocTypeQname, qname);
 | |
|             if (newNameAfterCopy != null && !newNameAfterCopy.equals(newName))
 | |
|             {
 | |
|                 newName = newNameAfterCopy;
 | |
|                 qname = QName.createQName(
 | |
|                         assocRef.getQName().getNamespaceURI(),
 | |
|                         QName.createValidLocalName(newNameAfterCopy));
 | |
|             }
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 // Copy the node.  The cm:name will be dropped and reset later.
 | |
|                 targetNodeRef = copyService.copy(
 | |
|                         sourceNodeRef,
 | |
|                         targetParentRef,
 | |
|                         assocTypeQname,
 | |
|                         qname,
 | |
|                         true);
 | |
|             }
 | |
|             catch (DuplicateChildNodeNameException e)
 | |
|             {
 | |
|                 throw new FileExistsException(targetParentRef, newName);
 | |
|             }
 | |
|         }
 | |
|        
 | |
|         // Only update the name if it has changed
 | |
|         String currentName = (String)nodeService.getProperty(targetNodeRef, ContentModel.PROP_NAME);
 | |
|         
 | |
|         // ALF-13949: WorkingCopyAspect intentionally generates new names for all copies of working copies (which no
 | |
|         // longer have the working copy aspect) so leave these alone after copy
 | |
|         if (!currentName.equals(newName) && (move || !nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_WORKING_COPY)))
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 // changed the name property
 | |
|                 nodeService.setProperty(targetNodeRef, ContentModel.PROP_NAME, newName);
 | |
|                 
 | |
|                 // May need to update the mimetype, to support apps using .tmp files when saving
 | |
|                 ContentData contentData = (ContentData)nodeService.getProperty(targetNodeRef, ContentModel.PROP_CONTENT);
 | |
| 
 | |
|                 // Check the newName and oldName extensions.
 | |
|                 // Keep previous mimetype if
 | |
|                 //      1. new extension is empty
 | |
|                 //      2. new extension is '.tmp'
 | |
|                 //      3. extension was not changed,
 | |
|                 // 
 | |
|                 // It fixes the ETWOTWO-16 issue.
 | |
|                 String oldExt = getExtension(beforeFileInfo.getName(), true).getSecond();
 | |
|                 String newExt = getExtension(newName, true).getSecond();
 | |
|                 if (contentData != null &&
 | |
|                         newExt.length() != 0 &&
 | |
|                         !"tmp".equalsIgnoreCase(newExt) &&
 | |
|                         !newExt.equalsIgnoreCase(oldExt))
 | |
|                 {
 | |
|                     String targetMimetype = contentData.getMimetype();
 | |
|                     final ContentReader reader = contentService.getReader(targetNodeRef, ContentModel.PROP_CONTENT);
 | |
|                     String newMimetype = mimetypeService.guessMimetype(newName, reader);
 | |
|                     if (!targetMimetype.equalsIgnoreCase(newMimetype))
 | |
|                     {
 | |
|                         contentData = ContentData.setMimetype(contentData, newMimetype);
 | |
|                         nodeService.setProperty(targetNodeRef, ContentModel.PROP_CONTENT, contentData);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             catch (DuplicateChildNodeNameException e)
 | |
|             {
 | |
|                 throw new FileExistsException(targetParentRef, newName);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // get the details after the operation
 | |
|         FileInfo afterFileInfo = toFileInfo(targetNodeRef, true);
 | |
|         // done
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("" + (move ? "Moved" : "Copied") + " node: \n" +
 | |
|                     "   parent: " + targetParentRef + "\n" +
 | |
|                     "   before: " + beforeFileInfo + "\n" +
 | |
|                     "   after: " + afterFileInfo);
 | |
|         }
 | |
|         return afterFileInfo;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Determine if the specified node is a special "system" folder path based node
 | |
|      * 
 | |
|      * TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
 | |
|      * 
 | |
|      * @param nodeRef  node to check
 | |
|      * @return  true => system folder path based node
 | |
|      */
 | |
|     private boolean isSystemPath(NodeRef nodeRef)
 | |
|     {
 | |
|         Path path = nodeService.getPath(nodeRef);
 | |
|         String prefixedPath = path.toPrefixString(namespaceService);
 | |
|         return systemPaths.contains(prefixedPath);
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException
 | |
|     {
 | |
|         return createImpl(parentNodeRef, name, typeQName, null);
 | |
|     } 
 | |
|     
 | |
|     @Override
 | |
|     public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException
 | |
|     {
 | |
|         return createImpl(parentNodeRef, name, typeQName, assocQName);
 | |
|     }
 | |
| 
 | |
|     private FileInfo createImpl(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException
 | |
|     {
 | |
|         // set up initial properties
 | |
|         Map<QName, Serializable> properties = new HashMap<QName, Serializable>(11);
 | |
|         properties.put(ContentModel.PROP_NAME, (Serializable) name);
 | |
|         
 | |
|         // Check the type is valid for file/folder service
 | |
|         FileFolderServiceType type = getType(typeQName);
 | |
|         
 | |
|         switch (type)
 | |
|         {
 | |
|             case SYSTEM_FOLDER:
 | |
|                 throw new InvalidTypeException("System Folders are not handled by this service :" + typeQName);
 | |
|             case INVALID:
 | |
|                 throw new InvalidTypeException("Type is not handled by this service: " + typeQName);
 | |
|             case FILE:
 | |
|             case FOLDER:
 | |
|             default:
 | |
|         }
 | |
|         
 | |
|         // create the node
 | |
|         if (assocQName == null)
 | |
|         {
 | |
|             assocQName = QName.createQName(
 | |
|                     NamespaceService.CONTENT_MODEL_1_0_URI,
 | |
|                     QName.createValidLocalName(name));
 | |
|         }
 | |
|         ChildAssociationRef assocRef = null;
 | |
|         try
 | |
|         {
 | |
|             assocRef = nodeService.createNode(
 | |
|                     parentNodeRef,
 | |
|                     ContentModel.ASSOC_CONTAINS,
 | |
|                     assocQName,
 | |
|                     typeQName,
 | |
|                     properties);
 | |
|         }
 | |
|         catch (DuplicateChildNodeNameException e)
 | |
|         {
 | |
|             throw new FileExistsException(parentNodeRef, name);
 | |
|         }
 | |
|         
 | |
|         NodeRef nodeRef = assocRef.getChildRef();
 | |
| 
 | |
|         FileInfo fileInfo = toFileInfo(nodeRef, true);
 | |
|         // done
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Created: \n" +
 | |
|                     "   parent: " + parentNodeRef + "\n" +
 | |
|                     "   created: " + fileInfo);
 | |
|         }
 | |
|         return fileInfo;
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public void delete(NodeRef nodeRef)
 | |
|     {
 | |
|         nodeService.deleteNode(nodeRef);
 | |
|         // Done
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Deleted: \n" +
 | |
|                     "   node: " + nodeRef);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Checks for the presence of, and creates as necessary, the folder structure in the provided path.
 | |
|      * <p>
 | |
|      * An empty path list is not allowed as it would be impossible to necessarily return file info
 | |
|      * for the parent node - it might not be a folder node.
 | |
|      * @param parentNodeRef the node under which the path will be created
 | |
|      * @param pathElements the folder name path to create - may not be empty
 | |
|      * @param folderTypeQName the types of nodes to create.  This must be a valid subtype of
 | |
|      *      {@link org.alfresco.model.ContentModel#TYPE_FOLDER they folder type}.
 | |
|      * @return Returns the info of the last folder in the path.
 | |
|      * @deprecated Use FileFolderUtil.makeFolders rather than directly accessing this implementation class.
 | |
|      */
 | |
|     public FileInfo makeFolders(NodeRef parentNodeRef, List<String> pathElements, QName folderTypeQName)
 | |
|     {
 | |
|         return FileFolderUtil.makeFolders(this, parentNodeRef, pathElements, folderTypeQName);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Checks for the presence of, and creates as necessary, the folder structure in the provided path.
 | |
|      * <p>
 | |
|      * An empty path list is not allowed as it would be impossible to necessarily return file info
 | |
|      * for the parent node - it might not be a folder node.
 | |
|      * @param parentNodeRef the node under which the path will be created
 | |
|      * @param pathElements the folder name path to create - may not be empty
 | |
|      * @param folderTypeQName the types of nodes to create.  This must be a valid subtype of
 | |
|      *      {@link org.alfresco.model.ContentModel#TYPE_FOLDER they folder type}.
 | |
|      * @return Returns the info of the last folder in the path.
 | |
|      * @deprecated Use FileFolderUtil.makeFolders rather than directly accessing this implementation class.
 | |
|      */
 | |
|     public static FileInfo makeFolders(FileFolderService service, NodeRef parentNodeRef, List<String> pathElements, QName folderTypeQName)
 | |
|     {
 | |
|         return FileFolderUtil.makeFolders(service, parentNodeRef, pathElements, folderTypeQName);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the file or folder information from the root down to and including the node provided.
 | |
|      * <ul>
 | |
|      *   <li>The root node can be of any type and is not included in the path list.</li>
 | |
|      *   <li>Only the primary path is considered.  If the target node is not a descendant of the
 | |
|      *       root along purely primary associations, then an exception is generated.</li>
 | |
|      *   <li>If an invalid type is encountered along the path, then an exception is generated.</li>
 | |
|      * </ul>
 | |
|      * 
 | |
|      * @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
 | |
|      *        node must be assumed.
 | |
|      * @param nodeRef a reference to the file or folder
 | |
|      * @return Returns a list of file/folder infos from the root (excluded) down to and
 | |
|      *         including the destination file or folder
 | |
|      * @throws FileNotFoundException if the node could not be found
 | |
|      */
 | |
|     @Override
 | |
|     public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
 | |
|     {
 | |
|         // check the root
 | |
|         if (rootNodeRef == null)
 | |
|         {
 | |
|             rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef());
 | |
|         }
 | |
|         try
 | |
|         {
 | |
|             ArrayList<FileInfo> results = new ArrayList<FileInfo>(10);
 | |
|             // get the primary path
 | |
|             Path path = nodeService.getPath(nodeRef);
 | |
|             // iterate and turn the results into file info objects
 | |
|             boolean foundRoot = false;
 | |
|             for (Path.Element element : path)
 | |
|             {
 | |
|                 // ignore everything down to the root
 | |
|                 Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element;
 | |
|                 final NodeRef childNodeRef = assocElement.getRef().getChildRef();
 | |
|                 if (childNodeRef.equals(rootNodeRef))
 | |
|                 {
 | |
|                     // just found the root - but we don't put in an entry for it
 | |
|                     foundRoot = true;
 | |
|                     continue;
 | |
|                 }
 | |
|                 else if (!foundRoot)
 | |
|                 {
 | |
|                     // keep looking for the root
 | |
|                     continue;
 | |
|                 }
 | |
|                 // we found the root and expect to be building the path up
 | |
|                 // Run as system as the user could not have access to all folders in the path, see ALF-13816
 | |
|                 FileInfo pathInfo = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<FileInfo>()
 | |
|                 {
 | |
|                     public FileInfo doWork() throws Exception
 | |
|                     {
 | |
|                         return toFileInfo(childNodeRef, true);
 | |
|                     }
 | |
|                 }, AuthenticationUtil.getSystemUserName());
 | |
|                 
 | |
|                 // we can't append a path element to the results if there is already a (non-folder) file at the tail
 | |
|                 // since this would result in a path anomoly - file's cannot contain other files.
 | |
|                 if (!results.isEmpty() && !results.get(results.size()-1).isFolder())
 | |
|                 {
 | |
|                     throw new InvalidTypeException(
 | |
|                                 "File is not the last element in path: files cannot contain other files.");
 | |
|                 }
 | |
|                 results.add(pathInfo);
 | |
|             }
 | |
|             // check that we found the root
 | |
|             if (!foundRoot)
 | |
|             {
 | |
|                 throw new FileNotFoundException(nodeRef);
 | |
|             }
 | |
|             // done
 | |
|             if (logger.isDebugEnabled())
 | |
|             {
 | |
|                 logger.debug("Built name path for node: \n" +
 | |
|                         "   root: " + rootNodeRef + "\n" +
 | |
|                         "   node: " + nodeRef + "\n" +
 | |
|                         "   path: " + results);
 | |
|             }
 | |
|             return results;
 | |
|         }
 | |
|         catch (InvalidNodeRefException e)
 | |
|         {
 | |
|             throw new FileNotFoundException(nodeRef);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get the file or folder names from the root down to and including the node provided.
 | |
|      * <ul>
 | |
|      *   <li>The root node can be of any type and is not included in the path list.</li>
 | |
|      *   <li>Only the primary path is considered.  If the target node is not a descendant of the
 | |
|      *       root along purely primary associations, then an exception is generated.</li>
 | |
|      *   <li>If an invalid type is encountered along the path, then an exception is generated.</li>
 | |
|      * </ul>
 | |
|      * 
 | |
|      * @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
 | |
|      *        node must be assumed.
 | |
|      * @param nodeRef a reference to the file or folder
 | |
|      * @return Returns a list of file/folder names from the root (excluded) down to and
 | |
|      *         including the destination file or folder
 | |
|      * @throws FileNotFoundException if the node could not be found
 | |
|      */
 | |
|     @Override
 | |
|     public List<String> getNameOnlyPath(NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException
 | |
|     {
 | |
|         // check the root
 | |
|         if (rootNodeRef == null)
 | |
|         {
 | |
|             rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef());
 | |
|         }
 | |
|         try
 | |
|         {
 | |
|             final NodeRef rNodeRef = rootNodeRef;
 | |
|             final ArrayList<String> results = new ArrayList<String>(10);
 | |
|             // Run as system as the user could not have access to all folders in the path, see ALF-13816
 | |
|             AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
 | |
|             {
 | |
|                 public Void doWork() throws Exception
 | |
|                 {
 | |
|                     // get the primary path
 | |
|                     Path path = nodeService.getPath(nodeRef);
 | |
|                     // iterate and turn the results into file info objects
 | |
|                     boolean foundRoot = false;
 | |
|                     for (Path.Element element : path)
 | |
|                     {
 | |
|                         // ignore everything down to the root
 | |
|                         Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element;
 | |
|                         final NodeRef childNodeRef = assocElement.getRef().getChildRef();
 | |
|                         if (childNodeRef.equals(rNodeRef))
 | |
|                         {
 | |
|                             // just found the root - but we don't put in an entry for it
 | |
|                             foundRoot = true;
 | |
|                             continue;
 | |
|                         }
 | |
|                         else if (!foundRoot)
 | |
|                         {
 | |
|                             // keep looking for the root
 | |
|                             continue;
 | |
|                         }
 | |
|                         results.add(nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME).toString());
 | |
|                     }
 | |
|                     // check that we found the root
 | |
|                     if (!foundRoot)
 | |
|                     {
 | |
|                         throw new FileNotFoundException(nodeRef);
 | |
|                     }
 | |
|                     // done
 | |
|                     if (logger.isDebugEnabled())
 | |
|                     {
 | |
|                         logger.debug("Built name path for node: \n" +
 | |
|                                 "   root: " + rNodeRef + "\n" +
 | |
|                                 "   node: " + nodeRef + "\n" +
 | |
|                                 "   path: " + results);
 | |
|                     }
 | |
|                     return null;
 | |
|                 }
 | |
|             }, AuthenticationUtil.getSystemUserName());
 | |
|             
 | |
|             return results;
 | |
|         }
 | |
|         catch (InvalidNodeRefException e)
 | |
|         {
 | |
|             throw new FileNotFoundException(nodeRef);
 | |
|         }
 | |
|         catch (RuntimeException e)
 | |
|         {
 | |
|             // the runAs() is too keen on wrapping everything in an outer RuntimeException - which we don't want.
 | |
|             if (e.getCause() instanceof FileNotFoundException)
 | |
|             {
 | |
|                 throw (FileNotFoundException)e.getCause();
 | |
|             }
 | |
|             else throw e;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public FileInfo resolveNamePath(NodeRef rootNodeRef, List<String> pathElements) throws FileNotFoundException
 | |
|     {
 | |
|         return resolveNamePath(rootNodeRef, pathElements, true);        
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public FileInfo resolveNamePath(NodeRef rootNodeRef, List<String> pathElements, boolean mustExist) throws FileNotFoundException
 | |
|     {
 | |
|         if (pathElements.size() == 0)
 | |
|         {
 | |
|             throw new IllegalArgumentException("Path elements list is empty");
 | |
|         }
 | |
|         // walk the folder tree first
 | |
|         NodeRef parentNodeRef = rootNodeRef;
 | |
|         StringBuilder currentPath = new StringBuilder(pathElements.size() << 4);
 | |
|         int folderCount = pathElements.size() - 1;
 | |
|         for (int i = 0; i < folderCount; i++)
 | |
|         {
 | |
|             String pathElement = pathElements.get(i);
 | |
|             currentPath.append("/").append(pathElement);
 | |
|             NodeRef folderNodeRef = searchSimple(parentNodeRef, pathElement);
 | |
|             if (folderNodeRef == null)
 | |
|             {
 | |
|                 if (mustExist)
 | |
|                 {
 | |
|                     throw new FileNotFoundException("Folder not found: " + currentPath + " (in " + rootNodeRef + ")");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return null;
 | |
|                 }
 | |
|             }
 | |
|             parentNodeRef = folderNodeRef;
 | |
|         }
 | |
|         // we have resolved the folder path - resolve the last component
 | |
|         String pathElement = pathElements.get(pathElements.size() - 1);
 | |
|         currentPath.append("/").append(pathElement);
 | |
|         NodeRef fileNodeRef = searchSimple(parentNodeRef, pathElement);
 | |
|         if (fileNodeRef == null)
 | |
|         {
 | |
|             if (mustExist)
 | |
|             {
 | |
|                 throw new FileNotFoundException("File not found: " + currentPath + " (in " + rootNodeRef + ")");
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return null;
 | |
|             }
 | |
|         }
 | |
|         FileInfo result = getFileInfo(fileNodeRef);
 | |
|         // found it
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Resoved path element: \n" +
 | |
|                     "   root: " + rootNodeRef + "\n" +
 | |
|                     "   path: " + currentPath + "\n" +
 | |
|                     "   node: " + result);
 | |
|         }
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public FileInfo getFileInfo(NodeRef nodeRef)
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             return toFileInfo(nodeRef, true);
 | |
|         }
 | |
|         catch (InvalidTypeException e)
 | |
|         {
 | |
|             return null;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public ContentReader getReader(NodeRef nodeRef)
 | |
|     {
 | |
|         FileInfo fileInfo = toFileInfo(nodeRef, false);
 | |
|         if (fileInfo.isFolder())
 | |
|         {
 | |
|             throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo);
 | |
|         }
 | |
|         return contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public ContentWriter getWriter(NodeRef nodeRef)
 | |
|     {
 | |
|         FileInfo fileInfo = toFileInfo(nodeRef, false);
 | |
|         if (fileInfo.isFolder())
 | |
|         {
 | |
|             throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo);
 | |
|         }
 | |
|         final ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
 | |
|         // Ensure that a mimetype is set based on the filename (ALF-6560)
 | |
|         // This has been removed from the create code in 3.4 to prevent insert-update behaviour
 | |
|         // of the ContentData.
 | |
|         if (writer.getMimetype() == null)
 | |
|         {
 | |
|             final String name = fileInfo.getName();
 | |
|             writer.guessMimetype(name);
 | |
|         }
 | |
|         // Done
 | |
|         return writer;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Split a filename into the base (part before the '.') and the extension (part after the '.')
 | |
|      */
 | |
|     private Pair<String, String> getExtension(String name, boolean useLastDot)
 | |
|     {
 | |
|         String ext = "";
 | |
|         String base = name;
 | |
|         if (name != null)
 | |
|         {
 | |
|             name = name.trim();
 | |
|             int index = useLastDot ? name.lastIndexOf('.') : name.indexOf('.');
 | |
|             if (index > -1 && (index < name.length() - 1))
 | |
|             {
 | |
|                 base = name.substring(0, index);
 | |
|                 ext = name.substring(index + 1);
 | |
|             }
 | |
|         }
 | |
|         return new Pair<String, String>(base, ext);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public void setHidden(NodeRef nodeRef, boolean isHidden)
 | |
|     {
 | |
|         int mask = 0;
 | |
|         boolean allVisible = true;
 | |
|         Visibility webDavVisibility = isHidden ? Visibility.NotVisible : Visibility.Visible;
 | |
|         for (Client client : hiddenAspect.getClients())
 | |
|         {
 | |
|             Visibility clientVisibility = client == FileFilterMode.getClient() ? webDavVisibility : hiddenAspect
 | |
|                     .getVisibility(client, nodeRef);
 | |
|             if (clientVisibility != Visibility.Visible)
 | |
|             {
 | |
|                 allVisible = false;
 | |
|             }
 | |
|             mask |= hiddenAspect.getClientVisibilityMask(client, clientVisibility);
 | |
|         }
 | |
|         if (allVisible)
 | |
|         {
 | |
|             hiddenAspect.showNode(nodeRef, true);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             hiddenAspect.hideNode(nodeRef, mask, true, true, false);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public boolean isHidden(NodeRef nodeRef)
 | |
|     {
 | |
|         return hiddenAspect.getVisibility(FileFilterMode.getClient(), nodeRef) != Visibility.Visible;
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public <M extends Trait> ExtendedTrait<M>  getTrait(Class<? extends M> traitAPI)
 | |
|     {
 | |
|         return (ExtendedTrait<M>) fileFolderTrait;
 | |
|     }
 | |
|     
 | |
|     private FileFolderServiceTrait createFileFolderTrait()
 | |
|     {
 | |
|         return new FileFolderServiceTraitImpl(this);
 | |
|     }
 | |
| 
 | |
|     public static class FileFolderServiceTraitImpl implements FileFolderServiceTrait
 | |
|     {
 | |
|         private FileFolderServiceImpl thisService;
 | |
| 
 | |
|         public FileFolderServiceTraitImpl(FileFolderServiceImpl fileFolderServiceImpl)
 | |
|         {
 | |
|             thisService = fileFolderServiceImpl;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public Pair<Set<QName>, Set<QName>> buildSearchTypesAndIgnoreAspects(boolean files, boolean folders,
 | |
|                     Set<QName> ignoreQNameTypes)
 | |
|         {
 | |
|             return thisService.buildSearchTypesAndIgnoreAspects(files,
 | |
|                                                                 folders,
 | |
|                                                                 ignoreQNameTypes);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public FileInfo createFileInfo(NodeRef nodeRef, QName typeQName, boolean isFolder, boolean isHidden,
 | |
|                     Map<QName, Serializable> properties)
 | |
|         {
 | |
|             return new FileInfoImpl(nodeRef,
 | |
|                                     typeQName,
 | |
|                                     isFolder,
 | |
|                                     isHidden,
 | |
|                                     properties);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public FileFolderServiceType getType(QName typeQName)
 | |
|         {
 | |
|             return thisService.getType(typeQName);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public List<FileInfo> list(final NodeRef contextNodeRef)
 | |
|         {
 | |
| 
 | |
|             return thisService.list(contextNodeRef);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public PagingResults<FileInfo> list(final NodeRef contextNodeRef, final boolean files, final boolean folders,
 | |
|                     final String pattern, final Set<QName> ignoreQNames, final List<Pair<QName, Boolean>> sortProps,
 | |
|                     final PagingRequest pagingRequest)
 | |
|         {
 | |
|             return thisService.list(contextNodeRef,
 | |
|                                     files,
 | |
|                                     folders,
 | |
|                                     pattern,
 | |
|                                     ignoreQNames,
 | |
|                                     sortProps,
 | |
|                                     pagingRequest);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public PagingResults<FileInfo> list(final NodeRef rootNodeRef, final Set<QName> searchTypeQNames,
 | |
|                     final Set<QName> ignoreAspectQNames, final List<Pair<QName, Boolean>> sortProps,
 | |
|                     final PagingRequest pagingRequest)
 | |
|         {
 | |
|             return thisService.list(rootNodeRef,
 | |
|                                     searchTypeQNames,
 | |
|                                     ignoreAspectQNames,
 | |
|                                     sortProps,
 | |
|                                     pagingRequest);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public List<FileInfo> search(final NodeRef contextNodeRef, final String namePattern, final boolean fileSearch,
 | |
|                     final boolean folderSearch, final boolean includeSubFolders)
 | |
|         {
 | |
|             return thisService.search(contextNodeRef,
 | |
|                                       namePattern,
 | |
|                                       fileSearch,
 | |
|                                       folderSearch,
 | |
|                                       includeSubFolders);
 | |
|         }
 | |
|        
 | |
|         @Override
 | |
|         public FileInfo rename(final NodeRef sourceNodeRef, final String newName) throws FileExistsException, FileNotFoundException
 | |
|         {
 | |
|             
 | |
|             return thisService.rename(sourceNodeRef, newName);
 | |
|                                        
 | |
|         }
 | |
| 
 | |
|         
 | |
|         public PagingResults<FileInfo> list(NodeRef contextNodeRef,
 | |
|                     boolean files,
 | |
|                     boolean folders,
 | |
|                     Set<QName> ignoreQNames,
 | |
|                     List<Pair<QName, Boolean>> sortProps,
 | |
|                     PagingRequest pagingRequest)
 | |
|         {
 | |
|             return thisService.list(contextNodeRef,
 | |
|                                     files,
 | |
|                                     folders,
 | |
|                                     ignoreQNames,
 | |
|                                     sortProps,
 | |
|                                     pagingRequest);
 | |
|         }
 | |
|     };
 | |
| }
 |