diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/Base.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java similarity index 68% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/Base.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java index 207957d9df..18e717bba0 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/Base.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java @@ -38,6 +38,9 @@ import java.util.Map; import org.alfresco.dataprep.AlfrescoHttpClient; import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.dataprep.ContentService; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; @@ -58,25 +61,41 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -public abstract class Base +/** + * The base API class containing common methods for making v0 API requests + * + * @author Kristijan Conkas + * @since 2.5 + */ +public abstract class BaseAPI { // logger - private static final Logger LOGGER = LoggerFactory.getLogger(Base.class); + private static final Logger LOGGER = LoggerFactory.getLogger(BaseAPI.class); /** exception key in JSON response body */ private static final String EXCEPTION_KEY = "exception"; + protected static final String FILE_PLAN_PATH = "Sites/rm/documentLibrary"; + protected static final String NODE_REF_WORKSPACE_SPACES_STORE = "workspace://SpacesStore/"; + protected static final String NODE_PREFIX = "workspace/SpacesStore/"; + protected static final String UPDATE_METADATA_API = "{0}node/{1}/formprocessor"; + protected static final String ACTIONS_API = "{0}actionQueue"; + protected static final String RM_ACTIONS_API = "{0}rma/actions/ExecutionQueue"; + protected static final String RM_SITE_ID = "rm"; @Autowired private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + @Autowired + private ContentService contentService; + /** * Helper method to extract list of properties values from result. * - * @param result + * @param result the response * @return list of specified property values in result * @throws RuntimeException for malformed response */ - public List getPropertyValues(JSONObject result, String propertyName) + protected List getPropertyValues(JSONObject result, String propertyName) { ArrayList results = new ArrayList(); try @@ -99,11 +118,11 @@ public abstract class Base /** * Helper method to extract property values from request result and put them in map as a list that corresponds to a unique property value. * - * @param requestResult + * @param requestResult the request response * @return a map containing information about multiple properties values that correspond to a unique one * @throws RuntimeException for malformed response */ - public Map> getPropertyValuesByUniquePropertyValue(JSONObject requestResult, String uniqueProperty, List otherProperties) + protected Map> getPropertyValuesByUniquePropertyValue(JSONObject requestResult, String uniqueProperty, List otherProperties) { Map> valuesByUniqueProperty = new HashMap<>(); try @@ -129,15 +148,51 @@ public abstract class Base return valuesByUniqueProperty; } + /** + * Retrieves the nodeRef of an item (category, folder or record) with the given path + * + * @param username the username + * @param password the password + * @param path the path to the container eg. in case of a category it would be the category name, + * in case of a folder it would be /categoryName/folderName + * when trying to get File Plan, the path would be "" + * @return the container nodeRef + */ + public String getItemNodeRef(String username, String password, String path) + { + return contentService.getNodeRefByPath(username, password, FILE_PLAN_PATH + path); + } + + /** + * Retrieve a Cmis object by its path + * + * @param username the user's username + * @param password its password + * @param path the object path + * @return the object in case it exists, null if its does not exist + */ + protected CmisObject getObjectByPath(String username, String password, String path) + { + CmisObject object; + try + { + object = contentService.getCMISSession(username, password).getObjectByPath(path); + } catch (CmisObjectNotFoundException notFoundError) + { + return null; + } + return object; + } + /** * Generic faceted request. * - * @param username - * @param password + * @param username the username + * @param password the password * @param parameters if the request has parameters * @return result object (see API reference for more details), null for any errors */ - public JSONObject facetedRequest(String username, String password, List parameters, String requestURI) + protected JSONObject facetedRequest(String username, String password, List parameters, String requestURI) { String requestURL; AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); @@ -166,7 +221,7 @@ public abstract class Base * @param urlTemplate request URL template * @param urlTemplateParams zero or more parameters used with urlTemplate */ - public JSONObject doGetRequest(String adminUser, + protected JSONObject doGetRequest(String adminUser, String adminPassword, String urlTemplate, String ... urlTemplateParams) @@ -195,7 +250,7 @@ public abstract class Base * @param urlTemplate request URL template * @param urlTemplateParams zero or more parameters used with urlTemplate */ - public JSONObject doDeleteRequest(String adminUser, + protected JSONObject doDeleteRequest(String adminUser, String adminPassword, String urlTemplate, String ... urlTemplateParams) @@ -225,7 +280,7 @@ public abstract class Base * @param urlTemplate request URL template * @param urlTemplateParams zero or more parameters used with urlTemplate */ - public JSONObject doPutRequest(String adminUser, + protected JSONObject doPutRequest(String adminUser, String adminPassword, JSONObject requestParams, String urlTemplate, @@ -256,7 +311,7 @@ public abstract class Base * @param urlTemplate request URL template * @param urlTemplateParams zero or more parameters used with urlTemplate */ - public JSONObject doPostRequest(String adminUser, + protected JSONObject doPostRequest(String adminUser, String adminPassword, JSONObject requestParams, String urlTemplate, @@ -288,7 +343,7 @@ public abstract class Base * @param urlTemplate request URL template * @param urlTemplateParams zero or more parameters used with urlTemplate */ - public boolean doPostJsonRequest(String adminUser, + protected boolean doPostJsonRequest(String adminUser, String adminPassword, JSONObject requestParams, String urlTemplate, @@ -436,4 +491,126 @@ public abstract class Base return false; } + + /** + * Used to set RM items properties + * including records, categories and folders + */ + public enum RMProperty + { + NAME, + TITLE, + CONTENT, + DESCRIPTION, + AUTHOR, + PHYSICAL_SIZE, + NUMBER_OF_COPIES, + STORAGE_LOCATION, + SHELF, + BOX, + FILE, + } + + public enum RETENTION_SCHEDULE + { + NAME, + DESCRIPTION, + RETENTION_AUTHORITY, + RETENTION_INSTRUCTIONS, + RETENTION_PERIOD, + RETENTION_LOCATION, + RETENTION_PERIOD_PROPERTY, + RETENTION_GHOST, + RETENTION_ELIGIBLE_FIRST_EVENT, + RETENTION_EVENTS, + + } + + /** + * Used to execute rm actions on a node + */ + public enum RM_ACTIONS + { + EDIT_DISPOSITION_DATE("editDispositionActionAsOfDate"), + CUT_OFF("cutoff"), + UNDO_CUT_OFF("undoCutoff"), + TRANSFER("transfer"), + DESTROY("destroy"); + String action; + + private RM_ACTIONS(String action) + { + this.action = action; + } + + public String getAction() + { + return action; + } + } + + public enum PermissionType + { + SET_READ, + REMOVE_READ, + SET_READ_AND_FILE, + REMOVE_READ_AND_FILE, + } + + /** + * Util to return the property value from a map + * + * @param properties the map containing properties + * @param property to get value for + * @return the property value + */ + public > String getPropertyValue(Map properties, Enum property) + { + String value = properties.get(property); + if (value == null) + { + return ""; + } + return value; + } + + /** + * Retrieves the property value and decides if that gets to be added to the request + * + * @param requestParams the request parameters + * @param propertyRequestValue the property name in the request, eg. "prop_cm_name" + * @param itemProperties map of item's properties values + * @param property the property in the property map to check value for + * @return the json object used in request with the property with its value added if that is not null or empty + */ + protected > JSONObject addPropertyToRequest(JSONObject requestParams, String propertyRequestValue, Map itemProperties, Enum property) throws JSONException + { + String propertyValue = getPropertyValue(itemProperties, property); + + if (!propertyValue.equals("")) + { + requestParams.put(propertyRequestValue, propertyValue); + } + return requestParams; + } + + /** + * Deletes the category, folder or record given as parameter + * + * @param username the username with whom the delete is performed + * @param password the user's password + * @param itemPath the path to the item eg. in case of a category it would be the "/" + category name, + * in case of a folder or subCategory it would be /categoryName/folderName or /categoryName/subCategoryName/ + * in case of a record /categoryName/folderName/recordName + * @return true if the deletion has been successful + */ + protected boolean deleteItem(String username, String password, String itemPath) + { + CmisObject container = getObjectByPath(username, password, FILE_PLAN_PATH + itemPath); + if (container != null) + { + container.delete(); + } + return getObjectByPath(username, password, itemPath) == null; + } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java new file mode 100644 index 0000000000..6d33a69c12 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java @@ -0,0 +1,353 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.rest.v0; + +import static org.alfresco.dataprep.AlfrescoHttpClient.MIME_TYPE_JSON; + +import java.io.IOException; +import java.text.MessageFormat; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Map; + +import org.alfresco.dataprep.AlfrescoHttpClient; +import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.dataprep.ContentService; +import org.alfresco.dataprep.UserService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; +import org.apache.http.util.EntityUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on RM items (move, update and other actions) including adding users to RM roles + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RMRolesAndActionsAPI extends BaseAPI +{ + private static final String RM_ROLES_AUTHORITIES = "{0}rm/roles/{1}/authorities/{2}?alf_ticket={3}"; + + // logger + private static final Logger LOGGER = LoggerFactory.getLogger(RMRolesAndActionsAPI.class); + private static final String MOVE_ACTIONS_API = "action/rm-move-to/site/rm/documentLibrary/{0}"; + private static final String CREATE_HOLDS_API = "{0}type/rma:hold/formprocessor"; + + /** http client factory */ + @Autowired + private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + + private ApplicationContext applicationContext; + + /** user service */ + @Autowired + private UserService userService; + + @Autowired + private ContentService contentService; + + /** + * create user and assign to records management role + */ + public void createUserAndAssignToRole( + String adminUser, + String adminPassword, + String userName, + String password, + String email, + String role, + String firstName, + String lastName) + { + if (!userService.userExists(adminUser, adminPassword, userName)) + { + userService.create(adminUser, adminPassword, userName, password, email, firstName, lastName); + } + assignUserToRole(adminUser, adminPassword, userName, role); + } + + /** + * assign user to records management role + */ + public boolean assignUserToRole(String adminUser, String adminPassword, String userName, String role) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String reqURL = MessageFormat.format( + RM_ROLES_AUTHORITIES, + client.getApiUrl(), + role, + userName, + client.getAlfTicket(adminUser, adminPassword)); + + HttpPost request = null; + HttpResponse response = null; + try + { + request = new HttpPost(reqURL); + response = client.execute(adminUser, adminPassword, request); + switch (response.getStatusLine().getStatusCode()) + { + case HttpStatus.SC_OK: + return true; + case HttpStatus.SC_CONFLICT: + break; + default: + break; + } + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return false; + } + + /** + * Move action + * + * @param user the user to move the contentPath + * @param password the user's password + * @param contentPath path to the content to be moved + * @param destinationPath destination path + * @return true if the action completed successfully + */ + public boolean moveTo(String user, String password, String contentPath, String destinationPath) + { + String contentNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + getItemNodeRef(user, password, contentPath); + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String url = MessageFormat.format(client.getAlfrescoUrl() + "alfresco/s/slingshot/doclib/" + MOVE_ACTIONS_API, destinationPath); + HttpPost request = new HttpPost(url); + + try + { + JSONObject body = new JSONObject(); + body.put("nodeRefs", new JSONArray(Arrays.asList(contentNodeRef))); + StringEntity se = new StringEntity(body.toString(), AlfrescoHttpClient.UTF_8_ENCODING); + se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, MIME_TYPE_JSON)); + request.setEntity(se); + + HttpResponse response = client.execute(user, password, request); + switch (response.getStatusLine().getStatusCode()) + { + case HttpStatus.SC_OK: + JSONObject json = new JSONObject(EntityUtils.toString(response.getEntity())); + return (Boolean) json.get("overallSuccess"); + case HttpStatus.SC_NOT_FOUND: + LOGGER.info("The provided paths couldn't be found " + response.toString()); + default: + LOGGER.error("Unable to move: " + response.toString()); + } + } + catch (JSONException e) + { + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return false; + } + + /** + * Perform an action on the record folder + * + * @param user the user executing the action + * @param password the user's password + * @param contentName the content name + * @return true if the action completed successfully + */ + public boolean executeAction(String user, String password, String contentName, RM_ACTIONS rm_action) + { + return executeAction(user, password, contentName, rm_action, null); + } + + /** + * Perform an action on the record folder + * + * @param user the user closing the folder + * @param password the user's password + * @param contentName the record folder name + * @param date the date to be updated + * @return true if the action completed successfully + */ + public boolean executeAction(String user, String password, String contentName, RM_ACTIONS action, ZonedDateTime date) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, contentName); + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", action.getAction()); + requestParams.put("nodeRef", recNodeRef); + if (date != null) + { + String thisMoment = date.format(DateTimeFormatter.ISO_INSTANT); + requestParams.put("params", new JSONObject() + .put("asOfDate", new JSONObject() + .put("iso8601", thisMoment) + ) + ); + } + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Deletes every item in the given container + * + * @param username the user's username + * @param password its password + * @param siteId the site id in which the container is located + * @param containerName the container to look for items into + * @return true if the deletion has been successful + */ + public boolean deleteAllItemsInContainer(String username, String password, String siteId, String containerName) + { + for (CmisObject item : contentService.getFolderObject(contentService.getCMISSession(username, password), siteId, containerName).getChildren()) + { + item.delete(); + } + return !(contentService.getFolderObject(contentService.getCMISSession(username, password), siteId, containerName).getChildren().getHasMoreItems()); + } + + /** + * Deletes hold + * + * @param username user's username + * @param password its password + * @param holdName the hold name + * @return true if the delete is successful + */ + public boolean deleteHold(String username, String password, String holdName) + { + return deleteItem(username, password, "/Holds/" + holdName); + } + + + /** + * Util method to create a hold + * + * @param user the user creating the category + * @param password the user's password + * @param holdName the hold name + * @param reason hold reason + * @param description hold description + * @return true if the hold creation has been successful + */ + public boolean createHold(String user, String password, String holdName, String reason, String description) + { + // if the hold already exists don't try to create it again + String holdsContainerPath = FILE_PLAN_PATH + "/Holds"; + + CmisObject hold = getObjectByPath(user, password, holdsContainerPath + "/" + holdName); + if (hold != null) + { + return true; + } + // retrieve the Holds container nodeRef + String parentNodeRef = getItemNodeRef(user, password, "/Holds"); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("alf_destination", NODE_REF_WORKSPACE_SPACES_STORE + parentNodeRef); + requestParams.put("prop_cm_name", holdName); + requestParams.put("prop_cm_description", description); + requestParams.put("prop_rma_holdReason", reason); + + boolean requestSucceeded = doPostJsonRequest(user, password, requestParams, CREATE_HOLDS_API); + return requestSucceeded && getObjectByPath(user, password, holdsContainerPath + "/" + holdName) != null; + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Updates metadata, can be used on records, folders and categories + * + * @param username the user updating the item + * @param password the user's password + * @param itemNodeRef the item noderef + * @return true if the update of the item properties has been successful + */ + public boolean updateMetadata(String username, String password, String itemNodeRef, Map properties) + { + try + { + JSONObject requestParams = new JSONObject(); + addPropertyToRequest(requestParams, "prop_cm_name", properties, RMProperty.NAME); + addPropertyToRequest(requestParams, "prop_cm_title", properties, RMProperty.TITLE); + addPropertyToRequest(requestParams, "prop_cm_description", properties, RMProperty.DESCRIPTION); + addPropertyToRequest(requestParams, "prop_cm_author", properties, RMProperty.AUTHOR); + + return doPostJsonRequest(username, password, requestParams, MessageFormat.format(UPDATE_METADATA_API, "{0}", itemNodeRef)); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java new file mode 100644 index 0000000000..855b633cd0 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java @@ -0,0 +1,191 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.rest.v0; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.rest.core.v0.BaseAPI; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on record categories + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordCategoriesAPI extends BaseAPI +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RecordCategoriesAPI.class); + private static final String RM_ACTIONS_API = "{0}rma/actions/ExecutionQueue"; + private static final String DISPOSITION_ACTIONS_API = "{0}node/{1}/dispositionschedule/dispositionactiondefinitions"; + + /** + * Creates a retention schedule for the category given as parameter + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param categoryName the category name to create the retention schedule for + * @return true if the creation completed successfully + */ + public boolean createRetentionSchedule(String user, String password, String categoryName) + { + String catNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + getItemNodeRef(user, password, "/" + categoryName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "createDispositionSchedule"); + requestParams.put("nodeRef", catNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Sets retention schedule authority and instructions, also if it is applied to records or folders + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param retentionNodeRef the retention nodeRef + * @return true if the creation completed successfully + */ + public boolean setRetentionScheduleGeneralFields(String user, String password, String retentionNodeRef, Map retentionProperties, Boolean appliedToRecords) + { + String dispRetentionNodeRef = NODE_PREFIX + retentionNodeRef; + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("prop_rma_dispositionAuthority", getPropertyValue(retentionProperties, RETENTION_SCHEDULE.RETENTION_AUTHORITY)); + requestParams.put("prop_rma_dispositionInstructions", getPropertyValue(retentionProperties, RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS)); + requestParams.put("prop_rma_recordLevelDisposition", appliedToRecords.toString()); + return doPostJsonRequest(user, password, requestParams, MessageFormat.format(UPDATE_METADATA_API, "{0}", dispRetentionNodeRef)); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Creates a retention schedule steps for the category given as parameter + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param categoryName the category name to create the retention schedule for + * @return true if the creation completed successfully + */ + public boolean addDispositionScheduleSteps(String user, String password, String categoryName, Map properties) + { + String catNodeRef = NODE_PREFIX + getItemNodeRef(user, password, "/" + categoryName); + { + try + { + JSONObject requestParams = new JSONObject(); + addPropertyToRequest(requestParams, "name", properties, RETENTION_SCHEDULE.NAME); + addPropertyToRequest(requestParams, "description", properties, RETENTION_SCHEDULE.DESCRIPTION); + addPropertyToRequest(requestParams, "period", properties, RETENTION_SCHEDULE.RETENTION_PERIOD); + addPropertyToRequest(requestParams, "ghostOnDestroy", properties, RETENTION_SCHEDULE.RETENTION_GHOST); + addPropertyToRequest(requestParams, "periodProperty", properties, RETENTION_SCHEDULE.RETENTION_PERIOD_PROPERTY); + addPropertyToRequest(requestParams, "events", properties, RETENTION_SCHEDULE.RETENTION_EVENTS); + addPropertyToRequest(requestParams, "eligibleOnFirstCompleteEvent", properties, RETENTION_SCHEDULE.RETENTION_ELIGIBLE_FIRST_EVENT); + + return doPostJsonRequest(user, password, requestParams, MessageFormat.format(DISPOSITION_ACTIONS_API, "{0}", catNodeRef)); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + } + + /** + * Delete a category + * + * @param username user's username + * @param password its password + * @param categoryName the name of the category + * @return true if the delete is successful + */ + public boolean deleteCategory(String username, String password, String categoryName) + { + return deleteItem(username, password, "/" + categoryName); + } + + /** + * Delete a sub-category + * + * @param username user's username + * @param password its password + * @param categoryName the name of the sub-category + * @return true if the delete is successful + */ + public boolean deleteSubCategory(String username, String password, String categoryName, String subCategoryName) + { + return deleteItem(username, password, "/" + categoryName + "/" + subCategoryName); + } + + /** + * Delete a folder inside a container in RM site + * + * @param username user's username + * @param password its password + * @param folderName folder name + * @param containerName the name of the category or container sin which the folder is + * @return true if the delete is successful + */ + public boolean deleteFolderInContainer(String username, String password, String folderName, String containerName) + { + return deleteItem(username, password, "/" + containerName + "/" + folderName); + } + + /** + * Returns a map of retention properties + * + * @param authority retention authority + * @param instructions retention authority + * @return the map + */ + public Map getRetentionProperties(String authority, String instructions) + { + Map retentionProperties = new HashMap<>(); + retentionProperties.put(RETENTION_SCHEDULE.RETENTION_AUTHORITY, authority); + retentionProperties.put(RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS, instructions); + return retentionProperties; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java new file mode 100644 index 0000000000..48ac2e0a82 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java @@ -0,0 +1,78 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.rest.v0; + +import org.alfresco.dataprep.ContentService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on records folders + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordFoldersAPI extends BaseAPI +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RecordFoldersAPI.class); + + @Autowired + private ContentService contentService; + + /** + * Close the record folder + * + * @param user the user closing the folder + * @param password the user's password + * @param recordFolder the record folder name + * @return true if the action completed successfully + */ + public boolean closeRecordFolder(String user, String password, String recordFolder) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, recordFolder); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "closeRecordFolder"); + requestParams.put("nodeRef", recNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java new file mode 100644 index 0000000000..126829a099 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java @@ -0,0 +1,291 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.rest.v0; + +import java.util.Map; + +import org.alfresco.dataprep.CMISUtil.DocumentType; +import org.alfresco.dataprep.ContentService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on records + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordsAPI extends BaseAPI +{ + // logger + private static final Logger LOGGER = LoggerFactory.getLogger(RecordsAPI.class); + + private static final String CREATE_NON_ELECTRONIC_RECORD_API = "{0}type/rma:nonElectronicDocument/formprocessor"; + + @Autowired + private ContentService contentService; + + + /** + * Declare documents as records + * + * @param user the user declaring the document as record + * @param password the user's password + * @param siteID the site id in which the document exists + * @param documentName the document name + * @return true if the action was successful + */ + public boolean declareDocumentAsRecord(String user, String password, String siteID, String documentName) + { + String docNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, siteID, documentName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("actionedUponNode", docNodeRef); + requestParams.put("actionDefinitionName", "create-record"); + + return doPostJsonRequest(user, password, requestParams, ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Completes the record given as parameter + * + * @param user the user declaring the document as record + * @param password the user's password + * @param recordName the record name + * @return true if the action completed successfully + */ + public boolean completeRecord(String user, String password, String recordName) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, recordName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "declareRecord"); + requestParams.put("nodeRef", recNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + + /** + * Declare document version as record + * + * @param user the user declaring the document version as record + * @param password the user's password + * @param siteID the site id in which the document exists + * @param documentName the document name + * @return true if the action was successful + */ + public boolean declareDocumentVersionAsRecord(String user, String password, String siteID, String documentName) + { + String docNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, siteID, documentName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("actionedUponNode", docNodeRef); + requestParams.put("actionDefinitionName", "declare-as-version-record"); + + return doPostJsonRequest(user, password, requestParams, ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Creates a non-electronic record + * + * @param username the username + * @param password the password + * @param properties a map of record properties and their values + * @param categoryName the category that contains the record, in the case in which the container would be Unfiled records use UNFILED_RECORDS_BREADCRUMB as value + * @param folderName the folder inside which the record exists, in the case in which the folder name is "", the record will be created directly in the specified container + * this case is useful when trying to create a record directly in Unfiled Records + * @return true if the creation of the record has been successful + * eg. of usage for Unfiled records with folder : createNonElectronicRecord(getAdminName(), getAdminPassword(), properties, UNFILED_RECORDS_BREADCRUMB, "unfiled records folder"); + * eg. of usage for creating record directly in Unfiled Records : createNonElectronicRecord(getAdminName(), getAdminPassword(), properties, UNFILED_RECORDS_BREADCRUMB, ""); + */ + public > boolean createNonElectronicRecord(String username, String password, Map properties, String categoryName, String folderName) + { + String recordName = properties.get(RMProperty.NAME); + if (getRecord(username, password, folderName, recordName) != null) + { + return true; + } + String recordPath = "/" + categoryName; + if (!folderName.equals("")) + { + recordPath = recordPath + "/" + folderName; + } + // if the record already exists don't try to create it again + CmisObject record = getObjectByPath(username, password, FILE_PLAN_PATH + recordPath + "/" + recordName); + + if (record != null) + { + return true; + } + // non-electronic properties + String recordTitle = getPropertyValue(properties, RMProperty.TITLE); + String description = getPropertyValue(properties, RMProperty.DESCRIPTION); + String physicalSize = getPropertyValue(properties, RMProperty.PHYSICAL_SIZE); + String numberOfCopies = getPropertyValue(properties, RMProperty.NUMBER_OF_COPIES); + String shelf = getPropertyValue(properties, RMProperty.SHELF); + String storage = getPropertyValue(properties, RMProperty.STORAGE_LOCATION); + String box = getPropertyValue(properties, RMProperty.BOX); + String file = getPropertyValue(properties, RMProperty.FILE); + + // retrieve the container nodeRef + String parentNodeRef = getItemNodeRef(username, password, recordPath); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("alf_destination", NODE_REF_WORKSPACE_SPACES_STORE + parentNodeRef); + requestParams.put("prop_cm_name", recordName); + requestParams.put("prop_cm_title", recordTitle); + requestParams.put("prop_cm_description", description); + requestParams.put("prop_rma_physicalSize", physicalSize); + requestParams.put("prop_rma_numberOfCopies", numberOfCopies); + requestParams.put("prop_rma_storageLocation", storage); + requestParams.put("prop_rma_shelf", shelf); + requestParams.put("prop_rma_box", box); + requestParams.put("prop_rma_file", file); + + return doPostJsonRequest(username, password, requestParams, CREATE_NON_ELECTRONIC_RECORD_API); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Uploads an electronic record + * + * @param username the username + * @param password the password + * @param properties a map of record properties and their values + * @param folderName the folder inside which the record will be created, it needs to have a unique name + * @return true if the creation of the record has been successful + * eg. of usage for creating record directly in Unfiled Records : uploadElectronicRecord(getAdminName(), getAdminPassword(), recordPropertiesStringMap, UNFILED_RECORDS_BREADCRUMB, DocumentType.HTML) + * the folder name in which the record gets created has to have a unique name, as this method doesn't check other containers than the folder name + */ + public boolean uploadElectronicRecord(String username, String password, Map properties, String folderName, DocumentType documentType) + { + String recordName = getPropertyValue(properties, RMProperty.NAME); + String recordContent = getPropertyValue(properties, RMProperty.CONTENT); + return (getRecord(username, password, folderName, recordName) != null) || (contentService.createDocumentInFolder(username, password, RM_SITE_ID, folderName, documentType, recordName, recordContent) != null); + } + + /** + * Delete a record from the given path + * + * @param username user's username + * @param password its password + * @param recordName the record name + * @param categoryName the name of the category in which the folder is, in case of unfiled record, this will have UNFILED_RECORDS_BREADCRUMB as container + * @param folderName folder name, in case in which trying to delete a record in Unfiled records directly, this will be "" + * @return true if the delete is successful + * eg. of usage in the case in which the record is inside a folder in Unfiled Records : deleteRecord(getAdminName(), getAdminPassword(), "f1 (2016-1472716888713)", UNFILED_RECORDS_BREADCRUMB, "unfiled records folder"); + * eg. of usage in the case in which the record is created directly in Unfiled Records : deleteRecord(getAdminName(), getAdminPassword(), "f1 (2016-1472716888713)", UNFILED_RECORDS_BREADCRUMB, ""); + */ + public boolean deleteRecord(String username, String password, String recordName, String categoryName, String folderName) + { + String recordPath = "/" + categoryName; + if (!folderName.equals("")) + { + recordPath = recordPath + "/" + folderName; + } + return deleteItem(username, password, recordPath + "/" + recordName); + } + + /** + * Retrieves the record object in case it exists + * + * @param username the user's username + * @param password its password + * @param folderName the folder in which the record is supposed to exist + * @param recordName the String with which the record name starts + * @return the record object in case it exists, null otherwise + */ + private CmisObject getRecord(String username, String password, String folderName, String recordName) + { + for (CmisObject record : contentService.getFolderObject(contentService.getCMISSession(username, password), RM_SITE_ID, folderName).getChildren()) + { + if (record.getName().startsWith(recordName)) + { + return record; + } + } + return null; + } + + /** + * Retrieves record full name for given partial name + * + * @param username the user's username + * @param password its password + * @param folderName the folder in which the record is supposed to exist + * @param recordPartialName the String with which the record name starts + * @return the record name in case it exists, empty String otherwise + */ + public String getRecordFullName(String username, String password, String folderName, String recordPartialName) + { + CmisObject record = getRecord(username, password, folderName, recordPartialName); + if (record != null) + { + return record.getName(); + } + return ""; + } + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/Search.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java similarity index 97% rename from rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/Search.java rename to rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java index acd4dffae5..7bcfff7d48 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/Search.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java @@ -32,7 +32,7 @@ import java.util.Arrays; import java.util.List; import org.alfresco.dataprep.AlfrescoHttpClientFactory; -import org.alfresco.rest.core.v0.Base; +import org.alfresco.rest.core.v0.BaseAPI; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; @@ -42,11 +42,12 @@ import org.springframework.stereotype.Component; /** * Helper methods for performing search using various Alfresco search APIs. + * * @author Kristijan Conkas * @since 2.5 */ @Component -public class Search extends Base +public class SearchAPI extends BaseAPI { /** http client factory */ @Autowired private AlfrescoHttpClientFactory alfrescoHttpClientFactory; @@ -101,14 +102,14 @@ public class Search extends Base String requestURL = MessageFormat.format( RM_SEARCH_ENDPOINT, alfrescoHttpClientFactory.getObject().getAlfrescoUrl(), - (site != null) ? site : "rm", + (site != null) ? site : RM_SITE_ID, URLEncodedUtils.format(searchParameters, "UTF-8")); return doSearch(requestURL, username, password); } /** - * Search as a user for records on site "rm" matching query, using RM_DEFAULT_RECORD_FILTERS. + * Search as a user for records on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS *
* If more fine-grained control of search parameters is required, use rmSearch() directly. * @param username diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java index 042bbf84fd..21c14ea732 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java @@ -214,7 +214,6 @@ public class BaseRMRestTest extends RestTest * @param recordCategoryId The id of the record category * @param name The name of the record category child * @param type The type of the record category child - * @param title The title of the record category child * @return The created {@link RecordCategoryChild} * @throws Exception {@link RecordCategoryAPI#createRecordCategoryChild(RecordCategoryChild, String)} */ @@ -227,11 +226,9 @@ public class BaseRMRestTest extends RestTest /** * Helper method to create a record category child as the admin user * - * @param user The user under whose privileges the node is going to be created * @param recordCategoryId The id of the record category * @param name The name of the record category child * @param type The type of the record category child - * @param title The title of the record category child * @return The created {@link RecordCategoryChild} * @throws Exception {@link RecordCategoryAPI#createRecordCategoryChild(RecordCategoryChild, String)} */ @@ -299,7 +296,6 @@ public class BaseRMRestTest extends RestTest * *@param user The user under whose privileges this structure is going to be created * @param parentId The id of the parent folder - * @param folderName The name of the folder * @param nodeType The child type * @return The created folder * @throws Exception on unsuccessful component creation @@ -317,7 +313,6 @@ public class BaseRMRestTest extends RestTest * Helper method to create child unfiled record folder as the admin user * * @param parentId The id of the parent folder - * @param folderName The name of the folder * @param nodeType The child type * @return The created folder * @throws Exception on unsuccessful component creation