diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml index be1376b68e..5ce959d53f 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml @@ -28,10 +28,11 @@ - + + {http://www.alfresco.org/model/content/1.0}content diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml index 42cb478e25..ce01a26573 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml @@ -20,6 +20,9 @@ + + + @@ -38,5 +41,33 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java index 844940b7a6..07a8e951ed 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java @@ -18,28 +18,20 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.dm; -import java.io.Serializable; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.AuditableActionExecuterAbstractBase; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.version.VersionModel; +import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.version.Version; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -52,7 +44,7 @@ import org.apache.commons.logging.LogFactory; * @author Roy Wetherall */ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstractBase - implements RecordsManagementModel + implements RecordsManagementModel { /** Logger */ private static Log logger = LogFactory.getLog(DeclareAsVersionRecordAction.class); @@ -64,10 +56,10 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac public static final String PARAM_FILE_PLAN = "file-plan"; /** Sync Model URI */ - static final String SYNC_MODEL_1_0_URI = "http://www.alfresco.org/model/sync/1.0"; + private static final String SYNC_MODEL_1_0_URI = "http://www.alfresco.org/model/sync/1.0"; /** Synced aspect */ - static final QName ASPECT_SYNCED = QName.createQName(SYNC_MODEL_1_0_URI, "synced"); + private static final QName ASPECT_SYNCED = QName.createQName(SYNC_MODEL_1_0_URI, "synced"); /** Node service */ private NodeService nodeService; @@ -78,8 +70,11 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac /** Dictionary service */ private DictionaryService dictionaryService; - /** version service */ - private VersionService versionService; + /** recordable version service */ + private RecordableVersionService recordableVersionService; + + /** authentication util */ + private AuthenticationUtil authenticationUtil; /** * @param nodeService node service @@ -106,12 +101,20 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac } /** - * @param versionService version service + * @param recordableVersionService recordable version service */ - public void setVersionService(VersionService versionService) + public void setRecordableVersionService(RecordableVersionService recordableVersionService) { - this.versionService = versionService; - } + this.recordableVersionService = recordableVersionService; + } + + /** + * @param authenticationUtil authentication util + */ + public void setAuthenticationUtil(AuthenticationUtil authenticationUtil) + { + this.authenticationUtil = authenticationUtil; + } /** * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) @@ -182,14 +185,14 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac { // TODO .. eventually make the file plan parameter required - filePlan = AuthenticationUtil.runAs(new RunAsWork() + filePlan = authenticationUtil.runAs(new org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork() { @Override public NodeRef doWork() { return filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); } - }, AuthenticationUtil.getAdminUserName()); + }, authenticationUtil.getAdminUserName()); // if the file plan is still null, raise an exception if (filePlan == null) @@ -214,15 +217,8 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac } } - // create version properties - Map versionProperties = new HashMap(4); - versionProperties.put(Version.PROP_DESCRIPTION, "Recorded version"); // TODO make this a configurable option of the action - versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); // TODO make this a configurable option of the action - versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); - versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); - - // create recordable version - versionService.createVersion(actionedUponNodeRef, versionProperties); + // create record from latest version + recordableVersionService.createRecordFromLatestVersion(filePlan, actionedUponNodeRef); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java index fc2ff23fe4..1b4d91b18c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java @@ -287,6 +287,7 @@ public class RMAfterInvocationProvider extends RMSecurityCommon } + @SuppressWarnings("rawtypes") private List extractSupportedDefinitions(ConfigAttributeDefinition config) { List definitions = new ArrayList<>(); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index a6edf01996..3be213ebcb 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -854,9 +854,6 @@ public class RecordServiceImpl extends BaseBehaviourBean // add the current owner to the list of extended writers String owner = ownableService.getOwner(nodeRef); - // remove the owner - ownableService.setOwner(nodeRef, OwnableService.NO_OWNER); - // get the documents primary parent assoc ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(nodeRef); @@ -907,9 +904,11 @@ public class RecordServiceImpl extends BaseBehaviourBean // set the extended security Set combinedWriters = new HashSet(writers); - combinedWriters.add(owner); + if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER)) + { + combinedWriters.add(owner); + } combinedWriters.add(AuthenticationUtil.getFullyAuthenticatedUser()); - extendedSecurityService.addExtendedSecurity(nodeRef, readers, combinedWriters); } finally @@ -1187,6 +1186,9 @@ public class RecordServiceImpl extends BaseBehaviourBean // remove versionable aspect(s) nodeService.removeAspect(document, RecordableVersionModel.ASPECT_VERSIONABLE); + + // remove the owner + ownableService.setOwner(document, OwnableService.NO_OWNER); } catch (FileNotFoundException e) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java new file mode 100755 index 0000000000..e5b0c8c6f2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionService.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.version; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; + +/** + * Recordable version service interface. + * + * @author Roy Wetherall + * @since 2.3 + */ +public interface RecordableVersionService +{ + /** + * Indicates whether the current version of a node is recorded or not. + *

+ * Returns false if not versionable or no version. + * + * @param nodeRef node reference + * @return boolean true if latest version recorded, false otherwise + */ + boolean isCurrentVersionRecorded(NodeRef nodeRef); + + /** + * Indicates whether a version is recorded or not. + * + * @param version version + * @return boolean true if recorded version, false otherwise + */ + boolean isRecordedVersion(Version version); + + /** + * Creates a record from the latest version, marking it as recorded. + *

+ * Does not create a record if the node is not versionable or the latest + * version is already recorded. + * + * @param nodeRef node reference + * @return NodeRef node reference to the crated record. + */ + NodeRef createRecordFromLatestVersion(NodeRef filePlan, NodeRef nodeRef); + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java index cc7e90df1e..a148fd3016 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java @@ -25,7 +25,9 @@ import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -34,19 +36,24 @@ import org.alfresco.module.org_alfresco_module_rm.model.rma.type.CmObjectType; import org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.repo.policy.PolicyScope; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService; import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.Version2ServiceImpl; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.version.ReservedVersionNameException; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -58,7 +65,8 @@ import org.apache.commons.logging.LogFactory; * @since 2.3 */ public class RecordableVersionServiceImpl extends Version2ServiceImpl - implements RecordableVersionModel + implements RecordableVersionModel, + RecordableVersionService { /** share logger with version2ServiceImpl */ private static Log logger = LogFactory.getLog(Version2ServiceImpl.class); @@ -69,6 +77,23 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** version record property */ public static final String PROP_VERSION_RECORD = "RecordVersion"; + + /** version aspect property names */ + private static final String[] VERSION_PROPERTY_NAMES = new String[] + { + Version2Model.PROP_CREATED_DATE, + Version2Model.PROP_VERSION_LABEL, + Version2Model.PROP_VERSION_DESCRIPTION, + + Version2Model.PROP_FROZEN_NODE_DBID, + Version2Model.PROP_FROZEN_NODE_REF, + + Version2Model.PROP_FROZEN_CREATED, + Version2Model.PROP_FROZEN_CREATOR, + Version2Model.PROP_FROZEN_MODIFIED, + Version2Model.PROP_FROZEN_MODIFIER, + Version2Model.PROP_FROZEN_ACCESSED + }; /** file plan service */ private FilePlanService filePlanService; @@ -87,6 +112,15 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** cm object type */ private CmObjectType cmObjectType; + + /** extended permission service */ + private ExtendedPermissionService extendedPermissionService; + + /** extended security service */ + private ExtendedSecurityService extendedSecurityService; + + /** ownable service */ + private OwnableService ownableService; /** * @param filePlanService file plan service @@ -135,6 +169,30 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl { this.cmObjectType = cmObjectType; } + + /** + * @param extendedPermissionService extended permission service + */ + public void setExtendedPermissionService(ExtendedPermissionService extendedPermissionService) + { + this.extendedPermissionService = extendedPermissionService; + } + + /** + * @param extendedSecurityService extended security service + */ + public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) + { + this.extendedSecurityService = extendedSecurityService; + } + + /** + * @param ownableService ownable service + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } /** * @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int) @@ -499,4 +557,245 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl dbNodeService.setProperty(nodeRef, PROP_RECORDABLE_VERSION_POLICY, versionPolicy); } } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isLatestVersionRecorded(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isCurrentVersionRecorded(NodeRef nodeRef) + { + boolean result = false; + Version version = getCurrentVersion(nodeRef); + if (version != null) + { + result = isRecordedVersion(version); + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#isRecordedVersion(org.alfresco.service.cmr.version.Version) + */ + @Override + public boolean isRecordedVersion(Version version) + { + boolean result = true; + if (version.getVersionProperties().get(PROP_VERSION_RECORD) == null) + { + result = false; + } + return result; + } + + /** + * Create Version Store Ref + * + * @param store ref + * @return store ref for version store + */ + public StoreRef convertStoreRef(StoreRef storeRef) + { + return new StoreRef(StoreRef.PROTOCOL_WORKSPACE, storeRef.getIdentifier()); + } + + /** + * Convert the incomming node ref (with the version store protocol specified) + * to the internal representation with the workspace protocol. + * + * @param nodeRef the incomming verison protocol node reference + * @return the internal version node reference + */ + public NodeRef convertNodeRef(NodeRef nodeRef) + { + return new NodeRef(convertStoreRef(nodeRef.getStoreRef()), nodeRef.getId()); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService#createRecordFromLatestVersion(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public NodeRef createRecordFromLatestVersion(final NodeRef filePlan, final NodeRef nodeRef) + { + ParameterCheck.mandatory("filePlan", filePlan); + ParameterCheck.mandatory("nodeRef", nodeRef); + + NodeRef record = null; + + // check for versionable aspect + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + // get the latest version + final Version currentVersion = getCurrentVersion(nodeRef); + + if (currentVersion != null && + !isRecordedVersion(currentVersion)) + { + // create the record from the current frozen state + record = authenticationUtil.runAsSystem(new RunAsWork() + { + public NodeRef doWork() throws Exception + { + // get the documents readers + Long aclId = nodeService.getNodeAclId(nodeRef); + Set readers = extendedPermissionService.getReaders(aclId); + Set writers = extendedPermissionService.getWriters(aclId); + + // add the current owner to the list of extended writers + String owner = ownableService.getOwner(nodeRef); + + // grab the frozen state + NodeRef currentFrozenState = currentVersion.getFrozenStateNodeRef(); + + // determine the type of the object + QName type = nodeService.getType(currentFrozenState); + + // grab all the properties + Map properties = nodeService.getProperties(currentFrozenState); + + // grab all the aspects + Set aspects = nodeService.getAspects(currentFrozenState); + + // create the record + NodeRef record = recordService.createRecordFromContent( + filePlan, + (String)properties.get(ContentModel.PROP_NAME), + type, + properties, + null); + + // apply aspects to record + for (QName aspect : aspects) + { + // add the aspect, properties have already been set + nodeService.addAspect(record, aspect, null); + } + + // apply version record aspect to record + PropertyMap versionRecordProps = new PropertyMap(3); + versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef); + versionRecordProps.put(RecordableVersionModel.PROP_VERSION_LABEL, currentVersion.getVersionLabel()); + versionRecordProps.put(RecordableVersionModel.PROP_VERSION_DESCRIPTION, currentVersion.getDescription()); + nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps); + + // wire record up to previous record + linkToPreviousVersionRecord(nodeRef, record); + + // set the extended security + Set combinedWriters = new HashSet(writers); + if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER)) + { + combinedWriters.add(owner); + } + combinedWriters.add(authenticationUtil.getFullyAuthenticatedUser()); + extendedSecurityService.addExtendedSecurity(record, readers, combinedWriters); + + return record; + } + }); + + // get the version history + NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); + + // get details from the version before we remove it + int versionNumber = getVersionNumber(currentVersion); + Map versionProperties = getVersionAspectProperties(currentVersion); + QName sourceTypeRef = getVersionType(currentVersion); + + // patch-up owner information, which needs to be frozen for recorded versions + String owner = (String)nodeService.getProperty(currentVersion.getFrozenStateNodeRef(), ContentModel.PROP_OWNER); + if (owner != null) + { + versionProperties.put(PROP_FROZEN_OWNER, owner); + } + + // delete the current version + this.dbNodeService.deleteNode(convertNodeRef(currentVersion.getFrozenStateNodeRef())); + + // create a new version history if we need to + if (!nodeService.exists(versionHistoryRef)) + { + versionHistoryRef = createVersionHistory(nodeRef); + } + + // create recorded version nodeRef + ChildAssociationRef childAssocRef = dbNodeService.createNode( + versionHistoryRef, + Version2Model.CHILD_QNAME_VERSIONS, + QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.CHILD_VERSIONS + "-" + versionNumber), + sourceTypeRef, + null); + NodeRef versionNodeRef = childAssocRef.getChildRef(); + + // add aspect with the standard version properties to the 'version' node + nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, versionProperties); + + // add the recordedVersion aspect with link to record + nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable)record)); + } + } + + return record; + } + + /** + * Helper method to get the version number of a given version by inspecting the + * name of the parent association. + * + * @param version version + * @return int version number + */ + private int getVersionNumber(Version version) + { + NodeRef versionNodeRef = getVersionNodeRef(version); + ChildAssociationRef assoc = dbNodeService.getPrimaryParent(versionNodeRef); + String fullVersionNumber = assoc.getQName().getLocalName(); + String versionNumber = fullVersionNumber.substring(fullVersionNumber.indexOf("-") + 1); + return Integer.parseInt(versionNumber); + } + + /** + * Helper method to get all the version aspect properties from an existing version + * + * @param version version + * @return Map property values + */ + private Map getVersionAspectProperties(Version version) + { + NodeRef versionNodeRef = getVersionNodeRef(version); + Map versionProps = dbNodeService.getProperties(versionNodeRef); + Map result = new HashMap(9); + for (String propertyName : VERSION_PROPERTY_NAMES) + { + QName propertyQName = QName.createQName(Version2Model.NAMESPACE_URI, propertyName); + result.put(propertyQName, versionProps.get(propertyQName)); + + if (propertyName.equals(Version2Model.PROP_FROZEN_NODE_DBID)) + { + System.out.println(versionProps.get(propertyQName)); + } + } + return result; + } + + /** + * Helper method to get the type of a versions frozen state + * + * @param currentVersion + * @return + */ + private QName getVersionType(Version version) + { + return nodeService.getType(getVersionNodeRef(version)); + } + + /** + * Helper method to get the internal node reference of a version + * + * @param version version + * @return NodeRef internal node reference to version + */ + private NodeRef getVersionNodeRef(Version version) + { + return convertNodeRef(version.getFrozenStateNodeRef()); + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java similarity index 96% rename from rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java rename to rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java index 4ac4106b6c..c7b1e08f4c 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersionsTest.java @@ -38,7 +38,7 @@ import org.alfresco.service.cmr.version.VersionType; * @author Roy Wetherall * @since 2.3 */ -public class AdHocRecordableVersions extends RecordableVersionsBaseTest +public class AdHocRecordableVersionsTest extends RecordableVersionsBaseTest { /** * Adhoc recorded version creation, with no policy defined as site collaborator diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java similarity index 87% rename from rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java rename to rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java index 21af34b743..a9e4d678a3 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersionsTest.java @@ -32,15 +32,15 @@ import org.alfresco.util.PropertyMap; /** * Auto Recordable Versions Integration Test - * + * * @author Roy Wetherall * @since 2.3 */ -public class AutoRecordableVersions extends RecordableVersionsBaseTest +public class AutoRecordableVersionsTest extends RecordableVersionsBaseTest { /** example content */ public final static String MY_NEW_CONTENT = "this is some new content that I have changed to trigger auto version"; - + /** * Given that all revisions will be recorded, * When I update the content of a document, @@ -51,34 +51,34 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) { public void given() throws Exception - { + { // set the recordable version policy PropertyMap recordableVersionProperties = new PropertyMap(1); recordableVersionProperties.put(PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); recordableVersionProperties.put(PROP_FILE_PLAN, filePlan); - nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); - + nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + // make the node versionable PropertyMap versionableProperties = new PropertyMap(1); versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); - nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); } - + public void when() - { + { // generate new version by updating content ContentWriter writer = contentService.getWriter(dmDocument, ContentModel.PROP_CONTENT, true); writer.putContent(MY_NEW_CONTENT); - } - + } + public void then() { // check that the record has been recorded checkRecordedVersion(dmDocument, null, "0.1"); } - }); - } - + }); + } + /** * Given that all revisions will be automatically recorded, * When I update a document 3 times, @@ -90,18 +90,18 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest { /** given **/ public void given() throws Exception - { + { doTestInTransaction(new VoidTest() { @Override public void runImpl() throws Exception - { + { // set the recordable version policy PropertyMap recordableVersionProperties = new PropertyMap(1); recordableVersionProperties.put(PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); recordableVersionProperties.put(PROP_FILE_PLAN, filePlan); - nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); - + nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + // make the node versionable PropertyMap versionableProperties = new PropertyMap(1); versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); @@ -109,16 +109,16 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest } }); } - + /** when **/ public void when() - { + { // update the content 3 times updateContent(); updateContent(); updateContent(); - } - + } + /** then */ public void then() { @@ -129,10 +129,10 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest { // check that the record has been recorded checkRecordedVersion(dmDocument, null, "0.3"); - - Version version = versionService.getCurrentVersion(dmDocument); + + Version version = versionService.getCurrentVersion(dmDocument); NodeRef record = (NodeRef)version.getVersionProperties().get(RecordableVersionServiceImpl.PROP_VERSION_RECORD); - + boolean foundPrevious = false; Set relationships = relationshipService.getRelationshipsFrom(record); assertNotNull(relationships); @@ -150,9 +150,9 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest } }); } - + /** - * Helper method to update content of dmDocument + * Helper method to update content of dmDocument */ private void updateContent() { @@ -165,7 +165,7 @@ public class AutoRecordableVersions extends RecordableVersionsBaseTest writer.putContent(MY_NEW_CONTENT); } }); - } - }); - } + } + }); + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java new file mode 100755 index 0000000000..d6e33fb515 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/DeclareAsRecordVersionTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.version; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionType; + +/** + * Declare as record version integration tests + * + * @author Roy Wetherall + * @since 2.3 + */ +public class DeclareAsRecordVersionTest extends RecordableVersionsBaseTest +{ + /** recordable version service */ + private RecordableVersionService recordableVersionService; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices() + */ + @Override + protected void initServices() + { + super.initServices(); + recordableVersionService = (RecordableVersionService)applicationContext.getBean("RecordableVersionService"); + } + + /** + * Given versionable content with a non-recorded latest version + * When I declare a version record + * Then the latest version is recorded and a record is created + */ + public void testDeclareLatestVersionAsRecord() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private NodeRef versionRecord; + private Map versionProperties; + + public void given() throws Exception + { + // setup version properties + versionProperties = new HashMap(4); + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + + // create version + versionService.createVersion(dmDocument, versionProperties); + + // assert that the latest version is not recorded + assertFalse(recordableVersionService.isCurrentVersionRecorded(dmDocument)); + } + + public void when() + { + // create version record from latest version + versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument); + } + + public void then() + { + // check the created record + assertNotNull(versionRecord); + assertTrue(recordService.isRecord(versionRecord)); + + // assert the current version is recorded + assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); + + // check the recorded version + checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); + } + }); + } + + /** + * Given versionable content with a recorded latest version + * When I delcare a version record + * Then nothing happens since the latest version is already recorded + * And a warning is logged + */ + public void testDeclareLatestVersionAsRecordButAlreadyRecorded() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private NodeRef versionRecord; + private Map versionProperties; + + public void given() throws Exception + { + // setup version properties + versionProperties = new HashMap(4); + 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); + + // create version + versionService.createVersion(dmDocument, versionProperties); + + // assert that the latest version is not recorded + assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); + } + + public void when() + { + // create version record from latest version + versionRecord = recordableVersionService.createRecordFromLatestVersion(filePlan, dmDocument); + } + + public void then() + { + // check that a record was not created + assertNull(versionRecord); + + // assert the current version is recorded + assertTrue(recordableVersionService.isCurrentVersionRecorded(dmDocument)); + + // check the recorded version + checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); + } + }); + } + + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java index 886bf6e351..767f3d2ec8 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java @@ -49,24 +49,33 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen protected static final String DESCRIPTION = "description"; protected static final String PUBLISHER = "publisher"; protected static final String SUBJECT = "subject"; - protected static final String OWNER = "Grace Wetherall"; + protected static final String OWNER = "GracieWetherall"; protected static final String CONTENT = "Simple + Smart. A smarter way to build, a smarter way to deploy. Its simple because we focus on the end " + "user and smart because we support more open standards than any other ECM platform, while delivering all " + "the value a traditional platform provides."; + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isCollaborationSiteTest() + */ @Override protected boolean isCollaborationSiteTest() { return true; } + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupCollaborationSiteTestDataImpl() + */ @Override protected void setupCollaborationSiteTestDataImpl() { super.setupCollaborationSiteTestDataImpl(); + // create authentication for owner + createPerson(OWNER); + // add titled aspect PropertyMap titledProperties = new PropertyMap(2); titledProperties.put(ContentModel.PROP_TITLE, "document title"); @@ -91,6 +100,18 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen writer.putContent(CONTENT); } + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#tearDownImpl() + */ + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + + // remove owner + personService.deletePerson(OWNER); + } + /** * Helper to check that the current version is recorded */ @@ -145,6 +166,9 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen assertNotNull(headVersion); } + /** + * Helper method to check that the current version is not recorded + */ protected void checkNotRecordedAspect(NodeRef document, String description, String versionLabel) { // double check that the document is not a record @@ -197,6 +221,16 @@ public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implemen } } + // filter out missing properties with null values + for (Map.Entry entry : frozenProperties.entrySet()) + { + if (entry.getValue() == null) + { + cloneFrozenProperties.remove(entry.getKey()); + } + } + + // frozen properties should be empty assertTrue("Properties in frozen state, but not in origional. " + cloneFrozenProperties.keySet(), cloneFrozenProperties.isEmpty()); } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java index 2f4a360ae5..c2d39cf2b6 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java @@ -31,8 +31,9 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses( { - AdHocRecordableVersions.class, - AutoRecordableVersions.class + AdHocRecordableVersionsTest.class, + AutoRecordableVersionsTest.class, + DeclareAsRecordVersionTest.class }) public class VersionTestSuite { diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/BaseActionUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/BaseActionUnitTest.java new file mode 100644 index 0000000000..3a6207aa7b --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/BaseActionUnitTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action; + +import static org.mockito.Mockito.doReturn; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.service.cmr.action.Action; +import org.mockito.Mock; + +/** + * Declare as version record action unit test. + * + * @author Roy Wetherall + * @since 2.3 + */ +public abstract class BaseActionUnitTest extends BaseUnitTest +{ + /** mocked action */ + private @Mock Action mockedAction; + + /** + * @return mocked action + */ + protected Action getMockedAction() + { + return mockedAction; + } + + /** + * Helper to mock an action parameter value + */ + protected void mockActionParameterValue(String name, Object value) + { + doReturn(value).when(mockedAction).getParameterValue(name); + } + +} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java new file mode 100644 index 0000000000..b66367cf6d --- /dev/null +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.dm; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +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 org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.BaseActionUnitTest; +import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.junit.Test; +import org.mockito.InjectMocks; + +/** + * Declare as version record action unit test. + * + * @author Roy Wetherall + * @since 2.3 + */ +public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest +{ + /** Sync Model */ + private static final String SYNC_MODEL_1_0_URI = "http://www.alfresco.org/model/sync/1.0"; + private static final QName ASPECT_SYNCED = QName.createQName(SYNC_MODEL_1_0_URI, "synced"); + + /** actioned upon node reference */ + private NodeRef actionedUponNodeRef; + + /** declare as version record action */ + private @InjectMocks DeclareAsVersionRecordAction declareAsVersionRecordAction; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest#before() + */ + @Override + public void before() throws Exception + { + super.before(); + + // mocked action + declareAsVersionRecordAction.setAuditable(false); + + // mocked actioned upon noderef + actionedUponNodeRef = generateNodeRef(); + } + + /** + * Given that the actioned upon node reference doesn't exist + * When I execute the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefDoesntExist() + { + doReturn(false).when(mockedNodeService).exists(actionedUponNodeRef); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference isn't a subtype of cm:content + * When I execute the action + * Then nothing happens + */ + @Test + public void aciontedUponNodeRefIsntSubTypeOfCmContent() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(false).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference doesn't have the versionable aspect applied + * When I executed the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefDoesntHaveVersionableApplied() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference is already an record + * When I execute the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefAlreadyRecord() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference is a working copy + * When I execute the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefWorkingCopy() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference is a rejected record + * When I execute the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefRejectedRecord() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that the actioned upon node reference is synced to the cloud + * When I execute the action + * Then nothing happens + */ + @Test + public void actionedUponNodeRefSynced() + { + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_SYNCED); + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, never()).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that no file plan is provided + * And no default file plan exists + * When I execute the action + * Then an exception is thrown + */ + @Test + public void noFilePlanParameterNoDefaultFilePlan() + { + // setup + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_SYNCED); + + // no default file plan + doReturn(null).when(mockedFilePlanService).getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); + + // expect exception + exception.expect(AlfrescoRuntimeException.class); + + // exceute action + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + } + + /** + * Given that no file plan is provided + * And adefault file plan exists + * When I execute the action + * Then a version record is declared + */ + @Test + public void noFilePlanParameterDefaultFilePlan() + { + // setup + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_SYNCED); + + // no default file plan + doReturn(filePlan).when(mockedFilePlanService).getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); + + // exceute action + declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + verify(mockedRecordableVersionService, times(1)).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + } + + /** + * Given that a file plan is provided + * And it isn't a file plan + * When I execute the action + * Then an exception is thrown + */ + @Test + public void invalidFilePlanParameter() + { + // setup + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_SYNCED); + + // not a file plan is provided in the parameters + mockActionParameterValue(DeclareAsVersionRecordAction.PARAM_FILE_PLAN, generateNodeRef()); + + // expect exception + exception.expect(AlfrescoRuntimeException.class); + + // exceute action + declareAsVersionRecordAction.executeImpl(getMockedAction(), actionedUponNodeRef); + } + + /** + * Given that a file plan is provided + * And it is a file plan + * When I execute the action + * Then a version record is declared + */ + @Test + public void validFilePlanParameter() + { + // setup + doReturn(true).when(mockedNodeService).exists(actionedUponNodeRef); + doReturn(true).when(mockedDictionaryService).isSubClass(any(QName.class), eq(ContentModel.TYPE_CONTENT)); + doReturn(true).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_RECORD_REJECTION_DETAILS); + doReturn(false).when(mockedNodeService).hasAspect(actionedUponNodeRef, ASPECT_SYNCED); + + // not a file plan is provided in the parameters + NodeRef myFilePlan = generateNodeRef(TYPE_FILE_PLAN); + doReturn(true).when(mockedFilePlanService).isFilePlan(myFilePlan); + mockActionParameterValue(DeclareAsVersionRecordAction.PARAM_FILE_PLAN, myFilePlan); + + // exceute action + declareAsVersionRecordAction.executeImpl(getMockedAction(), actionedUponNodeRef); + verify(mockedRecordableVersionService, times(1)).createRecordFromLatestVersion(myFilePlan, actionedUponNodeRef); + } +} diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileReportActionUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileReportActionUnitTest.java index b24e20089a..a3cbd6bc97 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileReportActionUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/FileReportActionUnitTest.java @@ -18,17 +18,14 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.impl; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verifyZeroInteractions; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.module.org_alfresco_module_rm.action.BaseActionUnitTest; import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.junit.Test; import org.mockito.InjectMocks; -import org.mockito.Mock; /** * Unit test for file report action. @@ -36,14 +33,11 @@ import org.mockito.Mock; * @author Roy Wetherall * @since 2.2 */ -public class FileReportActionUnitTest extends BaseUnitTest +public class FileReportActionUnitTest extends BaseActionUnitTest { /** actioned upon node reference */ private NodeRef actionedUponNodeRef; - /** mocked action */ - private @Mock Action mockedAction; - /** file report action */ private @InjectMocks FileReportAction fileReportAction; @@ -62,14 +56,6 @@ public class FileReportActionUnitTest extends BaseUnitTest fileReportAction.setAuditable(false); } - /** - * Helper to mock an action parameter value - */ - private void mockActionParameterValue(String name, String value) - { - doReturn(value).when(mockedAction).getParameterValue(name); - } - /** * given the destination is not set, ensure that an exception is thrown */ @@ -88,7 +74,7 @@ public class FileReportActionUnitTest extends BaseUnitTest // == when == // execute action - fileReportAction.executeImpl(mockedAction, actionedUponNodeRef); + fileReportAction.executeImpl(getMockedAction(), actionedUponNodeRef); // == then == verifyZeroInteractions(mockedReportService, mockedNodeService); @@ -112,7 +98,7 @@ public class FileReportActionUnitTest extends BaseUnitTest // == when == // execute action - fileReportAction.executeImpl(mockedAction, actionedUponNodeRef); + fileReportAction.executeImpl(getMockedAction(), actionedUponNodeRef); // == then == verifyZeroInteractions(mockedReportService, mockedNodeService); diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnlinkFromActionUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnlinkFromActionUnitTest.java index 60aa6f56f0..312bcda9f3 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnlinkFromActionUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UnlinkFromActionUnitTest.java @@ -18,10 +18,15 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.impl; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +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 org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.module.org_alfresco_module_rm.action.BaseActionUnitTest; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; import org.junit.Before; @@ -34,7 +39,7 @@ import org.mockito.InjectMocks; * @author Roy Wetherall * @since 2.3 */ -public class UnlinkFromActionUnitTest extends BaseUnitTest +public class UnlinkFromActionUnitTest extends BaseActionUnitTest { private NodeRef record; private NodeRef recordFolder; @@ -107,11 +112,10 @@ public class UnlinkFromActionUnitTest extends BaseUnitTest doReturn(false).when(mockedNodeService).hasAspect(record, ASPECT_PENDING_DELETE); // create action mock - Action mockedAction = mock(Action.class); - doReturn(null).when(mockedAction).getParameterValue(UnlinkFromAction.PARAM_RECORD_FOLDER); + mockActionParameterValue(UnlinkFromAction.PARAM_RECORD_FOLDER, null); // execute action - action.executeImpl(mockedAction, record); + action.executeImpl(getMockedAction(), record); } /** @@ -127,11 +131,10 @@ public class UnlinkFromActionUnitTest extends BaseUnitTest doReturn(false).when(mockedNodeService).hasAspect(record, ASPECT_PENDING_DELETE); // create action mock - Action mockedAction = mock(Action.class); - doReturn(recordFolder.toString()).when(mockedAction).getParameterValue(UnlinkFromAction.PARAM_RECORD_FOLDER); + mockActionParameterValue(UnlinkFromAction.PARAM_RECORD_FOLDER, recordFolder.toString()); // execute action - action.executeImpl(mockedAction, record); + action.executeImpl(getMockedAction(), record); // verify unlink verify(mockedRecordService, times(1)).unlink(record, recordFolder); diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java index 6e23e8abc8..0882c65579 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/AllUnitTestSuite.java @@ -18,6 +18,7 @@ */ package org.alfresco.module.org_alfresco_module_rm.test; +import org.alfresco.module.org_alfresco_module_rm.action.dm.DeclareAsVersionRecordActionUnitTest; import org.alfresco.module.org_alfresco_module_rm.action.impl.FileReportActionUnitTest; import org.alfresco.module.org_alfresco_module_rm.action.impl.UnlinkFromActionUnitTest; import org.alfresco.module.org_alfresco_module_rm.bootstrap.BootstrapImporterModuleComponentUnitTest; @@ -79,6 +80,7 @@ import org.junit.runners.Suite.SuiteClasses; // action implementations FileReportActionUnitTest.class, UnlinkFromActionUnitTest.class, + DeclareAsVersionRecordActionUnitTest.class, // patches RMv22RemoveInPlaceRolesFromAllPatchUnitTest.class, diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java index f684370515..3cce7a93a1 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java @@ -47,6 +47,7 @@ import org.alfresco.module.org_alfresco_module_rm.report.ReportService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -125,7 +126,8 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel @Mock(name="extendedPermissionService") protected ExtendedPermissionService mockedExtendedPermissionService; @Mock(name="extendedSecurityService") protected ExtendedSecurityService mockedExtendedSecurityService; @Mock(name="recordableVersionConfigService") protected RecordableVersionConfigService mockedRecordableVersionConfigService; - @Mock(name="cmObjectType") protected CmObjectType cmObjectType; + @Mock(name="cmObjectType") protected CmObjectType mockedCmObjectType; + @Mock(name="recordableVersionService") protected RecordableVersionService mockedRecordableVersionService; /** application context mock */ @Mock(name="applicationContext") protected ApplicationContext mockedApplicationContext; @@ -386,7 +388,17 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel */ protected void makePrimaryParentOf(NodeRef child, NodeRef parent) { - doReturn(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, parent, generateQName(), child)) + makePrimaryParentOf(child, parent, ContentModel.ASSOC_CONTAINS, generateQName()); + } + + protected void makePrimaryParentOf(NodeRef child, NodeRef parent, QName assocType, QName assocName) + { + makePrimaryParentOf(child, parent, assocType, assocName, mockedNodeService); + } + + protected void makePrimaryParentOf(NodeRef child, NodeRef parent, QName assocType, QName assocName, NodeService mockedNodeService) + { + doReturn(new ChildAssociationRef(assocType, parent, assocName, child)) .when(mockedNodeService) .getPrimaryParent(child); } diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java index 2020334a0c..21d590dd27 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImplUnitTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.Serializable; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -38,14 +39,19 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.VersionModel; +import org.alfresco.repo.version.common.VersionImpl; import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionType; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; /** * Recordable version service implementation unit test. @@ -71,7 +77,7 @@ public class RecordableVersionServiceImplUnitTest extends BaseUnitTest private @Mock(name="dbNodeService") NodeService mockedDbNodeService; /** recordable version service */ - private @InjectMocks TestRecordableVersionServiceImpl recordableVersionService; + private @InjectMocks @Spy TestRecordableVersionServiceImpl recordableVersionService; /** * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest#before() @@ -384,4 +390,109 @@ public class RecordableVersionServiceImplUnitTest extends BaseUnitTest // then the recorded version is created verify(mockedRecordService, times(1)).createRecordFromCopy(filePlan, nodeRef); } + + /** + * Given that a node is not versionable + * When I try and create a record from the latest version + * Then nothing will happen, because there is not version to record + */ + @Test + public void notVersionableCreateRecordFromVersion() + { + // content node is not versionable + doReturn(false).when(mockedNodeService).hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE); + + // create record from version + recordableVersionService.createRecordFromLatestVersion(filePlan, nodeRef); + + // nothing happens + verify(mockedRecordService, never()).createRecordFromCopy(eq(filePlan), any(NodeRef.class)); + } + + /** + * Given that a nodes last version is recorded + * When I try and create a record from the latest version + * Then nothing will happen, because the latest version is already recorded + */ + @Test + public void alreadyRecordedCreateRecordFromVersion() + { + // latest version is already recorded + Version mockedVersion = mock(VersionImpl.class); + doReturn(Collections.singletonMap(RecordableVersionServiceImpl.PROP_VERSION_RECORD, generateNodeRef())).when(mockedVersion).getVersionProperties(); + doReturn(true).when(mockedNodeService).hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE); + doReturn(mockedVersion).when(recordableVersionService).getCurrentVersion(nodeRef); + + // create record from version + recordableVersionService.createRecordFromLatestVersion(filePlan, nodeRef); + + // nothing happens + verify(mockedRecordService, never()).createRecordFromCopy(eq(filePlan), any(NodeRef.class)); + } + + /** + * Given that a nodes last version is not recorded + * When I try to create a record from the latest version + * Then the latest version is marked as record and a new record version is created to store the version state + */ + @SuppressWarnings("unchecked") + @Test + public void notRecordedCreateRecordFromVersion() + { + // latest version is not recorded + Version mockedVersion = mock(VersionImpl.class); + NodeRef versionNodeRef = generateNodeRef(); + doReturn(Collections.emptyMap()).when(mockedVersion).getVersionProperties(); + doReturn(true).when(mockedNodeService).hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE); + + // version history + NodeRef versionHistoryNodeRef = generateNodeRef(); + doReturn(versionHistoryNodeRef).when(mockedDbNodeService).getChildByName(any(NodeRef.class), eq(Version2Model.CHILD_QNAME_VERSION_HISTORIES), any(String.class)); + + // version number + doReturn(mockedVersion).when(recordableVersionService).getCurrentVersion(nodeRef); + doReturn(versionNodeRef).when(recordableVersionService).convertNodeRef(any(NodeRef.class)); + makePrimaryParentOf(versionNodeRef, versionHistoryNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "something-0"), mockedDbNodeService); + + // created version + NodeRef newVersionNodeRef = generateNodeRef(); + doReturn(generateChildAssociationRef(versionHistoryNodeRef, newVersionNodeRef)).when(mockedDbNodeService).createNode( + eq(versionHistoryNodeRef), + eq(Version2Model.CHILD_QNAME_VERSIONS), + any(QName.class), + any(QName.class), + any(Map.class)); + + // created record + NodeRef newRecordNodeRef = generateNodeRef(); + doReturn(newRecordNodeRef).when(mockedRecordService).createRecordFromContent( + eq(filePlan), + any(String.class), + any(QName.class), + any(Map.class), + any(ContentReader.class)); + + // create record from version + recordableVersionService.createRecordFromLatestVersion(filePlan, nodeRef); + + // verify that the version is converted to a recorded version + verify(mockedRecordService, times(1)).createRecordFromContent( + eq(filePlan), + any(String.class), + any(QName.class), + any(Map.class), + any(ContentReader.class)); + verify(mockedDbNodeService, times(1)).deleteNode(any(NodeRef.class)); + verify(mockedDbNodeService, times(1)).createNode( + eq(versionHistoryNodeRef), + eq(Version2Model.CHILD_QNAME_VERSIONS), + any(QName.class), + any(QName.class), + any(Map.class)); + verify(mockedNodeService, times(1)).addAspect(eq(newVersionNodeRef), eq(Version2Model.ASPECT_VERSION), any(Map.class)); + verify(mockedNodeService, times(1)).addAspect( + newVersionNodeRef, + RecordableVersionModel.ASPECT_RECORDED_VERSION, + Collections.singletonMap(RecordableVersionModel.PROP_RECORD_NODE_REF, (Serializable)newRecordNodeRef)); + } } \ No newline at end of file