sync release/V2.4 into feature-2.4/RM-3450_SFDCIntegration

This commit is contained in:
Roxana Lucanu-Ghetu
2016-07-05 11:54:19 +03:00
27 changed files with 1112 additions and 335 deletions

View File

@@ -17,6 +17,9 @@ imap.server.attachments.extraction.enabled=false
#
audit.enabled=true
audit.rm.enabled=true
#audit.rm.runas=admin
#audit.filter.alfresco-access.transaction.user=~null;.*
#
# Extended permission service cache sizing

View File

@@ -60,6 +60,11 @@
class="org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition.TransferredCapabilityCondition">
</bean>
<bean id="capabilityCondition.movable"
parent="capabilityCondition.base"
class="org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition.MovableCapabilityCondition">
</bean>
<bean id="capabilityCondition.destroyed"
parent="capabilityCondition.base"
class="org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition.DestroyedCapabilityCondition">

View File

@@ -129,8 +129,7 @@
</property>
<property name="conditions">
<map>
<entry key="capabilityCondition.cutoff" value="false"/>
<entry key="capabilityCondition.closed" value="false"/>
<entry key="capabilityCondition.movable" value="true"/>
</map>
</property>
<property name="targetCapability" ref="rmCreateRecordFolderCapability"/>

View File

@@ -38,3 +38,5 @@ rm.action.node-not-record-category=The disposition schedule could not be created
rm.action.parameter-not-supplied=The parameter ''{0}'' has not been supplied.
rm.action.delete-not-hold-type=The hold couldn't be deleted, because the node isn't of type {0}. (actionedUponNodeRef={1})
rm.action.cast-to-rm-type=Can't upload a custom folder type to the records management file plan.
rm.action.record-folder-create=Operation failed, because you can't place a record folder into another record folder.

View File

@@ -103,7 +103,7 @@
<!-- Import the RM service's -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-service-context.xml"/>
<!-- Import DOD 5015 -->
<!-- Import DOD 5015 -->
<import resource="classpath:alfresco/module/org_alfresco_module_rm/dod5015/dod5015-context.xml"/>
<!-- Import the RM identifier service's -->
@@ -195,7 +195,6 @@
<!-- init caveatConfig behaviours -->
<property name="caveatConfigService" ref="caveatConfigService"/>
<property name="customEmailMappingService" ref="customEmailMappingService"/>
<property name="recordsManagementAdminService" ref="RecordsManagementAdminService"/>
<property name="suggesterBootstrap" ref="nodeParameterSuggesterBootstrap"/>
</bean>

View File

@@ -51,23 +51,25 @@
</property>
</bean>
<!-- Disposition Lifecycle Job -->
<bean id="scheduledDispositionLifecyceleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="dispositionLifecycle"/>
<entry key="jobLockService">
<ref bean="jobLockService" />
</entry>
<entry key="jobExecuter">
<ref bean="dispositionLifecycleJobExecuter" />
</entry>
</map>
</property>
</bean>
<!-- Disposition Lifecycle Job -->
<bean id="scheduledDispositionLifecyceleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="dispositionLifecycle" />
<entry key="runAuditAs" value="${audit.rm.runas}" />
<entry key="jobLockService">
<ref bean="jobLockService" />
</entry>
<entry key="jobExecuter">
<ref bean="dispositionLifecycleJobExecuter" />
</entry>
</map>
</property>
</bean>
<bean id="dispositionLifecycleJobExecuter"
class="org.alfresco.module.org_alfresco_module_rm.job.DispositionLifecycleJobExecuter"
@@ -83,6 +85,7 @@
<property name="nodeService" ref="nodeService" />
<property name="searchService" ref="searchService" />
<property name="authenticationService" ref="authenticationService" />
<property name="recordsManagementActionService" ref="recordsManagementActionService" />
</bean>

View File

