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); }