From f6fbed84a0a00d4e2f2c6198d68f87b61263b09e Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 22 May 2007 05:05:00 +0000 Subject: [PATCH] Mergeing form EC-MC: Completed. Removed intwined aspect deletion behaviour git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5745 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/core-services-context.xml | 10 - config/alfresco/node-services-context.xml | 9 + config/alfresco/public-services-context.xml | 4 + .../public-services-security-context.xml | 1 + .../repo/model/filefolder/FolderType.java | 119 ------------ .../repo/model/filefolder/FolderTypeTest.java | 114 ------------ .../filefolder/MLTranslationInterceptor.java | 7 + .../repo/model/ml/EmptyTranslationAspect.java | 42 ----- .../repo/model/ml/MLContainerType.java | 31 +--- .../ml/MultilingualContentServiceImpl.java | 103 +++++++++-- .../model/ml/MultilingualDocumentAspect.java | 100 ++-------- .../repo/model/ml/MultilingualTestSuite.java | 3 - .../ml/tools/EmptyTranslationAspectTest.java | 46 ----- .../model/ml/tools/MLContainerTypeTest.java | 26 +-- .../tools/MultilingualDocumentAspectTest.java | 13 +- .../repo/node/MLTranslationInterceptor.java | 171 ++++++++++++++++++ .../repo/node/db/DbNodeServiceImpl.java | 104 ++++++----- .../cmr/ml/MultilingualContentService.java | 9 + 18 files changed, 373 insertions(+), 539 deletions(-) delete mode 100644 source/java/org/alfresco/repo/model/filefolder/FolderType.java delete mode 100644 source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java create mode 100644 source/java/org/alfresco/repo/node/MLTranslationInterceptor.java diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 47593b021e..26d4681a10 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -862,16 +862,6 @@ - - - - - - - - - - diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 430dcf6670..2676074cfa 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -16,6 +16,15 @@ + + + + + + + + + mlAwareNodeService diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 0c726d9822..75db5d0c25 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -69,6 +69,10 @@ + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 39e629d276..3076522463 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -539,6 +539,7 @@ org.alfresco.service.cmr.ml.MultilingualContentService.getPivotTranslation=ACL_NODE.0.sys:base.Read org.alfresco.service.cmr.ml.MultilingualContentService.isTranslation=ACL_NODE.0.sys:base.Read org.alfresco.service.cmr.ml.MultilingualContentService.makeTranslation=ACL_NODE.0.sys:base.Write + 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 org.alfresco.service.cmr.ml.MultilingualContentService.createEdition=ACL_NODE.0.sys:base.Write diff --git a/source/java/org/alfresco/repo/model/filefolder/FolderType.java b/source/java/org/alfresco/repo/model/filefolder/FolderType.java deleted file mode 100644 index 919fd9a866..0000000000 --- a/source/java/org/alfresco/repo/model/filefolder/FolderType.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.filefolder; - -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -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.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; - - -/** - * Class containing behaviour for the folder type. - * - * {@link ContentModel#TYPE_FOLDER folder type} - * - * @author yanipig - */ -public class FolderType implements - NodeServicePolicies.BeforeDeleteNodePolicy -{ - - // Dependencies - private PolicyComponent policyComponent; - private NodeService nodeService; - - - /** - * Initialise the Folder type - * - * Ensures that the {@link ContentModel#TYPE_FOLDER} folder type - */ - public void init() - { - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), - ContentModel.TYPE_FOLDER, - new JavaBehaviour(this, "beforeDeleteNode")); - - } - - /** - * @param policyComponent the policy component to register behaviour with - */ - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - - /** - * @param nodeService the Node Service to set - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - - /** - * Since the multilingual document don't keep their mlDocument aspect, the mlEmptyTranslation - * can't be archived and the deletion of a container doesn't call the right policies, the deletion of a - * folder must explicitly remove this aspect. - * - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) - */ - public void beforeDeleteNode(NodeRef nodeRef) - { - List childAssociations = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - - for(ChildAssociationRef childAssoc : childAssociations) - { - NodeRef child = childAssoc.getChildRef(); - - if(nodeService.exists(child)) - { - // Remove the mlDocument aspect of each multilingual node contained in the folder. - // The policies of this aspect perform the right logic. - if(nodeService.hasAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) - { - nodeService.removeAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); - } - // Recurse the process if needed - else if(nodeService.getType(child).equals(ContentModel.TYPE_FOLDER)) - { - beforeDeleteNode(child); - } - } - } - } -} diff --git a/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java b/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java deleted file mode 100644 index aa93590fa4..0000000000 --- a/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.filefolder; - -import java.util.Locale; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.model.ml.tools.AbstractMultilingualTestCases; -import org.alfresco.service.cmr.repository.NodeRef; - -/** - * Multilingual container type test cases - * - * @see org.alfresco.service.cmr.ml.MLContainerType - * - * @author yanipig - */ -public class FolderTypeTest extends AbstractMultilingualTestCases { - - - @SuppressWarnings("unused") - public void testRemoveSpace() throws Exception - { - NodeRef pivot = createContent(); - NodeRef trans2 = createContent(); - NodeRef trans3 = createContent(); - NodeRef empty; - - NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); - multilingualContentService.addTranslation(trans2, pivot, Locale.GERMAN); - multilingualContentService.addTranslation(trans3, pivot, Locale.ITALIAN);; - empty = multilingualContentService.addEmptyTranslation(pivot, "Empty_" + System.currentTimeMillis(), Locale.ENGLISH); - - - NodeRef space = fileFolderService.create( - nodeService.getPrimaryParent(pivot).getParentRef(), - "folder_" + System.currentTimeMillis(), - ContentModel.TYPE_FOLDER).getNodeRef(); - - nodeService.moveNode(pivot, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(pivot).getQName()); - nodeService.moveNode(trans2, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(trans2).getQName()); - nodeService.moveNode(trans3, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(trans3).getQName()); - nodeService.moveNode(empty, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(empty).getQName()); - - // Ensure that the nodes are correctly moved - assertEquals("Move nodes failed", 4, nodeService.getChildAssocs(space).size()); - - // 1. Delete space - - nodeService.deleteNode(space); - - // Ensute that the space is deleted - assertFalse("The deletion of the space failed", nodeService.exists(space)); - - // Ensure that the nodes are archived - assertTrue("The space " + space + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); - assertTrue("The node " + pivot + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); - assertTrue("The node " + trans2 + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); - assertTrue("The node " + trans3 + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); - - // Ensure that the mlContainer is deleted and not archived - assertFalse("The mlContainer " + mlContainer + " must be deleted", nodeService.exists(mlContainer)); - assertFalse("The mlContainer " + mlContainer + " can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(mlContainer))); - - // 2. Restore space - nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(space)); - - // Ensure that the nodes are restaured - assertFalse("The space " + space + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(space))); - assertFalse("The node " + pivot + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); - assertFalse("The node " + trans2 + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); - assertFalse("The node " + trans3 + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); - - // Ensure that the mlContainer is not restored - assertFalse("The mlContainer " + mlContainer + " must be deleted and can't be restored", nodeService.exists(mlContainer)); - - // 3. Delete space and remove it from the arhives - - nodeService.deleteNode(space); - nodeService.deleteNode(nodeArchiveService.getArchivedNode(space)); - - assertFalse("The space " + space + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(space))); - assertFalse("The node " + pivot + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); - assertFalse("The node " + trans2 + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); - assertFalse("The node " + trans3 + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); - - assertFalse("The space " + space + "must be deleted", nodeService.exists(space)); - assertFalse("The node " + pivot + "must be deleted", nodeService.exists(pivot)); - assertFalse("The node " + trans2 + "must be deleted", nodeService.exists(trans2)); - assertFalse("The node " + trans3 + "must be deleted", nodeService.exists(trans3)); - } -} diff --git a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java index 0b605b6dc0..082ed697d1 100644 --- a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java +++ b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java @@ -220,8 +220,15 @@ public class MLTranslationInterceptor implements MethodInterceptor } // Convert the set back to a list List orderedResults = new ArrayList(fileInfos.size()); + Set alreadyPresent = new HashSet(fileInfos.size() * 2 + 1); for (FileInfo info : fileInfos) { + if (alreadyPresent.contains(info)) + { + // We've done this one + continue; + } + alreadyPresent.add(info); orderedResults.add(translatedFileInfos.get(info)); } ret = orderedResults; diff --git a/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java index 303f558f0d..f6eb7a8594 100644 --- a/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java +++ b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java @@ -28,11 +28,9 @@ package org.alfresco.repo.model.ml; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.copy.CopyServicePolicies; -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.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -50,8 +48,6 @@ import org.alfresco.service.namespace.QName; */ public class EmptyTranslationAspect implements CopyServicePolicies.OnCopyNodePolicy, -// NodeServicePolicies.BeforeDeleteNodePolicy, -// NodeServicePolicies.OnRemoveAspectPolicy, ContentServicePolicies.OnContentUpdatePolicy { @@ -77,16 +73,6 @@ public class EmptyTranslationAspect implements QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"), ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, new JavaBehaviour(this, "onContentUpdate")); -// -// this.policyComponent.bindClassBehaviour( -// QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), -// ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, -// new JavaBehaviour(this, "beforeDeleteNode")); -// -// this.policyComponent.bindClassBehaviour( -// QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), -// ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, -// new JavaBehaviour(this, "onRemoveAspect")); } /** @@ -129,32 +115,4 @@ public class EmptyTranslationAspect implements nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY); } } - -// /** -// * If a cm:mlEmptyTranslation is deleted, it can't be archived. -// * -// * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) -// */ -// public void beforeDeleteNode(NodeRef nodeRef) -// { -// // add TEMPORARY ASPECT to force the deleteNode -// nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); -// } -// -// /** -// * If the aspect cm:mlEmptyTranslation is removed and the content url property is null, the node can be deleted. -// * The other time the aspect is removed is when new content is added, in which case the node must be kept. -// * -// * @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) * -// */ -// public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) -// { -// // Delete the node if the content is empty. -// // Keep the node if it has content -// ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); -// if(contentData.getContentUrl() == null) -// { -// nodeService.deleteNode(nodeRef); -// } -// } } diff --git a/source/java/org/alfresco/repo/model/ml/MLContainerType.java b/source/java/org/alfresco/repo/model/ml/MLContainerType.java index 250387f7c1..da1a7db629 100644 --- a/source/java/org/alfresco/repo/model/ml/MLContainerType.java +++ b/source/java/org/alfresco/repo/model/ml/MLContainerType.java @@ -49,7 +49,6 @@ import org.alfresco.service.namespace.QName; * @author yanipig */ public class MLContainerType implements - NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy { /** @@ -76,11 +75,6 @@ public class MLContainerType implements QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ContentModel.TYPE_MULTILINGUAL_CONTAINER, new JavaBehaviour(this, "onUpdateProperties")); - - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), - ContentModel.TYPE_MULTILINGUAL_CONTAINER, - new JavaBehaviour(this, "beforeDeleteNode")); } /** @@ -108,27 +102,6 @@ public class MLContainerType implements this.nodeService = nodeService; } - /** - * If a cm:mlContainer is deleted, it can't be archived. - * - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) - */ - public void beforeDeleteNode(NodeRef nodeRef) - { - Map translations = multilingualContentService.getTranslations(nodeRef); - - // add the DELETION_RUNNING property - nodeService.setProperty(nodeRef, PROP_NAME_DELETION_RUNNING, Boolean.TRUE); - - for(Map.Entry entry : translations.entrySet()) - { - nodeService.removeAspect(entry.getValue(), ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); - } - - // remove the DELETION_RUNNING property - nodeService.removeProperty(nodeRef, PROP_NAME_DELETION_RUNNING); - } - /** * The property locale of a cm:mlContainer represents the locale of the pivot translation. * @@ -139,6 +112,10 @@ public class MLContainerType implements */ public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) { + /* + * TODO: Move into MultilingualContentService + */ + Locale localeAfter = (Locale) after.get(ContentModel.PROP_LOCALE); Locale localeBefore = (Locale) before.get(ContentModel.PROP_LOCALE); diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index 334c329f45..c59c0fe74f 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -52,12 +52,26 @@ 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; +import org.alfresco.util.EqualsHelper; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Multilingual support implementation + * Multilingual support implementation. + *

+ * The basic structure supported is that of a hidden container of type + * cm:mlContainer containing one or more secondary children of + * type cm:mlDocument. One of these will have a matching locale + * and is referred to as the pivot translation. It is also possible + * to have several transient cm:emptyTranslation instances that + * live and die with the container until they get their own content. + *

+ * It is not possible to guarantee that there is always a pivot translation + * available in the set of sibling translations. The strategy is to hide + * all translations when there isn't a pivot translation available. A + * background task should be cleaning up the empty or invalid cm:mlContainer + * instances. * * @author Derek Hulley * @author Philippe Dubois @@ -244,6 +258,21 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // done return mlContainerNodeRef; } + + private boolean isPivotTranslation(NodeRef contentNodeRef) + { + Locale locale = (Locale) nodeService.getProperty(contentNodeRef, ContentModel.PROP_LOCALE); + // Get the container + NodeRef containerNodeRef = getOrCreateMLContainer(contentNodeRef, false); + Locale containerLocale = (Locale) nodeService.getProperty(containerNodeRef, ContentModel.PROP_LOCALE); + boolean isPivot = EqualsHelper.nullSafeEquals(locale, containerLocale); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Node " + (isPivot ? "is" : "is not") + " pivot: " + contentNodeRef); + } + return isPivot; + } /** @inheritDoc */ public boolean isTranslation(NodeRef contentNodeRef) @@ -257,17 +286,13 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic } return false; } - // Is there a ML container - List parentAssocRefs = nodeService.getParentAssocs( - contentNodeRef, - ContentModel.ASSOC_MULTILINGUAL_CHILD, - RegexQNamePattern.MATCH_ALL); - if (parentAssocRefs.size() > 0) + // Are there any associated translations + Map translations = getTranslations(contentNodeRef); + if (translations.size() > 0) { - // It has the parent required if (logger.isDebugEnabled()) { - logger.debug("Document has ML container parent: " + contentNodeRef); + logger.debug("Document is a translation: " + contentNodeRef); } return true; } @@ -275,12 +300,12 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic { if (logger.isDebugEnabled()) { - logger.debug("Document has no ML container parent: " + contentNodeRef); + logger.debug("Document is not a translation: " + contentNodeRef); } return false; } } - + /** @inheritDoc */ public NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale) { @@ -296,6 +321,52 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return mlContainerNodeRef; } + /** @inheritDoc */ + public void unmakeTranslation(NodeRef translationNodeRef) + { + boolean isPivot = isPivotTranslation(translationNodeRef); + + if (isPivot) + { + // Get the container + NodeRef containerNodeRef = getOrCreateMLContainer(translationNodeRef, false); + // Get all translation child associations + List mlChildAssocs = nodeService.getChildAssocs( + containerNodeRef, + ContentModel.ASSOC_MULTILINGUAL_CHILD, + RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef mlChildAssoc : mlChildAssocs) + { + NodeRef mlChildNodeRef = mlChildAssoc.getChildRef(); + // Delete empty translations + if (nodeService.hasAspect(mlChildNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + nodeService.deleteNode(mlChildNodeRef); + } + else + { + nodeService.removeAspect(mlChildNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + } + // Now delete the container + nodeService.deleteNode(containerNodeRef); + } + else + { + if (nodeService.hasAspect(translationNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + nodeService.deleteNode(translationNodeRef); + } + else + { + // Get the container and break the association to it + NodeRef containerNodeRef = getOrCreateMLContainer(translationNodeRef, false); + nodeService.removeChild(containerNodeRef, translationNodeRef); + nodeService.removeAspect(translationNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + } + } + /** @inheritDoc */ public NodeRef addTranslation(NodeRef newTranslationNodeRef, NodeRef translationOfNodeRef, Locale locale) { @@ -443,8 +514,6 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic /** @inheritDoc */ public NodeRef getTranslationForLocale(NodeRef translationNodeRef, Locale locale) { - // Get the container - getOrCreateMLContainer(translationNodeRef, false); // Get all the translations Map nodeRefsByLocale = getTranslations(translationNodeRef); // Get the closest matching locale @@ -529,8 +598,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic else if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(nodeRef))) { Locale containerLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); - - return getTranslationForLocale(getTranslations(nodeRef).get(containerLocale), containerLocale); + return getTranslationForLocale(nodeRef, containerLocale); } else { @@ -539,12 +607,9 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic return null; } } - + /** * @inheritDoc - * - * TODO: This logic merely creates a file with a specific aspect and is designed to support - * specific use-case in the UI. Examine if the logic should be here or in the UI. */ public NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale) { diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java index 82c33e7a10..79c05931e7 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java @@ -55,8 +55,6 @@ public class MultilingualDocumentAspect implements CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyCompletePolicy, NodeServicePolicies.BeforeDeleteNodePolicy, - NodeServicePolicies.BeforeRemoveAspectPolicy, - NodeServicePolicies.OnRemoveAspectPolicy, NodeServicePolicies.OnUpdatePropertiesPolicy { @@ -80,31 +78,20 @@ public class MultilingualDocumentAspect implements ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, new JavaBehaviour(this, "onCopyNode")); - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, - new JavaBehaviour(this, "beforeDeleteNode")); - - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRemoveAspect"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, - new JavaBehaviour(this, "beforeRemoveAspect")); - - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, - new JavaBehaviour(this, "onUpdateProperties")); - 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, "onRemoveAspect"), - ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, - new JavaBehaviour(this, "onRemoveAspect")); + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "beforeDeleteNode")); + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onUpdateProperties")); } /** @@ -153,72 +140,19 @@ public class MultilingualDocumentAspect implements } /** - * If the node is multilingual and it is the pivot translation, then deletion is not allowed unless the + * 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) - { -// checkRemoveParentMLContainer(nodeRef); - // Remove the aspect - nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); - } - - /** - * When a pivot translation is deleted, it's cm:mlContainer is deleted too. - * - * Note: When the pivot translation and the mlContainer are deleted, the mlDocument apsect is removed from - * the other translations. It will be better to don't allow the deletion of the pivot if other translation is - * available at the client side level. - * - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeRemoveAspectPolicy#beforeRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) - */ - public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) { - checkRemoveParentMLContainer(nodeRef); - } - - private void checkRemoveParentMLContainer(NodeRef nodeRef) - { - // Avoid nodes that are no longer translations - if (!multilingualContentService.isTranslation(nodeRef)) - { - return; - } - - NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef); - - // nothing to do if the mlContainer is in a deletion process - Boolean inDeleteProcess = (Boolean) nodeService.getProperty(mlContainer, MLContainerType.PROP_NAME_DELETION_RUNNING); - if(inDeleteProcess != null && inDeleteProcess == true) - { - // TODO: Is this still called? Can we get rid of the DELETION_RUNNING property? - return; - } - - Locale mlContainerLocale = (Locale) nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE); - Locale nodeRefLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); - - nodeService.removeChild(mlContainer, nodeRef); - - // if last translation or if nodeRef is pivot translation - if (multilingualContentService.getTranslations(mlContainer).size() == 0 - || mlContainerLocale.equals(nodeRefLocale)) - { - // delete the mlContainer - nodeService.deleteNode(mlContainer); - } - } - - /** - * Removes the document's locale and the cm:mlEmptyTranslation aspect, - * if present. - */ - public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) - { - nodeService.removeProperty(nodeRef, ContentModel.PROP_LOCALE); - // Remove the empty translation aspect if it is present if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) { - nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + // We just let it get deleted + } + else + { + // First unhook it + multilingualContentService.unmakeTranslation(nodeRef); } } @@ -232,6 +166,10 @@ public class MultilingualDocumentAspect implements */ 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; diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java index bd9d58ed8e..0b5547d33a 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java @@ -27,14 +27,12 @@ package org.alfresco.repo.model.ml; import junit.framework.Test; import junit.framework.TestSuite; -import org.alfresco.repo.model.filefolder.FolderTypeTest; import org.alfresco.repo.model.ml.tools.ContentFilterLanguagesMapTest; import org.alfresco.repo.model.ml.tools.EmptyTranslationAspectTest; import org.alfresco.repo.model.ml.tools.MLContainerTypeTest; import org.alfresco.repo.model.ml.tools.MultilingualContentServiceImplTest; import org.alfresco.repo.model.ml.tools.MultilingualDocumentAspectTest; - /** * Multilingual test suite * @@ -56,7 +54,6 @@ public class MultilingualTestSuite extends TestSuite suite.addTestSuite(MultilingualContentServiceImplTest.class); suite.addTestSuite(MultilingualDocumentAspectTest.class); suite.addTestSuite(MLContainerTypeTest.class); - suite.addTestSuite(FolderTypeTest.class); return suite; } 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 7a843a4b9f..843b58858a 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java @@ -169,50 +169,4 @@ public class EmptyTranslationAspectTest extends AbstractMultilingualTestCases { fileFolderService.getReader(empty).getContentString(), fileFolderService.getReader(pivot).getContentString()); } - - public void testRemoveAspect() throws Exception - { - NodeRef pivot = createContent(); - NodeRef empty = null; - - multilingualContentService.makeTranslation(pivot, Locale.FRENCH); - - // 1. remove mlEmptyTranslation aspect with empty content - empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); - nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); - - // Ensure that the empty translation is deleted - assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with no content the node must be deleted", - nodeService.exists(empty)); - assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with no content the node must be deleted and can't be archived", - nodeService.exists(nodeArchiveService.getArchivedNode(empty))); - - // 2. remove mlDocument aspect with empty content - empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); - nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); - - // Ensure that the empty translation is deleted - assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " of an empty translation with no content the node must be deleted", - nodeService.exists(empty)); - assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " of an empty translation with no content the node must be deleted and can't be archived", - nodeService.exists(nodeArchiveService.getArchivedNode(empty))); - - // 3. remove mlEmptyTranslation aspect with not empty content - empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); - ContentWriter writer = contentService.getWriter(empty, ContentModel.PROP_CONTENT, true); - writer.setMimetype("text/plain"); - writer.putContent("ANY_CONTENT"); - nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); - - // Ensure that the empty translation is NOT deleted - assertTrue("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content the node must NOT be deleted", - nodeService.exists(empty)); - // Ensure that the mlEmptyTranslation aspect is removed - assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content this aspect must be removed", - nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); - // Ensure that the mlEmptyDocument in NOT removed - assertTrue("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect must be keeped", - nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); - } - } 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 7e2b1fa13c..ff2762088c 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java @@ -39,30 +39,8 @@ import org.alfresco.service.namespace.QName; * * @author yanipig */ -public class MLContainerTypeTest extends AbstractMultilingualTestCases { - - - public void testDeleteNode() throws Exception - { - NodeRef pivot = createContent(); - NodeRef empty; - - NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); - empty = multilingualContentService.addEmptyTranslation(pivot, "Empty_" + System.currentTimeMillis(), Locale.ENGLISH); - - nodeService.deleteNode(mlContainer); - - // Ensure that the mlContainer is deleted - assertFalse("The mlContainer must be deleted", nodeService.exists(mlContainer)); - // Ensure that the empty translation is deleted - assertFalse("The mlEmptyTranslation must be deleted", nodeService.exists(empty)); - // Ensure that the non-empty document is not deleted - assertTrue("The mlDocument must not be deleted", nodeService.exists(pivot)); - // Ensure that the mlDocument property of the non-empty document is removed - assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect of the mlDocument must be removed", - nodeService.hasAspect(pivot, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); - } - +public class MLContainerTypeTest extends AbstractMultilingualTestCases +{ @SuppressWarnings("unused") public void testEditLocale() throws Exception { 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 7c1bdf37a4..f41d43778a 100644 --- a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java @@ -87,8 +87,11 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase assertEquals("The restored node would not be restaured to the mlContainer", 2, multilingualContentService.getTranslations(mlContainer).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)); - // Ensure that the restored node doesn't keep the locale property - assertNull("The restaured node can't keep the locale property", nodeService.getProperty(restoredNode, ContentModel.PROP_LOCALE)); +// DH: The locale is stored on an aspect that is independent of the ML model. +// It is therefore not possible to remove the locale just because the node +// is being unhooked from the ML structures +// // Ensure that the restored node doesn't keep the locale property +// assertNull("The restaured node can't keep the locale property", nodeService.getProperty(restoredNode, ContentModel.PROP_LOCALE)); } public void testDeletePivot() throws Exception @@ -109,8 +112,10 @@ public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCase assertTrue("The last translation would not be removed", nodeService.exists(trans1)); // Ensure that trans1 has no mlDocument aspect assertFalse("The last translation can't keep the multilingual aspect", nodeService.hasAspect(trans1, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); - // Ensure that trans1 has no locale propety - assertNull("The last translation can't keep the locale property", nodeService.getProperty(trans1, ContentModel.PROP_LOCALE)); +// DH: Here too, the sys:locale property must be left alone as it is independent of the +// ML model +// // Ensure that trans1 has no locale propety +// assertNull("The last translation can't keep the locale property", nodeService.getProperty(trans1, ContentModel.PROP_LOCALE)); } public void testDeleteLastNode() throws Exception diff --git a/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java b/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java new file mode 100644 index 0000000000..e26b900520 --- /dev/null +++ b/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java @@ -0,0 +1,171 @@ +/* + * 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.node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.ml.MultilingualContentService; +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.namespace.QName; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Interceptor to + * - filter the multilingual nodes to display the documents in the prefered language of teh user + * + * @author yanipig + */ +public class MLTranslationInterceptor implements MethodInterceptor +{ + private static Log logger = LogFactory.getLog(MLTranslationInterceptor.class); + + private NodeService directNodeService; + + private MultilingualContentService directMultilingualContentService; + + @SuppressWarnings("unchecked") + public Object invoke(MethodInvocation invocation) throws Throwable + { + Object ret = null; + String methodName = invocation.getMethod().getName(); + + // intercept the methods getChildAssocs and getChildByNames to apply filter. + if (methodName.equals("getChildAssocs") || methodName.equals("getChildByName")) + { + ret = invocation.proceed(); + + NodeRef parent = (NodeRef) invocation.getArguments()[0]; + + // all the association returned by the method + List allChildAssoc = (List) ret; + + // get the user content filter language + Locale filterLocale = I18NUtil.getContentLocaleOrNull(); + + if(filterLocale != null + && directNodeService.getType(parent).equals(ContentModel.TYPE_FOLDER) + && ret != null + && !allChildAssoc.isEmpty() + ) + { + + // the list of Association to return + List toReturn = new ArrayList(); + // the ml containers found in the folder + List mlContainers = new ArrayList(); + + // construct the list of ML Container + for (ChildAssociationRef assoc : allChildAssoc) + { + NodeRef child = assoc.getChildRef(); + + QName type = directNodeService.getType(child); + + if(type.equals(ContentModel.TYPE_CONTENT) && + directNodeService.hasAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + NodeRef container = directMultilingualContentService.getTranslationContainer(child); + + if (!mlContainers.contains(container)) + { + mlContainers.add(container); + } + } + else + { + // no specific treatment for folder and non-multilingual document + toReturn.add(assoc); + } + } + + // for each mlContainer found, choose the unique document to return + for(NodeRef container : mlContainers) + { + // get each translation language + Set locales = directMultilingualContentService.getTranslations(container).keySet(); + + if(locales != null && locales.size() > 0) + { + Locale matchedLocal = I18NUtil.getNearestLocale(filterLocale, locales); + + NodeRef matchedTranslation = null; + + // if the filter language is not found + if(matchedLocal == null) + { + // get the pivot translation + matchedTranslation = directMultilingualContentService.getPivotTranslation(container); + } + else + { + // get the matched translation + matchedTranslation = directMultilingualContentService.getTranslations(container).get(matchedLocal); + } + + toReturn.add(new ChildAssociationRef(null, null, null, matchedTranslation)); + } + } + + ret = toReturn; + + if (logger.isDebugEnabled()) + { + logger.debug("Filter has found " + + allChildAssoc.size() + " entries, " + + mlContainers.size() + " different ML Container " + + "and returns " + toReturn.size() + " nodes"); + } + } + + } + else + { + ret = invocation.proceed(); + } + + return ret; + } + + public void setDirectMultilingualContentService( + MultilingualContentService multilingualContentService) + { + this.directMultilingualContentService = multilingualContentService; + } + + public void setDirectNodeService(NodeService nodeService) + { + this.directNodeService = nodeService; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index bf039646ba..67cbc3d133 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -600,10 +600,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) throws InvalidNodeRefException, InvalidAspectException { - // Invoke policy behaviours - invokeBeforeUpdateNode(nodeRef); - invokeBeforeRemoveAspect(nodeRef, aspectTypeQName); - // get the aspect AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); if (aspectDef == null) @@ -612,56 +608,64 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // get the node Node node = getNodeNotNull(nodeRef); + Set nodeAspects = node.getAspects(); + + if (!nodeAspects.contains(aspectTypeQName)) + { + // The aspect isn't present so just leave it + return; + } + + // Invoke policy behaviours + invokeBeforeUpdateNode(nodeRef); + invokeBeforeRemoveAspect(nodeRef, aspectTypeQName); // remove the aspect, if present - boolean removed = node.getAspects().remove(aspectTypeQName); - // if the aspect was present, remove the associated properties and associations - if (removed) + node.getAspects().remove(aspectTypeQName); + + Map nodeProperties = node.getProperties(); + Map propertyDefs = aspectDef.getProperties(); + for (QName propertyName : propertyDefs.keySet()) { - Map nodeProperties = node.getProperties(); - Map propertyDefs = aspectDef.getProperties(); - for (QName propertyName : propertyDefs.keySet()) - { - nodeProperties.remove(propertyName); - } - - // Remove child associations - Map childAssocDefs = aspectDef.getChildAssociations(); - Collection childAssocs = nodeDaoService.getChildAssocs(node); - for (ChildAssoc childAssoc : childAssocs) - { - // Ignore if the association type is not defined by the aspect - QName childAssocQName = childAssoc.getTypeQName(); - if (!childAssocDefs.containsKey(childAssocQName)) - { - continue; - } - // The association is of a type that should be removed - nodeDaoService.deleteChildAssoc(childAssoc, true); - } - - // Remove regular associations - Map assocDefs = aspectDef.getAssociations(); - List nodeAssocs = nodeDaoService.getTargetNodeAssocs(node); - for (NodeAssoc nodeAssoc : nodeAssocs) - { - // Ignore if the association type is not defined by the aspect - QName nodeAssocQName = nodeAssoc.getTypeQName(); - if (!assocDefs.containsKey(nodeAssocQName)) - { - continue; - } - // Delete the association - nodeDaoService.deleteNodeAssoc(nodeAssoc); - } - - // Invoke policy behaviours - invokeOnUpdateNode(nodeRef); - invokeOnRemoveAspect(nodeRef, aspectTypeQName); - - // update the node status - nodeDaoService.recordChangeId(nodeRef); + nodeProperties.remove(propertyName); } + + // Remove child associations + Map childAssocDefs = aspectDef.getChildAssociations(); + Collection childAssocs = nodeDaoService.getChildAssocs(node); + for (ChildAssoc childAssoc : childAssocs) + { + // Ignore if the association type is not defined by the aspect + QName childAssocQName = childAssoc.getTypeQName(); + if (!childAssocDefs.containsKey(childAssocQName)) + { + continue; + } + // The association is of a type that should be removed + nodeDaoService.deleteChildAssoc(childAssoc, true); + } + + // Remove regular associations + Map assocDefs = aspectDef.getAssociations(); + List nodeAssocs = nodeDaoService.getTargetNodeAssocs(node); + for (NodeAssoc nodeAssoc : nodeAssocs) + { + // Ignore if the association type is not defined by the aspect + QName nodeAssocQName = nodeAssoc.getTypeQName(); + if (!assocDefs.containsKey(nodeAssocQName)) + { + continue; + } + // Delete the association + nodeDaoService.deleteNodeAssoc(nodeAssoc); + } + + // Invoke policy behaviours + invokeOnUpdateNode(nodeRef); + invokeOnRemoveAspect(nodeRef, aspectTypeQName); + + // update the node status + nodeDaoService.recordChangeId(nodeRef); } /** diff --git a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java index ff4f3c7304..1e11ef8efb 100644 --- a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java +++ b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java @@ -73,6 +73,15 @@ public interface MultilingualContentService @Auditable(key = Auditable.Key.ARG_0, parameters = {"contentNodeRef", "locale"}) NodeRef makeTranslation(NodeRef contentNodeRef, Locale locale); + /** + * 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.