Merged BRANCHES/DEV/V3.4-BUG-FIX to HEAD:

32244: Fix for ALF-11435:
   32246: Fix for ALF-11435 (part 2)
   32247: Fix for ALF-11435 (part 3)
   plus make sure that hidden files have the noindex aspect applied

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@32388 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Steven Glover
2011-11-29 14:28:39 +00:00
parent 56e64bd627
commit ea53ffea50
13 changed files with 446 additions and 150 deletions

View File

@@ -36,7 +36,6 @@ import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileType;
import org.alfresco.jlan.util.WildCard;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
@@ -71,7 +70,7 @@ public class CifsHelper
private FileFolderService fileFolderService;
private MimetypeService mimetypeService;
private PermissionService permissionService;
private Set<QName> excludedTypes = new HashSet<QName>();
private boolean isReadOnlyFlagOnFolders = false;
@@ -87,8 +86,8 @@ public class CifsHelper
{
this.dictionaryService = dictionaryService;
}
public void setNodeService(NodeService nodeService)
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
@@ -124,7 +123,7 @@ public class CifsHelper
this.excludedTypes.add(QName.createQName(exType));
}
}
/**
* Controls whether the read only flag is set on folders. This flag, when set, may cause problematic # behaviour in
* Windows clients and doesn't necessarily mean a folder can't be written to. See ALF-6727. Should we ever set the
@@ -286,6 +285,19 @@ public class CifsHelper
if (name != null)
{
fileInfo.setFileName(name);
// Check for file names that should be hidden
if(nodeService.hasAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_HIDDEN))
{
// Add the hidden file attribute
int attr = fileInfo.getFileAttributes();
if (( attr & FileAttribute.Hidden) == 0)
{
attr += FileAttribute.Hidden;
fileInfo.setFileAttributes( attr);
}
}
}
// Read/write access
@@ -704,4 +716,5 @@ public class CifsHelper
}
return false;
}
}

View File

@@ -746,7 +746,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
this.folderCacheLock.readLock().unlock();
}
}
List<FileInfo> fileInfos = fileFolderService.listFiles(contextNodeRef);
List<FileInfo> fileInfos = fileFolderService.removeHiddenFiles(fileFolderService.listFiles(contextNodeRef));
final NavigableMap<Long, FileInfo> currentSearch = new TreeMap<Long, FileInfo>();
switch (viewMode)
@@ -1047,7 +1047,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
// Only list this folder if we have a wildcard name. Otherwise do a direct lookup by name.
if (name.contains("*") || name.contains("%"))
{
list = fileFolderService.listFolders(root);
list = fileFolderService.removeHiddenFiles(fileFolderService.listFolders(root));
}
else
{

View File

@@ -256,9 +256,16 @@ public class FileFolderServiceImpl implements FileFolderService
// Is it a folder
QName typeQName = nodeService.getType(nodeRef);
boolean isFolder = isFolder(typeQName);
boolean isHidden = false;
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
{
isHidden = true;
}
// Construct the file info and add to the results
FileInfo fileInfo = new FileInfoImpl(nodeRef, typeQName, isFolder, properties);
FileInfo fileInfo = new FileInfoImpl(nodeRef, typeQName, isFolder, isHidden, properties);
// Done
return fileInfo;
}
@@ -569,7 +576,7 @@ public class FileFolderServiceImpl implements FileFolderService
}
return childNodeRef;
}
/**
* @see #search(NodeRef, String, boolean, boolean, boolean)
*/
@@ -1434,4 +1441,19 @@ public class FileFolderServiceImpl implements FileFolderService
}
return new Pair<String, String>(base, ext);
}
public List<FileInfo> removeHiddenFiles(List<FileInfo> files)
{
List<FileInfo> ret = new ArrayList<FileInfo>(files.size());
for(FileInfo file : files)
{
if(!nodeService.hasAspect(file.getNodeRef(), ContentModel.ASPECT_HIDDEN))
{
ret.add(file);
}
}
return ret;
}
}

View File

