diff --git a/README.txt b/README.txt index f31803686a..b46013c3f5 100644 --- a/README.txt +++ b/README.txt @@ -95,9 +95,6 @@ Follow these instructions install licence and Outlook plugin: - http://docs.alfresco.com/outlook2.1/tasks/Outlook-install_v2.html - - - SNAPSHOT dependencies: ---------------------- @@ -114,6 +111,7 @@ Code Formatting: This project follows the usual Alfresco Coding Standards. If you use Eclipse or IntelliJ, there are settings inside the ide-config directory for you to import. + Surf build errors: ------------------ @@ -121,3 +119,11 @@ If you get: [ERROR] Failed to execute goal on project alfresco-rm-community-share: Could not resolve dependencies for project org.alfresco:alfresco-rm-community-share:amp:2.6-SNAPSHOT: Failed to collect dependencies at org.alfresco.surf:spring-surf-api:jar:6.3 -> org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Failed to read artifact descriptor for org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Could not transfer artifact org.alfresco.surf:spring-surf:pom:${dependency.surf.version} from/to alfresco-internal (https://artifacts.alfresco.com/nexus/content/groups/private): Not authorized , ReasonPhrase:Unauthorized. -> [Help 1] then please re-run with -Ddependency.surf.version=6.3 + + +Install lombok plugin for IDEs: +------------------------------- + +To allow automation and benchmark projects to be built within an IDE the lombok 'plugin' needs to be installed. + +Execute lombok.jar (doubleclick it, or run java -jar lombok.jar). Follow instructions. diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java index 67e87511b4..abc4534aad 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java @@ -134,7 +134,7 @@ public class RecordCategoryChildProperties extends TestModel private Boolean recordSearchDispositionEventsEligible; @JsonProperty (PROPERTIES_RECORD_SEARCH_DISPOSITION_INSTRUCTIONS) - private String recordSearchDispositionInstructions; + private String recordSearchDispositionInstructions; @JsonProperty (PROPERTIES_OWNER) private Owner owner; diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/RecordCategoryTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/RecordCategoryTests.java index 92d1073588..b769d3d572 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/RecordCategoryTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/RecordCategoryTests.java @@ -52,6 +52,7 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.NoSuchElementException; @@ -66,7 +67,10 @@ import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; +import org.alfresco.rest.v0.RecordCategoriesAPI; +import org.alfresco.rest.core.v0.BaseAPI.RETENTION_SCHEDULE; import org.alfresco.utility.report.Bug; +import org.springframework.beans.factory.annotation.Autowired; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -83,6 +87,9 @@ public class RecordCategoryTests extends BaseRMRestTest private static final int NUMBER_OF_CHILDREN = 10; private static final int NUMBER_OF_FOLDERS = 5; + @Autowired + private RecordCategoriesAPI recordCategoriesAPI; + /** * Invalid containers that cannot be deleted with record category end-point */ @@ -287,6 +294,7 @@ public class RecordCategoryTests extends BaseRMRestTest assertEquals(folderProperties.getTitle(), TITLE_PREFIX + RECORD_FOLDER_NAME); assertNotNull(folderProperties.getIdentifier()); } + /** *
      * Given that a record category exists
@@ -294,17 +302,37 @@ public class RecordCategoryTests extends BaseRMRestTest
      * When I ask the API to get me the children of the record category
      * Then I am returned the contained record categories and record folders and their details
      * 
