From 81e364ebd3e9f194d8968041d56b5b92182c91ad Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Thu, 2 Aug 2007 23:57:38 +0000 Subject: [PATCH] Merged V2.1 to HEAD 6383: ML contributions 6400: AR-1625 Empty translations track pivot translation git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6406 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../model-specific-services-context.xml | 5 +- config/alfresco/node-services-context.xml | 3 + .../public-services-security-context.xml | 1 + .../repo/model/ml/EditionServiceImpl.java | 3 +- .../ml/MultilingualContentServiceImpl.java | 253 +++++++++++- .../model/ml/MultilingualDocumentAspect.java | 52 +-- .../ml/tools/EditionServiceImplTest.java | 16 +- .../MultilingualContentServiceImplTest.java | 385 +++++++++++++++--- .../tools/MultilingualDocumentAspectTest.java | 113 +++-- .../repo/node/MLPropertyInterceptor.java | 116 +++++- .../service/ServiceDescriptorRegistry.java | 56 ++- .../repo/version/VersionServiceImpl.java | 5 + .../common/VersionLabelComparator.java | 47 +++ .../org/alfresco/service/ServiceRegistry.java | 96 +++-- .../cmr/ml/MultilingualContentService.java | 26 +- 15 files changed, 913 insertions(+), 264 deletions(-) create mode 100644 source/java/org/alfresco/repo/version/common/VersionLabelComparator.java diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 998b150827..c1a5e98e6c 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -68,7 +68,7 @@ - + @@ -82,6 +82,9 @@ + + + diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 34f64c7d1e..d14d9c3a56 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -8,6 +8,9 @@ + + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 6ce5623fe2..a20bb7a19f 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -544,6 +544,7 @@ 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.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.MultilingualContentService.deleteTranslationContainer=ACL_NODE.0.sys:base.DeleteNode,ACL_NODE.0.sys:base.DeleteChildren diff --git a/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java index 94c8afdfbc..02bf2c2fbd 100644 --- a/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java @@ -120,7 +120,7 @@ public class EditionServiceImpl implements EditionService // Version the container and its translations versionService.createVersion(mlContainerToVersion, versionProperties, true); - // 2. Third step: prepare the current edition of the mlContainer + // 2. second step: prepare the current edition of the mlContainer // Get the new starting point node, it will be returned NodeRef startNode; @@ -169,6 +169,7 @@ public class EditionServiceImpl implements EditionService // set the starting translation become the pivot. nodeService.setProperty(mlContainerToVersion, ContentModel.PROP_LOCALE, locale); nodeService.setProperty(mlContainerToVersion, ContentModel.PROP_AUTHOR, author); + nodeService.setProperty(mlContainerToVersion, ContentModel.PROP_NAME, name); // Done if (logger.isDebugEnabled()) diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index e2c0c86dfd..d39d4b004f 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.model.ml; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -31,13 +32,18 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import javax.transaction.SystemException; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; +import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; 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.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; @@ -86,6 +92,8 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic private ContentFilterLanguagesService contentFilterLanguagesService; private FileFolderService fileFolderService; + private BehaviourFilter policyBehaviourFilter; + public MultilingualContentServiceImpl() { searchParametersMLRoot = new SearchParameters(); @@ -284,8 +292,12 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // Make one mlContainerNodeRef = getOrCreateMLContainer(contentNodeRef, true); - // set the pivot language + Serializable containerFunctionalName = nodeService.getProperty(contentNodeRef, ContentModel.PROP_NAME); + + // set the pivot language and the functional name nodeService.setProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE, locale); + nodeService.setProperty(mlContainerNodeRef, ContentModel.PROP_NAME, containerFunctionalName); + } else { @@ -369,7 +381,57 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic } } - /** {@inheritDoc} */ + + /** @inheritDoc */ + public void deleteTranslationContainer(NodeRef mlContainerNodeRef) + { + if(!ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(mlContainerNodeRef))) + { + throw new IllegalArgumentException( + "Node type must be " + ContentModel.TYPE_MULTILINGUAL_CONTAINER); + } + + // get the translations + Map translations = this.getTranslations(mlContainerNodeRef); + + // remember the number of childs + int translationCount = translations.size(); + + // remove the translations + for(NodeRef translationToRemove : translations.values()) + { + // unmake the translation + this.unmakeTranslation(translationToRemove); + + // remove it + if(nodeService.exists(translationToRemove)) + { + nodeService.deleteNode(translationToRemove); + } + } + + // if the mlContainer is not removed with the pivot, + if(nodeService.exists(mlContainerNodeRef)) + { + // force its deletion + nodeService.deleteNode(mlContainerNodeRef); + + if (logger.isWarnEnabled()) + { + logger.warn("The ML container " + mlContainerNodeRef + " was not removed with it's pivot translation in the unmakeTranslation process."); + } + } + + // done + if (logger.isDebugEnabled()) + { + logger.debug("ML container removed: \n" + + " Container: " + mlContainerNodeRef + "\n" + + " Number of translations: " + translationCount); + } + } + + /** @inheritDoc */ public void unmakeTranslation(NodeRef translationNodeRef) { // Get the container @@ -622,7 +684,9 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic public NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale) { boolean hasMLAspect = nodeService.hasAspect(translationOfNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); - if (hasMLAspect) + boolean isMLContainer = nodeService.getType(translationOfNodeRef).equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER); + + if (hasMLAspect || isMLContainer) { // Get the pivot translation NodeRef pivotTranslationNodeRef = getPivotTranslation(translationOfNodeRef); @@ -717,15 +781,177 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return newTranslationNodeRef; } - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; + /** + * @throws SystemException + * @throws Exception + * @throws FileNotFoundException + * @throws FileExistsException + * @inheritDoc + */ + public NodeRef copyTranslationContainer(NodeRef mlContainerNodeRef, NodeRef newParentRef, String prefixName) throws Exception + { + if(!ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(mlContainerNodeRef))) + { + throw new IllegalArgumentException( + "Node type must be " + ContentModel.TYPE_MULTILINGUAL_CONTAINER); + } + + // if the container has no translation: nothing to do + if(nodeService.getChildAssocs(mlContainerNodeRef, ContentModel.ASSOC_MULTILINGUAL_CHILD, RegexQNamePattern.MATCH_ALL).size() < 1) + { + if (logger.isDebugEnabled()) + { + logger.debug("MLContainer has no translation " + mlContainerNodeRef); + } + + return null; + } + + // keep a reference to the containing space before copy + NodeRef spaceBefore = nodeService.getPrimaryParent(getPivotTranslation(mlContainerNodeRef)).getParentRef(); + + if(spaceBefore.equals(newParentRef)) + { + throw new AlfrescoRuntimeException( + "Impossible to copy the mlContainer, source folder is the same as the destination container."); + } + + // get the pivot translation and its locale + NodeRef pivotNodeRef = getPivotTranslation(mlContainerNodeRef); + Locale pivotLocale = (Locale) nodeService.getProperty(pivotNodeRef, ContentModel.PROP_LOCALE); + String pivotName = prefixName + (String) nodeService.getProperty(pivotNodeRef, ContentModel.PROP_NAME); + + if(prefixName == null) + { + prefixName = ""; + } + + NodeRef pivotCopyNodeRef = null; + + pivotCopyNodeRef = fileFolderService.copy(pivotNodeRef, newParentRef, pivotName).getNodeRef(); + + // make the new pivot multilingual + this.makeTranslation(pivotCopyNodeRef, pivotLocale); + + // get a reference to the new mlContainer + NodeRef newMLContainerNodeRef = getMLContainer(pivotCopyNodeRef, false); + + // copy each other translation and make them multilingual too + for(Map.Entry entry : getTranslations(mlContainerNodeRef).entrySet()) + { + Locale translationLocale = entry.getKey(); + NodeRef translationNodeRef = entry.getValue(); + + String name = prefixName + (String) nodeService.getProperty(translationNodeRef, ContentModel.PROP_NAME); + + if(!translationNodeRef.equals(pivotNodeRef)) + { + if(nodeService.hasAspect(translationNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + // Turn off any empty translation policy behaviours to enabled the copy. + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + + try + { + // copy the translation + NodeRef copyNodeRef = fileFolderService.copy(translationNodeRef, newParentRef, name).getNodeRef(); + + // Add it to the newMLContainer + nodeService.addChild( + newMLContainerNodeRef, + copyNodeRef, + ContentModel.ASSOC_MULTILINGUAL_CHILD, + QNAME_ML_TRANSLATION); + } + finally + { + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + } + else + { + // copy the translation + NodeRef copyNodeRef = fileFolderService.copy(translationNodeRef, newParentRef, name).getNodeRef(); + + // add it to the mlContainer + this.addTranslation(copyNodeRef, newMLContainerNodeRef, translationLocale); + // set its locale property + nodeService.setProperty(copyNodeRef, ContentModel.PROP_LOCALE, translationLocale); + } + } + else + { + // the pivot is already created + } + } + + if (logger.isDebugEnabled()) + { + logger.debug("MLContainer copied: \n" + + " Copy of : " + mlContainerNodeRef + "(translations located in " + spaceBefore + ") \n" + + " Copy : " + newMLContainerNodeRef + "(translations located in " + newParentRef + ") \n"); + } + + return newMLContainerNodeRef; + } + + /** + * @inheritDoc + */ + public void moveTranslationContainer(NodeRef mlContainerNodeRef, NodeRef newParentRef) throws FileExistsException, FileNotFoundException + { + if(!ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(mlContainerNodeRef))) + { + throw new IllegalArgumentException( + "Node type must be " + ContentModel.TYPE_MULTILINGUAL_CONTAINER); + } + + // if the container has no translation: nothing to do + if(nodeService.getChildAssocs(mlContainerNodeRef, ContentModel.ASSOC_MULTILINGUAL_CHILD, RegexQNamePattern.MATCH_ALL).size() < 1) + { + if (logger.isDebugEnabled()) + { + logger.debug("MLContainer has no translation " + mlContainerNodeRef); + } + + return; + } + + // keep a reference to the containing space before moving + NodeRef spaceBefore = nodeService.getPrimaryParent(getPivotTranslation(mlContainerNodeRef)).getParentRef(); + + if(spaceBefore.equals(newParentRef)) + { + // nothing to do + return; + } + + // move each translation + for(NodeRef translationToMove : getTranslations(mlContainerNodeRef).values()) + { + fileFolderService.move(translationToMove, newParentRef, null); + } + + if (logger.isDebugEnabled()) + { + logger.debug("MLContainer moved: \n" + + " Old location of " + mlContainerNodeRef + " : " + spaceBefore + ") \n" + + " New location of " + mlContainerNodeRef + " : " + newParentRef + ")"); + } } - public void setSearchService(SearchService searchService) - { + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSearchService(SearchService searchService) + { this.searchService = searchService; - } + } public void setPermissionService(PermissionService permissionService) { @@ -742,13 +968,8 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic this.fileFolderService = fileFolderService; } - public NodeRef copyTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef) + public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter) { - 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"); + this.policyBehaviourFilter = policyBehaviourFilter; } } \ 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 3762fcbe1d..45b8b2d28f 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java @@ -35,7 +35,6 @@ 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; @@ -54,22 +53,10 @@ import org.alfresco.service.namespace.RegexQNamePattern; */ public class MultilingualDocumentAspect implements CopyServicePolicies.OnCopyNodePolicy, - CopyServicePolicies.OnCopyCompletePolicy, NodeServicePolicies.BeforeDeleteNodePolicy, - NodeServicePolicies.OnUpdatePropertiesPolicy, - VersionServicePolicies.OnCreateVersionPolicy + NodeServicePolicies.OnUpdatePropertiesPolicy { - /** - * 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; @@ -88,11 +75,6 @@ public class MultilingualDocumentAspect implements ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, new JavaBehaviour(this, "onCopyNode")); - this.policyComponent.bindClassBehaviour( - 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, @@ -103,11 +85,6 @@ public class MultilingualDocumentAspect implements 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")); - } /** @@ -145,16 +122,6 @@ public class MultilingualDocumentAspect implements copyDetails.removeAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); } - /** - * 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) - { - 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. @@ -191,10 +158,10 @@ public class MultilingualDocumentAspect implements // the after local property type can be either Locale or String Serializable objLocaleAfter = after.get(ContentModel.PROP_LOCALE); + if (objLocaleAfter instanceof Locale ) { localeAfter = (Locale) objLocaleAfter; - } else { @@ -242,19 +209,4 @@ public class MultilingualDocumentAspect implements // 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)); - } - } - } diff --git a/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java b/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java index 19beae7053..a8cef3f691 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java @@ -28,7 +28,6 @@ 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; @@ -36,6 +35,7 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.version.VersionModel; +import org.alfresco.repo.version.common.VersionLabelComparator; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; @@ -210,24 +210,12 @@ public class EditionServiceImplTest extends AbstractMultilingualTestCases 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); + Collections.sort(versionsAsList, new VersionLabelComparator()); return versionsAsList; } } 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 ea65fd55c4..a4893fc48d 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java @@ -15,17 +15,20 @@ * 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; +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,13 +42,13 @@ import org.alfresco.service.cmr.security.PermissionService; /** * @see org.alfresco.repo.ml.MultilingualContentServiceImpl - * + * * @author Derek Hulley * @author Philippe Dubois */ public class MultilingualContentServiceImplTest extends AbstractMultilingualTestCases { - + public void testMakeTranslation() throws Exception { NodeRef chineseContentNodeRef = createContent(); @@ -57,7 +60,7 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest // Check the container child count assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size()); } - + public void testAddTranslationUsingContent() throws Exception { // Make a container with a single translation @@ -77,58 +80,58 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest // Check the container child count assertEquals("Incorrect number of child nodes", 2, nodeService.getChildAssocs(mlContainerNodeRef).size()); } - - @SuppressWarnings("unused") + + @SuppressWarnings("unused") public void testGetMissingTranslation() throws Exception { List langList = contentFilterLanguagesService.getFilterLanguages(); int langListSize = langList.size(); - + // make sure that it exists at least tree language filter assertFalse("The testGetMissingTranslation test case needs at least three language", langListSize < 3); - - // get the first tree locale of the content filter language list + + // get the first tree locale of the content filter language list Locale loc1 = I18NUtil.parseLocale(langList.get(0)); Locale loc2 = I18NUtil.parseLocale(langList.get(1)); Locale loc3 = I18NUtil.parseLocale(langList.get(2)); - - // create three content + + // create three content NodeRef nodeRef1 = createContent(); NodeRef nodeRef2 = createContent(); NodeRef nodeRef3 = createContent(); - + multilingualContentService.makeTranslation(nodeRef1, loc1); - - List missing = multilingualContentService.getMissingTranslations(nodeRef1, false); - - // make sure that the missing language list size is correct + + List missing = multilingualContentService.getMissingTranslations(nodeRef1, false); + + // make sure that the missing language list size is correct assertFalse("Missing Translation Size false. " + "Real size : " + missing.size() + ". Normal Size " + (langListSize - 1), missing.size() != (langListSize - 1)); - + // make sure that the missing language list is correct assertFalse("Missing Translation List false. Locale " + loc1 + " found", missing.contains(loc1.toString())); - + multilingualContentService.addTranslation(nodeRef2, nodeRef1, loc2); multilingualContentService.addTranslation(nodeRef3, nodeRef1, loc3); - + // Add the missing translations in missing = multilingualContentService.getMissingTranslations(nodeRef1, false); - - // Make sure that the missing language list size is correct + + // Make sure that the missing language list size is correct assertFalse("Missing Translation Size false. " + "Real size : " + missing.size() + ". Normal Size " + (langListSize - 3), missing.size() != (langListSize - 3)); - + // make sure that the missing language list is correct assertFalse("Missing Translation List false. Locale " + loc2 + " or " + loc3 + " found", missing.contains(loc2.toString()) || missing.contains(loc3.toString())); } - + public void testGetTranslationForLocale() throws Exception { NodeRef chineseContentNodeRef = createContent(); multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); - NodeRef frenchContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); - + // Get the chinese translation assertEquals("Chinese translation should be present", chineseContentNodeRef, @@ -142,61 +145,61 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest chineseContentNodeRef, multilingualContentService.getTranslationForLocale(chineseContentNodeRef, Locale.ITALIAN)); } - - @SuppressWarnings("unused") + + @SuppressWarnings("unused") public void testGetPivotTranslation() throws Exception { NodeRef chineseContentNodeRef = createContent(); multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); - + // make sure that the pivot language is set assertNotNull("Pivot language not set", nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE)); - + // make sure that the pivot language is correctly set - assertEquals("Pivot language not correctly set", Locale.CHINESE, nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE)); - - NodeRef frenchContentNodeRef = createContent(); + assertEquals("Pivot language not correctly set", Locale.CHINESE, nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE)); + + NodeRef frenchContentNodeRef = createContent(); multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); - + // make sure that the pivot noderef is correct - assertEquals("Unable to get pivot from container", chineseContentNodeRef, multilingualContentService.getPivotTranslation(mlContainerNodeRef)); - assertEquals("Unable to get pivot from translation", chineseContentNodeRef, multilingualContentService.getPivotTranslation(frenchContentNodeRef)); - - // modify the pivot language + assertEquals("Unable to get pivot from container", chineseContentNodeRef, multilingualContentService.getPivotTranslation(mlContainerNodeRef)); + assertEquals("Unable to get pivot from translation", chineseContentNodeRef, multilingualContentService.getPivotTranslation(frenchContentNodeRef)); + + // modify the pivot language nodeService.setProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE, Locale.FRENCH); - + // make sure that the modified pivot noderef is correct assertEquals("Pivot node ref not correct", frenchContentNodeRef, multilingualContentService.getPivotTranslation(mlContainerNodeRef)); } - - @SuppressWarnings("unused") + + @SuppressWarnings("unused") public void testCreateEmptyTranslation() throws Exception { NodeRef chineseContentNodeRef = createContent("Document.txt"); multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); - + // This should use the pivot language NodeRef emptyNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, "Document.txt", Locale.CANADA); - + // Ensure that the empty translation is not null assertNotNull("The creation of the empty document failed ", emptyNodeRef); // Ensure that the empty translation has the mlDocument aspect assertTrue("The empty document must have the mlDocument aspect", nodeService.hasAspect(emptyNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); - // Ensure that the empty translation has the mlEmptyTranslation aspect + // Ensure that the empty translation has the mlEmptyTranslation aspect assertTrue("The empty document must have the mlEmptyTranslation aspect", nodeService.hasAspect(emptyNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); // Check that the auto renaming worked String emptyName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(emptyNodeRef, ContentModel.PROP_NAME)); assertEquals("Empty auto-rename didn't work for same-named document", "Document_en_CA.txt", emptyName); - + // Check that the content is identical ContentData chineseContentData = fileFolderService.getReader(chineseContentNodeRef).getContentData(); ContentData emptyContentData = fileFolderService.getReader(emptyNodeRef).getContentData(); } - + public void testCreateEmptyTranslationNames() throws Exception { NodeRef chineseContentNodeRef = createContent("Document.txt"); @@ -225,7 +228,7 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest String differentName = fileFolderService.getFileInfo(differentNameNodeRef).getName(); assertEquals("Empty translation name not generated correctly.", "Document2.txt", differentName); } - + public void testGetTranslationContainerPermissions() throws Exception { // Grant the guest user rights to our working folder @@ -252,7 +255,7 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest try { authenticationComponent.setCurrentAuthentication(authentication); } catch (Throwable e) {} } } - + /** * Check whether non-admin users can take part in ML document manipulation */ @@ -284,4 +287,284 @@ public class MultilingualContentServiceImplTest extends AbstractMultilingualTest try { authenticationComponent.setCurrentAuthentication(authentication); } catch (Throwable e) {} } } + + public void testDeleteMultilingualContent() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + NodeRef emptyGermanContentNodeRef = null; + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + emptyGermanContentNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.GERMAN); + + // the mlContainer to remove + NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); + + // Ensure that the the mlContainer is correctly created + assertEquals("Incorrect number of translations", 4, multilingualContentService.getTranslations(mlContainerNodeRef).size()); + + // remove the mlContainer + multilingualContentService.deleteTranslationContainer(mlContainerNodeRef); + + // get the archived node ref + NodeRef archivedChineseContentNodeRef = nodeArchiveService.getArchivedNode(chineseContentNodeRef); + NodeRef archivedFrenchContentNodeRef = nodeArchiveService.getArchivedNode(frenchContentNodeRef); + NodeRef archivedJapaneseContentNodeRef = nodeArchiveService.getArchivedNode(japaneseContentNodeRef); + NodeRef archivedEmptyGermanContentNodeRef = nodeArchiveService.getArchivedNode(emptyGermanContentNodeRef); + NodeRef archivedMlContainerNodeRef = nodeArchiveService.getArchivedNode(mlContainerNodeRef); + + // Ensure that the mlContainer is removed + assertFalse("The multilingual container must be removed", nodeService.exists(mlContainerNodeRef)); + // Ensure that the mlContainer IS NOT archived + assertFalse("The multilingual container can't be archived", nodeService.exists(archivedMlContainerNodeRef)); + // Ensure that the translations are removed + assertFalse("The translation must be removed: " + Locale.CHINESE, nodeService.exists(chineseContentNodeRef)); + assertFalse("The translation must be removed: " + Locale.JAPANESE, nodeService.exists(japaneseContentNodeRef)); + assertFalse("The translation must be removed: " + Locale.FRENCH, nodeService.exists(frenchContentNodeRef)); + assertFalse("The empty translation must be removed: " + Locale.GERMAN, nodeService.exists(emptyGermanContentNodeRef)); + + // Ensure that the translations ARE archived + assertTrue("The translation must be archived: " + Locale.CHINESE, nodeService.exists(archivedChineseContentNodeRef)); + assertTrue("The translation must be archived: " + Locale.JAPANESE, nodeService.exists(archivedJapaneseContentNodeRef)); + assertTrue("The translation must be archived: " + Locale.FRENCH, nodeService.exists(archivedFrenchContentNodeRef)); + + // Ensure that the empty translation IS NOT archived + assertFalse("The empty document can't be archived: " + Locale.GERMAN, nodeService.exists(archivedEmptyGermanContentNodeRef)); + + // Ensure that the mlDocument aspect is removed + assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect must be removed for " + Locale.CHINESE, nodeService.hasAspect(archivedChineseContentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect must be removed for " + Locale.JAPANESE, nodeService.hasAspect(archivedJapaneseContentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect must be removed for " + Locale.FRENCH, nodeService.hasAspect(archivedFrenchContentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + + } + + @SuppressWarnings("unused") + public void testCopyMLContainerInNewSpace() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + NodeRef emptyGermanContentNodeRef = null; + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + emptyGermanContentNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.GERMAN); + + // the mlContainer to copy + NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); + + // Ensure that the the mlContainer is correctly created + assertEquals("Incorrect number of translations", 4, multilingualContentService.getTranslations(mlContainerNodeRef).size()); + + // get the actual space + NodeRef actualSpace = folderNodeRef; + // create a new space + NodeRef destinationSpace = fileFolderService.create(folderNodeRef, "testCopyMLContainerInNewSpace" + System.currentTimeMillis(), ContentModel.TYPE_FOLDER).getNodeRef(); + + // Ensure that the new space is created + assertTrue("The destiation space is not created " + destinationSpace, nodeService.exists(destinationSpace)); + + // copy the mlContainer + NodeRef newMLContainer = multilingualContentService.copyTranslationContainer(mlContainerNodeRef, destinationSpace, ""); + + assertEquals("Incorrect number of translations for the new mlContainer", 4, multilingualContentService.getTranslations(newMLContainer).size()); + + // Ensure that a new mlContainer is created + assertTrue("The new mlContainer is not created ", nodeService.exists(newMLContainer)); + // Ensure that the newMLContainer is a copy of the source mlContainer + assertFalse("The newMLContainer is not a copy of the source mlContainer, the ref is the same " + newMLContainer , newMLContainer.equals(mlContainerNodeRef)); + assertEquals("The newMLContainer is not a copy of the source mlContainer, the locales are not the same " + newMLContainer , + nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE), + nodeService.getProperty(newMLContainer, ContentModel.PROP_LOCALE)); + assertEquals("The newMLContainer is not a copy of the source mlContainer, the authors are not the same " + newMLContainer , + nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_AUTHOR), + nodeService.getProperty(newMLContainer, ContentModel.PROP_AUTHOR)); + + // get the source translations + Map sourceTranslations = multilingualContentService.getTranslations(mlContainerNodeRef); + // get the copies + Map copyTranslations = multilingualContentService.getTranslations(newMLContainer); + + // Ensure that the translations are copies from the source translations + assertEquals("They are not the same number of translation in the source mlContainer and in its copy", sourceTranslations.size(), copyTranslations.size()); + + for(Map.Entry entry : sourceTranslations.entrySet()) + { + Locale locale = entry.getKey(); + + NodeRef sourceNodeRef = entry.getValue(); + NodeRef sourceParent = nodeService.getPrimaryParent(sourceNodeRef).getParentRef(); + + NodeRef copyTranslation = multilingualContentService.getTranslationForLocale(newMLContainer, locale); + NodeRef copyParent = nodeService.getPrimaryParent(copyTranslation).getParentRef(); + + // Ensure that the copy exists + assertNotNull("No copy found for the locale " + locale, copyTranslation); + assertTrue("No copy exists for the locale " + locale, nodeService.exists(copyTranslation)); + + // Ensure that the copy has the mlDocument aspect + assertTrue("The copy must have the mlDocument aspect", + nodeService.hasAspect(copyTranslation, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + + // Ensure that the copy is an empty translation if the source too + assertEquals("The call of nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION) must return the same result for the source and the copy", + nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION), + nodeService.hasAspect(copyTranslation, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); + + + // Ensure that the copy and the source are different + assertNotSame("The copy has the same ref as the source", sourceNodeRef, copyTranslation); + + // Ensure that the parent of the source is correct + assertEquals("The source would not be moved", sourceParent, actualSpace); + // Ensure that the parent of the copy is correct + assertEquals("The copy is not in the right space", copyParent, destinationSpace); + } + } + + @SuppressWarnings("unused") + public void testCopyMLContainerInSameSpace() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + NodeRef emptyGermanContentNodeRef = null; + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + emptyGermanContentNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.GERMAN); + + // the mlContainer to copy + NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); + + // Ensure that the the mlContainer is correctly created + assertEquals("Incorrect number of translations", 4, multilingualContentService.getTranslations(mlContainerNodeRef).size()); + + // get the actual space + NodeRef actualSpace = folderNodeRef; + + try + { + // copy the mlContainer + NodeRef newMLContainer = multilingualContentService.copyTranslationContainer(mlContainerNodeRef, actualSpace, ""); + + fail("The copy of the mlContainer in the same space would faile"); + } + catch(Exception e) + { + // test asserted + } + } + + @SuppressWarnings("unused") + public void testCopyAndRenameMLContainer() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + NodeRef emptyGermanContentNodeRef = null; + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + emptyGermanContentNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.GERMAN); + + // the mlContainer to copy + NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); + + // Ensure that the the mlContainer is correctly created + assertEquals("Incorrect number of translations", 4, multilingualContentService.getTranslations(mlContainerNodeRef).size()); + + // get the actual space + NodeRef actualSpace = folderNodeRef; + + // create a new space + NodeRef destinationSpace = fileFolderService.create(folderNodeRef, "testCopyMLContainerInNewSpace" + System.currentTimeMillis(), ContentModel.TYPE_FOLDER).getNodeRef(); + + // Ensure that the new space is created + assertTrue("The destiation space is not created " + destinationSpace, nodeService.exists(destinationSpace)); + + String PREFIX = "COPY OF " ; + + NodeRef newMLContainer = multilingualContentService.copyTranslationContainer(mlContainerNodeRef, destinationSpace, PREFIX); + + // Ensure that a new mlContainer is created + assertTrue("The new mlContainer is not created ", nodeService.exists(newMLContainer)); + // Ensure that the newMLContainer is a copy of the source mlContainer + assertFalse("The newMLContainer is not a copy of the source mlContainer, the ref is the same " + newMLContainer , newMLContainer.equals(mlContainerNodeRef)); + + // get the source translations + Map sourceTranslations = multilingualContentService.getTranslations(mlContainerNodeRef); + // get the copies + Map copyTranslations = multilingualContentService.getTranslations(newMLContainer); + + // Ensure that the translations are copies from the source translations + assertEquals("They are not the same number of translation in the source mlContainer and in its copy", sourceTranslations.size(), copyTranslations.size()); + + for(Map.Entry entry : sourceTranslations.entrySet()) + { + Locale locale = entry.getKey(); + + NodeRef sourceNodeRef = entry.getValue(); + NodeRef copyNodeRef = multilingualContentService.getTranslationForLocale(newMLContainer, locale); + + String sourceName = (String) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_NAME); + String copyName = (String) nodeService.getProperty(copyNodeRef, ContentModel.PROP_NAME); + + String theoricalCopyName = PREFIX + sourceName; + + // Ensure that the name of the copy is correct + assertTrue("The name of the copied translation is incorect: " + copyName + " and should be " + theoricalCopyName, theoricalCopyName.equals(copyName)); + } + + } + + @SuppressWarnings("unused") + public void testMoveMLContainer() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + NodeRef emptyGermanContentNodeRef = null; + + multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, chineseContentNodeRef, Locale.JAPANESE); + emptyGermanContentNodeRef = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, null, Locale.GERMAN); + + // the mlContainer to copy + NodeRef mlContainerNodeRef = multilingualContentService.getTranslationContainer(chineseContentNodeRef); + + // Ensure that the the mlContainer is correctly created + assertEquals("Incorrect number of translations", 4, multilingualContentService.getTranslations(mlContainerNodeRef).size()); + + // get the actual space + NodeRef actualSpace = folderNodeRef; + // create a new space + NodeRef destinationSpace = fileFolderService.create(folderNodeRef, "testCopyMLContainerInNewSpace", ContentModel.TYPE_FOLDER).getNodeRef(); + + // Ensure that the new space is created + assertTrue("The destiation space is not created " + destinationSpace, nodeService.exists(destinationSpace)); + + // move the mlContainer + multilingualContentService.moveTranslationContainer(mlContainerNodeRef, destinationSpace); + + // Esure that the nodes are moved + assertEquals("The node should be moved", destinationSpace, nodeService.getPrimaryParent(chineseContentNodeRef).getParentRef()); + assertEquals("The node should be moved", destinationSpace, nodeService.getPrimaryParent(frenchContentNodeRef).getParentRef()); + assertEquals("The node should be moved", destinationSpace, nodeService.getPrimaryParent(japaneseContentNodeRef).getParentRef()); + assertEquals("The node should be moved", destinationSpace, nodeService.getPrimaryParent(emptyGermanContentNodeRef).getParentRef()); + + // Ensure the mlContainer is not changed + assertEquals("The mlContainer should not be changed", mlContainerNodeRef, multilingualContentService.getTranslationContainer(chineseContentNodeRef)); + assertEquals("The mlContainer should not be changed", mlContainerNodeRef, multilingualContentService.getTranslationContainer(frenchContentNodeRef)); + assertEquals("The mlContainer should not be changed", mlContainerNodeRef, multilingualContentService.getTranslationContainer(japaneseContentNodeRef)); + assertEquals("The mlContainer should not be changed", mlContainerNodeRef, multilingualContentService.getTranslationContainer(emptyGermanContentNodeRef)); + + } } 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 132541d0e4..3822be4b09 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.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; @@ -34,62 +34,59 @@ import org.alfresco.service.namespace.QName; /** * Multilingual document aspect test cases - * + * * @see org.alfresco.service.cmr.ml.MultilingualDocumentAspect - * + * * @author Yannick Pignot */ -public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCases +public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCases { public void testCopy() throws Exception { NodeRef original = createContent(); multilingualContentService.makeTranslation(original, Locale.FRENCH); NodeRef mlContainer = multilingualContentService.getTranslationContainer(original); - - NodeRef copy = + + NodeRef copy = fileFolderService.copy(original, nodeService.getPrimaryParent(original).getParentRef(), "COPY" + System.currentTimeMillis()).getNodeRef(); // Ensure that the copy removes the mlDocument aspect assertFalse("The copy of a mlDocument can't have the multilingual aspect", nodeService.hasAspect(copy, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); - + // Ensure that the copy removes the association between the mlConatiner and the new node assertEquals("The copy of a mlDocument can't be a children of the mlContainer", 1, multilingualContentService.getTranslations(mlContainer).size()); - - // Ensure that the copy removes the Locale property of the new node - assertNull("The copy of a mlDocument can't keep the locale property", nodeService.getProperty(copy, ContentModel.PROP_LOCALE)); } - + public void testDeleteNode() throws Exception { NodeRef trad1 = createContent(); NodeRef trad2 = createContent(); NodeRef trad3 = createContent(); - + NodeRef parent = nodeService.getPrimaryParent(trad1).getParentRef(); - + multilingualContentService.makeTranslation(trad1, Locale.FRENCH); multilingualContentService.addTranslation(trad2, trad1, Locale.GERMAN); multilingualContentService.addTranslation(trad3, trad1, Locale.ITALIAN); - + nodeService.deleteNode(trad3); // Ensure that the deleted node is romoved from its space - assertEquals("The deleted node must be removed to the space", 2, nodeService.getChildAssocs(parent).size()); - // Ensure that the mlContainer doesn't keep an association to the deleted node + assertEquals("The deleted node must be removed to the space", 2, nodeService.getChildAssocs(parent).size()); + // Ensure that the mlContainer doesn't keep an association to the deleted node assertEquals("The deleted node must be removed to the child associations of the mlContainer", 2, multilingualContentService.getTranslations(trad1).size()); // retore the deleted node NodeRef restoredNode = nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(trad3)).getRestoredNodeRef(); - // Ensure that the restored node is restored to it s original space - assertEquals("The restored node must be restaured to the the space", 3, nodeService.getChildAssocs(parent).size()); - // Ensure that the restored node is not linked to the mlContainer + // Ensure that the restored node is restored to it s original space + assertEquals("The restored node must be restaured to the the space", 3, nodeService.getChildAssocs(parent).size()); + // Ensure that the restored node is not linked to the mlContainer assertEquals("The restored node would not be restaured to the mlContainer", 2, multilingualContentService.getTranslations(trad1).size()); // Ensure that the restored node doesn't keep the mlDocument aspect assertFalse("The restored node can't keep the multilingual aspect", nodeService.hasAspect(restoredNode, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); } - + public void testDeletePivot() throws Exception { NodeRef pivot = createContent(); @@ -97,11 +94,11 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase multilingualContentService.makeTranslation(pivot, Locale.FRENCH); NodeRef mlContainer = multilingualContentService.getTranslationContainer(pivot); multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); - + //nodeService.deleteNode(trans1); nodeService.deleteNode(pivot); - // Ensure that pivot is removed + // Ensure that pivot is removed assertFalse("The pivot would be removed", nodeService.exists(pivot)); // Ensure that the mlContainer is removed assertFalse("The mlContainer must be removed if the pivot is removed", nodeService.exists(mlContainer)); @@ -110,25 +107,25 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase // Ensure that trans1 has no mlDocument aspect assertFalse("The last translation can't keep the multilingual aspect", nodeService.hasAspect(trans1, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); } - + public void testDeleteLastNode() throws Exception { NodeRef pivot = createContent(); multilingualContentService.makeTranslation(pivot, Locale.FRENCH); NodeRef mlContainer = multilingualContentService.getTranslationContainer(pivot); - + nodeService.deleteNode(pivot); - + // Ensure that the mlContainer is removed too assertFalse("The mlContainer must be removed if the last translation is removed", nodeService.exists(mlContainer)); - + } - + public void testRemoveAspect() throws Exception { // entierly covered by the delete tests } - + public void testUpdateLocale() throws Exception { NodeRef pivot = createContent(); @@ -136,12 +133,12 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase multilingualContentService.makeTranslation(pivot, Locale.FRENCH); NodeRef mlContainer = multilingualContentService.getTranslationContainer(pivot); multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); - + // modify the locale for the translation Map props = nodeService.getProperties(trans1); props.put(ContentModel.PROP_LOCALE, Locale.GERMAN); nodeService.setProperties(trans1, props); - + // Ensure that the pivot reference is not changed for the mlContainer and the locale is changed for the translation assertEquals("The locale for the pivot would be changed ",Locale.GERMAN, nodeService.getProperty(trans1, ContentModel.PROP_LOCALE)); assertEquals("The pivot reference would not be changed in the mlContainer", Locale.FRENCH, nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); @@ -150,68 +147,68 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase props = nodeService.getProperties(pivot); props.put(ContentModel.PROP_LOCALE, Locale.US); nodeService.setProperties(pivot, props); - + // Ensure that the pivot reference is changed (in the pivot and in the mlContainer) assertEquals("The locale for the pivot would be changed ", Locale.US, nodeService.getProperty(pivot, ContentModel.PROP_LOCALE)); assertEquals("The pivot reference would be changes in the mlContainer", Locale.US, nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); } - + public void testUpdateRedundantLocale() throws Exception { NodeRef pivot = createContent(); NodeRef trans1 = createContent(); NodeRef trans2 = createContent(); - + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); multilingualContentService.addTranslation(trans2, pivot, Locale.JAPANESE); - + // 1. Try with redundant locale - + // modify the locale for the translation 2 Map props = nodeService.getProperties(trans2); props.put(ContentModel.PROP_LOCALE, Locale.KOREAN); - + boolean exceptionCatched = false; - - try + + try { nodeService.setProperties(trans2, props); - // test failed - } catch (Exception ignore) + // test failed + } catch (Exception ignore) { exceptionCatched = true; } - + // Ensure that the the exception was catched. assertTrue("The modification of this locale must catch an exception because it is already in use in another translation", exceptionCatched); // Ensure that the locale of the trans2 is unchanged - assertEquals("The locale must not be changed", - Locale.JAPANESE, + assertEquals("The locale must not be changed", + Locale.JAPANESE, (Locale) nodeService.getProperty(trans2, ContentModel.PROP_LOCALE)); - + // 2. Try with a non-redundant locale - + props = nodeService.getProperties(trans2); props.put(ContentModel.PROP_LOCALE, Locale.ITALIAN); - + exceptionCatched = false; - - try + + try { nodeService.setProperties(trans2, props); - } catch (Exception ignore) + } catch (Exception ignore) { - // test failed + // test failed exceptionCatched = true; } - + // Ensure that the exception was not catched assertFalse("The modification of the locale would not throws an exception", exceptionCatched); - // Ensure that the locale is modified - assertEquals("The locale must be changed", - Locale.ITALIAN, + // Ensure that the locale is modified + assertEquals("The locale must be changed", + Locale.ITALIAN, (Locale) nodeService.getProperty(trans2, ContentModel.PROP_LOCALE)); } } diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index 2c529980e7..0fe9151467 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -25,19 +25,24 @@ package org.alfresco.repo.node; import java.io.Serializable; +import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; @@ -71,6 +76,8 @@ public class MLPropertyInterceptor implements MethodInterceptor /** Direct access to the NodeService */ private NodeService nodeService; + /** Direct access to the MultilingualContentService */ + private MultilingualContentService multilingualContentService; /** Used to access property definitions */ private DictionaryService dictionaryService; @@ -108,7 +115,12 @@ public class MLPropertyInterceptor implements MethodInterceptor this.nodeService = bean; } - public void setDictionaryService(DictionaryService dictionaryService) + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } @@ -142,13 +154,19 @@ public class MLPropertyInterceptor implements MethodInterceptor NodeRef nodeRef = (NodeRef) args[0]; QName propertyQName = (QName) args[1]; + // Get the pivot translation, if appropriate + NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); + Serializable value = (Serializable) invocation.proceed(); - ret = convertOutboundProperty(contentLocale, nodeRef, propertyQName, value); + ret = convertOutboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, value); } else if (methodName.equals("getProperties")) { NodeRef nodeRef = (NodeRef) args[0]; + // Get the pivot translation, if appropriate + NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); + Map properties = (Map) invocation.proceed(); Map convertedProperties = new HashMap(properties.size() * 2); // Check each return value type @@ -156,7 +174,7 @@ public class MLPropertyInterceptor implements MethodInterceptor { QName propertyQName = entry.getKey(); Serializable value = entry.getValue(); - Serializable convertedValue = convertOutboundProperty(contentLocale, nodeRef, propertyQName, value); + Serializable convertedValue = convertOutboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, value); // Add it to the return map convertedProperties.put(propertyQName, convertedValue); } @@ -173,6 +191,10 @@ public class MLPropertyInterceptor implements MethodInterceptor else if (methodName.equals("setProperties")) { NodeRef nodeRef = (NodeRef) args[0]; + + // Get the pivot translation, if appropriate + NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); + Map newProperties =(Map) args[1]; // Get the current properties for the node Map currentProperties = nodeService.getProperties(nodeRef); @@ -185,7 +207,7 @@ public class MLPropertyInterceptor implements MethodInterceptor // Get the current property value Serializable currentValue = currentProperties.get(propertyQName); // Convert the inbound property value - inboundValue = convertInboundProperty(contentLocale, nodeRef, propertyQName, inboundValue, currentValue); + inboundValue = convertInboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, inboundValue, currentValue); // Put the value into the map convertedProperties.put(propertyQName, inboundValue); } @@ -199,8 +221,11 @@ public class MLPropertyInterceptor implements MethodInterceptor QName propertyQName = (QName) args[1]; Serializable inboundValue = (Serializable) args[2]; + // Get the pivot translation, if appropriate + NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); + // Convert the property - inboundValue = convertInboundProperty(contentLocale, nodeRef, propertyQName, inboundValue, null); + inboundValue = convertInboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, inboundValue, null); // Pass this through to the node service nodeService.setProperty(nodeRef, propertyQName, inboundValue); @@ -214,23 +239,73 @@ public class MLPropertyInterceptor implements MethodInterceptor return ret; } + /** + * @param nodeRef + * a potential empty translation + * @return + * the pivot translation node or null + */ + private NodeRef getPivotNodeRef(NodeRef nodeRef) + { + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + return multilingualContentService.getPivotTranslation(nodeRef); + } + else + { + return null; + } + } + /** * Ensure that content is spoofed for empty translations. */ private Serializable convertOutboundProperty( Locale contentLocale, NodeRef nodeRef, + NodeRef pivotNodeRef, QName propertyQName, Serializable outboundValue) { Serializable ret = null; - // Is it content? - if (outboundValue != null && outboundValue instanceof MLText) + if (outboundValue == null) + { + ret = null; + } + if (outboundValue instanceof MLText) { // It is MLText MLText mlText = (MLText) outboundValue; ret = mlText.getClosestValue(contentLocale); } + else if (pivotNodeRef != null) // It is an empty translation + { + if (propertyQName.equals(ContentModel.PROP_MODIFIED)) + { + // An empty translation's modified date must be the later of its own + // modified date and the pivot translation's modified date + Date emptyLastModified = (Date) outboundValue; + Date pivotLastModified = (Date) nodeService.getProperty(pivotNodeRef, ContentModel.PROP_MODIFIED); + if (emptyLastModified.compareTo(pivotLastModified) < 0) + { + ret = pivotLastModified; + } + else + { + ret = emptyLastModified; + } + } + else if (propertyQName.equals(ContentModel.PROP_CONTENT)) + { + // An empty translation's cm:content must track the cm:content of the + // pivot translation. + ret = nodeService.getProperty(pivotNodeRef, ContentModel.PROP_CONTENT); + } + else + { + ret = outboundValue; + } + } else { ret = outboundValue; @@ -257,6 +332,7 @@ public class MLPropertyInterceptor implements MethodInterceptor private Serializable convertInboundProperty( Locale contentLocale, NodeRef nodeRef, + NodeRef pivotNodeRef, QName propertyQName, Serializable inboundValue, Serializable currentValue) @@ -264,7 +340,11 @@ public class MLPropertyInterceptor implements MethodInterceptor Serializable ret = null; PropertyDefinition propertyDef = this.dictionaryService.getProperty(propertyQName); //if no type definition associated to the name then just proceed - if (propertyDef != null && propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) + if (propertyDef == null) + { + ret = inboundValue; + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) { // Don't mess with multivalued properties or instances already of type MLText if (propertyDef.isMultiValued() || (inboundValue instanceof MLText) ) @@ -293,7 +373,25 @@ public class MLPropertyInterceptor implements MethodInterceptor ret = returnMLValue; } } - else // It is not defined as d:mltext in the dictionary + else if (pivotNodeRef != null && propertyQName.equals(ContentModel.PROP_CONTENT)) + { + // It is an empty translation. The content must not change if it matches + // the content of the pivot translation + ContentData pivotContentData = (ContentData) nodeService.getProperty(pivotNodeRef, ContentModel.PROP_CONTENT); + ContentData emptyContentData = (ContentData) inboundValue; + String pivotContentUrl = pivotContentData == null ? null : pivotContentData.getContentUrl(); + String emptyContentUrl = emptyContentData == null ? null : emptyContentData.getContentUrl(); + if (EqualsHelper.nullSafeEquals(pivotContentUrl, emptyContentUrl)) + { + // They are a match. So the empty translation must be reset to it's original value + ret = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + } + else + { + ret = inboundValue; + } + } + else { ret = inboundValue; } diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 0365149b58..ef3f43109c 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.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.service; @@ -39,6 +39,8 @@ import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; 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.ContentService; import org.alfresco.service.cmr.repository.CopyService; @@ -71,7 +73,7 @@ import org.springframework.beans.factory.BeanFactoryAware; /** * Implementation of a Service Registry based on the definition of * Services contained within a Spring Bean Factory. - * + * * @author David Caruana */ public class ServiceDescriptorRegistry @@ -80,7 +82,7 @@ public class ServiceDescriptorRegistry // Bean Factory within which the registry lives private BeanFactory beanFactory = null; - + /* (non-Javadoc) * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) */ @@ -88,7 +90,7 @@ public class ServiceDescriptorRegistry { this.beanFactory = beanFactory; } - + /* (non-Javadoc) * @see org.alfresco.repo.service.ServiceRegistry#getServices() */ @@ -112,7 +114,7 @@ public class ServiceDescriptorRegistry */ public Object getService(QName service) { - return beanFactory.getBean(service.getLocalName()); + return beanFactory.getBean(service.getLocalName()); } /* (non-Javadoc) @@ -122,7 +124,7 @@ public class ServiceDescriptorRegistry { return (DescriptorService)getService(DESCRIPTOR_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() */ @@ -186,7 +188,7 @@ public class ServiceDescriptorRegistry { return (SearchService)getService(SEARCH_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getTransactionService() */ @@ -218,7 +220,7 @@ public class ServiceDescriptorRegistry { return (CheckOutCheckInService)getService(COCI_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getCategoryService() */ @@ -234,7 +236,7 @@ public class ServiceDescriptorRegistry { return (NamespaceService)getService(NAMESPACE_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getImporterService() */ @@ -250,7 +252,7 @@ public class ServiceDescriptorRegistry { return (ExporterService)getService(EXPORTER_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getRuleService() */ @@ -258,7 +260,7 @@ public class ServiceDescriptorRegistry { return (RuleService)getService(RULE_SERVICE); } - + /* * (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getActionService() @@ -275,7 +277,7 @@ public class ServiceDescriptorRegistry { return (PermissionService)getService(PERMISSIONS_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getAuthorityService() */ @@ -315,7 +317,7 @@ public class ServiceDescriptorRegistry { return (WorkflowService)getService(WORKFLOW_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getWorkflowService() */ @@ -366,7 +368,7 @@ public class ServiceDescriptorRegistry { return (PersonService)getService(PERSON_SERVICE); } - + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getCrossRepositoryCopyService() */ @@ -386,7 +388,7 @@ public class ServiceDescriptorRegistry /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getContentFilterLanguagesService() */ - public ContentFilterLanguagesService getContentFilterLanguagesService() + public ContentFilterLanguagesService getContentFilterLanguagesService() { return (ContentFilterLanguagesService) getService(CONTENT_FILTER_LANGUAGES_SERVICE); } @@ -406,4 +408,20 @@ public class ServiceDescriptorRegistry { return (VirtServerRegistry)getService(VIRT_SERVER_REGISTRY); } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getEditionService() + */ + public EditionService getEditionService() + { + return (EditionService) getService(EDITION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getMultilingualContentService() + */ + public MultilingualContentService getMultilingualContentService() + { + return (MultilingualContentService) getService(MULTILINGUAL_CONTENT_SERVICE); + } } diff --git a/source/java/org/alfresco/repo/version/VersionServiceImpl.java b/source/java/org/alfresco/repo/version/VersionServiceImpl.java index f092e429fc..b18a24e901 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImpl.java @@ -378,6 +378,11 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl ContentModel.PROP_VERSION_LABEL, version.getVersionLabel()); + // Freeze the version label property + Map versionLabelAsMap = new HashMap(1); + versionLabelAsMap.put(ContentModel.PROP_VERSION_LABEL, version.getVersionLabel()); + this.freezeProperties(newVersionRef, versionLabelAsMap); + // Invoke the policy behaviour invokeAfterCreateVersion(nodeRef, version); diff --git a/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java b/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java new file mode 100644 index 0000000000..de57d03ca6 --- /dev/null +++ b/source/java/org/alfresco/repo/version/common/VersionLabelComparator.java @@ -0,0 +1,47 @@ +/* + * 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.version.common; + +import java.util.Comparator; + +import org.alfresco.service.cmr.version.Version; + +/** + * A comparator to sort a version list according theires version labels ascending + * + * @author Yanick Pignot + */ +public class VersionLabelComparator implements Comparator +{ + + public int compare(Object version1, Object version2) + { + String labelV1 = ((Version) version1).getVersionLabel(); + String labelV2 = ((Version) version2).getVersionLabel(); + + // sort the list ascending + return labelV2.compareTo(labelV1); + } +} diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index af79387ec3..e7838bd144 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.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; @@ -38,6 +38,8 @@ import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; 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.ContentService; import org.alfresco.service.cmr.repository.CopyService; @@ -67,15 +69,15 @@ import org.alfresco.service.transaction.TransactionService; /** * This interface represents the registry of public Repository Services. * The registry provides meta-data about each service and provides - * access to the service interface. - * + * access to the service interface. + * * @author David Caruana */ @PublicService public interface ServiceRegistry { // Service Bean Names - + static final String SERVICE_REGISTRY = "ServiceRegistry"; static final QName REGISTRY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ServiceRegistry"); @@ -89,6 +91,8 @@ public interface ServiceRegistry static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); static final QName CONTENT_FILTER_LANGUAGES_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentFilterLanguagesService"); + static final QName MULTILINGUAL_CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MultilingualContentService"); + static final QName EDITION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "EditionService"); static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); @@ -126,14 +130,14 @@ public interface ServiceRegistry /** * Is the specified service provided by the Repository? - * + * * @param service name of service to test provision of * @return true => provided, false => not provided */ @NotAuditable boolean isServiceProvided(QName service); - /** + /** * Get the specified service. * * @param service name of service to retrieve @@ -141,13 +145,13 @@ public interface ServiceRegistry */ @NotAuditable Object getService(QName service); - + /** * @return the descriptor service */ @NotAuditable DescriptorService getDescriptorService(); - + /** * @return the transaction service */ @@ -165,13 +169,13 @@ public interface ServiceRegistry */ @NotAuditable NamespaceService getNamespaceService(); - + /** * @return the authentication service (or null, if one is not provided) */ @NotAuditable AuthenticationService getAuthenticationService(); - + /** * @return the node service (or null, if one is not provided) */ @@ -183,7 +187,7 @@ public interface ServiceRegistry */ @NotAuditable ContentService getContentService(); - + /** * @return the mimetype service (or null, if one is not provided) */ @@ -195,19 +199,19 @@ public interface ServiceRegistry */ @NotAuditable ContentFilterLanguagesService getContentFilterLanguagesService(); - + /** * @return the search service (or null, if one is not provided) */ @NotAuditable SearchService getSearchService(); - + /** * @return the version service (or null, if one is not provided) */ @NotAuditable VersionService getVersionService(); - + /** * @return the lock service (or null, if one is not provided) */ @@ -219,73 +223,73 @@ public interface ServiceRegistry */ @NotAuditable DictionaryService getDictionaryService(); - + /** * @return the copy service (or null, if one is not provided) */ @NotAuditable CopyService getCopyService(); - + /** * @return the checkout / checkin service (or null, if one is not provided) */ @NotAuditable - CheckOutCheckInService getCheckOutCheckInService(); - + CheckOutCheckInService getCheckOutCheckInService(); + /** * @return the category service (or null, if one is not provided) */ @NotAuditable CategoryService getCategoryService(); - + /** * @return the importer service or null if not present */ @NotAuditable ImporterService getImporterService(); - + /** * @return the exporter service or null if not present */ @NotAuditable ExporterService getExporterService(); - + /** * @return the rule service (or null, if one is not provided) */ @NotAuditable RuleService getRuleService(); - + /** * @return the action service (or null if one is not provided) */ @NotAuditable ActionService getActionService(); - + /** * @return the permission service (or null if one is not provided) */ @NotAuditable PermissionService getPermissionService(); - + /** * @return the authority service (or null if one is not provided) */ @NotAuditable AuthorityService getAuthorityService(); - + /** * @return the template service (or null if one is not provided) */ @NotAuditable TemplateService getTemplateService(); - + /** * @return the file-folder manipulation service (or null if one is not provided) */ @NotAuditable FileFolderService getFileFolderService(); - + /** * @return the script execution service (or null if one is not provided) */ @@ -297,7 +301,7 @@ public interface ServiceRegistry */ @NotAuditable WorkflowService getWorkflowService(); - + /** * @return the audit service (or null if one is not provided) */ @@ -306,13 +310,13 @@ public interface ServiceRegistry /** * Get the AVMService. - * @return The AVM service (or null if one is not provided); + * @return The AVM service (or null if one is not provided); */ @NotAuditable AVMService getAVMService(); /** - * Get the AVMLockingAwareService. + * Get the AVMLockingAwareService. * @return The AVM locking aware service (or null if one is not provided); */ @NotAuditable @@ -331,39 +335,53 @@ public interface ServiceRegistry */ @NotAuditable OwnableService getOwnableService(); - + /** * Get the person service (or null if one is not provided) * @return */ @NotAuditable PersonService getPersonService(); - + /** * Get the cross repository copy service (or null if one is not provided) * @return */ @NotAuditable CrossRepositoryCopyService getCrossRepositoryCopyService(); - + /** * Get the attribute service (or null if one is not provided) * @return */ @NotAuditable AttributeService getAttributeService(); - + /** * Get the AVM locking service (or null if one is not provided) * @return */ @NotAuditable AVMLockingService getAVMLockingService(); - + /** * Get the Virtualisation Server registry service bean * @return */ @NotAuditable VirtServerRegistry getVirtServerRegistry(); + + /** + * Get the Multilingual Content Service + * @return + */ + @NotAuditable + MultilingualContentService getMultilingualContentService(); + + /** + * Get the Edition Service + * @return + */ + @NotAuditable + EditionService getEditionService(); } diff --git a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java index 6a20407d7d..ae73a446c4 100644 --- a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java +++ b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java @@ -31,6 +31,8 @@ import java.util.Set; import org.alfresco.service.Auditable; import org.alfresco.service.PublicService; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -171,10 +173,14 @@ public interface MultilingualContentService * * @param translationNodeRef The cm:mlContainer to copy * @param newParentRef The new parent of the copied cm:mlDocument + * @param prefixName The prefix of the name of the copied translations. Can be null. * @return The copied cm:mlContainer + * @throws FileNotFoundException + * @throws FileExistsException + * @throws Exception */ - @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "newParentRef"}) - NodeRef copyTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef); + @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerNodeRef", "newParentRef"}) + NodeRef copyTranslationContainer(NodeRef mlContainerNodeRef, NodeRef newParentRef, String prefixName) throws FileExistsException, FileNotFoundException, Exception; /** * Moves the location of the given cm:mlContainer. @@ -184,10 +190,18 @@ public interface MultilingualContentService * * @param translationNodeRef The cm:mlContainer to move * @param newParentRef The new parent of the moved cm:mlDocument + * @throws FileExistsException + * @throws FileNotFoundException */ - @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "newParentRef"}) - void moveTranslationContainer(NodeRef translationNodeRef, NodeRef newParentRef); - - + @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerNodeRef", "newParentRef"}) + void moveTranslationContainer(NodeRef mlContainerNodeRef, NodeRef newParentRef) throws FileExistsException, FileNotFoundException; + /** + * Delete the given mlContainer and its translations. The translations will lost their cm:mlDocument aspect and + * will be archved. The empty translations will be permanently deleted. + * + * @param mlContainerNodeRef The cm:mlContainer to remove + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"mlContainerNodeRef"}) + void deleteTranslationContainer(NodeRef mlContainerNodeRef); }