@@ -775,6 +775,7 @@
<bean id="recordsManagementAdminService" class="org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminServiceImpl" parent="recordsManagementAdminBase">
<property name="relationshipService" ref="RelationshipService" />
<property name="transactionService" ref="transactionService"/>
<property name="customisableTypes">
<list>
<value>rma:recordCategory</value>
@@ -817,7 +818,6 @@
<property name="objectDefinitionSource">
<value>
<![CDATA[
org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService.initialiseCustomModel=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService.getCustomisable=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService.isCustomisable=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService.makeCustomisable=RM_ALLOW

View File

@@ -47,6 +47,9 @@ public class EditDispositionActionAsOfDateAction extends RMActionExecuterAbstrac
private static final String MSG_VALID_DATE_DISP_ASOF = "rm.action.valid-date-disp-asof";
private static final String MSG_DISP_ASOF_LIFECYCLE_APPLIED = "rm.action.disp-asof-lifecycle-applied";
/** Action name */
public static final String NAME = "editDispositionActionAsOfDate";
/** Action parameters */
public static final String PARAM_AS_OF_DATE = "asOfDate";

View File

@@ -54,11 +54,6 @@ import org.alfresco.service.namespace.RegexQNamePattern;
*/
public interface RecordsManagementAdminService
{
/**
* Initialise the custom model
*/
void initialiseCustomModel();
/**
* Get a list of all registered customisable types and aspects.
*

View File

@@ -24,7 +24,6 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.admin;
import static org.springframework.extensions.surf.util.ParameterCheck.mandatory;
@@ -61,6 +60,7 @@ import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.policy.annotation.BehaviourKind;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.Constraint;
@@ -73,7 +73,11 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.GUID;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.surf.util.URLDecoder;
@@ -83,10 +87,14 @@ import org.springframework.extensions.surf.util.URLDecoder;
* @author Neil McErlean, janv
*/
@BehaviourBean
public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBase implements RecordsManagementAdminService,
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnCreateNodePolicy
public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBase
implements RecordsManagementAdminService,
RecordsManagementCustomModel,
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnCreateNodePolicy,
ApplicationListener<ContextRefreshedEvent>,
Ordered
{
/** I18N messages*/
private static final String MSG_SERVICE_NOT_INIT = "rm.admin.service-not-init";
@@ -108,13 +116,25 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
/** Relationship service */
private RelationshipService relationshipService;
/** Transaction service */
private TransactionService transactionService;
/** List of types that can be customisable */
private List<QName> pendingCustomisableTypes;
private Map<QName, QName> customisableTypes;
/** indicates whether the custom map has been initialised or not */
private boolean isCustomMapInit = false;
/**
* @param transactionService transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the relationship instance
*
* @param relationshipService The relationship service instance
*/
public void setRelationshipService(RelationshipService relationshipService)
@@ -132,6 +152,48 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
return this.relationshipService;
}
/**
* Indicate that this application content listener must be executed with the lowest
* precedence. (ie last)
*
* @see Ordered#getOrder()
*/
@Override
public int getOrder()
{
return Ordered.LOWEST_PRECEDENCE;
}
/**
* Load the custom properties map
*
* @see ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event)
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// initialise custom properties
initCustomMap();
return null;
}
});
}
/**
* Helper method to indicate whether the custom map is initialised or not.
*
* @return boolean true if initialised, false otherwise
*/
public boolean isCustomMapInit()
{
return isCustomMapInit;
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
@@ -144,25 +206,25 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
)
public void onAddAspect(final NodeRef nodeRef, final QName aspectTypeQName)
{
mandatory("nodeRef", nodeRef);
mandatory("aspectTypeQName", aspectTypeQName);
AuthenticationUtil.runAs(new RunAsWork<Void>()
if (isCustomMapInit)
{
@Override
public Void doWork()
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
if (getNodeService().exists(nodeRef) &&
getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL) &&
isCustomisable(aspectTypeQName))
@Override
public Void doWork()
{
QName customPropertyAspect = getCustomAspect(aspectTypeQName);
getNodeService().addAspect(nodeRef, customPropertyAspect, null);
}
if (getNodeService().exists(nodeRef) &&
getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL) &&
isCustomisable(aspectTypeQName))
{
QName customPropertyAspect = getCustomAspect(aspectTypeQName);
getNodeService().addAspect(nodeRef, customPropertyAspect, null);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**
@@ -177,24 +239,24 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
)
public void onRemoveAspect(final NodeRef nodeRef, final QName aspectTypeQName)
{
mandatory("nodeRef", nodeRef);
mandatory("aspectTypeQName", aspectTypeQName);
AuthenticationUtil.runAs(new RunAsWork<Void>()
if (isCustomMapInit)
{
@Override
public Void doWork()
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
if (getNodeService().exists(nodeRef) &&
isCustomisable(aspectTypeQName))
@Override
public Void doWork()
{
QName customPropertyAspect = getCustomAspect(aspectTypeQName);
getNodeService().removeAspect(nodeRef, customPropertyAspect);
}
if (getNodeService().exists(nodeRef) &&
isCustomisable(aspectTypeQName))
{
QName customPropertyAspect = getCustomAspect(aspectTypeQName);
getNodeService().removeAspect(nodeRef, customPropertyAspect);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**
@@ -211,49 +273,41 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
)
public void onCreateNode(final ChildAssociationRef childAssocRef)
{
mandatory("nodeRef", childAssocRef);
AuthenticationUtil.runAs(new RunAsWork<Void>()
if (isCustomMapInit)
{
@Override
public Void doWork()
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
if (getDictionaryService().getAllModels().contains(RecordsManagementCustomModel.RM_CUSTOM_MODEL))
@Override
public Void doWork()
{
NodeRef nodeRef = childAssocRef.getChildRef();
QName type = getNodeService().getType(nodeRef);
while (type != null && !ContentModel.TYPE_CMOBJECT.equals(type))
if (getDictionaryService().getAllModels().contains(RecordsManagementCustomModel.RM_CUSTOM_MODEL))
{
if (isCustomisable(type))
NodeRef nodeRef = childAssocRef.getChildRef();
QName type = getNodeService().getType(nodeRef);
while (type != null && !ContentModel.TYPE_CMOBJECT.equals(type))
{
QName customPropertyAspect = getCustomAspect(type);
getNodeService().addAspect(nodeRef, customPropertyAspect, null);
}
if (isCustomisable(type))
{
QName customPropertyAspect = getCustomAspect(type);
getNodeService().addAspect(nodeRef, customPropertyAspect, null);
}
TypeDefinition def = getDictionaryService().getType(type);
if (def != null)
{
type = def.getParentName();
}
else
{
type = null;
TypeDefinition def = getDictionaryService().getType(type);
if (def != null)
{
type = def.getParentName();
}
else
{
type = null;
}
}
}
return null;
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#initialiseCustomModel()
*/
public void initialiseCustomModel()
{
// Initialise the map
getCustomisableMap();
}, AuthenticationUtil.getSystemUserName());
}
}
/**
@@ -339,6 +393,92 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
return result;
}
/**
* Initialise custom type map
*/
private void initCustomMap()
{
customisableTypes = new HashMap<QName, QName>(7);
Collection<QName> aspects = getDictionaryService().getAspects(RM_CUSTOM_MODEL);
for (QName aspect : aspects)
{
AspectDefinition aspectDef = getDictionaryService().getAspect(aspect);
String name = aspectDef.getName().getLocalName();
if (name.endsWith("Properties"))
{
QName type = null;
String prefixString = aspectDef.getDescription(getDictionaryService());
if (prefixString == null)
{
// Backward compatibility from previous RM V1.0 custom models
if (CompatibilityModel.NAME_CUSTOM_RECORD_PROPERTIES.equals(name))
{
type = RecordsManagementModel.ASPECT_RECORD;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_FOLDER_PROPERTIES.equals(name))
{
type = RecordsManagementModel.TYPE_RECORD_FOLDER;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_CATEGORY_PROPERTIES.equals(name))
{
type = RecordsManagementModel.TYPE_RECORD_CATEGORY;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_SERIES_PROPERTIES.equals(name) &&
// Only add the deprecated record series type as customisable if
// a v1.0 installation has added custom properties
aspectDef.getProperties().size() != 0)
{
type = CompatibilityModel.TYPE_RECORD_SERIES;
}
}
else
{
type = QName.createQName(prefixString, getNamespaceService());
}
// Add the customisable type to the map
if (type != null)
{
customisableTypes.put(type, aspect);
// Remove customisable type from the pending list
if (pendingCustomisableTypes != null && pendingCustomisableTypes.contains(type))
{
pendingCustomisableTypes.remove(type);
}
}
}
}
// Deal with any pending types left over
if (pendingCustomisableTypes != null && pendingCustomisableTypes.size() != 0)
{
NodeRef modelRef = getCustomModelRef(RecordsManagementModel.RM_CUSTOM_URI);
M2Model model = readCustomContentModel(modelRef);
try
{
for (QName customisableType : pendingCustomisableTypes)
{
QName customAspect = getCustomAspectImpl(customisableType);
// Create the new aspect to hold the custom properties
M2Aspect aspect = model.createAspect(customAspect.toPrefixString(getNamespaceService()));
aspect.setDescription(customisableType.toPrefixString(getNamespaceService()));
// Make a record of the customisable type
customisableTypes.put(customisableType, customAspect);
}
}
finally
{
writeCustomContentModel(modelRef, model);
}
}
// indicate map is initialised
isCustomMapInit = true;
}
/**
* Gets a map containing all the customisable types
*
@@ -346,86 +486,11 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
*/
private Map<QName, QName> getCustomisableMap()
{
if (customisableTypes == null)
{
customisableTypes = new HashMap<QName, QName>(7);
Collection<QName> aspects = getDictionaryService().getAspects(RM_CUSTOM_MODEL);
for (QName aspect : aspects)
{
AspectDefinition aspectDef = getDictionaryService().getAspect(aspect);
String name = aspectDef.getName().getLocalName();
if (name.endsWith("Properties"))
{
QName type = null;
String prefixString = aspectDef.getDescription(getDictionaryService());
if (prefixString == null)
{
// Backward compatibility from previous RM V1.0 custom models
if (CompatibilityModel.NAME_CUSTOM_RECORD_PROPERTIES.equals(name))
{
type = RecordsManagementModel.ASPECT_RECORD;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_FOLDER_PROPERTIES.equals(name))
{
type = RecordsManagementModel.TYPE_RECORD_FOLDER;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_CATEGORY_PROPERTIES.equals(name))
{
type = RecordsManagementModel.TYPE_RECORD_CATEGORY;
}
else if (CompatibilityModel.NAME_CUSTOM_RECORD_SERIES_PROPERTIES.equals(name) &&
// Only add the deprecated record series type as customisable if
// a v1.0 installation has added custom properties
aspectDef.getProperties().size() != 0)
{
type = CompatibilityModel.TYPE_RECORD_SERIES;
}
}
else
{
type = QName.createQName(prefixString, getNamespaceService());
}
// Add the customisable type to the map
if (type != null)
{
customisableTypes.put(type, aspect);
// Remove customisable type from the pending list
if (pendingCustomisableTypes != null && pendingCustomisableTypes.contains(type))
{
pendingCustomisableTypes.remove(type);
}
}
}
}
// Deal with any pending types left over
if (pendingCustomisableTypes != null && pendingCustomisableTypes.size() != 0)
{
NodeRef modelRef = getCustomModelRef(RecordsManagementModel.RM_CUSTOM_URI);
M2Model model = readCustomContentModel(modelRef);
try
{
for (QName customisableType : pendingCustomisableTypes)
{
QName customAspect = getCustomAspectImpl(customisableType);
// Create the new aspect to hold the custom properties
M2Aspect aspect = model.createAspect(customAspect.toPrefixString(getNamespaceService()));
aspect.setDescription(customisableType.toPrefixString(getNamespaceService()));
// Make a record of the customisable type
customisableTypes.put(customisableType, customAspect);
}
}
finally
{
writeCustomContentModel(modelRef, model);
}
}
}
return customisableTypes;
if (customisableTypes == null)
{
throw AlfrescoRuntimeException.create("Customisable map has not been initialised correctly.");
}
return customisableTypes;
}
/**

View File

@@ -28,7 +28,6 @@
package org.alfresco.module.org_alfresco_module_rm.bootstrap;
import org.alfresco.module.org_alfresco_module_rm.action.impl.SplitEmailAction;
import org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService;
import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService;
import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService;
import org.alfresco.repo.action.parameter.NodeParameterSuggesterBootstrap;
@@ -50,7 +49,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean
private TransactionService transactionService;
private RMCaveatConfigService caveatConfigService;
private CustomEmailMappingService customEmailMappingService;
private RecordsManagementAdminService adminService;
private NodeParameterSuggesterBootstrap suggesterBootstrap;
public NodeParameterSuggesterBootstrap getSuggesterBootstrap()
@@ -78,11 +76,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean
this.customEmailMappingService = customEmailMappingService;
}
public void setRecordsManagementAdminService(RecordsManagementAdminService adminService)
{
this.adminService = adminService;
}
public CustomEmailMappingService getCustomEmailMappingService()
{
return customEmailMappingService;
@@ -103,9 +96,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean
// initialise caveat config
caveatConfigService.init();
// Initialise the custom model
adminService.initialiseCustomModel();
// Initialize the suggester after the model
// in case it contains namespaces from custom models
suggesterBootstrap.init();

View File

@@ -0,0 +1,52 @@
/*
* #%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.capability.declarative.condition;
import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Movable capability condition.
*
* @author Roxana Dina
* @since 2.4.1
*
*/
public class MovableCapabilityCondition extends AbstractCapabilityCondition
{
/**
* A record folder should not be moved when it is cut off, but it should be possible to move it when it is destroyed.
*/
@Override
public boolean evaluateImpl(NodeRef nodeRef)
{
if (nodeService.hasAspect(nodeRef, ASPECT_GHOSTED) && dispositionService.isDisposableItemCutoff(nodeRef))
return true;
return !dispositionService.isDisposableItemCutoff(nodeRef);
}
}

View File

@@ -889,6 +889,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl
// Get the current action
String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID);
currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId);
// When the record has multiple disposition schedules the current disposition action may not be found by id
// In this case it will be searched by name
if(currentDispositionActionDefinition == null)
{
String currentADName = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION);
currentDispositionActionDefinition = di.getDispositionActionDefinitionByName(currentADName);
}
// Get the next disposition action
int index = currentDispositionActionDefinition.getIndex();

View File

@@ -43,16 +43,13 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The Disposition Lifecycle Job Finds all disposition action nodes which are
* for disposition actions specified Where asOf > now OR
* dispositionEventsEligible = true;
*
* Runs the cut off or retain action for
* eligible records.
* The Disposition Lifecycle Job Finds all disposition action nodes which are for disposition actions specified Where
* asOf > now OR dispositionEventsEligible = true; Runs the cut off or retain action for eligible records.
*
* @author mrogers
* @author Roy Wetherall
@@ -77,10 +74,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** search service */
private SearchService searchService;
/** authenticationService service */
private AuthenticationService authenticationService;
/**
* List of disposition actions to automatically execute when eligible.
*
* @param dispositionActions disposition actions
* @param dispositionActions disposition actions
*/
public void setDispositionActions(List<String> dispositionActions)
{
@@ -88,7 +88,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
/**
* @param recordsManagementActionService records management action service
* @param recordsManagementActionService records management action service
*/
public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService)
{
@@ -96,7 +96,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
/**
* @param nodeService node service
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
@@ -114,7 +114,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/**
* Get the search query string.
*
* @return job query string
* @return job query string
*/
protected String getQuery()
{
@@ -165,7 +165,8 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (dispositionActions != null && !dispositionActions.isEmpty())
{
// execute search
ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_FTS_ALFRESCO, getQuery());
ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_FTS_ALFRESCO, getQuery());
List<NodeRef> resultNodes = results.getNodeRefs();
results.close();
@@ -183,7 +184,8 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{
public Boolean execute()
{
final String dispAction = (String) nodeService.getProperty(currentNode, RecordsManagementModel.PROP_DISPOSITION_ACTION);
final String dispAction = (String) nodeService.getProperty(currentNode,
RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run disposition action
if (dispAction != null && dispositionActions.contains(dispAction))
@@ -192,12 +194,14 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
{
Map<String, Serializable> props = new HashMap<String, Serializable>(1);
props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK, Boolean.FALSE);
props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK,
Boolean.FALSE);
try
{
// execute disposition action
recordsManagementActionService.executeRecordsManagementAction(parent.getParentRef(), dispAction, props);
recordsManagementActionService.executeRecordsManagementAction(
parent.getParentRef(), dispAction, props);
if (logger.isDebugEnabled())
{
@@ -236,4 +240,14 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
}
}
public AuthenticationService getAuthenticationService()
{
return authenticationService;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
}

View File

@@ -46,8 +46,7 @@ import org.quartz.JobExecutionException;
/**
* Base records management job implementation.
* <p>
* Delegates job execution and ensures locking
* is enforced.
* Delegates job execution and ensures locking is enforced.
*
* @author Roy Wetherall
*/
@@ -55,11 +54,14 @@ public class RecordsManagementJob implements Job
{
private static Log logger = LogFactory.getLog(RecordsManagementJob.class);
/** which user should be used to log audit */
private String runAuditAs = AuthenticationUtil.getSystemUserName();
private static final long DEFAULT_TIME = 30000L;
private JobLockService jobLockService;
private RecordsManagementJobExecuter jobExecuter;
private RecordsManagementJobExecuter jobExecuter = null;
private String jobName;
@@ -85,11 +87,10 @@ public class RecordsManagementJob implements Job
}
}
/**
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
*
* @return Returns the lock token or <tt>null</tt>
* @return Returns the lock token or <tt>null</tt>
*/
private String getLock()
{
@@ -103,31 +104,54 @@ public class RecordsManagementJob implements Job
}
}
@Override
/**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
// get the job lock service
jobLockService = (JobLockService)context.getJobDetail().getJobDataMap().get("jobLockService");
if (jobLockService == null)
{
throw new AlfrescoRuntimeException("Job lock service has not been specified.");
}
jobLockService = (JobLockService) context.getJobDetail().getJobDataMap().get("jobLockService");
if (jobLockService == null) { throw new AlfrescoRuntimeException("Job lock service has not been specified."); }
// get the job executer
jobExecuter = (RecordsManagementJobExecuter)context.getJobDetail().getJobDataMap().get("jobExecuter");
if (jobExecuter == null)
{
throw new AlfrescoRuntimeException("Job executer has not been specified.");
}
jobExecuter = (RecordsManagementJobExecuter) context.getJobDetail().getJobDataMap().get("jobExecuter");
if (jobExecuter == null) { throw new AlfrescoRuntimeException("Job executer has not been specified."); }
// get the job name
jobName = (String)context.getJobDetail().getJobDataMap().get("jobName");
if (jobName == null)
jobName = (String) context.getJobDetail().getJobDataMap().get("jobName");
if (jobName == null) { throw new AlfrescoRuntimeException("Job name has not been specified."); }
if (jobName.compareTo("dispositionLifecycle") == 0)
{
throw new AlfrescoRuntimeException("Job name has not been specified.");
//RM-3293 - set user for audit
if (jobExecuter instanceof DispositionLifecycleJobExecuter)
{
String auditUser = (String) context.getJobDetail().getJobDataMap().get("runAuditAs");
if (((DispositionLifecycleJobExecuter) jobExecuter).getAuthenticationService()
.authenticationExists(auditUser))
{
setRunAuditAs(auditUser);
}
else
{
setRunAuditAs(AuthenticationUtil.getSystemUserName());
}
}
if (logger.isDebugEnabled())
{
logger.debug("DispositionLifecycleJobExecuter() logged audit history with user: " + getRunAuditAs());
}
}
final LockCallback lockCallback = new LockCallback();
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork()
@@ -154,7 +178,8 @@ public class RecordsManagementJob implements Job
// Ignore
if (logger.isDebugEnabled())
{
logger.debug("Lock release failed: " + getLockQName() + ": " + lockToken + "(" + e.getMessage() + ")");
logger.debug("Lock release failed: " + getLockQName() + ": " + lockToken + "("
+ e.getMessage() + ")");
}
}
}
@@ -163,6 +188,18 @@ public class RecordsManagementJob implements Job
// return
return null;
}
}, AuthenticationUtil.getSystemUserName());
}, getRunAuditAs());
}
public String getRunAuditAs()
{
return runAuditAs;
}
public void setRunAuditAs(String runAuditAs)
{
this.runAuditAs = runAuditAs;
}
}

View File

@@ -154,13 +154,22 @@ public class RecordAspect extends AbstractDisposableItem
kind = BehaviourKind.CLASS,
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
)
public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference)
public void onCreateReference(final NodeRef fromNodeRef, NodeRef toNodeRef, QName reference)
{
// Deal with versioned records
if (reference.equals(CUSTOM_REF_VERSIONS))
{
// Apply the versioned aspect to the from node
nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null);
// run as system, to apply the versioned aspect to the from node
// as we can't be sure if the user has add aspect rights
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null);
return null;
}
});
}
// Execute script if for the reference event

View File

@@ -31,6 +31,7 @@ import java.io.Serializable;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
@@ -49,6 +50,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* rma:recordFolder behaviour bean
@@ -73,6 +75,9 @@ public class RecordFolderType extends AbstractDisposableItem
/** vital record service */
protected VitalRecordService vitalRecordService;
/** I18N */
private static final String MSG_CANNOT_CREATE_RECORD_FOLDER = "rm.action.record-folder-create";
/**
* @param recordService record service
*/
@@ -211,12 +216,13 @@ public class RecordFolderType extends AbstractDisposableItem
public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew)
{
NodeRef nodeRef = childAssocRef.getChildRef();
if (nodeService.exists(nodeRef) && instanceOf(nodeRef, TYPE_RECORD_FOLDER))
if (nodeService.exists(nodeRef))
{
// ensure nothing is being added to a closed record folder
NodeRef recordFolder = childAssocRef.getParentRef();
Boolean isClosed = (Boolean) nodeService.getProperty(recordFolder, PROP_IS_CLOSED);
if (isClosed != null && Boolean.TRUE.equals(isClosed))
if (isClosed != null && isClosed)
{
throw new AlfrescoRuntimeException("You can't add new items to a closed record folder.");
}
@@ -238,6 +244,12 @@ public class RecordFolderType extends AbstractDisposableItem
{
final NodeRef recordFolder = childAssocRef.getChildRef();
// only records can be added in a record folder or hidden folders(is the case of e-mail attachments)
if (!instanceOf(recordFolder, ContentModel.TYPE_CONTENT) && !nodeService.hasAspect(recordFolder, ContentModel.ASPECT_HIDDEN))
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_CREATE_RECORD_FOLDER));
}
behaviourFilter.disableBehaviour();
try
{

View File

@@ -393,7 +393,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
final NodeRef record = recordService.createRecordFromCopy(filePlan, nodeRef);
// apply version record aspect to record
PropertyMap versionRecordProps = new PropertyMap(3);
final PropertyMap versionRecordProps = new PropertyMap(3);
versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef);
versionRecordProps.put(RecordableVersionModel.PROP_VERSION_LABEL,
standardVersionProperties.get(
@@ -403,7 +403,16 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl
standardVersionProperties.get(
QName.createQName(Version2Model.NAMESPACE_URI,
Version2Model.PROP_VERSION_DESCRIPTION)));
nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps);
// run as system as we can't be sure if the user has add aspect rights
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps);
return null;
}
});
// wire record up to previous record
linkToPreviousVersionRecord(nodeRef, record);

View File

@@ -0,0 +1,148 @@
/*
* #%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.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.PERIOD_ONE_WEEK;
import static org.alfresco.util.GUID.generate;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
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.action.impl.EditDispositionActionAsOfDateAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Update next disposition step integration tests.
*
* @author Roxana Lucanu
* @since 2.4.1
*/
public class UpdateNextDispositionActionTest extends BaseRMTestCase
{
/**
* Given a record with multiple dispositions
* When updating the next step
* Then the action is available
* <p>
* relates to https://issues.alfresco.com/jira/browse/RM-3060
*/
public void testUpdateNextDispositionAction_RM3060() throws Exception
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
NodeRef record;
NodeRef folder2;
@Override
public void given()
{
// create category1
NodeRef category1 = filePlanService.createRecordCategory(filePlan, generate());
// create disposition schedule for category1
createDispositionSchedule(category1);
// create category2
NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate());
// create disposition schedule for category2
createDispositionSchedule(category2);
// create folder2 inside category2
folder2 = recordFolderService.createRecordFolder(category2, generate());
// create folder1 inside category1
NodeRef folder1 = recordFolderService.createRecordFolder(category1, generate());
// create record inside folder1
record = utils.createRecord(folder1, generate(), generate());
}
@Override
public void when() throws Exception
{
// link the record to folder2
recordService.link(record, folder2);
// complete record
utils.completeRecord(record);
// set the disposition as of date to now on the record
rmActionService.executeRecordsManagementAction(record,
EditDispositionActionAsOfDateAction.NAME,
Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date()));
// cut off
rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null);
}
@Override
public void then() throws Exception
{
assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF));
}
});
}
private void createDispositionSchedule(NodeRef category)
{
DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false);
// create the properties for CUTOFF action and add it to the disposition action definition
Map<QName, Serializable> cutOff = new HashMap<QName, Serializable>(3);
cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME);
cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate());
cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK);
dispositionService.addDispositionActionDefinition(ds, cutOff);
// create the properties for TRANSFER action and add it to the disposition action definition
Map<QName, Serializable> transfer = new HashMap<QName, Serializable>(3);
transfer.put(PROP_DISPOSITION_ACTION_NAME, TransferAction.NAME);
transfer.put(PROP_DISPOSITION_DESCRIPTION, generate());
transfer.put(PROP_DISPOSITION_EVENT, (Serializable)Collections.singletonList(DEFAULT_EVENT_NAME));
dispositionService.addDispositionActionDefinition(ds, transfer);
// create the properties for DESTROY action and add it to the disposition action definition
Map<QName, Serializable> destroy = new HashMap<QName, Serializable>(3);
destroy.put(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME);
destroy.put(PROP_DISPOSITION_DESCRIPTION, generate());
destroy.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK);
dispositionService.addDispositionActionDefinition(ds, destroy);
}
}

