From 9e836f04f8212e002c69ee55f3adbdbc6c61edd1 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Wed, 13 Jun 2007 01:13:25 +0000 Subject: [PATCH] Yannick Pignot (European Commission) multilingual services - EditionService for versioning of groups of translations - Quite a bit of trimming of whitespace that I have no intention of undoing. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5927 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../model-specific-services-context.xml | 40 +- config/alfresco/public-services-context.xml | 319 ++++--- .../public-services-security-context.xml | 252 +++--- .../model/ml/ContentFilterLanguagesMap.java | 2 +- .../repo/model/ml/EditionServiceImpl.java | 420 +++++++++ .../repo/model/ml/EmptyTranslationAspect.java | 2 +- .../repo/model/ml/MLContainerType.java | 2 +- .../ml/MultilingualContentServiceImpl.java | 128 +-- .../model/ml/MultilingualDocumentAspect.java | 150 ++-- .../repo/model/ml/MultilingualTestSuite.java | 22 +- .../tools/AbstractMultilingualTestCases.java | 32 +- .../tools/ContentFilterLanguagesMapTest.java | 2 +- .../ml/tools/EditionServiceImplTest.java | 233 +++++ .../ml/tools/EmptyTranslationAspectTest.java | 2 +- .../model/ml/tools/MLContainerTypeTest.java | 2 +- .../MultilingualContentServiceImplTest.java | 58 -- .../tools/MultilingualDocumentAspectTest.java | 2 +- .../repo/version/NodeServiceImpl.java | 177 ++-- .../repo/version/VersionServiceImpl.java | 834 +++++++++--------- .../repo/version/common/VersionUtil.java | 38 +- .../cmr/ml/ContentFilterLanguagesService.java | 2 +- .../service/cmr/ml/EditionService.java | 95 ++ .../cmr/ml/MultilingualContentService.java | 88 +- 23 files changed, 1835 insertions(+), 1067 deletions(-) create mode 100644 source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java create mode 100644 source/java/org/alfresco/service/cmr/ml/EditionService.java diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 0778faf5b0..998b150827 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -2,9 +2,9 @@ - + - + @@ -39,7 +39,7 @@ - + @@ -50,8 +50,8 @@ - - + + @@ -64,7 +64,7 @@ - + @@ -73,9 +73,6 @@ - - - @@ -87,4 +84,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 4eec234d55..cd65a37438 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1,19 +1,19 @@ - + - + - + - + - + - + - + org.alfresco.service.descriptor.DescriptorService @@ -27,9 +27,9 @@ - + - + @@ -38,22 +38,22 @@ false - - + + - + - + - + - + - + - + - + @@ -76,7 +76,7 @@ - + @@ -90,9 +90,9 @@ - + - + org.alfresco.service.cmr.repository.ContentService @@ -110,7 +110,7 @@ - + @@ -121,17 +121,17 @@ - + - + - + - + - + - + org.alfresco.service.cmr.search.SearchService @@ -148,8 +148,8 @@ - - + + org.alfresco.service.cmr.search.SearchService @@ -165,7 +165,7 @@ - + @@ -176,9 +176,9 @@ - + - + org.alfresco.service.cmr.search.CategoryService @@ -195,7 +195,7 @@ - + @@ -206,9 +206,9 @@ - + - + org.alfresco.service.cmr.repository.CopyService @@ -225,7 +225,7 @@ - + @@ -236,9 +236,9 @@ - + - + org.alfresco.service.cmr.lock.LockService @@ -255,7 +255,7 @@ - + @@ -266,9 +266,9 @@ - + - + org.alfresco.service.cmr.version.VersionService @@ -285,7 +285,7 @@ - + @@ -296,9 +296,9 @@ - + - + org.alfresco.service.cmr.coci.CheckOutCheckInService @@ -315,7 +315,7 @@ - + @@ -326,9 +326,9 @@ - + - + org.alfresco.service.cmr.rule.RuleService @@ -345,7 +345,7 @@ - + @@ -356,9 +356,9 @@ - + - + org.alfresco.service.cmr.view.ImporterService @@ -375,7 +375,7 @@ - + @@ -386,9 +386,9 @@ - + - + org.alfresco.service.cmr.view.ExporterService @@ -403,7 +403,7 @@ - + @@ -414,9 +414,9 @@ - + - + org.alfresco.service.cmr.action.ActionService @@ -433,7 +433,7 @@ - + @@ -444,9 +444,9 @@ - + - + org.alfresco.service.cmr.security.PermissionService @@ -463,7 +463,7 @@ - + @@ -474,9 +474,9 @@ - + - + org.alfresco.service.cmr.security.AuthorityService @@ -493,7 +493,7 @@ - + @@ -504,9 +504,9 @@ - + - + org.alfresco.service.cmr.security.OwnableService @@ -523,7 +523,7 @@ - + @@ -534,9 +534,9 @@ - + - + org.alfresco.service.cmr.security.PersonService @@ -553,7 +553,7 @@ - + @@ -564,9 +564,9 @@ - + - + org.alfresco.service.cmr.security.AuthenticationService @@ -583,7 +583,7 @@ - + @@ -600,9 +600,9 @@ - + - + org.alfresco.service.cmr.repository.TemplateService @@ -619,7 +619,7 @@ - + @@ -630,9 +630,9 @@ - + - + org.alfresco.service.cmr.repository.ScriptService @@ -649,7 +649,7 @@ - + @@ -660,9 +660,9 @@ - + - + org.alfresco.service.cmr.model.FileFolderService @@ -682,7 +682,7 @@ - + @@ -697,9 +697,9 @@ - + - + @@ -718,9 +718,9 @@ - + - + @@ -731,7 +731,7 @@ - + @@ -747,9 +747,9 @@ - + - + @@ -758,7 +758,7 @@ - + @@ -848,7 +848,7 @@ - + @@ -881,7 +881,7 @@ - + @@ -892,27 +892,27 @@ true - + SYNCHRONOUS - - - SYNCHRONOUS:TYPE:STAGING - UNINDEXED:TYPE:STAGING_PREVIEW - UNINDEXED:TYPE:AUTHOR - UNINDEXED:TYPE:AUTHOR_PREVIEW - UNINDEXED:TYPE:WORKFLOW - UNINDEXED:TYPE:WORKFLOW_PREVIEW - UNINDEXED:TYPE:AUTHOR_WORKFLOW - UNINDEXED:TYPE:AUTHOR_WORKFLOW_PREVIEW - ASYNCHRONOUS:NAME:avmAsynchronousTest - SYNCHRONOUS:NAME:.* - - + + + SYNCHRONOUS:TYPE:STAGING + UNINDEXED:TYPE:STAGING_PREVIEW + UNINDEXED:TYPE:AUTHOR + UNINDEXED:TYPE:AUTHOR_PREVIEW + UNINDEXED:TYPE:WORKFLOW + UNINDEXED:TYPE:WORKFLOW_PREVIEW + UNINDEXED:TYPE:AUTHOR_WORKFLOW + UNINDEXED:TYPE:AUTHOR_WORKFLOW_PREVIEW + ASYNCHRONOUS:NAME:avmAsynchronousTest + SYNCHRONOUS:NAME:.* + + - + - + @@ -921,7 +921,7 @@ - + @@ -933,7 +933,7 @@ - + @@ -966,7 +966,7 @@ - + @@ -982,7 +982,7 @@ - + @@ -1018,7 +1018,7 @@ - + @@ -1037,7 +1037,7 @@ --> - + @@ -1068,7 +1068,7 @@ - + @@ -1077,9 +1077,9 @@ getLock getUserLocks - getWebProjectLocks - getWebProjects - getStoreLocks + getWebProjectLocks + getWebProjects + getStoreLocks hasAccess @@ -1094,9 +1094,9 @@ addWebProject lockPath removeLock - removeWebProject - modifyLock - removeStoreLocks + removeWebProject + modifyLock + removeStoreLocks @@ -1117,9 +1117,9 @@ - + - + org.alfresco.service.cmr.workflow.WorkflowService @@ -1136,7 +1136,7 @@ - + @@ -1147,9 +1147,9 @@ - + - + org.alfresco.service.cmr.audit.AuditService @@ -1166,7 +1166,7 @@ - + @@ -1177,10 +1177,10 @@ - - + + - + org.alfresco.service.cmr.ml.MultilingualContentService @@ -1192,11 +1192,11 @@ - + - + @@ -1209,9 +1209,38 @@ - + + + + + + + org.alfresco.service.cmr.ml.EditionService + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + - + org.alfresco.repo.configuration.ConfigurableService @@ -1225,7 +1254,7 @@ - + @@ -1238,9 +1267,9 @@ - + - + org.alfresco.repo.admin.registry.RegistryService @@ -1254,7 +1283,7 @@ - + @@ -1266,9 +1295,9 @@ - + - + org.alfresco.service.cmr.module.ModuleService @@ -1282,7 +1311,7 @@ - + @@ -1294,7 +1323,7 @@ - + @@ -1318,8 +1347,8 @@ - - + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index b5d7d4148e..b46a8e6154 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -4,13 +4,13 @@ - + - - + + @@ -38,7 +38,7 @@ - + @@ -75,19 +75,19 @@ - + - - + + - + @@ -114,43 +114,43 @@ - + - + - + ROLE_ - + - + GROUP_ - + - + - + @@ -161,7 +161,7 @@ - + @@ -169,15 +169,15 @@ - - + + - + - + false @@ -188,11 +188,11 @@ - + - + @@ -203,7 +203,7 @@ - + @@ -214,9 +214,9 @@ ${system.acl.maxPermissionChecks} - - - + + + @@ -224,41 +224,41 @@ - - + + - + - + - - + + - - + + - + - + - + - + - + @@ -272,20 +272,20 @@ - + - - + + - + - - + + - + - + @@ -306,7 +306,7 @@ - + @@ -319,8 +319,8 @@ - - + + @@ -365,11 +365,11 @@ - + - + @@ -395,14 +395,14 @@ - + - + - + @@ -420,22 +420,22 @@ - - + + - + - + - + - + @@ -450,14 +450,14 @@ - + - + - + @@ -477,24 +477,24 @@ - - + + - + - + - + - + - + @@ -510,23 +510,23 @@ - + - + - + - + - + - + @@ -543,19 +543,39 @@ org.alfresco.service.cmr.ml.MultilingualContentService.unmakeTranslation=ACL_NODE.0.sys:base.Write org.alfresco.service.cmr.ml.MultilingualContentService.addTranslation=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.Write org.alfresco.service.cmr.ml.MultilingualContentService.addEmptyTranslation=ACL_NODE.0.sys:base.Read,ACL_NODE.0.sys:base.CreateChildren - org.alfresco.service.cmr.ml.MultilingualContentService.createEdition=ACL_NODE.0.sys:base.Write + org.alfresco.service.cmr.ml.MultilingualContentService.copyTranslationContainer=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.CreateChildren + org.alfresco.service.cmr.ml.MultilingualContentService.moveTranslationContainer=ACL_NODE.0.sys:base.DeleteNode,ACL_NODE.1.sys:base.CreateChildren + + + + + + + + + + + + org.alfresco.service.cmr.ml.EditionService.createEdition=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.EditionService.getEditions=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.EditionService.getVersionedTranslations=ACL_ALLOW + org.alfresco.service.cmr.ml.EditionService.getVersionedMetadatas=ACL_ALLOW + + + + - + - + @@ -568,42 +588,42 @@ org.alfresco.service.cmr.coci.CheckOutCheckInService.getWorkingCopy=ACL_NODE.0.sys:base.Read - - + + - + - + - + - + - + - + - + - + - + @@ -626,13 +646,13 @@ - + - + - + @@ -655,11 +675,11 @@ - + - + @@ -690,14 +710,14 @@ - + - + - + @@ -711,10 +731,10 @@ - - + + - + @@ -737,37 +757,37 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java b/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java index 6483439728..4c74336b27 100644 --- a/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java +++ b/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java @@ -45,7 +45,7 @@ import org.apache.commons.logging.LogFactory; * * {@link org.alfresco.service.cmr.ml.ContentFilterLanguagesService Content Filter Languages Service} * - * @author yanipig + * @author Yannick Pignot */ public class ContentFilterLanguagesMap implements ContentFilterLanguagesService { diff --git a/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java new file mode 100644 index 0000000000..7c9bae3b9e --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.repo.version.common.VersionUtil; +import org.alfresco.service.cmr.ml.EditionService; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Edition support implementation + * + * @author Yannick Pignot + */ +public class EditionServiceImpl implements EditionService +{ + private static Log logger = LogFactory.getLog(EditionServiceImpl.class); + + private VersionService versionService; + private NodeService nodeService; + private BehaviourFilter policyBehaviourFilter; + private MultilingualContentService multilingualContentService; + private NodeArchiveService nodeArchiveService; + private NodeService versionNodeService; + private FileFolderService fileFolderService; + + /** + * List of properties to set persistent when an edition of the mlContainer is created + */ + public static final QName[] ML_CONTAINER_PROPERTIES_TO_VERSION = { + ContentModel.PROP_AUTHOR, + ContentModel.PROP_LOCALE + }; + + /** @inheritDoc */ + public NodeRef createEdition(NodeRef startingTranslationNodeRef, Map versionProperties) + { + if (nodeService.hasAspect(startingTranslationNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + return createEditionImpl( + startingTranslationNodeRef, + versionProperties + ); + } + else + { + throw new IllegalArgumentException("The node " + startingTranslationNodeRef + " is not multilingual."); + } + } + + private NodeRef createEditionImpl(NodeRef startingTranslationNodeRef, Map versionProperties) + { + + // 1. First step: prepare and version the mlContainer + + // Get the ml container to version + NodeRef mlContainerToVersion = multilingualContentService.getTranslationContainer(startingTranslationNodeRef); + // Get all the container's children + List childAssocRefs = nodeService.getChildAssocs( + mlContainerToVersion, ContentModel.ASSOC_MULTILINGUAL_CHILD, + RegexQNamePattern.MATCH_ALL); + + // get the last edition and add it the Version Histories property to the version + Version currentEdition = versionService.getCurrentVersion(mlContainerToVersion); + addVersionHitoryProperty(currentEdition, childAssocRefs); + + if(versionProperties == null) + { + versionProperties = new HashMap(); + } + + // get the properties to add to the edition history + addPropertiesToVersion(versionProperties, mlContainerToVersion); + + // Version the container and its translations + versionService.createVersion(mlContainerToVersion, versionProperties, true); + + // 2. Third step: prepare the current edition of the mlContainer + + // Get the new starting point node, it will be returned + NodeRef startNode; + + // copy the translation before its deletion and get usefull properties + NodeRef space = nodeService.getPrimaryParent(startingTranslationNodeRef).getParentRef(); + String name = (String) nodeService.getProperty(startingTranslationNodeRef, ContentModel.PROP_NAME); + Locale locale = (Locale) nodeService.getProperty(startingTranslationNodeRef, ContentModel.PROP_LOCALE); + String author = (String) nodeService.getProperty(startingTranslationNodeRef, ContentModel.PROP_AUTHOR); + + for (int count = 0;; count++) + { + try + { + // genererate a temporary name. + String tempName = "TEMP_NAME" + System.currentTimeMillis() + "_" + count; + + // try to copy the node + startNode = fileFolderService.copy(startingTranslationNodeRef, space, tempName).getNodeRef(); + + // copy completed without exception + break; + + } + catch (FileExistsException e) + { + // try again with a new name + } + catch (FileNotFoundException e) + { + throw new IllegalStateException(e); + } + } + + // remove the current translations of the mlContainer + removeTranslations(childAssocRefs); + + // restore the original name of the node + nodeService.setProperty(startNode, ContentModel.PROP_NAME, name); + + + // add the starting node to the mlContainer, and set the author + multilingualContentService.addTranslation(startNode, mlContainerToVersion, locale); + nodeService.setProperty(startNode, ContentModel.PROP_AUTHOR, author); + + // set the starting translation become the pivot. + nodeService.setProperty(mlContainerToVersion, ContentModel.PROP_LOCALE, locale); + nodeService.setProperty(mlContainerToVersion, ContentModel.PROP_AUTHOR, author); + + // Done + if (logger.isDebugEnabled()) + { + // Get the version information + Version mlContainerVersion = versionService.getCurrentVersion(mlContainerToVersion); + String mlContainerVersionLabel = mlContainerVersion.getVersionLabel(); + + logger.debug("Versioned multilingual container: \n" + + " Container: " + mlContainerToVersion + "\n" + + " Current Version: " + mlContainerVersionLabel); + } + + return startNode; + } + + /** @inheritDoc */ + public VersionHistory getEditions(NodeRef mlContainer) + { + VersionHistory editionHistory = null; + + // Only the mlContainer can have editions + if (nodeService.getType(mlContainer).equals( + ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + // get the editions of the mlContainer + editionHistory = versionService.getVersionHistory(mlContainer); + } + + else + { + throw new IllegalArgumentException("The type of the node must be " + + ContentModel.TYPE_CONTAINER); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Found all editions: \n" + " Node: " + + mlContainer + " (type " + + ContentModel.TYPE_MULTILINGUAL_CONTAINER + ")\n" + + " Editions: " + editionHistory); + } + + return editionHistory; + } + + /** @inheritDoc */ + public Map getVersionedMetadatas(Version version) + { + NodeRef frozenNodeRef = version.getFrozenStateNodeRef(); + + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(frozenNodeRef))) + { + // for the mlContainer, the properties are set as a version properties + Map properties = version.getVersionProperties(); + + // The returned map of this method need a QName type key, not a String. + Map convertedProperties = new HashMap(properties.size()); + + // perform the convertion + for(Map.Entry entry : properties.entrySet()) + { + convertedProperties.put( + QName.createQName(entry.getKey()), + entry.getValue()); + } + + return convertedProperties; + } + else + { + // for any other type of node, the properties are set as versioned metadata + return versionNodeService.getProperties(frozenNodeRef); + } + } + + /** @inheritDoc */ + public List getVersionedTranslations(Version mlContainerEdition) + { + // Ensure that the given version is an Edition of an mlContainer + if(!ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(mlContainerEdition.getVersionedNodeRef()))) + { + throw new IllegalArgumentException("The type of the node must be " + ContentModel.TYPE_CONTAINER); + } + + Map properties = versionNodeService.getProperties(mlContainerEdition.getFrozenStateNodeRef()); + + // get the serialisation of the version histories in the version properties + List versionHistories = (List) + properties.get(VersionModel.PROP_QNAME_TRANSLATION_VERIONS); + + if (versionHistories == null) + { + // the initial edition doesn't content translations (at the creation time of the mlContainer). + versionHistories = new ArrayList(); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Found all translations : \n" + + " Versioned mlContainer: " + mlContainerEdition.getVersionedNodeRef() + "\n" + + " Edition: " + mlContainerEdition + + " Translations: " + versionHistories); + } + + return versionHistories; + } + + /** + * Util method to add the version histories of translations as a property of the frozen mlContainer + */ + private void addVersionHitoryProperty(Version edition, List childAssocRefs) + { + List translationVersionHistories = new ArrayList(childAssocRefs.size()); + + for (ChildAssociationRef ref : childAssocRefs) + { + NodeRef translation = ref.getChildRef(); + + translationVersionHistories.add(versionService.getVersionHistory(translation)); + + } + + // properties in which the version histories will be stored + Map properties = new HashMap(); + + // add the version history of the translation as property of the Edition + properties.put(VersionModel.PROP_QNAME_QNAME, VersionModel.PROP_QNAME_TRANSLATION_VERIONS); + properties.put(VersionModel.PROP_QNAME_IS_MULTI_VALUE, true); + properties.put(VersionModel.PROP_QNAME_MULTI_VALUE, (Serializable) translationVersionHistories); + + // create the versioned property node + this.nodeService.createNode( + VersionUtil.convertNodeRef(edition.getFrozenStateNodeRef()), + VersionModel.CHILD_QNAME_VERSIONED_ATTRIBUTES, + VersionModel.CHILD_QNAME_VERSIONED_ATTRIBUTES, + VersionModel.TYPE_QNAME_VERSIONED_PROPERTY, + properties); + } + + /** + * Util method to add the usefull properties to the existing properties of the given, mlContainer + * + * @link {@link EditionServiceImpl#ML_CONTAINER_PROPERTIES_TO_VERSION} + */ + private void addPropertiesToVersion(Map versionProperties, NodeRef mlContainerToVersion) + { + // add useful properties + for(QName prop : ML_CONTAINER_PROPERTIES_TO_VERSION) + { + versionProperties.put(prop.toString(), nodeService.getProperty(mlContainerToVersion, prop)); + } + } + + /** + * Util method to remove the given translations after making a new edition + */ + private void removeTranslations(List childAssocRefs) + { + // Turn off any auto-version policy behaviours. Without that, + // the version history of the translations will be deleted. + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + + // Turn off any multilingual document policy behaviours. Without that, + // the mlcontainer of the translations will be deleted. + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + + try + { + for (ChildAssociationRef childAssoc : childAssocRefs) + { + NodeRef documentNodeRef = childAssoc.getChildRef(); + + // Permanently delete it + nodeService.deleteNode(documentNodeRef); + if(nodeService.exists(nodeArchiveService.getArchivedNode(documentNodeRef))) + { + nodeService.deleteNode(nodeArchiveService.getArchivedNode(documentNodeRef)); + } + } + } + finally + { + // Turn auto-version and multinlingual document policies back on + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + } + + /** + * @param nodeService + * the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param versionService + * the Version Service to set + */ + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + /** + * @param multilingualContentService + * the Multilingual Content Service to set + */ + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + /** + * @param versionNodeService + * the Version Store Node Service to set + */ + public void setVersionNodeService(NodeService versionNodeService) + { + this.versionNodeService = versionNodeService; + } + + /** + * @param policyBehaviourFilter the Behaviour Filter to set + */ + public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter) + { + this.policyBehaviourFilter = policyBehaviourFilter; + } + + /** + * @param nodeArchiveService the node Archive Service to set + */ + public void setNodeArchiveService(NodeArchiveService nodeArchiveService) + { + this.nodeArchiveService = nodeArchiveService; + } + + /** + * @param fileFolderService the fileFolder Service to set + */ + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java index f6eb7a8594..789cc9eeca 100644 --- a/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java +++ b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java @@ -44,7 +44,7 @@ import org.alfresco.service.namespace.QName; * * {@link ContentModel#ASPECT_MULTILINGUAL_EMPTY_TRANSLATION ml empty document aspect} * - * @author yanipig + * @author Yannick Pignot */ public class EmptyTranslationAspect implements CopyServicePolicies.OnCopyNodePolicy, diff --git a/source/java/org/alfresco/repo/model/ml/MLContainerType.java b/source/java/org/alfresco/repo/model/ml/MLContainerType.java index 313a78664f..0577bf5c12 100644 --- a/source/java/org/alfresco/repo/model/ml/MLContainerType.java +++ b/source/java/org/alfresco/repo/model/ml/MLContainerType.java @@ -45,7 +45,7 @@ import org.alfresco.service.namespace.QName; * * {@link ContentModel#TYPE_MULTILINGUAL_CONTAINER multilingual container type} * - * @author yanipig + * @author Yannick Pignot */ public class MLContainerType implements NodeServicePolicies.OnUpdatePropertiesPolicy diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index d145fc6b48..0d79e611d5 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -24,7 +24,6 @@ */ package org.alfresco.repo.model.ml; -import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,7 +34,6 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; -import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.model.FileFolderService; @@ -49,8 +47,6 @@ import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.version.Version; -import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -77,7 +73,7 @@ import org.apache.commons.logging.LogFactory; * * @author Derek Hulley * @author Philippe Dubois - * @author yanipig + * @author Yannick Pignot */ public class MultilingualContentServiceImpl implements MultilingualContentService { @@ -85,7 +81,6 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic private NodeService nodeService; private SearchService searchService; - private VersionService versionService; private PermissionService permissionService; private SearchParameters searchParametersMLRoot; private ContentFilterLanguagesService contentFilterLanguagesService; @@ -136,8 +131,8 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic NodeRef mlContainerRootNodeRef = getMLContainerRoot(); // Create the container PropertyMap versionProperties = new PropertyMap(); - versionProperties.put(ContentModel.PROP_AUTO_VERSION, Boolean.FALSE); - versionProperties.put(ContentModel.PROP_INITIAL_VERSION, Boolean.FALSE); + //versionProperties.put(ContentModel.PROP_AUTO_VERSION, Boolean.FALSE); + //versionProperties.put(ContentModel.PROP_INITIAL_VERSION, Boolean.FALSE); ChildAssociationRef assocRef = nodeService.createNode( mlContainerRootNodeRef, ContentModel.ASSOC_CHILDREN, @@ -146,8 +141,8 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic versionProperties); NodeRef mlContainerNodeRef = assocRef.getChildRef(); // TODO: Examine the usage of versioning - why is autoversioning on and used in the UI? -// // The model makes the container versionable by default, but why? -// nodeService.addAspect(mlContainerNodeRef, ContentModel.ASPECT_VERSIONABLE, versionProperties); + // The model makes the container versionable by default, but why? + nodeService.addAspect(mlContainerNodeRef, ContentModel.ASPECT_VERSIONABLE, versionProperties); // Set the permissions to allow anything by anyone permissionService.setPermission( mlContainerNodeRef, @@ -193,7 +188,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // Done return mlContainerNodeRef; } - + /** * Retrieve or create a cm:mlDocument container for the given node, which must have the * cm:mlDocument already applied. @@ -312,7 +307,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // done return mlContainerNodeRef; } - + private boolean isPivotTranslation(NodeRef contentNodeRef) { Locale locale = (Locale) nodeService.getProperty(contentNodeRef, ContentModel.PROP_LOCALE); @@ -359,7 +354,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return false; } } - + /** @inheritDoc */ public void makeTranslation(NodeRef contentNodeRef, Locale locale) { @@ -432,7 +427,17 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic public void addTranslation(NodeRef newTranslationNodeRef, NodeRef translationOfNodeRef, Locale locale) { // Get the container - NodeRef mlContainerNodeRef = getOrCreateMLContainer(translationOfNodeRef, false); + NodeRef mlContainerNodeRef = null; + + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(translationOfNodeRef))) + { + mlContainerNodeRef = translationOfNodeRef; + } + else + { + mlContainerNodeRef = getOrCreateMLContainer(translationOfNodeRef, false); + } + // Use the existing container to make the new content into a translation makeTranslationImpl(mlContainerNodeRef, newTranslationNodeRef, locale); // done @@ -453,72 +458,6 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return mlContainerNodeRef; } - /** @inheritDoc */ - public void createEdition( NodeRef translationNodeRef) - { - NodeRef mlContainerNodeRef = getOrCreateMLContainer(translationNodeRef, false); - // Ensure that the translation given is one of the children - getOrCreateMLContainer(translationNodeRef, false); - // Get all the container's children - List childAssocRefs = nodeService.getChildAssocs( - mlContainerNodeRef, - ContentModel.ASSOC_MULTILINGUAL_CHILD, - RegexQNamePattern.MATCH_ALL); - - - // Get and store the translation verions associated to the mlContainer - List versions = new ArrayList(childAssocRefs.size()); - - for (ChildAssociationRef childAssoc : childAssocRefs) - { - versions.add(versionService.getCurrentVersion(childAssoc.getChildRef())); - } - - Map editionProperties = new HashMap(); - editionProperties.put( - VersionModel.PROP_QNAME_TRANSLATION_VERIONS.toString(), - (Serializable) versions - ); - - // Version the container and all its children - versionService.createVersion(mlContainerNodeRef, editionProperties, true); - - // Remove all the child documents apart from the given node - boolean found = false; - for (ChildAssociationRef childAssoc : childAssocRefs) - { - NodeRef documentNodeRef = childAssoc.getChildRef(); - // Is this the node to keep? - if (documentNodeRef.equals(translationNodeRef)) - { - // It is, so keep it - found = true; - continue; - } - // Delete it - nodeService.deleteNode(documentNodeRef); - } - // Check that we left a document - if (!found) - { - throw new AlfrescoRuntimeException( - "The translation provided is not a child of the multilingual container: \n" + - " Container: " + mlContainerNodeRef + "\n" + - " Translation: " + translationNodeRef); - } - // Done - if (logger.isDebugEnabled()) - { - // Get the version information - Version mlContainerVersion = versionService.getCurrentVersion(mlContainerNodeRef); - String mlContainerVersionLabel = mlContainerVersion.getVersionLabel(); - logger.debug( - "Versioned multilingual container: \n" + - " Container: " + mlContainerNodeRef + "\n" + - " Current Version: " + mlContainerVersionLabel); - } - } - /** @inheritDoc */ public Map getTranslations(NodeRef translationOfNodeRef) { @@ -676,7 +615,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return nodeRefsByLocale.get(nearestLocale); } } - + /** * @inheritDoc */ @@ -704,7 +643,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic " Translation: " + translationOfNodeRef + "\n" + " Locale: " + locale); } - + FileInfo translationOfFileInfo = fileFolderService.getFileInfo(translationOfNodeRef); String translationOfName = translationOfFileInfo.getName(); // If name is null, supply one @@ -713,7 +652,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic name = translationOfName; } // If there is a name clash, add the locale to the main portion of the filename - if (name.equals(translationOfName)) + if (name.equalsIgnoreCase(translationOfName)) { String localeStr = locale.toString(); if (localeStr.endsWith("_")) @@ -744,7 +683,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic parentNodeRef, name, ContentModel.TYPE_CONTENT).getNodeRef(); - + // add the translation to the container addTranslation(newTranslationNodeRef, translationOfNodeRef, locale); @@ -788,15 +727,10 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic this.searchService = searchService; } - public void setVersionService(VersionService versionService) - { - this.versionService = versionService; - } - public void setPermissionService(PermissionService permissionService) -{ - this.permissionService = permissionService; -} + { + this.permissionService = permissionService; + } public void setContentFilterLanguagesService(ContentFilterLanguagesService contentFilterLanguagesService) { @@ -807,4 +741,14 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic { this.fileFolderService = fileFolderService; } + + public NodeRef copyTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef) + { + throw new UnsupportedOperationException("This operation is not yet supported"); + } + + public void moveTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef) + { + throw new UnsupportedOperationException("This operation is not yet supported"); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java index 79c05931e7..3762fcbe1d 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.model.ml; @@ -35,6 +35,7 @@ import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyScope; +import org.alfresco.repo.version.VersionServicePolicies; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -46,54 +47,69 @@ import org.alfresco.service.namespace.RegexQNamePattern; /** * Class containing behaviour for the multilingual document aspect. - * + * * {@link ContentModel#ASPECT_MULTILINGUAL_DOCUMENT ml document aspect} * * @author yanipig */ -public class MultilingualDocumentAspect implements +public class MultilingualDocumentAspect implements CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyCompletePolicy, NodeServicePolicies.BeforeDeleteNodePolicy, - NodeServicePolicies.OnUpdatePropertiesPolicy + NodeServicePolicies.OnUpdatePropertiesPolicy, + VersionServicePolicies.OnCreateVersionPolicy { + /** + * List of properties to set persistent when a version of the mlDocument is created + */ + public static final QName[] PROPERTIES_TO_VERSION = { + ContentModel.PROP_AUTHOR, + ContentModel.PROP_LOCALE, + ContentModel.PROP_TITLE, + ContentModel.PROP_DESCRIPTION, + }; + // Dependencies private PolicyComponent policyComponent; - private MultilingualContentService multilingualContentService; - private NodeService nodeService; - - + + /** * Initialise the Multilingual Aspect - * + * * Ensures that the {@link ContentModel#ASPECT_MULTILINGUAL_DOCUMENT ml document aspect} */ public void init() { this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, new JavaBehaviour(this, "onCopyNode")); - + this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"), + QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"), ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, new JavaBehaviour(this, "onCopyComplete")); - + this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, - new JavaBehaviour(this, "beforeDeleteNode")); - + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "beforeDeleteNode")); + this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, new JavaBehaviour(this, "onUpdateProperties")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateVersion"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onCreateVersion")); + } - + /** * @param policyComponent the policy component to register behaviour with */ @@ -101,12 +117,12 @@ public class MultilingualDocumentAspect implements { this.policyComponent = policyComponent; } - + /** * @param multilingualContentService the Multilingual Content Service to set */ public void setMultilingualContentService( - MultilingualContentService multilingualContentService) + MultilingualContentService multilingualContentService) { this.multilingualContentService = multilingualContentService; } @@ -114,36 +130,36 @@ public class MultilingualDocumentAspect implements /** * @param nodeService the Node Service to set */ - public void setNodeService(NodeService nodeService) + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; - } + } /** * The copy of a cm:mlDocument can't keep the Multilingual aspect. - * + * * @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope) */ - public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails) + public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails) { - copyDetails.removeAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + copyDetails.removeAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); } - + /** - * The copy of mlDocument don't keep the 'locale' property. - * + * The copy of mlDocument don't keep the 'locale' property. + * * @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyCompletePolicy#onCopyComplete(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, boolean, java.util.Map) */ - public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef destinationRef, boolean copyToNewNode, Map copyMap) + public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef destinationRef, boolean copyToNewNode, Map copyMap) { nodeService.removeProperty(destinationRef, ContentModel.PROP_LOCALE); } - + /** * If this is not an empty translation, then ensure that the node is properly * unhooked from the translation mechanism first. */ - public void beforeDeleteNode(NodeRef nodeRef) + public void beforeDeleteNode(NodeRef nodeRef) { if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) { @@ -155,63 +171,63 @@ public class MultilingualDocumentAspect implements multilingualContentService.unmakeTranslation(nodeRef); } } - + /** * Ensure that the locale is unique inside the mlContainer. - * + * * If the locale of a pivot translation is modified, the pivot locale reference of the mlContainer * must be modified too. - * + * * @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map) */ - public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) { /* * TODO: Move this into MultilingualContentService#setTranslationLocale */ - + Locale localeBefore = (Locale) before.get(ContentModel.PROP_LOCALE); Locale localeAfter; - - // the after local property type can be either Locale or String + + // the after local property type can be either Locale or String Serializable objLocaleAfter = after.get(ContentModel.PROP_LOCALE); - if (objLocaleAfter instanceof Locale ) + if (objLocaleAfter instanceof Locale ) { localeAfter = (Locale) objLocaleAfter; - + } else { localeAfter = I18NUtil.parseLocale(objLocaleAfter.toString()); } - + // if the local has been modified if(!localeBefore.equals(localeAfter)) { NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef); - - // Since the map returned by the getTranslations doesn't duplicate keys, the size of this map will be + + // Since the map returned by the getTranslations doesn't duplicate keys, the size of this map will be // different of the size of the number of children of the mlContainer if a duplicate locale is found. int transSize = multilingualContentService.getTranslations(mlContainer).size(); int childSize = nodeService.getChildAssocs(mlContainer, ContentModel.ASSOC_MULTILINGUAL_CHILD, RegexQNamePattern.MATCH_ALL).size(); - + // if duplicate locale found if(transSize != childSize) { - // throw an exception and the current transaction will be rolled back. The properties will not be + // throw an exception and the current transaction will be rolled back. The properties will not be // longer in an illegal state. - throw new IllegalArgumentException("The locale " + localeAfter + - " can't be changed for the node " + nodeRef + + throw new IllegalArgumentException("The locale " + localeAfter + + " can't be changed for the node " + nodeRef + " because this locale is already in use in an other translation of the same " + ContentModel.TYPE_MULTILINGUAL_CONTAINER + "."); } - + // get the locale of ML Container Locale localMlContainer = (Locale) nodeService.getProperty( mlContainer, ContentModel.PROP_LOCALE); - - // if locale of the container is equals to the locale of + + // if locale of the container is equals to the locale of // the node (before update). The nodeRef is the pivot language // and the locale of the mlContainer must be modified if(localeBefore.equals(localMlContainer)) @@ -219,12 +235,26 @@ public class MultilingualDocumentAspect implements nodeService.setProperty( mlContainer, ContentModel.PROP_LOCALE, - localeAfter); + localeAfter); } - + + } + + // else no action to perform + } + + /** + * Persist some specific properties in the version store + * + * @see org.alfresco.repo.model.ml.MultilingualDocumentAspect.PROPERTIES_TO_VERSION + * @see org.alfresco.repo.version.VersionServicePolicies.OnCreateVersionPolicy#onCreateVersion(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, java.util.Map, org.alfresco.repo.policy.PolicyScope) + */ + public void onCreateVersion(QName classRef, NodeRef versionableNode, Map versionProperties, PolicyScope nodeDetails) + { + for(QName prop : PROPERTIES_TO_VERSION) + { + nodeDetails.addProperty(prop, nodeService.getProperty(versionableNode, prop)); } - - // else no action to perform } } diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java index 0b5547d33a..38b113a631 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.model.ml; @@ -28,6 +28,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.alfresco.repo.model.ml.tools.ContentFilterLanguagesMapTest; +import org.alfresco.repo.model.ml.tools.EditionServiceImplTest; import org.alfresco.repo.model.ml.tools.EmptyTranslationAspectTest; import org.alfresco.repo.model.ml.tools.MLContainerTypeTest; import org.alfresco.repo.model.ml.tools.MultilingualContentServiceImplTest; @@ -35,26 +36,27 @@ import org.alfresco.repo.model.ml.tools.MultilingualDocumentAspectTest; /** * Multilingual test suite - * + * * @author yanipig */ public class MultilingualTestSuite extends TestSuite { /** * Creates the test suite - * + * * @return the test suite */ - public static Test suite() + public static Test suite() { TestSuite suite = new TestSuite(); - + suite.addTestSuite(EmptyTranslationAspectTest.class); suite.addTestSuite(ContentFilterLanguagesMapTest.class); suite.addTestSuite(MultilingualContentServiceImplTest.class); suite.addTestSuite(MultilingualDocumentAspectTest.class); suite.addTestSuite(MLContainerTypeTest.class); - + suite.addTestSuite(EditionServiceImplTest.class); + return suite; } } diff --git a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java index 13cf31c354..392c5dbc50 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java +++ b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.model.ml.tools; @@ -33,6 +33,7 @@ import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.alfresco.service.cmr.ml.EditionService; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -48,12 +49,12 @@ import org.springframework.context.ApplicationContext; /** * Base multilingual test cases - * + * * @author yanipig */ -public abstract class AbstractMultilingualTestCases extends TestCase +public abstract class AbstractMultilingualTestCases extends TestCase { - + protected static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); protected ServiceRegistry serviceRegistry; @@ -66,6 +67,8 @@ public abstract class AbstractMultilingualTestCases extends TestCase protected NodeRef folderNodeRef; protected ContentFilterLanguagesService contentFilterLanguagesService; protected NodeArchiveService nodeArchiveService; + protected EditionService editionService; + @Override protected void setUp() throws Exception { @@ -78,10 +81,11 @@ public abstract class AbstractMultilingualTestCases extends TestCase versionService = serviceRegistry.getVersionService(); multilingualContentService = (MultilingualContentService) ctx.getBean("MultilingualContentService"); contentFilterLanguagesService = (ContentFilterLanguagesService) ctx.getBean("ContentFilterLanguagesService"); - + editionService = (EditionService) ctx.getBean("EditionService"); + // Run as admin authenticationComponent.setCurrentUser("admin"); - + // Create a folder to work in TransactionWork createFolderWork = new TransactionWork() { @@ -101,7 +105,7 @@ public abstract class AbstractMultilingualTestCases extends TestCase }; folderNodeRef = TransactionUtil.executeInUserTransaction(transactionService, createFolderWork); } - + @Override protected void tearDown() throws Exception { @@ -115,13 +119,13 @@ public abstract class AbstractMultilingualTestCases extends TestCase e.printStackTrace(); } } - + protected NodeRef createContent() { String name = "" + System.currentTimeMillis(); return createContent(name); } - + protected NodeRef createContent(String name) { NodeRef contentNodeRef = fileFolderService.create( @@ -134,7 +138,7 @@ public abstract class AbstractMultilingualTestCases extends TestCase // done return contentNodeRef; } - + public void testSetup() throws Exception { // Ensure that content can be created diff --git a/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java b/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java index 1e576df048..d72c9e320c 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java @@ -35,7 +35,7 @@ import java.util.Locale; * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService * @see org.alfresco.repo.model.ml.ContentFilterLanguagesMap * - * @author yanipig + * @author Yannick Pignot */ public class ContentFilterLanguagesMapTest extends AbstractMultilingualTestCases { diff --git a/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java b/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java new file mode 100644 index 0000000000..19beae7053 --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionType; + +/** + * Edition Service test cases + * + * @since 2.1 + * @author Yannick Pignot + */ +public class EditionServiceImplTest extends AbstractMultilingualTestCases +{ + private static String FRENCH_CONTENT = "FRENCH_CONTENT"; + private static String CHINESE_CONTENT = "CHINESE_CONTENT"; + private static String JAPANESE_CONTENT = "JAPANESE_CONTENT"; + + public void testAutoEdition() throws Exception + { + // create a mlContainer with some content + checkFirstVersion(this.createMLContainerWithContent()); + } + + public void testEditionLabels() + { + // create a mlContainer with some content + NodeRef mlContainerNodeRef = createMLContainerWithContent(); + Map versionProperties = null; + List editions = null; + NodeRef pivot = multilingualContentService.getPivotTranslation(mlContainerNodeRef); + + checkFirstVersion(mlContainerNodeRef); + + /* + * at the creation (1.0) + */ + + Version rootEdition = editionService.getEditions(mlContainerNodeRef).getAllVersions().iterator().next(); + // Ensure that the version label is 1.0 + assertTrue("The edition label would be 1.0 and not " + rootEdition.getVersionLabel(), rootEdition.getVersionLabel().equals("1.0")); + + /* + * default (1.1) + */ + + pivot = editionService.createEdition(pivot, versionProperties); + editions = orderVersions(editionService.getEditions(mlContainerNodeRef).getAllVersions()); + Version firstEdition = editions.get(0); + // Ensure that the version label is 1.1 + assertTrue("The edition label would be 1.1 and not " + firstEdition.getVersionLabel(), firstEdition.getVersionLabel().equals("1.1")); + + /* + * major (2.0) + */ + + versionProperties = new HashMap(); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); + pivot = editionService.createEdition(pivot, versionProperties); + editions = orderVersions(editionService.getEditions(mlContainerNodeRef).getAllVersions()); + Version secondEdition = editions.get(0); + // Ensure that the version label is 2.0 + assertTrue("The edition label would be 2.0 and not " + secondEdition.getVersionLabel(), secondEdition.getVersionLabel().equals("2.0")); + + /* + * minor (2.1) + */ + + versionProperties = new HashMap(); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + pivot = editionService.createEdition(pivot, versionProperties); + editions = orderVersions(editionService.getEditions(mlContainerNodeRef).getAllVersions()); + Version thirdEdition = editions.get(0); + // Ensure that the version label is 2.1 + assertTrue("The edition label would be 2.1 and not " + thirdEdition.getVersionLabel(), thirdEdition.getVersionLabel().equals("2.1")); + } + + public void testCreateEdition() throws Exception + { + // create a mlContainer with some content + NodeRef mlContainerNodeRef = createMLContainerWithContent(); + // get the french translation + NodeRef frenchContentNodeRef = multilingualContentService.getTranslationForLocale(mlContainerNodeRef, Locale.FRENCH); + + checkFirstVersion(mlContainerNodeRef); + + // create a new edition form the french translation + NodeRef newStartingPoint = editionService.createEdition(frenchContentNodeRef, null); + // get the edition history + VersionHistory editionHistory = editionService.getEditions(mlContainerNodeRef); + + // Ensure that the edition history contains two versions + assertTrue("The edition history must contain two versions", editionHistory.getAllVersions().size() == 2); + + // Ensure that the locale of the container is changer + assertTrue("The locale of the conatiner should be changed", nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE).equals(Locale.FRENCH)); + + // get the two editions + Version rootEdition = editionHistory.getVersion("1.0"); + Version actualEdition = editionHistory.getVersion("1.1"); + + // get the translations of the root versions + List rootVersionTranslations = editionService.getVersionedTranslations(rootEdition); + + // Ensure that the editions are not null + assertNotNull("The root edition can't be null", rootEdition); + assertNotNull("The actual edition can't be null", actualEdition); + assertNotNull("The translations list of the root edition can't be null", rootVersionTranslations); + + // Ensure that the new starting document noderef is different that the initial one + assertFalse("The created starting document must be different that the starting document of the edition", frenchContentNodeRef.equals(newStartingPoint)); + // Ensure that the new starting document is the pivot of the current translation + assertTrue("The new pivot must be equal to the created starting document", newStartingPoint.equals(multilingualContentService.getPivotTranslation(mlContainerNodeRef))); + + int numberOfTranslations; + + // Ensure that the current translations size is 1 + numberOfTranslations = multilingualContentService.getTranslations(mlContainerNodeRef).size(); + assertEquals("The number of translations must be 1 and not " + numberOfTranslations, 1, numberOfTranslations); + // Ensure that the number of translations of the current edition is 0 + numberOfTranslations = editionService.getVersionedTranslations(actualEdition).size(); + assertEquals("The number of translations must be 0 and not " + numberOfTranslations, 0, numberOfTranslations); + // Ensure that the number of translations of the root verions is 3 + numberOfTranslations = rootVersionTranslations.size(); + assertEquals("The number of translations must be 3 and not " + numberOfTranslations, 3, numberOfTranslations); + } + + public void testReadVersionedContent() throws Exception + { + + } + + public void testReadVersionedProperties() throws Exception + { + + } + + private void checkFirstVersion(NodeRef mlContainerNodeRef) + { + // get the edition list of edition + VersionHistory editionHistory = editionService.getEditions(mlContainerNodeRef); + + // Ensure that the first edition of the mlContainer is created + assertNotNull("The edition history can't be null", editionHistory); + // Ensure that it contains only one version + assertTrue("The edition history must contain only one edition", editionHistory.getAllVersions().size() == 1); + + // get the edition + Version currentEdition = editionHistory.getAllVersions().iterator().next(); + + // Ensure that this version is the edition of the mlContainer + assertTrue("The versioned mlContainer noderef of the editon must be the noderef of the created mlContainer", currentEdition.getVersionedNodeRef().equals(mlContainerNodeRef)); + + // get the list of translations + List translations = editionService.getVersionedTranslations(currentEdition); + + // Ensure that the get versioned translations is empty + assertNotNull("The translations list of the current edition can't be null", translations); + // Ensure that the list is empty + assertTrue("The translations list of the current edition would be empty", translations.size() == 0); + } + + private NodeRef createMLContainerWithContent() + { + NodeRef chineseContentNodeRef = createContent(CHINESE_CONTENT + "_1.0"); + NodeRef frenchContentNodeRef = createContent(FRENCH_CONTENT + "_1.0"); + NodeRef japaneseContentNodeRef = createContent(JAPANESE_CONTENT + "_1.0"); + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + + return multilingualContentService.getTranslationContainer(chineseContentNodeRef); + } + + private Comparator versionComparator = new Comparator() + { + public int compare(Object o1, Object o2) + { + String label01 = ((Version) o1).getVersionLabel(); + String label02 = ((Version) o2).getVersionLabel(); + + // sort the list ascending + return label02.compareTo(label01); + } + }; + + @SuppressWarnings("unchecked") + private List orderVersions(Collection allVersions) + { + List versionsAsList = new ArrayList(allVersions.size()); + versionsAsList.addAll(allVersions); + Collections.sort(versionsAsList, versionComparator); + return versionsAsList; + } +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java b/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java index 0a890f904f..03cde1fbfb 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java @@ -40,7 +40,7 @@ import org.alfresco.service.namespace.QName; * * @see org.alfresco.service.cmr.ml.EmptyTranslationAspect * - * @author yanipig + * @author Yannick Pignot */ public class EmptyTranslationAspectTest extends AbstractMultilingualTestCases { diff --git a/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java b/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java index 67830200c7..a7390dda7a 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java @@ -37,7 +37,7 @@ import org.alfresco.service.namespace.QName; * * @see org.alfresco.service.cmr.ml.MLContainerType * - * @author yanipig + * @author Yannick Pignot */ public class MLContainerTypeTest extends AbstractMultilingualTestCases { diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java b/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java index aff0322815..ea65fd55c4 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java @@ -24,11 +24,8 @@ */ package org.alfresco.repo.model.ml.tools; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Locale; -import java.util.Map; import net.sf.acegisecurity.Authentication; @@ -39,8 +36,6 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.version.Version; -import org.alfresco.service.cmr.version.VersionHistory; /** * @see org.alfresco.repo.ml.MultilingualContentServiceImpl @@ -231,58 +226,6 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest assertEquals("Empty translation name not generated correctly.", "Document2.txt", differentName); } - @SuppressWarnings("unused") - public void testCreateEdition() throws Exception - { - // Make some content - NodeRef chineseContentNodeRef = createContent(); - NodeRef frenchContentNodeRef = createContent(); - NodeRef japaneseContentNodeRef = createContent(); - // Add to container - multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); - multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); - multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); - - NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); - // Check the container child count - assertEquals("Incorrect number of child nodes", 3, nodeService.getChildAssocs(mlContainerNodeRef).size()); - - // Version each of the documents - List nodeRefs = new ArrayList(3); - nodeRefs.add(chineseContentNodeRef); - nodeRefs.add(frenchContentNodeRef); - nodeRefs.add(japaneseContentNodeRef); - versionService.createVersion(nodeRefs, null); - // Get the current versions of each of the documents - Version chineseVersionPreEdition = versionService.getCurrentVersion(chineseContentNodeRef); - Version frenchVersionPreEdition = versionService.getCurrentVersion(frenchContentNodeRef); - Version japaneseVersionPreEdition = versionService.getCurrentVersion(japaneseContentNodeRef); - - // Create the edition, keeping the Chinese translation as the basis - multilingualContentService.createEdition(chineseContentNodeRef); - // Check the container child count - assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size()); - - // Get the document versions now - Version chineseVersionPostEdition = versionService.getCurrentVersion(chineseContentNodeRef); - assertFalse("Expected document to be gone", nodeService.exists(frenchContentNodeRef)); - assertFalse("Expected document to be gone", nodeService.exists(japaneseContentNodeRef)); - - // Now be sure that we can get the required information using the version service - VersionHistory mlContainerVersionHistory = versionService.getVersionHistory(mlContainerNodeRef); - Collection mlContainerVersions = mlContainerVersionHistory.getAllVersions(); - // Loop through and get all the children of each version - for (Version mlContainerVersion : mlContainerVersions) - { - NodeRef versionedMLContainerNodeRef = mlContainerVersion.getFrozenStateNodeRef(); - // Get all the children - Map translationsByLocale = multilingualContentService.getTranslations( - versionedMLContainerNodeRef); - // Count the children - int count = translationsByLocale.size(); - } - } - public void testGetTranslationContainerPermissions() throws Exception { // Grant the guest user rights to our working folder @@ -335,7 +278,6 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.JAPANESE); - multilingualContentService.createEdition(chineseContentNodeRef); } finally { diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java index fd64cbde97..132541d0e4 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java @@ -37,7 +37,7 @@ import org.alfresco.service.namespace.QName; * * @see org.alfresco.service.cmr.ml.MultilingualDocumentAspect * - * @author yanipig + * @author Yannick Pignot */ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCases { diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java index dedee57e73..9795d388e0 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.version; @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.repo.version.common.VersionUtil; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.InvalidAspectException; import org.alfresco.service.cmr.dictionary.PropertyDefinition; @@ -57,22 +58,22 @@ import org.alfresco.service.namespace.RegexQNamePattern; /** * The light weight version store node service implementation. - * + * * @author Roy Wetherall */ -public class NodeServiceImpl implements NodeService, VersionModel +public class NodeServiceImpl implements NodeService, VersionModel { /** * Error messages */ - private final static String MSG_UNSUPPORTED = + private final static String MSG_UNSUPPORTED = "This operation is not supported by a version store implementation of the node service."; - + /** * The name of the spoofed root association */ private static final QName rootAssocName = QName.createQName(VersionModel.NAMESPACE_URI, "versionedState"); - + /** * The db node service, used as the version store implementation */ @@ -83,16 +84,16 @@ public class NodeServiceImpl implements NodeService, VersionModel */ @SuppressWarnings("unused") private SearchService searcher; - + /** * The dictionary service */ protected DictionaryService dicitionaryService; - - + + /** * Sets the db node service, used as the version store implementation - * + * * @param nodeService the node service */ public void setDbNodeService(NodeService nodeService) @@ -102,24 +103,24 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * Sets the searcher - * + * * @param searcher the searcher */ public void setSearcher(SearchService searcher) { - this.searcher = searcher; + this.searcher = searcher; } - + /** * Sets the dictionary service - * + * * @param dictionaryService the dictionary service */ public void setDictionaryService(DictionaryService dictionaryService) { this.dicitionaryService = dictionaryService; } - + /** * Delegates to the NodeService used as the version store implementation */ @@ -127,7 +128,7 @@ public class NodeServiceImpl implements NodeService, VersionModel { return dbNodeService.getStores(); } - + /** * Delegates to the NodeService used as the version store implementation */ @@ -149,9 +150,9 @@ public class NodeServiceImpl implements NodeService, VersionModel */ public boolean exists(NodeRef nodeRef) { - return dbNodeService.exists(convertNodeRef(nodeRef)); + return dbNodeService.exists(VersionUtil.convertNodeRef(nodeRef)); } - + /** * Delegates to the NodeService used as the version store implementation */ @@ -160,18 +161,6 @@ public class NodeServiceImpl implements NodeService, VersionModel return dbNodeService.getNodeStatus(nodeRef); } - /** - * Convert the incomming node ref (with the version store protocol specified) - * to the internal representation with the workspace protocol. - * - * @param nodeRef the incomming verison protocol node reference - * @return the internal version node reference - */ - private NodeRef convertNodeRef(NodeRef nodeRef) - { - return new NodeRef(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, STORE_ID), nodeRef.getId()); - } - /** * Delegates to the NodeService used as the version store implementation */ @@ -184,7 +173,7 @@ public class NodeServiceImpl implements NodeService, VersionModel * @throws UnsupportedOperationException always */ public ChildAssociationRef createNode( - NodeRef parentRef, + NodeRef parentRef, QName assocTypeQName, QName assocQName, QName nodeTypeQName) throws InvalidNodeRefException @@ -192,12 +181,12 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ public ChildAssociationRef createNode( - NodeRef parentRef, + NodeRef parentRef, QName assocTypeQName, QName assocQName, QName nodeTypeQName, @@ -206,7 +195,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -215,7 +204,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -227,7 +216,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -274,9 +263,9 @@ public class NodeServiceImpl implements NodeService, VersionModel */ public QName getType(NodeRef nodeRef) throws InvalidNodeRefException { - return (QName)this.dbNodeService.getProperty(convertNodeRef(nodeRef), PROP_QNAME_FROZEN_NODE_TYPE); + return (QName)this.dbNodeService.getProperty(VersionUtil.convertNodeRef(nodeRef), PROP_QNAME_FROZEN_NODE_TYPE); } - + /** * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @@ -285,7 +274,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -318,7 +307,7 @@ public class NodeServiceImpl implements NodeService, VersionModel public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException { return new HashSet( - (ArrayList)this.dbNodeService.getProperty(convertNodeRef(nodeRef), PROP_QNAME_FROZEN_ASPECTS)); + (ArrayList)this.dbNodeService.getProperty(VersionUtil.convertNodeRef(nodeRef), PROP_QNAME_FROZEN_ASPECTS)); } /** @@ -326,9 +315,9 @@ public class NodeServiceImpl implements NodeService, VersionModel */ public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException { - Map result = new HashMap(); - - Collection children = this.dbNodeService.getChildAssocs(convertNodeRef(nodeRef), CHILD_QNAME_VERSIONED_ATTRIBUTES, RegexQNamePattern.MATCH_ALL); + Map result = new HashMap(); + + Collection children = this.dbNodeService.getChildAssocs(VersionUtil.convertNodeRef(nodeRef), CHILD_QNAME_VERSIONED_ATTRIBUTES, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef child : children) { NodeRef versionedAttribute = child.getChildRef(); @@ -337,35 +326,35 @@ public class NodeServiceImpl implements NodeService, VersionModel Serializable value = null; QName qName = (QName)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_QNAME); PropertyDefinition propDef = this.dicitionaryService.getProperty(qName); - + Boolean isMultiValue = (Boolean)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_IS_MULTI_VALUE); if (isMultiValue.booleanValue() == false) { - value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE); - value = (Serializable)DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), value); + value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE); + value = (Serializable)DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), value); } else { value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_MULTI_VALUE); } - + result.put(qName, value); - } - - return result; + } + + return result; } - + /** * Property translation for version store */ public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException - { + { // TODO should be doing this with a search ... - - Map properties = getProperties(convertNodeRef(nodeRef)); - return properties.get(qname); + + Map properties = getProperties(VersionUtil.convertNodeRef(nodeRef)); + return properties.get(qname); } - + /** * @throws UnsupportedOperationException always */ @@ -374,7 +363,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -383,7 +372,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -395,17 +384,17 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * The node will appear to be attached to the root of the version store - * + * * @see NodeService#getParentAssocs(NodeRef) */ public List getParentAssocs(NodeRef nodeRef) { return getParentAssocs(nodeRef, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL); - } - + } + /** * The node will apprear to be attached to the root of the version store - * + * * @see NodeService#getParentAssocs(NodeRef, QNamePattern, QNamePattern) */ public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) @@ -428,7 +417,7 @@ public class NodeServiceImpl implements NodeService, VersionModel */ public List getChildAssocs(NodeRef nodeRef) throws InvalidNodeRefException { - return getChildAssocs(convertNodeRef(nodeRef), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL); + return getChildAssocs(VersionUtil.convertNodeRef(nodeRef), RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL); } /** @@ -438,46 +427,46 @@ public class NodeServiceImpl implements NodeService, VersionModel { // Get the child assocs from the version store List childAssocRefs = this.dbNodeService.getChildAssocs( - convertNodeRef(nodeRef), + VersionUtil.convertNodeRef(nodeRef), RegexQNamePattern.MATCH_ALL, CHILD_QNAME_VERSIONED_CHILD_ASSOCS); List result = new ArrayList(childAssocRefs.size()); for (ChildAssociationRef childAssocRef : childAssocRefs) { // Get the child reference NodeRef childRef = childAssocRef.getChildRef(); - NodeRef referencedNode = (NodeRef)this.dbNodeService.getProperty(childRef, ContentModel.PROP_REFERENCE); - + NodeRef referencedNode = (NodeRef)this.dbNodeService.getProperty(childRef, ContentModel.PROP_REFERENCE); + if (this.dbNodeService.exists(referencedNode) == true) { // get the qualified name of the frozen child association and filter out unwanted names QName qName = (QName)this.dbNodeService.getProperty(childRef, PROP_QNAME_ASSOC_QNAME); - + if (qnamePattern.isMatch(qName) == true) - { + { // Retrieve the isPrimary and nthSibling values of the forzen child association QName assocType = (QName)this.dbNodeService.getProperty(childRef, PROP_QNAME_ASSOC_TYPE_QNAME); boolean isPrimary = ((Boolean)this.dbNodeService.getProperty(childRef, PROP_QNAME_IS_PRIMARY)).booleanValue(); int nthSibling = ((Integer)this.dbNodeService.getProperty(childRef, PROP_QNAME_NTH_SIBLING)).intValue(); - + // Build a child assoc ref to add to the returned list ChildAssociationRef newChildAssocRef = new ChildAssociationRef( assocType, - nodeRef, - qName, - referencedNode, - isPrimary, + nodeRef, + qName, + referencedNode, + isPrimary, nthSibling); result.add(newChildAssocRef); } } } - + // sort the results so that the order appears to be exactly as it was originally Collections.sort(result); - + return result; } - + /** * @throws UnsupportedOperationException always */ @@ -488,7 +477,7 @@ public class NodeServiceImpl implements NodeService, VersionModel } /** - * Simulates the node begin attached ot the root node of the version store. + * Simulates the node begin attached ot the root node of the version store. */ public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException { @@ -498,7 +487,7 @@ public class NodeServiceImpl implements NodeService, VersionModel rootAssocName, nodeRef); } - + /** * @throws UnsupportedOperationException always */ @@ -508,7 +497,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -517,7 +506,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -525,31 +514,31 @@ public class NodeServiceImpl implements NodeService, VersionModel { // Get the child assocs from the version store List childAssocRefs = this.dbNodeService.getChildAssocs( - convertNodeRef(sourceRef), + VersionUtil.convertNodeRef(sourceRef), RegexQNamePattern.MATCH_ALL, CHILD_QNAME_VERSIONED_ASSOCS); List result = new ArrayList(childAssocRefs.size()); for (ChildAssociationRef childAssocRef : childAssocRefs) { // Get the assoc reference NodeRef childRef = childAssocRef.getChildRef(); - NodeRef referencedNode = (NodeRef)this.dbNodeService.getProperty(childRef, ContentModel.PROP_REFERENCE); - + NodeRef referencedNode = (NodeRef)this.dbNodeService.getProperty(childRef, ContentModel.PROP_REFERENCE); + if (this.dbNodeService.exists(referencedNode) == true) { // get the qualified type name of the frozen child association and filter out unwanted names QName qName = (QName)this.dbNodeService.getProperty(childRef, PROP_QNAME_ASSOC_TYPE_QNAME); - + if (qnamePattern.isMatch(qName) == true) - { + { AssociationRef newAssocRef = new AssociationRef(sourceRef, qName, referencedNode); result.add(newAssocRef); } } } - + return result; } - + /** * @throws UnsupportedOperationException always */ @@ -558,7 +547,7 @@ public class NodeServiceImpl implements NodeService, VersionModel // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); } - + /** * @throws UnsupportedOperationException always */ @@ -567,9 +556,9 @@ public class NodeServiceImpl implements NodeService, VersionModel ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef); Path path = new Path(); path.append(new Path.ChildAssocElement(childAssocRef)); - return path; + return path; } - + /** * @throws UnsupportedOperationException always */ diff --git a/source/java/org/alfresco/repo/version/VersionServiceImpl.java b/source/java/org/alfresco/repo/version/VersionServiceImpl.java index ea8214b7dd..f092e429fc 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImpl.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.version; @@ -62,11 +62,11 @@ import org.alfresco.util.ParameterCheck; /** * The version service implementation. - * + * * @author Roy Wetheral */ public class VersionServiceImpl extends AbstractVersionServiceImpl - implements VersionService, VersionModel + implements VersionService, VersionModel { /** * Error message I18N id's @@ -77,17 +77,17 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl private static final String MSGID_ERR_ONE_PRECEEDING = "version_service.err_one_preceeding"; private static final String MSGID_ERR_RESTORE_NO_VERSION = "version_service.err_restore_no_version"; private static final String MSGID_ERR_REVERT_MISMATCH = "version_service.err_revert_mismatch"; - + /** * The version counter service */ private VersionCounterService versionCounterService; - + /** * The db node service, used as the version store implementation */ protected NodeService dbNodeService; - + /** * Policy behaviour filter */ @@ -97,11 +97,11 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl * The repository searcher */ @SuppressWarnings("unused") - private SearchService searcher; - + private SearchService searcher; + /** * Sets the db node service, used as the version store implementation - * + * * @param nodeService the node service */ public void setDbNodeService(NodeService nodeService) @@ -114,68 +114,74 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl */ public void setSearcher(SearchService searcher) { - this.searcher = searcher; + this.searcher = searcher; } - + /** * @param versionCounterService the version counter service */ public void setVersionCounterService(VersionCounterService versionCounterService) { this.versionCounterService = versionCounterService; - } - + } + /** * Set the policy behaviour filter - * + * * @param policyBehaviourFilter the policy behaviour filter */ public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter) { this.policyBehaviourFilter = policyBehaviourFilter; } - - /** - * Initialise method - */ - @Override + + /** + * Initialise method + */ + @Override public void initialise() { - super.initialise(); - - // Register the serial version label behaviour - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"), - ContentModel.TYPE_CMOBJECT, - new JavaBehaviour(new SerialVersionLabelPolicy(), "calculateVersionLabel")); - } - - /** + super.initialise(); + + // Register the serial version label behaviour + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"), + ContentModel.TYPE_CMOBJECT, + new JavaBehaviour(new SerialVersionLabelPolicy(), "calculateVersionLabel")); + + // Register the serial version label behaviour for the mlContainer too + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"), + ContentModel.TYPE_MULTILINGUAL_CONTAINER, + new JavaBehaviour(new SerialVersionLabelPolicy(), "calculateVersionLabel")); + } + + /** * Gets the reference to the version store - * + * * @return reference to the version store */ public StoreRef getVersionStoreReference() { return new StoreRef( StoreRef.PROTOCOL_WORKSPACE, - VersionModel.STORE_ID); + VersionModel.STORE_ID); } - + /** * @see VersionCounterService#nextVersionNumber(StoreRef) */ public Version createVersion( - NodeRef nodeRef, + NodeRef nodeRef, Map versionProperties) throws ReservedVersionNameException, AspectMissingException { // Get the next version number int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference()); - + // Create the version return createVersion(nodeRef, versionProperties, versionNumber); - } + } /** * The version's are created from the children upwards with the parent being created first. This will @@ -183,43 +189,43 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl * for the (possibly) newly created version histories. */ public Collection createVersion( - NodeRef nodeRef, + NodeRef nodeRef, Map versionProperties, boolean versionChildren) throws ReservedVersionNameException, AspectMissingException { // Get the next version number int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference()); - + // Create the versions return createVersion(nodeRef, versionProperties, versionChildren, versionNumber); } - + /** * Helper method used to create the version when the versionChildren flag is provided. This method - * ensures that all the children (if the falg is set to true) are created with the same version + * ensures that all the children (if the falg is set to true) are created with the same version * number, this ensuring that the version stripe is correct. - * + * * @param nodeRef the parent node reference * @param versionProperties the version properties * @param versionChildren indicates whether to version the children of the parent * node * @param versionNumber the version number - + * @return a collection of the created versions * @throws ReservedVersionNameException thrown if there is a reserved version property name clash * @throws AspectMissingException thrown if the version aspect is missing from a node */ private Collection createVersion( - NodeRef nodeRef, + NodeRef nodeRef, Map versionProperties, boolean versionChildren, - int versionNumber) + int versionNumber) throws ReservedVersionNameException, AspectMissingException { Collection result = new ArrayList(); - + if (versionChildren == true) { // Get the children of the node @@ -228,84 +234,84 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl { // Recurse into this method to version all the children with the same version number Collection childVersions = createVersion( - childAssoc.getChildRef(), - versionProperties, - versionChildren, + childAssoc.getChildRef(), + versionProperties, + versionChildren, versionNumber); result.addAll(childVersions); } } - + result.add(createVersion(nodeRef, versionProperties, versionNumber)); - + return result; } /** * Note: we can't control the order of the list, so if we have children and parents in the list and the - * parents get versioned before the children and the children are not already versioned then the parents + * parents get versioned before the children and the children are not already versioned then the parents * child references will be pointing to the node ref, rather than the verison history. */ public Collection createVersion( - Collection nodeRefs, + Collection nodeRefs, Map versionProperties) throws ReservedVersionNameException, AspectMissingException { Collection result = new ArrayList(nodeRefs.size()); - + // Get the next version number int versionNumber = this.versionCounterService.nextVersionNumber(getVersionStoreReference()); - + // Version each node in the list for (NodeRef nodeRef : nodeRefs) { result.add(createVersion(nodeRef, versionProperties, versionNumber)); } - + return result; } - + /** - * Creates a new version of the passed node assigning the version properties + * Creates a new version of the passed node assigning the version properties * accordingly. - * + * * @param nodeRef a node reference * @param versionProperties the version properties * @param versionNumber the version number * @return the newly created version * @throws ReservedVersionNameException - * thrown if there is a name clash in the version properties + * thrown if there is a name clash in the version properties */ private Version createVersion( - NodeRef nodeRef, - Map origVersionProperties, + NodeRef nodeRef, + Map origVersionProperties, int versionNumber) throws ReservedVersionNameException { - // Copy the version properties (to prevent unexpected side effects to the caller) - Map versionProperties = new HashMap(); + // Copy the version properties (to prevent unexpected side effects to the caller) + Map versionProperties = new HashMap(); if (origVersionProperties != null) { versionProperties.putAll(origVersionProperties); } - + // If the version aspect is not there then add it if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) { this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null); } - + // Call the policy behaviour - invokeBeforeCreateVersion(nodeRef); - + invokeBeforeCreateVersion(nodeRef); + // Check that the supplied additional version properties do not clash with the reserved ones VersionUtil.checkVersionPropertyNames(versionProperties.keySet()); - + // Check the repository for the version history for this node - NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); + NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); NodeRef currentVersionRef = null; - + if (versionHistoryRef == null) { // Create the version history @@ -315,13 +321,13 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl { // Since we have an exisiting version history we should be able to lookup // the current version - currentVersionRef = getCurrentVersionNodeRef(versionHistoryRef, nodeRef); - + currentVersionRef = getCurrentVersionNodeRef(versionHistoryRef, nodeRef); + if (currentVersionRef == null) { throw new VersionServiceException(MSGID_ERR_NOT_FOUND); } - + // Need to check that we are not about to create branch since this is not currently supported VersionHistory versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); Version currentVersion = getVersion(currentVersionRef); @@ -330,75 +336,75 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl throw new VersionServiceException(MSGID_ERR_NO_BRANCHES); } } - - // Create the node details - QName classRef = this.nodeService.getType(nodeRef); - PolicyScope nodeDetails = new PolicyScope(classRef); - - // Get the node details by calling the onVersionCreate policy behaviour - invokeOnCreateVersion(nodeRef, versionProperties, nodeDetails); - - // Create the new version node (child of the version history) + + // Create the node details + QName classRef = this.nodeService.getType(nodeRef); + PolicyScope nodeDetails = new PolicyScope(classRef); + + // Get the node details by calling the onVersionCreate policy behaviour + invokeOnCreateVersion(nodeRef, versionProperties, nodeDetails); + + // Create the new version node (child of the version history) NodeRef newVersionRef = createNewVersion( - nodeRef, + nodeRef, versionHistoryRef, getStandardVersionProperties(versionProperties, nodeRef, currentVersionRef, versionNumber), - versionProperties, + versionProperties, nodeDetails); - + if (currentVersionRef == null) { // Set the new version to be the root version in the version history this.dbNodeService.createAssociation( - versionHistoryRef, - newVersionRef, + versionHistoryRef, + newVersionRef, VersionServiceImpl.ASSOC_ROOT_VERSION); } else { // Relate the new version to the current version as its successor this.dbNodeService.createAssociation( - currentVersionRef, - newVersionRef, + currentVersionRef, + newVersionRef, VersionServiceImpl.ASSOC_SUCCESSOR); } - + // Create the version data object Version version = getVersion(newVersionRef); - + // Set the new version label on the versioned node this.nodeService.setProperty( - nodeRef, - ContentModel.PROP_VERSION_LABEL, + nodeRef, + ContentModel.PROP_VERSION_LABEL, version.getVersionLabel()); - + // Invoke the policy behaviour invokeAfterCreateVersion(nodeRef, version); - + // Return the data object representing the newly created version return version; } - + /** * Creates a new version history node, applying the root version aspect is required - * + * * @param nodeRef the node ref * @return the version history node reference */ private NodeRef createVersionHistory(NodeRef nodeRef) - { + { HashMap props = new HashMap(); props.put(ContentModel.PROP_NAME, nodeRef.getId()); props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId()); - + // Create a new version history node ChildAssociationRef childAssocRef = this.dbNodeService.createNode( - getRootNode(), - CHILD_QNAME_VERSION_HISTORIES, + getRootNode(), + CHILD_QNAME_VERSION_HISTORIES, QName.createQName(VersionModel.NAMESPACE_URI, nodeRef.getId()), TYPE_QNAME_VERSION_HISTORY, props); - return childAssocRef.getChildRef(); + return childAssocRef.getChildRef(); } /** @@ -407,237 +413,237 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl public VersionHistory getVersionHistory(NodeRef nodeRef) { VersionHistory versionHistory = null; - + if (this.nodeService.exists(nodeRef) == true) { - NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); - if (versionHistoryRef != null) - { - versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); - } + NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); + if (versionHistoryRef != null) + { + versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); + } } - + return versionHistory; - } - - /** - * @see VersionService#getCurrentVersion(NodeRef) - */ - public Version getCurrentVersion(NodeRef nodeRef) - { - Version version = null; - - if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) - { - VersionHistory versionHistory = getVersionHistory(nodeRef); - if (versionHistory != null) - { - String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - version = versionHistory.getVersion(versionLabel); - } - } - - return version; - } - + } + + /** + * @see VersionService#getCurrentVersion(NodeRef) + */ + public Version getCurrentVersion(NodeRef nodeRef) + { + Version version = null; + + if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) + { + VersionHistory versionHistory = getVersionHistory(nodeRef); + if (versionHistory != null) + { + String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); + version = versionHistory.getVersion(versionLabel); + } + } + + return version; + } + /** * Get a map containing the standard list of version properties populated. - * + * * @param versionProperties the version meta data properties * @param nodeRef the node reference * @param preceedingNodeRef the preceeding node reference * @param versionNumber the version number * @return the standard version properties */ - private Map getStandardVersionProperties(Map versionProperties, NodeRef nodeRef, NodeRef preceedingNodeRef, int versionNumber) - { + private Map getStandardVersionProperties(Map versionProperties, NodeRef nodeRef, NodeRef preceedingNodeRef, int versionNumber) + { Map result = new HashMap(10); - - // Set the version number for the new version + + // Set the version number for the new version result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_VERSION_NUMBER), Integer.toString(versionNumber)); - - // Set the versionable node id + + // Set the versionable node id result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_ID), nodeRef.getId()); - - // Set the versionable node store protocol + + // Set the versionable node store protocol result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL), nodeRef.getStoreRef().getProtocol()); - - // Set the versionable node store id + + // Set the versionable node store id result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID), nodeRef.getStoreRef().getIdentifier()); - + // Store the current node type QName nodeType = this.nodeService.getType(nodeRef); - result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_TYPE), nodeType); - + result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_TYPE), nodeType); + // Store the current aspects Set aspects = this.nodeService.getAspects(nodeRef); result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_ASPECTS), (Serializable)aspects); - + // Calculate the version label - QName classRef = this.nodeService.getType(nodeRef); - Version preceedingVersion = getVersion(preceedingNodeRef); + QName classRef = this.nodeService.getType(nodeRef); + Version preceedingVersion = getVersion(preceedingNodeRef); String versionLabel = invokeCalculateVersionLabel(classRef, preceedingVersion, versionNumber, versionProperties); result.put(QName.createQName(NAMESPACE_URI, VersionModel.PROP_VERSION_LABEL), versionLabel); - + return result; - } - + } + /** * Creates a new version node, setting the properties both calculated and specified. - * + * * @param versionableNodeRef the reference to the node being versioned * @param versionHistoryRef version history node reference * @param preceedingNodeRef the version node preceeding this in the version history - * , null if none + * , null if none * @param versionProperties version properties - * @param versionNumber the version number + * @param versionNumber the version number * @return the version node reference */ private NodeRef createNewVersion( - NodeRef versionableNodeRef, - NodeRef versionHistoryRef, + NodeRef versionableNodeRef, + NodeRef versionHistoryRef, Map standardVersionProperties, - Map versionProperties, - PolicyScope nodeDetails) - { + Map versionProperties, + PolicyScope nodeDetails) + { // Create the new version ChildAssociationRef childAssocRef = this.dbNodeService.createNode( - versionHistoryRef, - CHILD_QNAME_VERSIONS, + versionHistoryRef, + CHILD_QNAME_VERSIONS, CHILD_QNAME_VERSIONS, TYPE_QNAME_VERSION, standardVersionProperties); NodeRef versionNodeRef = childAssocRef.getChildRef(); - + // Store the meta data storeVersionMetaData(versionNodeRef, versionProperties); - - // Freeze the various parts of the node - freezeProperties(versionNodeRef, nodeDetails.getProperties()); - freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations()); - freezeAssociations(versionNodeRef, nodeDetails.getAssociations()); - freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects()); - - // Return the created node reference - return versionNodeRef; + + // Freeze the various parts of the node + freezeProperties(versionNodeRef, nodeDetails.getProperties()); + freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations()); + freezeAssociations(versionNodeRef, nodeDetails.getAssociations()); + freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects()); + + // Return the created node reference + return versionNodeRef; } - + /** * Store the version meta data - * + * * @param versionNodeRef the version node reference * @param versionProperties the version properties */ - private void storeVersionMetaData(NodeRef versionNodeRef, Map versionProperties) + private void storeVersionMetaData(NodeRef versionNodeRef, Map versionProperties) { for (Map.Entry entry : versionProperties.entrySet()) { HashMap properties = new HashMap(); - + properties.put(PROP_QNAME_META_DATA_NAME, entry.getKey()); properties.put(PROP_QNAME_META_DATA_VALUE, entry.getValue()); - + this.dbNodeService.createNode( versionNodeRef, - CHILD_QNAME_VERSION_META_DATA, - CHILD_QNAME_VERSION_META_DATA, + CHILD_QNAME_VERSION_META_DATA, + CHILD_QNAME_VERSION_META_DATA, TYPE_QNAME_VERSION_META_DATA_VALUE, properties); } } /** - * Freeze the aspects - * - * @param nodeDetails the node details - * @param versionNodeRef the version node reference - * @param aspects the set of aspects - */ - private void freezeAspects(PolicyScope nodeDetails, NodeRef versionNodeRef, Set aspects) - { - for (QName aspect : aspects) - { - // Freeze the details of the aspect - freezeProperties(versionNodeRef, nodeDetails.getProperties(aspect)); - freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations(aspect)); - freezeAssociations(versionNodeRef, nodeDetails.getAssociations(aspect)); - } - } + * Freeze the aspects + * + * @param nodeDetails the node details + * @param versionNodeRef the version node reference + * @param aspects the set of aspects + */ + private void freezeAspects(PolicyScope nodeDetails, NodeRef versionNodeRef, Set aspects) + { + for (QName aspect : aspects) + { + // Freeze the details of the aspect + freezeProperties(versionNodeRef, nodeDetails.getProperties(aspect)); + freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations(aspect)); + freezeAssociations(versionNodeRef, nodeDetails.getAssociations(aspect)); + } + } - /** - * Freeze associations - * - * @param versionNodeRef the version node reference - * @param associations the list of associations - */ - private void freezeAssociations(NodeRef versionNodeRef, List associations) - { - for (AssociationRef targetAssoc : associations) + /** + * Freeze associations + * + * @param versionNodeRef the version node reference + * @param associations the list of associations + */ + private void freezeAssociations(NodeRef versionNodeRef, List associations) + { + for (AssociationRef targetAssoc : associations) { HashMap properties = new HashMap(); - + // Set the qname of the association properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, targetAssoc.getTypeQName()); - + // Set the reference property to point to the child node properties.put(ContentModel.PROP_REFERENCE, targetAssoc.getTargetRef()); - + // Create child version reference this.dbNodeService.createNode( versionNodeRef, - CHILD_QNAME_VERSIONED_ASSOCS, - CHILD_QNAME_VERSIONED_ASSOCS, + CHILD_QNAME_VERSIONED_ASSOCS, + CHILD_QNAME_VERSIONED_ASSOCS, TYPE_QNAME_VERSIONED_ASSOC, properties); } - } + } + + /** + * Freeze child associations + * + * @param versionNodeRef the version node reference + * @param childAssociations the child associations + */ + private void freezeChildAssociations(NodeRef versionNodeRef, List childAssociations) + { + for (ChildAssociationRef childAssocRef : childAssociations) + { + HashMap properties = new HashMap(); - /** - * Freeze child associations - * - * @param versionNodeRef the version node reference - * @param childAssociations the child associations - */ - private void freezeChildAssociations(NodeRef versionNodeRef, List childAssociations) - { - for (ChildAssociationRef childAssocRef : childAssociations) - { - HashMap properties = new HashMap(); - // Set the qname, isPrimary and nthSibling properties properties.put(PROP_QNAME_ASSOC_QNAME, childAssocRef.getQName()); properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, childAssocRef.getTypeQName()); properties.put(PROP_QNAME_IS_PRIMARY, Boolean.valueOf(childAssocRef.isPrimary())); properties.put(PROP_QNAME_NTH_SIBLING, Integer.valueOf(childAssocRef.getNthSibling())); - + // Set the reference property to point to the child node properties.put(ContentModel.PROP_REFERENCE, childAssocRef.getChildRef()); - + // Create child version reference this.dbNodeService.createNode( versionNodeRef, - CHILD_QNAME_VERSIONED_CHILD_ASSOCS, - CHILD_QNAME_VERSIONED_CHILD_ASSOCS, + CHILD_QNAME_VERSIONED_CHILD_ASSOCS, + CHILD_QNAME_VERSIONED_CHILD_ASSOCS, TYPE_QNAME_VERSIONED_CHILD_ASSOC, properties); - } - } + } + } - /** - * Freeze properties - * - * @param versionNodeRef the version node reference - * @param properties the properties - */ - private void freezeProperties(NodeRef versionNodeRef, Map properties) - { - // Copy the property values from the node onto the version node + /** + * Freeze properties + * + * @param versionNodeRef the version node reference + * @param properties the properties + */ + private void freezeProperties(NodeRef versionNodeRef, Map properties) + { + // Copy the property values from the node onto the version node for (Map.Entry entry : properties.entrySet()) - { + { // Get the property values HashMap props = new HashMap(); - props.put(PROP_QNAME_QNAME, entry.getKey()); - + props.put(PROP_QNAME_QNAME, entry.getKey()); + if (entry.getValue() instanceof Collection) { props.put(PROP_QNAME_MULTI_VALUE, entry.getValue()); @@ -648,34 +654,34 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl props.put(PROP_QNAME_VALUE, entry.getValue()); props.put(PROP_QNAME_IS_MULTI_VALUE, false); } - + // Create the node storing the frozen attribute details this.dbNodeService.createNode( - versionNodeRef, - CHILD_QNAME_VERSIONED_ATTRIBUTES, + versionNodeRef, + CHILD_QNAME_VERSIONED_ATTRIBUTES, CHILD_QNAME_VERSIONED_ATTRIBUTES, TYPE_QNAME_VERSIONED_PROPERTY, - props); + props); } - } - - /** - * Gets the version stores root node - * - * @return the node ref to the root node of the version store - */ - private NodeRef getRootNode() - { - // Get the version store root node reference + } + + /** + * Gets the version stores root node + * + * @return the node ref to the root node of the version store + */ + private NodeRef getRootNode() + { + // Get the version store root node reference return this.dbNodeService.getRootNode(getVersionStoreReference()); - } - - /** + } + + /** * Builds a version history object from the version history reference. *

* The node ref is passed to enable the version history to be scoped to the * appropriate branch in the version history. - * + * * @param versionHistoryRef the node ref for the version history * @param nodeRef the node reference * @return a constructed version history object @@ -683,42 +689,42 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl private VersionHistory buildVersionHistory(NodeRef versionHistoryRef, NodeRef nodeRef) { VersionHistory versionHistory = null; - + ArrayList versionHistoryNodeRefs = new ArrayList(); NodeRef currentVersion = getCurrentVersionNodeRef(versionHistoryRef, nodeRef); - + while (currentVersion != null) { AssociationRef preceedingVersion = null; - + versionHistoryNodeRefs.add(0, currentVersion); - + List preceedingVersions = this.dbNodeService.getSourceAssocs( - currentVersion, - VersionModel.ASSOC_SUCCESSOR); + currentVersion, + VersionModel.ASSOC_SUCCESSOR); if (preceedingVersions.size() == 1) { preceedingVersion = (AssociationRef)preceedingVersions.toArray()[0]; - currentVersion = preceedingVersion.getSourceRef(); + currentVersion = preceedingVersion.getSourceRef(); } else if (preceedingVersions.size() > 1) { // Error since we only currently support one preceeding version throw new VersionServiceException(MSGID_ERR_ONE_PRECEEDING); - } + } else { currentVersion = null; } } - + // Build the version history object boolean isRoot = true; Version preceeding = null; for (NodeRef versionRef : versionHistoryNodeRefs) { Version version = getVersion(versionRef); - + if (isRoot == true) { versionHistory = new VersionHistoryImpl(version); @@ -730,13 +736,13 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl } preceeding = version; } - + return versionHistory; - } - + } + /** * Constructs the a version object to contain the version information from the version node ref. - * + * * @param versionRef the version reference * @return object containing verison data */ @@ -747,15 +753,15 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl return null; } Map versionProperties = new HashMap(); - + // Get the standard node details Map nodeProperties = this.dbNodeService.getProperties(versionRef); for (QName key : nodeProperties.keySet()) - { + { Serializable value = nodeProperties.get(key); versionProperties.put(key.getLocalName(), value); } - + // Get the meta data List metaData = this.dbNodeService.getChildAssocs( versionRef, @@ -768,17 +774,17 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl Serializable value = this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_VALUE); versionProperties.put(name, value); } - + // Create and return the version object NodeRef newNodeRef = new NodeRef(new StoreRef(STORE_PROTOCOL, STORE_ID), versionRef.getId()); Version result = new VersionImpl(versionProperties, newNodeRef); - // done - return result; + // done + return result; } - + /** * Gets a reference to the version history node for a given 'real' node. - * + * * @param nodeRef a node reference * @return a reference to the version history node, null of none */ @@ -786,13 +792,13 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl { return this.dbNodeService.getChildByName(getRootNode(), CHILD_QNAME_VERSION_HISTORIES, nodeRef.getId()); } - + /** * Gets a reference to the node for the current version of the passed node ref. - * + * * This uses the version label as a mechanism for looking up the version node in * the version history. - * + * * @param nodeRef a node reference * @return a reference to a version reference */ @@ -800,41 +806,41 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl { NodeRef result = null; String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - + Collection versions = this.dbNodeService.getChildAssocs(versionHistory); for (ChildAssociationRef version : versions) { String tempLabel = (String)this.dbNodeService.getProperty(version.getChildRef(), VersionModel.PROP_QNAME_VERSION_LABEL); if (tempLabel != null && tempLabel.equals(versionLabel) == true) { - result = version.getChildRef(); + result = version.getChildRef(); break; } } - + return result; - } - + } + /** * @see org.alfresco.cms.version.VersionService#revert(NodeRef) */ - public void revert(NodeRef nodeRef) + public void revert(NodeRef nodeRef) { - revert(nodeRef, getCurrentVersion(nodeRef), true); - } - + revert(nodeRef, getCurrentVersion(nodeRef), true); + } + /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, boolean) */ - public void revert(NodeRef nodeRef, boolean deep) + public void revert(NodeRef nodeRef, boolean deep) { revert(nodeRef, getCurrentVersion(nodeRef), deep); } - + /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version) */ - public void revert(NodeRef nodeRef, Version version) + public void revert(NodeRef nodeRef, Version version) { revert(nodeRef, version, true); } @@ -842,75 +848,75 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl /** * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version, boolean) */ - public void revert(NodeRef nodeRef, Version version, boolean deep) - { - // Check the mandatory parameters - ParameterCheck.mandatory("nodeRef", nodeRef); - ParameterCheck.mandatory("version", version); - + public void revert(NodeRef nodeRef, Version version, boolean deep) + { + // Check the mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("version", version); + // Cross check that the version provided relates to the node reference provided if (nodeRef.getId().equals(version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID)) == false) { // Error since the version provided does not correspond to the node reference provided throw new VersionServiceException(MSGID_ERR_REVERT_MISMATCH); } - + // Turn off any auto-version policy behaviours this.policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); try { // Store the current version label String currentVersionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - + // Get the node that represents the frozen state - NodeRef versionNodeRef = version.getFrozenStateNodeRef(); - - // Revert the property values - this.nodeService.setProperties(nodeRef, this.nodeService.getProperties(versionNodeRef)); - - // Apply/remove the aspects as required - Set aspects = new HashSet(this.nodeService.getAspects(nodeRef)); - for (QName versionAspect : this.nodeService.getAspects(versionNodeRef)) - { - if (aspects.contains(versionAspect) == false) - { - this.nodeService.addAspect(nodeRef, versionAspect, null); - } - else - { - aspects.remove(versionAspect); - } - } - for (QName aspect : aspects) - { - this.nodeService.removeAspect(nodeRef, aspect); - } - - // Re-add the versionable aspect to the reverted node + NodeRef versionNodeRef = version.getFrozenStateNodeRef(); + + // Revert the property values + this.nodeService.setProperties(nodeRef, this.nodeService.getProperties(versionNodeRef)); + + // Apply/remove the aspects as required + Set aspects = new HashSet(this.nodeService.getAspects(nodeRef)); + for (QName versionAspect : this.nodeService.getAspects(versionNodeRef)) + { + if (aspects.contains(versionAspect) == false) + { + this.nodeService.addAspect(nodeRef, versionAspect, null); + } + else + { + aspects.remove(versionAspect); + } + } + for (QName aspect : aspects) + { + this.nodeService.removeAspect(nodeRef, aspect); + } + + // Re-add the versionable aspect to the reverted node if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) { this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null); } - + // Re-set the version label property (since it should not be modified from the origional) this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, currentVersionLabel); - - // Add/remove the child nodes - List children = new ArrayList(this.nodeService.getChildAssocs(nodeRef)); - for (ChildAssociationRef versionedChild : this.nodeService.getChildAssocs(versionNodeRef)) - { - if (children.contains(versionedChild) == false) - { - if (this.nodeService.exists(versionedChild.getChildRef()) == true) - { - // The node was a primary child of the parent, but that is no longer the case. Dispite this - // the node still exits so this means it has been moved. - // The best thing to do in this situation will be to re-add the node as a child, but it will not - // be a primary child. - this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName()); - } - else - { + + // Add/remove the child nodes + List children = new ArrayList(this.nodeService.getChildAssocs(nodeRef)); + for (ChildAssociationRef versionedChild : this.nodeService.getChildAssocs(versionNodeRef)) + { + if (children.contains(versionedChild) == false) + { + if (this.nodeService.exists(versionedChild.getChildRef()) == true) + { + // The node was a primary child of the parent, but that is no longer the case. Dispite this + // the node still exits so this means it has been moved. + // The best thing to do in this situation will be to re-add the node as a child, but it will not + // be a primary child. + this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName()); + } + else + { if (versionedChild.isPrimary() == true) { // Only try to resotre missing children if we are doing a deep revert @@ -921,82 +927,82 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl restore( versionedChild.getChildRef(), nodeRef, - versionedChild.getTypeQName(), + versionedChild.getTypeQName(), versionedChild.getQName()); } // else the deleted child did not have a version history so we can't restore the child // and so we can't revert the association } - + // else // Since this was never a primary assoc and the child has been deleted we won't recreate // the missing node as it was never owned by the node and we wouldn't know where to put it. - } - } - else - { - children.remove(versionedChild); - } - } - for (ChildAssociationRef ref : children) - { - this.nodeService.removeChild(nodeRef, ref.getChildRef()); - } - - // Add/remove the target associations - for (AssociationRef assocRef : this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL)) - { - this.nodeService.removeAssociation(assocRef.getSourceRef(), assocRef.getTargetRef(), assocRef.getTypeQName()); - } - for (AssociationRef versionedAssoc : this.nodeService.getTargetAssocs(versionNodeRef, RegexQNamePattern.MATCH_ALL)) - { - if (this.nodeService.exists(versionedAssoc.getTargetRef()) == true) - { - this.nodeService.createAssociation(nodeRef, versionedAssoc.getTargetRef(), versionedAssoc.getTypeQName()); - } - - // else + } + } + else + { + children.remove(versionedChild); + } + } + for (ChildAssociationRef ref : children) + { + this.nodeService.removeChild(nodeRef, ref.getChildRef()); + } + + // Add/remove the target associations + for (AssociationRef assocRef : this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL)) + { + this.nodeService.removeAssociation(assocRef.getSourceRef(), assocRef.getTargetRef(), assocRef.getTypeQName()); + } + for (AssociationRef versionedAssoc : this.nodeService.getTargetAssocs(versionNodeRef, RegexQNamePattern.MATCH_ALL)) + { + if (this.nodeService.exists(versionedAssoc.getTargetRef()) == true) + { + this.nodeService.createAssociation(nodeRef, versionedAssoc.getTargetRef(), versionedAssoc.getTypeQName()); + } + + // else // Since the tareget of the assoc no longer exists we can't recreate the assoc - } + } } finally { // Turn auto-version policies back on this.policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); } - } - + } + /** * @see org.alfresco.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) */ public NodeRef restore( NodeRef nodeRef, - NodeRef parentNodeRef, + NodeRef parentNodeRef, QName assocTypeQName, QName assocQName) { return restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true); } - + /** * @see org.alfresco.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, boolean) */ public NodeRef restore( NodeRef nodeRef, - NodeRef parentNodeRef, + NodeRef parentNodeRef, QName assocTypeQName, QName assocQName, boolean deep) { NodeRef restoredNodeRef = null; - - // Check that the node does not exist + + // Check that the node does not exist if (this.nodeService.exists(nodeRef) == true) { // Error since you can not restore a node that already exists throw new VersionServiceException(MSGID_ERR_RESTORE_EXISTS, new Object[]{nodeRef.toString()}); } - + // Try and get the version details that we want to restore to Version version = getHeadVersion(nodeRef); if (version == null) @@ -1004,14 +1010,14 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl // Error since there is no version information available to restore the node from throw new VersionServiceException(MSGID_ERR_RESTORE_NO_VERSION, new Object[]{nodeRef.toString()}); } - + // Set the uuid of the new node Map props = new HashMap(1); props.put(ContentModel.PROP_NODE_UUID, version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID)); - + // Get the type of the node node QName type = (QName)version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_TYPE); - + // Disable auto-version behaviour this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); try @@ -1029,16 +1035,16 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl // Enable auto-version behaviour this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); } - + // Now we need to revert the newly restored node revert(restoredNodeRef, version, deep); - + return restoredNodeRef; } - + /** * Get the head version given a node reference - * + * * @param nodeRef the node reference * @return the 'head' version */ @@ -1046,7 +1052,7 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl { Version version = null; StoreRef storeRef = nodeRef.getStoreRef(); - + NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef); if (versionHistoryNodeRef != null) { @@ -1067,33 +1073,33 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl if (storeRef.equals(versionStoreRef) == true) { version = getVersion(versionNodeRef); - } + } } } } - + return version; } - /** - * @see org.alfresco.cms.version.VersionService#deleteVersionHistory(NodeRef) - */ - public void deleteVersionHistory(NodeRef nodeRef) - throws AspectMissingException - { - // Get the version history node for the node is question and delete it - NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef); - - if (versionHistoryNodeRef != null) - { - // Delete the version history node - this.dbNodeService.deleteNode(versionHistoryNodeRef); - - if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) - { - // Reset the version label property on the versionable node - this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, null); - } - } - } + /** + * @see org.alfresco.cms.version.VersionService#deleteVersionHistory(NodeRef) + */ + public void deleteVersionHistory(NodeRef nodeRef) + throws AspectMissingException + { + // Get the version history node for the node is question and delete it + NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef); + + if (versionHistoryNodeRef != null) + { + // Delete the version history node + this.dbNodeService.deleteNode(versionHistoryNodeRef); + + if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) + { + // Reset the version label property on the versionable node + this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, null); + } + } + } } diff --git a/source/java/org/alfresco/repo/version/common/VersionUtil.java b/source/java/org/alfresco/repo/version/common/VersionUtil.java index 35cd5d5e27..2d9fdaf58b 100644 --- a/source/java/org/alfresco/repo/version/common/VersionUtil.java +++ b/source/java/org/alfresco/repo/version/common/VersionUtil.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.version.common; @@ -27,11 +27,13 @@ package org.alfresco.repo.version.common; import java.util.Collection; import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.version.ReservedVersionNameException; /** * Helper class containing helper methods for the versioning services. - * + * * @author Roy Wetherall */ public class VersionUtil @@ -40,20 +42,20 @@ public class VersionUtil * Reserved property names */ public static final String[] RESERVED_PROPERTY_NAMES = new String[]{ - VersionModel.PROP_CREATED_DATE, - VersionModel.PROP_FROZEN_NODE_ID, - VersionModel.PROP_FROZEN_NODE_STORE_ID, + VersionModel.PROP_CREATED_DATE, + VersionModel.PROP_FROZEN_NODE_ID, + VersionModel.PROP_FROZEN_NODE_STORE_ID, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL, VersionModel.PROP_FROZEN_NODE_TYPE, VersionModel.PROP_FROZEN_ASPECTS, VersionModel.PROP_VERSION_LABEL, VersionModel.PROP_VERSION_NUMBER}; - + /** * Checks that the names of the additional version properties are valid and that they do not clash * with the reserved properties. - * - * @param versionProperties the property names + * + * @param versionProperties the property names * @return true is the names are considered valid, false otherwise * @throws ReservedVersionNameException */ @@ -68,4 +70,16 @@ public class VersionUtil } } } + + /** + * Convert the incomming node ref (with the version store protocol specified) + * to the internal representation with the workspace protocol. + * + * @param nodeRef the incomming verison protocol node reference + * @return the internal version node reference + */ + public static NodeRef convertNodeRef(NodeRef nodeRef) + { + return new NodeRef(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, VersionModel.STORE_ID), nodeRef.getId()); + } } diff --git a/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java b/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java index f7c76bc102..45399e5d35 100644 --- a/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java +++ b/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java @@ -35,7 +35,7 @@ import org.alfresco.service.PublicService; /** * This service interface provides support for content filter languages . * - * @author yanipig + * @author Yannick Pignot * */ @PublicService diff --git a/source/java/org/alfresco/service/cmr/ml/EditionService.java b/source/java/org/alfresco/service/cmr/ml/EditionService.java new file mode 100644 index 0000000000..6378631488 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/ml/EditionService.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.ml; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.QName; + +/** + * The API to manage editions of a mlContainer. An edition is a version of a mlContainer + * + * @since 2.1 + * @author Yannick Pignot + */ +@PublicService +public interface EditionService +{ + /** + * Create a new edition of an existing cm:mlContainer using any one of the + * associated cm:mlDocument transalations. + * + * If startingTranslationNodeRef is multilingual, it will be copied. The copy will become the pivot translation + * of the new Edition of the cm:mlContainer. The reference of the copy will be returned. + * + * @param translationNodeRef The specific cm:mlDocument to use as the starting point + * of the new edition. All other translations will be removed. + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "versionProperties"}) + NodeRef createEdition(NodeRef translationNodeRef, Map versionProperties); + + /** + * Get editions of an existing cm:mlContainer. + * + * @param mlContainer An existing cm:mlContainer + * @return The Version History of the mlContainer + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainer"}) + VersionHistory getEditions(NodeRef mlContainer); + + /** + * Get the different cm:mlDocument transalation version histories of a specific edition of a cm:mlContainer + * + * @param mlContainerEdition An existing version of a mlContainer + * @return The list of cm:mlDocument transalation versions of the edition + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerEdition"}) + List getVersionedTranslations(Version mlContainerEdition); + + /** + * Get the the versioned metadata of a specific cm:mlDocument transalation version or a specific + * cm:mlContainer version + * + * @see org.alfresco.repo.model.ml.MultilingualDocumentAspect.PROPERTIES_TO_VERSION the versioned metadata + * of a cm:mlDocument transalation added to the usual metadata versioned for a normal node. + * + * @see org.alfresco.repo.model.ml.MLContainerType.PROPERTIES_TO_VERSION the versioned metadata + * of a cm:mlContainer added to the usual metadata versioned for a normal node. + * + * @param version An existing version of a cm:mlDocument translation version or + * an existing version of a cm:mlContainer version. + * @return The versioned metadata + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"version"}) + Map getVersionedMetadatas(Version version); + + } diff --git a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java index 68be4dcee3..6a20407d7d 100644 --- a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java +++ b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.service.cmr.ml; @@ -35,7 +35,7 @@ import org.alfresco.service.cmr.repository.NodeRef; /** * The API to manage multilingual content and related structures. - * + * * @author Derek Hulley * @author Philippe Dubois */ @@ -44,19 +44,19 @@ public interface MultilingualContentService { /** * Checks whether an existing document is part of a translation group. - * + * * @param contentNodeRef An existing cm:content * @return Returns true if the document has a cm:mlContainer parent */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"contentNodeRef"}) boolean isTranslation(NodeRef contentNodeRef); - + /** * Make an existing document into a translation by adding the cm:mlDocument aspect and * creating a cm:mlContainer parent. If it is already a translation, then nothing is done. * * @param contentNodeRef An existing cm:content - * + * * @see org.alfresco.model.ContentModel#ASPECT_MULTILINGUAL_DOCUMENT */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"contentNodeRef", "locale"}) @@ -65,12 +65,12 @@ public interface MultilingualContentService /** * Removes the node from any associated translations. If the translation is the * pivot translation, then the entire set of translations will be unhooked. - * + * * @param translationNodeRef an existing cm:mlDocument */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"}) void unmakeTranslation(NodeRef translationNodeRef); - + /** * Make a translation out of an existing document. The necessary translation structures will be created * as necessary. @@ -90,61 +90,51 @@ public interface MultilingualContentService @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"}) NodeRef getTranslationContainer(NodeRef translationNodeRef); - /** - * Create a new edition of an existing cm:mlContainer using any one of the - * associated cm:mlDocument transalations. - * - * @param translationNodeRef The specific cm:mlDocument to use as the starting point - * of the new edition. All other translations will be removed. - */ - @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef"}) - void createEdition(NodeRef translationNodeRef); - /** * Gets the set of sibling translations associated with the given cm:mlDocument or * cm:mlContainer. - * + * * @param translationOfNodeRef An existing cm:mlDocument or cm:mlContainer * @return Returns a map of translation nodes keyed by locale */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationOfNodeRef"}) Map getTranslations(NodeRef translationOfNodeRef); - + /** * Given a cm:mlDocument, this method attempts to find the best translation for the given * locale. If there is not even a * {@link org.alfresco.i18n.I18NUtil#getNearestLocale(Locale, Set) partial match}, then the * {@link #getPivotTranslation(NodeRef) pivot translation} is used. If that also gives no results * then the translation itself is returned. - * + * * @param translationNodeRef the cm:mlDocument * @param locale the target locale * @return Returns the best match for the locale (never null) - * + * * @see #getTranslations(NodeRef) * @see org.alfresco.i18n.I18NUtil#getNearestLocale(Locale, Set) */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "locale"}) NodeRef getTranslationForLocale(NodeRef translationNodeRef, Locale locale); - - + + /** * Given a cm:mlDocument or cm:mlContainer this node returns each locale for - * which there isn't a translation. - * + * which there isn't a translation. + * * @param localizedNodeRef the cm:mlDocument or cm:mlContainer - * @param addThisNodeLocale if true, add the locale of the given cm:mlDocument in the list. + * @param addThisNodeLocale if true, add the locale of the given cm:mlDocument in the list. * @return Returns a list of missng locales */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"localizedNodeRef", "addThisNodeLocale"}) List getMissingTranslations(NodeRef localizedNodeRef, boolean addThisNodeLocale); - + /** * Given any node, this returns the pivot translation. All multilingual documents belong to * a group linked by a hidden parent node of type cm:mlContainer. The pivot language * for the translations is stored on the parent, and the child that has the same locale is the * pivot translation. - * + * * @param nodeRef a cm:mlDocument translation or cm:mlContainer translation * container * @return Returns a corresponding cm:mlDocument that matches the locale of @@ -153,7 +143,7 @@ public interface MultilingualContentService */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"}) NodeRef getPivotTranslation(NodeRef nodeRef); - + /** * Make a empty translation out of an existing pivot translation. The given translation or * container will be used to find the pivot translation. Failing this, the given translation @@ -164,12 +154,40 @@ public interface MultilingualContentService * *

* The necessary translation structures will be created as necessary. - * + * * @param translationOfNodeRef An existing cm:mlDocument * @param name The name of the file to create, or null to use * the default naming convention. - * @return Returns the new created cm:mlEmptyTranslation + * @return Returns the new created cm:mlEmptyTranslation */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationOfNodeRef", "name", "locale"}) NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale); + + /** + * Copies the given cm:mlContainer. + *

+ * This involves the copy of the cm:mlContainer node and the copy of its cm:mlDocument. + *

+ * + * @param translationNodeRef The cm:mlContainer to copy + * @param newParentRef The new parent of the copied cm:mlDocument + * @return The copied cm:mlContainer + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "newParentRef"}) + NodeRef copyTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef); + + /** + * Moves the location of the given cm:mlContainer. + *

+ * This not involves changing the cm:mlContainer node but moves its cm:mlDocument. + *

+ * + * @param translationNodeRef The cm:mlContainer to move + * @param newParentRef The new parent of the moved cm:mlDocument + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "newParentRef"}) + void moveTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef); + + + }