diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 63242f2ab1..84b6bb8ba6 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -248,6 +248,11 @@ + + + + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index ddd303c982..a3a01f47b0 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -143,6 +143,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml index 91c2f622ea..f9f7a7e409 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml @@ -16,6 +16,7 @@ + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java index 441af631bb..cf5e19cf26 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java @@ -38,6 +38,7 @@ import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; @@ -94,6 +95,9 @@ public class RecordAspect extends AbstractDisposableItem /** quickShare service */ private QuickShareService quickShareService; + /** Utility class for duplicating content */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** I18N */ private static final String MSG_CANNOT_UPDATE_RECORD_CONTENT = "rm.service.update-record-content"; @@ -130,6 +134,15 @@ public class RecordAspect extends AbstractDisposableItem this.quickShareService = quickShareService; } + /** + * Setter for content duplication utility class + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * Behaviour to ensure renditions have the appropriate extended security. * @@ -356,7 +369,7 @@ public class RecordAspect extends AbstractDisposableItem extendedSecurityService.remove(targetNodeRef); //create a new content URL for the copy - createNewContentURL(targetNodeRef); + contentBinDuplicationUtility.duplicate(targetNodeRef); } } @@ -401,20 +414,7 @@ public class RecordAspect extends AbstractDisposableItem if (!nodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).isEmpty() || !nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).isEmpty()) { - //disable versioning and auditing - behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); - try - { - //create a new content URL for the copy/original node - createNewContentURL(nodeRef); - } - finally - { - //enable versioning and auditing - behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); - } + contentBinDuplicationUtility.duplicate(nodeRef); } return null; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java index efab5cc2ea..c41e937aa1 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java @@ -33,6 +33,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.relationship.Relationship; import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; @@ -64,6 +65,11 @@ public class VersionRecordAspect extends BaseBehaviourBean /** relationship service */ private RelationshipService relationshipService; + /** + * Utility class for duplicating content + */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** * @param recordableVersionService recordable version service */ @@ -80,6 +86,16 @@ public class VersionRecordAspect extends BaseBehaviourBean this.relationshipService = relationshipService; } + /** + * Setter for content duplication utility class + * + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * If the record is a version record then delete the associated version entry * @@ -148,7 +164,7 @@ public class VersionRecordAspect extends BaseBehaviourBean if (!nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD_ORIGINATING_DETAILS)) { //create a new content URL for the version record - createNewContentURL(nodeRef); + contentBinDuplicationUtility.duplicate(nodeRef); } } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java new file mode 100644 index 0000000000..c1d7e879f0 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java @@ -0,0 +1,107 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco 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 Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.util; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Utility class to duplicate the content of a node without triggering the audit or versioning behaviours + * @author Ross Gale + * @since 2.7.2 + */ +public class ContentBinDuplicationUtility +{ + + /** + * Behaviour filter + */ + private BehaviourFilter behaviourFilter; + + /** + * Provides methods for accessing and transforming content. + */ + private ContentService contentService; + + /** + * Setter for behaviour filter + * @param behaviourFilter BehaviourFilter + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + /** + * Setter for content service + * @param contentService ContentService + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Duplicate the content of a node without triggering the audit or versioning behaviours + * + * @param nodeRef The node with the content to duplicate + */ + public void duplicate(NodeRef nodeRef) + { + //disabling versioning and auditing + behaviourFilter.disableBehaviour(); + try + { + //create a new content URL for the copy/original node + updateContentProperty(nodeRef); + } + finally + { + //enable versioning and auditing + behaviourFilter.enableBehaviour(); + } + } + + /** + * Helper to update the content property for the node + * + * @param nodeRef the node + */ + private void updateContentProperty(NodeRef nodeRef) + { + ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (reader != null) + { + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.putContent(reader); + } + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java index 8fc5094b05..f7a58ea1ff 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; -import org.alfresco.model.ContentModel; + import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; @@ -39,9 +39,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; @@ -554,30 +552,4 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte result.add(nodeService.getType(nodeRef)); return result; } - - /** - * Helper to update the given content property for the node - * - * @param nodeRef the node - * @param contentProperty the property to be updated - */ - protected void updateContentProperty(NodeRef nodeRef, QName contentProperty) - { - ContentReader reader = contentService.getReader(nodeRef, contentProperty); - if (reader != null) - { - ContentWriter writer = contentService.getWriter(nodeRef, contentProperty, true); - writer.putContent(reader); - } - } - - /** - * Helper to create a new content URL for the node - * - * @param nodeRef the node - */ - protected void createNewContentURL(NodeRef nodeRef) - { - updateContentProperty(nodeRef, ContentModel.PROP_CONTENT); - } } diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java index 730da904f3..84d0559590 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java @@ -30,8 +30,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASPECT_RECORD; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -39,7 +37,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; -import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; @@ -70,8 +68,6 @@ public class RecordAspectUnitTest @Mock private NodeService mockNodeService; @Mock - private BehaviourFilter mockBehaviorFilter; - @Mock private ContentService mockContentService; @Mock private ContentReader mockContentReader; @@ -79,6 +75,8 @@ public class RecordAspectUnitTest private ContentWriter mockContentWriter; @Mock private ExtendedSecurityService mockExtendedSecurityService; + @Mock + private ContentBinDuplicationUtility mockContentBinDuplicationUtility; @Before public void setUp() @@ -96,7 +94,7 @@ public class RecordAspectUnitTest recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(1); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(NODE_REF); } /** Check that the bin is duplicated before adding the aspect if the file is a copy. */ @@ -109,24 +107,7 @@ public class RecordAspectUnitTest recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(1); - } - - /** Check that no content bin is created if the file does not have content. */ - @Test - public void testBeforeAddAspectOnFileWithNoContent() - { - when(mockNodeService.getTargetAssocs(NODE_REF, ContentModel.ASSOC_ORIGINAL)).thenReturn(asList(TARGET_ASSOC_REF)); - when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(null); - - recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - - verify(mockBehaviorFilter, times(1)).disableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(1)).disableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); - verify(mockContentService, times(1)).getReader(NODE_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, never()).getWriter(NODE_REF, ContentModel.PROP_CONTENT, true); - verify(mockBehaviorFilter, times(1)).enableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(1)).enableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(NODE_REF); } /** Check that the bin is not duplicated before adding the aspect if the node has no copies. */ @@ -138,7 +119,7 @@ public class RecordAspectUnitTest recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(0); + verify(mockContentBinDuplicationUtility, times(0)).duplicate(NODE_REF); } /** Check that the bin is duplicated when copying a record. */ @@ -153,24 +134,6 @@ public class RecordAspectUnitTest recordAspect.onCopyComplete(null, NODE_REF, COPY_REF, true, null); verify(mockExtendedSecurityService, times(1)).remove(COPY_REF); - verify(mockContentService, times(1)).getReader(COPY_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, times(1)).getWriter(COPY_REF, ContentModel.PROP_CONTENT, true); - verify(mockContentWriter, times(1)).putContent(mockContentReader); - } - - /** - * Helper to verify beforeAddAspect methods invocations - * - * @param wantedNumberOfInvocations wanted number of invocations for each method - */ - private void verifyBeforeAddAspectMethodsInvocations(int wantedNumberOfInvocations) - { - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).disableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).disableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); - verify(mockContentService, times(wantedNumberOfInvocations)).getReader(NODE_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, times(wantedNumberOfInvocations)).getWriter(NODE_REF, ContentModel.PROP_CONTENT, true); - verify(mockContentWriter, times(wantedNumberOfInvocations)).putContent(mockContentReader); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).enableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).enableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(COPY_REF); } } diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java new file mode 100644 index 0000000000..c37437433a --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java @@ -0,0 +1,111 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco 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 Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.module.org_alfresco_module_rm.util; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Test class for the ContentBinDuplicationUtility + * @author Ross Gale + * @since 2.7.2 + */ +public class ContentBinDuplicationUtilityUnitTest +{ + + @Mock + private ContentService contentService; + + @Mock + private BehaviourFilter behaviourFilter; + + @Mock + private ContentReader contentReader; + + @Mock + private ContentWriter contentWriter; + + @InjectMocks + private ContentBinDuplicationUtility contentBinDuplicationUtility; + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests that the requests are made to disable and re-enable the audit and versioning and to update the content bin + */ + @Test + public void testContentUrlIsUpdated() + { + NodeRef nodeRef = new NodeRef("some://test/noderef"); + when(contentService.getReader(nodeRef, ContentModel.PROP_CONTENT)).thenReturn(contentReader); + when(contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true)).thenReturn(contentWriter); + contentBinDuplicationUtility.duplicate(nodeRef); + verify(contentWriter, times(1)).putContent(contentReader); + checkBehaviours(1); + } + + /** + * Test content duplication doesn't happen when node has no content + */ + @Test + public void testDuplicationDoesntHappenWithNoContent() + { + NodeRef nodeRef = new NodeRef("some://test/noderef"); + when(contentService.getReader(nodeRef, ContentModel.PROP_CONTENT)).thenReturn(null); + contentBinDuplicationUtility.duplicate(nodeRef); + verify(contentWriter, times(0)).putContent(contentReader); + checkBehaviours(1); + } + + /** + * Check that the behaviours are disabled and re-enabled the correct number of times + * @param times the times the behaviours should be called + */ + private void checkBehaviours(int times) + { + verify(behaviourFilter, times(times)).disableBehaviour(); + verify(behaviourFilter, times(times)).enableBehaviour(); + } +}