View File

@@ -0,0 +1,69 @@
/*
* #%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.issue.rm3314;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
/**
* Test for https://issues.alfresco.com/jira/browse/RM-3114
*
* @author Roy Wetherall
* @since 2.2.1.5
*/
public class RM3314Test extends BaseRMTestCase
{
/** registry to record callback from test beans "test.rm3114.1" and "test.rm3114.2" */
public static Map<String, Boolean> callback = new HashMap<String, Boolean>(2);
/**
* Given that the custom model hasn't been initialised
* When an aspect is added
* Then nothing happens
*
* Given that the custom model has been initialised
* When an aspect is added
* Then something happens
*/
public void testListenersExecutedInTheCorrectOrder()
{
/**
* The related test beans will call back into the callback map showing
* whether at the end of their execution whether the custom model has been
* initialised or not. Given the order in which these test beans are executed
* on spring context load, we would expect that .1 executes with the custom
* map unloaded, and the .2 with it loaded.
*/
assertFalse(callback.isEmpty());
assertFalse(callback.get("test.rm3314.1"));
assertTrue(callback.get("test.rm3314.2"));
}
}

View File

@@ -0,0 +1,142 @@
/*
* #%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.issue.rm3314;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminServiceImpl;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.jdbc.BadSqlGrammarException;
/**
* Simple bean used to test RM-3314
*
* @author rwetherall
* @since 2.2.1.5
*/
public class RM3314TestListener implements ApplicationListener<ContextRefreshedEvent>,
Ordered,
BeanNameAware
{
private RecordsManagementAdminServiceImpl recordsManagementAdminService;
private NodeService nodeService;
private FileFolderService fileFolderService;
private Repository repository;
private String name;
private int order = Ordered.LOWEST_PRECEDENCE;
public void setRecordsManagementAdminService(RecordsManagementAdminServiceImpl recordsManagementAdminService)
{
this.recordsManagementAdminService = recordsManagementAdminService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setRepository(Repository repository)
{
this.repository = repository;
}
@Override
public void setBeanName(String name)
{
this.name = name;
}
public void setOrder(int order)
{
this.order = order;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event)
{
// call back to show whether the custom map is initialised or not
RM3314Test.callback.put(name, recordsManagementAdminService.isCustomMapInit());
// Do some work on a node to show that reguardless of whether the custom map is
// init or not, things still work.
// Note: using public services to ensure new transaction for each service call
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
try
{
// create node
NodeRef folder = fileFolderService.create(
repository.getCompanyHome(),
name,
ContentModel.TYPE_FOLDER).getNodeRef();
try
{
// add aspect
nodeService.addAspect(folder, ContentModel.ASPECT_CLASSIFIABLE, null);
// remove aspect
nodeService.removeAspect(folder, ContentModel.ASPECT_CLASSIFIABLE);
}
finally
{
// delete node
nodeService.deleteNode(folder);
}
}
catch (BadSqlGrammarException e)
{
// ignore and carry on
}
return null;
}
});
}
@Override
public int getOrder()
{
return order;
}
}

