Merge branch 'release/V2.3' into merge-2.4/MergeV23ToV24_101016_2

# Conflicts:
#	rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml
#	rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java
#	rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseWebScriptUnitTest.java
#	rm-community/rm-community-share/config/alfresco/site-webscripts/org/alfresco/rm/components/audit/audit.get.html.ftl
#	rm-server/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java
#	rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java
#	rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/IssueTestSuite.java
#	rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/RecordTestSuite.java
#	rm-server/unit-test/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGetUnitTest.java
This commit is contained in:
Tuna Aksoy
2016-10-10 20:49:13 +01:00
18 changed files with 1307 additions and 685 deletions

View File

@@ -86,3 +86,10 @@ rm.patch.v22.convertToStandardFilePlan=false
# a document will be auto-versioned when its type is changed. # a document will be auto-versioned when its type is changed.
# #
version.store.enableAutoVersionOnTypeChange=false version.store.enableAutoVersionOnTypeChange=false
#
# Enable auto-version to be created when there is a difference between the document and latest record state
# to ensure that the created version record matches the current document state,
# otherwise create the version record from the version history
#
rm.enableAutoVersionOnRecordCreation=false

View File

@@ -18,4 +18,5 @@ rm.service.vital-def-missing=Vital record definition aspect is not present on no
rm.service.close-record-folder-not-folder=The record folder couldn't be closed because it's not defined as a record folder.(nodeRef={0}) rm.service.close-record-folder-not-folder=The record folder couldn't be closed because it's not defined as a record folder.(nodeRef={0})
rm.service.node-has-aspect=The node {0} has already the aspect {1}. rm.service.node-has-aspect=The node {0} has already the aspect {1}.
rm.service.final-version=Final rm.service.final-version=Final
rm.service.final-version-description=The final archived record version rm.service.final-version-description=The final archived record version
rm.service.enable-autoversion-on-record-creation=Auto Version on Record Creation

View File

@@ -1151,4 +1151,4 @@
<property name="auditable" value="false"/> <property name="auditable" value="false"/>
</bean> </bean>
</beans> </beans>

View File

@@ -154,6 +154,7 @@
org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getLastCompletedDispostionAction=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getLastCompletedDispostionAction=RM.Read.0
org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isDisposableItemCutoff=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isDisposableItemCutoff=RM.Read.0
org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.cutoffDisposableItem=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.cutoffDisposableItem=RM.Read.0
org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.calculateAsOfDate=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.*=RM_DENY org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.*=RM_DENY
]]> ]]>
</value> </value>

View File

@@ -11,7 +11,7 @@
</list> </list>
</property> </property>
</bean> </bean>
<!-- Recordable version model behaviours --> <!-- Recordable version model behaviours -->
<bean id="rmv.versionRecord" class="org.alfresco.module.org_alfresco_module_rm.model.rma.aspect.VersionRecordAspect" parent="rm.baseBehaviour"> <bean id="rmv.versionRecord" class="org.alfresco.module.org_alfresco_module_rm.model.rma.aspect.VersionRecordAspect" parent="rm.baseBehaviour">
<property name="recordableVersionService" ref="RecordableVersionService" /> <property name="recordableVersionService" ref="RecordableVersionService" />
@@ -21,6 +21,7 @@
<!-- extended version service bean definition --> <!-- extended version service bean definition -->
<bean id="rm.versionService" abstract="true" class="org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl"> <bean id="rm.versionService" abstract="true" class="org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl">
<property name="filePlanService" ref="FilePlanService" /> <property name="filePlanService" ref="FilePlanService" />
<property name="enableAutoVersionOnRecordCreation" value="${rm.enableAutoVersionOnRecordCreation}" />
<property name="authenticationUtil" ref="rm.authenticationUtil" /> <property name="authenticationUtil" ref="rm.authenticationUtil" />
<property name="relationshipService" ref="RelationshipService" /> <property name="relationshipService" ref="RelationshipService" />
<property name="recordService" ref="RecordService" /> <property name="recordService" ref="RecordService" />
@@ -44,8 +45,8 @@
</bean> </bean>
<!-- Extended auto-version behaviour --> <!-- Extended auto-version behaviour -->
<bean id="rm.extendedVersionableAspect" class="org.alfresco.module.org_alfresco_module_rm.version.ExtendedVersionableAspect"> <bean id="rm.extendedVersionableAspect" class="org.alfresco.module.org_alfresco_module_rm.version.ExtendedVersionableAspect">
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="versionService" ref="VersionService" /> <property name="versionService" ref="VersionService" />
<property name="lockService" ref="LockService" /> <property name="lockService" ref="LockService" />
<property name="authenticationUtil" ref="rm.authenticationUtil" /> <property name="authenticationUtil" ref="rm.authenticationUtil" />
@@ -56,7 +57,7 @@
<!-- rmv:versionable aspect behaviour bean --> <!-- rmv:versionable aspect behaviour bean -->
<bean id="rm.versionableAspect" class="org.alfresco.module.org_alfresco_module_rm.version.model.VersionableAspect" parent="rm.baseBehaviour"> <bean id="rm.versionableAspect" class="org.alfresco.module.org_alfresco_module_rm.version.model.VersionableAspect" parent="rm.baseBehaviour">
</bean> </bean>
<!-- recordable version service --> <!-- recordable version service -->
<bean id="RecordableVersionService" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="RecordableVersionService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService" /> <property name="proxyInterfaces" value="org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService" />
@@ -77,6 +78,7 @@
<value> <value>
<![CDATA[ <![CDATA[
org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.isCurrentVersionRecorded=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.isCurrentVersionRecorded=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.isEnableAutoVersionOnRecordCreation=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.isRecordedVersion=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.isRecordedVersion=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.getVersionRecord=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.getVersionRecord=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.getRecordedVersion=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService.getRecordedVersion=RM_ALLOW

View File

@@ -246,17 +246,11 @@ public class BroadcastDispositionActionDefinitionUpdateAction extends RMActionEx
* @param dispositionActionDef The disposition action definition node * @param dispositionActionDef The disposition action definition node
* @param nextAction The next disposition action * @param nextAction The next disposition action
*/ */
private void persistPeriodChanges(NodeRef dispositionActionDef, DispositionAction nextAction) protected void persistPeriodChanges(NodeRef dispositionActionDef, DispositionAction nextAction)
{ {
Date newAsOfDate = null; NodeRef dispositionedNode = getNodeService().getPrimaryParent(nextAction.getNodeRef()).getParentRef();
Period dispositionPeriod = (Period) getNodeService().getProperty(dispositionActionDef, PROP_DISPOSITION_PERIOD); DispositionActionDefinition definition = nextAction.getDispositionActionDefinition();
Date newAsOfDate = getDispositionService().calculateAsOfDate(dispositionedNode, definition, false);
if (dispositionPeriod != null)
{
// calculate the new as of date as we have been provided a new period
Date now = new Date();
newAsOfDate = dispositionPeriod.getNextDate(now);
}
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {

View File

@@ -29,6 +29,7 @@ package org.alfresco.module.org_alfresco_module_rm.disposition;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -241,4 +242,15 @@ public interface DispositionService
* @param nodeRef node reference * @param nodeRef node reference
*/ */
void refreshDispositionAction(NodeRef nodeRef); void refreshDispositionAction(NodeRef nodeRef);
/**
* Compute the "disposition as of" date (if necessary) for a disposition action and a node.
*
* @param nodeRef The node which the schedule applies to.
* @param dispositionActionDefinition The definition of the disposition action.
* @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property.
* @return The new "disposition as of" date.
*/
Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition,
boolean allowContextFromAsOf);
} }

View File

@@ -62,8 +62,8 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.ParameterCheck; import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log; import org.slf4j.Logger;
import org.apache.commons.logging.LogFactory; import org.slf4j.LoggerFactory;
/** /**
* Disposition service implementation. * Disposition service implementation.
@@ -77,7 +77,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
RecordsManagementPolicies.OnFileRecord RecordsManagementPolicies.OnFileRecord
{ {
/** Logger */ /** Logger */
private static Log logger = LogFactory.getLog(DispositionServiceImpl.class); private static final Logger LOGGER = LoggerFactory.getLogger(DispositionServiceImpl.class);
/** Behaviour filter */ /** Behaviour filter */
private BehaviourFilter behaviourFilter; private BehaviourFilter behaviourFilter;
@@ -226,7 +226,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
DispositionActionDefinition nextDispositionActionDefinition = dispositionActionDefinitions.get(0); DispositionActionDefinition nextDispositionActionDefinition = dispositionActionDefinitions.get(0);
// initialise the details of the next disposition action // initialise the details of the next disposition action
initialiseDispositionAction(nodeRef, nextDispositionActionDefinition); initialiseDispositionAction(nodeRef, nextDispositionActionDefinition, true);
} }
} }
} }
@@ -396,12 +396,10 @@ public class DispositionServiceImpl extends ServiceBaseImpl
{ {
// TODO in the future we should be able to support disposition schedule reuse, but for now just warn that // TODO in the future we should be able to support disposition schedule reuse, but for now just warn that
// only the first disposition schedule will be considered // only the first disposition schedule will be considered
if (logger.isWarnEnabled()) LOGGER.warn("Disposition schedule has more than one associated records management container. "
{ + "This is not currently supported so only the first container will be considered. "
logger.warn("Disposition schedule has more than one associated records management container. " + + "(dispositionScheduleNodeRef={})",
"This is not currently supported so only the first container will be considered. " + dispositionSchedule.getNodeRef().toString());
"(dispositionScheduleNodeRef=" + dispositionSchedule.getNodeRef().toString() + ")");
}
} }
// Get the container reference // Get the container reference
@@ -627,42 +625,16 @@ public class DispositionServiceImpl extends ServiceBaseImpl
* Initialises the details of the next disposition action based on the details of a disposition * Initialises the details of the next disposition action based on the details of a disposition
* action definition. * action definition.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param dispositionActionDefinition disposition action definition * @param dispositionActionDefinition disposition action definition
* @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property.
*/ */
private void initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition) private void initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, boolean allowContextFromAsOf)
{ {
// Create the properties // Create the properties
Map<QName, Serializable> props = new HashMap<QName, Serializable>(10); Map<QName, Serializable> props = new HashMap<QName, Serializable>(10);
// Calculate the asOf date Date asOfDate = calculateAsOfDate(nodeRef, dispositionActionDefinition, allowContextFromAsOf);
Date asOfDate = null;
Period period = dispositionActionDefinition.getPeriod();
if (period != null)
{
Date contextDate = null;
// Get the period properties value
QName periodProperty = dispositionActionDefinition.getPeriodProperty();
if (periodProperty != null)
{
// doesn't matter if the period property isn't set ... the asOfDate will get updated later
// when the value of the period property is set
contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty);
}
else
{
// for now use 'NOW' as the default context date
// TODO set the default period property ... cut off date or last disposition date depending on context
contextDate = new Date();
}
// Calculate the as of date
if (contextDate != null)
{
asOfDate = period.getNextDate(contextDate);
}
}
// Set the property values // Set the property values
props.put(PROP_DISPOSITION_ACTION_ID, dispositionActionDefinition.getId()); props.put(PROP_DISPOSITION_ACTION_ID, dispositionActionDefinition.getId());
@@ -690,6 +662,50 @@ public class DispositionServiceImpl extends ServiceBaseImpl
} }
} }
/**
* Compute the "disposition as of" date (if necessary) for a disposition action and a node.
*
* @param nodeRef The node which the schedule applies to.
* @param dispositionActionDefinition The definition of the disposition action.
* @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property.
* @return The new "disposition as of" date.
*/
@Override
public Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition,
boolean allowContextFromAsOf)
{
// Calculate the asOf date
Date asOfDate = null;
Period period = dispositionActionDefinition.getPeriod();
if (period != null)
{
Date contextDate = null;
// Get the period properties value
QName periodProperty = dispositionActionDefinition.getPeriodProperty();
if (periodProperty != null && (allowContextFromAsOf
|| !RecordsManagementModel.PROP_DISPOSITION_AS_OF.equals(periodProperty)))
{
// doesn't matter if the period property isn't set ... the asOfDate will get updated later
// when the value of the period property is set
contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty);
}
else
{
// for now use 'NOW' as the default context date
// TODO set the default period property ... cut off date or last disposition date depending on context
contextDate = new Date();
}
// Calculate the as of date
if (contextDate != null)
{
asOfDate = period.getNextDate(contextDate);
}
}
return asOfDate;
}
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isNextDispositionActionEligible(org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isNextDispositionActionEligible(org.alfresco.service.cmr.repository.NodeRef)
*/ */
@@ -914,63 +930,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
nodeService.addAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE, null); nodeService.addAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE, null);
} }
// Create the properties initialiseDispositionAction(nodeRef, nextDispositionActionDefinition, false);
Map<QName, Serializable> props = new HashMap<QName, Serializable>(10);
// Calculate the asOf date
Date asOfDate = null;
Period period = nextDispositionActionDefinition.getPeriod();
if (period != null)
{
Date contextDate = null;
// Get the period properties value
QName periodProperty = nextDispositionActionDefinition.getPeriodProperty();
if (periodProperty != null &&
!RecordsManagementModel.PROP_DISPOSITION_AS_OF.equals(periodProperty))
{
// doesn't matter if the period property isn't set ... the asOfDate will get updated later
// when the value of the period property is set
contextDate = (Date) nodeService.getProperty(nodeRef, periodProperty);
}
else
{
// for now use 'NOW' as the default context date
// TODO set the default period property ... cut off date or last disposition date depending on context
contextDate = new Date();
}
// Calculate the as of date
if (contextDate != null)
{
asOfDate = period.getNextDate(contextDate);
}
}
// Set the property values
props.put(PROP_DISPOSITION_ACTION_ID, nextDispositionActionDefinition.getId());
props.put(PROP_DISPOSITION_ACTION, nextDispositionActionDefinition.getName());
if (asOfDate != null)
{
props.put(PROP_DISPOSITION_AS_OF, asOfDate);
}
// Create a new disposition action object
NodeRef dispositionActionNodeRef = nodeService.createNode(
nodeRef,
ASSOC_NEXT_DISPOSITION_ACTION,
ASSOC_NEXT_DISPOSITION_ACTION,
TYPE_DISPOSITION_ACTION,
props).getChildRef();
DispositionAction da = new DispositionActionImpl(serviceRegistry, dispositionActionNodeRef);
// Create the events
List<RecordsManagementEvent> events = nextDispositionActionDefinition.getEvents();
for (RecordsManagementEvent event : events)
{
// For every event create an entry on the action
da.addEventCompletionDetails(event);
}
} }
} }
@@ -1025,7 +985,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
recordFolderService.closeRecordFolder(nodeRef); recordFolderService.closeRecordFolder(nodeRef);
return null; return null;
} }
}); });
} }
} }
else else

