diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index d7a8fe6f13..dadad1640d 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -406,4 +406,13 @@ + + + + + + false + + + diff --git a/config/alfresco/messages/action-config.properties b/config/alfresco/messages/action-config.properties index 84c4e57c15..f816114c55 100644 --- a/config/alfresco/messages/action-config.properties +++ b/config/alfresco/messages/action-config.properties @@ -90,3 +90,6 @@ start-workflow.description=This will start a workflow for the matched items. simple-avm-submit.title=Simple Direct Submit simple-avm-submit.description=This will submit any newer nodes in the matched item to the corresponding staging. +simple-avm-promote.title=Simple Sandbox Promotion +simple-avm-promote.description=This promotes any newer nodes in the matched item to the specified target sandbox. +simple-avm-promote.target-store.display-label=The name of the target AVM store. diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 234dfa772c..6d6e25ae52 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -351,7 +351,7 @@ public class AVMRepository String dstName) { // This is about as ugly as it gets. - if (dstPath.indexOf(srcPath + srcName) == 0) + if ((dstPath + "/").indexOf(srcPath + srcName + "/") == 0) { throw new AVMCycleException("Cyclic rename."); } diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index 401a8d1b27..3075347832 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -31,6 +31,8 @@ import java.util.Set; import java.util.TreeMap; import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.avm.actions.SimpleAVMPromoteAction; import org.alfresco.repo.avm.actions.SimpleAVMSubmitAction; import org.alfresco.repo.avm.util.BulkLoader; import org.alfresco.repo.domain.PropertyValue; @@ -53,6 +55,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; /** * Big test of AVM behavior. @@ -60,6 +63,45 @@ import org.alfresco.service.transaction.TransactionService; */ public class AVMServiceTest extends AVMServiceTestBase { + /** + * Test the promote action. + */ + public void testPromoteAction() + { + try + { + setupBasicTree(); + fService.createDirectory("main:/", "appBase"); + fService.rename("main:/", "a", "main:/appBase", "a"); + fService.rename("main:/", "d", "main:/appBase", "d"); + fService.createSnapshot("main"); + fService.createAVMStore("source"); + fService.createLayeredDirectory("main:/appBase", "source:/", "appBase"); + fService.getFileOutputStream("source:/appBase/a/b/c/foo").close(); + final ActionImpl action = new ActionImpl(AVMNodeConverter.ToNodeRef(-1, "source:/appBase/a"), + GUID.generate(), + SimpleAVMPromoteAction.NAME); + action.setParameterValue(SimpleAVMPromoteAction.PARAM_TARGET_STORE, "main"); + final SimpleAVMPromoteAction promote = (SimpleAVMPromoteAction)fContext.getBean("simple-avm-promote"); + class TxnWork implements TransactionUtil.TransactionWork + { + public Object doWork() throws Exception + { + promote.execute(action, AVMNodeConverter.ToNodeRef(-1, "source:/appBase/a")); + return null; + } + }; + TransactionUtil.executeInUserTransaction((TransactionService)fContext.getBean("transactionComponent"), + new TxnWork()); + assertEquals(0, fSyncService.compare(-1, "source:/appBase", -1, "main:/appBase").size()); + } + catch (Exception e) + { + e.printStackTrace(System.err); + fail(); + } + } + /** * Test a noodle update. */ @@ -96,28 +138,28 @@ public class AVMServiceTest extends AVMServiceTestBase try { fService.createAVMStore("foo-staging"); - fService.createDirectory("foo-staging:/", "layer"); - fService.createDirectory("foo-staging:/layer", "a"); - fService.createDirectory("foo-staging:/layer/a","b"); - fService.createDirectory("foo-staging:/layer/a/b", "c"); - fService.createFile("foo-staging:/layer/a/b/c", "foo").close(); - fService.createFile("foo-staging:/layer/a/b/c", "bar").close(); + fService.createDirectory("foo-staging:/", "appBase"); + fService.createDirectory("foo-staging:/appBase", "a"); + fService.createDirectory("foo-staging:/appBase/a","b"); + fService.createDirectory("foo-staging:/appBase/a/b", "c"); + fService.createFile("foo-staging:/appBase/a/b/c", "foo").close(); + fService.createFile("foo-staging:/appBase/a/b/c", "bar").close(); fService.createAVMStore("area"); fService.setStoreProperty("area", QName.createQName(null, ".website.name"), new PropertyValue(null, "foo")); - fService.createLayeredDirectory("foo-staging:/layer", "area:/", "layer"); - fService.createFile("area:/layer", "figs").close(); - fService.getFileOutputStream("area:/layer/a/b/c/foo").close(); - fService.removeNode("area:/layer/a/b/c/bar"); + fService.createLayeredDirectory("foo-staging:/appBase", "area:/", "appBase"); + fService.createFile("area:/appBase", "figs").close(); + fService.getFileOutputStream("area:/appBase/a/b/c/foo").close(); + fService.removeNode("area:/appBase/a/b/c/bar"); List diffs = - fSyncService.compare(-1, "area:/layer", -1, "foo-staging:/layer"); + fSyncService.compare(-1, "area:/appBase", -1, "foo-staging:/appBase"); assertEquals(3, diffs.size()); final SimpleAVMSubmitAction action = (SimpleAVMSubmitAction)fContext.getBean("simple-avm-submit"); class TxnWork implements TransactionUtil.TransactionWork { public Object doWork() throws Exception { - action.execute(null, AVMNodeConverter.ToNodeRef(-1, "area:/layer")); + action.execute(null, AVMNodeConverter.ToNodeRef(-1, "area:/appBase")); return null; } }; @@ -125,7 +167,7 @@ public class AVMServiceTest extends AVMServiceTestBase TransactionUtil.executeInUserTransaction((TransactionService)fContext.getBean("transactionComponent"), worker); diffs = - fSyncService.compare(-1, "area:/layer", -1, "foo-staging:/layer"); + fSyncService.compare(-1, "area:/appBase", -1, "foo-staging:/appBase"); assertEquals(0, diffs.size()); } catch (Exception e) diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index ea1394abc1..70504ff235 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -594,7 +594,7 @@ public class AVMSyncServiceImpl implements AVMSyncService { throw new AVMBadArgumentException("Illegal null path."); } - AVMNodeDescriptor layerNode = fAVMService.lookup(-1, layerPath); + AVMNodeDescriptor layerNode = fAVMService.lookup(-1, layerPath, true); if (layerNode == null) { throw new AVMNotFoundException("Not found: " + layerPath); diff --git a/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java b/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java new file mode 100644 index 0000000000..6561cb2686 --- /dev/null +++ b/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ + +package org.alfresco.repo.avm.actions; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.avmsync.AVMDifference; +import org.alfresco.service.cmr.avmsync.AVMSyncException; +import org.alfresco.service.cmr.avmsync.AVMSyncService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * An ActionExecuter that promotes content from one store to another. + * The NodeRef argument is in the source AVMStore. The 'target-store' + * mandatory argument is the name of the destination store. + * @author britt + */ +public class SimpleAVMPromoteAction extends ActionExecuterAbstractBase +{ + public static final String NAME = "simple-avm-promote"; + public static final String PARAM_TARGET_STORE = "target-store"; + + /** + * The AVMSyncService instance. + */ + private AVMSyncService fAVMSyncService; + + /** + * Default constructor. + */ + public SimpleAVMPromoteAction() + { + super(); + } + + /** + * Set the AVMSyncService instance. + * @param avmSyncService + */ + public void setAvmSyncService(AVMSyncService avmSyncService) + { + fAVMSyncService = avmSyncService; + } + + /** + * Do a promotion of an asset from one sandbox to another. + * Takes a mandatory parameter 'target-store' which is the name of the + * target AVMStore. + * @param action The source of parameters. + * @param actionedUponNodeRef The source AVM NodeRef. + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String targetStoreName = (String)action.getParameterValue(PARAM_TARGET_STORE); + // Crack the NodeRef. + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(actionedUponNodeRef); + int version = (Integer)avmVersionPath[0]; + String path = (String)avmVersionPath[1]; + // Get store name and path parts. + String [] storePath = path.split(":"); + if (storePath.length != 2) + { + throw new AVMSyncException("Malformed source path: " + path); + } + // Compute the corresponding target path. + String targetPath = targetStoreName + ":" + storePath[1]; + // Find the differences. + List diffs = + fAVMSyncService.compare(version, path, -1, targetPath); + // Do the promote. + fAVMSyncService.update(diffs, true, true, false, false); + // Flatten the source on top of the destination. + fAVMSyncService.flatten(storePath[0] + ":/appBase", + targetStoreName + ":/appBase"); + } + + /** + * Define needed parameters. + * @param paramList The List of ParameterDefinitions to add to. + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add(new ParameterDefinitionImpl(PARAM_TARGET_STORE, + DataTypeDefinition.TEXT, + true, + getParamDisplayLabel(PARAM_TARGET_STORE))); + } +} diff --git a/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java b/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java index c4e1dfe656..ec386c1496 100644 --- a/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java +++ b/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java @@ -26,6 +26,7 @@ import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avmsync.AVMDifference; +import org.alfresco.service.cmr.avmsync.AVMSyncException; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -92,6 +93,10 @@ public class SimpleAVMSubmitAction extends ActionExecuterAbstractBase String path = (String)avmVersionPath[1]; // Get store name and path parts. String [] storePath = path.split(":"); + if (storePath.length != 2) + { + throw new AVMSyncException("Malformed source path " + path); + } // Get the .website.name property. PropertyValue wsProp = fAVMService.getStoreProperty(storePath[0], @@ -111,7 +116,7 @@ public class SimpleAVMSubmitAction extends ActionExecuterAbstractBase // Do the update. fAVMSyncService.update(diffs, true, true, false, false); // Cleanup by flattening the source relative to the destination. - fAVMSyncService.flatten(path, avmDest); + fAVMSyncService.flatten(storePath[0] + ":/appBase", websiteName + "-staging:/appBase"); } /**