View File

@@ -34,6 +34,7 @@ import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
@@ -61,8 +62,8 @@ public class MoveRecordFolderTest extends BaseRMTestCase
}
/**
* Given two categories, both with cut off immediately schedules, when the record is move
* then all the parts of the record should be correct based on the new schedule.
* Given two categories, both with cut off immediately schedules, when the record is move then all the parts of the
* record should be correct based on the new schedule.
*
* @see https://issues.alfresco.com/jira/browse/RM-1345
*/
@@ -92,7 +93,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
assertNotNull(CutOffAction.NAME, dispositionAction.getName());
assertNotNull(dispositionAction.getAsOfDate());
assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder));
}
}
});
doTestInTransaction(new VoidTest()
@@ -101,9 +102,10 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// check the search aspect properties
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertEquals(CutOffAction.NAME,
nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF));
}
}
});
}
@@ -115,7 +117,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// move record folder
fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate());
}
}
});
}
@@ -135,12 +137,13 @@ public class MoveRecordFolderTest extends BaseRMTestCase
assertNotNull(dispositionAction.getAsOfDate());
assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder));
// check the search aspect properties
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF));
// check the search aspect properties
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME,
nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF));
}
});
});
}
});
}
@@ -174,7 +177,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
assertNotNull(CutOffAction.NAME, dispositionAction.getName());
assertNotNull(dispositionAction.getAsOfDate());
assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder));
}
}
});
doTestInTransaction(new VoidTest()
@@ -183,9 +186,10 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// check the search aspect properties
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertEquals(CutOffAction.NAME,
nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF));
}
}
});
}
@@ -197,7 +201,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// move record folder
fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate());
}
}
});
}
@@ -213,10 +217,10 @@ public class MoveRecordFolderTest extends BaseRMTestCase
// check the disposition action details
assertNull(dispositionService.getNextDispositionAction(recordFolder));
// check the search aspect properties
assertFalse(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
// check the search aspect properties
assertFalse(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH));
}
});
});
}
});
}
@@ -254,7 +258,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
assertNotNull(CutOffAction.NAME, dispositionAction.getName());
assertNotNull(dispositionAction.getAsOfDate());
assertTrue(dispositionService.isNextDispositionActionEligible(record));
}
}
});
doTestInTransaction(new VoidTest()
@@ -263,9 +267,10 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// check the search aspect properties
assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertEquals(CutOffAction.NAME,
nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF));
}
}
});
}
@@ -277,7 +282,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase
{
// move record folder
fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate());
}
}
});
}
@@ -299,12 +304,13 @@ public class MoveRecordFolderTest extends BaseRMTestCase
assertNotNull(dispositionAction.getAsOfDate());
assertTrue(dispositionService.isNextDispositionActionEligible(record));
// check the search aspect properties
assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF));
// check the search aspect properties
assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME,
nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF));
}
});
});
}
});
}
@@ -394,7 +400,6 @@ public class MoveRecordFolderTest extends BaseRMTestCase
});
}
// try and move a cutoff folder
public void testMoveCutoffRecordFolder() throws Exception
{
@@ -446,11 +451,66 @@ public class MoveRecordFolderTest extends BaseRMTestCase
});
}
// try and move a destroyed folder
public void testMoveDestroyedRecordFolder() throws Exception
{
final NodeRef destination = doTestInTransaction(new Test<NodeRef>()
{
@Override
public NodeRef run()
{
// create a record category (no disposition schedule)
return filePlanService.createRecordCategory(filePlan, "Caitlin Reed");
}
});
final NodeRef testFolder = doTestInTransaction(new Test<NodeRef>()
{
@Override
public NodeRef run()
{
// create folder
NodeRef testFolder = recordFolderService.createRecordFolder(rmContainer, "Peter Edward Francis");
// complete event
Map<String, Serializable> params = new HashMap<String, Serializable>(1);
params.put(CompleteEventAction.PARAM_EVENT_NAME, CommonRMTestUtils.DEFAULT_EVENT_NAME);
rmActionService.executeRecordsManagementAction(testFolder, CompleteEventAction.NAME, params);
// cutoff & destroy folder
rmActionService.executeRecordsManagementAction(testFolder, CutOffAction.NAME);
rmActionService.executeRecordsManagementAction(testFolder, DestroyAction.NAME);
return testFolder;
}
});
doTestInTransaction(new Test<NodeRef>()
{
@Override
public NodeRef run() throws Exception
{
Capability moveCapability = capabilityService.getCapability("MoveRecordFolder");
assertEquals(AccessDecisionVoter.ACCESS_GRANTED, moveCapability.evaluate(testFolder, destination));
return fileFolderService.move(testFolder, destination, null).getNodeRef();
}
@Override
public void test(NodeRef result) throws Exception
{
assertNotNull(result);
}
});
}
private NodeRef createRecordCategory(boolean recordLevel)
{
NodeRef rc = filePlanService.createRecordCategory(filePlan, GUID.generate());
DispositionSchedule dis = utils.createBasicDispositionSchedule(rc, GUID.generate(), GUID.generate(), recordLevel, false);
DispositionSchedule dis = utils.createBasicDispositionSchedule(rc, GUID.generate(), GUID.generate(),
recordLevel, false);
Map<QName, Serializable> adParams = new HashMap<QName, Serializable>(3);
adParams.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME);
adParams.put(PROP_DISPOSITION_DESCRIPTION, GUID.generate());