View File

@@ -1058,7 +1058,9 @@ public class RecordServiceImpl extends BaseBehaviourBean
private NodeRef getLatestVersionRecord(NodeRef nodeRef) private NodeRef getLatestVersionRecord(NodeRef nodeRef)
{ {
NodeRef versionRecord = null; NodeRef versionRecord = null;
recordableVersionService.createSnapshotVersion(nodeRef);
// wire record up to previous record // wire record up to previous record
VersionHistory versionHistory = versionService.getVersionHistory(nodeRef); VersionHistory versionHistory = versionService.getVersionHistory(nodeRef);
if (versionHistory != null) if (versionHistory != null)

View File

@@ -36,70 +36,93 @@ import org.alfresco.service.cmr.version.Version;
* @author Roy Wetherall * @author Roy Wetherall
* @since 2.3 * @since 2.3
*/ */
public interface RecordableVersionService public interface RecordableVersionService
{ {
/** /**
* Indicates whether the current version of a node is recorded or not. * Indicates whether the current version of a node is recorded or not.
* <p> * <p>
* Returns false if not versionable or no version. * Returns false if not versionable or no version.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @return boolean true if latest version recorded, false otherwise * @return boolean true if latest version recorded, false otherwise
*/ */
boolean isCurrentVersionRecorded(NodeRef nodeRef); boolean isCurrentVersionRecorded(NodeRef nodeRef);
/** /**
* Indicates whether a version is recorded or not. * Indicates whether a version is recorded or not.
* *
* @param version version * @param version version
* @return boolean true if recorded version, false otherwise * @return boolean true if recorded version, false otherwise
*/ */
boolean isRecordedVersion(Version version); boolean isRecordedVersion(Version version);
/** /**
* If the version is a recorded version, gets the related version * If the version is a recorded version, gets the related version record.
* record.
* *
* @param version version * @param version version
* @return NodeRef node reference of version record * @return NodeRef node reference of version record
*/ */
NodeRef getVersionRecord(Version version); NodeRef getVersionRecord(Version version);
/** /**
* Gets the version that relates to the version record * Gets the version that relates to the version record
* *
* @param versionRecord version record node reference * @param versionRecord version record node reference
* @return Version version or null if not found * @return Version version or null if not found
*/ */
Version getRecordedVersion(NodeRef record); Version getRecordedVersion(NodeRef record);
/**
* Creates a record from the latest frozen version, marking it as recorded.
* <p>
* Does not create a record if the node is not versionable or the latest version is already recorded.
*
* @param nodeRef node reference
* @return NodeRef node reference to the created record.
*/
NodeRef createRecordFromLatestVersion(NodeRef filePlan, NodeRef nodeRef);
/** /**
* Creates a record from the latest version, marking it as recorded. * Creates a record from the latest version, marking it as recorded.
* <p> * <p>
* Does not create a record if the node is not versionable or the latest * Does not create a record if the node is not versionable or the latest version is already recorded.
* version is already recorded. *
* @param nodeRef parent node reference
* @param nodeRef node reference
* @param autoVersion true, create new record version from latest version, false creates a record from the latest frozen version
* @return NodeRef node reference to the created record.
* *
* @param nodeRef node reference
* @return NodeRef node reference to the created record.
*/ */
NodeRef createRecordFromLatestVersion(NodeRef filePlan, NodeRef nodeRef); NodeRef createRecordFromLatestVersion(NodeRef filePlan, NodeRef nodeRef, boolean autoVersion);
/** /**
* Indicates whether a record version is destroyed or not. * Indicates whether a record version is destroyed or not.
* *
* @param version version * @param version version
* @return boolean true if destroyed, false otherwise * @return boolean true if destroyed, false otherwise
*/ */
boolean isRecordedVersionDestroyed(Version version); boolean isRecordedVersionDestroyed(Version version);
/** /**
* Marks a recorded version as destroyed. * Marks a recorded version as destroyed.
* <p> * <p>
* Note this method does not destroy the associated record, instead it marks the * Note this method does not destroy the associated record, instead it marks the version as destroyed.
* version as destroyed.
* *
* @param version version * @param version version
*/ */
void destroyRecordedVersion(Version version); void destroyRecordedVersion(Version version);
/**
* Flag that indicate to create new version on record creation if current node is modified
*
* @return boolean true to auto-version on record creation, false to use latest versioned version
*/
boolean isEnableAutoVersionOnRecordCreation();
/**
* Create a snapshot version of current node
*
* @param nodeRef node reference
*/
void createSnapshotVersion(NodeRef nodeRef);
} }

View File

@@ -33,6 +33,7 @@ import static org.codehaus.plexus.util.StringUtils.isNotBlank;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -66,6 +67,7 @@ import org.alfresco.util.PropertyMap;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/** /**
* Recordable version service implementation * Recordable version service implementation
@@ -88,6 +90,12 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
protected static final String PROP_VERSION_RECORD = "RecordVersion"; protected static final String PROP_VERSION_RECORD = "RecordVersion";
protected static final String PROP_RECORDED_VERSION_DESTROYED = "RecordedVersionDestroyed"; protected static final String PROP_RECORDED_VERSION_DESTROYED = "RecordedVersionDestroyed";
/** I18N */
private static final String AUTO_VERSION_ON_RECORD_CREATION = "rm.service.enable-autoversion-on-record-creation";
/** flag that enable auto-version on record creation */
private boolean isEnableAutoVersionOnRecordCreation = false;
/** version aspect property names */ /** version aspect property names */
private static final String[] VERSION_PROPERTY_NAMES = new String[] private static final String[] VERSION_PROPERTY_NAMES = new String[]
{ {
@@ -122,15 +130,15 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** cm object type */ /** cm object type */
private CmObjectType cmObjectType; private CmObjectType cmObjectType;
/** extended permission service */ /** extended permission service */
private ExtendedPermissionService extendedPermissionService; private ExtendedPermissionService extendedPermissionService;
/** extended security service */ /** extended security service */
private ExtendedSecurityService extendedSecurityService; private ExtendedSecurityService extendedSecurityService;
/** /**
* @param filePlanService file plan service * @param filePlanService file plan service
*/ */
public void setFilePlanService(FilePlanService filePlanService) public void setFilePlanService(FilePlanService filePlanService)
{ {
@@ -138,7 +146,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @param authenticationUtil authentication util helper * @param authenticationUtil authentication util helper
*/ */
public void setAuthenticationUtil(AuthenticationUtil authenticationUtil) public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
{ {
@@ -146,7 +154,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @param relationshipService relationship service * @param relationshipService relationship service
*/ */
public void setRelationshipService(RelationshipService relationshipService) public void setRelationshipService(RelationshipService relationshipService)
{ {
@@ -162,7 +170,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @param modelSecurityService model security service * @param modelSecurityService model security service
*/ */
public void setModelSecurityService(ModelSecurityService modelSecurityService) public void setModelSecurityService(ModelSecurityService modelSecurityService)
{ {
@@ -176,7 +184,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{ {
this.cmObjectType = cmObjectType; this.cmObjectType = cmObjectType;
} }
/** /**
* @param extendedPermissionService extended permission service * @param extendedPermissionService extended permission service
*/ */
@@ -184,9 +192,9 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{ {
this.extendedPermissionService = extendedPermissionService; this.extendedPermissionService = extendedPermissionService;
} }
/** /**
* @param extendedSecurityService extended security service * @param extendedSecurityService extended security service
*/ */
public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService)
{ {
@@ -194,7 +202,21 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int) * @param isEnableAutoVersionOnRecordCreation
*/
public void setEnableAutoVersionOnRecordCreation(boolean isEnableAutoVersionOnRecordCreation)
{
this.isEnableAutoVersionOnRecordCreation = isEnableAutoVersionOnRecordCreation;
}
public boolean isEnableAutoVersionOnRecordCreation()
{
return isEnableAutoVersionOnRecordCreation;
}
/**
* @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef,
* java.util.Map, int)
*/ */
@Override @Override
protected Version createVersion(NodeRef nodeRef, Map<String, Serializable> origVersionProperties, int versionNumber) throws ReservedVersionNameException protected Version createVersion(NodeRef nodeRef, Map<String, Serializable> origVersionProperties, int versionNumber) throws ReservedVersionNameException
@@ -214,7 +236,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
VersionType versionType = null; VersionType versionType = null;
if (origVersionProperties != null) if (origVersionProperties != null)
{ {
versionType = (VersionType)origVersionProperties.get(VersionModel.PROP_VERSION_TYPE); versionType = (VersionType) origVersionProperties.get(VersionModel.PROP_VERSION_TYPE);
} }
// determine whether this is a recorded version or not // determine whether this is a recorded version or not
@@ -241,12 +263,12 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @param nodeRef node reference * @param nodeRef node reference
* @return {@link NodeRef} associated file plan, default if none * @return {@link NodeRef} associated file plan, default if none
*/ */
private NodeRef getFilePlan(NodeRef nodeRef) private NodeRef getFilePlan(NodeRef nodeRef)
{ {
NodeRef filePlan = (NodeRef)nodeService.getProperty(nodeRef, PROP_FILE_PLAN); NodeRef filePlan = (NodeRef) nodeService.getProperty(nodeRef, PROP_FILE_PLAN);
if (filePlan == null) if (filePlan == null)
{ {
filePlan = getFilePlan(); filePlan = getFilePlan();
@@ -255,7 +277,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
/** /**
* @return {@link NodeRef} default file plan, exception if none * @return {@link NodeRef} default file plan, exception if none
*/ */
private NodeRef getFilePlan() private NodeRef getFilePlan()
{ {
@@ -285,7 +307,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
boolean result = false; boolean result = false;
if (nodeService.hasAspect(nodeRef, RecordableVersionModel.ASPECT_VERSIONABLE)) if (nodeService.hasAspect(nodeRef, RecordableVersionModel.ASPECT_VERSIONABLE))
{ {
String policyString = (String)nodeService.getProperty(nodeRef, PROP_RECORDABLE_VERSION_POLICY); String policyString = (String) nodeService.getProperty(nodeRef, PROP_RECORDABLE_VERSION_POLICY);
if (policyString != null) if (policyString != null)
{ {
RecordableVersionPolicy policy = RecordableVersionPolicy.valueOf(policyString.toUpperCase()); RecordableVersionPolicy policy = RecordableVersionPolicy.valueOf(policyString.toUpperCase());
@@ -315,7 +337,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
NodeRef version = null; NodeRef version = null;
if (versionProperties.containsKey(KEY_RECORDABLE_VERSION) && if (versionProperties.containsKey(KEY_RECORDABLE_VERSION) &&
((Boolean)versionProperties.get(KEY_RECORDABLE_VERSION)).booleanValue()) ((Boolean)versionProperties.get(KEY_RECORDABLE_VERSION)).booleanValue())
{ {
// create a recorded version // create a recorded version
version = createNewRecordedVersion(sourceTypeRef, versionHistoryRef, standardVersionProperties, versionProperties, versionNumber, nodeDetails); version = createNewRecordedVersion(sourceTypeRef, versionHistoryRef, standardVersionProperties, versionProperties, versionNumber, nodeDetails);
@@ -332,13 +354,13 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** /**
* Creates a new recorded version * Creates a new recorded version
* *
* @param sourceTypeRef source type name * @param sourceTypeRef source type name
* @param versionHistoryRef version history reference * @param versionHistoryRef version history reference
* @param standardVersionProperties standard version properties * @param standardVersionProperties standard version properties
* @param versionProperties version properties * @param versionProperties version properties
* @param versionNumber version number * @param versionNumber version number
* @param nodeDetails policy scope * @param nodeDetails policy scope
* @return {@link NodeRef} record version * @return {@link NodeRef} record version
*/ */
protected NodeRef createNewRecordedVersion(QName sourceTypeRef, protected NodeRef createNewRecordedVersion(QName sourceTypeRef,
NodeRef versionHistoryRef, NodeRef versionHistoryRef,
@@ -365,14 +387,14 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
try try
{ {
// get the destination file plan // get the destination file plan
final NodeRef filePlan = (NodeRef)versionProperties.get(KEY_FILE_PLAN); final NodeRef filePlan = (NodeRef) versionProperties.get(KEY_FILE_PLAN);
if (filePlan == null) if (filePlan == null)
{ {
throw new AlfrescoRuntimeException("Can't create a new recorded version, because no file plan has been specified in the version properties."); throw new AlfrescoRuntimeException("Can't create a new recorded version, because no file plan has been specified in the version properties.");
} }
// create a copy of the source node and place in the file plan // 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); final NodeRef nodeRef = (NodeRef) standardVersionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF);
cmObjectType.disableCopy(); cmObjectType.disableCopy();
try try
@@ -418,7 +440,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, standardVersionProperties); nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, standardVersionProperties);
// add the recordedVersion aspect with link to record // add the recordedVersion aspect with link to record
nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable)record)); nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable) record));
// freeze auditable aspect information // freeze auditable aspect information
freezeAuditableAspect(nodeRef, versionNodeRef); freezeAuditableAspect(nodeRef, versionNodeRef);
@@ -459,8 +481,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** /**
* Helper method to link the record to the previous version record * Helper method to link the record to the previous version record
* *
* @param nodeRef noderef source node reference * @param nodeRef noderef source node reference
* @param record record record node reference * @param record record record node reference
*/ */
private void linkToPreviousVersionRecord(final NodeRef nodeRef, final NodeRef record) private void linkToPreviousVersionRecord(final NodeRef nodeRef, final NodeRef record)
{ {
@@ -483,8 +505,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
/** /**
* Helper to get the latest version record for a given document (ie non-record) * Helper to get the latest version record for a given document (ie non-record)
* *
* @param nodeRef node reference * @param nodeRef node reference
* @return NodeRef latest version record, null otherwise * @return NodeRef latest version record, null otherwise
*/ */
private NodeRef getLatestVersionRecord(NodeRef nodeRef) private NodeRef getLatestVersionRecord(NodeRef nodeRef)
{ {
@@ -498,7 +520,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
for (Version previousVersion : previousVersions) for (Version previousVersion : previousVersions)
{ {
// look for the associated record // look for the associated record
final NodeRef previousRecord = (NodeRef)previousVersion.getVersionProperties().get(PROP_VERSION_RECORD); final NodeRef previousRecord = (NodeRef) previousVersion.getVersionProperties().get(PROP_VERSION_RECORD);
if (previousRecord != null && if (previousRecord != null &&
nodeService.exists(previousRecord)) nodeService.exists(previousRecord))
{ {
@@ -510,7 +532,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
return versionRecord; return versionRecord;
} }
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#getRecordedVersion(org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#getRecordedVersion(org.alfresco.service.cmr.repository.NodeRef)
*/ */
@@ -563,20 +585,21 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
@Override @Override
protected Version getVersion(NodeRef versionRef) protected Version getVersion(NodeRef versionRef)
{ {
Version version = super.getVersion(versionRef); Version version = super.getVersion(versionRef);
// place the version record reference in the version properties // place the version record reference in the version properties
NodeRef record = (NodeRef)dbNodeService.getProperty(versionRef, PROP_RECORD_NODE_REF); NodeRef record = (NodeRef) dbNodeService.getProperty(versionRef, PROP_RECORD_NODE_REF);
if (record != null) if (record != null)
{ {
version.getVersionProperties().put(PROP_VERSION_RECORD, record); version.getVersionProperties().put(PROP_VERSION_RECORD, record);
} }
// place information about the destruction of the version record in the properties // place information about the destruction of the version record in the properties
Boolean destroyed = (Boolean)dbNodeService.getProperty(versionRef, PROP_DESTROYED); Boolean destroyed = (Boolean) dbNodeService.getProperty(versionRef, PROP_DESTROYED);
if (destroyed == null) { destroyed = Boolean.FALSE; } if (destroyed == null) { destroyed = Boolean.FALSE; }
version.getVersionProperties().put(PROP_RECORDED_VERSION_DESTROYED, destroyed); version.getVersionProperties().put(PROP_RECORDED_VERSION_DESTROYED, destroyed);
return version; return version;
} }
@@ -595,22 +618,22 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
dbNodeService.setProperty(nodeRef, PROP_RECORDABLE_VERSION_POLICY, versionPolicy); dbNodeService.setProperty(nodeRef, PROP_RECORDABLE_VERSION_POLICY, versionPolicy);
} }
} }
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isLatestVersionRecorded(org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isLatestVersionRecorded(org.alfresco.service.cmr.repository.NodeRef)
*/ */
@Override @Override
public boolean isCurrentVersionRecorded(NodeRef nodeRef) public boolean isCurrentVersionRecorded(NodeRef nodeRef)
{ {
boolean result = false; boolean result = false;
Version version = getCurrentVersion(nodeRef); Version version = getCurrentVersion(nodeRef);
if (version != null) if (version != null)
{ {
result = isRecordedVersion(version); result = isRecordedVersion(version);
} }
return result; return result;
} }
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersion(org.alfresco.service.cmr.version.Version) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersion(org.alfresco.service.cmr.version.Version)
*/ */
@@ -620,7 +643,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
NodeRef versionNodeRef = getVersionNodeRef(version); NodeRef versionNodeRef = getVersionNodeRef(version);
return dbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_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) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#getVersionRecord(org.alfresco.service.cmr.version.Version)
*/ */
@@ -632,8 +655,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
if (dbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION)) if (dbNodeService.hasAspect(versionNodeRef, RecordableVersionModel.ASPECT_RECORDED_VERSION))
{ {
// get the version record // get the version record
result = (NodeRef)dbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF); result = (NodeRef) dbNodeService.getProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF);
// check that the version record exists // check that the version record exists
if (result != null && if (result != null &&
!dbNodeService.exists(result)) !dbNodeService.exists(result))
@@ -645,47 +668,59 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
return result; return result;
} }
/** /**
* Create Version Store Ref * Create Version Store Ref
* *
* @param store ref * @param store ref
* @return store ref for version store * @return store ref for version store
*/ */
public StoreRef convertStoreRef(StoreRef storeRef) public StoreRef convertStoreRef(StoreRef storeRef)
{ {
return new StoreRef(StoreRef.PROTOCOL_WORKSPACE, storeRef.getIdentifier()); return new StoreRef(StoreRef.PROTOCOL_WORKSPACE, storeRef.getIdentifier());
} }
/** /**
* Convert the incomming node ref (with the version store protocol specified) * Convert the incomming node ref (with the version store protocol specified)
* to the internal representation with the workspace protocol. * to the internal representation with the workspace protocol.
* *
* @param nodeRef the incomming verison protocol node reference * @param nodeRef the incomming verison protocol node reference
* @return the internal version node reference * @return the internal version node reference
*/ */
public NodeRef convertNodeRef(NodeRef nodeRef) public NodeRef convertNodeRef(NodeRef nodeRef)
{ {
return new NodeRef(convertStoreRef(nodeRef.getStoreRef()), nodeRef.getId()); return new NodeRef(convertStoreRef(nodeRef.getStoreRef()), nodeRef.getId());
} }
/**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#createRecordFromLatestVersion(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, autoVersion)
*/
@Override
public NodeRef createRecordFromLatestVersion(final NodeRef filePlan, final NodeRef nodeRef, final boolean isEnableAutoVersionOnRecordCreation)
{
setEnableAutoVersionOnRecordCreation(isEnableAutoVersionOnRecordCreation);
return createRecordFromLatestVersion(filePlan, nodeRef);
}
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#createRecordFromLatestVersion(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#createRecordFromLatestVersion(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef)
*/ */
@Override @Override
public NodeRef createRecordFromLatestVersion(final NodeRef filePlan, final NodeRef nodeRef) public NodeRef createRecordFromLatestVersion(final NodeRef filePlan, final NodeRef nodeRef)
{ {
ParameterCheck.mandatory("filePlan", filePlan); ParameterCheck.mandatory("filePlan", filePlan);
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
NodeRef record = null; NodeRef record = null;
// check for versionable aspect // check for versionable aspect
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE))
{ {
createSnapshotVersion(nodeRef);
// get the latest version // get the latest version
final Version currentVersion = getCurrentVersion(nodeRef); final Version currentVersion = getCurrentVersion(nodeRef);
if (currentVersion != null && if (currentVersion != null &&
!isRecordedVersion(currentVersion)) !isRecordedVersion(currentVersion))
{ {
@@ -696,19 +731,19 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{ {
// get the documents readers and writers // get the documents readers and writers
Pair<Set<String>, Set<String>> readersAndWriters = extendedPermissionService.getReadersAndWriters(nodeRef); Pair<Set<String>, Set<String>> readersAndWriters = extendedPermissionService.getReadersAndWriters(nodeRef);
// grab the frozen state // grab the frozen state
NodeRef currentFrozenState = currentVersion.getFrozenStateNodeRef(); NodeRef currentFrozenState = currentVersion.getFrozenStateNodeRef();
// determine the type of the object // determine the type of the object
QName type = nodeService.getType(currentFrozenState); QName type = nodeService.getType(currentFrozenState);
// grab all the properties // grab all the properties
Map<QName, Serializable> properties = nodeService.getProperties(currentFrozenState); Map<QName, Serializable> properties = nodeService.getProperties(currentFrozenState);
// grab all the aspects // grab all the aspects
Set<QName> aspects = nodeService.getAspects(currentFrozenState); Set<QName> aspects = nodeService.getAspects(currentFrozenState);
// create the record // create the record
NodeRef record = recordService.createRecordFromContent( NodeRef record = recordService.createRecordFromContent(
filePlan, filePlan,
@@ -716,14 +751,14 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
type, type,
properties, properties,
null); null);
// apply aspects to record // apply aspects to record
for (QName aspect : aspects) for (QName aspect : aspects)
{ {
// add the aspect, properties have already been set // add the aspect, properties have already been set
nodeService.addAspect(record, aspect, null); nodeService.addAspect(record, aspect, null);
} }
// apply version record aspect to record // apply version record aspect to record
PropertyMap versionRecordProps = new PropertyMap(3); PropertyMap versionRecordProps = new PropertyMap(3);
versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef); versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef);
@@ -733,38 +768,38 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
// wire record up to previous record // wire record up to previous record
linkToPreviousVersionRecord(nodeRef, record); linkToPreviousVersionRecord(nodeRef, record);
// set the extended security // set the extended security
extendedSecurityService.set(record, readersAndWriters); extendedSecurityService.set(record, readersAndWriters);
return record; return record;
} }
}); });
// get the version history // get the version history
NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
// get details from the version before we remove it // get details from the version before we remove it
int versionNumber = getVersionNumber(currentVersion); int versionNumber = getVersionNumber(currentVersion);
Map<QName, Serializable> versionProperties = getVersionAspectProperties(currentVersion); Map<QName, Serializable> versionProperties = getVersionAspectProperties(currentVersion);
QName sourceTypeRef = getVersionType(currentVersion); QName sourceTypeRef = getVersionType(currentVersion);
// patch-up owner information, which needs to be frozen for recorded versions // patch-up owner information, which needs to be frozen for recorded versions
String owner = (String)nodeService.getProperty(currentVersion.getFrozenStateNodeRef(), ContentModel.PROP_OWNER); String owner = (String) nodeService.getProperty(currentVersion.getFrozenStateNodeRef(), ContentModel.PROP_OWNER);
if (owner != null) if (owner != null)
{ {
versionProperties.put(PROP_FROZEN_OWNER, owner); versionProperties.put(PROP_FROZEN_OWNER, owner);
} }
// delete the current version // delete the current version
this.dbNodeService.deleteNode(convertNodeRef(currentVersion.getFrozenStateNodeRef())); this.dbNodeService.deleteNode(convertNodeRef(currentVersion.getFrozenStateNodeRef()));
// create a new version history if we need to // create a new version history if we need to
if (!nodeService.exists(versionHistoryRef)) if (!nodeService.exists(versionHistoryRef))
{ {
versionHistoryRef = createVersionHistory(nodeRef); versionHistoryRef = createVersionHistory(nodeRef);
} }
// create recorded version nodeRef // create recorded version nodeRef
ChildAssociationRef childAssocRef = dbNodeService.createNode( ChildAssociationRef childAssocRef = dbNodeService.createNode(
versionHistoryRef, versionHistoryRef,
@@ -778,34 +813,34 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, versionProperties); nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, versionProperties);
// add the recordedVersion aspect with link to record // add the recordedVersion aspect with link to record
nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable)record)); nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable) record));
} }
} }
return record; return record;
} }
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersionDestroyed(org.alfresco.service.cmr.version.Version) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersionDestroyed(org.alfresco.service.cmr.version.Version)
*/ */
@Override @Override
public boolean isRecordedVersionDestroyed(Version version) public boolean isRecordedVersionDestroyed(Version version)
{ {
boolean result = false; boolean result = false;
// get the version node reference // get the version node reference
NodeRef versionNodeRef = getVersionNodeRef(version); NodeRef versionNodeRef = getVersionNodeRef(version);
// get the destroyed property value // get the destroyed property value
Boolean isDestroyed = (Boolean)dbNodeService.getProperty(versionNodeRef, PROP_DESTROYED); Boolean isDestroyed = (Boolean) dbNodeService.getProperty(versionNodeRef, PROP_DESTROYED);
if (isDestroyed != null) if (isDestroyed != null)
{ {
result = isDestroyed.booleanValue(); result = isDestroyed.booleanValue();
} }
return result; return result;
} }
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#destroyRecordedVersion(org.alfresco.service.cmr.version.Version) * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#destroyRecordedVersion(org.alfresco.service.cmr.version.Version)
*/ */
@@ -814,24 +849,24 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{ {
// get the version node reference // get the version node reference
NodeRef versionNodeRef = getVersionNodeRef(version); NodeRef versionNodeRef = getVersionNodeRef(version);
// if it's a recorded version // if it's a recorded version
if (dbNodeService.hasAspect(versionNodeRef, ASPECT_RECORDED_VERSION)) if (dbNodeService.hasAspect(versionNodeRef, ASPECT_RECORDED_VERSION))
{ {
// mark it as destroyed // mark it as destroyed
dbNodeService.setProperty(versionNodeRef, PROP_DESTROYED, true); dbNodeService.setProperty(versionNodeRef, PROP_DESTROYED, true);
// clear the record node reference property // clear the record node reference property
dbNodeService.setProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF, null); dbNodeService.setProperty(versionNodeRef, RecordableVersionModel.PROP_RECORD_NODE_REF, null);
} }
} }
/** /**
* Helper method to get the version number of a given version by inspecting the * Helper method to get the version number of a given version by inspecting the
* name of the parent association. * name of the parent association.
* *
* @param version version * @param version version
* @return int version number * @return int version number
*/ */
private int getVersionNumber(Version version) private int getVersionNumber(Version version)
{ {
@@ -841,11 +876,11 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
String versionNumber = fullVersionNumber.substring(fullVersionNumber.indexOf("-") + 1); String versionNumber = fullVersionNumber.substring(fullVersionNumber.indexOf("-") + 1);
return Integer.parseInt(versionNumber); return Integer.parseInt(versionNumber);
} }
/** /**
* Helper method to get all the version aspect properties from an existing version * Helper method to get all the version aspect properties from an existing version
* *
* @param version version * @param version version
* @return Map<QName, Serializable> property values * @return Map<QName, Serializable> property values
*/ */
private Map<QName, Serializable> getVersionAspectProperties(Version version) private Map<QName, Serializable> getVersionAspectProperties(Version version)
@@ -857,7 +892,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
{ {
QName propertyQName = QName.createQName(Version2Model.NAMESPACE_URI, propertyName); QName propertyQName = QName.createQName(Version2Model.NAMESPACE_URI, propertyName);
result.put(propertyQName, versionProps.get(propertyQName)); result.put(propertyQName, versionProps.get(propertyQName));
if (propertyName.equals(Version2Model.PROP_FROZEN_NODE_DBID)) if (propertyName.equals(Version2Model.PROP_FROZEN_NODE_DBID))
{ {
System.out.println(versionProps.get(propertyQName)); System.out.println(versionProps.get(propertyQName));
@@ -865,7 +900,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
} }
return result; return result;
} }
/** /**
* Helper method to get the type of a versions frozen state * Helper method to get the type of a versions frozen state
* *
@@ -875,16 +910,57 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
private QName getVersionType(Version version) private QName getVersionType(Version version)
{ {
return nodeService.getType(getVersionNodeRef(version)); return nodeService.getType(getVersionNodeRef(version));
} }
/** /**
* Helper method to get the internal node reference of a version * Helper method to get the internal node reference of a version
* *
* @param version version * @param version version
* @return NodeRef internal node reference to version * @return NodeRef internal node reference to version
*/ */
private NodeRef getVersionNodeRef(Version version) private NodeRef getVersionNodeRef(Version version)
{ {
return convertNodeRef(version.getFrozenStateNodeRef()); return convertNodeRef(version.getFrozenStateNodeRef());
}
/**
* Check if current version of the node is modified compared with versioned version
*
* @param nodeRef internal node reference
* @return boolean true if nodeRef is modified, otherwise false
*/
public boolean isCurrentVersionDirty(NodeRef nodeRef)
{
if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) { return false; }
// get the latest version
Version currentVersion = getCurrentVersion(nodeRef);
Date modificationDate = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
if (currentVersion == null) { return true; }
// grab the frozen state
NodeRef currentFrozenState = currentVersion.getFrozenStateNodeRef();
Date frozenModificationDate = (Date) nodeService.getProperty(currentFrozenState, ContentModel.PROP_MODIFIED);
boolean versionStoreOutdated = ((frozenModificationDate != null) && (modificationDate.getTime() > frozenModificationDate.getTime()));
return versionStoreOutdated;
}
/**
* @see RecordableVersionService#createSnapshotVersion(NodeRef)
*/
public void createSnapshotVersion(NodeRef nodeRef)
{
boolean autoVersion = isEnableAutoVersionOnRecordCreation();
// if the flag autoversion on record creation set, create new version on dirty nodes
if (autoVersion && isCurrentVersionDirty(nodeRef))
{
Map<String, Serializable> autoVersionProperties = new HashMap<String, Serializable>(2);
autoVersionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
autoVersionProperties.put(VersionModel.PROP_DESCRIPTION, I18NUtil.getMessage(AUTO_VERSION_ON_RECORD_CREATION));
createVersion(nodeRef, autoVersionProperties);
}
} }
} }

View File

@@ -0,0 +1,172 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* 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/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition;
import static org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.ImmutableMap;
import org.alfresco.model.ContentModel;
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.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.job.publish.DispositionActionDefinitionPublishExecutor;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils;
import org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.extensions.webscripts.GUID;
/**
* Integration tests for updating the disposition schedule.
*
* @author Tom Page
* @since 2.3.1
*/
public class UpdateDispositionScheduleTest extends BaseRMTestCase
{
/** A unique prefix for the constants in this test. */
protected static final String TEST_PREFIX = UpdateDispositionScheduleTest.class.getName() + GUID.generate() + "_";
/** The name to use for the category. */
protected static final String CATEGORY_NAME = TEST_PREFIX + "Category";
/** The name to use for the folder. */
protected static final String FOLDER_NAME = TEST_PREFIX + "Folder";
/** The name to use for the record. */
protected static final String RECORD_NAME = TEST_PREFIX + "Record";
/** The executor for the disposition update job. */
private DispositionActionDefinitionPublishExecutor dispositionActionDefinitionPublishExecutor;
/** The internal disposition service is used to avoid permissions issues when updating the record. */
private DispositionService internalDispositionService;
/** The category node. */
private NodeRef category;
/** The folder node. */
private NodeRef folder;
/** The record node. */
private NodeRef record;
/** The 'disposition as of' date from before the 'when' step. */
private Date originalAsOfDate;
@Override
protected void setUp() throws Exception
{
super.setUp();
BehaviourTest.initBehaviourTests(retryingTransactionHelper);
// Get the application context
applicationContext = ApplicationContextHelper.getApplicationContext(getConfigLocations());
dispositionActionDefinitionPublishExecutor = applicationContext.getBean(DispositionActionDefinitionPublishExecutor.class);
internalDispositionService = (DispositionService) applicationContext.getBean("dispositionService");
}
/**
* <a href="https://issues.alfresco.com/jira/browse/RM-3386">RM-3386</a>
* <p><pre>
* Given a record subject to a disposition schedule
* And the next step is due to run at some period after the date the content was created
* When I update the period of the next step (and wait for this to be processed)
* Then the "as of" date is updated to be at the new period after the creation date.
* </pre>
*/
public void testUpdatePeriod()
{
test()
.given(() -> {
// Create a category.
category = filePlanService.createRecordCategory(filePlan, CATEGORY_NAME);
// Create a disposition schedule for the category (Cut off immediately, then Destroy 1 year after the creation date).
DispositionSchedule dispSched = utils.createBasicDispositionSchedule(category, "instructions", "authority", true, false);
Map<QName, Serializable> cutOffParams = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME,
PROP_DISPOSITION_DESCRIPTION, "description",
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY);
dispositionService.addDispositionActionDefinition(dispSched, cutOffParams);
Map<QName, Serializable> destroyParams = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME,
PROP_DISPOSITION_DESCRIPTION, "description",
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_YEAR,
PROP_DISPOSITION_PERIOD_PROPERTY, ContentModel.PROP_CREATED);
dispositionService.addDispositionActionDefinition(dispSched, destroyParams);
// Create a folder containing a record within the category.
folder = recordFolderService.createRecordFolder(category, FOLDER_NAME);
record = fileFolderService.create(folder, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef();
dispositionService.cutoffDisposableItem(record);
// Ensure the update has been applied to the record.
internalDispositionService.updateNextDispositionAction(record);
originalAsOfDate = dispositionService.getNextDispositionAction(record).getAsOfDate();
})
.when(() -> {
// Update the Destroy step to be 3 years after the creation date.
DispositionSchedule dispSched = dispositionService.getDispositionSchedule(category);
DispositionActionDefinition destroy = dispSched.getDispositionActionDefinitionByName(DestroyAction.NAME);
Map<QName, Serializable> destroyParams = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME,
PROP_DISPOSITION_DESCRIPTION, "description",
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_THREE_YEARS,
PROP_DISPOSITION_PERIOD_PROPERTY, ContentModel.PROP_CREATED);
dispositionService.updateDispositionActionDefinition(destroy, destroyParams);
// Make the disposition action definition update job run.
dispositionActionDefinitionPublishExecutor.publish(destroy.getNodeRef());
})
.then()
.expect(true)
.from(() -> aboutTwoYearsApart(originalAsOfDate, dispositionService.getNextDispositionAction(record).getAsOfDate()))
.because("Increasing the destroy period by two years should increase the 'as of' date by two years.");
}
/**
* Check that the two given dates are approximately two years apart.
* <p>
* This actually just checks that they're more than one and less than three years apart, because leap years make
* things hard to calculate.
*
* @return true if the two dates are about two years apart.
*/
private boolean aboutTwoYearsApart(Date start, Date end)
{
long days = daysBetween(start, end);
long yearInDays = 365;
return (yearInDays < days) && (days < 3 * yearInDays);
}
/** Find the number of days between the two dates. */
private long daysBetween(Date start, Date end)
{
return TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
}
}

