diff --git a/pom.xml b/pom.xml index e91a141ffe..23d477d21e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT Alfresco Records Management diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index 1033c353a4..496465be48 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT diff --git a/rm-automation/rm-automation-community-rest-api/pom.xml b/rm-automation/rm-automation-community-rest-api/pom.xml index 36080c3d8e..ce2e06b574 100644 --- a/rm-automation/rm-automation-community-rest-api/pom.xml +++ b/rm-automation/rm-automation-community-rest-api/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm-automation - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java index e3a37ff998..935dcc151c 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java @@ -662,7 +662,7 @@ public abstract class BaseAPI RETENTION_GHOST, RETENTION_ELIGIBLE_FIRST_EVENT, RETENTION_EVENTS, - + COMBINE_DISPOSITION_STEP_CONDITIONS } /** @@ -674,6 +674,8 @@ public abstract class BaseAPI CUT_OFF("cutoff"), UNDO_CUT_OFF("undoCutoff"), TRANSFER("transfer"), + COMPLETE_EVENT("completeEvent"), + UNDO_EVENT("undoEvent"), DESTROY("destroy"); String action; diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/RMEvents.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/RMEvents.java new file mode 100644 index 0000000000..a63fa5b903 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/RMEvents.java @@ -0,0 +1,49 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 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.core.v0; + +public enum RMEvents +{ + ABOLISHED("abolished"), + ALL_ALLOWANCES_GRANTED_ARE_TERMINATED("all_allowances_granted_are_terminated"), + CASE_CLOSED("case_closed"), + DECLASSIFICATION_REVIEW("declassification_review"), + OBSOLETE("obsolete"), + NO_LONGER_NEEDED("no_longer_needed"), + STUDY_COMPLETE("study_complete"); + private String eventName; + + RMEvents(String eventName) + { + this.eventName = eventName; + } + + public String getEventName() + { + return eventName; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java index 2fbbf3cc15..4732c28a17 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentFields.java @@ -58,6 +58,8 @@ public class FilePlanComponentFields public static final String PROPERTIES_RECORD_SEARCH_DISPOSITION_ACTION_NAME = "rma:recordSearchDispositionActionName"; public static final String PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS_ELIGIBLE = "rma:recordSearchDispositionEventsEligible"; public static final String PROPERTIES_RECORD_SEARCH_DISPOSITION_INSTRUCTIONS = "rma:recordSearchDispositionInstructions"; + public static final String PROPERTIES_DECLASSIFICATION_REVIEW_COMPLETED_BY = "rma:declassificationReviewCompletedBy"; + public static final String PROPERTIES_DECLASSIFICATION_REVIEW_COMPLETED_AT = "rma:declassificationReviewCompletedAt"; /** File plan properties */ 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 index 61de24b76c..122ab88bef 100644 --- 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 @@ -35,6 +35,7 @@ import static org.testng.AssertJUnit.fail; import java.io.IOException; import java.text.MessageFormat; +import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; @@ -46,6 +47,7 @@ import org.alfresco.dataprep.AlfrescoHttpClientFactory; import org.alfresco.dataprep.ContentService; import org.alfresco.dataprep.UserService; import org.alfresco.rest.core.v0.BaseAPI; +import org.alfresco.rest.core.v0.RMEvents; import org.apache.chemistry.opencmis.client.api.CmisObject; import org.apache.commons.httpclient.HttpStatus; import org.apache.http.HttpResponse; @@ -325,9 +327,9 @@ public class RMRolesAndActionsAPI extends BaseAPI /** * Perform an action on the record folder * - * @param user the user closing the folder + * @param user the user executing the action * @param password the user's password - * @param contentName the record folder name + * @param contentName the content name * @param date the date to be updated * @return The HTTP response. */ @@ -349,6 +351,56 @@ public class RMRolesAndActionsAPI extends BaseAPI return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API); } + /** + * Complete an event on the record/record folder + * + * @param user the user executing the action + * @param password the user's password + * @param nodeName the node name + * @param event the event to be completed + * @param date the date to be updated + * @return The HTTP response. + */ + public HttpResponse completeEvent(String user, String password, String nodeName, RMEvents event, Instant date) + { + String recNodeRef = getNodeRefSpacesStore() + contentService.getNodeRef(user, password, RM_SITE_ID, nodeName); + JSONObject requestParams = new JSONObject(); + requestParams.put("name", RM_ACTIONS.COMPLETE_EVENT.getAction()); + requestParams.put("nodeRef", recNodeRef); + date = (date != null) ? date : Instant.now(); + String formattedDate = DateTimeFormatter.ISO_INSTANT.format(date); + requestParams.put("params", new JSONObject() + .put("eventName", event.getEventName()) + .put("eventCompletedBy", user) + .put("eventCompletedAt", new JSONObject() + .put("iso8601", formattedDate) + ) + ); + + return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API); + } + + /** + * Undo an event on the record/record folder + * + * @param user the user executing the action + * @param password the user's password + * @param contentName the content name + * @param event the event to be undone + * @return The HTTP response. + */ + public HttpResponse undoEvent(String user, String password, String contentName, RMEvents event) + { + String recNodeRef = getNodeRefSpacesStore() + contentService.getNodeRef(user, password, RM_SITE_ID, contentName); + JSONObject requestParams = new JSONObject(); + requestParams.put("name", RM_ACTIONS.UNDO_EVENT.getAction()); + requestParams.put("nodeRef", recNodeRef); + requestParams.put("params", new JSONObject() + .put("eventName", event.getEventName())); + + return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API); + } + /** * Deletes every item in the given container * 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 index 34ff344f92..6a3caed2cc 100644 --- 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 @@ -51,6 +51,8 @@ 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"; + private static final String DISPOSITION_SCHEDULE_API = "{0}node/{1}/dispositionschedule"; + /** * Creates a retention schedule for the category given as parameter @@ -71,6 +73,21 @@ public class RecordCategoriesAPI extends BaseAPI return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API); } + /** + * Get the disposition schedule nodeRef + * + * @param user + * @param password + * @param categoryName + * @return the disposition schedule nodeRef + */ + public String getDispositionScheduleNodeRef(String user, String password, String categoryName) + { + String catNodeRef = NODE_PREFIX + getItemNodeRef(user, password, "/" + categoryName); + JSONObject dispositionSchedule = doGetRequest(user, password, MessageFormat.format(DISPOSITION_SCHEDULE_API, "{0}", catNodeRef)); + return dispositionSchedule.getJSONObject("data").getString("nodeRef").replace(getNodeRefSpacesStore(), ""); + } + /** * Sets retention schedule authority and instructions, also if it is applied to records or folders * @@ -108,7 +125,12 @@ public class RecordCategoriesAPI extends BaseAPI 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); + String events = getPropertyValue(properties, RETENTION_SCHEDULE.RETENTION_EVENTS); + if(!events.equals("")) + { + requestParams.append("events", events); + } + addPropertyToRequest(requestParams, "combineDispositionStepConditions", properties, RETENTION_SCHEDULE.COMBINE_DISPOSITION_STEP_CONDITIONS); addPropertyToRequest(requestParams, "eligibleOnFirstCompleteEvent", properties, RETENTION_SCHEDULE.RETENTION_ELIGIBLE_FIRST_EVENT); return doPostJsonRequest(user, password, SC_OK, requestParams, MessageFormat.format(DISPOSITION_ACTIONS_API, "{0}", catNodeRef)); diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java index f462f06cdf..2e91ee2928 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java @@ -66,6 +66,9 @@ public class SearchAPI extends BaseAPI /** RM document search filters */ private static final String RM_DEFAULT_RECORD_FILTERS = "records/true,undeclared/true,vital/false,folders/false,categories/false,frozen/false,cutoff/false"; + /** RM all nodes search filters */ + private static final String RM_DEFAULT_NODES_FILTERS = + "records/true,undeclared/true,vital/false,folders/true,categories/true,frozen/false,cutoff/false"; /** * Perform search request on search endpoint as a user. @@ -139,6 +142,26 @@ public class SearchAPI extends BaseAPI return getItemNames(rmSearch(username, password, "rm", query, RM_DEFAULT_RECORD_FILTERS, sortby)); } + /** + * Search as a user for content on site "rm" matching query, using SearchAPI.RM_DEFAULT_NODES_FILTERS and sorted + * by sortby + *
+ * If more fine-grained control of search parameters is required, use rmSearch() directly. + * @param username + * @param password + * @param query + * @param sortby + * @return list of record names + */ + public List searchForRmContentAsUser( + String username, + String password, + String query, + String sortby) + { + return getItemNames(rmSearch(username, password, "rm", query, RM_DEFAULT_NODES_FILTERS, sortby)); + } + /** * Generic faceted search. * @param username diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/DispositionScheduleService.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/DispositionScheduleService.java new file mode 100644 index 0000000000..e5265f69ac --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/DispositionScheduleService.java @@ -0,0 +1,139 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 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.service; + + +import java.util.HashMap; + +import org.alfresco.rest.core.v0.BaseAPI; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.v0.RecordCategoriesAPI; +import org.alfresco.utility.data.DataUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Service for different disposition schedule actions + * + * @author jcule + * @since 2.7.0.1 + */ +@Service +public class DispositionScheduleService extends BaseAPI +{ + @Autowired + private RecordCategoriesAPI recordCategoriesAPI; + + @Autowired + private DataUser dataUser; + + /** + * Helper method for adding a cut off after period step + * + * @param categoryName the category in whose schedule the step will be added + * @param period + * @return + */ + public void addCutOffAfterPeriodStep(String categoryName, String period) + { + HashMap cutOffStep = new HashMap<>(); + cutOffStep.put(RETENTION_SCHEDULE.NAME, "cutoff"); + cutOffStep.put(RETENTION_SCHEDULE.RETENTION_PERIOD, period); + cutOffStep.put(RETENTION_SCHEDULE.DESCRIPTION, "Cut off after a period step"); + recordCategoriesAPI.addDispositionScheduleSteps(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), categoryName, cutOffStep); + } + + /** + * Helper method for adding a cut off after an event occurs step + * + * @param categoryName the category in whose schedule the step will be added + * @param events + */ + public void addCutOffAfterEventStep(String categoryName, String events) + { + HashMap cutOffStep = new HashMap<>(); + cutOffStep.put(RETENTION_SCHEDULE.NAME, "cutoff"); + cutOffStep.put(RETENTION_SCHEDULE.RETENTION_EVENTS, events); + cutOffStep.put(RETENTION_SCHEDULE.DESCRIPTION, "Cut off after event step"); + + recordCategoriesAPI.addDispositionScheduleSteps(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), categoryName, cutOffStep); + } + + /** + * Helper method for adding an accession step + * + * @param timeOrEvent + * @param events + * @param period + * @param periodProperty + * @param combineConditions + * @return + */ + public void addAccessionStep(String categoryName, Boolean timeOrEvent, String events, String period, String + periodProperty, Boolean combineConditions) + { + HashMap accessionStep = new HashMap<>(); + accessionStep.put(RETENTION_SCHEDULE.NAME, "accession"); + accessionStep.put(RETENTION_SCHEDULE.COMBINE_DISPOSITION_STEP_CONDITIONS, Boolean.toString(combineConditions)); + accessionStep.put(RETENTION_SCHEDULE.RETENTION_PERIOD, period); + accessionStep.put(RETENTION_SCHEDULE.RETENTION_PERIOD_PROPERTY, periodProperty); + if (!timeOrEvent) + { + accessionStep.put(RETENTION_SCHEDULE.RETENTION_ELIGIBLE_FIRST_EVENT, Boolean.toString(timeOrEvent)); + } + accessionStep.put(RETENTION_SCHEDULE.RETENTION_EVENTS, events); + accessionStep.put(RETENTION_SCHEDULE.DESCRIPTION, + "Accession step with time and event conditions."); + recordCategoriesAPI.addDispositionScheduleSteps(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), categoryName, accessionStep); + } + + /** + * Helper method to create retention schedule with general fields for the given category as admin + * and apply it to the records + * + * @param categoryName + * @param appliedToRecords + */ + public void createCategoryRetentionSchedule(String categoryName, Boolean appliedToRecords) + { + recordCategoriesAPI.createRetentionSchedule(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), categoryName); + String retentionScheduleNodeRef = recordCategoriesAPI.getDispositionScheduleNodeRef( + dataUser.getAdminUser().getUsername(), dataUser.getAdminUser().getPassword(), categoryName); + + HashMap retentionScheduleGeneralFields = new HashMap<>(); + retentionScheduleGeneralFields.put(RETENTION_SCHEDULE.RETENTION_AUTHORITY, "Authority"); + retentionScheduleGeneralFields.put(RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS, "Instructions"); + recordCategoriesAPI.setRetentionScheduleGeneralFields(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), retentionScheduleNodeRef, retentionScheduleGeneralFields, + appliedToRecords); + + } +} 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 38a424db96..4ca0ff6f6a 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 @@ -707,7 +707,7 @@ public class BaseRMRestTest extends RestTest } } - results = searchApi.searchForRecordsAsUser(user.getUsername(), user.getPassword(), term, sortby); + results = searchApi.searchForRmContentAsUser(user.getUsername(), user.getPassword(), term, sortby); if (!results.isEmpty() && results.containsAll(expectedResults)) { break; diff --git a/rm-community/pom.xml b/rm-community/pom.xml index 9e3e1d0779..ff1f35ec79 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml index dc0e5277b3..d23a9f7b37 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -1132,6 +1132,19 @@ false + + d:date + true + + + d:text + true + + true + false + false + +
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml index 3c3d0ccf20..c922181ba7 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml @@ -467,12 +467,18 @@ + + + + + + org.alfresco alfresco-rm-community - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java index 2e5a12664f..c8d474cac8 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionActionImpl.java @@ -35,11 +35,13 @@ import java.util.List; import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.script.slingshot.RMSearchGet; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -328,6 +330,12 @@ public class DispositionActionImpl implements DispositionAction, props.put(PROP_EVENT_EXECUTION_COMPLETED_BY, completedByValue); services.getNodeService().setProperties(eventNodeRef, props); + // check a specific event from rmEventConfigBootstrap.json + if (eventName.equals("declassification_review")) + { + setDeclassificationReview(eventNodeRef, completedAtValue, completedByValue); + } + // Check to see if the events eligible property needs to be updated updateEventEligible(); @@ -518,4 +526,38 @@ public class DispositionActionImpl implements DispositionAction, return eligible; } + + /** + * Sets declassification review authority and date on records and record folder + * + * @param eventNodeRef Declassification review event node ref + * @param completedAtValue Declassification review authority + * @param completedByValue Declassification review date + */ + private void setDeclassificationReview(NodeRef eventNodeRef, Date completedAtValue, String completedByValue) + { + NodeRef nextDispositionActionNodeRef = services.getNodeService().getPrimaryParent(eventNodeRef).getParentRef(); + NodeRef nodeRef = services.getNodeService().getPrimaryParent(nextDispositionActionNodeRef).getParentRef(); + setPropsOnContent(nodeRef, completedAtValue, completedByValue); + + // check if the node is a record folder then set the declassification review on the records also + if (services.getNodeService().getType(nodeRef).equals(RecordsManagementModel.TYPE_RECORD_FOLDER)) + { + // get all the records inside the record folder + List records = services.getNodeService().getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef child : records) + { + NodeRef recordNodeRef = child.getChildRef(); + setPropsOnContent(recordNodeRef, completedAtValue, completedByValue); + } + } + } + + private void setPropsOnContent(NodeRef nodeRef, Date completedAtValue, String completedByValue) + { + Map nodeProps = services.getNodeService().getProperties(nodeRef); + nodeProps.put(PROP_RS_DECLASSIFICATION_REVIEW_COMPLETED_AT, completedAtValue); + nodeProps.put(PROP_RS_DECLASSIFICATION_REVIEW_COMPLETED_BY, completedByValue); + services.getNodeService().setProperties(nodeRef, nodeProps); + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java index f7211b7d33..0b77311cf4 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java @@ -247,6 +247,8 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel QName PROP_RS_HAS_DISPOITION_SCHEDULE = QName.createQName(RM_URI, "recordSearchHasDispositionSchedule"); QName PROP_RS_DISPOITION_INSTRUCTIONS = QName.createQName(RM_URI, "recordSearchDispositionInstructions"); QName PROP_RS_DISPOITION_AUTHORITY = QName.createQName(RM_URI, "recordSearchDispositionAuthority"); + QName PROP_RS_DECLASSIFICATION_REVIEW_COMPLETED_AT = QName.createQName(RM_URI, "declassificationReviewCompletedAt"); + QName PROP_RS_DECLASSIFICATION_REVIEW_COMPLETED_BY = QName.createQName(RM_URI, "declassificationReviewCompletedBy"); /** @depreacted as of 2.2, because disposable items can now be in multiple holds */ @Deprecated QName PROP_RS_HOLD_REASON = QName.createQName(RM_URI, "recordSearchHoldReason"); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtil.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtil.java new file mode 100644 index 0000000000..fe51b975b9 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtil.java @@ -0,0 +1,86 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 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.script.slingshot; + +import static org.alfresco.model.ContentModel.PROP_NAME; +import static org.alfresco.service.namespace.QName.createQName; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; + +/** + * Method to replace the plain text classification reason id with the correct nodeRef during record search + * @author Ross Gale + * @since 2.7 + */ +public class ClassificationReasonsUtil extends SearchUtil +{ + + public static final String CR_URI = "http://www.alfresco.org/model/securitymarks/1.0"; + public static final QName CLASSIFICATION_REASONS_CONTAINER = createQName(CR_URI,"classificationReasonsContainer"); + public static final QName PROP_CLASSIFICATION_REASON_CODE = createQName(CR_URI, "classificationReasonCode"); + public static final String REASONS_KEY = "clf:classificationReasons:"; + + /** + * Replace plain text reason id with nodeRef + * @param searchQuery String e.g. clf:classificationReasons:1.4(a) + * @return String e.g. clf:classificationReasons:5cc6d344-fa94-4370-9c81-d947b7e8f2ac + */ + public String replaceReasonWithNodeRef(String searchQuery) + { + List queries = new ArrayList<>(Arrays.asList(searchQuery.split(" "))); + StringBuilder stringBuilder = new StringBuilder(); + for (String queryToEdit : queries) + { + if(queryToEdit.contains(REASONS_KEY)) + { + for (String reasonId : retrieveAllNodeIds(getRootContainer(CLASSIFICATION_REASONS_CONTAINER))) + { + NodeRef reasonNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, reasonId); + Map properties = nodeService.getProperties(reasonNodeRef); + if (queryToEdit.equals(REASONS_KEY + properties.get(PROP_CLASSIFICATION_REASON_CODE).toString()) || + queryToEdit.equals(REASONS_KEY +"\""+ properties.get(PROP_CLASSIFICATION_REASON_CODE).toString() + "\"")) + { + queryToEdit = REASONS_KEY + properties.get(PROP_NAME).toString(); + break; + } + } + } + stringBuilder.append(queryToEdit).append(" "); + } + return stringBuilder.toString(); + } + + +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java index e8ac49ff96..ca66efefee 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchGet.java @@ -27,6 +27,8 @@ package org.alfresco.module.org_alfresco_module_rm.script.slingshot; +import static org.alfresco.module.org_alfresco_module_rm.script.slingshot.ClassificationReasonsUtil.REASONS_KEY; + import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -103,6 +105,9 @@ public class RMSearchGet extends DeclarativeWebScript /** Utility class for record categories */ private RecordCategoryUtil recordCategoryUtil; + /** Utility class for classification reasons (enterprise only) */ + private ClassificationReasonsUtil classificationReasonsUtil; + /** * @param recordsManagementSearchService records management search service */ @@ -159,6 +164,11 @@ public class RMSearchGet extends DeclarativeWebScript this.recordCategoryUtil = recordCategoryUtil; } + public void setClassificationReasonsUtil(ClassificationReasonsUtil classificationReasonsUtil) + { + this.classificationReasonsUtil = classificationReasonsUtil; + } + /** * @param personService person service */ @@ -198,6 +208,12 @@ public class RMSearchGet extends DeclarativeWebScript String filters = req.getParameter(PARAM_FILTERS); // TODO this is optional + //Replace any plain text reason ids with the appropriate node reference + if(query.contains(REASONS_KEY)) + { + query = classificationReasonsUtil.replaceReasonWithNodeRef(query); + } + // Convert into a rm search parameter object RecordsManagementSearchParameters searchParameters = SavedSearchDetailsCompatibility.createSearchParameters(filters, new String[]{",", "/"}, sortby, namespaceService); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/SearchUtil.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/SearchUtil.java new file mode 100644 index 0000000000..84c5b9a61d --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/SearchUtil.java @@ -0,0 +1,103 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 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.script.slingshot; + +import static org.alfresco.model.ContentModel.ASSOC_CHILDREN; +import static org.alfresco.model.ContentModel.TYPE_CONTAINER; +import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Parent class for records search utilities + * + * @author Ross Gale + * @since 2.7 + */ +public class SearchUtil +{ + /** + * Node service + */ + protected NodeService nodeService; + + /** + * Setter for node service + * + * @param nodeService Node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Use a container node ref and return the nodeIds of the contents + * + * @param nodeRef container + * @return list of nodeIds + */ + protected Set retrieveAllNodeIds(NodeRef nodeRef) + { + List childAssocRefs = nodeService.getChildAssocs(nodeRef); + return childAssocRefs.stream().map(assoc -> assoc.getChildRef().getId()).collect(Collectors.toSet()); + } + + /** + * Helper method to get the classification reason root container. + * The method creates the container if it doesn't already exist. + * + * @return reference to the classification reason root container + */ + protected NodeRef getRootContainer(QName container) + { + NodeRef rootNodeRef = nodeService.getRootNode(STORE_REF_WORKSPACE_SPACESSTORE); + List assocRefs = nodeService.getChildAssocs(rootNodeRef, ASSOC_CHILDREN, container); + + if (assocRefs.isEmpty()) + { + return nodeService.createNode(rootNodeRef, ASSOC_CHILDREN, container, TYPE_CONTAINER).getChildRef(); + } + else if (assocRefs.size() != 1) + { + throw new AlfrescoRuntimeException("Only one container is allowed."); + } + else + { + return assocRefs.iterator().next().getChildRef(); + } + } +} diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtilUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtilUnitTest.java new file mode 100644 index 0000000000..ac0dac5d03 --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/ClassificationReasonsUtilUnitTest.java @@ -0,0 +1,128 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2018 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.script.slingshot; + +import static org.alfresco.model.ContentModel.ASSOC_CHILDREN; +import static org.alfresco.model.ContentModel.PROP_NAME; +import static org.alfresco.module.org_alfresco_module_rm.script.slingshot.ClassificationReasonsUtil.CLASSIFICATION_REASONS_CONTAINER; +import static org.alfresco.module.org_alfresco_module_rm.script.slingshot.ClassificationReasonsUtil.PROP_CLASSIFICATION_REASON_CODE; +import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * @author Ross Gale + * @since 2.7 + */ +public class ClassificationReasonsUtilUnitTest +{ + + @Mock + private NodeService nodeService; + + @Mock + private ChildAssociationRef childAssociationRef; + + @Mock + private ChildAssociationRef reason; + + @Mock + private Map properties; + + @InjectMocks + private ClassificationReasonsUtil classificationReasonsUtil; + + private NodeRef childNodeRef; + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + NodeRef rootNodeRef = new NodeRef("workspace://SpacesStore/rootNodeRef"); + NodeRef containerNodeRef = new NodeRef("workspace://SpacesStore/containerNodeRef"); + childNodeRef = new NodeRef("workspace://SpacesStore/childNodeRef"); + List assocRefs = new ArrayList<>(); + List childAssocRefs = new ArrayList<>(); + assocRefs.add(childAssociationRef); + childAssocRefs.add(reason); + when(reason.getChildRef()).thenReturn(childNodeRef); + when(nodeService.getRootNode(STORE_REF_WORKSPACE_SPACESSTORE)).thenReturn(rootNodeRef); + when(nodeService.getChildAssocs(rootNodeRef, ASSOC_CHILDREN, CLASSIFICATION_REASONS_CONTAINER)).thenReturn(assocRefs); + when(childAssociationRef.getChildRef()).thenReturn(containerNodeRef); + when(nodeService.getChildAssocs(containerNodeRef)).thenReturn(childAssocRefs); + } + + /** + * Check no modifications are made to non matching parts of the query string + */ + @Test + public void testNoChangeMadeToStringIfKeyNotFound() + { + String stringToTest = "noChangeMadeToString"; + assertEquals("Change made to string",stringToTest, classificationReasonsUtil.replaceReasonWithNodeRef(stringToTest).trim()); + } + + /** + * Check no modifications made if the plain text parameter doesn't have a stored match + */ + @Test + public void testNoChangeMadeToStringIfMatchNotFound() + { + when(nodeService.getProperties(childNodeRef)).thenReturn(properties); + when(properties.get(PROP_CLASSIFICATION_REASON_CODE)).thenReturn("not a match!"); + String stringToTest = "clf:classificationReasons:noChangeMadeToString"; + assertEquals("Change made to string", stringToTest, classificationReasonsUtil.replaceReasonWithNodeRef(stringToTest).trim()); + } + + /** + * Check the query is updated correctly when a match is found + */ + @Test + public void testChangeMadeToStringIfMatchFound() + { + when(nodeService.getProperties(childNodeRef)).thenReturn(properties); + when(properties.get(PROP_CLASSIFICATION_REASON_CODE)).thenReturn("stringToChange"); + when(properties.get(PROP_NAME)).thenReturn("newString"); + String stringToTest = "clf:classificationReasons:stringToChange"; + assertEquals("No change made to string", "clf:classificationReasons:newString", classificationReasonsUtil.replaceReasonWithNodeRef(stringToTest).trim()); + } +} diff --git a/rm-community/rm-community-rest-api-explorer/pom.xml b/rm-community/rm-community-rest-api-explorer/pom.xml index cded13c0aa..ddf33cf85c 100644 --- a/rm-community/rm-community-rest-api-explorer/pom.xml +++ b/rm-community/rm-community-rest-api-explorer/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-rm-community - 3.0.0-SNAPSHOT + 2.7.0.1-SNAPSHOT