diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml index 6c70ad4bf8..40a454449b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-record-context.xml @@ -276,7 +276,6 @@ RECORD - RECORD_FOLDER 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 2420858b34..97f9aee406 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 @@ -62,6 +62,27 @@ + + + + + + RECORD_FOLDER + + + + + + + + + + + + + + + @@ -114,7 +115,8 @@ - + + @@ -232,6 +234,10 @@ + + + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json index 5771b8d991..257f495b84 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/security/rm-default-roles-bootstrap.json @@ -49,7 +49,8 @@ "RequestRecordInformation", "FileUnfiledRecords", "RejectRecords", - "LinkToRecords" + "LinkToRecords", + "FileVersionRecords" ] }, { @@ -74,7 +75,8 @@ "FileUnfiledRecords", "RejectRecords", "LinkToRecords", - "ManageAccessControls" + "ManageAccessControls", + "FileVersionRecords" ] }, { @@ -145,8 +147,8 @@ "DeleteHold", "EndRetention", "EditHold", - "ManageAccessControls" - + "ManageAccessControls", + "FileVersionRecords" ] }, { @@ -219,7 +221,8 @@ "FileHoldReport", "DeleteHold", "EditHold", - "EndRetention" + "EndRetention", + "FileVersionRecords" ] } ] \ No newline at end of file diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java index ef9245ed29..2a6ab6942f 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -27,6 +27,8 @@ package org.alfresco.module.org_alfresco_module_rm.action.dm; +import static org.alfresco.module.org_alfresco_module_rm.action.dm.RecordActionUtils.resolvePath; + import java.util.List; import org.alfresco.module.org_alfresco_module_rm.action.AuditableActionExecuterAbstractBase; @@ -128,13 +130,13 @@ public class CreateRecordAction extends AuditableActionExecuterAbstractBase Boolean hideRecordValue = ((Boolean) action.getParameterValue(PARAM_HIDE_RECORD)); if (hideRecordValue != null) { - hideRecord = hideRecordValue.booleanValue(); + hideRecord = hideRecordValue; } if (pathParameter != null && !pathParameter.isEmpty()) { RecordActionUtils.Services services = new Services(nodeService, filePlanService, authenticationUtil); - destinationRecordFolder = RecordActionUtils.resolvePath(services, filePlan, pathParameter, NAME); + destinationRecordFolder = resolvePath(services, filePlan, pathParameter, NAME); } synchronized (this) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java index a05b3eddd4..3ad3c4cf00 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordAction.java @@ -74,7 +74,7 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac public static final String PARAM_FILE_PLAN = "file-plan"; public static final String PARAM_PATH = "path"; - private static final String EDIT_RECORD_METADATA_CAPABILITY = "EditRecordMetadata"; + private static final String FILE_VERSION_RECORDS_CAPABILITY = "FileVersionRecords"; /** Sync Model URI */ private static final String SYNC_MODEL_1_0_URI = "http://www.alfresco.org/model/sync/1.0"; @@ -181,24 +181,21 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac logger.debug("Can not declare version as record, because " + actionedUponNodeRef.toString() + " is not a supported type."); } } - else if (!isActionEligible(actionedUponNodeRef)) + else if (isActionEligible(actionedUponNodeRef)) { NodeRef filePlan = (NodeRef)action.getParameterValue(PARAM_FILE_PLAN); if (filePlan == null) { filePlan = RecordActionUtils.getDefaultFilePlan(authenticationUtil, filePlanService, NAME); } - else + // verify that the provided file plan is actually a file plan + else if (!filePlanService.isFilePlan(filePlan)) { - // verify that the provided file plan is actually a file plan - if (!filePlanService.isFilePlan(filePlan)) + if (logger.isDebugEnabled()) { - if (logger.isDebugEnabled()) - { - logger.debug("Can not declare version record, because the provided file plan node reference is not a file plan."); - } - throw new AlfrescoRuntimeException("Can not declare version record, because the provided file plan node reference is not a file plan."); + logger.debug("Can not declare version record, because the provided file plan node reference is not a file plan."); } + throw new AlfrescoRuntimeException("Can not declare version record, because the provided file plan node reference is not a file plan."); } // resolve destination record folder if path supplied @@ -213,7 +210,7 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac // create record from latest version if (destinationRecordFolder != null) { - boolean hasCapability = capabilityService.hasCapability(destinationRecordFolder, EDIT_RECORD_METADATA_CAPABILITY); + boolean hasCapability = capabilityService.hasCapability(destinationRecordFolder, FILE_VERSION_RECORDS_CAPABILITY); // validate destination record folder if (hasCapability) { @@ -261,7 +258,7 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac { logger.debug("Can not declare version record, because " + actionedUponNodeRef.toString() + aspect.getValue()); } - return true; + return false; } } if (!nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_VERSIONABLE)) @@ -270,8 +267,8 @@ public class DeclareAsVersionRecordAction extends AuditableActionExecuterAbstrac { logger.debug("Can not declare version record, because " + actionedUponNodeRef.toString() + " does not have the versionable aspect applied."); } - return true; + return false; } - return false; + return true; } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/RecordActionUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/RecordActionUtils.java index bfae201062..de087d4edd 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/RecordActionUtils.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/RecordActionUtils.java @@ -37,16 +37,20 @@ import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; +/** + * Utility class containing helper methods for record + */ + public class RecordActionUtils { /** * Logger */ - private static final Log LOGGER = LogFactory.getLog(RecordActionUtils.class); + private static final Logger LOGGER = LoggerFactory.getLogger(RecordActionUtils.class); /** Private constructor to prevent instantiation. */ private RecordActionUtils() @@ -135,15 +139,12 @@ public class RecordActionUtils { throw new AlfrescoRuntimeException("Unable to execute " + actionName + " action, because the destination path could not be found."); } - else + QName nodeType = nodeService.getType(nodeRef); + if (nodeType.equals(RecordsManagementModel.TYPE_HOLD_CONTAINER) || + nodeType.equals(RecordsManagementModel.TYPE_TRANSFER_CONTAINER) || + nodeType.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER)) { - QName nodeType = nodeService.getType(nodeRef); - if (nodeType.equals(RecordsManagementModel.TYPE_HOLD_CONTAINER) || - nodeType.equals(RecordsManagementModel.TYPE_TRANSFER_CONTAINER) || - nodeType.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER)) - { - throw new AlfrescoRuntimeException("Unable to execute " + actionName + " action, because the destination path is invalid."); - } + throw new AlfrescoRuntimeException("Unable to execute " + actionName + " action, because the destination path is invalid."); } if (pathElements.size() > 1) { @@ -164,11 +165,10 @@ public class RecordActionUtils // if the file plan is still null, raise an exception if (filePlan == null) { - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("Unable to execute " + actionName + " action, because the fileplan path could not be determined. Make sure at least one file plan has been created."); - } - throw new AlfrescoRuntimeException("Unable to execute " + actionName + " action, because the fileplan path could not be determined."); + final String logMessage = + String.format("Unable to execute %s action, because the fileplan path could not be determined. Make sure at least one file plan has been created.", actionName); + LOGGER.debug(logMessage); + throw new AlfrescoRuntimeException(logMessage); } return filePlan; } diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java index 0610fbd27e..86a9f49bf0 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareAsVersionRecordActionUnitTest.java @@ -218,14 +218,8 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest 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); - + setupMockedAspects(); + // no default file plan doReturn(null).when(mockedFilePlanService).getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); @@ -235,7 +229,7 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest // execute action declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); } - + /** * Given that no file plan is provided * And a default file plan exists @@ -246,13 +240,7 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest 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); + setupMockedAspects(); // no default file plan doReturn(filePlan).when(mockedFilePlanService).getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); @@ -261,7 +249,7 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest 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 @@ -272,14 +260,8 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest 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); - + setupMockedAspects(); + // not a file plan is provided in the parameters mockActionParameterValue(DeclareAsVersionRecordAction.PARAM_FILE_PLAN, generateNodeRef()); @@ -300,14 +282,8 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest 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); - + setupMockedAspects(); + // not a file plan is provided in the parameters NodeRef myFilePlan = generateNodeRef(TYPE_FILE_PLAN); doReturn(true).when(mockedFilePlanService).isFilePlan(myFilePlan); @@ -326,29 +302,25 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest @Test public void validDestinationRecordFolderProvided() { - String childName = GUID.generate(); + String pathParameter = GUID.generate(); // 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); + setupMockedAspects(); + + mockActionParameterValue(DeclareAsVersionRecordAction.PARAM_PATH, pathParameter); // provided location - doReturn(destinationRecordFolderNodeRef).when(mockedNodeService).getChildByName(parentDestinationNodeRef, ContentModel.ASSOC_CONTAINS, childName); + doReturn(destinationRecordFolderNodeRef).when(mockedNodeService).getChildByName(filePlan, ContentModel.ASSOC_CONTAINS, pathParameter); doReturn(TYPE_RECORD_FOLDER).when(mockedNodeService).getType(destinationRecordFolderNodeRef); // capability check - doReturn(true).when(mockedCapabilityService).hasCapability(destinationRecordFolderNodeRef, "EditRecordMetadata"); + doReturn(true).when(mockedCapabilityService).hasCapability(destinationRecordFolderNodeRef, "FileVersionRecords"); // file plan service doReturn(filePlan).when(mockedFilePlanService).getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID); // execute action - declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); - verify(mockedRecordableVersionService, times(1)).createRecordFromLatestVersion(filePlan, actionedUponNodeRef); + declareAsVersionRecordAction.executeImpl(getMockedAction(), actionedUponNodeRef); + verify(mockedRecordableVersionService, times(1)).createRecordFromLatestVersion(destinationRecordFolderNodeRef, actionedUponNodeRef); } /** @@ -361,13 +333,7 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest { String childName = GUID.generate(); // 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); + setupMockedAspects(); // provided location doReturn(destinationRecordFolderNodeRef).when(mockedNodeService).getChildByName(parentDestinationNodeRef, ContentModel.ASSOC_CONTAINS, childName); @@ -380,6 +346,17 @@ public class DeclareAsVersionRecordActionUnitTest extends BaseActionUnitTest exception.expect(AlfrescoRuntimeException.class); // execute action - declareAsVersionRecordAction.executeImpl(mock(Action.class), actionedUponNodeRef); + declareAsVersionRecordAction.executeImpl(getMockedAction(), actionedUponNodeRef); + } + + private void setupMockedAspects() + { + 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); } }