View File

@@ -28,20 +28,23 @@
package org.alfresco.module.org_alfresco_module_rm.test.integration.version; package org.alfresco.module.org_alfresco_module_rm.test.integration.version;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService;
import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl;
import org.alfresco.repo.version.VersionModel; import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
/** /**
* Declare as record version integration tests * Declare as record version integration tests
* *
* @author Roy Wetherall * @author Roy Wetherall
* @since 2.3 * @since 2.3
*/ */
@@ -49,7 +52,7 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest
{ {
/** recordable version service */ /** recordable version service */
private RecordableVersionService recordableVersionService; private RecordableVersionService recordableVersionService;
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices() * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices()
*/ */
@@ -57,56 +60,56 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest
protected void initServices() protected void initServices()
{ {
super.initServices(); super.initServices();
recordableVersionService = (RecordableVersionService)applicationContext.getBean("RecordableVersionService"); recordableVersionService = (RecordableVersionService) applicationContext.getBean("RecordableVersionService");
} }
/** /**
* Given versionable content with a non-recorded latest version * Given versionable content with a non-recorded latest version
* When I declare a version record * When I declare a version record
* Then the latest version is recorded and a record is created * Then the latest version is recorded and a record is created
*/ */
public void testDeclareLatestVersionAsRecord() public void testDeclareLatestVersionAsRecord()
{ {
doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator)
{ {
private NodeRef versionRecord; private NodeRef versionRecord;
private Map<String, Serializable> versionProperties; private Map<String, Serializable> versionProperties;
public void given() throws Exception public void given() throws Exception
{ {
// setup version properties // setup version properties
versionProperties = new HashMap<String, Serializable>(4); versionProperties = new HashMap<String, Serializable>(4);
versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
// create version // create version
versionService.createVersion(dmDocument, versionProperties); versionService.createVersion(dmDocument, versionProperties);
// assert that the latest version is not recorded // assert that the latest version is not recorded
assertFalse(recordableVersionService.isCurrentVersionRecorded(dmDocument)); assertFalse(recordableVersionService.isCurrentVersionRecorded(dmDocument));
} }
public void when() public void when()
{ {
// create version record from latest version // create version record from latest version
versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument); versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument);
} }
public void then() public void then()
{ {
// check the created record // check the created record
assertNotNull(versionRecord); assertNotNull(versionRecord);
assertTrue(recordService.isRecord(versionRecord)); assertTrue(recordService.isRecord(versionRecord));
// assert the current version is recorded // assert the current version is recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument));
// check the recorded version // check the recorded version
checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); checkRecordedVersion(dmDocument, DESCRIPTION, "0.1");
} }
}); });
} }
/** /**
* Given versionable content with a recorded latest version * Given versionable content with a recorded latest version
* When I declare a version record * When I declare a version record
@@ -116,10 +119,10 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest
public void testDeclareLatestVersionAsRecordButAlreadyRecorded() public void testDeclareLatestVersionAsRecordButAlreadyRecorded()
{ {
doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator)
{ {
private NodeRef versionRecord; private NodeRef versionRecord;
private Map<String, Serializable> versionProperties; private Map<String, Serializable> versionProperties;
public void given() throws Exception public void given() throws Exception
{ {
// setup version properties // setup version properties
@@ -128,90 +131,270 @@ public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true);
versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan);
// create version // create version
versionService.createVersion(dmDocument, versionProperties); versionService.createVersion(dmDocument, versionProperties);
// assert that the latest version is not recorded // assert that the latest version is not recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument));
} }
public void when() public void when()
{ {
// create version record from latest version // create version record from latest version
versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument); versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument);
} }
public void then() public void then()
{ {
// check that a record was not created // check that a record was not created
assertNull(versionRecord); assertNull(versionRecord);
// assert the current version is recorded // assert the current version is recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument));
// check the recorded version // check the recorded version
checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); checkRecordedVersion(dmDocument, DESCRIPTION, "0.1");
} }
}); });
} }
/** /**
* Given that a document is a specialized type * Given that a document is a specialized type
* When version is declared as a record * When version is declared as a record
* Then the record is the same type as the source document * Then the record is the same type as the source document
* *
* @see https://issues.alfresco.com/jira/browse/RM-2194 * @see https://issues.alfresco.com/jira/browse/RM-2194
*/ */
public void testSpecializedContentType() public void testSpecializedContentType()
{ {
doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator)
{ {
private NodeRef customDocument; private NodeRef customDocument;
private NodeRef versionRecord; private NodeRef versionRecord;
private Map<String, Serializable> versionProperties; private Map<String, Serializable> versionProperties;
public void given() throws Exception public void given() throws Exception
{ {
// create content // create content
customDocument = fileFolderService.create(dmFolder, GUID.generate(), TYPE_CUSTOM_TYPE).getNodeRef(); customDocument = fileFolderService.create(dmFolder, GUID.generate(), TYPE_CUSTOM_TYPE).getNodeRef();
prepareContent(customDocument); prepareContent(customDocument);
// setup version properties // setup version properties
versionProperties = new HashMap<String, Serializable>(2); versionProperties = new HashMap<String, Serializable>(2);
versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
// create version // create version
versionService.createVersion(customDocument, versionProperties); versionService.createVersion(customDocument, versionProperties);
// assert that the latest version is not recorded // assert that the latest version is not recorded
assertFalse(recordableVersionService.isCurrentVersionRecorded(customDocument)); assertFalse(recordableVersionService.isCurrentVersionRecorded(customDocument));
} }
public void when() public void when()
{ {
// create version record from latest version // create version record from latest version
versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, customDocument); versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, customDocument);
} }
public void then() public void then()
{ {
// check the created record // check the created record
assertNotNull(versionRecord); assertNotNull(versionRecord);
assertTrue(recordService.isRecord(versionRecord)); assertTrue(recordService.isRecord(versionRecord));
// check the record type is correct // check the record type is correct
assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(versionRecord)); assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(versionRecord));
// assert the current version is recorded // assert the current version is recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(customDocument)); assertTrue(recordableVersionService.isCurrentVersionRecorded(customDocument));
// check the recorded version // check the recorded version
checkRecordedVersion(customDocument, DESCRIPTION, "0.1"); checkRecordedVersion(customDocument, DESCRIPTION, "0.1");
} }
}); });
}
/**
* Given versionable content with a recorded latest version and autoversion is true
* When I declare this version record and contains local modifications
* Then a new minor version is created for document
*
* @see https://issues.alfresco.com/jira/browse/RM-2368
*/
public void testCreateRecordFromLatestVersionAutoTrue()
{
doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator)
{
private NodeRef myDocument;
private NodeRef versionedRecord;
private Map<String, Serializable> versionProperties;
private Date createdDate;
private Date modificationDate;
private String record_name = "initial_name";
private String AUTO_VERSION_DESCRIPTION = "Auto Version on Record Creation";
private boolean autoVersion = true;
public void given() throws Exception
{
// create a document
myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
createdDate = (Date) nodeService.getProperty(myDocument, ContentModel.PROP_CREATED);
modificationDate = (Date) nodeService.getProperty(myDocument, ContentModel.PROP_MODIFIED);
assertTrue("Modified date must be after or on creation date", createdDate.getTime() == modificationDate.getTime());
// Set initial set of properties
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(3);
// Ensure default behaviour autoversion on change properties is set to false
properties.put(ContentModel.PROP_AUTO_VERSION_PROPS, false);
// Set initial name
properties.put(ContentModel.PROP_NAME, "initial_name");
nodeService.setProperties(myDocument, properties);
nodeService.setProperty(myDocument, ContentModel.PROP_DESCRIPTION, DESCRIPTION);
nodeService.addAspect(myDocument, ContentModel.ASPECT_OWNABLE, null);
// make sure document is versionable
nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, null);
// Change Type to a custom document
nodeService.setType(myDocument, TYPE_CUSTOM_TYPE);
// setup version properties
versionProperties = new HashMap<String, Serializable>(2);
versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR);
// create initial version
versionService.createVersion(myDocument, versionProperties);
}
public void when()
{
// Apply a custom aspect
nodeService.addAspect(myDocument, ContentModel.ASPECT_TITLED, null);
// Update properties
nodeService.setProperty(myDocument, ContentModel.PROP_NAME, "updated_name");
nodeService.setProperty(myDocument, ContentModel.PROP_DESCRIPTION, DESCRIPTION);
// test RM-2368
versionedRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, myDocument, autoVersion);
}
public void then()
{
// Properties updated / flag as modified
// check the created record
assertNotNull(versionedRecord);
assertTrue(recordService.isRecord(versionedRecord));
// check the record type is correct
assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(versionedRecord));
// assert the current version is recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(myDocument));
// get name of record
record_name = (String) nodeService.getProperty(versionedRecord, ContentModel.PROP_NAME);
// new version is create, current node was modified
assertTrue("Name was updated:", record_name.contains("updated_name"));
// check record
checkRecordedVersion(myDocument, AUTO_VERSION_DESCRIPTION, "1.1");
}
});
} }
/**
*
* Given versionable content with a recorded latest version and autoversion is false
* When I declare this version record and contains local modifications
* Then a record is created from latest version
*
* @see https://issues.alfresco.com/jira/browse/RM-2368
*/
public void testCreateRecordFromLatestVersion()
{
doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator)
{
private NodeRef myDocument;
private NodeRef versionedRecord;
private Map<String, Serializable> versionProperties;
private Date createdDate;
private Date modificationDate;
private String record_name = "initial_name";
private boolean autoVersion = false;
public void given() throws Exception
{
// create a document
myDocument = fileFolderService.create(dmFolder, GUID.generate(), ContentModel.TYPE_CONTENT).getNodeRef();
createdDate = (Date) nodeService.getProperty(myDocument, ContentModel.PROP_CREATED);
modificationDate = (Date) nodeService.getProperty(myDocument, ContentModel.PROP_MODIFIED);
assertTrue("Modified date must be after or on creation date", createdDate.getTime() == modificationDate.getTime());
// Set initial set of properties
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(3);
// Ensure default behaviour autoversion on change properties is set to false
properties.put(ContentModel.PROP_AUTO_VERSION_PROPS, false);
// Set initial name
properties.put(ContentModel.PROP_NAME, "initial_name");
nodeService.setProperties(myDocument, properties);
nodeService.setProperty(myDocument, ContentModel.PROP_DESCRIPTION, DESCRIPTION);
nodeService.addAspect(myDocument, ContentModel.ASPECT_OWNABLE, null);
// make sure document is versionable
nodeService.addAspect(myDocument, ContentModel.ASPECT_VERSIONABLE, null);
// Change Type to a custom document
nodeService.setType(myDocument, TYPE_CUSTOM_TYPE);
// setup version properties
versionProperties = new HashMap<String, Serializable>(2);
versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR);
// create initial version
versionService.createVersion(myDocument, versionProperties);
}
public void when()
{
// Apply a custom aspect
nodeService.addAspect(myDocument, ContentModel.ASPECT_TITLED, null);
// Update properties
nodeService.setProperty(myDocument, ContentModel.PROP_NAME, "initial_name");
nodeService.setProperty(myDocument, ContentModel.PROP_DESCRIPTION, DESCRIPTION);
// test RM-2368
versionedRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, myDocument, autoVersion);
}
public void then()
{
// Properties updated / flag as modified
// check the created record
assertNotNull(versionedRecord);
assertTrue(recordService.isRecord(versionedRecord));
// check the record type is correct
assertEquals(TYPE_CUSTOM_TYPE, nodeService.getType(versionedRecord));
// assert the current version is recorded
assertTrue(recordableVersionService.isCurrentVersionRecorded(myDocument));
// get name of record
record_name = (String) nodeService.getProperty(versionedRecord, ContentModel.PROP_NAME);
// record is created based on existing frozen, which does not contain any modification of node
assertTrue("Name is not modified: ", record_name.contains("initial_name"));
checkRecordedVersion(myDocument, DESCRIPTION, "1.0");
}
});
}
} }