+ *
+     * Given that a record category with a disposition schedule exists
+     * And contains a number of record categories and record folders
+     * When I ask the API to get me the children of the record category
+     * Then I am returned the contained record categories and record folders but not the disposition schedule
+     * 
*/ @Test - ( - description = "Get children of a record category" - ) + ( + description = "Get children of a record category excluding the disposition schedule" + ) + @Bug (id="RM-5115") public void getRecordCategoryChildren() throws Exception { // Create root level category RecordCategory rootRecordCategory = createRootCategory(getRandomAlphanumeric()); assertNotNull(rootRecordCategory.getId()); + // Create disposition schedule + String userName = getAdminUser().getUsername(); + String userPassword = getAdminUser().getPassword(); + String categoryName = rootRecordCategory.getName(); + recordCategoriesAPI.createRetentionSchedule(userName, userPassword, categoryName); + + // Add disposition schedule cut off step + HashMap cutOffStep = new HashMap<>(); + cutOffStep.put(RETENTION_SCHEDULE.NAME, "cutoff"); + cutOffStep.put(RETENTION_SCHEDULE.RETENTION_PERIOD, "day|2"); + cutOffStep.put(RETENTION_SCHEDULE.DESCRIPTION, "Cut off after 2 days"); + recordCategoriesAPI.addDispositionScheduleSteps(userName, userPassword, categoryName, cutOffStep); + // Add record category children List children = new ArrayList(); for (int i=0; i < NUMBER_OF_CHILDREN; i++) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java index fce0877b72..923b70c636 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/fileplans/FilePlanChildrenRelation.java @@ -198,8 +198,8 @@ public class FilePlanChildrenRelation implements RelationshipResourceAction.Read for (RecordCategory nodeInfo : nodeInfos) { // Create the node - NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), RECORD_CATEGORY_TYPE, - nodeInfo.getProperties(), nodeInfo.getAspectNames()); + nodeInfo.setNodeType(RECORD_CATEGORY_TYPE); + NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo, parameters); createdNodes.add(newNodeRef); } return createdNodes; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java index 7ba7add131..8efd431607 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/FilePlanComponentsApiUtils.java @@ -134,6 +134,12 @@ public class FilePlanComponentsApiUtils RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, RecordsManagementModel.TYPE_HOLD_CONTAINER); + public static final List TYPES_CAN_USE_AUTORENAME = Arrays.asList( + RecordsManagementModel.TYPE_RECORD_CATEGORY, + RecordsManagementModel.TYPE_RECORD_FOLDER, + RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER, + RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER); + /** RM Nodes API */ private Nodes nodes; private FileFolderService fileFolderService; @@ -598,27 +604,43 @@ public class FilePlanComponentsApiUtils * Create an RM node * * @param parentNodeRef the parent of the node - * @param name the name of the new node - * @param type the type of the node - * @param properties properties to set on the new node - * @param aspects aspects to set on the new node + * @param nodeInfo the node infos to create + * @param parameters the object to get the parameters passed into the request * @return the new node */ - public NodeRef createRMNode(NodeRef parentNodeRef, String name, String type, Map properties, List aspects) + public NodeRef createRMNode(NodeRef parentNodeRef, RMNode nodeInfo, Parameters parameters) { mandatory("parentNodeRef", parentNodeRef); - checkNotBlank(RMNode.PARAM_NAME, name); - checkNotBlank(RMNode.PARAM_NODE_TYPE, type); + mandatory("nodeInfo", nodeInfo); + mandatory("parameters", parameters); + + String nodeName = nodeInfo.getName(); + String nodeType = nodeInfo.getNodeType(); + checkNotBlank(RMNode.PARAM_NAME, nodeName); + checkNotBlank(RMNode.PARAM_NODE_TYPE, nodeType); // Create the node NodeRef newNodeRef = null; + boolean autoRename = Boolean.valueOf(parameters.getParameter(RMNode.PARAM_AUTO_RENAME)); + try { - QName typeQName = nodes.createQName(type); - newNodeRef = fileFolderService.create(parentNodeRef, name, typeQName).getNodeRef(); + QName typeQName = nodes.createQName(nodeType); + + // Existing file/folder name handling + if (TYPES_CAN_USE_AUTORENAME.contains(typeQName) && autoRename) + { + NodeRef existingNode = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, nodeName); + if (existingNode != null) + { + // File already exists, find a unique name + nodeName = findUniqueName(parentNodeRef, nodeName); + } + } + newNodeRef = fileFolderService.create(parentNodeRef, nodeName, typeQName).getNodeRef(); // Set the provided properties if any - Map qnameProperties = mapToNodeProperties(properties); + Map qnameProperties = mapToNodeProperties(nodeInfo.getProperties()); if (qnameProperties != null) { nodeService.addProperties(newNodeRef, qnameProperties); @@ -628,18 +650,19 @@ public class FilePlanComponentsApiUtils if (!typeQName.equals(RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT) && dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT)) { - writeContent(newNodeRef, name, new ByteArrayInputStream("".getBytes()), false); + writeContent(newNodeRef, nodeName, new ByteArrayInputStream("".getBytes()), false); } // Add the provided aspects if any - if (aspects != null) + List aspectNames = nodeInfo.getAspectNames(); + if (aspectNames != null) { - nodes.addCustomAspects(newNodeRef, aspects, ApiNodesModelFactory.EXCLUDED_ASPECTS); + nodes.addCustomAspects(newNodeRef, aspectNames, ApiNodesModelFactory.EXCLUDED_ASPECTS); } } catch (InvalidTypeException ex) { - throw new InvalidArgumentException("The given type:'" + type + "' is invalid '"); + throw new InvalidArgumentException("The given type:'" + nodeType + "' is invalid '"); } return newNodeRef; @@ -955,7 +978,45 @@ public class FilePlanComponentsApiUtils activityPoster.postFileFolderActivity(activityType, null, TenantUtil.getCurrentDomain(), activityInfo.getSiteId(), activityInfo.getParentNodeRef(), activityInfo.getNodeRef(), activityInfo.getFileName(), Activities.APP_TOOL, Activities.RESTAPI_CLIENT, activityInfo.getFileInfo()); - } } + /** + * Creates a unique file name, if the upload component was configured to + * find a new unique name for clashing filenames. + * + * @param parentNodeRef the parent node + * @param fileName the original fileName + * @return a new file name + */ + private String findUniqueName(NodeRef parentNodeRef, String fileName) + { + int counter = 1; + String tmpFilename; + NodeRef existingFile; + do + { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex == 0) + { + // File didn't have a proper 'name' instead it + // had just a suffix and started with a ".", create "1.txt" + tmpFilename = counter + fileName; + } + else if (dotIndex > 0) + { + // Filename contained ".", create "fileName-1.txt" + tmpFilename = fileName.substring(0, dotIndex) + "-" + counter + fileName.substring(dotIndex); + } + else + { + // Filename didn't contain a dot at all, create "fileName-1" + tmpFilename = fileName + "-" + counter; + } + existingFile = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, tmpFilename); + counter++; + + } while (existingFile != null); + + return tmpFilename; + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java index 2e103a9874..cbaee33f3b 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java @@ -198,6 +198,7 @@ public class SearchTypesFactory Boolean isRecordFolder = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_FOLDER, WhereClauseParser.EQUALS, Boolean.class); Boolean isRecordCategory = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY, WhereClauseParser.EQUALS, Boolean.class); + if ((isRecordFolder != null && isRecordFolder.booleanValue()) || (isRecordCategory != null && !isRecordCategory.booleanValue())) { includeRecordFolders = true; @@ -221,6 +222,7 @@ public class SearchTypesFactory if (nodeTypeQNameStr.equals(RecordsManagementModel.TYPE_RECORD_FOLDER)) { includeRecordFolders = true; + } else if (filterNodeTypeQName.equals(RecordsManagementModel.TYPE_RECORD_CATEGORY)) { diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java index d4d590e3e8..7ddc40ded9 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RMNode.java @@ -57,6 +57,7 @@ public abstract class RMNode public static final String PARAM_PROPERTIES = "properties"; public static final String PARAM_PATH = "path"; public static final String PARAM_ALLOWABLE_OPERATIONS = "allowableOperations"; + public static final String PARAM_AUTO_RENAME = "autoRename"; public static final String PARAM_ISPRIMARY = "isPrimary"; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java index 8da2cbf427..e1bdb8a680 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordcategories/RecordCategoryChildrenRelation.java @@ -33,6 +33,7 @@ import static org.alfresco.util.ParameterCheck.mandatory; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -40,6 +41,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.query.PagingResults; import org.alfresco.repo.node.getchildren.FilterProp; @@ -123,10 +125,11 @@ public class RecordCategoryChildrenRelation implements RelationshipResourceActio // list record categories and record folders Set searchTypeQNames = searchTypesFactory.buildSearchTypesCategoriesEndpoint(parameters, LIST_RECORD_CATEGORY_CHILDREN_EQUALS_QUERY_PROPERTIES); + Set assocTypeQNames = Collections.singleton(ContentModel.ASSOC_CONTAINS); List filterProps = apiUtils.getListChildrenFilterProps(parameters, LIST_RECORD_CATEGORY_CHILDREN_EQUALS_QUERY_PROPERTIES); final PagingResults pagingResults = fileFolderService.list(parentNodeRef, - null, + assocTypeQNames, searchTypeQNames, null, apiUtils.getSortProperties(parameters), @@ -189,8 +192,7 @@ public class RecordCategoryChildrenRelation implements RelationshipResourceActio RecordsManagementModel.TYPE_RECORD_CATEGORY); } // Create the node - NodeRef newNode = apiUtils.createRMNode(nodeParent, nodeInfo.getName(), nodeInfo.getNodeType(), - nodeInfo.getProperties(), nodeInfo.getAspectNames()); + NodeRef newNode = apiUtils.createRMNode(nodeParent, nodeInfo, parameters); createdNodes.add(newNode); } return createdNodes; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java index 84a6b1f84a..1b4fd75877 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/recordfolders/RecordFolderChildrenRelation.java @@ -167,7 +167,7 @@ public class RecordFolderChildrenRelation implements RelationshipResourceAction. List createdNodes = new LinkedList<>(); for (Record nodeInfo : nodeInfos) { - NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames()); + NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo, parameters); createdNodes.add(newNodeRef); } return createdNodes; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java index f163b7a796..34107358ff 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledcontainers/UnfiledContainerChildrenRelation.java @@ -186,7 +186,7 @@ public class UnfiledContainerChildrenRelation implements RelationshipResourceAct List createdNodes = new LinkedList<>(); for (UnfiledContainerChild nodeInfo : nodeInfos) { - NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames()); + NodeRef newNodeRef = apiUtils.createRMNode(parentNodeRef, nodeInfo, parameters); createdNodes.add(newNodeRef); } return createdNodes; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java index ec18fc0e25..9fff8274af 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/unfiledrecordfolders/UnfiledRecordFolderChildrenRelation.java @@ -174,7 +174,7 @@ public class UnfiledRecordFolderChildrenRelation implements RelationshipResource mandatory("parameters", parameters); NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER); - + // Create the children RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { @@ -193,7 +193,7 @@ public class UnfiledRecordFolderChildrenRelation implements RelationshipResource nodeParent = parentNodeRef; } - NodeRef newNodeRef = apiUtils.createRMNode(nodeParent, nodeInfo.getName(), nodeInfo.getNodeType(), nodeInfo.getProperties(), nodeInfo.getAspectNames()); + NodeRef newNodeRef = apiUtils.createRMNode(nodeParent, nodeInfo, parameters); createdNodes.add(newNodeRef); } return createdNodes;