diff --git a/.gitignore b/.gitignore index 4b9784e791..9c15326934 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ test-output # /rm-enterprise/rm-automation-enterprise/ /rm-enterprise/rm-automation-enterprise/l10n -/rm-enterprise/rm-automation-enterprise/root +/rm-enterprise/rm-automation-enterprise/root \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties index 221a3f5fc1..505f28255b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties @@ -17,6 +17,9 @@ imap.server.attachments.extraction.enabled=false # audit.enabled=true audit.rm.enabled=true +#audit.rm.runas=admin + +#audit.filter.alfresco-access.transaction.user=~null;.* # # Extended permission service cache sizing diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml index 408cbbf617..d5509295c4 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml @@ -60,6 +60,11 @@ class="org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition.TransferredCapabilityCondition"> + + + @@ -218,7 +223,7 @@ - + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml index dbb0c48acb..dc42dd25f2 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml @@ -129,8 +129,7 @@ - - + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties index 7b444b2cc9..96f6f06b8a 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties @@ -37,4 +37,6 @@ rm.action.event-not-undone=The event {0} can't be undone, because it's not defin rm.action.node-not-record-category=The disposition schedule could not be created, because the actioned upon node ({0}) was not a record category. rm.action.parameter-not-supplied=The parameter ''{0}'' has not been supplied. rm.action.delete-not-hold-type=The hold couldn't be deleted, because the node isn't of type {0}. (actionedUponNodeRef={1}) -rm.action.cast-to-rm-type=Can't upload a custom folder type to the records management file plan. \ No newline at end of file +rm.action.cast-to-rm-type=Can't upload a custom folder type to the records management file plan. +rm.action.record-folder-create=Operation failed, because you can't place a record folder into another record folder. + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 50e505c39f..a5b3d722b5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -103,7 +103,7 @@ - + @@ -195,7 +195,6 @@ - diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml index c975a79827..44bd92fe34 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml @@ -51,23 +51,25 @@ - - - - org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob - - - - - - - - - - - - - + + + + + org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob + + + + + + + + + + + + + + + @@ -94,7 +97,7 @@ - + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 7ddb3f28b2..d1d575c4d2 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -775,6 +775,7 @@ + rma:recordCategory @@ -817,7 +818,6 @@ getCustomConstraintDefinitions(QName modelQName); + List getCustomConstraintDefinitions(QName modelQName); /** * This method adds a Constraint definition to the custom model. @@ -374,22 +369,22 @@ public interface RecordsManagementAdminService * @param title the human-readable title e.g. My foo list * @param caseSensitive * @param allowedValues the allowed values list - * @param matchLogic AND (all values must match), OR (at least one values must match) + * @param matchLogic AND (all values must match), OR (at least one values must match) */ - void addCustomConstraintDefinition(QName constraintName, String title, boolean caseSensitive, List allowedValues, MatchLogic matchLogic); + void addCustomConstraintDefinition(QName constraintName, String title, boolean caseSensitive, List allowedValues, MatchLogic matchLogic); /** - * Remove custom constraint definition - if not referenced (by any properties) + * Remove custom constraint definition - if not referenced (by any properties) * * - * @param constraintName the name e.g. rmc:foo + * @param constraintName the name e.g. rmc:foo */ void removeCustomConstraintDefinition(QName constraintName); /** - * Update custom constraint definition with new list of values (replaces existing list, if any) + * Update custom constraint definition with new list of values (replaces existing list, if any) * - * @param constraintName the name e.g. rmc:foo + * @param constraintName the name e.g. rmc:foo * @param newValues */ void changeCustomConstraintValues(QName constraintName, List newValues); @@ -409,7 +404,7 @@ public interface RecordsManagementAdminService * @return the QName of the property, association definition which matches, or null. */ QName getQNameForClientId(String localName); - + /** * Given a compound id for source and target strings (as used with parent/child * custom references), this method splits the String and returns an array containing @@ -419,7 +414,7 @@ public interface RecordsManagementAdminService * @return a String array, where result[0] == sourceId and result[1] == targetId. */ String[] splitSourceTargetId(String sourceTargetId); - + /** * This method retrieves a compound ID (client-side) for the specified * sourceId and targetId. diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java index 8fa2f8b10d..c17b74b822 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java @@ -24,7 +24,6 @@ * along with Alfresco. If not, see . * #L% */ - package org.alfresco.module.org_alfresco_module_rm.admin; import static org.springframework.extensions.surf.util.ParameterCheck.mandatory; @@ -61,6 +60,7 @@ import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.Constraint; @@ -73,7 +73,11 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.GUID; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.URLDecoder; @@ -83,10 +87,14 @@ import org.springframework.extensions.surf.util.URLDecoder; * @author Neil McErlean, janv */ @BehaviourBean -public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBase implements RecordsManagementAdminService, - NodeServicePolicies.OnAddAspectPolicy, - NodeServicePolicies.OnRemoveAspectPolicy, - NodeServicePolicies.OnCreateNodePolicy +public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBase + implements RecordsManagementAdminService, + RecordsManagementCustomModel, + NodeServicePolicies.OnAddAspectPolicy, + NodeServicePolicies.OnRemoveAspectPolicy, + NodeServicePolicies.OnCreateNodePolicy, + ApplicationListener, + Ordered { /** I18N messages*/ private static final String MSG_SERVICE_NOT_INIT = "rm.admin.service-not-init"; @@ -107,14 +115,26 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas /** Relationship service */ private RelationshipService relationshipService; + + /** Transaction service */ + private TransactionService transactionService; /** List of types that can be customisable */ private List pendingCustomisableTypes; private Map customisableTypes; + + /** indicates whether the custom map has been initialised or not */ + private boolean isCustomMapInit = false; + + /** + * @param transactionService transaction service + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } /** - * Sets the relationship instance - * * @param relationshipService The relationship service instance */ public void setRelationshipService(RelationshipService relationshipService) @@ -132,6 +152,48 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas return this.relationshipService; } + /** + * Indicate that this application content listener must be executed with the lowest + * precedence. (ie last) + * + * @see Ordered#getOrder() + */ + @Override + public int getOrder() + { + return Ordered.LOWEST_PRECEDENCE; + } + + /** + * Load the custom properties map + * + * @see ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + @Override + public void onApplicationEvent(ContextRefreshedEvent event) + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // initialise custom properties + initCustomMap(); + + return null; + } + }); + } + + /** + * Helper method to indicate whether the custom map is initialised or not. + * + * @return boolean true if initialised, false otherwise + */ + public boolean isCustomMapInit() + { + return isCustomMapInit; + } + /** * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @@ -144,25 +206,25 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas ) public void onAddAspect(final NodeRef nodeRef, final QName aspectTypeQName) { - mandatory("nodeRef", nodeRef); - mandatory("aspectTypeQName", aspectTypeQName); - - AuthenticationUtil.runAs(new RunAsWork() + if (isCustomMapInit) { - @Override - public Void doWork() + AuthenticationUtil.runAs(new RunAsWork() { - if (getNodeService().exists(nodeRef) && - getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL) && - isCustomisable(aspectTypeQName)) + @Override + public Void doWork() { - QName customPropertyAspect = getCustomAspect(aspectTypeQName); - getNodeService().addAspect(nodeRef, customPropertyAspect, null); + if (getNodeService().exists(nodeRef) && + getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL) && + isCustomisable(aspectTypeQName)) + { + QName customPropertyAspect = getCustomAspect(aspectTypeQName); + getNodeService().addAspect(nodeRef, customPropertyAspect, null); + } + + return null; } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); + }, AuthenticationUtil.getSystemUserName()); + } } /** @@ -177,24 +239,24 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas ) public void onRemoveAspect(final NodeRef nodeRef, final QName aspectTypeQName) { - mandatory("nodeRef", nodeRef); - mandatory("aspectTypeQName", aspectTypeQName); - - AuthenticationUtil.runAs(new RunAsWork() + if (isCustomMapInit) { - @Override - public Void doWork() + AuthenticationUtil.runAs(new RunAsWork() { - if (getNodeService().exists(nodeRef) && - isCustomisable(aspectTypeQName)) + @Override + public Void doWork() { - QName customPropertyAspect = getCustomAspect(aspectTypeQName); - getNodeService().removeAspect(nodeRef, customPropertyAspect); + if (getNodeService().exists(nodeRef) && + isCustomisable(aspectTypeQName)) + { + QName customPropertyAspect = getCustomAspect(aspectTypeQName); + getNodeService().removeAspect(nodeRef, customPropertyAspect); + } + + return null; } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); + }, AuthenticationUtil.getSystemUserName()); + } } /** @@ -211,49 +273,41 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas ) public void onCreateNode(final ChildAssociationRef childAssocRef) { - mandatory("nodeRef", childAssocRef); - - AuthenticationUtil.runAs(new RunAsWork() - { - @Override - public Void doWork() + if (isCustomMapInit) + { + AuthenticationUtil.runAs(new RunAsWork() { - if (getDictionaryService().getAllModels().contains(RecordsManagementCustomModel.RM_CUSTOM_MODEL)) + @Override + public Void doWork() { - NodeRef nodeRef = childAssocRef.getChildRef(); - QName type = getNodeService().getType(nodeRef); - while (type != null && !ContentModel.TYPE_CMOBJECT.equals(type)) + if (getDictionaryService().getAllModels().contains(RecordsManagementCustomModel.RM_CUSTOM_MODEL)) { - if (isCustomisable(type)) + NodeRef nodeRef = childAssocRef.getChildRef(); + QName type = getNodeService().getType(nodeRef); + while (type != null && !ContentModel.TYPE_CMOBJECT.equals(type)) { - QName customPropertyAspect = getCustomAspect(type); - getNodeService().addAspect(nodeRef, customPropertyAspect, null); - } - - TypeDefinition def = getDictionaryService().getType(type); - if (def != null) - { - type = def.getParentName(); - } - else - { - type = null; + if (isCustomisable(type)) + { + QName customPropertyAspect = getCustomAspect(type); + getNodeService().addAspect(nodeRef, customPropertyAspect, null); + } + + TypeDefinition def = getDictionaryService().getType(type); + if (def != null) + { + type = def.getParentName(); + } + else + { + type = null; + } } } + + return null; } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService#initialiseCustomModel() - */ - public void initialiseCustomModel() - { - // Initialise the map - getCustomisableMap(); + }, AuthenticationUtil.getSystemUserName()); + } } /** @@ -338,6 +392,92 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas return result; } + + /** + * Initialise custom type map + */ + private void initCustomMap() + { + customisableTypes = new HashMap(7); + Collection aspects = getDictionaryService().getAspects(RM_CUSTOM_MODEL); + for (QName aspect : aspects) + { + AspectDefinition aspectDef = getDictionaryService().getAspect(aspect); + String name = aspectDef.getName().getLocalName(); + if (name.endsWith("Properties")) + { + QName type = null; + String prefixString = aspectDef.getDescription(getDictionaryService()); + if (prefixString == null) + { + // Backward compatibility from previous RM V1.0 custom models + if (CompatibilityModel.NAME_CUSTOM_RECORD_PROPERTIES.equals(name)) + { + type = RecordsManagementModel.ASPECT_RECORD; + } + else if (CompatibilityModel.NAME_CUSTOM_RECORD_FOLDER_PROPERTIES.equals(name)) + { + type = RecordsManagementModel.TYPE_RECORD_FOLDER; + } + else if (CompatibilityModel.NAME_CUSTOM_RECORD_CATEGORY_PROPERTIES.equals(name)) + { + type = RecordsManagementModel.TYPE_RECORD_CATEGORY; + } + else if (CompatibilityModel.NAME_CUSTOM_RECORD_SERIES_PROPERTIES.equals(name) && + // Only add the deprecated record series type as customisable if + // a v1.0 installation has added custom properties + aspectDef.getProperties().size() != 0) + { + type = CompatibilityModel.TYPE_RECORD_SERIES; + } + } + else + { + type = QName.createQName(prefixString, getNamespaceService()); + } + + // Add the customisable type to the map + if (type != null) + { + customisableTypes.put(type, aspect); + + // Remove customisable type from the pending list + if (pendingCustomisableTypes != null && pendingCustomisableTypes.contains(type)) + { + pendingCustomisableTypes.remove(type); + } + } + } + } + + // Deal with any pending types left over + if (pendingCustomisableTypes != null && pendingCustomisableTypes.size() != 0) + { + NodeRef modelRef = getCustomModelRef(RecordsManagementModel.RM_CUSTOM_URI); + M2Model model = readCustomContentModel(modelRef); + try + { + for (QName customisableType : pendingCustomisableTypes) + { + QName customAspect = getCustomAspectImpl(customisableType); + + // Create the new aspect to hold the custom properties + M2Aspect aspect = model.createAspect(customAspect.toPrefixString(getNamespaceService())); + aspect.setDescription(customisableType.toPrefixString(getNamespaceService())); + + // Make a record of the customisable type + customisableTypes.put(customisableType, customAspect); + } + } + finally + { + writeCustomContentModel(modelRef, model); + } + } + + // indicate map is initialised + isCustomMapInit = true; + } /** * Gets a map containing all the customisable types @@ -346,86 +486,11 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas */ private Map getCustomisableMap() { - if (customisableTypes == null) - { - customisableTypes = new HashMap(7); - Collection aspects = getDictionaryService().getAspects(RM_CUSTOM_MODEL); - for (QName aspect : aspects) - { - AspectDefinition aspectDef = getDictionaryService().getAspect(aspect); - String name = aspectDef.getName().getLocalName(); - if (name.endsWith("Properties")) - { - QName type = null; - String prefixString = aspectDef.getDescription(getDictionaryService()); - if (prefixString == null) - { - // Backward compatibility from previous RM V1.0 custom models - if (CompatibilityModel.NAME_CUSTOM_RECORD_PROPERTIES.equals(name)) - { - type = RecordsManagementModel.ASPECT_RECORD; - } - else if (CompatibilityModel.NAME_CUSTOM_RECORD_FOLDER_PROPERTIES.equals(name)) - { - type = RecordsManagementModel.TYPE_RECORD_FOLDER; - } - else if (CompatibilityModel.NAME_CUSTOM_RECORD_CATEGORY_PROPERTIES.equals(name)) - { - type = RecordsManagementModel.TYPE_RECORD_CATEGORY; - } - else if (CompatibilityModel.NAME_CUSTOM_RECORD_SERIES_PROPERTIES.equals(name) && - // Only add the deprecated record series type as customisable if - // a v1.0 installation has added custom properties - aspectDef.getProperties().size() != 0) - { - type = CompatibilityModel.TYPE_RECORD_SERIES; - } - } - else - { - type = QName.createQName(prefixString, getNamespaceService()); - } - - // Add the customisable type to the map - if (type != null) - { - customisableTypes.put(type, aspect); - - // Remove customisable type from the pending list - if (pendingCustomisableTypes != null && pendingCustomisableTypes.contains(type)) - { - pendingCustomisableTypes.remove(type); - } - } - } - } - - // Deal with any pending types left over - if (pendingCustomisableTypes != null && pendingCustomisableTypes.size() != 0) - { - NodeRef modelRef = getCustomModelRef(RecordsManagementModel.RM_CUSTOM_URI); - M2Model model = readCustomContentModel(modelRef); - try - { - for (QName customisableType : pendingCustomisableTypes) - { - QName customAspect = getCustomAspectImpl(customisableType); - - // Create the new aspect to hold the custom properties - M2Aspect aspect = model.createAspect(customAspect.toPrefixString(getNamespaceService())); - aspect.setDescription(customisableType.toPrefixString(getNamespaceService())); - - // Make a record of the customisable type - customisableTypes.put(customisableType, customAspect); - } - } - finally - { - writeCustomContentModel(modelRef, model); - } - } - } - return customisableTypes; + if (customisableTypes == null) + { + throw AlfrescoRuntimeException.create("Customisable map has not been initialised correctly."); + } + return customisableTypes; } /** diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/bootstrap/RecordsManagementBootstrap.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/bootstrap/RecordsManagementBootstrap.java index f78b5a15bd..2c59e2f155 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/bootstrap/RecordsManagementBootstrap.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/bootstrap/RecordsManagementBootstrap.java @@ -28,7 +28,6 @@ package org.alfresco.module.org_alfresco_module_rm.bootstrap; import org.alfresco.module.org_alfresco_module_rm.action.impl.SplitEmailAction; -import org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminService; import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; import org.alfresco.module.org_alfresco_module_rm.email.CustomEmailMappingService; import org.alfresco.repo.action.parameter.NodeParameterSuggesterBootstrap; @@ -50,7 +49,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean private TransactionService transactionService; private RMCaveatConfigService caveatConfigService; private CustomEmailMappingService customEmailMappingService; - private RecordsManagementAdminService adminService; private NodeParameterSuggesterBootstrap suggesterBootstrap; public NodeParameterSuggesterBootstrap getSuggesterBootstrap() @@ -78,11 +76,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean this.customEmailMappingService = customEmailMappingService; } - public void setRecordsManagementAdminService(RecordsManagementAdminService adminService) - { - this.adminService = adminService; - } - public CustomEmailMappingService getCustomEmailMappingService() { return customEmailMappingService; @@ -102,9 +95,6 @@ public class RecordsManagementBootstrap extends AbstractLifecycleBean { // initialise caveat config caveatConfigService.init(); - - // Initialise the custom model - adminService.initialiseCustomModel(); // Initialize the suggester after the model // in case it contains namespaces from custom models diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MovableCapabilityCondition.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MovableCapabilityCondition.java new file mode 100644 index 0000000000..99aebc7f29 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MovableCapabilityCondition.java @@ -0,0 +1,52 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.capability.declarative.condition; + +import org.alfresco.module.org_alfresco_module_rm.capability.declarative.AbstractCapabilityCondition; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Movable capability condition. + * + * @author Roxana Dina + * @since 2.4.1 + * + */ +public class MovableCapabilityCondition extends AbstractCapabilityCondition +{ + + /** + * A record folder should not be moved when it is cut off, but it should be possible to move it when it is destroyed. + */ + @Override + public boolean evaluateImpl(NodeRef nodeRef) + { + if (nodeService.hasAspect(nodeRef, ASPECT_GHOSTED) && dispositionService.isDisposableItemCutoff(nodeRef)) + return true; + return !dispositionService.isDisposableItemCutoff(nodeRef); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 95f0aae459..04455015dd 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -889,6 +889,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Get the current action String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); + // When the record has multiple disposition schedules the current disposition action may not be found by id + // In this case it will be searched by name + if(currentDispositionActionDefinition == null) + { + String currentADName = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION); + currentDispositionActionDefinition = di.getDispositionActionDefinitionByName(currentADName); + } // Get the next disposition action int index = currentDispositionActionDefinition.getIndex(); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java index 2043244909..aadbbf6f19 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java @@ -43,16 +43,13 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthenticationService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * The Disposition Lifecycle Job Finds all disposition action nodes which are - * for disposition actions specified Where asOf > now OR - * dispositionEventsEligible = true; - * - * Runs the cut off or retain action for - * eligible records. + * The Disposition Lifecycle Job Finds all disposition action nodes which are for disposition actions specified Where + * asOf > now OR dispositionEventsEligible = true; Runs the cut off or retain action for eligible records. * * @author mrogers * @author Roy Wetherall @@ -77,10 +74,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute /** search service */ private SearchService searchService; + /** authenticationService service */ + private AuthenticationService authenticationService; + /** * List of disposition actions to automatically execute when eligible. * - * @param dispositionActions disposition actions + * @param dispositionActions disposition actions */ public void setDispositionActions(List dispositionActions) { @@ -88,7 +88,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute } /** - * @param recordsManagementActionService records management action service + * @param recordsManagementActionService records management action service */ public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService) { @@ -96,7 +96,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute } /** - * @param nodeService node service + * @param nodeService node service */ public void setNodeService(NodeService nodeService) { @@ -114,7 +114,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute /** * Get the search query string. * - * @return job query string + * @return job query string */ protected String getQuery() { @@ -165,7 +165,8 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute if (dispositionActions != null && !dispositionActions.isEmpty()) { // execute search - ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_FTS_ALFRESCO, getQuery()); + ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + SearchService.LANGUAGE_FTS_ALFRESCO, getQuery()); List resultNodes = results.getNodeRefs(); results.close(); @@ -183,7 +184,8 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute { public Boolean execute() { - final String dispAction = (String) nodeService.getProperty(currentNode, RecordsManagementModel.PROP_DISPOSITION_ACTION); + final String dispAction = (String) nodeService.getProperty(currentNode, + RecordsManagementModel.PROP_DISPOSITION_ACTION); // Run disposition action if (dispAction != null && dispositionActions.contains(dispAction)) @@ -192,12 +194,14 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION)) { Map props = new HashMap(1); - props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK, Boolean.FALSE); + props.put(RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK, + Boolean.FALSE); try { // execute disposition action - recordsManagementActionService.executeRecordsManagementAction(parent.getParentRef(), dispAction, props); + recordsManagementActionService.executeRecordsManagementAction( + parent.getParentRef(), dispAction, props); if (logger.isDebugEnabled()) { @@ -236,4 +240,14 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute } } } + + public AuthenticationService getAuthenticationService() + { + return authenticationService; + } + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJob.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJob.java index 409a0f0d32..a1dc0c3a65 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJob.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJob.java @@ -46,8 +46,7 @@ import org.quartz.JobExecutionException; /** * Base records management job implementation. *

