From c6075654c85d46fbbf4fff4ba0ed50cfbe7d4237 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Mon, 27 Oct 2014 03:01:34 +0000 Subject: [PATCH] RM-1671: Automatic "Versioned By" Relationship * as versions are record they are automatically connected to previous record version via the "versions" relationship in the file plan. * unit and integration tests * meta-data sotred on record about origional version, for example version label and description git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@89170 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../model/recordableVersionModel.xml | 42 ++++++- .../rm-version-context.xml | 1 + .../record/RecordServiceImpl.java | 3 + .../version/RecordableVersionModel.java | 6 + .../version/RecordableVersionServiceImpl.java | 78 ++++++++++-- .../version/AdHocRecordableVersions.java | 10 +- .../version/AutoRecordableVersions.java | 115 ++++++++++++++++-- .../version/RecordableVersionsBaseTest.java | 20 ++- .../RecordableVersionServiceImplUnitTest.java | 1 - 9 files changed, 251 insertions(+), 25 deletions(-) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml index f0e810723e..b3865d3518 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml @@ -30,6 +30,13 @@ + + + + + + + Recordable Version Policy List @@ -45,13 +52,18 @@ + + + + File Plan d:noderef + Recordable Version Policy d:text @@ -64,13 +76,20 @@ + + + + + Record Node Reference d:noderef - + + + Frozen Owner d:text @@ -78,6 +97,27 @@ + + + + + + + + d:noderef + + + + + d:text + + + + d:text + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml index 9571419fd0..5245ccc2ea 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml @@ -20,6 +20,7 @@ + diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 4b7dc3702d..9a621f42ea 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -989,6 +989,9 @@ public class RecordServiceImpl extends BaseBehaviourBean props.put(PROP_IDENTIFIER, recordId); props.put(PROP_ORIGIONAL_NAME, name); nodeService.addAspect(document, RecordsManagementModel.ASPECT_RECORD, props); + + // remove versionable aspect(s) + nodeService.removeAspect(document, RecordableVersionModel.ASPECT_VERSIONABLE); } catch (FileNotFoundException e) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java index 323f3acfb0..f21b28043d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java @@ -41,4 +41,10 @@ public interface RecordableVersionModel QName ASPECT_RECORDED_VERSION = QName.createQName(RMV_URI, "recordedVersion"); QName PROP_RECORD_NODE_REF = QName.createQName(RMV_URI, "recordNodeRef"); QName PROP_FROZEN_OWNER = QName.createQName(RMV_URI, "frozenOwner"); + + /** version record aspect */ + QName ASPECT_VERSION_RECORD = QName.createQName(RMV_URI, "versionRecord"); + QName PROP_VERSIONED_NODEREF = QName.createQName(RMV_URI, "versionedNodeRef"); + QName PROP_VERSION_LABEL = QName.createQName(RMV_URI, "versionLabel"); + QName PROP_VERSION_DESCRIPTION = QName.createQName(RMV_URI, "versionDescription"); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java index 5ec960d6d9..374a06db1f 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java @@ -19,6 +19,7 @@ package org.alfresco.module.org_alfresco_module_rm.version; import java.io.Serializable; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -29,6 +30,7 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.repo.policy.PolicyScope; @@ -46,8 +48,10 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.version.ReservedVersionNameException; import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -66,6 +70,9 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** key used to indicate a recordable version */ public static final String KEY_RECORDABLE_VERSION = "recordable-version"; public static final String KEY_FILE_PLAN = "file-plan"; + + /** version record property */ + public static final String PROP_VERSION_RECORD = "RecordVersion"; /** file plan service */ protected FilePlanService filePlanService; @@ -84,6 +91,9 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** authentication util helper */ protected AuthenticationUtil authenticationUtil; + + /** relationship service */ + protected RelationshipService relationshipService; /** * @param filePlanService file plan service @@ -132,6 +142,14 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl { this.authenticationUtil = authenticationUtil; } + + /** + * @param relationshipService relationship service + */ + public void setRelationshipService(RelationshipService relationshipService) + { + this.relationshipService = relationshipService; + } /** * @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int) @@ -265,13 +283,13 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** * Creates a new recorded version * - * @param sourceTypeRef - * @param versionHistoryRef - * @param standardVersionProperties - * @param versionProperties - * @param versionNumber - * @param nodeDetails - * @return + * @param sourceTypeRef source type name + * @param versionHistoryRef version history reference + * @param standardVersionProperties standard version properties + * @param versionProperties version properties + * @param versionNumber version number + * @param nodeDetails policy scope + * @return {@link NodeRef} record version */ protected NodeRef createNewRecordedVersion(QName sourceTypeRef, NodeRef versionHistoryRef, @@ -302,13 +320,51 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl final NodeRef nodeRef = (NodeRef)standardVersionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF); // create record - NodeRef record = createRecord(nodeRef, filePlan); + final NodeRef record = createRecord(nodeRef, filePlan); + + // apply version record aspect to record + PropertyMap versionRecordProps = new PropertyMap(3); + versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef); + versionRecordProps.put(RecordableVersionModel.PROP_VERSION_LABEL, + standardVersionProperties.get( + QName.createQName(Version2Model.NAMESPACE_URI, + Version2Model.PROP_VERSION_LABEL))); + versionRecordProps.put(RecordableVersionModel.PROP_VERSION_DESCRIPTION, + standardVersionProperties.get( + QName.createQName(Version2Model.NAMESPACE_URI, + Version2Model.PROP_VERSION_DESCRIPTION))); + nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps); + + // wire record up to previous record + VersionHistory versionHistory = getVersionHistory(nodeRef); + if (versionHistory != null) + { + Collection previousVersions = versionHistory.getAllVersions(); + for (Version previousVersion : previousVersions) + { + // look for the associated record + final NodeRef previousRecord = (NodeRef)previousVersion.getVersionProperties().get(PROP_VERSION_RECORD); + if (previousRecord != null) + { + authenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + // indicate that the new record versions the previous record + relationshipService.addRelationship("versions", record, previousRecord); + return null; + } + }); + break; + } + } + } // create version nodeRef ChildAssociationRef childAssocRef = dbNodeService.createNode( versionHistoryRef, Version2Model.CHILD_QNAME_VERSIONS, - // TODO - testing - note: all children (of a versioned node) will have the same version number, maybe replace with a version sequence of some sort 001-...00n QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.CHILD_VERSIONS + "-" + versionNumber), sourceTypeRef, null); @@ -393,7 +449,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl // create a copy of the original state and add it to the unfiled record container FileInfo recordInfo = fileFolderService.copy(nodeRef, unfiledRecordFolder, null); - record = recordInfo.getNodeRef(); + record = recordInfo.getNodeRef(); // remove added copy assocs List recordAssocs = dbNodeService.getTargetAssocs(record, ContentModel.ASSOC_ORIGINAL); @@ -465,7 +521,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl NodeRef record = (NodeRef)dbNodeService.getProperty(versionRef, PROP_RECORD_NODE_REF); if (record != null) { - version.getVersionProperties().put("RecordVersion", record); + version.getVersionProperties().put(PROP_VERSION_RECORD, record); } return version; diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java index e04fa328b9..a529df9f8c 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java @@ -126,7 +126,13 @@ public class AdHocRecordableVersions extends RecordableVersionsBaseTest { // create version versionService.createVersion(dmDocument, versionProperties); - } + } + + public void then() + { + // check that the record has been recorded + checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); + } }); } @@ -157,7 +163,7 @@ public class AdHocRecordableVersions extends RecordableVersionsBaseTest Version version = versionService.createVersion(dmDocument, versionProperties); // add custom meta-data to record - NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); + NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); assertNotNull(record); recordService.addRecordType(record, TestModel.ASPECT_RECORD_METADATA); nodeService.setProperty(record, TestModel.PROPERTY_RECORD_METADATA, "Peter Wetherall"); diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java index 7ee66178ed..23299e4262 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java @@ -18,10 +18,16 @@ */ package org.alfresco.module.org_alfresco_module_rm.test.integration.version; +import java.util.Set; + import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.relationship.Relationship; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionPolicy; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; import org.alfresco.util.PropertyMap; /** @@ -32,24 +38,30 @@ import org.alfresco.util.PropertyMap; */ public class AutoRecordableVersions extends RecordableVersionsBaseTest { + /** example content */ public final static String MY_NEW_CONTENT = "this is some new content that I have changed to trigger auto version"; + /** + * Given that all revisions will be recorded, + * When I update the content of a document, + * Then a recorded version will be created + */ public void testAutoVersionRecordAllRevisions() { doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) { public void given() throws Exception { - // make the node versionable - PropertyMap versionableProperties = new PropertyMap(1); - versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); - nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); - // set the recordable version policy PropertyMap recordableVersionProperties = new PropertyMap(1); recordableVersionProperties.put(PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); recordableVersionProperties.put(PROP_FILE_PLAN, filePlan); - nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + + // make the node versionable + PropertyMap versionableProperties = new PropertyMap(1); + versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); } public void when() @@ -62,9 +74,98 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest public void then() { // check that the record has been recorded - checkRecordedVersion(dmDocument, null, "0.2"); + checkRecordedVersion(dmDocument, null, "0.1"); } }); } + /** + * Given that all revisions will be automatically recorded, + * When I update a document 3 times, + * Then all 3 created records will be related together using the "VersionedBy" relationship + */ + public void testVersionRecordsRelated() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator, false) + { + /** given **/ + public void given() throws Exception + { + doTestInTransaction(new VoidTest() + { + @Override + public void runImpl() throws Exception + { + // set the recordable version policy + PropertyMap recordableVersionProperties = new PropertyMap(1); + recordableVersionProperties.put(PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); + recordableVersionProperties.put(PROP_FILE_PLAN, filePlan); + nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + + // make the node versionable + PropertyMap versionableProperties = new PropertyMap(1); + versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); + } + }); + } + + /** when **/ + public void when() + { + // update the content 3 times + updateContent(); + updateContent(); + updateContent(); + } + + /** then */ + public void then() + { + doTestInTransaction(new VoidTest() + { + @Override + public void runImpl() throws Exception + { + // check that the record has been recorded + checkRecordedVersion(dmDocument, null, "0.3"); + + Version version = versionService.getCurrentVersion(dmDocument); + NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + + boolean foundPrevious = false; + Set relationships = relationshipService.getRelationshipsFrom(record); + assertNotNull(relationships); + assertEquals(1, relationships.size()); + for (Relationship relationship : relationships) + { + if (relationship.getUniqueName().equals("versions")) + { + NodeRef previousVersionRecord = relationship.getTarget(); + assertNotNull(previousVersionRecord); + foundPrevious = true; + } + } + assertTrue(foundPrevious); + } + }); + } + + /** + * Helper method to update content of dmDocument + */ + private void updateContent() + { + doTestInTransaction(new VoidTest() + { + @Override + public void runImpl() throws Exception + { + ContentWriter writer = contentService.getWriter(dmDocument, ContentModel.PROP_CONTENT, true); + writer.putContent(MY_NEW_CONTENT); + } + }); + } + }); + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java index f7a42b1960..a1420cbc01 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java @@ -27,6 +27,7 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -122,12 +123,21 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen checkAspects(frozen, beforeAspects); // record version node reference is available on version - NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); + NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); assertNotNull(record); + // check that the version record information has been added + assertTrue(nodeService.hasAspect(record, ASPECT_VERSION_RECORD)); + assertEquals(versionLabel, nodeService.getProperty(record, RecordableVersionModel.PROP_VERSION_LABEL)); + assertEquals(description, nodeService.getProperty(record, RecordableVersionModel.PROP_VERSION_DESCRIPTION)); + // record version is an unfiled record assertTrue(recordService.isRecord(record)); assertFalse(recordService.isFiled(record)); + + // check that the created record does not have either of the versionable aspects + assertFalse(nodeService.hasAspect(record, ContentModel.ASPECT_VERSIONABLE)); + assertFalse(nodeService.hasAspect(record, RecordableVersionModel.ASPECT_VERSIONABLE)); // check the version history VersionHistory versionHistory = versionService.getVersionHistory(document); @@ -152,7 +162,7 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen assertEquals(versionLabel, version.getVersionLabel()); // record version node reference is available on version - NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); + NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); assertNull(record); // check the version history @@ -179,7 +189,10 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen cloneFrozenProperties.remove(beforePropertyName); } else if (!PROP_FILE_PLAN.equals(beforePropertyName) && - !PROP_RECORDABLE_VERSION_POLICY.equals(beforePropertyName)) + !PROP_RECORDABLE_VERSION_POLICY.equals(beforePropertyName) && + !ContentModel.PROP_AUTO_VERSION_PROPS.equals(beforePropertyName) && + !ContentModel.PROP_AUTO_VERSION.equals(beforePropertyName) && + !ContentModel.PROP_INITIAL_VERSION.equals(beforePropertyName)) { fail("Property missing from frozen state .. " + beforePropertyName); } @@ -200,6 +213,7 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen Set frozenAspects = nodeService.getAspects(frozen); cloneBeforeAspects.removeAll(frozenAspects); cloneBeforeAspects.remove(RecordableVersionModel.ASPECT_VERSIONABLE); + cloneBeforeAspects.remove(ContentModel.ASPECT_VERSIONABLE); if (!cloneBeforeAspects.isEmpty()) { fail("Aspects not present in frozen state. " + cloneBeforeAspects.toString()); diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java index 4b3adc8462..169d55eded 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java @@ -70,7 +70,6 @@ public class RecordableVersionServiceImplUnitTest extends BaseUnitTest private Map versionProperties; /** mocked services */ - @SuppressWarnings("unused") private @Mock(name="versionMigrator") VersionMigrator mockedVersionMigrator; private @Mock(name="dbNodeService") NodeService mockedDbNodeService;