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 6158bea0b5..a4587c061f 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 @@ -94,6 +94,12 @@ d:text + + + Destroyed + d:boolean + false> + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml index 184ba003c1..342e5dff92 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -259,6 +259,7 @@ + ${rm.ghosting.enabled} diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index f79173a8a2..7ddb3f28b2 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -1060,6 +1060,7 @@ + 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 ce01a26573..c9921401f7 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 @@ -11,6 +11,12 @@ + + + + + + @@ -63,7 +69,11 @@ diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.js index 00875b74eb..6f7babb711 100644 --- a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.js +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.js @@ -62,7 +62,7 @@ function main() { var nodeRef = args["nodeRef"], node = search.findNode(nodeRef), - versionHistory, version, p, recordNodeRef; + versionHistory, version, p, recordNodeRef, isRecordedVersionDestroyed; if (node != null) { @@ -73,11 +73,14 @@ function main() { version = versionHistory[i]; p = getPerson(version.creator); + recordNodeRef = version.getVersionProperty("recordNodeRef"); + isRecordedVersionDestroyed = version.getVersionProperty("RecordedVersionDestroyed"); + versions[versions.length] = { nodeRef: version.node.nodeRef.toString(), - name: version.node.name, + name: (isRecordedVersionDestroyed == true) ? "" : version.node.name, label: version.label, description: version.description, createdDate: version.createdDate, @@ -87,7 +90,8 @@ function main() firstName: p.firstName, lastName: p.lastName }, - recordNodeRef: recordNodeRef ? recordNodeRef.toString() : "" + recordNodeRef: recordNodeRef ? recordNodeRef.toString() : "", + isRecordedVersionDestroyed: isRecordedVersionDestroyed }; } } @@ -107,7 +111,8 @@ function main() firstName: p.firstName, lastName: p.lastName }, - recordNodeRef: "" + recordNodeRef: "", + isRecordedVersionDestroyed: false }; } } diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.json.ftl b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.json.ftl index 84b3ecc3de..773b1421e0 100644 --- a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.json.ftl +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/version/rm-version.get.json.ftl @@ -14,7 +14,8 @@ "firstName": "${v.creator.firstName!""}", "lastName": "${v.creator.lastName!""}" }, - "recordNodeRef": "${v.recordNodeRef}" + "recordNodeRef": "${v.recordNodeRef}", + "isRecordedVersionDestroyed": ${v.isRecordedVersionDestroyed?c} }<#if (v_has_next)>, ] diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java index aa82b65efa..b073b2c7a1 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DestroyAction.java @@ -27,9 +27,11 @@ import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.content.ContentDestructionComponent; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.version.Version; import org.alfresco.service.namespace.QName; import org.apache.commons.lang.StringUtils; @@ -48,6 +50,9 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase /** Capability service */ private CapabilityService capabilityService; + + /** Recordable version service */ + private RecordableVersionService recordableVersionService; /** Indicates if ghosting is enabled or not */ private boolean ghostingEnabled = true; @@ -67,6 +72,14 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase { this.capabilityService = capabilityService; } + + /** + * @param recordableVersionService recordable version service + */ + public void setRecordableVersionService(RecordableVersionService recordableVersionService) + { + this.recordableVersionService = recordableVersionService; + } /** * @param ghostingEnabled true if ghosting is enabled, false otherwise @@ -140,6 +153,13 @@ public class DestroyAction extends RMDispositionActionExecuterAbstractBase { if (isGhostOnDestroySetForAction(action, record)) { + // mark version as destroyed + Version version = recordableVersionService.getRecordedVersion(record); + if (version != null) + { + recordableVersionService.destroyRecordedVersion(version); + } + // Add the ghosted aspect getNodeService().addAspect(record, ASPECT_GHOSTED, null); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java index 8f8f8019c9..e43845deea 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java @@ -36,7 +36,6 @@ import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -71,7 +70,7 @@ public class RecordAspect extends AbstractDisposableItem /** record service */ protected RecordService recordService; - + /** * @param extendedSecurityService extended security service */ @@ -110,7 +109,7 @@ public class RecordAspect extends AbstractDisposableItem ) public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean bNew) { - AuthenticationUtil.runAsSystem(new RunAsWork() + authenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() @@ -173,7 +172,7 @@ public class RecordAspect extends AbstractDisposableItem // Deal with versioned records if (reference.equals(CUSTOM_REF_VERSIONS)) { - AuthenticationUtil.runAsSystem(new RunAsWork() + authenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() @@ -240,7 +239,7 @@ public class RecordAspect extends AbstractDisposableItem isFilePlanComponent(oldChildAssocRef.getParentRef())) { final NodeRef record = newChildAssocRef.getChildRef(); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAs(new RunAsWork() { public Object doWork() { @@ -256,7 +255,7 @@ public class RecordAspect extends AbstractDisposableItem return null; } - }, AuthenticationUtil.getAdminUserName()); + }, authenticationUtil.getAdminUserName()); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java new file mode 100644 index 0000000000..3eb9e0865d --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.model.rma.aspect; + +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; +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.version.RecordableVersionService; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; + +/** + * rmv:versionRecord behaviour bean + * + * @author Roy Wetherall + * @since 2.3.1 + */ +@BehaviourBean +( + defaultType = "rmv:versionRecord" +) +public class VersionRecordAspect extends BaseBehaviourBean + implements NodeServicePolicies.BeforeDeleteNodePolicy +{ + /** recordable version service */ + private RecordableVersionService recordableVersionService; + + /** relationship service */ + private RelationshipService relationshipService; + + /** + * @param recordableVersionService recordable version service + */ + public void setRecordableVersionService(RecordableVersionService recordableVersionService) + { + this.recordableVersionService = recordableVersionService; + } + + /** + * @param relationshipService relationship service + */ + public void setRelationshipService(RelationshipService relationshipService) + { + this.relationshipService = relationshipService; + } + + /** + * If the record is a version record then delete the associated version entry + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + @Behaviour (kind = BehaviourKind.CLASS) + public void beforeDeleteNode(final NodeRef nodeRef) + { + final Version version = recordableVersionService.getRecordedVersion(nodeRef); + if (version != null) + { + authenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + behaviourFilter.disableBehaviour(); + try + { + // mark the associated version as destroyed + recordableVersionService.destroyRecordedVersion(version); + + // re-organise the versions relationships ... + // if there is only one "to" reference since a version can only have one predecessor + Set tos = relationshipService.getRelationshipsTo(nodeRef, RelationshipService.RELATIONSHIP_VERSIONS); + if (!tos.isEmpty() && tos.size() == 1) + { + // if there is some "from" references + Set froms = relationshipService.getRelationshipsFrom(nodeRef, RelationshipService.RELATIONSHIP_VERSIONS); + if (!froms.isEmpty()) + { + // get predecessor version relationship + Relationship to = tos.iterator().next(); + + for (Relationship from : froms) + { + // point the "to" the all the "from's" + relationshipService.addRelationship(RelationshipService.RELATIONSHIP_VERSIONS, to.getSource(), from.getTarget()); + } + } + } + } + finally + { + behaviourFilter.enableBehaviour(); + } + + return null; + } + }); + } + } +} 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 dbccfd6eec..66ed9c9e34 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 @@ -19,9 +19,6 @@ package org.alfresco.module.org_alfresco_module_rm.record; import static com.google.common.collect.Lists.newArrayList; -import static org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel.PROP_VERSIONED_NODEREF; -import static org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel.PROP_VERSION_LABEL; -import static org.apache.commons.lang.StringUtils.isNotBlank; import java.io.Serializable; import java.util.ArrayList; @@ -62,7 +59,7 @@ import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.role.Role; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; -import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.ClassPolicyDelegate; @@ -123,8 +120,7 @@ public class RecordServiceImpl extends BaseBehaviourBean NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnAddAspectPolicy, NodeServicePolicies.OnRemoveAspectPolicy, - NodeServicePolicies.OnUpdatePropertiesPolicy, - NodeServicePolicies.BeforeDeleteNodePolicy + NodeServicePolicies.OnUpdatePropertiesPolicy { /** Logger */ private static Log logger = LogFactory.getLog(RecordServiceImpl.class); @@ -232,6 +228,9 @@ public class RecordServiceImpl extends BaseBehaviourBean /** records management container type */ private RecordsManagementContainerType recordsManagementContainerType; + + /** recordable version service */ + private RecordableVersionService recordableVersionService; /** list of available record meta-data aspects and the file plan types the are applicable to */ private Map> recordMetaDataAspects; @@ -385,6 +384,14 @@ public class RecordServiceImpl extends BaseBehaviourBean { this.recordsManagementContainerType = recordsManagementContainerType; } + + /** + * @param recordableVersionService recordable version service + */ + public void setRecordableVersionService(RecordableVersionService recordableVersionService) + { + this.recordableVersionService = recordableVersionService; + } /** * Init method @@ -1034,7 +1041,19 @@ public class RecordServiceImpl extends BaseBehaviourBean { recordsManagementContainerType.enable(); } - + + // if versionable, then remove without destroying version history, + // because it is being shared with the originating document + behaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + try + { + nodeService.removeAspect(record, ContentModel.ASPECT_VERSIONABLE); + } + finally + { + behaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); + } + // make record makeRecord(record); @@ -1092,7 +1111,7 @@ public class RecordServiceImpl extends BaseBehaviourBean for (Version previousVersion : previousVersions) { // look for the associated record - final NodeRef previousRecord = (NodeRef)previousVersion.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + final NodeRef previousRecord = recordableVersionService.getVersionRecord(previousVersion); if (previousRecord != null) { versionRecord = previousRecord; @@ -1819,38 +1838,5 @@ public class RecordServiceImpl extends BaseBehaviourBean // can only unlink a record from a record folder throw new RecordLinkRuntimeException("Can only unlink a record from a record folder."); } - } - - /** - * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) - */ - @Override - @Behaviour - ( - kind = BehaviourKind.CLASS, - type = "rma:record" - ) - public void beforeDeleteNode(NodeRef nodeRef) - { - final NodeRef versionedNodeRef = (NodeRef) nodeService.getProperty(nodeRef, PROP_VERSIONED_NODEREF); - if (versionedNodeRef != null) - { - String versionLabel = (String) nodeService.getProperty(nodeRef, PROP_VERSION_LABEL); - if (isNotBlank(versionLabel)) - { - final Version version = versionService.getVersionHistory(versionedNodeRef).getVersion(versionLabel); - - AuthenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() - { - versionService.deleteVersion(versionedNodeRef, version); - - return null; - } - }); - } - } - } + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipImpl.java index 118f83a197..574a804cf5 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipImpl.java @@ -21,6 +21,8 @@ package org.alfresco.module.org_alfresco_module_rm.relationship; import static org.alfresco.util.ParameterCheck.mandatory; import static org.alfresco.util.ParameterCheck.mandatoryString; +import java.io.Serializable; + import org.alfresco.service.cmr.repository.NodeRef; /** @@ -29,8 +31,11 @@ import org.alfresco.service.cmr.repository.NodeRef; * @author Tuna Aksoy * @since 2.3 */ -public class RelationshipImpl implements Relationship +public class RelationshipImpl implements Relationship, Serializable { + /** serial UID */ + private static final long serialVersionUID = 9120649510198344978L; + /** The unique name of the relationship */ private String uniqueName; @@ -114,4 +119,39 @@ public class RelationshipImpl implements Relationship { this.target = target; } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj instanceof Relationship) + { + RelationshipImpl that = (RelationshipImpl) obj; + return (this.uniqueName.equals(that.uniqueName) + && this.source.equals(that.source) + && this.target.equals(that.target)); + } + else + { + return false; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + int prime = 31; + int result = prime + uniqueName.hashCode(); + result = (prime*result) + source.hashCode(); + return (prime*result) + target.hashCode(); + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipService.java index 0a619f53d1..dcdab7b285 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipService.java @@ -30,6 +30,9 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public interface RelationshipService { + /** System relationship names */ + static final String RELATIONSHIP_VERSIONS = "versions"; + /** * Gets all the existing relationship definitions * @@ -85,14 +88,42 @@ public interface RelationshipService * @return All relationships that come out from the given node reference */ Set getRelationshipsFrom(NodeRef nodeRef); - + /** - * Gets all the relationships that go in to the given node reference + * Gets all the relationships that come out from the given node reference + * that match the a given name filter. + *

