RM-1635: Recordable Version Store Service Prototype

* integration tests to ensure recorded version retireves the state of the versioned content accurately
 * various improvements to satisfy tests



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@81055 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2014-08-25 06:08:06 +00:00
parent a64f4db528
commit c0c2d3ca39
25 changed files with 1043 additions and 187 deletions

View File

@@ -195,7 +195,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
};
// run as system user
runAsSystem(work);
authenticationUtil.runAsSystem(work);
}
}
@@ -561,7 +561,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
if (!getHeld(hold).contains(nodeRef))
{
// run as system to ensure we have all the appropriate permissions to perform the manipulations we require
runAsSystem(new RunAsWork<Void>()
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork()
@@ -684,7 +684,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
{
// run as system so we don't run into further permission issues
// we already know we have to have the correct capability to get here
runAsSystem(new RunAsWork<Void>()
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork()
@@ -703,7 +703,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
}
// run as system as we can't be sure if have remove aspect rights on node
runAsSystem(new RunAsWork<Void>()
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork()

View File

@@ -79,6 +79,29 @@ public interface RecordService
*/
@Deprecated
Set<QName> getRecordMetaDataAspects();
/**
* Indicates whether the provided aspect is a registered record meta-data
* aspect.
*
* @param aspect aspect {@link QName}
* @return boolean true if the aspect is a registered record meta-data aspect, false otherwise
*
* @since 2.3
*/
boolean isRecordMetadataAspect(QName aspect);
/**
* Indicates whther the provided property is declared on a registered record
* meta-data aspect.
*
* @param property property {@link QName}
* @return boolean true if the property is declared on a registered record meta-data aspect,
* false otherwise
*
* @since 2.3
*/
boolean isRecordMetadataProperty(QName property);
/**
* Gets a list of all the record metadata aspects relevant to the file plan type of the

View File

@@ -47,9 +47,11 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.model.security.ModelAccessDeniedException;
import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.report.ReportModel;
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.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.ClassPolicyDelegate;
@@ -136,10 +138,12 @@ public class RecordServiceImpl extends BaseBehaviourBean
};
/** record model URI's */
private static final String[] RECORD_MODEL_URIS = new String[]
public static final String[] RECORD_MODEL_URIS = new String[]
{
RM_URI,
RM_CUSTOM_URI,
ReportModel.RMR_URI,
RecordableVersionModel.RMV_URI,
DOD5015Model.DOD_URI
};
@@ -682,6 +686,35 @@ public class RecordServiceImpl extends BaseBehaviourBean
return getRecordMetadataAspects(TYPE_FILE_PLAN);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isRecordMetadataAspect(org.alfresco.service.namespace.QName)
*/
@Override
public boolean isRecordMetadataAspect(QName aspect)
{
return getRecordMetadataAspectsMap().containsKey(aspect);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isRecordMetadataProperty(org.alfresco.service.namespace.QName)
*/
@Override
public boolean isRecordMetadataProperty(QName property)
{
boolean result = false;
PropertyDefinition propertyDefinition = dictionaryService.getProperty(property);
if (propertyDefinition != null)
{
ClassDefinition classDefinition = propertyDefinition.getContainerClass();
if (classDefinition != null &&
getRecordMetadataAspectsMap().containsKey(classDefinition.getName()))
{
result = true;
}
}
return result;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects(org.alfresco.service.cmr.repository.NodeRef)
*/

View File

@@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.security;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -31,6 +32,7 @@ import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
@@ -236,18 +238,24 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl
// get the reference count
Map<String, Integer> referenceCountMap = (Map<String, Integer>)nodeService.getProperty(filePlan, propertyName);
// set of assigned authorities
Set<String> assignedAuthorities = new HashSet<String>(authorities.size());
for (String authority : authorities)
{
if ((!authority.equals(PermissionService.ALL_AUTHORITIES) && !authority.equals(PermissionService.OWNER_AUTHORITY)) &&
if ((!authority.equals(PermissionService.ALL_AUTHORITIES) &&
!authority.equals(PermissionService.OWNER_AUTHORITY)) &&
!AuthorityType.ROLE.equals(AuthorityType.getAuthorityType(authority)) &&
(referenceCountMap == null || !referenceCountMap.containsKey(authority)))
{
// add the authority to the role
filePlanRoleService.assignRoleToAuthority(filePlan, roleName, authority);
assignedAuthorities.add(authority);
}
}
// update the reference count
nodeService.setProperty(filePlan, propertyName, (Serializable)addToMap(referenceCountMap, authorities));
nodeService.setProperty(filePlan, propertyName, (Serializable)addToMap(referenceCountMap, assignedAuthorities));
}
}

View File

@@ -225,7 +225,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
if (nodeService.exists(parent))
{
runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork()
{
@@ -300,7 +300,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
*/
public void onAddRecord(final NodeRef record, final QName aspectTypeQName)
{
runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork()
{
@@ -375,7 +375,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
{
if (nodeService.exists(nodeRef))
{
runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork()
{
@@ -410,7 +410,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
ParameterCheck.mandatory("authority", authority);
ParameterCheck.mandatory("permission", permission);
runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{
public Void doWork()
{
@@ -557,7 +557,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
*/
public void deletePermission(final NodeRef nodeRef, final String authority, final String permission)
{
runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{
public Void doWork()
{

View File

@@ -0,0 +1,81 @@
/*
* 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.util;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
/**
* Helper bean to allow injection of AuthenticationUtil methods.
* <p>
* Useful when testing using mocks.
*
* @author Roy Wetherall
* @since 2.3
*/
public class AuthenticationUtil
{
/**
* Helper method that executed work as system user.
* <p>
* Useful when testing using mocks.
*
* @see org.alfresco.repo.security.authentication.AuthenticationUtil#runAsSystem(RunAsWork, String)
*/
public <R> R runAsSystem(RunAsWork<R> runAsWork)
{
return org.alfresco.repo.security.authentication.AuthenticationUtil.runAsSystem(runAsWork);
}
/**
* Helper method that executed work as given user.
* <p>
* Useful when testing using mocks.
*
* @see org.alfresco.repo.security.authentication.AuthenticationUtil#runAs(RunAsWork, String)
*/
public <R> R runAs(RunAsWork<R> runAsWork, String uid)
{
return org.alfresco.repo.security.authentication.AuthenticationUtil.runAs(runAsWork, uid);
}
/**
* Helper method that gets the fully authenticated user.
* <p>
* Useful when testing using mocks.
*
* @see org.alfresco.repo.security.authentication.AuthenticationUtil#getFullyAuthenticatedUser()
*/
public String getFullyAuthenticatedUser()
{
return org.alfresco.repo.security.authentication.AuthenticationUtil.getFullyAuthenticatedUser();
}
/**
* Helper method that gets the admin user name.
* <p>
* Usefule when testing using mocks.
*
* @see org.alfresco.repo.security.authentication.AuthenticationUtil#getAdminUserName()
*/
public String getAdminUserName()
{
return org.alfresco.repo.security.authentication.AuthenticationUtil.getAdminUserName();
}
}

View File

@@ -26,8 +26,6 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -58,6 +56,9 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
/** internal node service */
private NodeService internalNodeService;
/** authentication helper */
protected AuthenticationUtil authenticationUtil;
/**
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
@@ -84,6 +85,14 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
this.dictionaryService = dictionaryService;
}
/**
* @param authenticationUtil authentication util helper
*/
public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
{
this.authenticationUtil = authenticationUtil;
}
/**
* Helper to get internal node service.
* <p>
@@ -470,30 +479,4 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte
result.add(nodeService.getType(nodeRef));
return result;
}
/**
* Helper method that executed work as system user.
* <p>
* Useful when testing using mocks.
*
* @param runAsWork work to execute as system user
* @return
*/
public <R> R runAsSystem(RunAsWork<R> runAsWork)
{
return AuthenticationUtil.runAsSystem(runAsWork);
}
/**
* Helper method that executed work as given user.
* <p>
* Useful when testing using mocks.
*
* @param runAsWork work to execute as given user
* @return
*/
public <R> R runAs(RunAsWork<R> runAsWork, String uid)
{
return AuthenticationUtil.runAs(runAsWork, uid);
}
}

View File

@@ -40,4 +40,5 @@ public interface RecordableVersionModel
/** recorded version aspect */
public QName ASPECT_RECORDED_VERSION = QName.createQName(RMV_URI, "recordedVersion");
public QName PROP_RECORD_NODE_REF = QName.createQName(RMV_URI, "recordNodeRef");
public QName PROP_FROZEN_OWNER = QName.createQName(RMV_URI, "frozenOwner");
}

View File

@@ -19,17 +19,27 @@
package org.alfresco.module.org_alfresco_module_rm.version;
import java.io.Serializable;
import java.util.Date;
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.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl;
import org.alfresco.repo.version.Node2ServiceImpl;
import org.alfresco.repo.version.Version2Model;
import org.alfresco.repo.version.common.VersionUtil;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.lang.ArrayUtils;
/**
* Extended version node service implementation that supports the retrieval of
* recorded version state.
*
* @author Roy Wetherall
* @since 2.3
@@ -37,16 +47,31 @@ import org.alfresco.service.namespace.QName;
public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl
implements RecordableVersionModel
{
/** record service */
private RecordService recordService;
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/**
* @see org.alfresco.repo.version.Node2ServiceImpl#getProperties(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public Map<QName, Serializable> getProperties(NodeRef nodeRef) throws InvalidNodeRefException
{
// TODO only supported for Version2
NodeRef converted = VersionUtil.convertNodeRef(nodeRef);
if (dbNodeService.hasAspect(converted, ASPECT_RECORDED_VERSION))
{
NodeRef record = (NodeRef)dbNodeService.getProperty(converted, PROP_RECORD_NODE_REF);
Map<QName, Serializable> properties = dbNodeService.getProperties(record);
return processProperties(properties);
return processProperties(converted, properties);
}
else
{
@@ -54,18 +79,153 @@ public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl
}
}
protected Map<QName, Serializable> processProperties(Map<QName, Serializable> properties)
/**
* Process properties map before returning as frozen state.
*
* @param properties properties map
* @return {@link Map}<{@link QName}, {@link Serializable}> processed property map
*/
protected Map<QName, Serializable> processProperties(NodeRef version, Map<QName, Serializable> properties)
{
Map<QName, Serializable> cloneProperties = new HashMap<QName, Serializable>(properties);
// revert modified record name
properties.put(ContentModel.PROP_NAME, properties.get(RecordsManagementModel.PROP_ORIGIONAL_NAME));
// remove all rma, rmc, rmr and rmv properties
// remove any properties relating to custom record-meta data
for (QName property : cloneProperties.keySet())
{
if (!PROP_RECORDABLE_VERSION_POLICY.equals(property) &&
!PROP_FILE_PLAN.equals(property) &&
(recordService.isRecordMetadataProperty(property) ||
ArrayUtils.contains(RecordServiceImpl.RECORD_MODEL_URIS, property.getNamespaceURI())))
{
properties.remove(property);
}
}
// do standard property processing
VersionUtil.convertFrozenToOriginalProps(properties);
processVersionProperties(version, properties);
return properties;
}
/**
* Process version properties.
*
* @param version version node reference
* @param properties properties map
*/
protected void processVersionProperties(NodeRef version, Map<QName, Serializable> properties) throws InvalidNodeRefException
{
// get version properties
Map<QName, Serializable> versionProperties = dbNodeService.getProperties(version);
if (versionProperties != null)
{
String versionLabel = (String)versionProperties.get(Version2Model.PROP_QNAME_VERSION_LABEL);
properties.put(ContentModel.PROP_VERSION_LABEL, versionLabel);
// Convert frozen sys:referenceable properties
NodeRef nodeRef = (NodeRef)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF);
if (nodeRef != null)
{
properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol());
properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier());
properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId());
}
Long dbid = (Long)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_DBID);
properties.put(ContentModel.PROP_NODE_DBID, dbid);
// Convert frozen cm:auditable properties
String creator = (String)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_CREATOR);
if (creator != null)
{
properties.put(ContentModel.PROP_CREATOR, creator);
}
Date created = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_CREATED);
if (created != null)
{
properties.put(ContentModel.PROP_CREATED, created);
}
// TODO - check use-cases for get version, revert, restore ....
String modifier = (String)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_MODIFIER);
if (modifier != null)
{
properties.put(ContentModel.PROP_MODIFIER, modifier);
}
Date modified = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_MODIFIED);
if (modified != null)
{
properties.put(ContentModel.PROP_MODIFIED, modified);
}
Date accessed = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_ACCESSED);
if (accessed != null)
{
properties.put(ContentModel.PROP_ACCESSED, accessed);
}
String owner = (String)versionProperties.get(PROP_FROZEN_OWNER);
if (owner != null)
{
properties.put(ContentModel.PROP_OWNER, owner);
}
}
}
/**
* @see org.alfresco.repo.version.Node2ServiceImpl#getAspects(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public Set<QName> getAspects(NodeRef nodeRef) throws InvalidNodeRefException
{
// TODO only supported for Version2
NodeRef converted = VersionUtil.convertNodeRef(nodeRef);
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);
}
else
{
return super.getAspects(nodeRef);
}
}
/**
* Process frozen aspects.
*
* @param aspects aspect set
* @return {@link Set}<{@link QName}> processed aspect set
*/
protected Set<QName> processAspects(Set<QName> aspects)
{
Set<QName> result = new HashSet<QName>(aspects);
// remove version aspects
result.remove(ASPECT_VERSION);
result.remove(ASPECT_RECORDED_VERSION);
// remove rm aspects
for (QName aspect : aspects)
{
if (!ASPECT_VERSIONABLE.equals(aspect) &&
(recordService.isRecordMetadataAspect(aspect) ||
ArrayUtils.contains(RecordServiceImpl.RECORD_MODEL_URIS, aspect.getNamespaceURI())))
{
result.remove(aspect);
}
}
// remove custom record meta-data aspects
return result;
}
}

View File

@@ -21,20 +21,29 @@ package org.alfresco.module.org_alfresco_module_rm.version;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
import org.alfresco.repo.policy.PolicyScope;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService;
import org.alfresco.repo.version.Version2Model;
import org.alfresco.repo.version.Version2ServiceImpl;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.version.ReservedVersionNameException;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionType;
@@ -64,6 +73,18 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** file folder service */
protected FileFolderService fileFolderService;
/** extended permission service */
protected ExtendedPermissionService extendedPermissionService;
/** ownable service */
protected OwnableService ownableService;
/** extended security service */
protected ExtendedSecurityService extendedSecurityService;
/** authentication util helper */
protected AuthenticationUtil authenticationUtil;
/**
* @param filePlanService file plan service
*/
@@ -80,6 +101,38 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
this.fileFolderService = fileFolderService;
}
/**
* @param extendedPermissionService extended permission service
*/
public void setExtendedPermissionService(ExtendedPermissionService extendedPermissionService)
{
this.extendedPermissionService = extendedPermissionService;
}
/**
* @param ownableService ownable service
*/
public void setOwnableService(OwnableService ownableService)
{
this.ownableService = ownableService;
}
/**
* @param extendedSecurityService extended security service
*/
public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService)
{
this.extendedSecurityService = extendedSecurityService;
}
/**
* @param authenticationUtil authentication util helper
*/
public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
{
this.authenticationUtil = authenticationUtil;
}
/**
* @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int)
*/
@@ -128,9 +181,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
}
/**
*
* @param nodeRef
* @return
* @param nodeRef node reference
* @return {@link NodeRef} associated file plan, default if none
*/
private NodeRef getFilePlan(NodeRef nodeRef)
{
@@ -143,8 +195,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
}
/**
*
* @return
* @return {@link NodeRef} default file plan, exception if none
*/
private NodeRef getFilePlan()
{
@@ -212,6 +263,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
}
/**
* Creates a new recorded version
*
* @param sourceTypeRef
* @param versionHistoryRef
@@ -240,61 +292,35 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
try
{
// get the destination file plan
NodeRef filePlan = (NodeRef)versionProperties.get(KEY_FILE_PLAN);
final NodeRef filePlan = (NodeRef)versionProperties.get(KEY_FILE_PLAN);
if (filePlan == null)
{
throw new AlfrescoRuntimeException("Can't create a new recorded version, because no file plan has been specified in the version properties.");
}
// get the unfiled record folder
final NodeRef unfiledRecordFolder = filePlanService.getUnfiledContainer(filePlan);
// create a copy of the source node and place in the file plan
final NodeRef nodeRef = (NodeRef)standardVersionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF);
// copy version state and create record
NodeRef record = null;
try
{
FileInfo recordInfo = fileFolderService.copy(nodeRef, unfiledRecordFolder, null);
record = recordInfo.getNodeRef();
}
catch (FileNotFoundException e)
{
throw new AlfrescoRuntimeException("Can't create recorded version, because copy fails.", e);
}
// set up extended permissions
// TODO
// create record
NodeRef record = createRecord(nodeRef, filePlan);
// create version nodeRef
ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
ChildAssociationRef childAssocRef = dbNodeService.createNode(
versionHistoryRef,
Version2Model.CHILD_QNAME_VERSIONS,
QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.CHILD_VERSIONS + "-" + versionNumber), // TODO - testing - note: all children (of a versioned node) will have the same version number, maybe replace with a version sequence of some sort 001-...00n
sourceTypeRef,
null);
versionNodeRef = childAssocRef.getChildRef();
// NOTE: special ML case - see also MultilingualContentServiceImpl.makeMLContainer
// if (sourceTypeRef.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER))
// {
// // Set the permissions to allow anything by anyone
// permissionService.setPermission(
// versionNodeRef,
// PermissionService.ALL_AUTHORITIES,
// PermissionService.ALL_PERMISSIONS, true);
// permissionService.setPermission(
// versionNodeRef,
// AuthenticationUtil.getGuestUserName(),
// PermissionService.ALL_PERMISSIONS, true);
// }
versionNodeRef = childAssocRef.getChildRef();
// add aspect with the standard version properties to the 'version' node
nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, standardVersionProperties);
// add the recordedVersion aspect with link to record
nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable)record));
// freeze auditable aspect information
freezeAuditableAspect(nodeRef, versionNodeRef);
}
finally
{
@@ -318,6 +344,118 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
return versionNodeRef;
}
/**
* Create record from current version
*
* @param nodeRef state to freeze
* @param filePlan destination file plan
* @return {@link NodeRef} versioned record
*/
private NodeRef createRecord(final NodeRef nodeRef, final NodeRef filePlan)
{
return authenticationUtil.runAs(new RunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
// get the unfiled record folder
final NodeRef unfiledRecordFolder = filePlanService.getUnfiledContainer(filePlan);
// get the documents readers
Long aclId = dbNodeService.getNodeAclId(nodeRef);
Set<String> readers = extendedPermissionService.getReaders(aclId);
Set<String> writers = extendedPermissionService.getWriters(aclId);
// add the current owner to the list of extended writers
Set<String> modifiedWrtiers = new HashSet<String>(writers);
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE))
{
String owner = ownableService.getOwner(nodeRef);
if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER))
{
modifiedWrtiers.add(owner);
}
}
// add the current user as extended writer
modifiedWrtiers.add(authenticationUtil.getFullyAuthenticatedUser());
// copy version state and create record
NodeRef record = null;
try
{
List<AssociationRef> originalAssocs = null;
if (dbNodeService.hasAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM))
{
// take a note of any copyFrom information already on the node
originalAssocs = dbNodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL);
}
// create a copy of the original state and add it to the unfiled record container
FileInfo recordInfo = fileFolderService.copy(nodeRef, unfiledRecordFolder, null);
record = recordInfo.getNodeRef();
// remove added copy assocs
List<AssociationRef> recordAssocs = dbNodeService.getTargetAssocs(record, ContentModel.ASSOC_ORIGINAL);
for (AssociationRef recordAssoc : recordAssocs)
{
dbNodeService.removeAssociation(
recordAssoc.getSourceRef(),
recordAssoc.getTargetRef(),
ContentModel.ASSOC_ORIGINAL);
}
// re-add origional assocs or remove aspect
if (originalAssocs == null)
{
dbNodeService.removeAspect(record, ContentModel.ASPECT_COPIEDFROM);
}
else
{
for (AssociationRef originalAssoc : originalAssocs)
{
dbNodeService.createAssociation(originalAssoc.getSourceRef(), originalAssoc.getTargetRef(), ContentModel.ASSOC_ORIGINAL);
}
}
}
catch (FileNotFoundException e)
{
throw new AlfrescoRuntimeException("Can't create recorded version, because copy fails.", e);
}
// set extended security on record
extendedSecurityService.addExtendedSecurity(record, readers, writers);
return record;
}
}, authenticationUtil.getAdminUserName());
}
/**
* Freezes audit aspect properties.
*
* @param nodeRef
* @param versionNodeRef
*/
private void freezeAuditableAspect(NodeRef nodeRef, NodeRef versionNodeRef)
{
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_AUDITABLE))
{
Map<QName, Serializable> properties = dbNodeService.getProperties(nodeRef);
dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATOR, properties.get(ContentModel.PROP_CREATOR));
dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATED, properties.get(ContentModel.PROP_CREATED));
dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIER, properties.get(ContentModel.PROP_MODIFIER));
dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIED, properties.get(ContentModel.PROP_MODIFIED));
dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_ACCESSED, properties.get(ContentModel.PROP_ACCESSED));
if (properties.get(ContentModel.PROP_OWNER) != null)
{
dbNodeService.setProperty(versionNodeRef, PROP_FROZEN_OWNER, properties.get(ContentModel.PROP_OWNER));
}
}
}
/**
* @see org.alfresco.repo.version.Version2ServiceImpl#getVersion(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected Version getVersion(NodeRef versionRef)
{