From a8f0689c1ccb3887214f3963938f31ca1cda74c6 Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Mon, 6 Jun 2011 10:57:20 +0000 Subject: [PATCH] Merged V3.4-BUG-FIX to HEAD 28158: ALF-8925: Merged V3.3 to V3.4-BUG-FIX 28121: ALF-8878: Prevent authentication errors in SMBSrvSession.cleanupSession() 28159: Merged HEAD to V3.4-BUG-FIX (RECORD ONLY) 27951: Fixed merge issue in ContentDiskDriverTest 28161: ALF-8861: Fix from Bitrock to kill OpenOffice process on uninstallation if all else fails 28165: ALF-8260: Corrected French translations of 'Library' 28166: Fix for ALF-8751 - Dates are not localized in Document Lists dashlet 28167: Fix for ALF-8493 28169: ALF-5797: Translation corrections to complex strings 28171: Merged DEV TO V3.4-BUG-FIX ALF-8808 : CLONE - NFS: User with editor role cannot edit content - unit test required 28181: Fixed ALF-280: Unfriendly message appears when trying to specify non-existent file-store 28184: Merged BRANCHES/DEV/BELARUS/V3.4-BUG-FIX-2011_04_12 to BRANCHES/DEV/V3.4-BUG-FIX: 28179: ALF-8754: Cannot preview content on other webapp folder than ROOT (merged w/ trivial clean-up) 28185: Fixed ALF-8020: Multivalue date in document details causes error in alfresco share NOTE: We do not support any multi-valued fields other than text currently, this fix is therefore to handle the case where a multivalued date or dateTime field is configured in a form. The form will list the dates in view mode but in create and edit modes the control is not displayed at all when there are multiple values, if there is only one value the control continues to function as it has done previously. 28188: Fix for ALF-731 - in a cluster environment (high availibility), when a node goes down, the users are asked to login git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28208 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../filesys/repo/ContentDiskDriver.java | 9 +- .../filesys/repo/ContentDiskDriverTest.java | 471 +++++++++++++----- .../node/PropertyFieldProcessor.java | 22 + .../repo/template/FreeMarkerProcessor.java | 7 +- .../wcm/webproject/WebProjectServiceImpl.java | 19 +- 5 files changed, 379 insertions(+), 149 deletions(-) diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index f08052452c..142ac89dbe 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -3448,8 +3448,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Check permissions on the file/folder node if ( permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED) - throw new org.alfresco.repo.security.permissions.AccessDeniedException("No write access to " + name); - + { + throw new AccessDeniedException("No write access to " + name); + } + // Inhibit versioning for this transaction getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE); @@ -3459,7 +3461,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose()) { // Check deleting permissions on the node if action of deleting was configured only - if ((AccessStatus.DENIED == permissionService.hasPermission(nodeRef, PermissionService.DELETE)) && (null == fstate.findAttribute(CanDeleteWithoutPerms))) + if ((AccessStatus.DENIED == permissionService.hasPermission(nodeRef, PermissionService.DELETE)) + && ((null == fstate) || (null == fstate.findAttribute(CanDeleteWithoutPerms)))) { throw new org.alfresco.repo.security.permissions.AccessDeniedException("No delete access to " + name); } diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java index 797789529e..45dddff6b8 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java @@ -23,13 +23,13 @@ import java.io.InputStream; import java.io.Serializable; import java.net.InetAddress; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.transaction.UserTransaction; +import javax.xml.ws.Holder; import junit.framework.TestCase; @@ -39,6 +39,7 @@ 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.DiskSharedDevice; import org.alfresco.jlan.server.filesys.FileAction; @@ -56,6 +57,7 @@ import org.alfresco.repo.action.evaluator.NoConditionEvaluator; import org.alfresco.repo.management.subsystems.ApplicationContextFactory; import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transfer.TransferModel; @@ -63,6 +65,7 @@ import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.CompositeAction; +import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentService; @@ -70,13 +73,19 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; 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.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; @@ -88,6 +97,12 @@ import org.springframework.extensions.config.element.GenericConfigElement; */ public class ContentDiskDriverTest extends TestCase { + private static final String TEST_PROTOTYPE_NAME = "test"; + private static final String TEST_REMOTE_NAME = "remoteName"; + private static final String TEST_SERVER_NAME = "testServer"; + + private static final String TEST_USER_AUTHORITY = "userx"; + private Repository repositoryHelper; private CifsHelper cifsHelper; private ContentDiskDriver driver; @@ -97,6 +112,11 @@ public class ContentDiskDriverTest extends TestCase private ContentService contentService; private RuleService ruleService; private ActionService actionService; + private PersonService personService; + private MutableAuthenticationService authenticationService; + private PermissionService permissionService; + private OwnableService ownableService; + private FileFolderService fileFolderService; private static Log logger = LogFactory.getLog(ContentDiskDriverTest.class); @@ -123,6 +143,11 @@ public class ContentDiskDriverTest extends TestCase contentService = (ContentService)applicationContext.getBean("contentService"); ruleService = (RuleService)applicationContext.getBean("ruleService"); actionService = (ActionService)this.applicationContext.getBean("actionService"); + personService = (PersonService) this.applicationContext.getBean("personService"); + authenticationService = (MutableAuthenticationService) this.applicationContext.getBean("authenticationService"); + permissionService = (PermissionService) this.applicationContext.getBean("permissionService"); + ownableService = (OwnableService) this.applicationContext.getBean("ownableService"); + fileFolderService = (FileFolderService) this.applicationContext.getBean("fileFolderService"); assertNotNull("content disk driver is null", driver); assertNotNull("repositoryHelper is null", repositoryHelper); @@ -441,158 +466,158 @@ public class ContentDiskDriverTest extends TestCase /* * MER : I can't see what DeleteOnClose does. Test commented out */ - public void testSetFileInfo() throws Exception - { - logger.debug("testSetFileInfo"); - 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(); - - Date now = new Date(); - - // CREATE 6 hours ago - final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6); - // Modify one hour ago - final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1); - - class TestContext - { - NodeRef testNodeRef; - }; - - final TestContext testContext = new TestContext(); - - /** - * Step 1 : Create a new file in read/write mode and add some content. - * Call SetInfo to set the creation date - */ - int openAction = FileAction.CreateNotExist; - - final String FILE_NAME="testSetFileInfo.txt"; - final String FILE_PATH="\\"+FILE_NAME; - - final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); - - final NetworkFile file = driver.createFile(testSession, testConnection, params); - assertNotNull("file is null", file); - assertFalse("file is read only, should be read-write", file.isReadOnly()); - - RetryingTransactionCallback writeStuffCB = new RetryingTransactionCallback() { - - @Override - public Void execute() throws Throwable - { - byte[] stuff = "Hello World".getBytes(); - file.writeFile(stuff, stuff.length, 0, 0); - file.close(); // needed to actually flush content to node - - FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); - info.setFileInformationFlags(FileInfo.SetModifyDate); - info.setModifyDateTime(MODIFIED.getTime()); - driver.setFileInformation(testSession, testConnection, FILE_PATH, info); - return null; - } - }; - tran.doInTransaction(writeStuffCB); - - RetryingTransactionCallback validateCB = new RetryingTransactionCallback() { - - @Override - public Void execute() throws Throwable - { - NodeRef companyHome = repositoryHelper.getCompanyHome(); - NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); - testContext.testNodeRef = newNode; - assertNotNull("can't find new node", newNode); - Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT); - assertNotNull("content is null", content); - Date modified = (Date)nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED); - assertEquals("modified time not set correctly", MODIFIED, modified); - return null; - } - }; - tran.doInTransaction(validateCB); - - /** - * Step 2: Change the created date - */ - logger.debug("Step 2: Change the created date"); - RetryingTransactionCallback changeCreatedCB = new RetryingTransactionCallback() { - - @Override - public Void execute() throws Throwable - { - FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); - info.setFileInformationFlags(FileInfo.SetCreationDate); - info.setCreationDateTime(CREATED.getTime()); - driver.setFileInformation(testSession, testConnection, FILE_PATH, info); - return null; - } - }; - tran.doInTransaction(changeCreatedCB); - - RetryingTransactionCallback validateCreatedCB = new RetryingTransactionCallback() { - - @Override - public Void execute() throws Throwable - { - NodeRef companyHome = repositoryHelper.getCompanyHome(); - NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); - testContext.testNodeRef = newNode; - assertNotNull("can't find new node", newNode); - Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT); - assertNotNull("content is null", content); - Date created = (Date)nodeService.getProperty(newNode, ContentModel.PROP_CREATED); - assertEquals("created time not set correctly", CREATED, created); - return null; - } - }; - tran.doInTransaction(validateCreatedCB); - +// public void testSetFileInfo() throws Exception +// { +// logger.debug("testSetFileInfo"); +// 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(); +// +// Date now = new Date(); +// +// // CREATE 6 hours ago +// final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6); +// // Modify one hour ago +// final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1); +// +// class TestContext +// { +// NodeRef testNodeRef; +// }; +// +// final TestContext testContext = new TestContext(); +// // /** -// * Step 3: Test -// */ -// logger.debug("Step 3: test deleteOnClose"); -// RetryingTransactionCallback deleteOnCloseCB = new RetryingTransactionCallback() { +// * Step 1 : Create a new file in read/write mode and add some content. +// * Call SetInfo to set the creation date +// */ +// int openAction = FileAction.CreateNotExist; +// +// final String FILE_NAME="testSetFileInfo.txt"; +// final String FILE_PATH="\\"+FILE_NAME; +// +// final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); +// +// final NetworkFile file = driver.createFile(testSession, testConnection, params); +// assertNotNull("file is null", file); +// assertFalse("file is read only, should be read-write", file.isReadOnly()); +// +// RetryingTransactionCallback writeStuffCB = new RetryingTransactionCallback() { // // @Override // public Void execute() throws Throwable // { -// NetworkFile f2 = driver.openFile(testSession, testConnection, params); -// -// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); -// info.setFileInformationFlags(FileInfo.SetDeleteOnClose); -// driver.setFileInformation(testSession, testConnection, FILE_PATH, info); -// -// byte[] stuff = "Update".getBytes(); -// f2.writeFile(stuff, stuff.length, 0, 0); -// f2.close(); // needed to actually flush content to node -// -// return null; +// byte[] stuff = "Hello World".getBytes(); +// file.writeFile(stuff, stuff.length, 0, 0); +// file.close(); // needed to actually flush content to node +// +// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); +// info.setFileInformationFlags(FileInfo.SetModifyDate); +// info.setModifyDateTime(MODIFIED.getTime()); +// driver.setFileInformation(testSession, testConnection, FILE_PATH, info); +// return null; // } // }; -// tran.doInTransaction(deleteOnCloseCB); -// -// RetryingTransactionCallback validateDeleteOnCloseCB = new RetryingTransactionCallback() { +// tran.doInTransaction(writeStuffCB); +// +// RetryingTransactionCallback validateCB = new RetryingTransactionCallback() { // // @Override // public Void execute() throws Throwable // { // NodeRef companyHome = repositoryHelper.getCompanyHome(); // NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); -// assertNull("can still find new node", newNode); +// testContext.testNodeRef = newNode; +// assertNotNull("can't find new node", newNode); +// Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT); +// assertNotNull("content is null", content); +// Date modified = (Date)nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED); +// assertEquals("modified time not set correctly", MODIFIED, modified); // return null; // } // }; -// tran.doInTransaction(validateDeleteOnCloseCB); - - // clean up so we could run the test again - driver.deleteFile(testSession, testConnection, FILE_PATH); - - } // test set file info +// tran.doInTransaction(validateCB); +// +// /** +// * Step 2: Change the created date +// */ +// logger.debug("Step 2: Change the created date"); +// RetryingTransactionCallback changeCreatedCB = new RetryingTransactionCallback() { +// +// @Override +// public Void execute() throws Throwable +// { +// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); +// info.setFileInformationFlags(FileInfo.SetCreationDate); +// info.setCreationDateTime(CREATED.getTime()); +// driver.setFileInformation(testSession, testConnection, FILE_PATH, info); +// return null; +// } +// }; +// tran.doInTransaction(changeCreatedCB); +// +// RetryingTransactionCallback validateCreatedCB = new RetryingTransactionCallback() { +// +// @Override +// public Void execute() throws Throwable +// { +// NodeRef companyHome = repositoryHelper.getCompanyHome(); +// NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); +// testContext.testNodeRef = newNode; +// assertNotNull("can't find new node", newNode); +// Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT); +// assertNotNull("content is null", content); +// Date created = (Date)nodeService.getProperty(newNode, ContentModel.PROP_CREATED); +// assertEquals("created time not set correctly", CREATED, created); +// return null; +// } +// }; +// tran.doInTransaction(validateCreatedCB); +// +//// /** +//// * Step 3: Test +//// */ +//// logger.debug("Step 3: test deleteOnClose"); +//// RetryingTransactionCallback deleteOnCloseCB = new RetryingTransactionCallback() { +//// +//// @Override +//// public Void execute() throws Throwable +//// { +//// NetworkFile f2 = driver.openFile(testSession, testConnection, params); +//// +//// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH); +//// info.setFileInformationFlags(FileInfo.SetDeleteOnClose); +//// driver.setFileInformation(testSession, testConnection, FILE_PATH, info); +//// +//// byte[] stuff = "Update".getBytes(); +//// f2.writeFile(stuff, stuff.length, 0, 0); +//// f2.close(); // needed to actually flush content to node +//// +//// return null; +//// } +//// }; +//// tran.doInTransaction(deleteOnCloseCB); +//// +//// RetryingTransactionCallback validateDeleteOnCloseCB = new RetryingTransactionCallback() { +//// +//// @Override +//// public Void execute() throws Throwable +//// { +//// NodeRef companyHome = repositoryHelper.getCompanyHome(); +//// NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); +//// assertNull("can still find new node", newNode); +//// return null; +//// } +//// }; +//// tran.doInTransaction(validateDeleteOnCloseCB); +// +// // clean up so we could run the test again +// driver.deleteFile(testSession, testConnection, FILE_PATH); +// +// } // test set file info /** @@ -2512,7 +2537,177 @@ public class ContentDiskDriverTest extends TestCase }; tran.doInTransaction(deleteNodeCB, false, true); } //testDirListing - + + + public void testFileInformationUpdatingByEditorUserForAlf8808() throws Exception + { + final Holder editorFolder = new Holder(); + final Holder testFile = new Holder(); + + // Configuring test server with test server configuration and getting test tree connection for test shared device + ServerConfiguration config = new ServerConfiguration(ContentDiskDriverTest.TEST_SERVER_NAME); + TestServer server = new TestServer(ContentDiskDriverTest.TEST_SERVER_NAME, config); + DiskSharedDevice device = getDiskSharedDevice(); + final TreeConnection treeConnection = server.getTreeConnection(device); + + // Getting target entity for testing - ContentDiskDriver + final ContentDiskDriver deviceInterface = (ContentDiskDriver) treeConnection.getInterface(); + // Creating mock-session + final SrvSession session = new TestSrvSession(13, server, ContentDiskDriverTest.TEST_PROTOTYPE_NAME, ContentDiskDriverTest.TEST_REMOTE_NAME); + + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + try + { + NodeRef rootNode = repositoryHelper.getCompanyHome(); + // Creating test user to invite him as Editor for test content. This user will be created correctly (with person and authentication options) + createUser(ContentDiskDriverTest.TEST_USER_AUTHORITY, ContentDiskDriverTest.TEST_USER_AUTHORITY, rootNode); + // Safely creating folder for test content + editorFolder.value = getOrCreateNode(rootNode, PermissionService.EDITOR, ContentModel.TYPE_FOLDER).getFirst(); + // Creating test content which will be editable by user created above + testFile.value = getOrCreateNode(rootNode, "Test.txt", ContentModel.TYPE_CONTENT).getFirst(); + + // Applying 'Editor' role for test user to test file + permissionService.setPermission(testFile.value.getNodeRef(), ContentDiskDriverTest.TEST_USER_AUTHORITY, PermissionService.EDITOR, true); + + try + { + // Creating data for target method invocation + final FileInfo updatedInfo = new FileInfo(); + updatedInfo.setFileName(testFile.value.getName()); + updatedInfo.setFileId(DefaultTypeConverter.INSTANCE.intValue(testFile.value.getProperties().get(ContentModel.PROP_NODE_DBID))); + + // Testing ContentDiskDriver.setFileInformation() with test user authenticated who has 'Editor' role for test content. + // This method should fail if check on 'DELETE' permission was not moved to 'DeleteOnClose' context + AuthenticationUtil.runAs(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + deviceInterface.setFileInformation(session, treeConnection, testFile.value.getName(), updatedInfo); + return null; + } + }, ContentDiskDriverTest.TEST_USER_AUTHORITY); + } + catch (Exception e) + { + // Informing about test failure. Expected exception is 'org.alfresco.jlan.server.filesys.AccessDeniedException' + if (e.getCause() instanceof AccessDeniedException) + { + fail("For user='" + TEST_USER_AUTHORITY + "' " + e.getCause().toString()); + } + else + { + fail("Unexpected exception was caught: " + e.toString()); + } + } + } + finally + { + // Cleaning all test data and rolling back transaction to revert all introduced changes during testing + + if (authenticationService.authenticationExists(ContentDiskDriverTest.TEST_USER_AUTHORITY)) + { + authenticationService.deleteAuthentication(ContentDiskDriverTest.TEST_USER_AUTHORITY); + } + + if (personService.personExists(ContentDiskDriverTest.TEST_USER_AUTHORITY)) + { + personService.deletePerson(ContentDiskDriverTest.TEST_USER_AUTHORITY); + } + + try + { + if (null != testFile.value) + { + nodeService.deleteNode(testFile.value.getNodeRef()); + } + } + catch (Exception e) + { + // Doing nothing + } + + try + { + if (null != editorFolder.value) + { + nodeService.deleteNode(editorFolder.value.getNodeRef()); + } + } + catch (Exception e) + { + // Doing nothing + } + } + + return null; + } + }, false, true); + } + + /** + * Searching for file object with specified name or creating new one if such object is not exist + * + * @param parentRef - {@link NodeRef} of desired parent object + * @param name - {@link String} value for name of desired file object + * @param type - {@link QName} instance which determines type of the object. It may be cm:content, cm:folder etc (see {@link ContentModel}) + * @return {@link Pair}<{@link org.alfresco.service.cmr.model.FileInfo}, {@link Boolean}> instance which contains {@link NodeRef} of newly created object and + * true value if file object with specified name was not found or {@link NodeRef} of existent file object and false in other case + */ + private Pair getOrCreateNode(NodeRef parentRef, String name, QName type) + { + NodeRef result = nodeService.getChildByName(parentRef, ContentModel.ASSOC_CONTAINS, name); + Boolean created = false; + if (null == result) + { + result = nodeService.getChildByName(parentRef, ContentModel.ASSOC_CHILDREN, name); + } + if (created = (null == result)) + { + result = fileFolderService.create(parentRef, name, type).getNodeRef(); + } + return new Pair(fileFolderService.getFileInfo(result), created); + } + + /** + * Creates correct user entity with correct user home space, person and authentication with password equal to 'password' options if these options are not exist. + * Method searches for space with name equal to 'name' to make it user home space or creates new folder with name equal to 'name'. All required + * permissions and roles will be applied to user home space + * + * @param name - {@link String} value which contains new user name + * @param password - {@link String} value of text password for new user + * @param parentNodeRef - {@link NodeRef} instance of parent folder where user home space should be found or created + */ + private void createUser(String name, String password, NodeRef parentNodeRef) + { + Map properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, name); + Pair userHome = getOrCreateNode(parentNodeRef, name, ContentModel.TYPE_FOLDER); + if (userHome.getSecond()) + { + NodeRef nodeRef = userHome.getFirst().getNodeRef(); + permissionService.setPermission(nodeRef, name, permissionService.getAllPermission(), true); + permissionService.setPermission(nodeRef, permissionService.getAllAuthorities(), PermissionService.CONSUMER, true); + permissionService.setPermission(nodeRef, permissionService.getOwnerAuthority(), permissionService.getAllPermission(), true); + ownableService.setOwner(nodeRef, name); + permissionService.setInheritParentPermissions(nodeRef, false); + + properties.put(ContentModel.PROP_HOMEFOLDER, nodeRef); + if (!personService.personExists(name)) + { + personService.createPerson(properties); + } + if (!authenticationService.authenticationExists(name)) + { + authenticationService.createAuthentication(name, password.toCharArray()); + } + } + } + /** * Test server */ diff --git a/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java index b31b72c071..af2b755c8f 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java @@ -24,6 +24,7 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DAT import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; @@ -42,6 +43,7 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.Period; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.StringUtils; @@ -116,6 +118,26 @@ public class PropertyFieldProcessor extends QNameFieldProcessor values = (Collection) value; + + // if the non empty collection is a List of Date objects + // we need to convert each date to a ISO8601 format + if (value instanceof List && !values.isEmpty()) + { + List list = (List)values; + if (list.get(0) instanceof Date) + { + List isoDates = new ArrayList(list.size()); + for (Object date : list) + { + isoDates.add(ISO8601DateFormat.format((Date)date)); + } + + // return the ISO formatted dates as a comma delimited string + return StringUtils.collectionToCommaDelimitedString(isoDates); + } + } + + // return everything else using toString() return StringUtils.collectionToCommaDelimitedString(values); } else if (value instanceof ContentData) diff --git a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java index f9df99b289..bdcd90b815 100644 --- a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java +++ b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java @@ -38,9 +38,11 @@ import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.repository.TemplateValueConverter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; import freemarker.cache.MruCacheStorage; import freemarker.cache.StringTemplateLoader; +import freemarker.core.Environment; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; @@ -194,7 +196,10 @@ public class FreeMarkerProcessor extends BaseProcessor implements TemplateProces { // perform the template processing against supplied data model Object freeMarkerModel = convertToFreeMarkerModel(model); - t.process(freeMarkerModel, out); + Environment env = t.createProcessingEnvironment(freeMarkerModel, out); + // set the locale to ensure dates etc. are appropriate localised + env.setLocale(I18NUtil.getLocale()); + env.process(); } catch (Throwable err) { diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java index 02a66f1705..e329f343c5 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java @@ -305,7 +305,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService inviteWebUser(wpNodeRef, AuthenticationUtil.getFullyAuthenticatedUser(), WCMUtil.ROLE_CONTENT_MANAGER, true); // Bind the post-commit transaction listener with data required for virtualization server notification - CreateWebProjectTransactionListener tl = new CreateWebProjectTransactionListener(wpStoreId); + CreateWebAppTransactionListener tl = new CreateWebAppTransactionListener(wpStoreId, WCMUtil.DIR_ROOT); AlfrescoTransactionSupport.bindListener(tl); if (logger.isDebugEnabled()) @@ -357,7 +357,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService new PropertyValue(DataTypeDefinition.TEXT, webAppDescription)); } - + // Snapshot the store with the empty webapp avmService.createSnapshot(stagingStoreId, null, null); @@ -365,6 +365,9 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService } }, AuthenticationUtil.getSystemUserName()); + CreateWebAppTransactionListener tl = new CreateWebAppTransactionListener(wpInfo.getStoreId(), webAppName); + AlfrescoTransactionSupport.bindListener(tl); + if (logger.isDebugEnabled()) { logger.debug("Created web app: "+webAppName+" in "+(System.currentTimeMillis()-start)+" ms (store id: "+wpInfo.getStoreId()+")"); @@ -1541,15 +1544,17 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService /** - * Transaction listener - invoked after commit + * Create WebProject/WebApp Transaction listener - invoked after commit */ - private class CreateWebProjectTransactionListener extends TransactionListenerAdapter + private class CreateWebAppTransactionListener extends TransactionListenerAdapter { private String wpStoreId; + private String webApp; - public CreateWebProjectTransactionListener(String wpStoreId) + public CreateWebAppTransactionListener(String wpStoreId, String webApp) { this.wpStoreId = wpStoreId; + this.webApp = webApp; } /** @@ -1561,11 +1566,11 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService // post-commit if (wpStoreId != null) { - // update the virtualisation server with the default ROOT webapp path + // update the virtualisation server with webapp // performed after the main txn has committed successfully String newStoreName = WCMUtil.buildStagingStoreName(wpStoreId); - String path = WCMUtil.buildStoreWebappPath(newStoreName, WCMUtil.DIR_ROOT); + String path = WCMUtil.buildStoreWebappPath(newStoreName, webApp); WCMUtil.updateVServerWebapp(virtServerRegistry, path, true); }