View File

@@ -0,0 +1,126 @@
/*
* #%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.legacy.action;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction;
import org.alfresco.module.org_alfresco_module_rm.action.dm.DeclareAsVersionRecordAction;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel;
import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionType;
public class DeclareVersionAsRecordActionTest extends BaseRMTestCase
{
private RuleService ruleService;
private NodeRef ruleFile;
protected static final String DESCRIPTION = "description";
@Override
protected void initServices()
{
super.initServices();
ruleService = (RuleService)applicationContext.getBean("RuleService");
}
@Override
protected boolean isCollaborationSiteTest()
{
return true;
}
@Override
protected boolean isRecordTest()
{
return true;
}
/**
* Given a node set to auto-declare documents as records for minor and major versions
* When I try to upload a minor or major version
* Then the version record aspect is added
*/
public void testUpdateNextDispositionAction_RM3060() throws Exception
{
doBehaviourDrivenTest(new BehaviourDrivenTest(dmContributor)
{
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(4);
Version recordedVersion;
@Override
public void given()
{
// create the file
ruleFile = fileFolderService.create(documentLibrary, "mytestfile", ContentModel.TYPE_CONTENT).getNodeRef();
Action action = actionService.createAction(DeclareAsVersionRecordAction.NAME);
action.setParameterValue(CreateRecordAction.PARAM_FILE_PLAN, filePlan);
Rule rule = new Rule();
rule.setRuleType(RuleType.INBOUND);
rule.setTitle("my rule");
rule.setAction(action);
rule.setExecuteAsynchronously(true);
ruleService.saveRule(ruleFile, rule);
// setup version properties
versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true);
versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan);
}
@Override
public void when()
{
recordedVersion = versionService.createVersion(ruleFile, versionProperties);
}
@Override
public void then() throws Exception
{
NodeRef recordedVersionNodeRef= (NodeRef)recordedVersion.getVersionProperties().get(RecordableVersionModel.PROP_RECORD_NODE_REF.getLocalName());
assertNotNull("Recorded version shouldn't be null.", recordedVersionNodeRef);
assertTrue(nodeService.hasAspect(recordedVersionNodeRef, RecordableVersionModel.ASPECT_VERSION_RECORD));
}
});
}
}

