diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java index d1b5d5f907..2306424f57 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java @@ -702,6 +702,7 @@ public abstract class BaseAPI public enum RM_ACTIONS { EDIT_DISPOSITION_DATE("editDispositionActionAsOfDate"), + END_RETENTION("retain"), CUT_OFF("cutoff"), UNDO_CUT_OFF("undoCutoff"), TRANSFER("transfer"), diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java index a21d2b69f4..6abe3d69b9 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java @@ -47,6 +47,7 @@ import java.io.File; import org.alfresco.dataprep.CMISUtil; import org.alfresco.rest.core.JsonBodyGenerator; +import org.alfresco.rest.core.v0.BaseAPI.RM_ACTIONS; import org.alfresco.rest.rm.community.base.BaseRMRestTest; import org.alfresco.rest.rm.community.model.common.ReviewPeriod; import org.alfresco.rest.rm.community.model.record.Record; @@ -55,6 +56,7 @@ import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderProperties; import org.alfresco.rest.v0.HoldsAPI; +import org.alfresco.rest.v0.RMRolesAndActionsAPI; import org.alfresco.rest.v0.service.DispositionScheduleService; import org.alfresco.test.AlfrescoTest; import org.alfresco.utility.Utility; @@ -81,6 +83,7 @@ public class PreventActionsOnFrozenContentTests extends BaseRMRestTest private static FolderModel folderModel; private static RecordCategoryChild recordFolder; private static Record recordFrozen, recordNotHeld; + private static RecordCategory categoryWithRS; @Autowired private HoldsAPI holdsAPI; @@ -88,6 +91,9 @@ public class PreventActionsOnFrozenContentTests extends BaseRMRestTest @Autowired private DispositionScheduleService dispositionScheduleService; + @Autowired + private RMRolesAndActionsAPI rmRolesAndActionsAPI; + @BeforeClass (alwaysRun = true) public void preconditionForPreventActionsOnFrozenContent() throws Exception { @@ -276,12 +282,52 @@ public class PreventActionsOnFrozenContentTests extends BaseRMRestTest assertNotNull(folderWithRS.getProperties().getRecordSearchDispositionInstructions()); } + + /** + * Given a record category with a disposition schedule applied to records + * And the disposition schedule has a retain step immediately and destroy step immediately + * And a complete record added to one hold + * When I execute the retain action + * Then the action is executed + * And the record search disposition schedule properties are updated + * + * @throws Exception + */ + @Test + @AlfrescoTest (jira = "RM-6931") + public void retainActionOnFrozenHeldRecords() throws Exception + { + STEP("Add a category with a disposition schedule."); + categoryWithRS = createRootCategory(getRandomName("CategoryWithRS")); + dispositionScheduleService.createCategoryRetentionSchedule(categoryWithRS.getName(), true); + dispositionScheduleService.addRetainAfterPeriodStep(categoryWithRS.getName(), "immediately"); + dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(categoryWithRS.getName(), "immediately"); + + STEP("Create record folder with a record."); + RecordCategoryChild folder = createFolder(categoryWithRS.getId(), getRandomName("RecFolder")); + Record record = createElectronicRecord(folder.getId(), getRandomName("elRecord")); + completeRecord(record.getId()); + + STEP("Add the record to the hold"); + holdsAPI.addItemToHold(getAdminUser().getUsername(), getAdminUser().getPassword(), record.getId(), HOLD_ONE); + + STEP("Execute the retain action"); + rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getPassword(), record.getName(), + RM_ACTIONS.END_RETENTION); + + STEP("Check the record search disposition properties"); + Record recordUpdated = getRestAPIFactory().getRecordsAPI().getRecord(record.getId()); + assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName().contains(RM_ACTIONS.DESTROY.getAction())); + assertTrue(recordUpdated.getProperties().getRecordSearchDispositionPeriod().contains("immediately")); + } + @AfterClass (alwaysRun = true) public void cleanUpPreventActionsOnFrozenContent() throws Exception { holdsAPI.deleteHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD_ONE); dataSite.usingAdmin().deleteSite(testSite); getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(recordFolder.getParentId()); + getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(categoryWithRS.getId()); } } diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml index 7e9807e15b..d8e94ea0ba 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml @@ -18,6 +18,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties index 76742abc10..f5ecef752f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties @@ -56,4 +56,5 @@ log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info # Job debug # #log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug -log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info \ No newline at end of file +log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info +log4j.logger.org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAOImpl=info \ No newline at end of file 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 f182740df2..d999b109d7 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 @@ -251,6 +251,8 @@ + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml index 766c80c519..457b70432e 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml @@ -7,6 +7,10 @@ + + + + + + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml index 8144e44bf6..fd97f207cb 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml @@ -2,6 +2,11 @@ + + + + + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java index fb77f3efb6..b86b1b0c2a 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java @@ -33,6 +33,7 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -63,6 +64,9 @@ public class ContentDestructionComponent /** behaviour filter */ private BehaviourFilter behaviourFilter; + /** Utility class for duplicating content */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** indicates whether cleansing is enabled or not */ private boolean cleansingEnabled = false; @@ -138,6 +142,15 @@ public class ContentDestructionComponent this.behaviourFilter = behaviourFilter; } + /** + * Setter for content duplication utility class + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * @param cleansingEnabled true if cleansing enabled, false otherwise */ @@ -214,16 +227,19 @@ public class ContentDestructionComponent // get content data ContentData dataContent = (ContentData)entry.getValue(); - // if enabled cleanse content - if (isCleansingEnabled()) + if (!contentBinDuplicationUtility.hasAtLeastOneOtherReference(nodeRef)) { - // register for cleanse then immediate destruction - getEagerContentStoreCleaner().registerOrphanedContentUrlForCleansing(dataContent.getContentUrl()); - } - else - { - // register for immediate destruction - getEagerContentStoreCleaner().registerOrphanedContentUrl(dataContent.getContentUrl(), true); + // if enabled cleanse content + if (isCleansingEnabled()) + { + // register for cleanse then immediate destruction + getEagerContentStoreCleaner().registerOrphanedContentUrlForCleansing(dataContent.getContentUrl()); + } + else + { + // register for immediate destruction + getEagerContentStoreCleaner().registerOrphanedContentUrl(dataContent.getContentUrl(), true); + } } // clear the property diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java index b6e778e094..ed9f0ce31b 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java @@ -587,7 +587,16 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel } } - nodeService.setProperties(record, props); + try + { + //disable on properties update policy for the frozen aspect + frozenAspect.disableOnPropUpdateFrozenAspect(); + nodeService.setProperties(record, props); + } + finally + { + frozenAspect.enableOnPropUpdateFrozenAspect(); + } if (logger.isDebugEnabled()) { diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java index 88c29d63cd..a3c041fc9e 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java @@ -61,4 +61,10 @@ public interface RecordsManagementQueryDAO * @return list of distinct property values */ public Set getChildrenStringPropertyValues(NodeRef parent, QName property); + + /** + * @param contentUrl the URL of the content url entity + * @return Set a set of nodes that reference the given content url + */ + Set getNodeRefsWhichReferenceContentUrl(String contentUrl); } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java index 57e879cc0b..a13a6fcd44 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java @@ -30,17 +30,22 @@ package org.alfresco.module.org_alfresco_module_rm.query; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.domain.contentdata.ContentUrlEntity; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.version.Version2Model; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.mybatis.spring.SqlSessionTemplate; /** @@ -51,8 +56,14 @@ import org.mybatis.spring.SqlSessionTemplate; */ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, RecordsManagementModel { + /** logger */ + @SuppressWarnings ("unused") + private static final Log logger = LogFactory.getLog(RecordsManagementQueryDAOImpl.class); + + /** query names */ private static final String COUNT_IDENTIFIER = "alfresco.query.rm.select_CountRMIndentifier"; private static final String GET_CHILDREN_PROPERTY_VALUES = "select_GetStringPropertyValuesOfChildren"; + private static final String SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL = "select_NodeIdsWhichReferenceContentUrl"; /** SQL session template */ protected SqlSessionTemplate template; @@ -143,4 +154,90 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, return new HashSet<>(template.selectList(GET_CHILDREN_PROPERTY_VALUES, queryParams)); } + + /** + * Get a set of node reference which reference the provided content URL + * + * @return Set set of nodes that reference the provided content URL + * @param String contentUrl content URL + */ + @Override + public Set getNodeRefsWhichReferenceContentUrl(String contentUrl) + { + if (logger.isDebugEnabled()) + { + logger.debug("Getting nodes that reference content URL = " + contentUrl); + } + + // create the content URL entity used to query for nodes + ContentUrlEntity contentUrlEntity = new ContentUrlEntity(); + contentUrlEntity.setContentUrl(contentUrl.toLowerCase()); + + if (logger.isDebugEnabled()) + { + logger.debug("Executing query " + SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL); + } + + // Get all the node ids which reference the given content url + List nodeIds = template.selectList(SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL, contentUrlEntity); + + if (logger.isDebugEnabled()) + { + logger.debug("Query " + SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL + " returned " + nodeIds.size() + " results"); + } + + // create a set of uuids which reference the content url + Set nodesReferencingContentUrl = new HashSet(nodeIds.size()); + for (Long nodeId : nodeIds) + { + StringBuilder logMessage = null; + NodeRef nodeRefToAdd; + + if (nodeId != null && nodeDAO.exists(nodeId)) + { + if (logger.isDebugEnabled()) + { + logMessage = new StringBuilder("Adding noderef "); + } + + // if the referencing node is a version2Store reference to the content url, add the version 2 frozen node ref + NodeRef version2FrozenNodeRef = (NodeRef) nodeDAO.getNodeProperty(nodeId, Version2Model.PROP_QNAME_FROZEN_NODE_REF); + if (version2FrozenNodeRef != null && nodeDAO.exists(version2FrozenNodeRef)) + { + nodeRefToAdd = version2FrozenNodeRef; + + if (logger.isDebugEnabled()) + { + logMessage.append(nodeRefToAdd).append(" (from version)"); + } + } + + // add the node ref of the referencing node + else + { + nodeRefToAdd = nodeDAO.getNodeIdStatus(nodeId).getNodeRef(); + if (logger.isDebugEnabled()) + { + logMessage.append(nodeRefToAdd); + } + } + + nodesReferencingContentUrl.add(nodeRefToAdd); + + if (logger.isDebugEnabled()) + { + logger.debug(logMessage.toString()); + } + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Not adding " + nodeId + " (exist==false)"); + } + } + } + + return nodesReferencingContentUrl; + } } 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 index 417bd924a2..c1d6f31643 100644 --- 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 @@ -27,11 +27,15 @@ package org.alfresco.module.org_alfresco_module_rm.util; import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO; 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 java.util.Set; + /** * Utility class to duplicate the content of a node without triggering the audit or versioning behaviours * @author Ross Gale @@ -44,6 +48,14 @@ public class ContentBinDuplicationUtility extends ServiceBaseImpl */ private BehaviourFilter behaviourFilter; + /** + * Provides methods for accessing and transforming content. + */ + private ContentService contentService; + + /** Records Management Query DAO */ + private RecordsManagementQueryDAO recordsManagementQueryDAO; + /** * Setter for behaviour filter * @param behaviourFilter BehaviourFilter @@ -53,6 +65,44 @@ public class ContentBinDuplicationUtility extends ServiceBaseImpl this.behaviourFilter = behaviourFilter; } + /** + * Setter for content service + * @param contentService ContentService + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Setter for the Records Management QueryDAO + * + * @param recordsManagementQueryDAO The RM query DAO to set + */ + public void setRecordsManagementQueryDAO(RecordsManagementQueryDAO recordsManagementQueryDAO) + { + this.recordsManagementQueryDAO = recordsManagementQueryDAO; + } + + /** + * Determines whether the bin file for a given node has at least one other reference to it + * Will return true if the binary exists and is referenced by at least one other node + * @param nodeRef Node with the binary in question + * @return boolean for if the bin has at least one other reference to it + */ + public boolean hasAtLeastOneOtherReference(NodeRef nodeRef) + { + boolean hasAtLeastOneOtherReference = false; + String contentUrl = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT).getContentUrl(); + + Set referencesToContentNode = recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(contentUrl); + if (referencesToContentNode.size() > 1) + { + hasAtLeastOneOtherReference = true; + } + return hasAtLeastOneOtherReference; + } + /** * Duplicate the content of a node without triggering the audit or versioning behaviours * 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 index b5dcd55d56..df5844b62d 100644 --- 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 @@ -28,11 +28,18 @@ package org.alfresco.module.org_alfresco_module_rm.util; import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASPECT_ARCHIVED; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; @@ -52,6 +59,9 @@ import org.mockito.MockitoAnnotations; */ public class ContentBinDuplicationUtilityUnitTest { + private final static NodeRef NODE_REF = new NodeRef("some://test/noderef"); + private final static NodeRef NODE_REF2 = new NodeRef("some://test/anothernoderef"); + private final static String CONTENT_URL = "someContentUrl"; @Mock private ContentService mockContentService; @@ -67,6 +77,9 @@ public class ContentBinDuplicationUtilityUnitTest @Mock private NodeService mockNodeService; + @Mock + private RecordsManagementQueryDAO recordsManagementQueryDAO; + @InjectMocks private ContentBinDuplicationUtility contentBinDuplicationUtility; @@ -82,10 +95,9 @@ public class ContentBinDuplicationUtilityUnitTest @Test public void testContentUrlIsUpdated() { - NodeRef nodeRef = new NodeRef("some://test/noderef"); - when(mockContentService.getReader(nodeRef, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); - when(mockContentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true)).thenReturn(mockContentWriter); - contentBinDuplicationUtility.duplicate(nodeRef); + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); + when(mockContentService.getWriter(NODE_REF, ContentModel.PROP_CONTENT, true)).thenReturn(mockContentWriter); + contentBinDuplicationUtility.duplicate(NODE_REF); verify(mockContentWriter, times(1)).putContent(mockContentReader); checkBehaviours(1); } @@ -96,9 +108,8 @@ public class ContentBinDuplicationUtilityUnitTest @Test public void testDuplicationDoesntHappenWithNoContent() { - NodeRef nodeRef = new NodeRef("some://test/noderef"); - when(mockContentService.getReader(nodeRef, ContentModel.PROP_CONTENT)).thenReturn(null); - contentBinDuplicationUtility.duplicate(nodeRef); + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(null); + contentBinDuplicationUtility.duplicate(NODE_REF); verify(mockContentWriter, times(0)).putContent(mockContentReader); checkBehaviours(1); } @@ -118,6 +129,52 @@ public class ContentBinDuplicationUtilityUnitTest verify(mockContentReader, times(0)).getReader(); checkBehaviours(0); } + /** + * Test hasAtLeastOneOtherReference returns true when node has another reference to it + */ + @Test + public void testHasAtLeastOneOtherReference() + { + Set multipleReferences = new HashSet<>(); + Collections.addAll(multipleReferences, NODE_REF, NODE_REF2); + + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(multipleReferences); + + assertTrue(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + + /** + * Test hasAtLeastOneOtherReference returns false when node has no other reference to it other than its own content ref + */ + @Test + public void testHasNoOtherReference() + { + Set singleReference = Collections.singleton(NODE_REF); + + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(singleReference); + + assertFalse(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + + /** + * Test hasAtLeastOneOtherReference returns false when node has no references to it at all + */ + @Test + public void testHasNoReferences() + { + Set noReferences = Collections. emptySet(); + + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); + when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(noReferences); + + assertFalse(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + /** * Check that the behaviours are disabled and re-enabled the correct number of times * @param times the times the behaviours should be called