RM-2562: Deleting records from versions produces inconsistent behaviour

* Versions are not deleted from collab version history, rather marked as destroyed (prevents inconsistencies since we don't support deleting versions well)
 * Share UI updated to acomadate above
 * Version relationship reorganised based on changes to version record history
 * Unit tests and integration tests for above
 * some unit tests relating to RM-2194
 * Hover message on version record icon in version history

+review RM



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.3@119284 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2015-12-02 02:09:43 +00:00
parent 11e59543bd
commit f142e7647b
24 changed files with 1405 additions and 160 deletions

View File

@@ -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<Void>()
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@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<Void>()
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@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<Object>()
authenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
@@ -256,7 +255,7 @@ public class RecordAspect extends AbstractDisposableItem
return null;
}
}, AuthenticationUtil.getAdminUserName());
}, authenticationUtil.getAdminUserName());
}
}

View File

@@ -0,0 +1,152 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.model.rma.aspect;
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 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;
import org.alfresco.service.cmr.version.VersionHistory;
import org.alfresco.service.cmr.version.VersionService;
import org.apache.commons.lang.StringUtils;
/**
* rmv:versionRecord behaviour bean
*
* @author Roy Wetherall
* @since 2.3.1
*/
@BehaviourBean
(
defaultType = "rmv:versionRecord"
)
public class VersionRecordAspect extends BaseBehaviourBean
implements NodeServicePolicies.BeforeDeleteNodePolicy
{
/** version service */
private VersionService versionService;
/** recordable version service */
private RecordableVersionService recordableVersionService;
/** relationship service */
private RelationshipService relationshipService;
/**
* @param versionService version service
*/
public void setVersionService(VersionService versionService)
{
this.versionService = versionService;
}
/**
* @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 NodeRef versionedNodeRef = (NodeRef) nodeService.getProperty(nodeRef, PROP_VERSIONED_NODEREF);
if (versionedNodeRef != null)
{
String versionLabel = (String) nodeService.getProperty(nodeRef, PROP_VERSION_LABEL);
if (StringUtils.isNotBlank(versionLabel))
{
final VersionHistory versionHistory = versionService.getVersionHistory(versionedNodeRef);
if (versionHistory != null)
{
final Version version = versionHistory.getVersion(versionLabel);
if (version != null)
{
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@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<Relationship> tos = relationshipService.getRelationshipsTo(nodeRef, RelationshipService.RELATIONSHIP_VERSIONS);
if (!tos.isEmpty() && tos.size() == 1)
{
// if there is some "from" references
Set<Relationship> 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;
}
});
}
}
}
}
}
}

View File

@@ -18,10 +18,6 @@
*/
package org.alfresco.module.org_alfresco_module_rm.record;
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;
import java.util.Arrays;
@@ -61,7 +57,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;
@@ -122,8 +118,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);
@@ -229,6 +224,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<QName, Set<QName>> recordMetaDataAspects;
@@ -382,6 +380,14 @@ public class RecordServiceImpl extends BaseBehaviourBean
{
this.recordsManagementContainerType = recordsManagementContainerType;
}
/**
* @param recordableVersionService recordable version service
*/
public void setRecordableVersionService(RecordableVersionService recordableVersionService)
{
this.recordableVersionService = recordableVersionService;
}
/**
* Init method
@@ -1023,7 +1029,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);
@@ -1081,7 +1099,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;
@@ -1808,38 +1826,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<Void>()
{
@Override
public Void doWork()
{
versionService.deleteVersion(versionedNodeRef, version);
return null;
}
});
}
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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<Relationship> 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.
* <p>
* 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<Relationship> 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<Relationship> getRelationshipsTo(NodeRef nodeRef);
/**
* Gets all the relationships that go into the given node reference
* that match the a given name filter.
* <p>
* 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<Relationship> getRelationshipsTo(NodeRef nodeRef, String nameFilter);
/**
* Adds a relationship from the given node <code>source</code>

View File

@@ -376,16 +376,25 @@ public class RelationshipServiceImpl extends RecordsManagementAdminBase implemen
*/
@Override
public Set<Relationship> 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<Relationship> getRelationshipsFrom(NodeRef nodeRef, String nameFilter)
{
mandatory("nodeRef", nodeRef);
Set<Relationship> relationships = new HashSet<Relationship>();
List<AssociationRef> customReferencesFrom = getNodeService().getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
relationships.addAll(generateRelationshipFromAssociationRef(customReferencesFrom));
relationships.addAll(generateRelationshipFromAssociationRef(customReferencesFrom, nameFilter));
List<ChildAssociationRef> 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<Relationship> 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<Relationship> getRelationshipsTo(NodeRef nodeRef, String nameFilter)
{
mandatory("nodeRef", nodeRef);
Set<Relationship> relationships = new HashSet<Relationship>();
List<AssociationRef> customReferencesTo = getNodeService().getSourceAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
relationships.addAll(generateRelationshipFromAssociationRef(customReferencesTo));
relationships.addAll(generateRelationshipFromAssociationRef(customReferencesTo, nameFilter));
List<ChildAssociationRef> 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<Relationship> generateRelationshipFromAssociationRef(List<AssociationRef> associationRefs)
private Set<Relationship> generateRelationshipFromAssociationRef(List<AssociationRef> associationRefs, String nameFilter)
{
Set<Relationship> relationships = new HashSet<Relationship>();
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<Relationship> generateRelationshipFromParentChildAssociationRef(List<ChildAssociationRef> childAssociationRefs)
private Set<Relationship> generateRelationshipFromParentChildAssociationRef(List<ChildAssociationRef> childAssociationRefs, String nameFilter)
{
Set<Relationship> relationships = new HashSet<Relationship>();
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();

View File

@@ -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");

View File

@@ -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<QName> 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<QName> aspects = dbNodeService.getAspects(record);
return processAspects(aspects);
if (dbNodeService.exists(record))
{
Set<QName> aspects = dbNodeService.getAspects(record);
return processAspects(aspects);
}
else
{
return (Set<QName>)Collections.EMPTY_SET;
}
}
else
{

View File

@@ -47,6 +47,15 @@ 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);
/**
* Creates a record from the latest version, marking it as recorded.
* <p>
@@ -54,8 +63,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.
* <p>
* Note this method does not destroy the associated record, instead it marks the
* version as destroyed.
*
* @param version version
*/
void destroyRecordedVersion(Version version);
}

View File

@@ -76,7 +76,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 +492,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;
@@ -502,6 +504,66 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
return versionRecord;
}
@Override
protected VersionHistory buildVersionHistory(NodeRef versionHistoryRef, NodeRef nodeRef)
{
VersionHistory versionHistory = super.buildVersionHistory(versionHistoryRef, nodeRef);
// create an empty version history if appropriate
if (versionHistoryRef != null &&
nodeRef != null &&
versionHistory == null &&
getAllVersions(versionHistoryRef).isEmpty() == true)
{
versionHistory = new EmptyVersionHistory();
}
return versionHistory;
}
public class EmptyVersionHistory implements VersionHistory
{
private static final long serialVersionUID = 3449832161314670033L;
@Override
public Version getRootVersion()
{
return null;
}
@Override
public Version getHeadVersion()
{
return null;
}
@SuppressWarnings("unchecked")
@Override
public Collection<Version> getAllVersions()
{
return (Collection<Version>)Collections.EMPTY_LIST;
}
@Override
public Version getPredecessor(Version version)
{
return null;
}
@SuppressWarnings("unchecked")
@Override
public Collection<Version> getSuccessors(Version version)
{
return (Collection<Version>)Collections.EMPTY_LIST;
}
@Override
public Version getVersion(String versionLabel)
{
return null;
}
}
/**
* Freezes audit aspect properties.
*
@@ -533,12 +595,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 +647,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 +826,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.