View File

@@ -271,6 +271,8 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
/** collaboration site users */
protected String dmConsumer;
protected NodeRef dmConsumerNodeRef;
protected String dmContributor;
protected NodeRef dmContributorNodeRef;
protected String dmCollaborator;
protected NodeRef dmCollaboratorNodeRef;
@@ -773,6 +775,10 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
dmConsumerNodeRef = createPerson(dmConsumer);
siteService.setMembership(collabSiteId, dmConsumer, SiteModel.SITE_CONSUMER);
dmContributor = GUID.generate();
dmContributorNodeRef = createPerson(dmContributor);
siteService.setMembership(collabSiteId, dmContributor, SiteModel.SITE_CONTRIBUTOR);
dmCollaborator = GUID.generate();
dmCollaboratorNodeRef = createPerson(dmCollaborator);
siteService.setMembership(collabSiteId, dmCollaborator, SiteModel.SITE_COLLABORATOR);

View File

@@ -86,6 +86,7 @@ public class CommonRMTestUtils implements RecordsManagementModel
public static final String DEFAULT_EVENT_NAME = "case_closed";
public static final String PERIOD_NONE = "none|0";
public static final String PERIOD_IMMEDIATELY = "immediately|0";
public static final String PERIOD_ONE_WEEK = "week|1";
/**
* Constructor

View File

@@ -246,4 +246,25 @@
<bean id="contentCleanser.test" class="org.alfresco.module.org_alfresco_module_rm.test.util.TestContentCleanser"/>
<!-- Test listeners -->
<!-- Executed with '0' order. This is default order and as such the application event will be executed relative to the other
beans in the order in which they appear in the spring context -->
<bean id="test.rm3314.1" class="org.alfresco.module.org_alfresco_module_rm.test.integration.issue.rm3314.RM3314TestListener">
<property name="order" value="0"/>
<property name="recordsManagementAdminService" ref="recordsManagementAdminService"/>
<property name="nodeService" ref="NodeService"/>
<property name="fileFolderService" ref="FileFolderService" />
<property name="repository" ref="repositoryHelper" />
</bean>
<!-- The default order has this bean executing its application event with the lowest precendence, ie after all the other
beans set to '0' precendence by default -->
<bean id="test.rm3314.2" class="org.alfresco.module.org_alfresco_module_rm.test.integration.issue.rm3314.RM3314TestListener">
<property name="recordsManagementAdminService" ref="recordsManagementAdminService"/>
<property name="nodeService" ref="NodeService"/>
<property name="fileFolderService" ref="FileFolderService" />
<property name="repository" ref="repositoryHelper" />
</bean>
</beans>