- * Delegates job execution and ensures locking - * is enforced. + * Delegates job execution and ensures locking is enforced. * * @author Roy Wetherall */ @@ -55,11 +54,14 @@ public class RecordsManagementJob implements Job { private static Log logger = LogFactory.getLog(RecordsManagementJob.class); + /** which user should be used to log audit */ + private String runAuditAs = AuthenticationUtil.getSystemUserName(); + private static final long DEFAULT_TIME = 30000L; private JobLockService jobLockService; - private RecordsManagementJobExecuter jobExecuter; + private RecordsManagementJobExecuter jobExecuter = null; private String jobName; @@ -67,17 +69,17 @@ public class RecordsManagementJob implements Job { return QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, jobName); } - + private class LockCallback implements JobLockRefreshCallback { final AtomicBoolean running = new AtomicBoolean(true); - + @Override public boolean isActive() { return running.get(); } - + @Override public void lockReleased() { @@ -85,11 +87,10 @@ public class RecordsManagementJob implements Job } } - /** - * Attempts to get the lock. If the lock couldn't be taken, then null is returned. + * Attempts to get the lock. If the lock couldn't be taken, then null is returned. * - * @return Returns the lock token or null + * @return Returns the lock token or null */ private String getLock() { @@ -103,31 +104,54 @@ public class RecordsManagementJob implements Job } } - @Override + /** + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ public void execute(JobExecutionContext context) throws JobExecutionException { + // get the job lock service - jobLockService = (JobLockService)context.getJobDetail().getJobDataMap().get("jobLockService"); - if (jobLockService == null) - { - throw new AlfrescoRuntimeException("Job lock service has not been specified."); - } + jobLockService = (JobLockService) context.getJobDetail().getJobDataMap().get("jobLockService"); + if (jobLockService == null) { throw new AlfrescoRuntimeException("Job lock service has not been specified."); } // get the job executer - jobExecuter = (RecordsManagementJobExecuter)context.getJobDetail().getJobDataMap().get("jobExecuter"); - if (jobExecuter == null) - { - throw new AlfrescoRuntimeException("Job executer has not been specified."); - } + jobExecuter = (RecordsManagementJobExecuter) context.getJobDetail().getJobDataMap().get("jobExecuter"); + if (jobExecuter == null) { throw new AlfrescoRuntimeException("Job executer has not been specified."); } // get the job name - jobName = (String)context.getJobDetail().getJobDataMap().get("jobName"); - if (jobName == null) + jobName = (String) context.getJobDetail().getJobDataMap().get("jobName"); + + if (jobName == null) { throw new AlfrescoRuntimeException("Job name has not been specified."); } + + if (jobName.compareTo("dispositionLifecycle") == 0) { - throw new AlfrescoRuntimeException("Job name has not been specified."); + //RM-3293 - set user for audit + if (jobExecuter instanceof DispositionLifecycleJobExecuter) + { + String auditUser = (String) context.getJobDetail().getJobDataMap().get("runAuditAs"); + if (((DispositionLifecycleJobExecuter) jobExecuter).getAuthenticationService() + .authenticationExists(auditUser)) + { + + setRunAuditAs(auditUser); + } + else + { + setRunAuditAs(AuthenticationUtil.getSystemUserName()); + } + + } + + if (logger.isDebugEnabled()) + { + logger.debug("DispositionLifecycleJobExecuter() logged audit history with user: " + getRunAuditAs()); + + } + } final LockCallback lockCallback = new LockCallback(); + AuthenticationUtil.runAs(new RunAsWork() { public Void doWork() @@ -154,7 +178,8 @@ public class RecordsManagementJob implements Job // Ignore if (logger.isDebugEnabled()) { - logger.debug("Lock release failed: " + getLockQName() + ": " + lockToken + "(" + e.getMessage() + ")"); + logger.debug("Lock release failed: " + getLockQName() + ": " + lockToken + "(" + + e.getMessage() + ")"); } } } @@ -163,6 +188,18 @@ public class RecordsManagementJob implements Job // return return null; } - }, AuthenticationUtil.getSystemUserName()); + }, getRunAuditAs()); } + + public String getRunAuditAs() + { + return runAuditAs; + } + + public void setRunAuditAs(String runAuditAs) + { + + this.runAuditAs = runAuditAs; + } + } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java index 16bf1078d1..2d266a39ea 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java @@ -154,13 +154,22 @@ public class RecordAspect extends AbstractDisposableItem kind = BehaviourKind.CLASS, notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT ) - public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + public void onCreateReference(final NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) { // Deal with versioned records if (reference.equals(CUSTOM_REF_VERSIONS)) { - // Apply the versioned aspect to the from node - nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null); + // run as system, to apply the versioned aspect to the from node + // as we can't be sure if the user has add aspect rights + authenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null); + return null; + } + }); } // Execute script if for the reference event diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java index 6c3aaafc87..5974c922a6 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java @@ -31,6 +31,7 @@ import java.io.Serializable; import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; @@ -49,6 +50,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.apache.commons.lang.ArrayUtils; +import org.springframework.extensions.surf.util.I18NUtil; /** * rma:recordFolder behaviour bean @@ -73,6 +75,9 @@ public class RecordFolderType extends AbstractDisposableItem /** vital record service */ protected VitalRecordService vitalRecordService; + /** I18N */ + private static final String MSG_CANNOT_CREATE_RECORD_FOLDER = "rm.action.record-folder-create"; + /** * @param recordService record service */ @@ -211,12 +216,13 @@ public class RecordFolderType extends AbstractDisposableItem public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew) { NodeRef nodeRef = childAssocRef.getChildRef(); - if (nodeService.exists(nodeRef) && instanceOf(nodeRef, TYPE_RECORD_FOLDER)) + + if (nodeService.exists(nodeRef)) { // ensure nothing is being added to a closed record folder NodeRef recordFolder = childAssocRef.getParentRef(); Boolean isClosed = (Boolean) nodeService.getProperty(recordFolder, PROP_IS_CLOSED); - if (isClosed != null && Boolean.TRUE.equals(isClosed)) + if (isClosed != null && isClosed) { throw new AlfrescoRuntimeException("You can't add new items to a closed record folder."); } @@ -238,6 +244,12 @@ public class RecordFolderType extends AbstractDisposableItem { final NodeRef recordFolder = childAssocRef.getChildRef(); + // only records can be added in a record folder or hidden folders(is the case of e-mail attachments) + if (!instanceOf(recordFolder, ContentModel.TYPE_CONTENT) && !nodeService.hasAspect(recordFolder, ContentModel.ASPECT_HIDDEN)) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_CANNOT_CREATE_RECORD_FOLDER)); + } + behaviourFilter.disableBehaviour(); try { diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java index 845c1d3117..4ebfc07cf8 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java @@ -393,7 +393,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl final NodeRef record = recordService.createRecordFromCopy(filePlan, nodeRef); // apply version record aspect to record - PropertyMap versionRecordProps = new PropertyMap(3); + final PropertyMap versionRecordProps = new PropertyMap(3); versionRecordProps.put(PROP_VERSIONED_NODEREF, nodeRef); versionRecordProps.put(RecordableVersionModel.PROP_VERSION_LABEL, standardVersionProperties.get( @@ -403,7 +403,16 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl standardVersionProperties.get( QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.PROP_VERSION_DESCRIPTION))); - nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps); + // run as system as we can't be sure if the user has add aspect rights + authenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + nodeService.addAspect(record, ASPECT_VERSION_RECORD, versionRecordProps); + return null; + } + }); // wire record up to previous record linkToPreviousVersionRecord(nodeRef, record); diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java new file mode 100644 index 0000000000..462198f5a7 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java @@ -0,0 +1,148 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; + +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.PERIOD_ONE_WEEK; +import static org.alfresco.util.GUID.generate; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.EditDispositionActionAsOfDateAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** +* Update next disposition step integration tests. +* +* @author Roxana Lucanu +* @since 2.4.1 +*/ +public class UpdateNextDispositionActionTest extends BaseRMTestCase +{ + /** + * Given a record with multiple dispositions + * When updating the next step + * Then the action is available + *

+ * relates to https://issues.alfresco.com/jira/browse/RM-3060 + */ + public void testUpdateNextDispositionAction_RM3060() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + NodeRef record; + NodeRef folder2; + + @Override + public void given() + { + // create category1 + NodeRef category1 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category1 + createDispositionSchedule(category1); + + // create category2 + NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category2 + createDispositionSchedule(category2); + + // create folder2 inside category2 + folder2 = recordFolderService.createRecordFolder(category2, generate()); + + // create folder1 inside category1 + NodeRef folder1 = recordFolderService.createRecordFolder(category1, generate()); + + // create record inside folder1 + record = utils.createRecord(folder1, generate(), generate()); + + } + @Override + public void when() throws Exception + { + // link the record to folder2 + recordService.link(record, folder2); + + // complete record + utils.completeRecord(record); + + // set the disposition as of date to now on the record + rmActionService.executeRecordsManagementAction(record, + EditDispositionActionAsOfDateAction.NAME, + Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date())); + + // cut off + rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); + } + + @Override + public void then() throws Exception + { + assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF)); + } + }); + } + + private void createDispositionSchedule(NodeRef category) + { + DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false); + + // create the properties for CUTOFF action and add it to the disposition action definition + Map cutOff = new HashMap(3); + cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); + cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate()); + cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, cutOff); + + // create the properties for TRANSFER action and add it to the disposition action definition + Map transfer = new HashMap(3); + transfer.put(PROP_DISPOSITION_ACTION_NAME, TransferAction.NAME); + transfer.put(PROP_DISPOSITION_DESCRIPTION, generate()); + transfer.put(PROP_DISPOSITION_EVENT, (Serializable)Collections.singletonList(DEFAULT_EVENT_NAME)); + dispositionService.addDispositionActionDefinition(ds, transfer); + + // create the properties for DESTROY action and add it to the disposition action definition + Map destroy = new HashMap(3); + destroy.put(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME); + destroy.put(PROP_DISPOSITION_DESCRIPTION, generate()); + destroy.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, destroy); + } +} \ No newline at end of file diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314Test.java new file mode 100644 index 0000000000..c4068d010a --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314Test.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.module.org_alfresco_module_rm.test.integration.issue.rm3314; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; + +/** + * Test for https://issues.alfresco.com/jira/browse/RM-3114 + * + * @author Roy Wetherall + * @since 2.2.1.5 + */ +public class RM3314Test extends BaseRMTestCase +{ + /** registry to record callback from test beans "test.rm3114.1" and "test.rm3114.2" */ + public static Map callback = new HashMap(2); + + /** + * Given that the custom model hasn't been initialised + * When an aspect is added + * Then nothing happens + * + * Given that the custom model has been initialised + * When an aspect is added + * Then something happens + */ + public void testListenersExecutedInTheCorrectOrder() + { + /** + * The related test beans will call back into the callback map showing + * whether at the end of their execution whether the custom model has been + * initialised or not. Given the order in which these test beans are executed + * on spring context load, we would expect that .1 executes with the custom + * map unloaded, and the .2 with it loaded. + */ + + assertFalse(callback.isEmpty()); + assertFalse(callback.get("test.rm3314.1")); + assertTrue(callback.get("test.rm3314.2")); + } +} diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314TestListener.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314TestListener.java new file mode 100644 index 0000000000..dc71f608ae --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/rm3314/RM3314TestListener.java @@ -0,0 +1,142 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.module.org_alfresco_module_rm.test.integration.issue.rm3314; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminServiceImpl; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; +import org.springframework.jdbc.BadSqlGrammarException; + +/** + * Simple bean used to test RM-3314 + * + * @author rwetherall + * @since 2.2.1.5 + */ +public class RM3314TestListener implements ApplicationListener, + Ordered, + BeanNameAware +{ + private RecordsManagementAdminServiceImpl recordsManagementAdminService; + private NodeService nodeService; + private FileFolderService fileFolderService; + private Repository repository; + + private String name; + private int order = Ordered.LOWEST_PRECEDENCE; + + public void setRecordsManagementAdminService(RecordsManagementAdminServiceImpl recordsManagementAdminService) + { + this.recordsManagementAdminService = recordsManagementAdminService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public void setRepository(Repository repository) + { + this.repository = repository; + } + + @Override + public void setBeanName(String name) + { + this.name = name; + } + + public void setOrder(int order) + { + this.order = order; + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) + { + // call back to show whether the custom map is initialised or not + RM3314Test.callback.put(name, recordsManagementAdminService.isCustomMapInit()); + + // Do some work on a node to show that reguardless of whether the custom map is + // init or not, things still work. + // Note: using public services to ensure new transaction for each service call + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Void doWork() throws Exception + { + try + { + // create node + NodeRef folder = fileFolderService.create( + repository.getCompanyHome(), + name, + ContentModel.TYPE_FOLDER).getNodeRef(); + try + { + // add aspect + nodeService.addAspect(folder, ContentModel.ASPECT_CLASSIFIABLE, null); + + // remove aspect + nodeService.removeAspect(folder, ContentModel.ASPECT_CLASSIFIABLE); + } + finally + { + // delete node + nodeService.deleteNode(folder); + } + } + catch (BadSqlGrammarException e) + { + // ignore and carry on + } + + return null; + } + }); + } + + @Override + public int getOrder() + { + return order; + } +} diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java index 0d35057d2f..b359887c37 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java @@ -34,6 +34,7 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction; +import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction; import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; @@ -48,31 +49,31 @@ import net.sf.acegisecurity.vote.AccessDecisionVoter; /** * Move record folder tests. - * + * * @author Roy Wetherall * @since 2.2 */ public class MoveRecordFolderTest extends BaseRMTestCase -{ +{ @Override protected boolean isRecordTest() { return true; } - + /** - * Given two categories, both with cut off immediately schedules, when the record is move - * then all the parts of the record should be correct based on the new schedule. - * + * Given two categories, both with cut off immediately schedules, when the record is move then all the parts of the + * record should be correct based on the new schedule. + * * @see https://issues.alfresco.com/jira/browse/RM-1345 */ public void testMoveRecordFolderBeforeCutOffFolderLevelDisposition() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) - { - NodeRef recordFolder; + { + NodeRef recordFolder; NodeRef destinationRecordCategory; - + public void given() { doTestInTransaction(new VoidTest() @@ -82,43 +83,44 @@ public class MoveRecordFolderTest extends BaseRMTestCase NodeRef rcOne = createRecordCategory(false); destinationRecordCategory = createRecordCategory(false); recordFolder = recordFolderService.createRecordFolder(rcOne, GUID.generate()); - + // check for the lifecycle aspect assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details DispositionAction dispositionAction = dispositionService.getNextDispositionAction(recordFolder); assertNotNull(dispositionAction); assertNotNull(CutOffAction.NAME, dispositionAction.getName()); assertNotNull(dispositionAction.getAsOfDate()); assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder)); - } + } }); - + doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // check the search aspect properties assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); - assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); + assertEquals(CutOffAction.NAME, + nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF)); - } + } }); } - + public void when() throws Exception - { + { doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // move record folder - fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); - } - }); + fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); + } + }); } - + public void then() { doTestInTransaction(new VoidTest() @@ -127,34 +129,35 @@ public class MoveRecordFolderTest extends BaseRMTestCase { // check for the lifecycle aspect assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details DispositionAction dispositionAction = dispositionService.getNextDispositionAction(recordFolder); assertNotNull(dispositionAction); assertNotNull(CutOffAction.NAME, dispositionAction.getName()); assertNotNull(dispositionAction.getAsOfDate()); assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder)); - - // check the search aspect properties - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); - assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); - assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF)); + + // check the search aspect properties + assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); + assertEquals(CutOffAction.NAME, + nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); + assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF)); } - }); + }); } - }); + }); } - + /** * */ public void testMoveRecordFolderBeforeCutOffIntoAFolderWithNoDisposition() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) - { - NodeRef recordFolder; + { + NodeRef recordFolder; NodeRef destinationRecordCategory; - + public void given() { doTestInTransaction(new VoidTest() @@ -164,43 +167,44 @@ public class MoveRecordFolderTest extends BaseRMTestCase NodeRef rcOne = createRecordCategory(false); destinationRecordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate()); recordFolder = recordFolderService.createRecordFolder(rcOne, GUID.generate()); - + // check for the lifecycle aspect assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details DispositionAction dispositionAction = dispositionService.getNextDispositionAction(recordFolder); assertNotNull(dispositionAction); assertNotNull(CutOffAction.NAME, dispositionAction.getName()); assertNotNull(dispositionAction.getAsOfDate()); assertTrue(dispositionService.isNextDispositionActionEligible(recordFolder)); - } + } }); - + doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // check the search aspect properties assertTrue(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); - assertEquals(CutOffAction.NAME, nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); + assertEquals(CutOffAction.NAME, + nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_NAME)); assertNotNull(nodeService.getProperty(recordFolder, PROP_RS_DISPOSITION_ACTION_AS_OF)); - } + } }); } - + public void when() throws Exception - { + { doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // move record folder - fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); - } - }); + fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); + } + }); } - + public void then() { doTestInTransaction(new VoidTest() @@ -209,29 +213,29 @@ public class MoveRecordFolderTest extends BaseRMTestCase { // check for the lifecycle aspect assertFalse(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details assertNull(dispositionService.getNextDispositionAction(recordFolder)); - - // check the search aspect properties - assertFalse(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); + + // check the search aspect properties + assertFalse(nodeService.hasAspect(recordFolder, ASPECT_RM_SEARCH)); } - }); + }); } - }); + }); } - + /** - * + * */ public void testMoveRecordFolderWithRecordsBeforeCutOffRecordLevelDisposition() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) - { - NodeRef record; + { + NodeRef record; NodeRef recordFolder; NodeRef destinationRecordCategory; - + public void given() { doTestInTransaction(new VoidTest() @@ -242,11 +246,11 @@ public class MoveRecordFolderTest extends BaseRMTestCase destinationRecordCategory = createRecordCategory(true); recordFolder = recordFolderService.createRecordFolder(rcOne, GUID.generate()); record = utils.createRecord(recordFolder, GUID.generate()); - + // check for the lifecycle aspect assertFalse(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details assertNull(dispositionService.getNextDispositionAction(recordFolder)); DispositionAction dispositionAction = dispositionService.getNextDispositionAction(record); @@ -254,33 +258,34 @@ public class MoveRecordFolderTest extends BaseRMTestCase assertNotNull(CutOffAction.NAME, dispositionAction.getName()); assertNotNull(dispositionAction.getAsOfDate()); assertTrue(dispositionService.isNextDispositionActionEligible(record)); - } + } }); - + doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // check the search aspect properties assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH)); - assertEquals(CutOffAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME)); + assertEquals(CutOffAction.NAME, + nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME)); assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF)); - } + } }); } - + public void when() throws Exception - { + { doTestInTransaction(new VoidTest() { public void runImpl() throws Exception { // move record folder - fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); - } - }); + fileFolderService.move(recordFolder, destinationRecordCategory, GUID.generate()); + } + }); } - + public void then() { doTestInTransaction(new VoidTest() @@ -290,7 +295,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase // check for the lifecycle aspect assertFalse(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - + // check the disposition action details assertNull(dispositionService.getNextDispositionAction(recordFolder)); DispositionAction dispositionAction = dispositionService.getNextDispositionAction(record); @@ -298,20 +303,21 @@ public class MoveRecordFolderTest extends BaseRMTestCase assertNotNull(CutOffAction.NAME, dispositionAction.getName()); assertNotNull(dispositionAction.getAsOfDate()); assertTrue(dispositionService.isNextDispositionActionEligible(record)); - - // check the search aspect properties - assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH)); - assertEquals(CutOffAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME)); - assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF)); + + // check the search aspect properties + assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH)); + assertEquals(CutOffAction.NAME, + nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME)); + assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF)); } - }); + }); } - }); + }); } /** * Try and move a folder from no disposition schedule to a disposition schedule - * + * * @see https://issues.alfresco.com/jira/browse/RM-1039 */ public void testMoveRecordFolderFromNoDisToDis() throws Exception @@ -333,7 +339,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase { assertNotNull(result); assertNull(dispositionService.getDispositionSchedule(result)); - assertFalse(nodeService.hasAspect(result, ASPECT_DISPOSITION_LIFECYCLE)); + assertFalse(nodeService.hasAspect(result, ASPECT_DISPOSITION_LIFECYCLE)); assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(result, FILING)); } }); @@ -394,7 +400,6 @@ public class MoveRecordFolderTest extends BaseRMTestCase }); } - // try and move a cutoff folder public void testMoveCutoffRecordFolder() throws Exception { @@ -426,7 +431,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase return testFolder; } - + @Override public void test(NodeRef testFolder) throws Exception { @@ -446,11 +451,66 @@ public class MoveRecordFolderTest extends BaseRMTestCase }); } - + // try and move a destroyed folder + public void testMoveDestroyedRecordFolder() throws Exception + { + final NodeRef destination = doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + // create a record category (no disposition schedule) + return filePlanService.createRecordCategory(filePlan, "Caitlin Reed"); + } + }); + + final NodeRef testFolder = doTestInTransaction(new Test() + { + @Override + public NodeRef run() + { + // create folder + NodeRef testFolder = recordFolderService.createRecordFolder(rmContainer, "Peter Edward Francis"); + + // complete event + Map params = new HashMap(1); + params.put(CompleteEventAction.PARAM_EVENT_NAME, CommonRMTestUtils.DEFAULT_EVENT_NAME); + rmActionService.executeRecordsManagementAction(testFolder, CompleteEventAction.NAME, params); + + // cutoff & destroy folder + rmActionService.executeRecordsManagementAction(testFolder, CutOffAction.NAME); + rmActionService.executeRecordsManagementAction(testFolder, DestroyAction.NAME); + + return testFolder; + } + + }); + + doTestInTransaction(new Test() + { + @Override + public NodeRef run() throws Exception + { + Capability moveCapability = capabilityService.getCapability("MoveRecordFolder"); + assertEquals(AccessDecisionVoter.ACCESS_GRANTED, moveCapability.evaluate(testFolder, destination)); + + return fileFolderService.move(testFolder, destination, null).getNodeRef(); + } + + @Override + public void test(NodeRef result) throws Exception + { + assertNotNull(result); + } + }); + + } + private NodeRef createRecordCategory(boolean recordLevel) { NodeRef rc = filePlanService.createRecordCategory(filePlan, GUID.generate()); - DispositionSchedule dis = utils.createBasicDispositionSchedule(rc, GUID.generate(), GUID.generate(), recordLevel, false); + DispositionSchedule dis = utils.createBasicDispositionSchedule(rc, GUID.generate(), GUID.generate(), + recordLevel, false); Map adParams = new HashMap(3); adParams.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); adParams.put(PROP_DISPOSITION_DESCRIPTION, GUID.generate()); diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/DeclareVersionAsRecordActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/DeclareVersionAsRecordActionTest.java new file mode 100644 index 0000000000..05d81a1268 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/DeclareVersionAsRecordActionTest.java @@ -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 . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.test.legacy.action; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction; +import org.alfresco.module.org_alfresco_module_rm.action.dm.DeclareAsVersionRecordAction; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.rule.Rule; +import org.alfresco.service.cmr.rule.RuleService; +import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionType; + +public class DeclareVersionAsRecordActionTest extends BaseRMTestCase +{ + private RuleService ruleService; + private NodeRef ruleFile; + protected static final String DESCRIPTION = "description"; + + + @Override + protected void initServices() + { + super.initServices(); + ruleService = (RuleService)applicationContext.getBean("RuleService"); + } + + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + @Override + protected boolean isRecordTest() + { + return true; + } + + + /** + * Given a node set to auto-declare documents as records for minor and major versions + * When I try to upload a minor or major version + * Then the version record aspect is added + */ + public void testUpdateNextDispositionAction_RM3060() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmContributor) + { + Map versionProperties = new HashMap(4); + Version recordedVersion; + + @Override + public void given() + { + // create the file + ruleFile = fileFolderService.create(documentLibrary, "mytestfile", ContentModel.TYPE_CONTENT).getNodeRef(); + + Action action = actionService.createAction(DeclareAsVersionRecordAction.NAME); + action.setParameterValue(CreateRecordAction.PARAM_FILE_PLAN, filePlan); + + Rule rule = new Rule(); + rule.setRuleType(RuleType.INBOUND); + rule.setTitle("my rule"); + rule.setAction(action); + rule.setExecuteAsynchronously(true); + ruleService.saveRule(ruleFile, rule); + + // setup version properties + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); + versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); + } + + @Override + public void when() + { + recordedVersion = versionService.createVersion(ruleFile, versionProperties); + } + + @Override + public void then() throws Exception + { + NodeRef recordedVersionNodeRef= (NodeRef)recordedVersion.getVersionProperties().get(RecordableVersionModel.PROP_RECORD_NODE_REF.getLocalName()); + assertNotNull("Recorded version shouldn't be null.", recordedVersionNodeRef); + assertTrue(nodeService.hasAspect(recordedVersionNodeRef, RecordableVersionModel.ASPECT_VERSION_RECORD)); + } + }); + } + +} diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index ed71716105..4365d3339b 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -271,6 +271,8 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase /** collaboration site users */ protected String dmConsumer; protected NodeRef dmConsumerNodeRef; + protected String dmContributor; + protected NodeRef dmContributorNodeRef; protected String dmCollaborator; protected NodeRef dmCollaboratorNodeRef; @@ -772,6 +774,10 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase dmConsumer = GUID.generate(); dmConsumerNodeRef = createPerson(dmConsumer); siteService.setMembership(collabSiteId, dmConsumer, SiteModel.SITE_CONSUMER); + + dmContributor = GUID.generate(); + dmContributorNodeRef = createPerson(dmContributor); + siteService.setMembership(collabSiteId, dmContributor, SiteModel.SITE_CONTRIBUTOR); dmCollaborator = GUID.generate(); dmCollaboratorNodeRef = createPerson(dmCollaborator); diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java index 8f4ddd7969..1938535988 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java @@ -86,6 +86,7 @@ public class CommonRMTestUtils implements RecordsManagementModel public static final String DEFAULT_EVENT_NAME = "case_closed"; public static final String PERIOD_NONE = "none|0"; public static final String PERIOD_IMMEDIATELY = "immediately|0"; + public static final String PERIOD_ONE_WEEK = "week|1"; /** * Constructor diff --git a/rm-community/rm-community-repo/test/resources/test-context.xml b/rm-community/rm-community-repo/test/resources/test-context.xml index f00d3e56b6..e79bdda973 100644 --- a/rm-community/rm-community-repo/test/resources/test-context.xml +++ b/rm-community/rm-community-repo/test/resources/test-context.xml @@ -245,5 +245,26 @@ - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file