View File

@@ -66,7 +66,7 @@ import org.springframework.context.ApplicationContext;
/** /**
* Common RM test utility methods. * Common RM test utility methods.
* *
* @author Roy Wetherall * @author Roy Wetherall
*/ */
public class CommonRMTestUtils implements RecordsManagementModel public class CommonRMTestUtils implements RecordsManagementModel
@@ -87,10 +87,12 @@ public class CommonRMTestUtils implements RecordsManagementModel
public static final String PERIOD_NONE = "none|0"; public static final String PERIOD_NONE = "none|0";
public static final String PERIOD_IMMEDIATELY = "immediately|0"; public static final String PERIOD_IMMEDIATELY = "immediately|0";
public static final String PERIOD_ONE_WEEK = "week|1"; public static final String PERIOD_ONE_WEEK = "week|1";
public static final String PERIOD_ONE_YEAR = "year|1";
public static final String PERIOD_THREE_YEARS = "year|3";
/** /**
* Constructor * Constructor
* *
* @param applicationContext application context * @param applicationContext application context
*/ */
public CommonRMTestUtils(ApplicationContext applicationContext) public CommonRMTestUtils(ApplicationContext applicationContext)
@@ -106,7 +108,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
/** /**
* Create a disposition schedule * Create a disposition schedule
* *
* @param container record category * @param container record category
* @return {@link DispositionSchedule} created disposition schedule node reference * @return {@link DispositionSchedule} created disposition schedule node reference
*/ */
@@ -140,15 +142,15 @@ public class CommonRMTestUtils implements RecordsManagementModel
boolean extendedDispositionSchedule) boolean extendedDispositionSchedule)
{ {
return createDispositionSchedule( return createDispositionSchedule(
container, container,
dispositionInstructions, dispositionInstructions,
dispositionAuthority, dispositionAuthority,
isRecordLevel, isRecordLevel,
defaultDispositionActions, defaultDispositionActions,
extendedDispositionSchedule, extendedDispositionSchedule,
DEFAULT_EVENT_NAME); DEFAULT_EVENT_NAME);
} }
/** /**
* Create test disposition schedule * Create test disposition schedule
*/ */
@@ -203,7 +205,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
/** /**
* Helper method to create a record in a record folder. * Helper method to create a record in a record folder.
* *
* @param recordFolder record folder * @param recordFolder record folder
* @param name name of record * @param name name of record
* @return {@link NodeRef} record node reference * @return {@link NodeRef} record node reference
@@ -215,7 +217,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
/** /**
* Helper method to create a record in a record folder. * Helper method to create a record in a record folder.
* *
* @param recordFolder record folder * @param recordFolder record folder
* @param name name of the record * @param name name of the record
* @param title title of the record * @param title title of the record
@@ -230,7 +232,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
/** /**
* Helper method to create a record in a record folder. * Helper method to create a record in a record folder.
* *
* @param recordFolder record folder * @param recordFolder record folder
* @param name name of record * @param name name of record
* @param properties properties of the record * @param properties properties of the record
@@ -250,10 +252,10 @@ public class CommonRMTestUtils implements RecordsManagementModel
return record; return record;
} }
/** /**
* Helper method to create a record in a record folder. * Helper method to create a record in a record folder.
* *
* @param recordFolder record folder * @param recordFolder record folder
* @param name name of record * @param name name of record
* @param properties properties of the record * @param properties properties of the record
@@ -273,7 +275,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
return record; return record;
} }
/** /**
* Helper to consolidate creation of contentless record * Helper to consolidate creation of contentless record
*/ */
@@ -292,10 +294,10 @@ public class CommonRMTestUtils implements RecordsManagementModel
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
properties).getChildRef(); properties).getChildRef();
return record; return record;
} }
/** /**
* Helper method to complete record. * Helper method to complete record.
*/ */
@@ -309,8 +311,8 @@ public class CommonRMTestUtils implements RecordsManagementModel
modelSecurityService.setEnabled(false); modelSecurityService.setEnabled(false);
try try
{ {
nodeService.setProperty(record, RecordsManagementModel.PROP_DATE_FILED, new Date()); nodeService.setProperty(record, RecordsManagementModel.PROP_DATE_FILED, new Date());
nodeService.setProperty(record, ContentModel.PROP_TITLE, "titleValue"); nodeService.setProperty(record, ContentModel.PROP_TITLE, "titleValue");
actionService.executeRecordsManagementAction(record, "declareRecord"); actionService.executeRecordsManagementAction(record, "declareRecord");
} }
finally finally
@@ -323,7 +325,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
}, AuthenticationUtil.getAdminUserName()); }, AuthenticationUtil.getAdminUserName());
} }
public void closeFolder(final NodeRef recordFolder) public void closeFolder(final NodeRef recordFolder)
{ {
@@ -361,10 +363,10 @@ public class CommonRMTestUtils implements RecordsManagementModel
return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities); return filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
} }
/** /**
* Helper method to complete event on disposable item * Helper method to complete event on disposable item
* *
* @param disposableItem disposable item (record or record folder) * @param disposableItem disposable item (record or record folder)
* @param eventName event name * @param eventName event name
*/ */
@@ -373,8 +375,8 @@ public class CommonRMTestUtils implements RecordsManagementModel
// build action properties // build action properties
Map<String, Serializable> params = new HashMap<String, Serializable>(1); Map<String, Serializable> params = new HashMap<String, Serializable>(1);
params.put(CompleteEventAction.PARAM_EVENT_NAME, eventName); params.put(CompleteEventAction.PARAM_EVENT_NAME, eventName);
// complete event // complete event
actionService.executeRecordsManagementAction(disposableItem, CompleteEventAction.NAME, params); actionService.executeRecordsManagementAction(disposableItem, CompleteEventAction.NAME, params);
} }
} }