@@ -33,6 +33,7 @@ import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.jlan.server.FileFilterMode;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.query.PagingRequest;
@@ -1277,6 +1278,35 @@ public class FileFolderServiceImplTest extends TestCase
beforeSleep.compareTo((Date)nodeService.getProperty(destinationFolderNodeRef, ContentModel.PROP_MODIFIED)) < 0);
}
public void testHiddenFiles()
{
FileFilterMode.setMode(FileFilterMode.Mode.ENHANCED);
NodeRef parent = fileFolderService.create(rootNodeRef, "New Folder", ContentModel.TYPE_FOLDER).getNodeRef();
NodeRef child = fileFolderService.create(parent, "file.tmp", ContentModel.TYPE_CONTENT).getNodeRef();
assertTrue(nodeService.hasAspect(child, ContentModel.ASPECT_TEMPORARY));
assertTrue(!nodeService.hasAspect(child, ContentModel.ASPECT_HIDDEN));
NodeRef parent1 = fileFolderService.create(rootNodeRef, ".TemporaryItems", ContentModel.TYPE_FOLDER).getNodeRef();
NodeRef child1 = fileFolderService.create(parent1, "file1", ContentModel.TYPE_CONTENT).getNodeRef();
assertTrue(nodeService.hasAspect(child1, ContentModel.ASPECT_TEMPORARY));
assertTrue(nodeService.hasAspect(child1, ContentModel.ASPECT_HIDDEN));
NodeRef parent2 = fileFolderService.create(rootNodeRef, "Folder 2", ContentModel.TYPE_FOLDER).getNodeRef();
NodeRef child2 = fileFolderService.create(parent2, "Thumbs.db", ContentModel.TYPE_CONTENT).getNodeRef();
assertTrue(!nodeService.hasAspect(child2, ContentModel.ASPECT_TEMPORARY));
assertTrue(nodeService.hasAspect(child2, ContentModel.ASPECT_HIDDEN));
List<FileInfo> children = fileFolderService.list(parent);
assertEquals(1, children.size());
children = fileFolderService.list(parent1);
assertEquals(1, children.size());
children = fileFolderService.list(parent2);
assertEquals(1, children.size());
}
public void testPatterns()
{
// sanity checks only (see also GetChildrenCannedQueryTest)

View File

@@ -42,6 +42,7 @@ public class FileInfoImpl implements FileInfo
private NodeRef linkNodeRef;
private boolean isFolder;
private boolean isLink;
private boolean isHindden;
private Map<QName, Serializable> properties;
private QName typeQName;
@@ -52,6 +53,7 @@ public class FileInfoImpl implements FileInfo
NodeRef nodeRef,
QName typeQName,
boolean isFolder,
boolean isHidden,
Map<QName, Serializable> properties)
{
this.nodeRef = nodeRef;
@@ -59,6 +61,7 @@ public class FileInfoImpl implements FileInfo
this.isFolder = isFolder;
this.properties = properties;
this.isHindden = isHidden;
// Check if this is a link node
if ( properties.containsKey( ContentModel.PROP_LINK_DESTINATION))
@@ -135,6 +138,10 @@ public class FileInfoImpl implements FileInfo
return isLink;
}
public boolean isHidden() {
return isHindden;
}
public NodeRef getLinkNodeRef()
{
return linkNodeRef;

View File

@@ -0,0 +1,311 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>. */
package org.alfresco.repo.model.filefolder;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.alfresco.jlan.server.FileFilterMode;
import org.alfresco.jlan.server.FileFilterMode.Mode;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.model.FileInfo;
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.repository.Path.Element;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PatternFilter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An interceptor that intercepts the FileFolderService create method, creating files and folders as the system user
* and applying temporary and hidden aspects when they match a pattern.
*
*/
public class FilenameFilteringInterceptor implements MethodInterceptor
{
private static Log logger = LogFactory.getLog(FilenameFilteringInterceptor.class);
private NodeService nodeService;
private PermissionService permissionService;
private PatternFilter temporaryFiles;
private PatternFilter hiddenFiles;
private PatternFilter systemPaths;
public FilenameFilteringInterceptor()
{
}
/**
* A list of regular expressions that represent patterns of hidden files.
*
*/
public void setHiddenFiles(PatternFilter hiddenFiles)
{
this.hiddenFiles = hiddenFiles;
}
/**
* A list of regular expressions that represent patterns of temporary files.
*
*/
public void setTemporaryFiles(PatternFilter temporaryFiles)
{
this.temporaryFiles = temporaryFiles;
}
/**
* A list of regular expressions that represent patterns of system paths.
*
*/
public void setSystemPaths(PatternFilter systemPaths)
{
this.systemPaths = systemPaths;
}
public Mode getMode()
{
return FileFilterMode.getMode();
}
/**
* @param nodeService the service to use to apply the <b>sys:temporary</b> aspect
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
private void checkTemporaryAspect(boolean isTemporary, FileInfo fileInfo)
{
NodeRef nodeRef = fileInfo.getNodeRef();
if(isTemporary)
{
// it matched, so apply the temporary and hidden aspects
nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
if (logger.isDebugEnabled())
{
logger.debug("Applied temporary marker: " + fileInfo);
}
}
else
{
// If there was NOT a match then the file should not be marked as temporary
// after any of the operations in question.
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY))
{
// Remove the aspect
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY);
if (logger.isDebugEnabled())
{
logger.debug("Removed temporary marker: " + fileInfo);
}
}
}
}
private void checkHiddenAspect(boolean isHidden, FileInfo fileInfo)
{
NodeRef nodeRef = fileInfo.getNodeRef();
if (isHidden)
{
nodeService.addAspect(nodeRef, ContentModel.ASPECT_HIDDEN, null);
Map<QName, Serializable> props = new HashMap<QName, Serializable>(2);
props.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE);
props.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE);
nodeService.addAspect(nodeRef, ContentModel.ASPECT_INDEX_CONTROL, props);
if (logger.isDebugEnabled())
{
logger.debug("Applied hidden marker: " + fileInfo);
}
}
else
{
if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
{
// Remove the aspect
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_HIDDEN);
if (logger.isDebugEnabled())
{
logger.debug("Removed hidden marker: " + fileInfo);
}
}
}
}
private void addIndexedAspect(FileInfo fileInfo)
{
NodeRef nodeRef = fileInfo.getNodeRef();
Map<QName, Serializable> props = new HashMap<QName, Serializable>(2);
props.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE);
props.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE);
nodeService.addAspect(nodeRef, ContentModel.ASPECT_INDEX_CONTROL, props);
}
private Object runAsSystem(MethodInvocation invocation) throws Throwable
{
Object ret = null;
// We're creating in enhanced mode and have a matching filename or path. Switch to
// the system user to do the operation.
AuthenticationUtil.pushAuthentication();
try
{
AuthenticationUtil.setRunAsUserSystem();
ret = invocation.proceed();
}
finally
{
AuthenticationUtil.popAuthentication();
}
return ret;
}
private boolean isSystemPath(NodeRef parentNodeRef, String filename)
{
boolean ret = false;
Path path = nodeService.getPath(parentNodeRef);
Iterator<Element> it = path.iterator();
while(it.hasNext())
{
Path.ChildAssocElement elem = (Path.ChildAssocElement)it.next();
QName qname = elem.getRef().getQName();
if(qname != null && systemPaths.isFiltered(qname.getLocalName()))
{
ret = true;
break;
}
}
return ret;
}
public Object invoke(final MethodInvocation invocation) throws Throwable
{
// execute and get the result
String methodName = invocation.getMethod().getName();
Object ret = null;
// do the invocation
if (methodName.startsWith("create"))
{
NodeRef nodeRef = (NodeRef)invocation.getArguments()[0];
String filename = (String)invocation.getArguments()[1];
if(getMode() == Mode.ENHANCED)
{
if(systemPaths.isFiltered(filename))
{
// it's a system file/folder, create as system and allow full control to all authorities
ret = runAsSystem(invocation);
permissionService.setPermission(((FileInfo)ret).getNodeRef(), PermissionService.ALL_AUTHORITIES, PermissionService.FULL_CONTROL, true);
// it's always marked temporary and hidden
checkTemporaryAspect(true, (FileInfo)ret);
checkHiddenAspect(true, (FileInfo)ret);
addIndexedAspect((FileInfo)ret);
}
else
{
// it's not a temporary file/folder, create as normal
ret = invocation.proceed();
// if it's on a temporary path check whether temporary and hidden aspects need to be applied
if(isSystemPath(nodeRef, filename))
{
checkTemporaryAspect(true, (FileInfo)ret);
checkHiddenAspect(true, (FileInfo)ret);
addIndexedAspect((FileInfo)ret);
}
else
{
checkTemporaryAspect(temporaryFiles.isFiltered(filename), (FileInfo)ret);
boolean isHidden = hiddenFiles.isFiltered(filename);
checkHiddenAspect(isHidden, (FileInfo)ret);
if(isHidden)
{
addIndexedAspect((FileInfo)ret);
}
}
}
}
else
{
ret = invocation.proceed();
checkTemporaryAspect(temporaryFiles.isFiltered(filename), (FileInfo)ret);
}
}
else if (methodName.startsWith("rename") ||
methodName.startsWith("move") ||
methodName.startsWith("copy"))
{
ret = invocation.proceed();
FileInfo fileInfo = (FileInfo) ret;
String filename = fileInfo.getName();
if (logger.isDebugEnabled())
{
logger.debug("Checking filename returned by " + methodName + ": " + filename);
}
// check against all the regular expressions
checkTemporaryAspect(temporaryFiles.isFiltered(filename), fileInfo);
boolean isHidden = hiddenFiles.isFiltered(filename);
checkHiddenAspect(isHidden, fileInfo);
if(isHidden)
{
addIndexedAspect((FileInfo)ret);
}
}
else
{
ret = invocation.proceed();
}
// done
return ret;
}
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.model.filefolder;
import java.util.Collections;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An interceptor for the {@link org.alfresco.service.cmr.model.FileFolderService FileFolderService}
* that marks files or folders with the <b>sys:temporary</b> aspect depending on the
* name pattern {@link #setFilterRegularExpressions(List) provided}.
*
* @author Derek Hulley
*/
public class TempFileMarkerInterceptor implements MethodInterceptor
{
private static Log logger = LogFactory.getLog(TempFileMarkerInterceptor.class);
private NodeService nodeService;
private List<String> filterRegularExpressions;
public TempFileMarkerInterceptor()
{
filterRegularExpressions = Collections.emptyList();
}
/**
* @param nodeService the service to use to apply the <b>sys:temporary</b> aspect
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* A list of regular expressions that represent patterns of temporary files.
*
* @param regexps list of regular expressions
*
* @see String#matches(java.lang.String)
*/
public void setFilterRegularExpressions(List<String> regexps)
{
this.filterRegularExpressions = regexps;
}
/**
* Handles <b>rename</b>, <b>move</b>, <b>copy</b>
*/
public Object invoke(MethodInvocation invocation) throws Throwable
{
Object ret = invocation.proceed();
// execute and get the result
String methodName = invocation.getMethod().getName();
if (methodName.startsWith("create") ||
methodName.startsWith("rename") ||
methodName.startsWith("move") ||
methodName.startsWith("copy"))
{
FileInfo fileInfo = (FileInfo) ret;
String filename = fileInfo.getName();
NodeRef nodeRef = fileInfo.getNodeRef();
if (logger.isDebugEnabled())
{
logger.debug("Checking filename returned by " + methodName + ": " + filename);
}
// check against all the regular expressions
boolean matched = false;
for (String regexp : filterRegularExpressions)
{
if (!filename.matches(regexp))
{
// it is not a match - try next one
continue;
}
else
{
matched = true;
// it matched, so apply the aspect
nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
// no further checking required
if (logger.isDebugEnabled())
{
logger.debug("Applied temporary marker: " + fileInfo);
}
break;
}
}
// If there was NOT a match then the file should not be marked as temporary
// after any of the operations in question.
if (!matched && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY))
{
// Remove the aspect
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY);
if (logger.isDebugEnabled())
{
logger.debug("Removed temporary marker: " + fileInfo);
}
}
}
// done
return ret;
}
}

View File

@@ -53,7 +53,7 @@ public interface FileFolderService
*/
@Auditable(parameters = {"contextNodeRef"})
public List<FileInfo> list(NodeRef contextNodeRef);
/**
* Lists page of immediate child files and/or folders of the given context node
* with optional filtering (exclusion of certain child file/folder subtypes) and sorting
@@ -396,4 +396,12 @@ public interface FileFolderService
*/
@Auditable(parameters = {"typeQName"})
public FileFolderServiceType getType(QName typeQName);
/**
* Removes any hidden files from the file list.
*
* @param files
* @return a list of files with hidden files removed
*/
public List<FileInfo> removeHiddenFiles(List<FileInfo> files);
}

View File

@@ -52,6 +52,11 @@ public interface FileInfo extends PermissionCheckValue, Serializable
*/
public boolean isLink();
/**
* @return true if this instance represents a hidden file
*/
public boolean isHidden();
/**
* @return Return the reference to the node that this node is linked to
*/