From d5278c4ec7709537afdb83d70e3ff5a580de962c Mon Sep 17 00:00:00 2001 From: Ross Gale Date: Fri, 9 Aug 2019 10:06:30 +0100 Subject: [PATCH] RM-6873 adding code to allow for the removal of active content from a hold --- .../model/rma/aspect/FrozenAspect.java | 20 +- .../hold/RemoveActiveContentToHoldTest.java | 194 ++++++++++++++++++ .../rma/aspect/FrozenAspectUnitTest.java | 126 ++++++++++++ 3 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/RemoveActiveContentToHoldTest.java create mode 100644 rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspectUnitTest.java diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java index c3dcc1f4fb..909cf8bd23 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspect.java @@ -190,35 +190,33 @@ public class FrozenAspect extends BaseBehaviourBean kind = BehaviourKind.CLASS, notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT ) - public void onRemoveAspect(final NodeRef record, QName aspectTypeQName) + public void onRemoveAspect(final NodeRef nodeRef, QName aspectTypeQName) { AuthenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() { - if (nodeService.exists(record) && - isRecord(record)) + if (nodeService.exists(nodeRef) && + (isRecord(nodeRef) || instanceOf(nodeRef, TYPE_CONTENT))) { - // get the owning record folder - NodeRef recordFolder = nodeService.getPrimaryParent(record).getParentRef(); + // get the owning folder + NodeRef owningFolder = nodeService.getPrimaryParent(nodeRef).getParentRef(); // check that the aspect has been added - if (nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN)) + if (nodeService.hasAspect(owningFolder, ASPECT_HELD_CHILDREN)) { // decrement current count - int currentCount = (Integer)nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT); + int currentCount = (Integer)nodeService.getProperty(owningFolder, PROP_HELD_CHILDREN_COUNT); if (currentCount > 0) { - currentCount = currentCount - 1; - nodeService.setProperty(recordFolder, PROP_HELD_CHILDREN_COUNT, currentCount); + nodeService.setProperty(owningFolder, PROP_HELD_CHILDREN_COUNT, currentCount - 1 ); } } } return null; } - }); - + }); } } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/RemoveActiveContentToHoldTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/RemoveActiveContentToHoldTest.java new file mode 100644 index 0000000000..e182af97da --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/hold/RemoveActiveContentToHoldTest.java @@ -0,0 +1,194 @@ +/* + * #%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.test.integration.hold; + +import static org.alfresco.repo.security.authentication.AuthenticationUtil.getAdminUserName; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.GUID; + +/** + * Remove Active Content To Hold Integration Tests + * + * @author Ross Gale + * @since 3.2 + */ +public class RemoveActiveContentToHoldTest extends BaseRMTestCase +{ + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + @Override + protected boolean isUserTest() + { + return true; + } + + /** + * Given a piece of active content on hold + * When I try to remove the active content from the hold + * Then the active content is unfrozen + * And the active content is not contained within the hold + */ + public void testRemoveDocumentFromHold() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef hold; + Integer before; + + public void given() + { + hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + holdService.addToHold(hold, dmDocument); + } + + public void when() + { + before = (Integer) nodeService.getProperty(dmFolder, PROP_HELD_CHILDREN_COUNT); + holdService.removeFromHold(hold, dmDocument); + } + + public void then() + { + // active content is no longer frozen + assertFalse(freezeService.isFrozen(dmDocument)); + + // check the content is no longer held + assertFalse(holdService.getHeld(hold).contains(dmDocument)); + assertFalse(holdService.heldBy(dmDocument, true).contains(hold)); + + // check the held count on the folder has been reduced + assertTrue(before > (Integer) nodeService.getProperty(dmFolder, PROP_HELD_CHILDREN_COUNT)); + } + }); + } + + /** + * Given a piece of active content in multiple holds + * When I try to remove the active content from a single hold + * Then the active content is still frozen + * And the active content is not contained within the specified hold + * And is still added to any other holds + */ + public void testRemoveDocumentFromASingleHold() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef hold; + private NodeRef hold2; + Integer before; + + public void given() + { + hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + hold2 = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + holdService.addToHold(hold, dmDocument); + holdService.addToHold(hold2, dmDocument); + } + + public void when() + { + before = (Integer) nodeService.getProperty(dmFolder, PROP_HELD_CHILDREN_COUNT); + holdService.removeFromHold(hold, dmDocument); + } + + public void then() + { + assertTrue(freezeService.isFrozen(dmDocument)); + assertFalse(holdService.heldBy(dmDocument, true).contains(hold)); + assertTrue(holdService.heldBy(dmDocument, true).contains(hold2)); + } + }); + } + + /** + * Given a piece of active content on hold + * When I try to remove the active content to the hold without permission + * Then an access denied exception is thrown + */ + public void testRemoveDocumentFromHoldFailsWithoutPermission() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(AccessDeniedException.class) + { + private NodeRef hold; + + public void given() + { + hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + holdService.addToHold(hold, dmDocument); + } + + public void when() + { + AuthenticationUtil.runAs( + (RunAsWork) () -> { + holdService.removeFromHold(hold, dmDocument); + return null; + }, recordsManagerName ); + } + }); + } + + /** + * Given a piece of active content on hold + * When I try to remove the active content to the hold without the remove hold capability + * Then an access denied exception is thrown + */ + public void testRemoveDocumentFromHoldFailsWithoutRemoveHoldPermission() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(AccessDeniedException.class, powerUserName, false) + { + private NodeRef hold; + + public void given() + { + //add powerUserPerson as manager in collaboration site to have write permissions on dmDocument + AuthenticationUtil.runAs( + (RunAsWork) () -> { + siteService.setMembership(collabSiteId, powerUserName, SiteModel.SITE_MANAGER); + hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate()); + holdService.addToHold(hold, dmDocument); + return null; + }, getAdminUserName()); + } + + public void when() + { + holdService.removeFromHold(hold, dmDocument); + } + }); + } +} diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspectUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspectUnitTest.java new file mode 100644 index 0000000000..6eb3bef240 --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/FrozenAspectUnitTest.java @@ -0,0 +1,126 @@ +/* + * #%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.model.rma.aspect; + +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASPECT_HELD_CHILDREN; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASPECT_RECORD; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_HELD_CHILDREN_COUNT; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.context.ApplicationContext; + +/** + * Test class for frozen aspect + * @author Ross Gale + * @since 3.2 + */ +public class FrozenAspectUnitTest +{ + @Mock + private NodeService nodeService; + + @Mock + private ApplicationContext applicationContext; + + @Mock + private ChildAssociationRef childAssociationRef; + + @Mock + private DictionaryService dictionaryService; + + @InjectMocks + private FrozenAspect frozenAspect; + + private NodeRef record = new NodeRef("workspace://record/node"); + private NodeRef folder = new NodeRef("workspace://folder/node"); + private NodeRef content = new NodeRef("workspace://content/node"); + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + when(nodeService.exists(record)).thenReturn(true); + when(nodeService.exists(content)).thenReturn(true); + when(nodeService.hasAspect(folder, ASPECT_HELD_CHILDREN)).thenReturn(true); + when(nodeService.getProperty(folder, PROP_HELD_CHILDREN_COUNT)).thenReturn(1); + when(applicationContext.getBean("dbNodeService")).thenReturn(nodeService); + when(nodeService.hasAspect(folder, ASPECT_HELD_CHILDREN)).thenReturn(true); + when(nodeService.getProperty(folder, PROP_HELD_CHILDREN_COUNT)).thenReturn(1); + } + + /** + * Test that the held count is reduced on a record folder after the frozen aspect is removed from a record + */ + @Test + public void testRemoveAspectForRecords() + { + when(nodeService.hasAspect(record, ASPECT_RECORD)).thenReturn(true); + when(nodeService.getPrimaryParent(record)).thenReturn(childAssociationRef); + when(childAssociationRef.getParentRef()).thenReturn(folder); + frozenAspect.onRemoveAspect(record, null); + verify(nodeService, times(1)).setProperty(folder, PROP_HELD_CHILDREN_COUNT, 0); + } + + /** + * Test that the held count is reduced on a folder after the frozen aspect is removed from a piece of content + */ + @Test + public void testRemoveAspectForContent() + { + when(nodeService.hasAspect(content, ASPECT_RECORD)).thenReturn(false); + when(nodeService.getType(content)).thenReturn(ContentModel.TYPE_CONTENT); + when(nodeService.getPrimaryParent(content)).thenReturn(childAssociationRef); + when(childAssociationRef.getParentRef()).thenReturn(folder); + frozenAspect.onRemoveAspect(content, null); + verify(nodeService, times(1)).setProperty(folder, PROP_HELD_CHILDREN_COUNT, 0); + } + + /** + * Test that the remove code is only ran for records or active content + */ + @Test + public void testRemoveAspectForContentDoesntUpdateForOtherTypes() + { + when(nodeService.hasAspect(content, ASPECT_RECORD)).thenReturn(false); + when(nodeService.getType(content)).thenReturn(ContentModel.TYPE_FOLDER); + when(dictionaryService.isSubClass(ContentModel.TYPE_FOLDER, ContentModel.TYPE_CONTENT)).thenReturn(false); + frozenAspect.onRemoveAspect(content, null); + verify(nodeService, times(0)).setProperty(folder, PROP_HELD_CHILDREN_COUNT, 0); + } +}