View File

@@ -0,0 +1,103 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* 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/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.action.impl;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_DISPOSITION_AS_OF;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Date;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for {@link BroadcastDispositionActionDefinitionUpdateAction}.
*
* @author Tom Page
* @since 2.3.1
*/
public class BroadcastDispositionActionDefinitionUpdateActionUnitTest
{
/** The node under the category containing information about the definition of the action. */
private static final NodeRef DISPOSITION_ACTION_DEF_NODE = new NodeRef("disposition://Action/Def");
/** The node containing the details of the next disposition step for the content. */
private static final NodeRef NEXT_ACTION_NODE_REF = new NodeRef("next://Step/");
/** The node being subject to the disposition step. */
private static final NodeRef CONTENT_NODE_REF = new NodeRef("content://Node/Ref");
/** The class under test. */
private BroadcastDispositionActionDefinitionUpdateAction action = new BroadcastDispositionActionDefinitionUpdateAction();
private NodeService mockNodeService = mock(NodeService.class);
private DispositionService mockDispositionService = mock(DispositionService.class);
/** Inject the mock services into the class under test and link the content and next action nodes. */
@Before
public void setUp()
{
action.setNodeService(mockNodeService);
action.setDispositionService(mockDispositionService);
ChildAssociationRef mockAssocRef = mock(ChildAssociationRef.class);
when(mockNodeService.getPrimaryParent(NEXT_ACTION_NODE_REF)).thenReturn(mockAssocRef);
when(mockAssocRef.getParentRef()).thenReturn(CONTENT_NODE_REF);
}
/**
* Check that the disposition service is used to determine the "disposition as of" date when changes are made to the
* disposition period.
*/
@Test
public void testPersistPeriodChanges()
{
// Set up the data associated with the next disposition action.
DispositionAction mockAction = mock(DispositionAction.class);
when(mockAction.getNodeRef()).thenReturn(NEXT_ACTION_NODE_REF);
DispositionActionDefinition mockDispositionActionDefinition = mock(DispositionActionDefinition.class);
when(mockAction.getDispositionActionDefinition()).thenReturn(mockDispositionActionDefinition);
when(mockAction.getName()).thenReturn("mockAction");
// Set up the disposition service to return a known "disposition as of" date.
Date asOfDate = new Date();
when(mockDispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, false))
.thenReturn(asOfDate);
// Call the method under test.
action.persistPeriodChanges(DISPOSITION_ACTION_DEF_NODE, mockAction);
// Check that the "disposition as of" date has been set on the next action.
verify(mockNodeService).setProperty(NEXT_ACTION_NODE_REF, PROP_DISPOSITION_AS_OF, asOfDate);
}
}

