diff --git a/config/alfresco/transfer-service-context.xml b/config/alfresco/transfer-service-context.xml index f3df681afb..a098c5c51b 100644 --- a/config/alfresco/transfer-service-context.xml +++ b/config/alfresco/transfer-service-context.xml @@ -153,6 +153,8 @@ + + > orphans = new HashMap>(89); + + /** + * node ref mapping from source to destination categories + */ + private Map categoryMap = new HashMap(); /** * @param transferId @@ -277,6 +294,8 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB // First, create a shallow copy of the supplied property map... Map props = new HashMap(node.getProperties()); + processCategories(props, node.getManifestCategories()); + injectTransferred(props); // Split out the content properties and sanitise the others @@ -532,6 +551,8 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB Map props = new HashMap(node.getProperties()); Map existingProps = nodeService.getProperties(nodeToUpdate); + processCategories(props, node.getManifestCategories()); + // inject transferred properties/aspect here injectTransferred(props); @@ -966,6 +987,181 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB { return permissionService; } + + /** + * Process categories. + * + * CRUD of Categories and Tags - also maps noderefs of type d:content from source to target + * + * + * @param properties + * @param manifestCategories + */ + private void processCategories(Map properties, Map manifestCategories) + { + if(manifestCategories != null) + { + for(Map.Entry val : properties.entrySet()) + { + PropertyDefinition def = dictionaryService.getProperty(val.getKey()); + if(def != null) + { + if(def.getDataType().getName().isMatch(DataTypeDefinition.CATEGORY)) + { + Serializable thing = val.getValue(); + if(thing != null) + { + if(def.isMultiValued()) + { + if(thing instanceof java.util.Collection) + { + List newCategories = new ArrayList(); + java.util.Collection c = (java.util.Collection)thing; + for(NodeRef sourceCategoryNodeRef : c) + { + if(log.isDebugEnabled()) + { + log.debug("sourceCategoryNodeRef" + sourceCategoryNodeRef); + } + // substitute target node ref fot source node ref + NodeRef targetNodeRef = processCategory(sourceCategoryNodeRef, manifestCategories); + newCategories.add(targetNodeRef); + } + // substitute target node refs for source node refs + properties.put(val.getKey(), (Serializable)newCategories); + } + else + { + throw new AlfrescoRuntimeException("Multi valued object is not a collection" + val.getKey() ); + } + } + else + { + NodeRef sourceCategoryNodeRef = (NodeRef)thing; + if(log.isDebugEnabled()) + { + log.debug("sourceCategoryNodeRef:" + sourceCategoryNodeRef); + } + NodeRef targetNodeRef = processCategory(sourceCategoryNodeRef, manifestCategories); + // substitute target node ref for source node ref + properties.put(val.getKey(), targetNodeRef); + } + } + } + } + } + } + } + + /** + * process category - maps the category node ref from the source system to the target system. + * + * It will lazily create any missing categories and tags as it executes. + * + * @param sourceCategoryNodeRef + * @param manifestCategories + * @return targetNodeRef + */ + private NodeRef processCategory(final NodeRef sourceCategoryNodeRef, final Map manifestCategories) + { + + // first check cache to see whether we have already mapped this category + NodeRef destinationNodeRef = categoryMap.get(sourceCategoryNodeRef); + if(destinationNodeRef != null) + { + return destinationNodeRef; + } + + // No we havn't seen this category before, have we got the details in the manifest + ManifestCategory category = manifestCategories.get(sourceCategoryNodeRef); + if(category != null) + { + final String path = category.getPath(); + final Path catPath = PathHelper.stringToPath(path); + + final Path.Element aspectName = catPath.get(2); + final QName aspectQName = QName.createQName(aspectName.getElementString()); + + if(aspectQName.equals(ContentModel.ASPECT_TAGGABLE)) + { + Path.Element tagName = catPath.get(3); + // Category is a tag + QName tagQName = QName.createQName(tagName.getElementString()); + destinationNodeRef = taggingService.getTagNodeRef(sourceCategoryNodeRef.getStoreRef(), tagQName.getLocalName()); + if(destinationNodeRef != null) + { + log.debug("found existing tag" + tagQName.getLocalName()); + categoryMap.put(sourceCategoryNodeRef, destinationNodeRef); + return destinationNodeRef; + } + destinationNodeRef = taggingService.createTag(sourceCategoryNodeRef.getStoreRef(), tagQName.getLocalName()); + if(destinationNodeRef != null) + { + log.debug("created new tag" + tagQName.getLocalName()); + categoryMap.put(sourceCategoryNodeRef, destinationNodeRef); + return destinationNodeRef; + } + } + else + { + // Categories are finniky about permissions, so run as system + RunAsWork processCategory = new RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + QName rootCatName = QName.createQName(catPath.get(3).getElementString()); + + Collection roots = categoryService.getRootCategories(sourceCategoryNodeRef.getStoreRef(), aspectQName); + + /** + * Get the root category node ref + */ + NodeRef rootCategoryNodeRef = null; + for(ChildAssociationRef ref : roots) + { + if(ref.getQName().equals(rootCatName)) + { + rootCategoryNodeRef = ref.getChildRef(); + break; + } + } + + if(rootCategoryNodeRef == null) + { + // Root category does not exist + rootCategoryNodeRef = categoryService.createRootCategory(sourceCategoryNodeRef.getStoreRef(), aspectQName, rootCatName.getLocalName()); + } + + NodeRef workingNodeRef = rootCategoryNodeRef; + // Root category does already exist - step through any sub-categories + for(int i = 4; i < catPath.size(); i++) + { + Path.Element element = catPath.get(i); + QName subCatName = QName.createQName(element.toString()); + ChildAssociationRef child = categoryService.getCategory(workingNodeRef, aspectQName, subCatName.getLocalName()); + if(child != null) + { + workingNodeRef = child.getChildRef(); + } + else + { + workingNodeRef = categoryService.createCategory(workingNodeRef, subCatName.getLocalName()); + } + } + return workingNodeRef; + } + + }; + destinationNodeRef = AuthenticationUtil.runAs(processCategory, AuthenticationUtil.SYSTEM_USER_NAME); + categoryMap.put(sourceCategoryNodeRef, destinationNodeRef); + return destinationNodeRef; + } + } // if manifest category exists + + return sourceCategoryNodeRef; + + } /** * inject transferred @@ -1010,4 +1206,24 @@ public class RepoPrimaryManifestProcessorImpl extends AbstractManifestProcessorB return alienProcessor; } + public CategoryService getCategoryService() + { + return categoryService; + } + + public void setCategoryService(CategoryService categoryService) + { + this.categoryService = categoryService; + } + + public TaggingService getTaggingService() + { + return taggingService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + } diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java index 30f290efca..d25dee7bdd 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java @@ -576,6 +576,8 @@ public class TransferServiceImpl2 implements TransferService2 } }; eventProcessor.addObserver(reportCallback); + + TransferContext transferContext = new TransferContext(); // start transfer ClientTransferState clientState = ClientTransferState.Begin; @@ -589,7 +591,7 @@ public class TransferServiceImpl2 implements TransferService2 { eventProcessor.start(); - manifest = createManifest(definition, localRepositoryId, fromVersion); + manifest = createManifest(definition, localRepositoryId, fromVersion, transferContext); logger.debug("transfer begin"); target = getTransferTarget(targetName); checkTargetEnabled(target); @@ -919,7 +921,7 @@ public class TransferServiceImpl2 implements TransferService2 } } - private File createManifest(TransferDefinition definition, String repositoryId, TransferVersion fromVersion) + private File createManifest(TransferDefinition definition, String repositoryId, TransferVersion fromVersion, TransferContext transferContext) throws IOException, SAXException { // which nodes to write to the snapshot @@ -966,7 +968,7 @@ public class TransferServiceImpl2 implements TransferService2 { for (NodeRef nodeRef : nodes) { - TransferManifestNode node = transferManifestNodeFactory.createTransferManifestNode(nodeRef, definition); + TransferManifestNode node = transferManifestNodeFactory.createTransferManifestNode(nodeRef, definition, transferContext); formatter.writeTransferManifestNode(node); } } @@ -974,7 +976,7 @@ public class TransferServiceImpl2 implements TransferService2 { for (NodeRef nodeRef : nodesToRemove) { - TransferManifestNode node = transferManifestNodeFactory.createTransferManifestNode(nodeRef, definition, + TransferManifestNode node = transferManifestNodeFactory.createTransferManifestNode(nodeRef, definition, transferContext, true); formatter.writeTransferManifestNode(node); } diff --git a/source/java/org/alfresco/repo/transfer/manifest/ManifestModel.java b/source/java/org/alfresco/repo/transfer/manifest/ManifestModel.java index 3a1c01dca1..b38bf19578 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/ManifestModel.java +++ b/source/java/org/alfresco/repo/transfer/manifest/ManifestModel.java @@ -57,6 +57,9 @@ public interface ManifestModel extends TransferModel static final String LOCALNAME_ELEMENT_CONTENT_HEADER = "content"; static final String LOCALNAME_ELEMENT_ACL = "acl"; static final String LOCALNAME_ELEMENT_ACL_PERMISSION = "permission"; + static final String LOCALNAME_ELEMENT_CATEGORIES = "categories"; + static final String LOCALNAME_ELEMENT_CATEGORY = "category"; + // Manifest file prefix static final String MANIFEST_PREFIX = "xfer"; diff --git a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactory.java b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactory.java index 76895979e4..a5e954aa13 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactory.java +++ b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactory.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.transfer.manifest; +import org.alfresco.repo.transfer.TransferContext; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.transfer.TransferDefinition; @@ -29,9 +30,10 @@ public interface TransferManifestNodeFactory * specifying false as the value of the forceDelete parameter. * @param nodeRef The identifier of the node to be distilled for transfer * @param definition The transfer definition against which the node is being transferred + * @param transferContext internal runtime context of a transfer * @return An object that holds a snapshot of the state of the specified node suitable for transfer elsewhere. */ - TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition); + TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext); /** * Create an object that represents the specified node in a form that can be used to transfer it elsewhere @@ -40,7 +42,8 @@ public interface TransferManifestNodeFactory * @param forceDelete If this flag is set then the returned TransferManifestNode object will represent the removal * of the specified node, even if the node still exists in this repository. This allows a node to be removed from the * target repository even if it hasn't been removed in the source repository. + * @param transferContext internal runtime context of a transfer * @return An object that holds a snapshot of the state of the specified node suitable for transfer elsewhere. */ - TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, boolean forceDelete); + TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext, boolean forceDelete); } diff --git a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactoryImpl.java b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactoryImpl.java index 4c20c7e5e7..506508ed5a 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactoryImpl.java +++ b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNodeFactoryImpl.java @@ -27,6 +27,8 @@ import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.repo.transfer.TransferContext; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -60,12 +62,12 @@ public class TransferManifestNodeFactoryImpl implements TransferManifestNodeFact //NOOP } - public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition) + public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext) { - return createTransferManifestNode(nodeRef, definition, false); + return createTransferManifestNode(nodeRef, definition, transferContext, false); } - public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, boolean forceDelete) + public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext, boolean forceDelete) { NodeRef.Status status = nodeService.getNodeStatus(nodeRef); @@ -203,6 +205,57 @@ public class TransferManifestNodeFactoryImpl implements TransferManifestNodeFact } } acl.setPermissions(mps); + + /** + * Expand d:category information so we can re-create on target + */ + Map categories = new HashMap(); + + Map properties = node.getProperties(); + + for(Map.Entry val : properties.entrySet()) + { + PropertyDefinition def = dictionaryService.getProperty(val.getKey()); + if(def != null) + { + if(def.getDataType().getName().isMatch(DataTypeDefinition.CATEGORY)) + { + if(def.isMultiValued()) + { + Serializable thing = val.getValue(); + if(thing instanceof java.util.Collection) + { + java.util.Collection c = (java.util.Collection)thing; + for(NodeRef categoryNodeRef : c) + { + if(categoryNodeRef != null) + { + categories.put(categoryNodeRef, getManifestCategory(transferContext, categoryNodeRef)); + } + } + } + else + { + NodeRef categoryNodeRef = (NodeRef)val.getValue(); + if(categoryNodeRef != null) + { + categories.put(categoryNodeRef, getManifestCategory(transferContext, categoryNodeRef)); + } + } + } + else + { + NodeRef categoryNodeRef = (NodeRef)val.getValue(); + if(categoryNodeRef != null) + { + categories.put(categoryNodeRef, getManifestCategory(transferContext, categoryNodeRef)); + } + } + } + } + } + + node.setManifestCategories(categories); return node; } @@ -264,6 +317,22 @@ public class TransferManifestNodeFactoryImpl implements TransferManifestNodeFact return filteredProperties; } } + + private ManifestCategory getManifestCategory(TransferContext transferContext, NodeRef categoryNodeRef) + { + ManifestCategory c = transferContext.getManifestCategoriesCache().get(categoryNodeRef); + + if(c != null) + { + return c; + } + c = new ManifestCategory(); + + Path p = nodeService.getPath(categoryNodeRef); + c.setPath(p.toString()); + transferContext.getManifestCategoriesCache().put(categoryNodeRef, c); + return c; + } public void setNodeService(NodeService nodeService) { diff --git a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNormalNode.java b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNormalNode.java index 9c9867c397..3d8acde77e 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNormalNode.java +++ b/source/java/org/alfresco/repo/transfer/manifest/TransferManifestNormalNode.java @@ -51,6 +51,9 @@ public class TransferManifestNormalNode implements TransferManifestNode private List targetAssocs; private Path parentPath; private ManifestAccessControl accessControl; + + // NodeRef is noderef of type d:category ManifestCategory provides the extra meta-data + private Map categories; public void setNodeRef(NodeRef nodeRef) { @@ -190,5 +193,15 @@ public class TransferManifestNormalNode implements TransferManifestNode { this.ancestorType = ancestorType; } - + + public void setManifestCategories(Map categories) + { + this.categories = categories; + } + + public Map getManifestCategories() + { + return this.categories; + } + } diff --git a/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestReader.java b/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestReader.java index 0b985a5708..2153d79a1e 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestReader.java +++ b/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestReader.java @@ -349,6 +349,18 @@ public class XMLTransferManifestReader extends DefaultHandler implements Content perm.setStatus(status); props.put("permission", perm); } + else if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CATEGORIES)) + { + props.put("categories", new HashMap()); + } + else if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CATEGORY)) + { + ManifestCategory cat = new ManifestCategory(); + String path = (String)atts.getValue("", "path"); + cat.setPath(path); + props.put("category", cat); + } + } // if transfer URI } // startElement @@ -634,6 +646,18 @@ public class XMLTransferManifestReader extends DefaultHandler implements Content ManifestPermission permission = (ManifestPermission)props.get("permission"); acl.addPermission(permission); } + else if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CATEGORIES)) + { + TransferManifestNormalNode node = (TransferManifestNormalNode)props.get("node"); + Map categories = (Map )props.get("categories"); + node.setManifestCategories(categories); + } + else if(elementName.equals(ManifestModel.LOCALNAME_ELEMENT_CATEGORY)) + { + Map categories = (Map )props.get("categories"); + ManifestCategory category = (ManifestCategory)props.get("category"); + categories.put(new NodeRef(buffer.toString().trim()),category); + } } // if transfer URI diff --git a/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestWriter.java b/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestWriter.java index aa47b16946..ee0342dcc9 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestWriter.java +++ b/source/java/org/alfresco/repo/transfer/manifest/XMLTransferManifestWriter.java @@ -36,6 +36,7 @@ import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.repository.datatype.TypeConversionException; @@ -281,6 +282,8 @@ public class XMLTransferManifestWriter implements TransferManifestWriter { writePrimaryParent(node.getPrimaryParentAssoc(), node.getParentPath()); } + + writeCategories(node.getManifestCategories()); writeAspects(node.getAspects()); @@ -300,6 +303,42 @@ public class XMLTransferManifestWriter implements TransferManifestWriter ManifestModel.LOCALNAME_ELEMENT_NODE, PREFIX + ":" + ManifestModel.LOCALNAME_ELEMENT_NODE); } + + private void writeCategories(Map categories) throws SAXException + { + writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, + ManifestModel.LOCALNAME_ELEMENT_PROPERTIES, PREFIX + ":" + + ManifestModel.LOCALNAME_ELEMENT_CATEGORIES, EMPTY_ATTRIBUTES); + if (categories != null) + { + for (Entry entry : categories.entrySet()) + { + writeCategory(entry.getKey(), entry.getValue()); + } + } + + writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, + ManifestModel.LOCALNAME_ELEMENT_PROPERTIES, PREFIX + ":" + + ManifestModel.LOCALNAME_ELEMENT_CATEGORIES); + } + + @SuppressWarnings("unchecked") + private void writeCategory (NodeRef nodeRef, ManifestCategory value) throws SAXException + { + + AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(TransferModel.TRANSFER_MODEL_1_0_URI, "path", "path", "String", + value.getPath()); + writer.startElement(TransferModel.TRANSFER_MODEL_1_0_URI, + ManifestModel.LOCALNAME_ELEMENT_CATEGORY, PREFIX + ":" + + ManifestModel.LOCALNAME_ELEMENT_CATEGORY, attributes); + writeValue(nodeRef); + + writer.endElement(TransferModel.TRANSFER_MODEL_1_0_URI, + ManifestModel.LOCALNAME_ELEMENT_CATEGORY, PREFIX + ":" + + ManifestModel.LOCALNAME_ELEMENT_CATEGORY); + } + private void writeProperties(Map properties) throws SAXException { diff --git a/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java b/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java index 072cff450f..26c5eef80e 100644 --- a/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java @@ -69,6 +69,8 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.cmr.search.CategoryService.Depth; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchService; @@ -76,6 +78,7 @@ import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.cmr.transfer.TransferCallback; import org.alfresco.service.cmr.transfer.TransferDefinition; import org.alfresco.service.cmr.transfer.TransferEvent; @@ -125,6 +128,8 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest private DescriptorService descriptorService; private CopyService copyService; private Descriptor serverDescriptor; + private TaggingService taggingService; + private CategoryService categoryService; String COMPANY_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home"; String GUEST_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home/{http://www.alfresco.org/model/application/1.0}guest_home"; @@ -159,6 +164,8 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest this.personService = (PersonService)this.applicationContext.getBean("PersonService"); this.descriptorService = (DescriptorService)this.applicationContext.getBean("DescriptorService"); this.copyService = (CopyService)this.applicationContext.getBean("CopyService"); + this.taggingService = ((TaggingService)this.applicationContext.getBean("TaggingService")); + this.categoryService = (CategoryService)this.applicationContext.getBean("CategoryService"); this.serverDescriptor = descriptorService.getServerDescriptor(); @@ -1886,7 +1893,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest */ String guestHomeQuery = "/app:company_home/app:guest_home"; ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery); - assertEquals("", 1, guestHomeResult.length()); + assertEquals("unable to find guest home", 1, guestHomeResult.length()); final NodeRef guestHome = guestHomeResult.getNodeRef(0); final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); @@ -3156,7 +3163,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(testContext.folderNodeRef, ContentModel.PROP_NAME, name); // Side effect - initialisee nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def, new TransferContext()); child = nodeService.createNode(testContext.folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("source"), ContentModel.TYPE_CONTENT); testContext.sourceNodeRef = child.getChildRef(); @@ -3164,17 +3171,17 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(testContext.sourceNodeRef, ContentModel.PROP_NAME, "source"); // Side effect - initialise nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.sourceNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.sourceNodeRef, def, new TransferContext()); child = nodeService.createNode(testContext.folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("target"), ContentModel.TYPE_CONTENT); testContext.targetNodeRef = child.getChildRef(); nodeService.setProperty(testContext.targetNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); nodeService.setProperty(testContext.targetNodeRef, ContentModel.PROP_NAME, "target"); - testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def, new TransferContext()); nodeService.createAssociation(testContext.sourceNodeRef, testContext.targetNodeRef, ContentModel.ASSOC_REFERENCES); // Side effect - initialise nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.targetNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.targetNodeRef, def, new TransferContext()); /** * Make sure the transfer target exists and is enabled. @@ -3378,7 +3385,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(testContext.rootNodeRef, ContentModel.PROP_NAME, name); // Side effect - initialisee nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.rootNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.rootNodeRef, def, new TransferContext()); child = nodeService.createNode(testContext.rootNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A1"), ContentModel.TYPE_FOLDER); testContext.folderNodeRef = child.getChildRef(); @@ -3386,7 +3393,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(testContext.folderNodeRef, ContentModel.PROP_NAME, "A1"); // Side effect - initialise nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def, new TransferContext()); child = nodeService.createNode(testContext.folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("A2"), ContentModel.TYPE_CONTENT); testContext.contentNodeRef = child.getChildRef(); @@ -3394,7 +3401,7 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest nodeService.setProperty(testContext.contentNodeRef, ContentModel.PROP_NAME, "A2"); // Side effect - initialise nodeid mapping - testNodeFactory.createTransferManifestNode(testContext.contentNodeRef, def); + testNodeFactory.createTransferManifestNode(testContext.contentNodeRef, def, new TransferContext()); // Put nodes into destination @@ -3465,7 +3472,280 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest tran.doInTransaction(transferCB); tran.doInTransaction(validateCB); - } // testExistingNodes + } // testExistingNodes + + /** + * Test categories and tags (CRUD). + * + * Step 1: Create a new node with a tag + * transfer + * + * Step 2: Add another tag + * transfer + * + * Step 3: Delete a tag + * transfer + * + * Step 4: Add a category + * transfer + * + * Step 5: Add another category this one deep + * transfer + * + * Step 6: Delete a category + * transfer + * + * This is a unit test so it does some shenanigans to send to the same instance of alfresco. + */ + public void testCategoriesAndTags() throws Exception + { + final String CONTENT_TITLE = "ContentTitle"; + final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated"; + final Locale CONTENT_LOCALE = Locale.GERMAN; + final String CONTENT_STRING = "Hello World"; + final String CONTENT_UPDATE_STRING = "Foo Bar"; + final String targetName = "testCategoriesAndTags"; + final String TAG_1_NAME = "tag1"; + final String TAG_2_NAME = "tag2"; + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + class TestContext + { + TransferTarget transferMe; + NodeRef contentNodeRef; + NodeRef destNodeRef; + }; + + /** + * Unit test kludge to transfer from guest home to company home + */ + final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome(); + + DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A); + transferServiceImpl.setDescriptorService(mockedDescriptorService); + + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() + { + @Override + public TestContext execute() throws Throwable + { + TestContext ctx = new TestContext(); + + /** + * Get guest home + */ + String guestHomeQuery = "/app:company_home/app:guest_home"; + ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery); + assertEquals("", 1, guestHomeResult.length()); + NodeRef guestHome = guestHomeResult.getNodeRef(0); + + /** + * Create a test node that we will read and write + */ + String name = GUID.generate(); + ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_CONTENT); + ctx.contentNodeRef = child.getChildRef(); + nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE); + nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, name); + + // Add the TAG to be transferred + taggingService.addTag(ctx.contentNodeRef, TAG_1_NAME); + + if(!transferService.targetExists(targetName)) + { + ctx.transferMe = createTransferTarget(targetName); + } + else + { + ctx.transferMe = transferService.getTransferTarget(targetName); + } + transferService.enableTransferTarget(targetName, true); + + return ctx; + } + }; + final TestContext testContext = tran.doInTransaction(setupCB); + + /** + * Step 1: Transfer our which has a tag + */ + logger.debug("First transfer - create new node with a tag"); + RetryingTransactionCallback transferCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + TransferDefinition definition = new TransferDefinition(); + Setnodes = new HashSet(); + nodes.add(testContext.contentNodeRef); + definition.setNodes(nodes); + transferService.transfer(targetName, definition); + return null; + } + }; + tran.doInTransaction(transferCB); + + RetryingTransactionCallback validateStep1CB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + // Now validate that the target node exists and has similar properties to the source + testContext.destNodeRef = testNodeFactory.getMappedNodeRef( testContext.contentNodeRef); + assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals( testContext.transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists( testContext.destNodeRef)); + assertEquals("title is wrong", (String)nodeService.getProperty( testContext.destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE); + assertEquals("type is wrong", nodeService.getType( testContext.contentNodeRef), nodeService.getType( testContext.destNodeRef)); + + List tags = taggingService.getTags(testContext.contentNodeRef); + assertNotNull(tags); + assertTrue(tags.size() == 1); + assertTrue(tags.contains(TAG_1_NAME)); + + // Now add another tag for step number 2 + taggingService.addTag(testContext.contentNodeRef, TAG_2_NAME); + return null; + + } + }; + tran.doInTransaction(validateStep1CB); + + /** + * Step 2: + * Transfer our node again - With another tag + */ + logger.debug("Second transfer - add a second tag"); + tran.doInTransaction(transferCB); + + RetryingTransactionCallback validateStep2CB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + + // Now validate that the target node exists and has similar properties to the source + assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals(testContext.transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists(testContext.destNodeRef)); + + List tags = taggingService.getTags(testContext.contentNodeRef); + + assertNotNull(tags); + assertTrue(tags.size() == 2); + assertTrue(tags.contains(TAG_1_NAME)); + assertTrue(tags.contains(TAG_2_NAME)); + + return null; + } + }; + + tran.doInTransaction(validateStep2CB); + + /** + * Step 3 - delete a tag + */ + + RetryingTransactionCallback deleteTagCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + taggingService.removeTag(testContext.contentNodeRef, TAG_2_NAME); + return null; + } + }; + + tran.doInTransaction(deleteTagCB); + + + logger.debug("Transfer again - this is to delete a tag"); + tran.doInTransaction(transferCB); + + // should probably be in contentModel + final QName ASPECT_GENERAL_CLASSIFIABLE = ContentModel.ASPECT_GEN_CLASSIFIABLE; + + RetryingTransactionCallback validateStep3CB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals(testContext.transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists(testContext.destNodeRef)); + + List tags = taggingService.getTags(testContext.destNodeRef); + assertNotNull(tags); + assertTrue(tags.size() == 1); + assertTrue(tags.contains(TAG_1_NAME)); + + return null; + } + }; + + tran.doInTransaction(validateStep3CB); + + /** + * Step 4 - update to add a category that already exists + */ + logger.debug("Step 4 - add a category"); + + RetryingTransactionCallback step4WriteContentCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + // + StoreRef workspaceSpacesStore = new StoreRef("workspace", "SpacesStore"); + Collection rootCategories = categoryService.getRootCategories(workspaceSpacesStore, ASPECT_GENERAL_CLASSIFIABLE); + + NodeRef languageCategory = null; + for (ChildAssociationRef ref : rootCategories) + { + if(ref.getQName().getLocalName().equalsIgnoreCase("LANGUAGES")) + { + languageCategory = ref.getChildRef(); + } + } + + assertNotNull("language category is null", languageCategory); + + ChildAssociationRef categoryRef = categoryService.getCategory(languageCategory, ASPECT_GENERAL_CLASSIFIABLE, "English"); + + // Collection allCategories = categoryService.getCategories(workspaceSpacesStore, ASPECT_GENERAL_CLASSIFIABLE, Depth.ANY); + + assertNotNull("ENGLISH CATEGORY REF is null", categoryRef); + + List newCats = new ArrayList(); + newCats.add(categoryRef.getChildRef()); + + nodeService.setProperty(testContext.contentNodeRef, ContentModel.PROP_CATEGORIES, (Serializable)newCats); + + return null; + } + }; + + RetryingTransactionCallback validateStep4CB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals(testContext.transferMe.getNodeRef())); + assertTrue("dest node ref does not exist", nodeService.exists(testContext.destNodeRef)); + + assertTrue("destination node is missing aspect general classifiable", nodeService.hasAspect(testContext.destNodeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE)); + Serializable categories = nodeService.getProperty(testContext.destNodeRef, ContentModel.PROP_CATEGORIES); + assertNotNull("categories is missing on destination node", categories); + + return null; + } + }; + + tran.doInTransaction(step4WriteContentCB); + + tran.doInTransaction(transferCB); + + tran.doInTransaction(validateStep4CB); + + } // testCategoriesAndTags // Utility methods below. private TransferTarget createTransferTarget(String name) @@ -3495,5 +3775,5 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest when(descriptorService.getServerDescriptor()).thenReturn(serverDescriptor); return descriptorService; - } + } } diff --git a/source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java b/source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java index 739a8764a5..15605452e0 100644 --- a/source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java +++ b/source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java @@ -71,14 +71,14 @@ public class UnitTestTransferManifestNodeFactory implements TransferManifestNode this.realFactory = realFactory; } - public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition) + public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext) { - return createTransferManifestNode(nodeRef, definition, false); + return createTransferManifestNode(nodeRef, definition, transferContext, false); } - public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, boolean forceDelete) + public TransferManifestNode createTransferManifestNode(NodeRef nodeRef, TransferDefinition definition, TransferContext transferContext, boolean forceDelete) { - TransferManifestNode newNode = realFactory.createTransferManifestNode(nodeRef, definition); + TransferManifestNode newNode = realFactory.createTransferManifestNode(nodeRef, definition, transferContext); NodeRef origNodeRef = newNode.getNodeRef(); diff --git a/source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java b/source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java index e8b9187447..9d922af9eb 100644 --- a/source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java @@ -36,6 +36,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.alfresco.model.ContentModel; +import org.alfresco.repo.transfer.TransferContext; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; @@ -157,7 +158,7 @@ public class ManifestIntegrationTest extends BaseAlfrescoSpringTest formatter.writeTransferManifestHeader(header); for(NodeRef nodeRef : nodes) { - TransferManifestNode node = nodeFactory.createTransferManifestNode(nodeRef, null); + TransferManifestNode node = nodeFactory.createTransferManifestNode(nodeRef, null, new TransferContext()); formatter.writeTransferManifestNode(node); sentNodes.put(nodeRef, node); }