diff --git a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml index 4782c8cc03..c55c67a3db 100644 --- a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml +++ b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml @@ -269,6 +269,7 @@ 20000 MEDIUM true + .*\\.TemporaryItems.*\\Word Work File.*\.tmp @@ -277,6 +278,7 @@ 20000 MEDIUM false + .*\\.TemporaryItems.*\\Word Work File.*\.tmp @@ -284,6 +286,7 @@ 20000 MEDIUM false + true diff --git a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java index f4cf9f89d4..c0c8df770a 100644 --- a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java +++ b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java @@ -134,7 +134,7 @@ public interface RepositoryDiskInterface * @param newName java.lang.String * @exception java.io.IOException The exception description. */ - public void renameFile(NodeRef rootNode, String oldName, String newName, boolean soft) + public void renameFile(NodeRef rootNode, String oldName, String newName, boolean soft, boolean moveAsSystem) throws java.io.IOException; diff --git a/source/java/org/alfresco/filesys/repo/ClientHelper.java b/source/java/org/alfresco/filesys/repo/ClientHelper.java new file mode 100644 index 0000000000..0070e6b317 --- /dev/null +++ b/source/java/org/alfresco/filesys/repo/ClientHelper.java @@ -0,0 +1,28 @@ +package org.alfresco.filesys.repo; + +import org.alfresco.jlan.server.SrvSession; +import org.alfresco.util.FileFilterMode.Client; + +public class ClientHelper +{ + public static Client getClient(SrvSession srvSession) + { + String clientStr = srvSession.getServer().getProtocolName().toLowerCase(); + if(clientStr.equals("cifs")) + { + return Client.cifs; + } + else if(clientStr.equals("nfs")) + { + return Client.nfs; + } + else if(clientStr.equals("ftp")) + { + return Client.ftp; + } + else + { + return null; + } + } +} diff --git a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java index 12319d5a04..bd1e3e71ad 100644 --- a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java +++ b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java @@ -30,7 +30,6 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.FileFilterMode; -import org.alfresco.util.FileFilterMode.Client; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -159,27 +158,6 @@ public class CommandExecutorImpl implements CommandExecutor return ret; } - private Client getClient(SrvSession srvSession) - { - String clientStr = srvSession.getServer().getProtocolName().toLowerCase(); - if(clientStr.equals("cifs")) - { - return Client.cifs; - } - else if(clientStr.equals("nfs")) - { - return Client.nfs; - } - else if(clientStr.equals("ftp")) - { - return Client.ftp; - } - else - { - return null; - } - } - /** * @param sess * @param tree @@ -190,7 +168,7 @@ public class CommandExecutorImpl implements CommandExecutor */ private Object executeInternal(SrvSession sess, TreeConnection tree, Command command, Object result) throws IOException { - FileFilterMode.setClient(getClient(sess)); + FileFilterMode.setClient(ClientHelper.getClient(sess)); try { if(command instanceof CompoundCommand) @@ -255,13 +233,13 @@ public class CommandExecutorImpl implements CommandExecutor logger.debug("rename command"); RenameFileCommand rename = (RenameFileCommand)command; - repositoryDiskInterface.renameFile(rename.getRootNode(), rename.getFromPath(), rename.getToPath(), rename.isSoft()); + repositoryDiskInterface.renameFile(rename.getRootNode(), rename.getFromPath(), rename.getToPath(), rename.isSoft(), false); } else if(command instanceof MoveFileCommand) { logger.debug("move command"); MoveFileCommand move = (MoveFileCommand)command; - repositoryDiskInterface.renameFile(move.getRootNode(), move.getFromPath(), move.getToPath(), false); + repositoryDiskInterface.renameFile(move.getRootNode(), move.getFromPath(), move.getToPath(), false, move.isMoveAsSystem()); } else if(command instanceof CopyContentCommand) { diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java index ec17e0f215..762500908d 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java @@ -1337,7 +1337,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD * @param newName path/name of new file * @exception java.io.IOException The exception description. */ - public void renameFile(NodeRef rootNode, final String oldName, final String newName, boolean soft) + public void renameFile(NodeRef rootNode, final String oldName, final String newName, boolean soft, boolean moveAsSystem) throws IOException { @@ -1390,7 +1390,25 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD } else { - fileFolderService.moveFrom(nodeToMoveRef, sourceFolderRef, targetFolderRef, name); + if (moveAsSystem) + { + if (logger.isDebugEnabled()) + { + logger.debug("Run move as System for: " + oldName); + } + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + return fileFolderService.moveFrom(nodeToMoveRef, sourceFolderRef, targetFolderRef, name); + } + }, AuthenticationUtil.getSystemUserName()); + } + else + { + fileFolderService.moveFrom(nodeToMoveRef, sourceFolderRef, targetFolderRef, name); + } + logger.debug( "Moved between different folders: \n" + " Old name: " + oldName + "\n" + diff --git a/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java index 4b99b71054..ce02486bbc 100644 --- a/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java @@ -50,6 +50,7 @@ import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.TreeConnection; import org.alfresco.jlan.smb.SharingMode; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.FileFilterMode; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -170,8 +171,15 @@ public class NonTransactionalRuleContentDiskDriver implements ExtendedDiskInterf public void createDirectory(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException { - diskInterface.createDirectory(sess, tree, params); - + FileFilterMode.setClient(ClientHelper.getClient(sess)); + try + { + diskInterface.createDirectory(sess, tree, params); + } + finally + { + FileFilterMode.clearClient(); + } } @Override diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffle.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffle.java index b9e9f1444c..a6e73ac72a 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffle.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffle.java @@ -18,12 +18,10 @@ */ package org.alfresco.filesys.repo.rules; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking; -import org.alfresco.filesys.repo.rules.operations.CreateFileOperation; import org.alfresco.filesys.repo.rules.operations.RenameFileOperation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,8 +44,10 @@ public class ScenarioDoubleRenameShuffle implements Scenario */ private Pattern pattern; private String strPattern; + private Pattern interimPattern; + private String strInterimPattern; private boolean deleteBackup; - + private boolean moveAsSystem; private long timeout = 30000; @@ -69,12 +69,17 @@ public class ScenarioDoubleRenameShuffle implements Scenario { if(logger.isDebugEnabled()) { - logger.debug("New Scenario Double Rename Shuffle Instance strPattern:" + pattern + " matches" + r.getTo() ); + logger.debug("New Scenario Double Rename Shuffle Instance strPattern:" + pattern + " matches" + r.getTo()); } ScenarioDoubleRenameShuffleInstance instance = new ScenarioDoubleRenameShuffleInstance(); instance.setTimeout(timeout); instance.setRanking(ranking); instance.setDeleteBackup(deleteBackup); + instance.setMoveAsSystem(moveAsSystem); + if (interimPattern != null) + { + instance.setInterimPattern(interimPattern); + } return instance; } } @@ -124,4 +129,28 @@ public class ScenarioDoubleRenameShuffle implements Scenario { return deleteBackup; } + + public boolean isMoveAsSystem() + { + return moveAsSystem; + } + + public void setMoveAsSystem(boolean retryAsSystem) + { + this.moveAsSystem = retryAsSystem; + } + + public void setInterimPattern(String intermediateMovePattern) + { + if (null != intermediateMovePattern) + { + this.interimPattern = Pattern.compile(intermediateMovePattern, Pattern.CASE_INSENSITIVE); + this.strInterimPattern = intermediateMovePattern; + } + } + + public String getInterimPattern() + { + return this.strInterimPattern; + } } diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java index 2749666e36..756fb9dc2b 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java @@ -22,16 +22,14 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -import org.alfresco.filesys.repo.OpenFileMode; import org.alfresco.filesys.repo.rules.commands.CompoundCommand; import org.alfresco.filesys.repo.rules.commands.CopyContentCommand; import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand; import org.alfresco.filesys.repo.rules.commands.MoveFileCommand; import org.alfresco.filesys.repo.rules.commands.RenameFileCommand; -import org.alfresco.filesys.repo.rules.operations.CreateFileOperation; -import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation; -import org.alfresco.filesys.repo.rules.operations.OpenFileOperation; import org.alfresco.filesys.repo.rules.operations.MoveFileOperation; import org.alfresco.filesys.repo.rules.operations.RenameFileOperation; import org.alfresco.jlan.server.filesys.FileName; @@ -72,6 +70,8 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance private Ranking ranking; private boolean deleteBackup; + private boolean moveAsSystem; + private Pattern interimPattern; /** * Timeout in ms. Default 30 seconds. @@ -85,7 +85,7 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance /** * Keep track of re-names */ - private Maprenames = new HashMap(); + private Map renames = new HashMap(); /** * Evaluate the next operation @@ -201,7 +201,7 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance } else { - RenameFileCommand r2 = new RenameFileCommand(fileFrom, fileEnd, r.getRootNodeRef(), oldFolder + "\\" + fileFrom, oldFolder + "\\" + fileEnd); + RenameFileCommand r2 = new RenameFileCommand(fileFrom, fileEnd, r.getRootNodeRef(), oldFolder + "\\" + fileFrom, oldFolder + "\\" + fileEnd); commands.add(r2); } @@ -263,8 +263,8 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance } else { - MoveFileCommand m1 = new MoveFileCommand(fileFrom, fileEnd, r.getRootNodeRef(), oldFolder + "\\" + fileFrom, folderEnd + "\\" + fileEnd); - commands.add(m1); + MoveFileCommand m1 = new MoveFileCommand(fileFrom, fileEnd, r.getRootNodeRef(), oldFolder + "\\" + fileFrom, folderEnd + "\\" + fileEnd, isMoveAsSystem()); + commands.add(m1); } /** * TODO - we may need to copy a new node for the backup and delete the temp node. @@ -274,6 +274,22 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance isComplete = true; return new CompoundCommand(commands); } + else + { + if ((interimPattern != null)) + { + // ALF-16257: temporary Word file moved from .TemporaryItems + Matcher m = interimPattern.matcher(r.getFromPath()); + if(m.matches() && r.getFrom().equals(r.getTo())) + { + if(logger.isDebugEnabled()) + { + logger.debug("Got system move from temporary folder: " + r.getFrom() + " to " + r.getToPath()); + } + return new MoveFileCommand(r.getFrom(), r.getTo(), r.getRootNodeRef(), r.getFromPath(), r.getToPath(), true); + } + } + } } break; @@ -282,6 +298,16 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance return null; } + public boolean isMoveAsSystem() + { + return moveAsSystem; + } + + public void setMoveAsSystem(boolean moveAsSystem) + { + this.moveAsSystem = moveAsSystem; + } + @Override public boolean isComplete() { @@ -323,4 +349,9 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance { return deleteBackup; } + + public void setInterimPattern(Pattern interimPattern) + { + this.interimPattern = interimPattern; + } } diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java index 6d2d69dd99..ac90f21119 100644 --- a/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java +++ b/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java @@ -18,8 +18,6 @@ */ package org.alfresco.filesys.repo.rules.commands; -import java.util.List; - import org.alfresco.filesys.repo.rules.Command; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.service.cmr.repository.NodeRef; @@ -32,6 +30,7 @@ public class MoveFileCommand implements Command private NodeRef rootNode; private String fromPath; private String toPath; + private boolean isMoveAsSystem = false; public MoveFileCommand(String from, String to, NodeRef rootNode, String fromPath, String toPath) { @@ -42,7 +41,13 @@ public class MoveFileCommand implements Command this.toPath = toPath; } - + // ALF-16257: in shuffle scenarios if user has insufficient permissions rename should be done as System + public MoveFileCommand(String from, String to, NodeRef rootNode, String fromPath, String toPath, boolean moveAsSystem) + { + this(from, to, rootNode, fromPath, toPath); + this.isMoveAsSystem = moveAsSystem; + } + public String getFrom() { return from; @@ -95,4 +100,9 @@ public class MoveFileCommand implements Command { return toPath; } + + public boolean isMoveAsSystem() + { + return isMoveAsSystem; + } } diff --git a/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java b/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java index bd040ccd4d..d7d1d8aeda 100644 --- a/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java +++ b/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java @@ -25,11 +25,11 @@ import java.io.Serializable; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Date; import javax.transaction.UserTransaction; import javax.xml.ws.Holder; @@ -41,26 +41,21 @@ import org.alfresco.jlan.server.NetworkServer; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.auth.ClientInfo; import org.alfresco.jlan.server.config.ServerConfiguration; -import org.alfresco.jlan.server.core.DeviceContext; import org.alfresco.jlan.server.core.DeviceContextException; import org.alfresco.jlan.server.core.SharedDevice; import org.alfresco.jlan.server.filesys.AccessDeniedException; import org.alfresco.jlan.server.filesys.AccessMode; -import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskSharedDevice; import org.alfresco.jlan.server.filesys.FileAction; import org.alfresco.jlan.server.filesys.FileAttribute; import org.alfresco.jlan.server.filesys.FileExistsException; import org.alfresco.jlan.server.filesys.FileInfo; import org.alfresco.jlan.server.filesys.FileOpenParams; -import org.alfresco.jlan.server.filesys.FilesystemsConfigSection; import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.jlan.server.filesys.NetworkFileServer; +import org.alfresco.jlan.server.filesys.PermissionDeniedException; import org.alfresco.jlan.server.filesys.SearchContext; import org.alfresco.jlan.server.filesys.TreeConnection; -import org.alfresco.filesys.config.ServerConfigurationBean; - - import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; import org.alfresco.repo.action.evaluator.NoConditionEvaluator; @@ -97,12 +92,9 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.FileFilterMode; -import org.alfresco.util.FileFilterMode.Client; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.type.VersionType; import org.springframework.context.ApplicationContext; import org.springframework.core.io.ClassPathResource; @@ -7271,6 +7263,415 @@ public class ContentDiskDriverTest extends TestCase } // testNFS + private void doTransactionWorkAsEditor(final RunAsWork work, RetryingTransactionHelper tran) + { + RetryingTransactionCallback transactionCallback = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + try + { + AuthenticationUtil.runAs(work, ContentDiskDriverTest.TEST_USER_AUTHORITY); + } + catch (Exception e) + { + // Informing about test failure. Expected exception is 'AccessDeniedException' or 'PermissionDeniedException' + if (e.getCause() instanceof AccessDeniedException || e.getCause() instanceof PermissionDeniedException) + { + fail("For user='" + TEST_USER_AUTHORITY + "' " + e.getCause().toString()); + } + else + { + fail("Unexpected exception was caught: " + e.toString()); + } + } + return null; + } + }; + tran.doInTransaction(transactionCallback, false, true); + } + + /** + * 0. test.txt exist in folder where user has Editor permissions + * 1. as Editor create temporary file in temporary directory + * 2. as Editor rename test.txt to test.txt.sb-1eefba7a-rkC6XE + * 3. as Editor move temporary file to working directory + */ + public void testScenarioMacLionTextEditByEditor_ALF_16257() throws Exception + { + logger.debug("test Collaborator/editor edit txt file on Mac Os Mountain Lion : Alf16257"); + final String FILE_NAME = "test.txt"; + final String FILE_BACKUP = "test.txt.sb-1eefba7a-rkC6XE"; + final String FILE_NEW_TEMP = FILE_NAME; // the same but in temp dir + + class TestContext + { + NetworkFile firstFileHandle; + NetworkFile newFileHandle; + NodeRef testNodeRef; // node ref of FILE_NAME + }; + final TestContext testContext = new TestContext(); + + final String TEST_ROOT_DIR = "\\ContentDiskDriverTest"; + final String TEST_DIR = TEST_ROOT_DIR + "\\testALF16257txt"; + final String TEST_TEMP_DIR = TEST_DIR + "\\.TemporaryItems"; + final String UPDATED_TEXT = "This is new content"; + + ServerConfiguration scfg = new ServerConfiguration("testServer"); + TestServer testServer = new TestServer("testServer", scfg); + final SrvSession testSession = new TestSrvSession(666, testServer, "cifs", "remoteName"); + DiskSharedDevice share = getDiskSharedDevice(); + final TreeConnection testConnection = testServer.getTreeConnection(share); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + /** + * Clean up just in case garbage is left from a previous run + */ + RetryingTransactionCallback deleteGarbageFileCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME); + return null; + } + }; + try + { + tran.doInTransaction(deleteGarbageFileCB); + } + catch (Exception e) + { + // expect to go here + } + + logger.debug("Step 0 - initialise"); + /** + * Create a file in the test directory + */ + RetryingTransactionCallback createFileCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + /** + * Create the test directory we are going to use + */ + FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + driver.createDirectory(testSession, testConnection, createRootDirParams); + driver.createDirectory(testSession, testConnection, createDirParams); + driver.createDirectory(testSession, testConnection, createTempDirParams); + + /** + * Create the file we are going to use + */ + FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams); + assertNotNull(testContext.firstFileHandle); + + // no need to test lots of different properties, that's already been tested above + testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true); + nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null); + + String testContent = "CIFS: Collaborator/editor could not edit file on Mac Os Mountain Lion"; + byte[] testContentBytes = testContent.getBytes(); + testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0); + testContext.firstFileHandle.close(); + + // Apply 'Editor' role for test user to test folder + permissionService.setPermission(getNodeForPath(testConnection, TEST_DIR), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true); + // Apply full control on temporary directory + permissionService.setPermission(getNodeForPath(testConnection, TEST_TEMP_DIR), PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true); + + return null; + } + }; + tran.doInTransaction(createFileCB, false, true); + + /** + * a) Save the temp file in the temp dir + */ + logger.debug("Step a - create a temp file in the temp dir"); + RunAsWork saveNewFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + FileOpenParams createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams); + assertNotNull(testContext.newFileHandle); + + byte[] testContentBytes = UPDATED_TEXT.getBytes(); + driver.writeFile(testSession, testConnection, testContext.newFileHandle, testContentBytes, 0, testContentBytes.length, 0); + driver.closeFile(testSession, testConnection, testContext.newFileHandle); + + NodeRef tempNodeRef = getNodeForPath(testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP); + ContentReader reader = contentService.getReader(tempNodeRef, ContentModel.PROP_CONTENT); + assertNotNull(reader); + String actualContent = reader.getContentString(); + assertEquals("new contents were not written to temporary file", UPDATED_TEXT, actualContent); + return null; + } + }; + doTransactionWorkAsEditor(saveNewFileCB, tran); + + /** + * b) rename the target file to a backup file + */ + logger.debug("Step b - rename the target file as Editor"); + RunAsWork renameOldFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_BACKUP); + return null; + } + }; + doTransactionWorkAsEditor(renameOldFileCB, tran); + + /** + * c) Move the new file into target dir, stuff should get shuffled + */ + logger.debug("Step c - move new file into target dir as Editor"); + RunAsWork moveNewFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME); + return null; + } + }; + doTransactionWorkAsEditor(moveNewFileCB, tran); + + /** + * Validate + */ + RetryingTransactionCallback validateCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + + Map props = nodeService.getProperties(shuffledNodeRef); + assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED)); + assertEquals("name wrong", FILE_NAME, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_NAME)); + ContentReader reader = contentService.getReader(testContext.testNodeRef, ContentModel.PROP_CONTENT); + assertNotNull(reader); + String actualContent = reader.getContentString(); + assertEquals("contents were not updated", UPDATED_TEXT, actualContent); + return null; + } + }; + tran.doInTransaction(validateCB, false, true); + + logger.debug("end testScenarioMacLionTextEditByEditor For ALF-16257"); + } // testScenarioMacLionTextEditByEditorForAlf16257 + + /** + * 0. MacWord1.docx exist in folder where user has Editor permissions + * 1. as Editor rename MacWord1.docx to backup Word Work File L_5.tmp + * 2. as Editor create temporary file in temporary directory and move it to working dir + * 3. as Editor rename Word Work File D_2.tmp to MacWord1.docx + */ + public void testScenarioMountainLionWord2011EditByEditor_ALF_16257() throws Exception + { + logger.debug("testScenarioMountainLionWord2011 Edit By Editor ALF-16257"); + + final String FILE_NAME = "MacWord1.docx"; + final String FILE_OLD_TEMP = "Word Work File L_5.tmp"; + final String FILE_NEW_TEMP = "Word Work File D_2.tmp"; + + class TestContext + { + NetworkFile firstFileHandle; + String mimetype; + }; + final TestContext testContext = new TestContext(); + + final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testALF16257Word"; + final String TEST_TEMP_DIR = TEST_DIR + "\\.TemporaryItems"; // need to match with interimPattern + + ServerConfiguration scfg = new ServerConfiguration("testServer"); + TestServer testServer = new TestServer("testServer", scfg); + final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName"); + DiskSharedDevice share = getDiskSharedDevice(); + final TreeConnection testConnection = testServer.getTreeConnection(share); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + /** + * Clean up just in case garbage is left from a previous run + */ + RetryingTransactionCallback deleteGarbageFileCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME); + return null; + } + }; + try + { + tran.doInTransaction(deleteGarbageFileCB); + } + catch (Exception e) + { + // expect to go here + } + + logger.debug("a) create new file"); + RetryingTransactionCallback createFileCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + /** + * Create the test directory we are going to use + */ + FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + driver.createDirectory(testSession, testConnection, createRootDirParams); + driver.createDirectory(testSession, testConnection, createDirParams); + driver.createDirectory(testSession, testConnection, createTempDirParams); + + /** + * Create the file we are going to test + */ + FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams); + assertNotNull(testContext.firstFileHandle); + + ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc"); + assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource); + writeResourceToNetworkFile(fileResource, testContext.firstFileHandle); + driver.closeFile(testSession, testConnection, testContext.firstFileHandle); + NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null); + + // Apply 'Editor' role for test user to test folder + permissionService.setPermission(getNodeForPath(testConnection, TEST_DIR), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true); + // Apply full control on temporary directory + permissionService.setPermission(getNodeForPath(testConnection, TEST_TEMP_DIR), PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true); + + return null; + } + }; + tran.doInTransaction(createFileCB, false, true); + + /** + * b) rename the old file, should fire doubleRenameShuffle scenario + */ + logger.debug("b) rename old file"); + RunAsWork renameOldFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + Map props = nodeService.getProperties(file1NodeRef); + ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT); + testContext.mimetype = data.getMimetype(); + + driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + FILE_OLD_TEMP); + return null; + } + }; + doTransactionWorkAsEditor(renameOldFileCB, tran); + + /** + * c) as Editor Save the new file in .TemporaryItems + * and move it to working directory (should be detected by scenario) + * Write ContentDiskDriverTest3.doc, + */ + logger.debug("c) create temp file in temp dir"); + RunAsWork writeFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + FileOpenParams createFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams); + + ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest3.doc"); + assertNotNull("unable to find test resource filesys/ContentDiskDriverTest3.doc", fileResource); + writeResourceToNetworkFile(fileResource, testContext.firstFileHandle); + driver.closeFile(testSession, testConnection, testContext.firstFileHandle); + + driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NEW_TEMP); + return null; + } + }; + doTransactionWorkAsEditor(writeFileCB, tran); + + /** + * d) Move the new file into place, stuff should get shuffled + */ + logger.debug("d) move new file into place"); + RunAsWork moveNewFileCB = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME); + return null; + } + }; + doTransactionWorkAsEditor(moveNewFileCB, tran); + + /** + * e) Delete the old file + */ + logger.debug("e) delete the old file"); + RetryingTransactionCallback deleteOldFileCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP); + return null; + } + }; + + tran.doInTransaction(deleteOldFileCB, false, true); + + logger.debug("e) validate results"); + + logger.debug("f) validate results"); + /** + * Now validate everything is correct + */ + RetryingTransactionCallback validateCB = new RetryingTransactionCallback() { + @Override + public Void execute() throws Throwable + { + NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + + Map props = nodeService.getProperties(shuffledNodeRef); + + ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT); + //assertNotNull("data is null", data); + //assertEquals("size is wrong", 123904, data.getSize()); + + NodeRef file1NodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + assertTrue("file has lost versionable aspect", nodeService.hasAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE)); + + assertEquals("mimeType is wrong", testContext.mimetype, data.getMimetype()); + + + return null; + } + }; + tran.doInTransaction(validateCB, true, true); + logger.debug("end testScenarioMountainLionWord2011 Edit By Editor ALF-16257"); + } // testScenarioMountainLionWord2011EditByEditor_ALF_16257 /** * Test server