View File

@@ -0,0 +1,102 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* 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/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.disposition;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Date;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Period;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for {@link DispositionServiceImpl}.
*
* @author Tom Page
* @since 2.3.1
*/
public class DispositionServiceImplUnitTest
{
/** The node being subject to the disposition step. */
NodeRef CONTENT_NODE_REF = new NodeRef("content://node/");
/** The class under test. */
private DispositionServiceImpl dispositionService = new DispositionServiceImpl();
private NodeService mockNodeService = mock(NodeService.class);
@Before
public void setUp()
{
dispositionService.setNodeService(mockNodeService);
}
/**
* Check that the relevant information is retrieved from the DispositionActionDefinition in order to determine the
* "disposition as of" date.
*/
@Test
public void testCalculateAsOfDate()
{
// Set up a mock for the disposition action definition.
DispositionActionDefinition mockDispositionActionDefinition = mock(DispositionActionDefinition.class);
Period mockPeriod = mock(Period.class);
when(mockDispositionActionDefinition.getPeriod()).thenReturn(mockPeriod);
when(mockDispositionActionDefinition.getPeriodProperty()).thenReturn(ContentModel.PROP_CREATED);
// Set up a created date and another date that is some Period later.
Date createdDate = new Date(1234567890);
when(mockNodeService.getProperty(CONTENT_NODE_REF, ContentModel.PROP_CREATED)).thenReturn(createdDate);
Date nextDate = new Date(1240000000);
when(mockPeriod.getNextDate(createdDate)).thenReturn(nextDate);
// Call the method under test.
Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, true);
assertEquals("Unexpected calculation for 'as of' date", nextDate, asOfDate);
}
/** Check that the calculated "disposition as of" date is null if a null period is given. */
@Test
public void testCalculateAsOfDate_nullPeriod()
{
DispositionActionDefinition mockDispositionActionDefinition = mock(DispositionActionDefinition.class);
when(mockDispositionActionDefinition.getPeriod()).thenReturn(null);
// Call the method under test.
Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, true);
assertNull("It should not be possible to determine the 'as of' date.", asOfDate);
}
}

