Merge remote-tracking branch 'origin/master' into feature/RM-4023_UnfiledRecordFolderAPITests

This commit is contained in:
Kristijan Conkas
2016-11-17 09:29:57 +00:00
37 changed files with 487 additions and 212 deletions

View File

@@ -23,7 +23,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* POJO for file plan component
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class FilePlanComponent
{

View File

@@ -17,7 +17,7 @@ import static org.alfresco.rest.rm.util.ParameterCheck.mandatoryString;
* File plan component alias enumeration
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public enum FilePlanComponentAlias
{

View File

@@ -21,7 +21,7 @@ import org.alfresco.rest.core.RestModels;
* POJO for file plan component entry
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class FilePlanComponentEntry extends RestModels<FilePlanComponent, FilePlanComponentEntry>
{

View File

@@ -15,7 +15,7 @@ package org.alfresco.rest.rm.model.fileplancomponents;
* File plan component field names constants
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class FilePlanComponentFields
{

View File

@@ -15,7 +15,7 @@ package org.alfresco.rest.rm.model.fileplancomponents;
* POJO for id/name pair
*
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
public class FilePlanComponentIdNamePair
{

View File

@@ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
* POJO for FilePlanComponent path parameter
* <br>
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class FilePlanComponentPath

View File

@@ -28,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* POJO for file plan component properties
*
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
//FIXME: Once the fields have been added the JsonIgnoreProperties annotation should be removed
@JsonIgnoreProperties (ignoreUnknown = true)

View File

@@ -17,7 +17,7 @@ import static org.alfresco.rest.rm.util.ParameterCheck.mandatoryString;
* File plan component type enumeration
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public enum FilePlanComponentType
{

View File

@@ -15,7 +15,7 @@ package org.alfresco.rest.rm.model.fileplancomponents;
* POJO for file plan component created by object
*
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
public class FilePlanComponentUserInfo
{

View File

@@ -17,7 +17,7 @@ import org.alfresco.rest.core.RestModels;
* Handle collection of FilePlanComponents
*
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
public class FilePlanComponentsCollection extends RestModels<FilePlanComponentEntry, FilePlanComponentsCollection>
{

View File

@@ -4,7 +4,7 @@ package org.alfresco.rest.rm.model.fileplancomponents;
* POJO for the review period
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class ReviewPeriod
{

View File

@@ -19,7 +19,7 @@ import org.alfresco.rest.model.RestSiteModel;
* POJO for RM Site component
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class RMSite extends RestSiteModel
{

View File

@@ -15,7 +15,7 @@ package org.alfresco.rest.rm.model.site;
* RM Site compliance
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public enum RMSiteCompliance
{

View File

@@ -24,7 +24,7 @@ package org.alfresco.rest.rm.model.site;
*}
* @author Tuna Aksoy
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class RMSiteFields
{

View File

@@ -33,7 +33,7 @@ import org.springframework.stereotype.Component;
*
* @author Tuna Aksoy
* @author Kristijan Conkas
* @since 1.0
* @since 2.6
*/
@Component
@Scope(value = "prototype")

View File

@@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
*
* @author Tuna Aksoy
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
@Component
@Scope (value = "prototype")

View File

@@ -17,7 +17,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
* Utility class for checking parameters
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class ParameterCheck
{

View File

@@ -15,7 +15,7 @@ package org.alfresco.rest.rm.base;
* List of allowable operations
*
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class AllowableOperations
{

View File

@@ -51,7 +51,7 @@ import org.testng.annotations.BeforeClass;
*
* @author Kristijan Conkas
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
@Configuration
@PropertySource("classpath:default.properties")

View File

@@ -27,7 +27,7 @@ import org.testng.annotations.DataProvider;
* Test data used in tests
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public interface TestData
{

View File

@@ -52,7 +52,7 @@ import org.testng.annotations.Test;
* the File Plan CRUD API
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class FilePlanTests extends BaseRestTest
{
@@ -198,7 +198,7 @@ public class FilePlanTests extends BaseRestTest
/**
* Given that a file plan exists
* When I ask the API to delete the file plan
* Then the 403 response code is returned.
* Then the 422 response code is returned.
*/
@Test
(
@@ -217,6 +217,40 @@ public class FilePlanTests extends BaseRestTest
// Delete the file plan component
filePlanComponentAPI.deleteFilePlanComponent(filePlanAlias.toString());
// Check the DELETE response status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(UNPROCESSABLE_ENTITY);
}
/**
* Given that a file plan exists and I am a non RM user
* When I ask the API to delete the file plan
* Then the 403 response code is returned.
*/
@Test
(
description = "Check the response code when deleting the special file plan components with non RM user",
dataProviderClass = TestData.class,
dataProvider = "getContainers"
)
public void deleteFilePlanSpecialComponentsNonRMUser(String filePlanAlias) throws Exception
{
// Create RM Site if doesn't exist
createRMSiteIfNotExists();
// Disconnect the current user from the API session
rmSiteAPI.usingRestWrapper().disconnect();
// Authenticate admin user to Alfresco REST API
restClient.authenticateUser(dataUser.getAdminUser());
// Create a random user
UserModel nonRMuser = dataUser.createRandomTestUser("testUser");
// Authenticate using the random user
filePlanComponentAPI.usingRestWrapper().authenticateUser(nonRMuser);
// Delete the file plan component
filePlanComponentAPI.deleteFilePlanComponent(filePlanAlias.toString());
// Check the DELETE response status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(FORBIDDEN);
}

View File

@@ -51,7 +51,7 @@ import org.testng.annotations.Test;
*
* @author Kristijan Conkas
* @author Tuna Aksoy
* @since 1.0
* @since 2.6
*/
public class RecordCategoryTest extends BaseRestTest
{

View File

@@ -61,7 +61,7 @@ import org.testng.annotations.Test;
* the Record Folder CRUD API
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class RecordFolderTests extends BaseRestTest
{

View File

@@ -51,7 +51,7 @@ import org.testng.annotations.Test;
* the RM site CRUD API
*
* @author Rodica Sutu
* @since 1.0
* @since 2.6
*/
public class RMSiteTests extends BaseRestTest
{

View File

@@ -94,4 +94,35 @@
</property>
</bean>
<bean id="rmDeleteTransferContainerCapability"
parent="declarativeCapability">
<property name="name" value="DeleteTransferContainer"/>
<property name="kinds">
<list>
<value>TRANSFER_CONTAINER</value>
</list>
</property>
<property name="conditions">
<map>
<entry key="capabilityCondition.filling" value="true"/>
</map>
</property>
<property name="private" value="true"/>
</bean>
<bean id="rmDeleteHoldContainerCapability"
parent="declarativeCapability">
<property name="name" value="DeleteHoldContainer"/>
<property name="kinds">
<list>
<value>HOLD_CONTAINER</value>
</list>
</property>
<property name="conditions">
<map>
<entry key="capabilityCondition.filling" value="true"/>
</map>
</property>
<property name="private" value="true"/>
</bean>
</beans>

View File

@@ -25,6 +25,8 @@
<ref bean="rmDeleteUnfiledRecordsContainerFolderCapability"/>
<ref bean="rmDeleteHoldCapability"/>
<ref bean="rmUnlinkFromRecordFolderCapability"/>
<ref bean="rmDeleteTransferContainerCapability"/>
<ref bean="rmDeleteHoldContainerCapability"/>
</list>
</property>
</bean>

View File

@@ -76,6 +76,9 @@
<property name="filePlanService" ref="FilePlanService" />
<property name="recordFolderService" ref="RecordFolderService" />
<property name="filePlanRoleService" ref="FilePlanRoleService" />
<property name="unfilerRecordContainerType" ref="rma.unfiledRecordsContainer" />
<property name="transferContainerType" ref="rma.transferContainer" />
<property name="holdContainerType" ref="rma.holdContainer" />
</bean>
<bean id="rma.holdContainer" class="org.alfresco.module.org_alfresco_module_rm.model.rma.type.HoldContainerType" parent="rm.baseBehaviour">
@@ -112,6 +115,7 @@
<property name="recordsManagementSearchService" ref="RecordsManagementSearchService" />
<property name="capabilityService" ref="CapabilityService" />
<property name="authorityService" ref="AuthorityService" />
<property name="filePlanType" ref="rma.filePlan" />
</bean>
<!-- rma model aspects -->

View File

@@ -519,48 +519,45 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel
*/
private void updateDispositionActionProperties(NodeRef record, NodeRef dispositionAction)
{
if (!methodCached("updateDispositionActionProperties", record))
Map<QName, Serializable> props = nodeService.getProperties(record);
DispositionAction da = new DispositionActionImpl(recordsManagementServiceRegistry, dispositionAction);
props.put(PROP_RS_DISPOSITION_ACTION_NAME, da.getName());
props.put(PROP_RS_DISPOSITION_ACTION_AS_OF, da.getAsOfDate());
props.put(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE, nodeService.getProperty(dispositionAction, PROP_DISPOSITION_EVENTS_ELIGIBLE));
DispositionActionDefinition daDefinition = da.getDispositionActionDefinition();
if (daDefinition != null)
{
Map<QName, Serializable> props = nodeService.getProperties(record);
DispositionAction da = new DispositionActionImpl(recordsManagementServiceRegistry, dispositionAction);
props.put(PROP_RS_DISPOSITION_ACTION_NAME, da.getName());
props.put(PROP_RS_DISPOSITION_ACTION_AS_OF, da.getAsOfDate());
props.put(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE, nodeService.getProperty(dispositionAction, PROP_DISPOSITION_EVENTS_ELIGIBLE));
DispositionActionDefinition daDefinition = da.getDispositionActionDefinition();
if (daDefinition != null)
Period period = daDefinition.getPeriod();
if (period != null)
{
Period period = daDefinition.getPeriod();
if (period != null)
{
props.put(PROP_RS_DISPOSITION_PERIOD, period.getPeriodType());
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, period.getExpression());
}
else
{
props.put(PROP_RS_DISPOSITION_PERIOD, null);
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, null);
}
props.put(PROP_RS_DISPOSITION_PERIOD, period.getPeriodType());
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, period.getExpression());
}
nodeService.setProperties(record, props);
if (logger.isDebugEnabled())
else
{
logger.debug("Set rma:recordSearchDispositionActionName for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_NAME));
logger.debug("Set rma:recordSearchDispositionActionAsOf for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_AS_OF));
logger.debug("Set rma:recordSearchDispositionEventsEligible for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE));
logger.debug("Set rma:recordSearchDispositionPeriod for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD));
logger.debug("Set rma:recordSearchDispositionPeriodExpression for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD_EXPRESSION));
props.put(PROP_RS_DISPOSITION_PERIOD, null);
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, null);
}
}
nodeService.setProperties(record, props);
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchDispositionActionName for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_NAME));
logger.debug("Set rma:recordSearchDispositionActionAsOf for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_AS_OF));
logger.debug("Set rma:recordSearchDispositionEventsEligible for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE));
logger.debug("Set rma:recordSearchDispositionPeriod for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD));
logger.debug("Set rma:recordSearchDispositionPeriodExpression for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD_EXPRESSION));
}
}
/**

View File

@@ -37,6 +37,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean;
@@ -61,10 +62,12 @@ import org.alfresco.service.namespace.QName;
public class FilePlanType extends BaseBehaviourBean
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnDeleteNodePolicy
NodeServicePolicies.OnDeleteNodePolicy,
NodeServicePolicies.BeforeDeleteNodePolicy
{
private final static List<QName> ACCEPTED_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_HOLD_CONTAINER, TYPE_TRANSFER_CONTAINER, TYPE_UNFILED_RECORD_CONTAINER);
private final static List<QName> ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_RECORD_CATEGORY);
private static final String BEHAVIOUR_NAME = "onDeleteFilePlan";
/** file plan service */
private FilePlanService filePlanService;
@@ -78,6 +81,21 @@ public class FilePlanType extends BaseBehaviourBean
/** file plan role service */
private FilePlanRoleService filePlanRoleService;
/**
* Unfiled Record Container Type behaviour bean
*/
private UnfiledRecordContainerType unfilerRecordContainerType;
/**
* Transfer Container Type behaviour bean
*/
private TransferContainerType transferContainerType;
/**
* Hold Container Type behaviour bean
*/
private HoldContainerType holdContainerType;
/**
* @return File plan service
*/
@@ -142,6 +160,49 @@ public class FilePlanType extends BaseBehaviourBean
this.filePlanRoleService = filePlanRoleService;
}
/**
* @param unfilerRecordContainerType - unfiled record container type behaviour bean
*/
public void setUnfilerRecordContainerType(UnfiledRecordContainerType unfilerRecordContainerType)
{
this.unfilerRecordContainerType = unfilerRecordContainerType;
}
/**
* @param transferContainerType - transfer container type behaviour bean
*/
public void setTransferContainerType(TransferContainerType transferContainerType)
{
this.transferContainerType = transferContainerType;
}
/**
* @param holdContainerType - hold container type behaviour bean
*/
public void setHoldContainerType(HoldContainerType holdContainerType)
{
this.holdContainerType = holdContainerType;
}
/**
* Disable the behaviours for this transaction
*
*/
public void disable()
{
getBehaviour(BEHAVIOUR_NAME).disable();
}
/**
* Enable behaviours for this transaction
*
*/
public void enable()
{
getBehaviour(BEHAVIOUR_NAME).enable();
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
*/
@@ -194,15 +255,49 @@ public class FilePlanType extends BaseBehaviourBean
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
*/
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
notificationFrequency = NotificationFrequency.FIRST_EVENT,
name = BEHAVIOUR_NAME
)
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean archived)
{
unfilerRecordContainerType.enable();
transferContainerType.enable();
holdContainerType.enable();
throw new IntegrityException("Operation failed. Deletion of File Plan is not allowed.", null);
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
notificationFrequency = NotificationFrequency.FIRST_EVENT
)
public void beforeDeleteNode(NodeRef nodeRef)
{
unfilerRecordContainerType.disable();
transferContainerType.disable();
holdContainerType.disable();
}
@Behaviour
(
kind = BehaviourKind.CLASS,
policy = "alf:onDeleteNode",
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
)
@Override
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean archived)
public void onDeleteNodeOnCommit(ChildAssociationRef childAssocRef, boolean archived)
{
// tear down the file plan roles
getFilePlanRoleService().tearDownFilePlanRoles(childAssocRef.getChildRef());
unfilerRecordContainerType.enable();
transferContainerType.enable();
holdContainerType.enable();
}
}

View File

@@ -33,6 +33,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.policy.annotation.BehaviourKind;
@@ -49,10 +50,31 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
@BehaviourBean(defaultType = "rma:holdContainer")
public class HoldContainerType extends BaseBehaviourBean
implements NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnDeleteNodePolicy
{
private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container";
private final static List<QName> ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_HOLD);
private static final String DELETE_BEHAVIOUR_NAME = "onDeleteHoldContainer";
/**
* Disable the behaviours for this transaction
*
*/
public void disable()
{
getBehaviour(DELETE_BEHAVIOUR_NAME).disable();
}
/**
* Enable behaviours for this transaction
*
*/
public void enable()
{
getBehaviour(DELETE_BEHAVIOUR_NAME).enable();
}
/**
* On every event
@@ -74,6 +96,16 @@ public class HoldContainerType extends BaseBehaviourBean
NodeRef nodeRef = childAssocRef.getChildRef();
if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) { throw new AlfrescoRuntimeException(
I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); }
}
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
name = DELETE_BEHAVIOUR_NAME
)
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
throw new IntegrityException("Operation failed. Deletion of Hold Container is not allowed.", null);
}
}

View File

@@ -97,6 +97,8 @@ public class RmSiteType extends BaseBehaviourBean
/** Authority service */
private AuthorityService authorityService;
private FilePlanType filePlanType;
/** Map of file plan type's key'ed by corresponding site types */
protected Map<QName, QName> mapFilePlanType = new HashMap<QName, QName>(3);
@@ -133,6 +135,11 @@ public class RmSiteType extends BaseBehaviourBean
this.authorityService = authorityService;
}
public void setFilePlanType(FilePlanType filePlanType)
{
this.filePlanType = filePlanType;
}
/**
* Registers a file plan type for a specific site type.
*
@@ -302,6 +309,7 @@ public class RmSiteType extends BaseBehaviourBean
return null;
}
});
filePlanType.disable();
}
}
}
@@ -354,4 +362,15 @@ public class RmSiteType extends BaseBehaviourBean
throw new IntegrityException(I18NUtil.getMessage(MULTIPLE_CHILDREN_TYPE_ERROR, ContentModel.TYPE_FOLDER), null);
}
}
@Behaviour
(
kind = BehaviourKind.CLASS,
policy = "alf:onDeleteNode",
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
)
public void onDeleteNodeOnCommit(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
filePlanType.enable();
}
}

View File

@@ -46,11 +46,15 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
@BehaviourBean(defaultType = "rma:transferContainer")
public class TransferContainerType extends BaseBehaviourBean
implements NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnDeleteNodePolicy
{
private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container";
private final static String MSG_ERROR_ADD_CHILD_TO_TRANSFER_CONTAINER = "rm.action.create.transfer.container.child-error-message";
private static final String BEHAVIOUR_NAME = "onCreateChildAssocsForTransferContainer";
private static final String DELETE_BEHAVIOUR_NAME = "onDeleteTransferContainer";
/**
* Disable the behaviours for this transaction
@@ -59,6 +63,7 @@ public class TransferContainerType extends BaseBehaviourBean
public void disable()
{
getBehaviour(BEHAVIOUR_NAME).disable();
getBehaviour(DELETE_BEHAVIOUR_NAME).disable();
}
/**
@@ -68,6 +73,7 @@ public class TransferContainerType extends BaseBehaviourBean
public void enable()
{
getBehaviour(BEHAVIOUR_NAME).enable();
getBehaviour(DELETE_BEHAVIOUR_NAME).enable();
}
/**
@@ -94,4 +100,15 @@ public class TransferContainerType extends BaseBehaviourBean
if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) { throw new AlfrescoRuntimeException(
I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); }
}
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
name = DELETE_BEHAVIOUR_NAME
)
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
throw new IntegrityException("Operation failed. Deletion of Transfer Container is not allowed.", null);
}
}

View File

@@ -33,6 +33,7 @@ import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.policy.annotation.BehaviourKind;
@@ -47,9 +48,30 @@ import org.alfresco.service.namespace.QName;
*/
@BehaviourBean(defaultType = "rma:unfiledRecordContainer")
public class UnfiledRecordContainerType extends BaseBehaviourBean
implements NodeServicePolicies.OnCreateChildAssociationPolicy
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnDeleteNodePolicy
{
private static final String BEHAVIOUR_NAME = "onDeleteUnfiledRecordContainer";
private final static List<QName> ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_UNFILED_RECORD_FOLDER, ContentModel.TYPE_CONTENT, TYPE_NON_ELECTRONIC_DOCUMENT);
/**
* Disable the behaviours for this transaction
*
*/
public void disable()
{
getBehaviour(BEHAVIOUR_NAME).disable();
}
/**
* Enable behaviours for this transaction
*
*/
public void enable()
{
getBehaviour(BEHAVIOUR_NAME).enable();
}
@Override
@Behaviour(kind = BehaviourKind.ASSOCIATION)
public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode)
@@ -57,4 +79,15 @@ public class UnfiledRecordContainerType extends BaseBehaviourBean
// check the created child is of an accepted type
validateNewChildAssociationSubTypesIncluded(childAssocRef.getChildRef(), ACCEPTED_NON_UNIQUE_CHILD_TYPES);
}
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
name = BEHAVIOUR_NAME
)
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
throw new IntegrityException("Operation failed. Deletion of Unfiled Record Container is not allowed.", null);
}
}

View File

@@ -48,8 +48,6 @@ import org.alfresco.rest.api.impl.NodesImpl;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.UserInfo;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.RMNodes;
import org.alfresco.rm.rest.api.model.FileplanComponentNode;
import org.alfresco.rm.rest.api.model.RecordCategoryNode;
@@ -380,22 +378,4 @@ public class RMNodesImpl extends NodesImpl implements RMNodes
return false;
}
/**
* Overridden this method just in order to use our isSpecialNode method since core method could not be overridden.
*
* TODO remove this after isSpecialNode will be made protected in core(REPO-1459).
*/
@Override
public void deleteNode(String nodeId, Parameters parameters)
{
NodeRef nodeRef = validateOrLookupNode(nodeId, null);
QName nodeType = nodeService.getType(nodeRef);
if (isSpecialNode(nodeRef, nodeType))
{
throw new PermissionDeniedException("Cannot delete: " + nodeId);
}
super.deleteNode(nodeId, parameters);
}
}

View File

@@ -66,6 +66,10 @@ public class MultipleSchedulesTest extends BaseRMTestCase
protected static final String CATEGORY_B_NAME = TEST_PREFIX + "CategoryB";
/** The name to use for the folder within the second category. */
protected static final String FOLDER_B_NAME = TEST_PREFIX + "FolderB";
/** The name to use for the third category. */
protected static final String CATEGORY_C_NAME = TEST_PREFIX + "CategoryC";
/** The name to use for the folder within the third category. */
protected static final String FOLDER_C_NAME = TEST_PREFIX + "FolderC";
/** The name to use for the record. */
protected static final String RECORD_NAME = TEST_PREFIX + "Record";
@@ -80,6 +84,10 @@ public class MultipleSchedulesTest extends BaseRMTestCase
private NodeRef categoryB;
/** The folder node within the second category. */
private NodeRef folderB;
/** The third category node. */
private NodeRef categoryC;
/** The folder node within the third category. */
private NodeRef folderC;
/** The record node. */
private NodeRef record;
@@ -114,6 +122,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase
// Create two categories.
categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME);
categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_NAME);
categoryC = filePlanService.createRecordCategory(filePlan, CATEGORY_C_NAME);
// Create a disposition schedule for category A (Cut off immediately, then Destroy immediately).
DispositionSchedule dispSchedA = utils.createBasicDispositionSchedule(categoryA, "instructions", "authority", true, false);
Map<QName, Serializable> cutOffParamsA = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME,
@@ -135,9 +144,21 @@ public class MultipleSchedulesTest extends BaseRMTestCase
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_WEEK,
PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE);
dispositionService.addDispositionActionDefinition(dispSchedB, destroyParamsB);
// Create a disposition schedule for category C (Cut off immediately, then Destroy one year after cutoff).
DispositionSchedule dispSchedC = utils.createBasicDispositionSchedule(categoryC, "instructions", "authority", true, false);
Map<QName, Serializable> cutOffParamsC = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME,
PROP_DISPOSITION_DESCRIPTION, "description",
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY);
dispositionService.addDispositionActionDefinition(dispSchedC, cutOffParamsC);
Map<QName, Serializable> destroyParamsC = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME,
PROP_DISPOSITION_DESCRIPTION, "description",
PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_YEAR,
PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE);
dispositionService.addDispositionActionDefinition(dispSchedC, destroyParamsC);
// Create a folder within each category.
folderA = recordFolderService.createRecordFolder(categoryA, FOLDER_A_NAME);
folderB = recordFolderService.createRecordFolder(categoryB, FOLDER_B_NAME);
folderC = recordFolderService.createRecordFolder(categoryC, FOLDER_C_NAME);
}
/**
@@ -155,6 +176,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase
test()
.given(() -> {
setUpFilePlan();
// Create a record filed under category A and linked to category B.
record = fileFolderService.create(folderA, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef();
recordService.link(record, folderB);
@@ -219,19 +241,22 @@ public class MultipleSchedulesTest extends BaseRMTestCase
test()
.given(() -> {
setUpFilePlan();
// Create a record filed under category A and linked to category B.
// Create a record filed under category A and linked to category B and C.
record = fileFolderService.create(folderA, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef();
recordService.link(record, folderB);
})
.when(() -> {
recordService.link(record, folderC);
// Cut off the record.
dispositionService.cutoffDisposableItem(record);
// Ensure the update has been applied to the record.
internalDispositionService.updateNextDispositionAction(record);
})
.when(() -> {
// Unlink the record from folder B.
recordService.unlink(record, folderB);
})
.then()
.expect(true)
.expect(false)
.from(() -> dispositionService.isNextDispositionActionEligible(record))
.because("Destroy action should be available, as the record should follow its origin disposition schedule.");
.because("Destroy action shouldn't be available, as the record should follow disposition schedule from category C.");
}
}

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.integration.issue;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Test for RM-4293
*
* @author Silviu Dinuta
* @since 2.6
*
*/
public class RM4293Test extends BaseRMTestCase
{
public void testDeleteSpecialContainers() throws Exception
{
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
NodeRef holdContainer = filePlanService.getHoldContainer(filePlan);
assertNotNull(holdContainer);
try
{
fileFolderService.delete(holdContainer);
fail("This should have thrown an exception");
}
catch (IntegrityException e)
{
// ("Hold Container can't be deleted.")
}
return null;
}
});
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
NodeRef transferContainer = filePlanService.getTransferContainer(filePlan);
assertNotNull(transferContainer);
try
{
fileFolderService.delete(transferContainer);
fail("This should have thrown an exception");
}
catch (IntegrityException e)
{
// ("Transfer Container can't be deleted.")
}
return null;
}
});
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
NodeRef unfiledRecordContainer = filePlanService.getUnfiledContainer(filePlan);
assertNotNull(unfiledRecordContainer);
try
{
fileFolderService.delete(unfiledRecordContainer);
fail("This should have thrown an exception");
}
catch (IntegrityException e)
{
// ("Unfiled Record Container can't be deleted.")
}
return null;
}
});
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
try
{
fileFolderService.delete(filePlan);
fail("This should have thrown an exception");
}
catch (IntegrityException e)
{
// ("FilePlan can't be deleted.")
}
return null;
}
});
}
}