+ * Exact match only. * * @param nodeRef The node reference - * @return All relationships that go in to the given node reference + * @param nameFilter Name filter for results + * @return All relationships that come out from the given node reference + * + * @since 2.3.1 + */ + Set getRelationshipsFrom(NodeRef nodeRef, String nameFilter); + + /** + * Gets all the relationships that go into the given node reference + * + * @param nodeRef The node reference + * @return All relationships that go into the given node reference */ Set getRelationshipsTo(NodeRef nodeRef); + + /** + * Gets all the relationships that go into the given node reference + * that match the a given name filter. + *

+ * Exact match only. + * + * @param nodeRef The node reference + * @param nameFilter Name filter for results + * @return All relationships that go into the given node reference + * + * @since 2.3.1 + */ + Set getRelationshipsTo(NodeRef nodeRef, String nameFilter); /** * Adds a relationship from the given node source diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipServiceImpl.java index 92b4fa7be4..df7487e6ad 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/relationship/RelationshipServiceImpl.java @@ -376,16 +376,25 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen */ @Override public Set getRelationshipsFrom(NodeRef nodeRef) + { + return getRelationshipsFrom(nodeRef, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService#getRelationshipsFrom(org.alfresco.service.cmr.repository.NodeRef, String) + */ + @Override + public Set getRelationshipsFrom(NodeRef nodeRef, String nameFilter) { mandatory("nodeRef", nodeRef); Set relationships = new HashSet(); List customReferencesFrom = getNodeService().getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL); - relationships.addAll(generateRelationshipFromAssociationRef(customReferencesFrom)); + relationships.addAll(generateRelationshipFromAssociationRef(customReferencesFrom, nameFilter)); List customChildReferences = getNodeService().getChildAssocs(nodeRef); - relationships.addAll(generateRelationshipFromParentChildAssociationRef(customChildReferences)); + relationships.addAll(generateRelationshipFromParentChildAssociationRef(customChildReferences, nameFilter)); return relationships; } @@ -395,20 +404,29 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen */ @Override public Set getRelationshipsTo(NodeRef nodeRef) + { + return getRelationshipsTo(nodeRef, null); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService#getRelationshipsTo(org.alfresco.service.cmr.repository.NodeRef, String) + */ + @Override + public Set getRelationshipsTo(NodeRef nodeRef, String nameFilter) { mandatory("nodeRef", nodeRef); Set relationships = new HashSet(); List customReferencesTo = getNodeService().getSourceAssocs(nodeRef, RegexQNamePattern.MATCH_ALL); - relationships.addAll(generateRelationshipFromAssociationRef(customReferencesTo)); + relationships.addAll(generateRelationshipFromAssociationRef(customReferencesTo, nameFilter)); List customParentReferences = getNodeService().getParentAssocs(nodeRef); - relationships.addAll(generateRelationshipFromParentChildAssociationRef(customParentReferences)); + relationships.addAll(generateRelationshipFromParentChildAssociationRef(customParentReferences, nameFilter)); return relationships; } - + /** * @see org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService#addRelationship(java.lang.String, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) */ @@ -418,7 +436,19 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen mandatoryString("uniqueName", uniqueName); mandatory("source", source); mandatory("target", target); - + + // check the source node exists + if (!getNodeService().exists(source)) + { + throw new AlfrescoRuntimeException("Can't create relationship '" + uniqueName + "', because source node doesn't exist."); + } + + // check the target node exists + if (!getNodeService().exists(target)) + { + throw new AlfrescoRuntimeException("Can't create relationship " + uniqueName + ", because target node doesn't exist."); + } + if (getNodeService().hasAspect(target, ASPECT_FROZEN)) { StringBuilder sb = new StringBuilder(); @@ -622,14 +652,15 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen * @param associationRefs Association references * @return Relationships generated from the given association references */ - private Set generateRelationshipFromAssociationRef(List associationRefs) + private Set generateRelationshipFromAssociationRef(List associationRefs, String nameFilter) { Set relationships = new HashSet(); for (AssociationRef associationRef : associationRefs) { String uniqueName = associationRef.getTypeQName().getLocalName(); - if (existsRelationshipDefinition(uniqueName)) + if (existsRelationshipDefinition(uniqueName) && + (nameFilter == null || uniqueName.equals(nameFilter))) { NodeRef from = associationRef.getSourceRef(); NodeRef to = associationRef.getTargetRef(); @@ -646,14 +677,15 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen * @param childAssociationRefs Child association references * @return Relationships generated from the given child association references */ - private Set generateRelationshipFromParentChildAssociationRef(List childAssociationRefs) + private Set generateRelationshipFromParentChildAssociationRef(List childAssociationRefs, String nameFilter) { Set relationships = new HashSet(); for (ChildAssociationRef childAssociationRef : childAssociationRefs) { String uniqueName = childAssociationRef.getQName().getLocalName(); - if (existsRelationshipDefinition(uniqueName)) + if (existsRelationshipDefinition(uniqueName)&& + (nameFilter == null || uniqueName.equals(nameFilter))) { NodeRef from = childAssociationRef.getParentRef(); NodeRef to = childAssociationRef.getChildRef(); 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 f21b28043d..180b191bfd 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,6 +41,7 @@ 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"); + QName PROP_DESTROYED = QName.createQName(RMV_URI, "destroyed"); /** version record aspect */ QName ASPECT_VERSION_RECORD = QName.createQName(RMV_URI, "versionRecord"); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java index c90dc17da9..78759b8713 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java @@ -21,6 +21,7 @@ package org.alfresco.module.org_alfresco_module_rm.version; import static org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl.RECORD_MODEL_URIS; import java.io.Serializable; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -181,6 +182,7 @@ public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl /** * @see org.alfresco.repo.version.Node2ServiceImpl#getAspects(org.alfresco.service.cmr.repository.NodeRef) */ + @SuppressWarnings("unchecked") @Override public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException { @@ -190,8 +192,15 @@ public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl if (dbNodeService.hasAspect(converted, ASPECT_RECORDED_VERSION)) { NodeRef record = (NodeRef)dbNodeService.getProperty(converted, PROP_RECORD_NODE_REF); - Set aspects = dbNodeService.getAspects(record); - return processAspects(aspects); + if (dbNodeService.exists(record)) + { + Set aspects = dbNodeService.getAspects(record); + return processAspects(aspects); + } + else + { + return (Set)Collections.EMPTY_SET; + } } else { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java index e5b0c8c6f2..15f3757333 100755 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java @@ -47,6 +47,23 @@ public interface RecordableVersionService */ boolean isRecordedVersion(Version version); + /** + * If the version is a recorded version, gets the related version + * record. + * + * @param version version + * @return NodeRef node reference of version record + */ + NodeRef getVersionRecord(Version version); + + /** + * Gets the version that relates to the version record + * + * @param versionRecord version record node reference + * @return Version version or null if not found + */ + Version getRecordedVersion(NodeRef record); + /** * Creates a record from the latest version, marking it as recorded. *

@@ -54,8 +71,26 @@ public interface RecordableVersionService * version is already recorded. * * @param nodeRef node reference - * @return NodeRef node reference to the crated record. + * @return NodeRef node reference to the created record. */ NodeRef createRecordFromLatestVersion(NodeRef filePlan, NodeRef nodeRef); + + /** + * Indicates whether a record version is destroyed or not. + * + * @param version version + * @return boolean true if destroyed, false otherwise + */ + boolean isRecordedVersionDestroyed(Version version); + + /** + * Marks a recorded version as destroyed. + *

+ * Note this method does not destroy the associated record, instead it marks the + * version as destroyed. + * + * @param version version + */ + void destroyRecordedVersion(Version version); } 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 a148fd3016..417e66fe08 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 @@ -55,6 +55,7 @@ import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyMap; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -76,7 +77,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl public static final String KEY_FILE_PLAN = "file-plan"; /** version record property */ - public static final String PROP_VERSION_RECORD = "RecordVersion"; + protected static final String PROP_VERSION_RECORD = "RecordVersion"; + protected static final String PROP_RECORDED_VERSION_DESTROYED = "RecordedVersionDestroyed"; /** version aspect property names */ private static final String[] VERSION_PROPERTY_NAMES = new String[] @@ -491,7 +493,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl { // look for the associated record final NodeRef previousRecord = (NodeRef)previousVersion.getVersionProperties().get(PROP_VERSION_RECORD); - if (previousRecord != null) + if (previousRecord != null && + nodeService.exists(previousRecord)) { versionRecord = previousRecord; break; @@ -501,6 +504,29 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl return versionRecord; } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#getRecordedVersion(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public Version getRecordedVersion(NodeRef versionRecord) + { + Version version = null; + NodeRef versionedNodeRef = (NodeRef) nodeService.getProperty(versionRecord, RecordableVersionModel.PROP_VERSIONED_NODEREF); + if (versionedNodeRef != null) + { + String versionLabel = (String) nodeService.getProperty(versionRecord, RecordableVersionModel.PROP_VERSION_LABEL); + if (StringUtils.isNotBlank(versionLabel)) + { + VersionHistory versionHistory = getVersionHistory(versionedNodeRef); + if (versionHistory != null) + { + version = versionHistory.getVersion(versionLabel); + } + } + } + return version; + } /** * Freezes audit aspect properties. @@ -533,12 +559,18 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl { Version version = super.getVersion(versionRef); + // place the version record reference in the version properties NodeRef record = (NodeRef)dbNodeService.getProperty(versionRef, PROP_RECORD_NODE_REF); if (record != null) - { + { version.getVersionProperties().put(PROP_VERSION_RECORD, record); } + // place information about the destruction of the version record in the properties + Boolean destroyed = (Boolean)dbNodeService.getProperty(versionRef, PROP_DESTROYED); + if (destroyed == null) { destroyed = Boolean.FALSE; } + version.getVersionProperties().put(PROP_RECORDED_VERSION_DESTROYED, destroyed); + return version; } @@ -579,10 +611,31 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl @Override public boolean isRecordedVersion(Version version) { - boolean result = true; - if (version.getVersionProperties().get(PROP_VERSION_RECORD) == null) + NodeRef versionNodeRef = getVersionNodeRef(version); + return dbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#getVersionRecord(org.alfresco.service.cmr.version.Version) + */ + @Override + public NodeRef getVersionRecord(Version version) + { + NodeRef result = null; + NodeRef versionNodeRef = getVersionNodeRef(version); + if (dbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) { - result = false; + // get the version record + result = (NodeRef)dbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF); + + // check that the version record exists + if (result != null && + !dbNodeService.exists(result)) + { + throw new AlfrescoRuntimeException("Version record node doesn't exist. Indicates version has not been updated " + + "when associated version record was deleted. " + + "(nodeRef=" + result.toString() + ")"); + } } return result; } @@ -737,6 +790,47 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl return record; } + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersionDestroyed(org.alfresco.service.cmr.version.Version) + */ + @Override + public boolean isRecordedVersionDestroyed(Version version) + { + boolean result = false; + + // get the version node reference + NodeRef versionNodeRef = getVersionNodeRef(version); + + // get the destroyed property value + Boolean isDestroyed = (Boolean)dbNodeService.getProperty(versionNodeRef, PROP_DESTROYED); + if (isDestroyed != null) + { + result = isDestroyed.booleanValue(); + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#destroyRecordedVersion(org.alfresco.service.cmr.version.Version) + */ + @Override + public void destroyRecordedVersion(Version version) + { + // get the version node reference + NodeRef versionNodeRef = getVersionNodeRef(version); + + // if it's a recorded version + if (dbNodeService.hasAspect(versionNodeRef, ASPECT_RECORDED_VERSION)) + { + // mark it as destroyed + dbNodeService.setProperty(versionNodeRef, PROP_DESTROYED, true); + + // clear the record node reference property + dbNodeService.setProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF, null); + } + } + /** * Helper method to get the version number of a given version by inspecting the * name of the parent association. diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java index c7b1e08f4c..b8a2ed6e94 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java @@ -169,7 +169,7 @@ public class AdHocRecordableVersionsTest extends RecordableVersionsBaseTest public Void doWork() throws Exception { // add custom meta-data to record - NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + NodeRef record = recordableVersionService.getVersionRecord(version); 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/AutoRecordableVersionsTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java index a9e4d678a3..81a49f3652 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java @@ -24,7 +24,6 @@ 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; @@ -131,7 +130,7 @@ public class AutoRecordableVersionsTest extends RecordableVersionsBaseTest checkRecordedVersion(dmDocument, null, "0.3"); Version version = versionService.getCurrentVersion(dmDocument); - NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + NodeRef record = recordableVersionService.getVersionRecord(version); boolean foundPrevious = false; Set relationships = relationshipService.getRelationshipsFrom(record); diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoVersionTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoVersionTest.java new file mode 100755 index 0000000000..99c6eec74d --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoVersionTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.version; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; + + +/** + * Test recorded version histories when interacting with cm:versionable aspect + * and the auto-version behvaiour. + * + * @author Roy Wetherall + * @since 2.3.1 + */ +public class AutoVersionTest extends RecordableVersionsBaseTest +{ + /** + * Given a versionable document + * When I specialise the type of the document + * Then the version history has only one initial version + * And it does not represent the current type of the document + */ + public void testSpecialisedNodeInitialVersionCreated() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private NodeRef myDocument; + + public void given() throws Exception + { + // create a document + myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); + + // make versionable + nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, null); + } + + public void when() + { + // specialise document + nodeService.setType(myDocument, TYPE_CUSTOM_TYPE); + } + + public void then() + { + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + + assertNotNull(versionHistory); + assertEquals(1, versionHistory.getAllVersions().size()); + + NodeRef frozenState = versionHistory.getHeadVersion().getFrozenStateNodeRef(); + assertEquals(ContentModel.TYPE_CONTENT, nodeService.getType(frozenState)); + assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(myDocument)); + } + }); + } + + /** + * Given a versionable document with initial version turned off + * When I specialise the type of the document + * Then the version history remains empty + */ + public void testSpecialisedNodeInitialVersionNotCreated() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private NodeRef myDocument; + + public void given() throws Exception + { + // create a document + myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); + + // make versionable + Map props = new HashMap(1); + props.put(ContentModel.PROP_INITIAL_VERSION, false); + nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, props); + } + + public void when() + { + // specialise document + nodeService.setType(myDocument, TYPE_CUSTOM_TYPE); + } + + public void then() + { + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNull(versionHistory); + } + }); + } + + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java index d6e33fb515..31e4a430dc 100755 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java @@ -28,6 +28,7 @@ import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionType; +import org.alfresco.util.GUID; /** * Declare as record version integration tests @@ -99,7 +100,7 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest /** * Given versionable content with a recorded latest version - * When I delcare a version record + * When I declare a version record * Then nothing happens since the latest version is already recorded * And a warning is logged */ @@ -146,5 +147,62 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest }); } + /** + * Given that a document is a specialized type + * When version is declared as a record + * Then the record is the same type as the source document + * + * @see https://issues.alfresco.com/jira/browse/RM-2194 + */ + public void testSpecializedContentType() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private NodeRef customDocument; + private NodeRef versionRecord; + private Map versionProperties; + + public void given() throws Exception + { + // create content + customDocument = fileFolderService.create(dmFolder, GUID.generate(), TYPE_CUSTOM_TYPE).getNodeRef(); + prepareContent(customDocument); + + // setup version properties + versionProperties = new HashMap(2); + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + + // create version + versionService.createVersion(customDocument, versionProperties); + + // assert that the latest version is not recorded + assertFalse(recordableVersionService.isCurrentVersionRecorded(customDocument)); + } + + public void when() + { + // create version record from latest version + versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, customDocument); + } + + public void then() + { + // check the created record + assertNotNull(versionRecord); + assertTrue(recordService.isRecord(versionRecord)); + + // check the record type is correct + assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(versionRecord)); + + // assert the current version is recorded + assertTrue(recordableVersionService.isCurrentVersionRecorded(customDocument)); + + // check the recorded version + checkRecordedVersion(customDocument, DESCRIPTION, "0.1"); + } + }); + + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeleteRecordVersionTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeleteRecordVersionTest.java new file mode 100644 index 0000000000..8f716cc4fe --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeleteRecordVersionTest.java @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.version; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction; +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.test.util.CommonRMTestUtils; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionPolicy; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; + + +/** + * Recordable version history integration tests. + * + * @author Roy Wetherall + * @since 2.3.1 + */ +public class DeleteRecordVersionTest extends RecordableVersionsBaseTest +{ + /** + * Given that a document is created + * And the initial version is record + * When I delete the version record + * Then the version is deleted + * And the version history is not deleted + * + * @see https://issues.alfresco.com/jira/browse/RM-2562 + */ + public void testDeleteFirstRecordedVersion() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef myDocument; + + public void given() throws Exception + { + // create a document + myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); + + // make versionable + Map props = new HashMap(2); + props.put(RecordableVersionModel.PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); + props.put(RecordableVersionModel.PROP_FILE_PLAN, filePlan); + nodeService.addAspect(myDocument, RecordableVersionModel.ASPECT_VERSIONABLE, props); + nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, null); + } + + public void when() + { + // check the initial version label + assertEquals("1.0", nodeService.getProperty(myDocument, ContentModel.PROP_VERSION_LABEL)); + + // check that the version history contains a single version that is recorded + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNotNull(versionHistory); + assertEquals(1, versionHistory.getAllVersions().size()); + + // check the recorded version is not marked as destroyed + Version head = versionHistory.getHeadVersion(); + assertNotNull(head); + assertFalse(recordableVersionService.isRecordedVersionDestroyed(head)); + + // check the version record + NodeRef record = recordableVersionService.getVersionRecord(head); + assertTrue(recordService.isRecord(record)); + + // record should not have a version history because it is immutable + assertFalse(nodeService.hasAspect(record, ContentModel.ASPECT_VERSIONABLE)); + VersionHistory recordVersionHistory = versionService.getVersionHistory(record); + assertNull(recordVersionHistory); + + // destroy record + nodeService.deleteNode(record); + } + + public void then() + { + // document is still versionable + assertTrue(nodeService.hasAspect(myDocument, ContentModel.ASPECT_VERSIONABLE)); + + // check the initial version label + assertEquals("1.0", nodeService.getProperty(myDocument, ContentModel.PROP_VERSION_LABEL)); + + // still has a version history, but the version is marked as destroyed + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNotNull(versionHistory); + assertEquals(1, versionHistory.getAllVersions().size()); + + // check the recorded version is marked as destroyed and the record version is not longer available + Version version = versionHistory.getHeadVersion(); + assertNotNull(version); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(version)); + assertNull(recordableVersionService.getVersionRecord(version)); + } + }); + } + + /** + * Given that a document is created + * And the initial version is record + * And the associated version record is deleted + * When a new version is created + * Then a new associated version record is created + * And the version is 1.1 (not 1.0 since this was deleted, but the version history maintained) + * + * @see https://issues.alfresco.com/jira/browse/RM-2562 + */ + public void testDeleteFirstRecordedVersionAndCreateNewVersion() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef myDocument; + + public void given() throws Exception + { + // create a document + myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); + ContentWriter writer = fileFolderService.getWriter(myDocument); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent(GUID.generate()); + + // make versionable + Map props = new HashMap(2); + props.put(RecordableVersionModel.PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); + props.put(RecordableVersionModel.PROP_FILE_PLAN, filePlan); + nodeService.addAspect(myDocument, RecordableVersionModel.ASPECT_VERSIONABLE, props); + nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, null); + } + + public void when() + { + // get the created version record + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + Version head = versionHistory.getHeadVersion(); + NodeRef record = recordableVersionService.getVersionRecord(head); + + // destroy record + nodeService.deleteNode(record); + + // update the content to create a new version (and version record) + ContentWriter writer = fileFolderService.getWriter(myDocument); + writer.putContent(GUID.generate()); + } + + public void then() + { + // document is still versionable + assertTrue(nodeService.hasAspect(myDocument, ContentModel.ASPECT_VERSIONABLE)); + + // check the version number has been incremented + assertEquals("1.1", nodeService.getProperty(myDocument, ContentModel.PROP_VERSION_LABEL)); + + // still has a version history, with 2 enties + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNotNull(versionHistory); + assertEquals(2, versionHistory.getAllVersions().size()); + + // latest version is current + Version head = versionHistory.getHeadVersion(); + assertFalse(recordableVersionService.isRecordedVersionDestroyed(head)); + assertNotNull(recordableVersionService.getVersionRecord(head)); + + // first version is destroyed + Version destroyed = versionHistory.getPredecessor(head); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(destroyed)); + assertNull(recordableVersionService.getVersionRecord(destroyed)); + + // get the version record for the current version + NodeRef versionRecord = recordableVersionService.getVersionRecord(head); + assertNotNull(versionRecord); + assertTrue(nodeService.exists(versionRecord)); + + Set from = relationshipService.getRelationshipsFrom(versionRecord); + assertTrue(from.isEmpty()); + + Set to = relationshipService.getRelationshipsTo(versionRecord); + assertTrue(to.isEmpty()); + } + }); + } + + /** + * Given a chain of version records (1.0, 1.1, 1.2) which are all related + * When I delete version record 1.0 + * Then 1.1 is the oldest version + */ + public void testDeleteOldestVersion() + { + final NodeRef myDocument = createDocumentWithRecordVersions(); + + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private VersionHistory versionHistory; + + public void given() throws Exception + { + // get version history + versionHistory = versionService.getVersionHistory(myDocument); + } + + public void when() + { + Version version10 = versionHistory.getVersion("1.0"); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + + // delete record version 1.0 + nodeService.deleteNode(recordVersion10); + } + + public void then() + { + // check the deleted version + Version version10 = versionHistory.getVersion("1.0"); + assertNotNull(version10); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(version10)); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + assertNull(recordVersion10); + + // verify 1.2 setup as expected + Version version12 = versionHistory.getHeadVersion(); + assertEquals("1.2", version12.getVersionLabel()); + NodeRef recordVersion12 = recordableVersionService.getVersionRecord(version12); + assertNotNull(recordVersion12); + + assertTrue(relationshipService.getRelationshipsTo(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + + Set from12 = relationshipService.getRelationshipsFrom(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, from12.size()); + + // verify 1.1 setup as expected + Version version11 = versionHistory.getPredecessor(version12); + assertEquals("1.1", version11.getVersionLabel()); + NodeRef recordVersion11 = recordableVersionService.getVersionRecord(version11); + assertNotNull(recordVersion11); + + Set to11 = relationshipService.getRelationshipsTo(recordVersion11, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, to11.size()); + assertEquals(recordVersion12, to11.iterator().next().getSource()); + + assertTrue(relationshipService.getRelationshipsFrom(recordVersion11, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + } + }); + } + + /** + * Given a chain of version records (1.0, 1.1, 1.2) which are all related + * When I delete version record 1.1 + * Then 1.2 now 'versions' 1.0 + */ + public void testDeleteMiddleVersion() + { + final NodeRef myDocument = createDocumentWithRecordVersions(); + + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private VersionHistory versionHistory; + + public void given() throws Exception + { + // get version history + versionHistory = versionService.getVersionHistory(myDocument); + } + + public void when() + { + Version version11 = versionHistory.getVersion("1.1"); + NodeRef recordVersion11 = recordableVersionService.getVersionRecord(version11); + + // delete record version 1.1 + nodeService.deleteNode(recordVersion11); + } + + public void then() + { + // check the deleted version + Version version11 = versionHistory.getVersion("1.1"); + assertNotNull(version11); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(version11)); + NodeRef recordVersion11 = recordableVersionService.getVersionRecord(version11); + assertNull(recordVersion11); + + // verify 1.2 setup as expected + Version version12 = versionHistory.getHeadVersion(); + assertEquals("1.2", version12.getVersionLabel()); + NodeRef recordVersion12 = recordableVersionService.getVersionRecord(version12); + assertNotNull(recordVersion12); + + assertTrue(relationshipService.getRelationshipsTo(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + + Set from12 = relationshipService.getRelationshipsFrom(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, from12.size()); + + // verify 1.0 setup as expected + Version version10 = versionHistory.getVersion("1.0"); + assertEquals("1.0", version10.getVersionLabel()); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + assertNotNull(recordVersion10); + + Set to10 = relationshipService.getRelationshipsTo(recordVersion10, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, to10.size()); + assertEquals(recordVersion12, to10.iterator().next().getSource()); + + assertTrue(relationshipService.getRelationshipsFrom(recordVersion10, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + + } + }); + } + + /** + * Given a chain of version records (1.0, 1.1, 1.2) which are all related + * When I delete version record 1.2 + * Then 1.1 is the most recent version + */ + public void testDeleteLatestVersion() + { + final NodeRef myDocument = createDocumentWithRecordVersions(); + + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private VersionHistory versionHistory; + + public void given() throws Exception + { + // get version history + versionHistory = versionService.getVersionHistory(myDocument); + } + + public void when() + { + Version version10 = versionHistory.getVersion("1.0"); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + + // delete record version 1.0 + nodeService.deleteNode(recordVersion10); + } + + public void then() + { + // check the deleted version + Version version10 = versionHistory.getVersion("1.0"); + assertNotNull(version10); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(version10)); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + assertNull(recordVersion10); + + // verify 1.2 setup as expected + Version version12 = versionHistory.getHeadVersion(); + assertEquals("1.2", version12.getVersionLabel()); + NodeRef recordVersion12 = recordableVersionService.getVersionRecord(version12); + assertNotNull(recordVersion12); + + assertTrue(relationshipService.getRelationshipsTo(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + + Set from12 = relationshipService.getRelationshipsFrom(recordVersion12, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, from12.size()); + + // verify 1.1 setup as expected + Version version11 = versionHistory.getPredecessor(version12); + assertEquals("1.1", version11.getVersionLabel()); + NodeRef recordVersion11 = recordableVersionService.getVersionRecord(version11); + assertNotNull(recordVersion11); + + Set to11 = relationshipService.getRelationshipsTo(recordVersion11, RelationshipService.RELATIONSHIP_VERSIONS); + assertEquals(1, to11.size()); + assertEquals(recordVersion12, to11.iterator().next().getSource()); + + assertTrue(relationshipService.getRelationshipsFrom(recordVersion11, RelationshipService.RELATIONSHIP_VERSIONS).isEmpty()); + } + }); + } + + /** + * Given that a version record + * When the version record is destroyed whilst retaining the meta data + * Then the version is marked as destroyed in the collab version history + */ + public void testDetroyVerionRecordWithMetadata() + { + final NodeRef myDocument = createDocumentWithRecordVersions(); + + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private VersionHistory versionHistory; + private NodeRef recordVersion11; + + public void given() throws Exception + { + // create file plan structure + NodeRef myCategory = filePlanService.createRecordCategory(filePlan, GUID.generate()); + utils.createBasicDispositionSchedule(myCategory, GUID.generate(), GUID.generate(), true, true); + + NodeRef myRecordFolder = recordFolderService.createRecordFolder(myCategory, GUID.generate()); + + // get version history + versionHistory = versionService.getVersionHistory(myDocument); + + // file and complete all the version records into my record folder + for (Version version : versionHistory.getAllVersions()) + { + NodeRef record = recordableVersionService.getVersionRecord(version); + fileFolderService.move(record, myRecordFolder, null); + utils.completeRecord(record); + } + } + + public void when() + { + Version version11 = versionHistory.getVersion("1.1"); + recordVersion11 = recordableVersionService.getVersionRecord(version11); + + Map params = new HashMap(1); + params.put(CompleteEventAction.PARAM_EVENT_NAME, CommonRMTestUtils.DEFAULT_EVENT_NAME); + rmActionService.executeRecordsManagementAction(recordVersion11, CompleteEventAction.NAME, params); + + rmActionService.executeRecordsManagementAction(recordVersion11, CutOffAction.NAME); + + rmActionService.executeRecordsManagementAction(recordVersion11, DestroyAction.NAME); + } + + public void then() + { + // verify that the version history looks as expected + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNotNull(versionHistory); + Collection versions = versionHistory.getAllVersions(); + assertEquals(3, versions.size()); + + // verify 1.2 setup as expected + Version version12 = versionHistory.getHeadVersion(); + assertEquals("1.2", version12.getVersionLabel()); + assertFalse(recordableVersionService.isRecordedVersionDestroyed(version12)); + NodeRef recordVersion12 = recordableVersionService.getVersionRecord(version12); + assertNotNull(recordVersion12); + assertFalse(recordService.isMetadataStub(recordVersion12)); + + assertTrue(relationshipService.getRelationshipsTo(recordVersion12, "versions").isEmpty()); + + Set from12 = relationshipService.getRelationshipsFrom(recordVersion12, "versions"); + assertEquals(1, from12.size()); + + // verify 1.1 setup as expected + Version version11 = versionHistory.getPredecessor(version12); + assertEquals("1.1", version11.getVersionLabel()); + assertTrue(recordableVersionService.isRecordedVersionDestroyed(version11)); + assertNotNull(recordVersion11); + assertTrue(recordService.isMetadataStub(recordVersion11)); + + Set to11 = relationshipService.getRelationshipsTo(recordVersion11, "versions"); + assertEquals(1, to11.size()); + assertEquals(recordVersion12, to11.iterator().next().getSource()); + + Set from11 = relationshipService.getRelationshipsFrom(recordVersion11, "versions"); + assertEquals(1, from11.size()); + + // verify 1.0 setup as expected + Version version10 = versionHistory.getPredecessor(version11); + assertEquals("1.0", version10.getVersionLabel()); + assertFalse(recordableVersionService.isRecordedVersionDestroyed(version10)); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + assertNotNull(recordVersion10); + assertFalse(recordService.isMetadataStub(recordVersion10)); + + Set to10 = relationshipService.getRelationshipsTo(recordVersion10, "versions"); + assertEquals(1, to10.size()); + assertEquals(recordVersion11, to10.iterator().next().getSource()); + + assertTrue(relationshipService.getRelationshipsFrom(recordVersion10, "versions").isEmpty()); + + } + }); + } +} 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 767f3d2ec8..32f1269bf3 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 @@ -19,15 +19,18 @@ package org.alfresco.module.org_alfresco_module_rm.test.integration.version; import java.io.Serializable; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; 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.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.module.org_alfresco_module_rm.version.RecordableVersionPolicy; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -35,9 +38,12 @@ import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; import org.alfresco.util.PropertyMap; /** + * Recordable versions base integration test + * * @author Roy Wetherall * @since 2.3 */ @@ -54,7 +60,16 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen protected static final String CONTENT = "Simple + Smart. A smarter way to build, a smarter way to deploy. Its simple because we focus on the end " + "user and smart because we support more open standards than any other ECM platform, while delivering all " - + "the value a traditional platform provides."; + + "the value a traditional platform provides."; + + protected RecordableVersionService recordableVersionService; + + @Override + protected void initServices() + { + super.initServices(); + recordableVersionService = (RecordableVersionService)applicationContext.getBean("RecordableVersionService"); + } /** * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isCollaborationSiteTest() @@ -76,25 +91,31 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen // create authentication for owner createPerson(OWNER); + // prepare content + prepareContent(dmDocument); + } + + protected void prepareContent(NodeRef content) + { // add titled aspect PropertyMap titledProperties = new PropertyMap(2); titledProperties.put(ContentModel.PROP_TITLE, "document title"); titledProperties.put(ContentModel.PROP_DESCRIPTION, "document description"); - nodeService.addAspect(dmDocument, ContentModel.ASPECT_TITLED, titledProperties); + nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProperties); // add ownable aspect PropertyMap ownableProperties = new PropertyMap(1); ownableProperties.put(ContentModel.PROP_OWNER, OWNER); - nodeService.addAspect(dmDocument, ContentModel.ASPECT_OWNABLE, ownableProperties); + nodeService.addAspect(content, ContentModel.ASPECT_OWNABLE, ownableProperties); // add Dublin core aspect PropertyMap dublinCoreProperties = new PropertyMap(2); dublinCoreProperties.put(QNAME_PUBLISHER, PUBLISHER); dublinCoreProperties.put(QNAME_SUBJECT, SUBJECT); - nodeService.addAspect(dmDocument, ContentModel.ASPECT_DUBLINCORE, dublinCoreProperties); + nodeService.addAspect(content, ContentModel.ASPECT_DUBLINCORE, dublinCoreProperties); // add content - ContentWriter writer = contentService.getWriter(dmDocument, ContentModel.PROP_CONTENT, true); + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); writer.setEncoding("UTF-8"); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.putContent(CONTENT); @@ -122,7 +143,7 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen // store document state Map beforeProperties = nodeService.getProperties(document); - Set beforeAspects = nodeService.getAspects(dmDocument); + Set beforeAspects = nodeService.getAspects(document); // get the current version Version version = versionService.getCurrentVersion(document); @@ -144,7 +165,7 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen checkAspects(frozen, beforeAspects); // record version node reference is available on version - NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + NodeRef record = recordableVersionService.getVersionRecord(version); assertNotNull(record); // check that the version record information has been added @@ -185,7 +206,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(RecordableVersionServiceImpl.PROP_VERSION_RECORD); + NodeRef record = recordableVersionService.getVersionRecord(version); assertNull(record); // check the version history @@ -230,6 +251,9 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen } } + // remove "owner" from cloneFrozenProperties + cloneFrozenProperties.remove(ContentModel.PROP_OWNER); + // frozen properties should be empty assertTrue("Properties in frozen state, but not in origional. " + cloneFrozenProperties.keySet(), cloneFrozenProperties.isEmpty()); @@ -258,5 +282,121 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen fail("Aspects in the frozen state, but not in origional. " + frozenAspects.toString()); } } + + /** + * Creates a document with three versions (1.0, 1.1, 1.2) all of which + * are recorded. + * + * @return NodeRef node reference + */ + protected NodeRef createDocumentWithRecordVersions() + { + // create document and initial version (1.0) + final NodeRef myDocument = doTestInTransaction(new Test() + { + @Override + public NodeRef run() throws Exception + { + // create a document + NodeRef testDoc = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef(); + ContentWriter writer = fileFolderService.getWriter(testDoc); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent(GUID.generate()); + + // make versionable + Map props = new HashMap(2); + props.put(RecordableVersionModel.PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); + props.put(RecordableVersionModel.PROP_FILE_PLAN, filePlan); + nodeService.addAspect(testDoc, RecordableVersionModel.ASPECT_VERSIONABLE, props); + nodeService.addAspect(testDoc, ContentModel.ASPECT_VERSIONABLE, null); + + return testDoc; + } + }); + + // create 1.1 + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + // update content + ContentWriter writer = fileFolderService.getWriter(myDocument); + writer.putContent(GUID.generate()); + + return null; + } + }); + + // create 1.2 + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + // update content + ContentWriter writer = fileFolderService.getWriter(myDocument); + writer.putContent(GUID.generate()); + + return null; + } + }); + + // we do these checks to ensure that the test data is in the correct state before we + // start to manipulate the versions and execute tests + doTestInTransaction(new Test() + { + @Override + public Void run() throws Exception + { + // verify that the version history looks as expected + VersionHistory versionHistory = versionService.getVersionHistory(myDocument); + assertNotNull(versionHistory); + Collection versions = versionHistory.getAllVersions(); + assertEquals(3, versions.size()); + + // verify 1.2 setup as expected + Version version12 = versionHistory.getHeadVersion(); + assertEquals("1.2", version12.getVersionLabel()); + NodeRef recordVersion12 = recordableVersionService.getVersionRecord(version12); + assertNotNull(recordVersion12); + + assertTrue(relationshipService.getRelationshipsTo(recordVersion12, "versions").isEmpty()); + + Set from12 = relationshipService.getRelationshipsFrom(recordVersion12, "versions"); + assertEquals(1, from12.size()); + + // verify 1.1 setup as expected + Version version11 = versionHistory.getPredecessor(version12); + assertEquals("1.1", version11.getVersionLabel()); + NodeRef recordVersion11 = recordableVersionService.getVersionRecord(version11); + assertNotNull(recordVersion11); + + Set to11 = relationshipService.getRelationshipsTo(recordVersion11, "versions"); + assertEquals(1, to11.size()); + assertEquals(recordVersion12, to11.iterator().next().getSource()); + + Set from11 = relationshipService.getRelationshipsFrom(recordVersion11, "versions"); + assertEquals(1, from11.size()); + + // verify 1.0 setup as expected + Version version10 = versionHistory.getPredecessor(version11); + assertEquals("1.0", version10.getVersionLabel()); + NodeRef recordVersion10 = recordableVersionService.getVersionRecord(version10); + assertNotNull(recordVersion10); + + Set to10 = relationshipService.getRelationshipsTo(recordVersion10, "versions"); + assertEquals(1, to10.size()); + assertEquals(recordVersion11, to10.iterator().next().getSource()); + + assertTrue(relationshipService.getRelationshipsFrom(recordVersion10, "versions").isEmpty()); + + return null; + } + }); + + return myDocument; + } } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspectUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspectUnitTest.java new file mode 100644 index 0000000000..8069678d65 --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspectUnitTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.model.rma.aspect; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** + * Version record aspect unit tests + * + * @author Roy Wetherall + * @since 2.3.1 + */ +public class VersionRecordAspectUnitTest extends BaseUnitTest +{ + /** service mocks */ + private @Mock VersionHistory mockedVersionHistory; + private @Mock Version mockedVersion; + private @Mock VersionService mockedVersionService; + private @Mock RelationshipService mockedRelationshipService; + + /** test object */ + private @InjectMocks VersionRecordAspect versionRecordAspect; + + /** + * given that there is no recorded version + * before delete of record + * then nothing happens + */ + @Test + public void beforeDeleteNoVersionNodeRef() + { + NodeRef nodeRef = generateNodeRef(); + + when(mockedRecordableVersionService.getRecordedVersion(nodeRef)) + .thenReturn(null); + + versionRecordAspect.beforeDeleteNode(nodeRef); + + verify(mockedNodeService, never()).getProperty(nodeRef, RecordableVersionModel.PROP_VERSION_LABEL); + verify(mockedRecordableVersionService, never()).destroyRecordedVersion(any(Version.class)); + } + + /** + * given that there is a recorded version + * before delete of record + * then the version is marked as destroyed + */ + @Test + public void beforeDeleteMarkVersionDestroyed() + { + NodeRef nodeRef = generateNodeRef(); + + when(mockedRecordableVersionService.getRecordedVersion(nodeRef)) + .thenReturn(mockedVersion); + + versionRecordAspect.beforeDeleteNode(nodeRef); + + verify(mockedRecordableVersionService).destroyRecordedVersion(mockedVersion); + } +} 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 21d590dd27..19dd235cae 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,8 @@ */ package org.alfresco.module.org_alfresco_module_rm.version; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.Serializable; import java.util.Collections; @@ -419,9 +422,16 @@ public class RecordableVersionServiceImplUnitTest extends BaseUnitTest { // latest version is already recorded Version mockedVersion = mock(VersionImpl.class); - doReturn(Collections.singletonMap(RecordableVersionServiceImpl.PROP_VERSION_RECORD, generateNodeRef())).when(mockedVersion).getVersionProperties(); - doReturn(true).when(mockedNodeService).hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE); - doReturn(mockedVersion).when(recordableVersionService).getCurrentVersion(nodeRef); + NodeRef versionNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + + when(mockedNodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + .thenReturn(true); + when(mockedDbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) + .thenReturn(true); + doReturn(mockedVersion) + .when(recordableVersionService).getCurrentVersion(nodeRef); // create record from version recordableVersionService.createRecordFromLatestVersion(filePlan, nodeRef); @@ -495,4 +505,157 @@ public class RecordableVersionServiceImplUnitTest extends BaseUnitTest RecordableVersionModel.ASPECT_RECORDED_VERSION, Collections.singletonMap(RecordableVersionModel.PROP_RECORD_NODE_REF, (Serializable)newRecordNodeRef)); } + + + /** + * given the destroyed prop isn't set + * when I ask if the version is destroyed + * then the result is false + */ + @Test + public void propNotSetVersionNotDestroyed() + { + // set up version + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + + // set prop not set + when(mockedDbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED)) + .thenReturn(null); + + // is version destroyed + assertFalse(recordableVersionService.isRecordedVersionDestroyed(mockedVersion)); + } + + /** + * given the destroyed prop is set + * when I ask if the version is destroyed + * then the result matches the value set in the destroy property + */ + @Test + public void propSetVersionDestroyed() + { + // set up version + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + + // set prop + when(mockedDbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED)) + .thenReturn(Boolean.TRUE); + + // is version destroyed + assertTrue(recordableVersionService.isRecordedVersionDestroyed(mockedVersion)); + + // set prop + when(mockedDbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED)) + .thenReturn(Boolean.FALSE); + + // is version destroyed + assertFalse(recordableVersionService.isRecordedVersionDestroyed(mockedVersion)); + } + + /** + * given that the version node doesn't have the recorded version aspect applied + * when I mark the version as destroyed + * then nothing happens + */ + @Test + public void noAspectMarkAsDestroyed() + { + // set up version + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + + // indicate that the version doesn't have the aspect + when(mockedDbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) + .thenReturn(false); + + // mark as destroyed + recordableVersionService.destroyRecordedVersion(mockedVersion); + + // verify nothing happened + verify(mockedDbNodeService, never()) + .setProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED, Boolean.TRUE); + } + + /** + * given that the version node ref has the recorded version aspect applied + * and the record version reference exists + * when I mark the version as destroyed + * then the version is marked as destroyed + */ + @Test + public void markAsDestroyed() + { + // set up version + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + NodeRef versionRecordNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + when(mockedDbNodeService.exists(versionRecordNodeRef)) + .thenReturn(true); + + // indicate that the version doesn't have the aspect + when(mockedDbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) + .thenReturn(true); + + // indicate that the associated version record exists + when(mockedDbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF)) + .thenReturn(versionRecordNodeRef); + + // mark as destroyed + recordableVersionService.destroyRecordedVersion(mockedVersion); + + // verify that the version was marked as destroyed + verify(mockedDbNodeService) + .setProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED, Boolean.TRUE); + // and the reference to the version record was cleared + verify(mockedDbNodeService) + .setProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF, null); + } + + /** + * given that the version node ref has the recorded version aspect applied + * and the associated version record has been deleted + * when I mark the version as destroyed + * then the version is marked as destroyed + * and the reference to the deleted version record is removed + */ + @Test + public void markAsDestroyedClearNodeRef() + { + // set up version + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + NodeRef versionRecordNodeRef = generateNodeRef(); + when(mockedVersion.getFrozenStateNodeRef()) + .thenReturn(versionNodeRef); + when(mockedDbNodeService.exists(versionRecordNodeRef)) + .thenReturn(false); + + // indicate that the version doesn't have the aspect + when(mockedDbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) + .thenReturn(true); + + // indicate that the associated version record exists + when(mockedDbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF)) + .thenReturn(versionRecordNodeRef); + + // mark as destroyed + recordableVersionService.destroyRecordedVersion(mockedVersion); + + // verify that the version was marked as destroyed + verify(mockedDbNodeService) + .setProperty(versionNodeRef, RecordableVersionModel.PROP_DESTROYED, Boolean.TRUE); + // and the reference to the version record was cleared + verify(mockedDbNodeService) + .setProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF, null); + } } \ No newline at end of file