View File

@@ -46,7 +46,7 @@ import org.json.JSONObject;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import org.springframework.extensions.surf.util.Content; import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.AbstractWebScript; import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Container; import org.springframework.extensions.webscripts.Container;
import org.springframework.extensions.webscripts.Description; import org.springframework.extensions.webscripts.Description;
import org.springframework.extensions.webscripts.Description.RequiredCache; import org.springframework.extensions.webscripts.Description.RequiredCache;
@@ -83,7 +83,7 @@ public abstract class BaseWebScriptUnitTest extends BaseUnitTest
/** /**
* @return declarative webscript * @return declarative webscript
*/ */
protected abstract AbstractWebScript getWebScript(); protected abstract AbstractWebScript getWebScript();
/** /**
* @return classpath location of webscript template * @return classpath location of webscript template
@@ -151,7 +151,7 @@ public abstract class BaseWebScriptUnitTest extends BaseUnitTest
*/ */
protected String executeWebScript(Map<String, String> parameters, String content) throws Exception protected String executeWebScript(Map<String, String> parameters, String content) throws Exception
{ {
AbstractWebScript webScript = getWebScript(); AbstractWebScript webScript = getWebScript();
String template = getWebScriptTemplate(); String template = getWebScriptTemplate();
// initialise webscript // initialise webscript
@@ -173,7 +173,7 @@ public abstract class BaseWebScriptUnitTest extends BaseUnitTest
* @return {@link WebScriptRequest} mocked web script request * @return {@link WebScriptRequest} mocked web script request
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
protected WebScriptRequest getMockedWebScriptRequest(AbstractWebScript webScript, final Map<String, String> parameters, String content) throws Exception protected WebScriptRequest getMockedWebScriptRequest(AbstractWebScript webScript, final Map<String, String> parameters, String content) throws Exception
{ {
Match match = new Match(null, parameters, null, webScript); Match match = new Match(null, parameters, null, webScript);
org.springframework.extensions.webscripts.Runtime mockedRuntime = mock(org.springframework.extensions.webscripts.Runtime.class); org.springframework.extensions.webscripts.Runtime mockedRuntime = mock(org.springframework.extensions.webscripts.Runtime.class);

View File

@@ -24,24 +24,6 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
/*
* 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.repo.web.scripts.roles; package org.alfresco.repo.web.scripts.roles;
@@ -49,14 +31,14 @@ import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -64,7 +46,7 @@ import static org.mockito.Mockito.when;
import java.io.File; import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -87,9 +69,9 @@ import org.alfresco.repo.domain.patch.PatchDAO;
import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.web.scripts.content.ContentStreamer; import org.alfresco.repo.web.scripts.content.ContentStreamer;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
@@ -105,11 +87,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.AbstractWebScript; import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
/** /**
* DynamicAuthoritiesGet Unit Test * DynamicAuthoritiesGet Unit Test
@@ -140,17 +122,17 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
private TransactionService mockedTransactionService; private TransactionService mockedTransactionService;
@Mock @Mock
private RetryingTransactionHelper mockedRetryingTransactionHelper; private RetryingTransactionHelper mockedRetryingTransactionHelper;
@Mock @Mock
private ContentStreamer contentStreamer; private ContentStreamer contentStreamer;
@Mock @Mock
private FileFolderService mockedFileFolderService; private FileFolderService mockedFileFolderService;
/** test component */ /** test component */
@InjectMocks @InjectMocks
private DynamicAuthoritiesGet webScript; private DynamicAuthoritiesGet webScript;
@Override @Override
protected AbstractWebScript getWebScript() protected AbstractWebScript getWebScript()
{ {
return webScript; return webScript;
} }
@@ -172,7 +154,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
webScript.setNodeService(mockedNodeService); webScript.setNodeService(mockedNodeService);
webScript.setPermissionService(mockedPermissionService); webScript.setPermissionService(mockedPermissionService);
webScript.setExtendedSecurityService(mockedExtendedSecurityService); webScript.setExtendedSecurityService(mockedExtendedSecurityService);
webScript.setFileFolderService(mockedFileFolderService); webScript.setFileFolderService(mockedFileFolderService);
// setup retrying transaction helper // setup retrying transaction helper
Answer<Object> doInTransactionAnswer = new Answer<Object>() Answer<Object> doInTransactionAnswer = new Answer<Object>()
{ {
@@ -200,7 +182,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
/** /**
* Given that there are no nodes with the extended security aspect * Given that there are no nodes with the extended security aspect
* When the action is executed Nothing happens * When the action is executed Nothing happens
* *
* @throws Exception * @throws Exception
*/ */
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
@@ -237,7 +219,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
* Then the aspect is removed * Then the aspect is removed
* And the dynamic authorities permissions are cleared * And the dynamic authorities permissions are cleared
* And extended security is set via the updated API * And extended security is set via the updated API
* *
* @throws Exception * @throws Exception
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -246,7 +228,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
@@ -282,8 +264,8 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
/** /**
* Given that there are non-records with the extended security aspect * Given that there are non-records with the extended security aspect
* When the web script is executed * When the web script is executed
* Then the aspect is removed And the dynamic authorities permissions are cleared * Then the aspect is removed And the dynamic authorities permissions are cleared
* *
* @throws Exception * @throws Exception
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -292,7 +274,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
@@ -328,50 +310,50 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
@Test @Test
public void missingBatchSizeParameter() throws Exception public void missingBatchSizeParameter() throws Exception
{ {
try try
{ {
executeJSONWebScript(emptyMap()); executeJSONWebScript(emptyMap());
fail("Expected exception as parameter batchsize is mandatory."); fail("Expected exception as parameter batchsize is mandatory.");
} }
catch (WebScriptException e) catch (WebScriptException e)
{ {
assertEquals("If parameter batchsize is not provided then 'Bad request' should be returned.", assertEquals("If parameter batchsize is not provided then 'Bad request' should be returned.",
Status.STATUS_BAD_REQUEST, e.getStatus()); Status.STATUS_BAD_REQUEST, e.getStatus());
} }
} }
@Test @Test
public void invalidBatchSizeParameter() throws Exception public void invalidBatchSizeParameter() throws Exception
{ {
try try
{ {
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "dd"); Map<String, String> parameters = ImmutableMap.of("batchsize", "dd");
executeJSONWebScript(parameters); executeJSONWebScript(parameters);
fail("Expected exception as parameter batchsize is invalid."); fail("Expected exception as parameter batchsize is invalid.");
} }
catch (WebScriptException e) catch (WebScriptException e)
{ {
assertEquals("If parameter batchsize is invalid then 'Bad request' should be returned.", assertEquals("If parameter batchsize is invalid then 'Bad request' should be returned.",
Status.STATUS_BAD_REQUEST, e.getStatus()); Status.STATUS_BAD_REQUEST, e.getStatus());
} }
} }
@Test @Test
public void batchSizeShouldBeGraterThanZero() throws Exception public void batchSizeShouldBeGraterThanZero() throws Exception
{ {
try try
{ {
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "0"); Map<String, String> parameters = ImmutableMap.of("batchsize", "0");
executeJSONWebScript(parameters); executeJSONWebScript(parameters);
fail("Expected exception as parameter batchsize is not a number greater than 0."); fail("Expected exception as parameter batchsize is not a number greater than 0.");
} }
catch (WebScriptException e) catch (WebScriptException e)
{ {
assertEquals("If parameter batchsize is not a number greater than 0 then 'Bad request' should be returned.", assertEquals("If parameter batchsize is not a number greater than 0 then 'Bad request' should be returned.",
Status.STATUS_BAD_REQUEST, e.getStatus()); Status.STATUS_BAD_REQUEST, e.getStatus());
} }
} }
@Test @Test
@@ -393,7 +375,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
{ {
List<Long> ids = Stream.of(1l, 2l, 3l,4l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l,4l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
@@ -421,7 +403,7 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
{ {
List<Long> ids = Stream.of(1l, 2l, 3l, 4l, 5l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l, 4l, 5l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
@@ -443,302 +425,302 @@ public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest impleme
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed first 4 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed first 4 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
@Test @Test
public void recordsWithExtendedSecurityAspectAndNullWritersAndReaders() throws Exception public void recordsWithExtendedSecurityAspectAndNullWritersAndReaders() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef)); when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)).thenReturn(null); when(mockedNodeService.getProperty(nodeRef, PROP_READERS)).thenReturn(null);
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)).thenReturn(null); when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)).thenReturn(null);
}); });
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4"); Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4");
JSONObject json = executeJSONWebScript(parameters); JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json); assertNotNull(json);
String actualJSONString = json.toString(); String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
ArgumentCaptor<Set> readerKeysCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor<Set> readerKeysCaptor = ArgumentCaptor.forClass(Set.class);
ArgumentCaptor<Set> writersKeysCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor<Set> writersKeysCaptor = ArgumentCaptor.forClass(Set.class);
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS)); verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS)); verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS));
verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY)); verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class), verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedReaderDynamicAuthority.EXTENDED_READER)); eq(ExtendedReaderDynamicAuthority.EXTENDED_READER));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class), verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER)); eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
verify(mockedExtendedSecurityService, times(3)).set(any(NodeRef.class), readerKeysCaptor.capture(), verify(mockedExtendedSecurityService, times(3)).set(any(NodeRef.class), readerKeysCaptor.capture(),
writersKeysCaptor.capture()); writersKeysCaptor.capture());
List<Set> allReaderKeySets = readerKeysCaptor.getAllValues(); List<Set> allReaderKeySets = readerKeysCaptor.getAllValues();
List<Set> allWritersKeySets = writersKeysCaptor.getAllValues(); List<Set> allWritersKeySets = writersKeysCaptor.getAllValues();
for (Set keySet : allReaderKeySets) for (Set keySet : allReaderKeySets)
{ {
assertNull(keySet); assertNull(keySet);
} }
for (Set keySet : allWritersKeySets) for (Set keySet : allWritersKeySets)
{ {
assertNull(keySet); assertNull(keySet);
} }
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
@Test @Test
public void recordsWithExtendedSecurityAspectAndNullWriters() throws Exception public void recordsWithExtendedSecurityAspectAndNullWriters() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef)); when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)) when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)).thenReturn(null); when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)).thenReturn(null);
}); });
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4"); Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4");
JSONObject json = executeJSONWebScript(parameters); JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json); assertNotNull(json);
String actualJSONString = json.toString(); String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
ArgumentCaptor<Set> readerKeysCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor<Set> readerKeysCaptor = ArgumentCaptor.forClass(Set.class);
ArgumentCaptor<Set> writersKeysCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor<Set> writersKeysCaptor = ArgumentCaptor.forClass(Set.class);
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS)); verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_READERS));
verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS)); verify(mockedNodeService, times(3)).getProperty(any(NodeRef.class), eq(PROP_WRITERS));
verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY)); verify(mockedNodeService, times(3)).removeAspect(any(NodeRef.class), eq(ASPECT_EXTENDED_SECURITY));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class), verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedReaderDynamicAuthority.EXTENDED_READER)); eq(ExtendedReaderDynamicAuthority.EXTENDED_READER));
verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class), verify(mockedPermissionService, times(3)).clearPermission(any(NodeRef.class),
eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER)); eq(ExtendedWriterDynamicAuthority.EXTENDED_WRITER));
verify(mockedExtendedSecurityService, times(3)).set(any(NodeRef.class), readerKeysCaptor.capture(), verify(mockedExtendedSecurityService, times(3)).set(any(NodeRef.class), readerKeysCaptor.capture(),
writersKeysCaptor.capture()); writersKeysCaptor.capture());
List<Set> allReaderKeySets = readerKeysCaptor.getAllValues(); List<Set> allReaderKeySets = readerKeysCaptor.getAllValues();
List<Set> allWritersKeySets = writersKeysCaptor.getAllValues(); List<Set> allWritersKeySets = writersKeysCaptor.getAllValues();
for (Set keySet : allReaderKeySets) for (Set keySet : allReaderKeySets)
{ {
assertNotNull(keySet); assertNotNull(keySet);
} }
for (Set keySet : allWritersKeySets) for (Set keySet : allWritersKeySets)
{ {
assertNull(keySet); assertNull(keySet);
} }
} }
/** /**
* Given I have records that require migration * Given I have records that require migration
* And I am interested in knowning which records are migrated * And I am interested in knowning which records are migrated
* When I run the migration tool * When I run the migration tool
* Then I will be returned a CSV file containing the name and node reference of the record migrated * Then I will be returned a CSV file containing the name and node reference of the record migrated
* *
* @throws Exception * @throws Exception
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void processWithCSVFile() throws Exception public void processWithCSVFile() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef)); when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)) when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)) when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
String name = "name" + i; String name = "name" + i;
when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name); when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name);
}); });
ArgumentCaptor<File> csvFileCaptor = ArgumentCaptor.forClass(File.class); ArgumentCaptor<File> csvFileCaptor = ArgumentCaptor.forClass(File.class);
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4", "export", Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4", "export",
"true"); "true");
executeWebScript(parameters); executeWebScript(parameters);
verify(contentStreamer, times(1)).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class), verify(contentStreamer, times(1)).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class),
csvFileCaptor.capture(), any(Long.class), any(Boolean.class), any(String.class), any(Map.class)); csvFileCaptor.capture(), any(Long.class), any(Boolean.class), any(String.class), any(Map.class));
File fileForDownload = csvFileCaptor.getValue(); File fileForDownload = csvFileCaptor.getValue();
assertNotNull(fileForDownload); assertNotNull(fileForDownload);
} }
/** /**
* Given that I have record that require migration * Given that I have record that require migration
* And I'm not interested in knowing which records were migrated * And I'm not interested in knowing which records were migrated
* When I run the migration tool * When I run the migration tool
* Then I will not be returned a CSV file of details. * Then I will not be returned a CSV file of details.
* *
* @throws Exception * @throws Exception
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void processedWithouthCSVFile() throws Exception public void processedWithouthCSVFile() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids) when(mockedPatchDAO.getNodesByAspectQNameId(eq(ASPECT_ID), anyLong(), anyLong())).thenReturn(ids)
.thenReturn(Collections.emptyList()); .thenReturn(Collections.emptyList());
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef)); when(mockedNodeDAO.getNodePair(i)).thenReturn(new Pair<Long, NodeRef>(i, nodeRef));
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)) when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)) when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
}); });
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4", "export", Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "maxProcessedRecords", "4", "export",
"false"); "false");
JSONObject json = executeJSONWebScript(parameters); JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json); assertNotNull(json);
String actualJSONString = json.toString(); String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class), verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class),
any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class)); any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class));
} }
@Test @Test
public void invalidParentNodeRefParameter() throws Exception public void invalidParentNodeRefParameter() throws Exception
{ {
try try
{ {
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "parentNodeRef", "invalidNodeRef"); Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "parentNodeRef", "invalidNodeRef");
executeJSONWebScript(parameters); executeJSONWebScript(parameters);
fail("Expected exception as parameter parentNodeRef is invalid."); fail("Expected exception as parameter parentNodeRef is invalid.");
} }
catch (WebScriptException e) catch (WebScriptException e)
{ {
assertEquals("If parameter parentNodeRef is invalid then 'Internal server error' should be returned.", assertEquals("If parameter parentNodeRef is invalid then 'Internal server error' should be returned.",
Status.STATUS_INTERNAL_SERVER_ERROR, e.getStatus()); Status.STATUS_INTERNAL_SERVER_ERROR, e.getStatus());
} }
} }
@Test @Test
public void inexistentParentNodeRefParameter() throws Exception public void inexistentParentNodeRefParameter() throws Exception
{ {
try try
{ {
NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeService.exists(parentNodeRef)).thenReturn(false); when(mockedNodeService.exists(parentNodeRef)).thenReturn(false);
// Set up parameters. // Set up parameters.
Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "parentNodeRef", Map<String, String> parameters = ImmutableMap.of("batchsize", "10", "parentNodeRef",
parentNodeRef.toString()); parentNodeRef.toString());
executeJSONWebScript(parameters); executeJSONWebScript(parameters);
fail("Expected exception as parameter parentNodeRef does not exist."); fail("Expected exception as parameter parentNodeRef does not exist.");
} }
catch (WebScriptException e) catch (WebScriptException e)
{ {
assertEquals("If parameter parentNodeRef is does not exist then 'Bad Reequest' should be returned.", assertEquals("If parameter parentNodeRef is does not exist then 'Bad Reequest' should be returned.",
Status.STATUS_BAD_REQUEST, e.getStatus()); Status.STATUS_BAD_REQUEST, e.getStatus());
} }
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void processedWithParentNodeRef() throws Exception public void processedWithParentNodeRef() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l).collect(Collectors.toList());
NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
List<FileInfo> children = new ArrayList<FileInfo>(); List<FileInfo> children = new ArrayList<FileInfo>();
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(true);
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)) when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)) when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
String name = "name" + i; String name = "name" + i;
when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name); when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name);
FileInfo mockedFileInfo = mock(FileInfo.class); FileInfo mockedFileInfo = mock(FileInfo.class);
when(mockedFileInfo.getNodeRef()).thenReturn(nodeRef); when(mockedFileInfo.getNodeRef()).thenReturn(nodeRef);
children.add(mockedFileInfo); children.add(mockedFileInfo);
}); });
when(mockedFileFolderService.search(eq(parentNodeRef), eq("*"), eq(true), eq(true), eq(true))) when(mockedFileFolderService.search(eq(parentNodeRef), eq("*"), eq(true), eq(true), eq(true)))
.thenReturn(children); .thenReturn(children);
Map<String, String> parameters = ImmutableMap.of("batchsize", "3", "maxProcessedRecords", "4", "export", Map<String, String> parameters = ImmutableMap.of("batchsize", "3", "maxProcessedRecords", "4", "export",
"false", "parentNodeRef", parentNodeRef.toString()); "false", "parentNodeRef", parentNodeRef.toString());
JSONObject json = executeJSONWebScript(parameters); JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json); assertNotNull(json);
String actualJSONString = json.toString(); String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 3 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class), verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class),
any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class)); any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void processedWithParentNodeRefWithFirstTwoBatchesAlreadyProcessed() throws Exception public void processedWithParentNodeRefWithFirstTwoBatchesAlreadyProcessed() throws Exception
{ {
List<Long> ids = Stream.of(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l).collect(Collectors.toList()); List<Long> ids = Stream.of(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l).collect(Collectors.toList());
NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef parentNodeRef = AlfMock.generateNodeRef(mockedNodeService);
List<FileInfo> children = new ArrayList<FileInfo>(); List<FileInfo> children = new ArrayList<FileInfo>();
ids.stream().forEach((i) -> { ids.stream().forEach((i) -> {
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService); NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT_RECORD)).thenReturn(true);
if (i <= 6l) if (i <= 6l)
{ {
when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(false); when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(false);
} }
else else
{ {
when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(true); when(mockedNodeService.hasAspect(nodeRef, ASPECT)).thenReturn(true);
} }
when(mockedNodeService.getProperty(nodeRef, PROP_READERS)) when(mockedNodeService.getProperty(nodeRef, PROP_READERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS)) when(mockedNodeService.getProperty(nodeRef, PROP_WRITERS))
.thenReturn((Serializable) Collections.emptyMap()); .thenReturn((Serializable) Collections.emptyMap());
String name = "name" + i; String name = "name" + i;
when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name); when(mockedNodeService.getProperty(nodeRef, ContentModel.PROP_NAME)).thenReturn((Serializable) name);
FileInfo mockedFileInfo = mock(FileInfo.class); FileInfo mockedFileInfo = mock(FileInfo.class);
when(mockedFileInfo.getNodeRef()).thenReturn(nodeRef); when(mockedFileInfo.getNodeRef()).thenReturn(nodeRef);
children.add(mockedFileInfo); children.add(mockedFileInfo);
}); });
when(mockedFileFolderService.search(eq(parentNodeRef), eq("*"), eq(true), eq(true), eq(true))) when(mockedFileFolderService.search(eq(parentNodeRef), eq("*"), eq(true), eq(true), eq(true)))
.thenReturn(children); .thenReturn(children);
Map<String, String> parameters = ImmutableMap.of("batchsize", "3", "parentNodeRef", parentNodeRef.toString()); Map<String, String> parameters = ImmutableMap.of("batchsize", "3", "parentNodeRef", parentNodeRef.toString());
JSONObject json = executeJSONWebScript(parameters); JSONObject json = executeJSONWebScript(parameters);
assertNotNull(json); assertNotNull(json);
String actualJSONString = json.toString(); String actualJSONString = json.toString();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 2 records.\"}"; String expectedJSONString = "{\"responsestatus\":\"success\",\"message\":\"Processed 2 records.\"}";
assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString)); assertEquals(mapper.readTree(expectedJSONString), mapper.readTree(actualJSONString));
verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class), verify(contentStreamer, never()).streamContent(any(WebScriptRequest.class), any(WebScriptResponse.class),
any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class)); any(File.class), any(Long.class), any(Boolean.class), any(String.class), any(Map.class));
} }
} }