View File

@@ -35,9 +35,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.security.InvalidParameterException;
@@ -52,8 +49,6 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.repo.model.Repository;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.RMNodes;
import org.alfresco.rm.rest.api.model.FileplanComponentNode;
import org.alfresco.rm.rest.api.model.RecordCategoryNode;
@@ -609,121 +604,6 @@ public class RMNodesImplUnitTest extends BaseUnitTest
assertEquals(nodeRef, validateOrLookupNode);
}
@Test
public void testDeleteNode() throws Exception
{
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
Parameters mockedParameters = mock(Parameters.class);
QName mockedType = AlfMock.generateQName();
when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
setupCompanyHomeAndPrimaryParent(nodeRef);
rmNodesImpl.deleteNode(nodeRef.getId(), mockedParameters);
verify(mockedFileFolderService, times(1)).delete(nodeRef);
}
@Test
public void testDeleteFileplanNode() throws Exception
{
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
Parameters mockedParameters = mock(Parameters.class);
QName mockedType = AlfMock.generateQName();
when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(nodeRef);
try
{
rmNodesImpl.deleteNode(nodeRef.getId(), mockedParameters);
fail("Expected ecxeption as filePlan can't be deleted.");
}
catch(PermissionDeniedException ex)
{
assertEquals("Cannot delete: " + nodeRef.getId(), ex.getMsgId());
}
verify(mockedFileFolderService, never()).delete(nodeRef);
}
@Test
public void testDeleteTransfersContainerNode() throws Exception
{
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
Parameters mockedParameters = mock(Parameters.class);
QName mockedType = AlfMock.generateQName();
when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(nodeRef);
try
{
rmNodesImpl.deleteNode(nodeRef.getId(), mockedParameters);
fail("Expected ecxeption as Trnsfers container can't be deleted.");
}
catch(PermissionDeniedException ex)
{
assertEquals("Cannot delete: " + nodeRef.getId(), ex.getMsgId());
}
verify(mockedFileFolderService, never()).delete(nodeRef);
}
@Test
public void testDeleteHoldsContainerNode() throws Exception
{
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
Parameters mockedParameters = mock(Parameters.class);
QName mockedType = AlfMock.generateQName();
when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
NodeRef transferContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(transferContainerNodeRef);
when(mockedFilePlanService.getHoldContainer(filePlanNodeRef)).thenReturn(nodeRef);
try
{
rmNodesImpl.deleteNode(nodeRef.getId(), mockedParameters);
fail("Expected ecxeption as Holds container can't be deleted.");
}
catch(PermissionDeniedException ex)
{
assertEquals("Cannot delete: " + nodeRef.getId(), ex.getMsgId());
}
verify(mockedFileFolderService, never()).delete(nodeRef);
}
@Test
public void testDeleteUnfiledRecordsContainerNode() throws Exception
{
NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService);
Parameters mockedParameters = mock(Parameters.class);
QName mockedType = AlfMock.generateQName();
when(mockedNodeService.getType(nodeRef)).thenReturn(mockedType);
NodeRef filePlanNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getFilePlanBySiteId(RM_SITE_ID)).thenReturn(filePlanNodeRef);
NodeRef transferContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getTransferContainer(filePlanNodeRef)).thenReturn(transferContainerNodeRef);
NodeRef holdContainerNodeRef = AlfMock.generateNodeRef(mockedNodeService);
when(mockedFilePlanService.getHoldContainer(filePlanNodeRef)).thenReturn(holdContainerNodeRef);
when(mockedFilePlanService.getUnfiledContainer(filePlanNodeRef)).thenReturn(nodeRef);
try
{
rmNodesImpl.deleteNode(nodeRef.getId(), mockedParameters);
fail("Expected ecxeption as Unfiled Records container can't be deleted.");
}
catch(PermissionDeniedException ex)
{
assertEquals("Cannot delete: " + nodeRef.getId(), ex.getMsgId());
}
verify(mockedFileFolderService, never()).delete(nodeRef);
}
private void setupCompanyHomeAndPrimaryParent(NodeRef nodeRef)
{
NodeRef companyHomeNodeRef = AlfMock.generateNodeRef(mockedNodeService);