diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..bc34200905 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at katalin.zanaty@alfresco.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/README.md b/README.md new file mode 100644 index 0000000000..1ec3fe91f5 --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +# Records Management: README + +## Contributing +Please refer to our [How to contribute](/CONTRIBUTING.md) guide and our [Contributor Covenant Code of Conduct](/CODE_OF_CONDUCT.md). + +## Configuring and starting Alfresco/Share +* Clone the project (e.g. `git clone git@gitlab.alfresco.com:records-management/records-management.git`) +* Import the project as a maven project +* Start the Alfresco/Share instances with the following commands: + + ``` + mvn clean install -Pstart-repo + mvn clean install -Pstart-share + ``` + + (these commands work best if run from the specific directories, e.g. start Share from + rm-community/rm-community-share/ or rm-enterprise/rm-enterprise-share/ ) + +## Configuring a different DB other than H2 (e.g. MySQL or PostgreSQL) +* Create a file called _local.properties_ under src/main/resources in alfresco-rm-enterprise-repo +* Add the following properties in this new file + + ``` + my.db.name -> The name of the database schema + my.db.port -> The port number for your database (default port number for postgres is 5432 and for mysql it is 3306) + ``` +* Run the following commands to start your Alfresco instance: + + to start Alfresco (using Postgres): + ``` + mvn clean install -Pstart-repo,use-postgres + ``` + + to start Alfresco (using MySQL): + + ``` + mvn clean install -Pstart-repo,use-mysql + ``` + +## Technical documentation +Technical documentation is available at [rm-community/documentation/README.md](/rm-community/documentation/README.md) and [rm-enterprise/documentation/README.md](/rm-enterprise/documentation/README.md). This should be particularly useful for anyone wanting to integrate with or extend RM. + +## Running integration test +In order to execute the integration tests run the following command (unit tests will be executed every time before you start Alfresco/Share): + +``` +mvn clean install -Dskip.integrationtests=false +``` + +## Running UI Automation tests +To run the automated UI tests, change to the rm-automation directory and run: + +``` +mvn clean install -Dskip.automationtests=false +``` + +Note: due to Selenium Firefox driver changes, the highest supported Firefox version for UI tests is 43.0.4 (with Selenium 2.52.0). + +It is possible to have multiple versions of Firefox installed onto your workstation (e.g. one for running the UI tests and the other, kept +up to date, for everyday browsing) but beware Firefox auto-updates. In this scenario the best approach is to create a non-default profile +(default profiles will be shared between your Firefox installations!) for which auto-updates are disabled and forcing the use of this +profile in your tests (`-Dwebdriver.firefox.profile="ProfileName"`). If your Firefox 43 install isn't in your path, you can use the +`-Dwebdriver.firefox.profile` option set to the full path of its "firefox-bin" executable. + +MacOS X Sierra users: if you experience by order of magnitude slower performance when connected to a WiFi network (e.g. office WiFi) +add your workstation to your local /etc/hosts file as described on https://github.com/SeleniumHQ/selenium/issues/2824. + +To use Chrome instead of Firefox: +1. copy webdriver.properties from https://github.com/AlfrescoTestAutomation/selenium-grid/tree/master/src/main/resources +2. put it under src/test/resource in rm-automation-ui project +3. download the chrome driver from http://chromedriver.storage.googleapis.com and extract it +4. change the following properties in webdriver.properties: webdriver.browser (Chrome) and webdriver.chrome.server.path (path/to/chrome/driver) +5. run the tests as usual + +## Updating License Headers +In order to refesh out of date license source headers run the following command: + +``` +mvn clean install -Dlicense.update.dryrun=false +``` + +## Running tests against latest Aikau snapshot +The latest Aikau snapshot can be pulled by running the following command in rm-community: + +``` +mvn clean install -DskipTests -Dalfresco.aikau.version=LATEST -U +``` + +Thereafter start the Share instance and run automation tests as described above. + +## Configuring Outlook Integration +To download and run RM with the Outlook Integration AMPs installed on the repo and Share use the following commands: + +``` +mvn clean install -Pstart-repo,outlook-integration +mvn clean install -Pstart-share,outlook-integration +``` + +Follow these instructions to install licence and Outlook plugin: + +* http://docs.alfresco.com/outlook2.1/tasks/Outlook-license.html +* http://docs.alfresco.com/outlook2.1/tasks/Outlook-install_v2.html + +## SNAPSHOT dependencies +If you're building Enterprise RM, the base project (Community) is pulled in via a snapshot dependency configured in maven. +This dependency will either be loaded from your local .m2 cache, or from Nexus if the version in your .m2 doesn't exist or is old +('old' in maven terms is anything over 24 hours old). If maven fetches community dependencies from Nexus, then it's unlikely to contain your changes. +You want to always use the version in your local cache - this means either doing a daily build at the root project level +that pushes a new copy of the correct version into your cache, or alternatively you could run mvn with the +`--no-snapshot-dependency` (or `-nsu`) option, which won't try to download a newer version. + +## 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 +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 the instructions. + +## Use Solr 6 with Alfresco 5.2.x +In alfresco-global.properties (depending on the RM edition `/records-management/rm-community/rm-community-repo/src/test/properties/local` or `/records-management/rm-enterprise/rm-enterprise-repo/src/test/properties/local`) +change the value for "index.subsystem.name" from "solr4" to "solr6". +Add also the following property "solr.port=8983". + +Download the latest Alfresco Search Services from +[https://nexus.alfresco.com/nexus/#nexus-search;gav\~\~alfresco-search-services\~\~\~](https://nexus.alfresco.com/nexus/#nexus-search;gav~~alfresco-search-services~~~) +Currently it's 1.0.0 (alfresco-search-services-1.0.0.zip) + +Unzip it and change to the "solr" folder within it. Start the Solr server using the following command: +``` +solr start -a "-Dcreate.alfresco.defaults=alfresco,archive" +``` +Start your repository diff --git a/README.txt b/README.txt deleted file mode 100644 index 1d260c7466..0000000000 --- a/README.txt +++ /dev/null @@ -1,155 +0,0 @@ -Configuring and starting Alfresco/Share: ----------------------------------------- - -- Clone the project (e.g. git clone git@gitlab.alfresco.com:records-management/records-management.git) - -- Import the project as a maven project - -- Start the Alfresco/Share instances with the following commands: - - mvn clean install -Pstart-repo - mvn clean install -Pstart-share - - (these commands work best if run from the specific directories, e.g. start share from - rm-enterprise/rm-enterprise-share/ or rm-community/rm-community-share/ ) - - -Configuring a different DB other than H2 (e.g. MySQL or PostgreSQL): --------------------------------------------------------------------- - -- Create a file called "local.properties" under src/main/resources in alfresco-rm-enterprise-repo - -- Add the following properties in this new file - my.db.name -> The name of the database schema - my.db.port -> The port number for your database (default port number for postgres is 5432 and for mysql it is 3306) - -- Run the following commands to start your Alfresco instance: - - to start Alfresco (using Postgres): - mvn clean install -Pstart-repo,use-postgres - - to start Alfresco (using MySQL): - mvn clean install -Pstart-repo,use-mysql - - -Technical documentation: ------------------------- - -Technical documentation is available at rm-community/documentation/README.md and rm-enterprise/documentation/README.md. -This should be particularly useful for anyone wanting to integrate with or extend RM. - - -Running integration test: -------------------------- - -In order to execute the integration tests run the following command (unit tests will be executed every time before you start Alfresco/Share): - -mvn clean install -Dskip.integrationtests=false - - -Running UI Automation tests: ----------------------------- - -To run the automated UI tests, change to the rm-automation directory and run: - - mvn clean install -Dskip.automationtests=false - -Note: due to Selenium Firefox driver changes, the highest supported Firefox version for UI tests is 43.0.4 (with Selenium 2.52.0). - -It is possible to have multiple versions of Firefox installed onto your workstation (e.g. one for running the UI tests and the other, kept -up to date, for everyday browsing) but beware Firefox auto-updates. In this scenario the best approach is to create a non-default profile -(default profiles will be shared between your Firefox installations!) for which auto-updates are disabled and forcing the use of this -profile in your tests (-Dwebdriver.firefox.profile="ProfileName"). If your Firefox 43 install isn't in your path, you can use the --Dwebdriver.firefox.profile option set to the full path of its "firefox-bin" executable. - -MacOS X Sierra users: if you experience by order of magnitude slower performance when connected to a WiFi network (e.g. office WiFi) -add your workstation to your local /etc/hosts file as described on https://github.com/SeleniumHQ/selenium/issues/2824. - -To use Chrome instead of Firefox: - - copy webdriver.properties from https://github.com/AlfrescoTestAutomation/selenium-grid/tree/master/src/main/resources - 
- put it under src/test/resource in rm-automation-ui project - 
- download the chrome driver from http://chromedriver.storage.googleapis.com/ and extract it - 
- change the following properties in webdriver.properties: webdriver.browser (Chrome) and webdriver.chrome.server.path (path/to/chrome/driver) - 
- run the tests as usual - - -Updating License Headers: -------------------------- - -In order to refesh out of date license source headers run the following command: - -mvn clean install -Dlicense.update.dryrun=false - - -Running tests against latest Aikau snapshot: --------------------------------------------- - -The latest Aikau snapshot can be pulled by running the following command in rm-community: - - mvn clean install -DskipTests -Dalfresco.aikau.version=LATEST -U - -Thereafter start the Share instance and run automation tests as described above. - - -Configuring Outlook Integration: -------------------------------- - -To download and run RM with the Outlook Integration AMPs installed on the repo and Share use the following commands: - - mvn clean install -Pstart-repo,outlook-integration - mvn clean install -Pstart-share,outlook-integration - -Follow these instructions install licence and Outlook plugin: - - - http://docs.alfresco.com/outlook2.1/tasks/Outlook-license.html - - http://docs.alfresco.com/outlook2.1/tasks/Outlook-install_v2.html - - -SNAPSHOT dependencies: ----------------------- - -If you're building Enterprise RM, the base project (Community) is pulled in via a snapshot dependency configured in maven. -This dependency will either be loaded from your local .m2 cache or from Nexus if the version in your .m2 doesn't exist or is old -(Old in maven terms is anything over 24hrs old). If maven fetches it from Nexus, your code it's unlikely to be the correct version. -You want to always use the version in your local cache - this means either doing a daily build at the root project level -that pushes a new copy of the correct version into your cache, or alternatively you could run mvn with the ---no-snapshot-dependency (or -nsu) option, which won't try to download a newer version. - - -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: ------------------- - -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. - - -Use Solr 6 with Alfresco 5.2.x: -------------------------------- -In alfresco-global.properties (depending on the RM edition /records-management/rm-community/rm-community-repo/src/test/properties/local or /records-management/rm-enterprise/rm-enterprise-repo/src/test/properties/local) -change the value for "index.subsystem.name" from "solr4" to "solr6". -Add also the following property "solr.port=8983". - -Download the latest Alfresco Search Services from -https://nexus.alfresco.com/nexus/#nexus-search;gav~~alfresco-search-services~~~ -Currently it's 1.0.0 (alfresco-search-services-1.0.0.zip) - -Unzip it and change to the "solr" folder within it. Start the Solr server using the following command: -solr start -a "-Dcreate.alfresco.defaults=alfresco,archive" - -Start your repository diff --git a/pom.xml b/pom.xml index 1858f130d1..5d5169f3ac 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 2.6.3-SNAPSHOT + 2.7.3-SNAPSHOT Alfresco Records Management @@ -24,7 +24,7 @@ scm:git:https://git.alfresco.com/records-management/records-management.git scm:git:https://git.alfresco.com/records-management/records-management.git https://git.alfresco.com/records-management/records-management - HEAD + HEAD diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index 13b5157af8..13198cafdc 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.6.3-SNAPSHOT + 2.7.3-SNAPSHOT diff --git a/rm-automation/rm-automation-community-rest-api/pom.xml b/rm-automation/rm-automation-community-rest-api/pom.xml index 68715e4554..9e5f413a04 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 - 2.6.3-SNAPSHOT + 2.7.3-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 5fbc949a45..20acd4cf57 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 @@ -121,6 +121,37 @@ public abstract class BaseAPI return results; } + /** + * Helper method to extract the property value for the given nodeRef and property name + * + * @param result + * @param nodeRef + * @param propertyName + * @return + */ + protected String getPropertyValue(JSONObject result, String nodeRef, String propertyName) + { + String propertyValue = ""; + try + { + JSONArray items = result.getJSONArray("items"); + for (int i = 0; i < items.length(); i++) + { + JSONObject item = items.getJSONObject(i); + if(nodeRef.equals(item.getString("nodeRef"))) + { + propertyValue = item.getJSONObject("properties").getString(propertyName); + } + } + } + catch (JSONException error) + { + throw new RuntimeException("Unable to parse result", error); + } + + return propertyValue; + } + /** * Helper method to extract property values from request result and put them in map as a list that corresponds to a unique property value. * @@ -310,6 +341,78 @@ public abstract class BaseAPI } } + /** + * Helper method for PUT requests + * + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param expectedStatusCode The expected return status code. + * @param requestParams zero or more endpoint specific request parameters + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected HttpResponse doPutJsonRequest(String adminUser, + String adminPassword, + int expectedStatusCode, + JSONObject requestParams, + String urlTemplate, + String... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + return doPutJsonRequest(adminUser, adminPassword, expectedStatusCode, client.getApiUrl(), requestParams, urlTemplate, urlTemplateParams); + } + + /** + * Helper method for PUT requests + * + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param expectedStatusCode The expected return status code. + * @param urlStart the start of the URL (for example "alfresco/s/slingshot"). + * @param requestParams zero or more endpoint specific request parameters + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + * @throws AssertionError if the returned status code is not as expected. + */ + private HttpResponse doPutJsonRequest(String adminUser, + String adminPassword, + int expectedStatusCode, + String urlStart, + JSONObject requestParams, + String urlTemplate, + String... urlTemplateParams) + { + String requestUrl = formatRequestUrl(urlStart, urlTemplate, urlTemplateParams); + try + { + HttpResponse httpResponse = doRequestJson(HttpPut.class, requestUrl, adminUser, adminPassword, requestParams); + assertEquals("PUT request to " + requestUrl + " was not successful.", expectedStatusCode, httpResponse.getStatusLine().getStatusCode()); + return httpResponse; + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doPutRequest failed", error); + } + } + + /** + * Fill in the parameters for a URL template. + * + * @param urlStart The start of the URL. + * @param urlTemplate The template. + * @param urlTemplateParams Any parameters that need to be filled into the URL template. + * @return The resultant URL. + */ + private String formatRequestUrl(String urlStart, String urlTemplate, String[] urlTemplateParams) + { + if (urlTemplateParams.length == 1) + { + // The format method needs some help to know not to use the whole array object. + return MessageFormat.format(urlTemplate, urlStart, urlTemplateParams[0]); + } + return MessageFormat.format(urlTemplate, urlStart, urlTemplateParams); + } + /** * Helper method for POST requests * @param adminUser user with administrative privileges @@ -403,15 +506,12 @@ public abstract class BaseAPI String urlTemplate, String... urlTemplateParams) { - // Ensure the host is part of the request URL. - String requestUrl = MessageFormat.format( - urlTemplate, - urlStart, - urlTemplateParams); + String requestUrl; + requestUrl = formatRequestUrl(urlStart, urlTemplate, urlTemplateParams); try { HttpResponse httpResponse = doRequestJson(HttpPost.class, requestUrl, adminUser, adminPassword, requestParams); - assertEquals("POST request to " + requestUrl + " was not successful.", httpResponse.getStatusLine().getStatusCode(), expectedStatusCode); + assertEquals("POST request to " + requestUrl + " was not successful.", expectedStatusCode, httpResponse.getStatusLine().getStatusCode()); return httpResponse; } catch (InstantiationException | IllegalAccessException error) @@ -453,7 +553,10 @@ public abstract class BaseAPI { ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestParams.toString())); } + LOGGER.info("Sending {} request to {}", requestType.getSimpleName(), requestUrl); + LOGGER.info("Request body: {}", requestParams); response = client.execute(adminUser, adminPassword, request); + LOGGER.info("Response: {}", response.getStatusLine()); try { @@ -573,6 +676,9 @@ public abstract class BaseAPI SHELF, BOX, FILE, + ORIGINATOR, + ORIGINATING_ORGANIZATION, + PUBLICATION_DATE } public enum RETENTION_SCHEDULE @@ -587,7 +693,7 @@ public abstract class BaseAPI RETENTION_GHOST, RETENTION_ELIGIBLE_FIRST_EVENT, RETENTION_EVENTS, - + COMBINE_DISPOSITION_STEP_CONDITIONS } /** @@ -599,6 +705,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..e54c5536de --- /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 - 2019 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/audit/AuditEntry.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/audit/AuditEntry.java new file mode 100644 index 0000000000..117f13fb76 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/audit/AuditEntry.java @@ -0,0 +1,89 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.model.audit; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.alfresco.utility.model.TestModel; + +/** + * POJO for audit entry + * + * @author Rodica Sutu + * @since 2.7 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties (ignoreUnknown = true) +public class AuditEntry extends TestModel +{ + @JsonProperty (required = true) + private String nodeName; + + @JsonProperty (required = true) + private List changedValues; + + @JsonProperty (required = true) + private String identifier; + + @JsonProperty (required = true) + private String path; + + @JsonProperty (required = true) + private String nodeRef; + + @JsonProperty (required = true) + private String fullName; + + @JsonProperty + private String createPerson; + + @JsonProperty (required = true) + private String userName; + + @JsonProperty (required = true) + private String userRole; + + @JsonProperty (required = true) + private String nodeType; + + @JsonProperty (required = true) + private String event; + + @JsonProperty (required = true) + private String timestamp; + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/audit/AuditEvents.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/audit/AuditEvents.java new file mode 100644 index 0000000000..31d05ecece --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/audit/AuditEvents.java @@ -0,0 +1,57 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.model.audit; + +/** + * Enumerates the list of events audited + * + * @author Rodica Sutu + * @since 2.7 + * + */ +public enum AuditEvents +{ + CREATE_PERSON("Create Person", "Create User"), + DELETE_PERSON("Delete Person", "Delete User"), + CREATE_USER_GROUP("Create User Group", "Create User Group"), + DELETE_USER_GROUP("Delete User Group", "Delete User Group"), + ADD_TO_USER_GROUP("Add To User Group", "Add To User Group"), + REMOVE_FROM_USER_GROUP("Remove From User Group", "Remove From User Group"), + LOGIN_UNSUCCESSFUL("Login.Failure", "Login Unsuccessful"); + + /** event audited */ + public final String event; + + /** display name for the event audited */ + public final String eventDisplayName; + + AuditEvents(String event, String displayName) + { + this.event = event; + this.eventDisplayName = displayName; + } +} 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 e8d4e344f4..5804fbdd94 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 */ @@ -74,7 +76,9 @@ public class FilePlanComponentFields public static final String PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD = "rma:recordSearchVitalRecordReviewPeriod"; public static final String PROPERTIES_RECORD_SEARCH_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION = "rma:recordSearchVitalRecordReviewPeriodExpression"; - /** Record properties */ + /** + * Record properties + */ public static final String PROPERTIES_CLASSIFICATION = "sc:classification"; public static final String PROPERTIES_DATE_FILED = "rma:dateFiled"; public static final String PROPERTIES_ORIGINAL_NAME = "rma:origionalName"; diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java index 326012606b..7abae8755d 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/site/RMSite.java @@ -42,7 +42,6 @@ import lombok.NoArgsConstructor; * @author Rodica Sutu * @since 2.6 */ -@Builder @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @@ -51,4 +50,13 @@ public class RMSite extends RestSiteModel { @JsonProperty (required = true) private RMSiteCompliance compliance; + + /** Private constructor allowing Lombok to include superclass fields in the builder. */ + @Builder + private RMSite(String title, String description, RMSiteCompliance compliance) + { + this.setTitle(title); + this.setDescription(description); + this.compliance = compliance; + } } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserCapabilities.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserCapabilities.java new file mode 100644 index 0000000000..e3c5d241b7 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserCapabilities.java @@ -0,0 +1,42 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.model.user; + +/** + * Constants for RM user capabilities + * + * @author Rodica Sutu + * @since 2.7 + */ +public class UserCapabilities +{ + + /** The id of the view records capability. */ + public static final String VIEW_RECORDS_CAP = "ViewRecords"; + /** The id of the declare records capability. */ + public static final String DECLARE_RECORDS_CAP = "DeclareRecords"; +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserRoles.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserRoles.java index 320bac6454..98b0586eb1 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserRoles.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/user/UserRoles.java @@ -24,19 +24,32 @@ * along with Alfresco. If not, see . * #L% */ + package org.alfresco.rest.rm.community.model.user; /** * Constants for RM user roles - * + * * @author Kristijan Conkas * @since 2.6 */ -public class UserRoles +public enum UserRoles { - public static final String ROLE_RM_ADMIN = "Administrator"; - public static final String ROLE_RM_MANAGER = "RecordsManager"; - public static final String ROLE_RM_POWER_USER = "PowerUser"; - public static final String ROLE_RM_SECURITY_OFFICER = "SecurityOfficer"; - public static final String ROLE_RM_USER = "User"; + IN_PLACE_WRITERS("ExtendedWriters", "In-Place Writers"), + ROLE_RM_ADMIN("Administrator", "Records Management Administrator"), + ROLE_RM_MANAGER("RecordsManager", "Records Management Manager"), + ROLE_RM_POWER_USER("PowerUser", "Records Management Power User"), + ROLE_RM_SECURITY_OFFICER("SecurityOfficer", "Records Management Security Officer"), + ROLE_RM_USER("User", "Records Management User"); + + public final String roleId; + public final String displayName; + + UserRoles(String roleId, String displayName) + { + this.roleId = roleId; + this.displayName = displayName; + } + + } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java index b9e5cb6ef2..7d0d08f98a 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/util/PojoUtility.java @@ -28,9 +28,18 @@ package org.alfresco.rest.rm.community.util; import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; +import java.io.IOException; +import java.util.List; + import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Utility class for creating the json object @@ -40,6 +49,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; */ public class PojoUtility { + /** + * Logger for the class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(PojoUtility.class); + /** * see {@link #toJson(Object, Class, Class)} */ @@ -85,4 +99,64 @@ public class PojoUtility return error.toString(); } } + + /** + * Converting json to java object + * + * @param json The json object to convert + * @param classz Class for the java object + * @return The converted java object + * @throws JsonProcessingException Throws exceptions if the given object doesn't match to the POJO class model + */ + public static T jsonToObject(JSONObject json, Class classz) + { + mandatoryObject("model", classz); + mandatoryObject("jsonObject", json); + + ObjectMapper mapper = new ObjectMapper(); + + T obj = null; + try + { + obj = mapper.readValue(json.toString(), classz); + } + catch (IOException e) + { + LOGGER.error("Unable to convert the json into a java object.", e.toString()); + } + + return obj; + } + + /** + * Converting json array into a list of java objects + * + * @param json The json array to convert + * @param classz Class for the java object + * @return The list of converted java objects + * @throws JsonProcessingException Throws exceptions if the given object doesn't match to the POJO class model + */ + public static List jsonToObject(JSONArray json, Class classz) + { + + mandatoryObject("model", classz); + mandatoryObject("jsonObject", json); + + ObjectMapper mapper = new ObjectMapper(); + + CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(List.class, classz); + List asList = null; + try + { + asList = mapper.readValue(json.toString(), collectionType); + } + catch (IOException e) + { + LOGGER.error("Unable to convert the json array into a java collection.", e.toString()); + } + + + return asList; + } + } diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/NodeAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/NodeAPI.java new file mode 100644 index 0000000000..9ed86f7e37 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/NodeAPI.java @@ -0,0 +1,74 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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 org.alfresco.dataprep.AlfrescoHttpClient; +import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.rest.core.v0.BaseAPI; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * The v0 REST API for nodes + * + * @author jcule + * @since 2.7EA1 + */ +@Component +public class NodeAPI extends BaseAPI +{ + /** Logger for the class. */ + private static final Logger LOGGER = LoggerFactory.getLogger(NodeAPI.class); + + /** The URI for the get node API. */ + private static final String GET_NODE_API = "{0}alfresco/s/slingshot/doclib2/node/{1}"; + + @Autowired + private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + + /** + * Get the node metadata using the using the node data webscript: Document List v2 Component + * + * @param username + * @param password + * @param nodeId + * @return + */ + public JSONObject getNode(String username, String password, String nodeId) + { + String requestURL; + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + requestURL = MessageFormat.format(GET_NODE_API, client.getAlfrescoUrl(), NODE_PREFIX + nodeId); + client.close(); + return doGetRequest(username, password, requestURL); + } + +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMAuditAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMAuditAPI.java new file mode 100644 index 0000000000..10c7125ec8 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMAuditAPI.java @@ -0,0 +1,104 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.testng.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.util.List; + +import org.alfresco.rest.core.v0.BaseAPI; +import org.alfresco.rest.rm.community.model.audit.AuditEntry; +import org.alfresco.rest.rm.community.util.PojoUtility; +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * The v0 REST API for rm audit logs + * + * @author Rodica Sutu + * @since 2.7 + */ +@Component +public class RMAuditAPI extends BaseAPI +{ + /** Logger for the class. */ + private static final Logger LOGGER = LoggerFactory.getLogger(RMAuditAPI.class); + + /** The URI for the audit API. */ + private static final String RM_AUDIT_API = "{0}rma/admin/rmauditlog"; + private static final String RM_AUDIT_LOG_API = RM_AUDIT_API + "?{1}"; + + /** + * Returns a list of rm audit entries . + * + * @param user The username of the user to use. + * @param password The password of the user. + * @param size Maximum number of log entries to return + * @param event The name of audit event to be retrieved + * @return return Only return log entries matching this event + */ + public List getRMAuditLog(String user, String password, final int size, final String event) + { + String parameters = null; + try + { + parameters = "size=" + size + (event != null ? "&event=" + URLEncoder.encode(event, "UTF-8"):""); + } + catch (UnsupportedEncodingException e) + { + LOGGER.error("Unable to encode the event name" + e.getMessage()); + } + JSONArray auditEntries = doGetRequest(user, password, + MessageFormat.format(RM_AUDIT_LOG_API,"{0}", parameters)).getJSONObject("data").getJSONArray("entries"); + + return PojoUtility.jsonToObject(auditEntries, AuditEntry.class); + } + + /** + * Clear the list of audit entries. + * + * @param username The username of the user to use. + * @param password The password of the user. + * @throws AssertionError If the API call didn't clear the audit log. + */ + public void clearAuditLog(String username, String password) + { + JSONObject deleteStatus = doDeleteRequest(username, password, RM_AUDIT_API); + + assertTrue(deleteStatus != null + //audit clear and login events are returned + && getRMAuditLog(username, password, 100, null).size() == 2); + } + + +} 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 a9f5a69a9e..6f07fcf3ab 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,16 +35,19 @@ 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; import java.util.Map; +import java.util.Set; 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.alfresco.rest.core.v0.RMEvents; import org.apache.chemistry.opencmis.client.api.CmisObject; import org.apache.commons.httpclient.HttpStatus; import org.apache.http.HttpResponse; @@ -70,6 +73,10 @@ import org.springframework.stereotype.Component; @Component public class RMRolesAndActionsAPI extends BaseAPI { + /** The URI to view the configured roles and capabilities. */ + private static final String RM_ROLES = "{0}rma/admin/rmroles"; + /** The URI for REST requests about a particular configured role. */ + private static final String RM_ROLES_ROLE = RM_ROLES + "/{1}"; private static final String RM_ROLES_AUTHORITIES = "{0}rm/roles/{1}/authorities/{2}?alf_ticket={3}"; // logger @@ -88,6 +95,89 @@ public class RMRolesAndActionsAPI extends BaseAPI @Autowired private ContentService contentService; + /** + * Get all the configured RM roles. + * + * @param adminUser The RM admin user. + * @param adminPassword The password of the user. + * @return The RM roles in the system (Note that this will be the internal names, not the display labels). + */ + public Set getConfiguredRoles(String adminUser, String adminPassword) + { + // Using "is=true" includes the in-place readers and writers. + JSONObject jsonObject = doGetRequest(adminUser, adminPassword, RM_ROLES + "?is=true").getJSONObject("data"); + return jsonObject.toMap().keySet(); + } + + /** + * Get the capabilities for a given role. + * + * @param adminUser The RM admin user. + * @param adminPassword The password of the user. + * @param role The role to get capabilities for. + * @return The set of system names for the capabilities. + */ + public Set getCapabilitiesForRole(String adminUser, String adminPassword, String role) + { + JSONObject jsonObject = doGetRequest(adminUser, adminPassword, RM_ROLES + "?is=true").getJSONObject("data"); + assertTrue("Could not find role '" + role + "' in " + jsonObject.keySet(), jsonObject.has(role)); + return jsonObject.getJSONObject(role).getJSONObject("capabilities").keySet(); + } + + /** + * Create a new RM role. + * + * @param adminUser The username of the admin user. + * @param adminPassword The password for the admin user. + * @param roleName The name of the new role. + * @param roleDisplayLabel A human-readable label for the role. + * @param capabilities A list of capabilities for the role. + */ + public void createRole(String adminUser, String adminPassword, String roleName, String roleDisplayLabel, Set capabilities) + { + JSONObject requestBody = new JSONObject(); + requestBody.put("name", roleName); + requestBody.put("displayLabel", roleDisplayLabel); + JSONArray capabilitiesArray = new JSONArray(); + capabilities.forEach(capabilitiesArray::put); + requestBody.put("capabilities", capabilitiesArray); + doPostJsonRequest(adminUser, adminPassword, HttpStatus.SC_OK, requestBody, RM_ROLES); + } + + /** + * Update an existing RM role. + * + * @param adminUser The username of the admin user. + * @param adminPassword The password for the admin user. + * @param roleName The name of the new role. + * @param roleDisplayLabel A human-readable label for the role. + * @param capabilities A list of capabilities for the role. + */ + public void updateRole(String adminUser, String adminPassword, String roleName, String roleDisplayLabel, Set capabilities) + { + JSONObject requestBody = new JSONObject(); + requestBody.put("name", roleName); + requestBody.put("displayLabel", roleDisplayLabel); + JSONArray capabilitiesArray = new JSONArray(); + capabilities.forEach(capabilitiesArray::put); + requestBody.put("capabilities", capabilitiesArray); + doPutJsonRequest(adminUser, adminPassword, HttpStatus.SC_OK, requestBody, RM_ROLES_ROLE, roleName); + } + + /** + * Delete a created RM role. + * + * @param adminUser The username of the admin user. + * @param adminPassword The password for the admin user. + * @param roleName The name of the role to be deleted. + */ + public void deleteRole(String adminUser, String adminPassword, String roleName) + { + doDeleteRequest(adminUser, adminPassword, MessageFormat.format(RM_ROLES_ROLE, "{0}", roleName)); + boolean success = !getConfiguredRoles(adminUser, adminPassword).contains(roleName); + assertTrue("Failed to delete role " + roleName + " with " + adminUser, success); + } + /** * create user and assign to records management role */ @@ -237,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. */ @@ -261,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 * @@ -345,6 +485,10 @@ public class RMRolesAndActionsAPI extends BaseAPI addPropertyToRequest(requestParams, "prop_cm_title", properties, RMProperty.TITLE); addPropertyToRequest(requestParams, "prop_cm_description", properties, RMProperty.DESCRIPTION); addPropertyToRequest(requestParams, "prop_cm_author", properties, RMProperty.AUTHOR); + addPropertyToRequest(requestParams, "prop_dod_originator", properties, RMProperty.ORIGINATOR); + addPropertyToRequest(requestParams, "prop_dod_originatingOrganization", properties, RMProperty + .ORIGINATING_ORGANIZATION); + addPropertyToRequest(requestParams, "prop_dod_publicationDate", properties, RMProperty.PUBLICATION_DATE); return doPostJsonRequest(username, password, SC_OK, requestParams, MessageFormat.format(UPDATE_METADATA_API, "{0}", itemNodeRef)); } 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 96b94a5f14..d49a895676 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 @@ -130,6 +130,7 @@ public class RecordCategoriesAPI extends BaseAPI { 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/RecordsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java index b62f76f3e3..ac2fe6933f 100644 --- 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 @@ -36,6 +36,7 @@ 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.apache.commons.lang3.tuple.Pair; import org.apache.http.HttpResponse; import org.json.JSONException; import org.json.JSONObject; @@ -44,8 +45,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import javafx.util.Pair; - /** * Methods to make API requests using v0 API on records * @@ -322,13 +321,13 @@ public class RecordsAPI extends BaseAPI { if (response.has("sharedId")) { - return new Pair<>(true, response.getString("sharedId")); + return Pair.of(true, response.getString("sharedId")); } } catch (JSONException e) { LOGGER.info("Unable to extract response parameter", e); } - return new Pair<>(false, String.valueOf(response.getJSONObject("status").getInt("code"))); + return Pair.of(false, String.valueOf(response.getJSONObject("status").getInt("code"))); } /** 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 76e82cd8db..d93c51e910 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 @@ -63,9 +63,9 @@ public class SearchAPI extends BaseAPI /** RM search URL template */ private static final String RM_SEARCH_ENDPOINT = "{0}alfresco/s/slingshot/rmsearch/{1}?{2}"; - /** 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/{0},categories/{1},frozen/false,cutoff/false"; /** * Perform search request on search endpoint as a user. @@ -91,6 +91,7 @@ public class SearchAPI extends BaseAPI * @param site * @param query * @param filters + * @param sortby * @return search results (see API reference for more details), null for any errors */ public JSONObject rmSearch( @@ -98,11 +99,16 @@ public class SearchAPI extends BaseAPI String password, String site, String query, - String filters) + String filters, + String sortby) { List searchParameters = new ArrayList(); searchParameters.add(new BasicNameValuePair("query", query)); searchParameters.add(new BasicNameValuePair("filters", filters)); + if (sortby != null) + { + searchParameters.add(new BasicNameValuePair("sortby", sortby)); + } String requestURL = MessageFormat.format( RM_SEARCH_ENDPOINT, @@ -114,22 +120,45 @@ public class SearchAPI extends BaseAPI } /** - * Search as a user for records on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS + * Search as a user for nodes on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS and sorted + * by sortby *
- * If more fine-grained control of search parameters is required, use rmSearch() directly. + * * @param username * @param password * @param query - * @return list of record names + * @param sortby + * @return list of node names */ - public List searchForRecordsAsUser( - String username, - String password, - String query) + + public List searchForNodeNamesAsUser(String username, String password, String query, String sortby, + boolean includeCategories, boolean includeFolders) { - return getItemNames(rmSearch(username, password, "rm", query, RM_DEFAULT_RECORD_FILTERS)); + String searchFilterParamaters = MessageFormat.format(RM_DEFAULT_NODES_FILTERS, Boolean.toString(includeFolders), + Boolean.toString(includeCategories)); + return getItemNames(rmSearch(username, password, "rm", query, searchFilterParamaters, sortby)); } + /** + * Search as a user for nodes on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS and sorted + * by sortby and returns the property value for the given nodeRef and property name + * + * @param username + * @param password + * @param query + * @param sortby + * @param includeCategories + * @param includeFolders + * @return list of node properties + */ + public String searchForNodePropertyAsUser(String username, String password, String nodeRef, String propertyName, String query, String sortby, + boolean includeCategories, boolean includeFolders) + { + String searchFilterParamaters = MessageFormat.format(RM_DEFAULT_NODES_FILTERS, Boolean.toString(includeFolders), + Boolean.toString(includeCategories)); + return getItemProperty(rmSearch(username, password, "rm", query, searchFilterParamaters, sortby), nodeRef, propertyName); + } + /** * Generic faceted search. * @param username @@ -199,12 +228,35 @@ public class SearchAPI extends BaseAPI /** * Helper method to extract list of names from search result. + * * @param searchResult * @return list of document or record names in search result + * @throws FileNotFoundException + * @throws JsonSyntaxException + * @throws JsonIOException * @throws RuntimeException for malformed search response */ + /** + * Helper method to extract list of names from search result. + * + * @param searchResult + * @param getProperties + * @return + */ private List getItemNames(JSONObject searchResult) { return getPropertyValues(searchResult, "name"); } + + /** + * Helper method to extract list of property values from search result for the given nodeRef. + * + * @param searchResult + * @param getProperties + * @return + */ + private String getItemProperty(JSONObject searchResult, String nodeRef, String propertyName) + { + return getPropertyValue(searchResult, nodeRef, propertyName); + } } 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 index 6de77ec194..67fb23a68e 100644 --- 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 @@ -116,6 +116,34 @@ public class DispositionScheduleService extends BaseAPI 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 diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/RoleService.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/RoleService.java new file mode 100644 index 0000000000..ccf2785cd8 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/service/RoleService.java @@ -0,0 +1,84 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.HashSet; +import java.util.Set; + +import org.alfresco.rest.rm.community.model.user.UserRoles; +import org.alfresco.rest.v0.RMRolesAndActionsAPI; +import org.alfresco.utility.data.DataUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Produces processed results from roles API calls + * + * @author Rodica Sutu + * @since 2.6 + */ +@Service +public class RoleService +{ + @Autowired + private RMRolesAndActionsAPI rmRolesAndActionsAPI; + + @Autowired + private DataUser dataUser; + + /** + * Add capabilities to a role + * + * @param role role to be updated + * @param capabilities list of capabilities to be added + */ + public void addCapabilitiesToRole(UserRoles role, Set capabilities) + { + Set roleCapabilities = new HashSet<>(); + roleCapabilities.addAll(rmRolesAndActionsAPI.getCapabilitiesForRole(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), role.roleId)); + capabilities.stream().forEach(cap -> roleCapabilities.add(cap)); + + rmRolesAndActionsAPI.updateRole(dataUser.getAdminUser().getUsername(), dataUser.getAdminUser().getPassword(), + role.roleId, role.displayName, roleCapabilities); + } + + /** + * Remove capabilities from a role + * + * @param role role to be updated + * @param capabilities list of capabilities to be removed + */ + public void removeCapabilitiesFromRole(UserRoles role, Set capabilities) + { + Set roleCapabilities = rmRolesAndActionsAPI.getCapabilitiesForRole(dataUser.getAdminUser().getUsername(), + dataUser.getAdminUser().getPassword(), role.roleId); + roleCapabilities.removeAll(capabilities); + rmRolesAndActionsAPI.updateRole(dataUser.getAdminUser().getUsername(), dataUser.getAdminUser().getPassword(), + role.roleId, role.displayName, roleCapabilities); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditGroupEventsTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditGroupEventsTests.java new file mode 100644 index 0000000000..3443630bb6 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditGroupEventsTests.java @@ -0,0 +1,174 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.audit; + +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.ADD_TO_USER_GROUP; +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_USER_GROUP; +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.DELETE_USER_GROUP; +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.REMOVE_FROM_USER_GROUP; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.List; + +import com.google.common.collect.ImmutableMap; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.audit.AuditEntry; +import org.alfresco.rest.v0.RMAuditAPI; +import org.alfresco.test.AlfrescoTest; +import org.alfresco.utility.model.GroupModel; +import org.alfresco.utility.model.UserModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * This class contains the tests that check the group events are audited + * + * @author Claudia Agache + * @since 2.7 + */ +@AlfrescoTest (jira = "RM-5236") +public class AuditGroupEventsTests extends BaseRMRestTest +{ + @Autowired + private RMAuditAPI rmAuditAPI; + + private GroupModel testGroup; + private UserModel testUser; + + @BeforeClass (alwaysRun = true) + public void cleanAuditLogs() + { + //clean audit logs + rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword()); + } + + /** + * Given I have created a new group + * When I view the RM audit + * Then there is an entry showing that I created a group + */ + @Test + public void createGroupEventIsAudited() + { + testGroup = dataGroup.createRandomGroup(); + + STEP("Get the list of audit entries for the create group event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, CREATE_USER_GROUP.event); + + STEP("Check the audit log contains only the entries for the created group."); + assertTrue("The list of events is not filtered by " + CREATE_USER_GROUP.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(CREATE_USER_GROUP.eventDisplayName))); + + assertTrue("The group name for the new group created is not audited.", + auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(CREATE_USER_GROUP.eventDisplayName)) + .anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier()))); + } + + /** + * Given I have added a user to a group + * When I view the RM audit + * Then there is an entry showing that I have added a user to a group + */ + @Test + public void addUserToGroupEventIsAudited() + { + testGroup = dataGroup.createRandomGroup(); + testUser = getDataUser().createRandomTestUser(); + dataGroup.usingUser(testUser).addUserToGroup(testGroup); + + STEP("Get the list of audit entries for the add user to group event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, ADD_TO_USER_GROUP.event); + + STEP("Check the audit log contains only the entries for the add user to group event."); + assertTrue("The list of events is not filtered by " + ADD_TO_USER_GROUP.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(ADD_TO_USER_GROUP.eventDisplayName))); + + assertTrue("The username and destination group are not audited.", + auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(ADD_TO_USER_GROUP.eventDisplayName)) + .anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier()) + && auditEntry.getChangedValues().contains(ImmutableMap.of("new", testUser.getUsername(), "previous", "", "name", "User Name")) + && auditEntry.getChangedValues().contains(ImmutableMap.of("new", testGroup.getGroupIdentifier(), "previous", "", "name", "Parent Group")))); + } + + /** + * Given I have removed a user from a group + * When I view the RM audit + * Then there is an entry showing that I have removed a user from a group + */ + @Test + public void removeUserFromGroupEventIsAudited() + { + testGroup = dataGroup.createRandomGroup(); + testUser = getDataUser().createRandomTestUser(); + dataGroup.usingUser(testUser).addUserToGroup(testGroup); + dataGroup.removeUserFromGroup(testGroup, testUser); + + STEP("Get the list of audit entries for the add user to group event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, REMOVE_FROM_USER_GROUP.event); + + STEP("Check the audit log contains only the entries for the remove user from group event."); + assertTrue("The list of events is not filtered by " + REMOVE_FROM_USER_GROUP.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(REMOVE_FROM_USER_GROUP.eventDisplayName))); + + assertTrue("The username and previous parent group are not audited.", + auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(REMOVE_FROM_USER_GROUP.eventDisplayName)) + .anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier()) + && auditEntry.getChangedValues().contains(ImmutableMap.of("new", "", "previous", testUser.getUsername(), "name", "User Name")) + && auditEntry.getChangedValues().contains(ImmutableMap.of("new", "","previous", testGroup.getGroupIdentifier(), "name", "Parent Group")))); + } + + /** + * Given I have deleted a group + * When I view the RM audit + * Then there is an entry showing that I have deleted a group + */ + @Test + public void deleteGroupEventIsAudited() + { + testGroup = dataGroup.createRandomGroup(); + dataGroup.deleteGroup(testGroup); + + STEP("Get the list of audit entries for the delete group event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, DELETE_USER_GROUP.event); + + STEP("Check the audit log contains only the entries for the created group."); + assertTrue("The list of events is not filtered by " + DELETE_USER_GROUP.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(DELETE_USER_GROUP.eventDisplayName))); + + assertTrue("The group name for the deleted group is not audited.", + auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(DELETE_USER_GROUP.eventDisplayName)) + .anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier()) + && auditEntry.getChangedValues().contains(ImmutableMap.of("new", "", "previous", testGroup.getGroupIdentifier(), "name", "authorityDisplayName")))); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditLoginEvents.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditLoginEvents.java new file mode 100644 index 0000000000..1b86d8b3ef --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditLoginEvents.java @@ -0,0 +1,82 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.audit; + +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.LOGIN_UNSUCCESSFUL; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.List; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.audit.AuditEntry; +import org.alfresco.rest.v0.RMAuditAPI; +import org.alfresco.test.AlfrescoTest; +import org.alfresco.utility.model.UserModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * This class contains the tests that check the login events are audited + * + * @author Claudia Agache + * @since 2.7 + */ +@AlfrescoTest (jira = "RM-5234") +public class AuditLoginEvents extends BaseRMRestTest +{ + @Autowired + private RMAuditAPI rmAuditAPI; + + @BeforeClass (alwaysRun = true) + public void cleanAuditLogs() + { + //clean audit logs + rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword()); + } + + /** + * Given I have tried to login using invalid credentials + * When I view the RM audit filtered by Login unsuccessful event + * Then the audit log contains only the entries for the Login unsuccessful event + */ + @Test + public void filterByLoginUnsuccessful() throws Exception + { + restClient.authenticateUser(new UserModel(getAdminUser().getUsername(), "InvalidPassword")); + restClient.withCoreAPI().getSites(); + + STEP("Get the list of audit entries for the login unsuccessful event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, LOGIN_UNSUCCESSFUL.event); + + STEP("Check the audit log contains only the entries for the login unsuccessful event."); + assertTrue("The list of events is not filtered by " + LOGIN_UNSUCCESSFUL.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(LOGIN_UNSUCCESSFUL.eventDisplayName))); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditUserEventsTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditUserEventsTests.java new file mode 100644 index 0000000000..a71094bcd7 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditUserEventsTests.java @@ -0,0 +1,101 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.audit; + +import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_PERSON; +import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.testng.AssertJUnit.assertTrue; + +import java.util.List; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.audit.AuditEntry; +import org.alfresco.rest.v0.RMAuditAPI; +import org.alfresco.test.AlfrescoTest; +import org.alfresco.utility.model.UserModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * This class contains the tests that check the user events are audited + * + * @author Rodica Sutu + * @since 2.7 + */ +public class AuditUserEventsTests extends BaseRMRestTest +{ + private final String PREFIX = generateTestPrefix(AuditUserEventsTests.class); + + private UserModel createUser; + @Autowired + private RMAuditAPI rmAuditAPI; + + /** + * Given I have created a new user + * When I view the RM audit + * Then there is an entry showing that I created a user + * + * @throws Exception + */ + @Test + @AlfrescoTest(jira = "RM-6223") + public void createUserEventIsAudited() throws Exception + { + STEP("Create a new user."); + String userName = "auditCreateUser" + PREFIX; + createUser = getDataUser().createUser(userName); + + STEP("Get the list of audit entries for the create person event."); + List auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(), + getAdminUser().getPassword(), 100, CREATE_PERSON.event); + + STEP("Check the audit log contains only the entries for the created user."); + assertTrue("The list of events is not filtered by " + CREATE_PERSON.event, + auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(CREATE_PERSON.eventDisplayName))); + + assertTrue("The username value for the user created is not audited.", + auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(CREATE_PERSON.eventDisplayName)) + .allMatch(auditEntry -> auditEntry.getNodeName().equals(userName))); + } + + @BeforeClass (alwaysRun = true) + public void cleanAuditLogs() + { + //clean audit logs + rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword()); + } + + @AfterClass (alwaysRun = true) + public void cleanUp() + { + //delete the created user + getDataUser().deleteUser(createUser); + } +} 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 1cdfe48a87..a95b4e04b3 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 @@ -65,6 +65,7 @@ import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderProperties; +import org.alfresco.rest.rm.community.model.site.RMSite; import org.alfresco.rest.rm.community.model.transfercontainer.TransferContainer; import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer; import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; @@ -76,6 +77,7 @@ import org.alfresco.rest.search.RestRequestQueryModel; import org.alfresco.rest.search.SearchNodeModel; import org.alfresco.rest.search.SearchRequest; import org.alfresco.rest.v0.RMRolesAndActionsAPI; +import org.alfresco.rest.v0.SearchAPI; import org.alfresco.utility.data.DataUser; import org.alfresco.utility.model.ContentModel; import org.alfresco.utility.model.FolderModel; @@ -112,7 +114,11 @@ public class BaseRMRestTest extends RestTest @Autowired @Getter(value = PROTECTED) private RMRolesAndActionsAPI rmRolesAndActionsAPI; - + + @Autowired + @Getter(value = PROTECTED) + private SearchAPI searchApi; + /** * Asserts the given status code * @@ -178,6 +184,21 @@ public class BaseRMRestTest extends RestTest } } + /** + * Helper method to delete the RM site if exists and to create a new one + */ + public void createRMSite(RMSite rmSiteModel) throws Exception + { + RMSiteAPI rmSiteAPI = getRestAPIFactory().getRMSiteAPI(); + if (rmSiteAPI.existsRMSite()) + { + rmSiteAPI.deleteRMSite(); + } + + rmSiteAPI.createRMSite(rmSiteModel); + assertStatusCode(CREATED); + } + /** * Helper method to create root category as the admin user * @@ -489,7 +510,7 @@ public class BaseRMRestTest extends RestTest { return getFilePlanAsUser(getAdminUser(), componentId); } - + /** * Recursively delete a folder * @@ -570,6 +591,17 @@ public class BaseRMRestTest extends RestTest RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI(); recordFolderAPI.deleteRecordFolder(recordFolderId); } + + /** + * Delete a record + * + * @param recordId the id of the record to delete + */ + public void deleteRecord(String recordId) + { + RecordsAPI recordsAPI = restAPIFactory.getRecordsAPI(); + recordsAPI.deleteRecord(recordId); + } /** * Delete a record category @@ -637,7 +669,8 @@ public class BaseRMRestTest extends RestTest names.add(childNode.onModel().getName()); }); break; - } else + } + else { counter++; } @@ -647,6 +680,100 @@ public class BaseRMRestTest extends RestTest return names; } + /** + * Returns the list of node names returned by search results for the given search term + * + * @param user + * @param term + * @param sortby + * @param includeFolders + * @param includeCategories + * @param expectedResults + * @return List + */ + public List searchForRMContentAsUser(UserModel user, String term, String sortby, boolean includeFolders, + boolean includeCategories, List expectedResults) + { + List results = new ArrayList<>(); + // wait for solr indexing + int counter = 0; + int waitInMilliSeconds = 6000; + while (counter < 3) + { + synchronized (this) + { + try + { + this.wait(waitInMilliSeconds); + } + catch (InterruptedException e) + { + } + } + results = searchApi.searchForNodeNamesAsUser(user.getUsername(), user.getPassword(), term, sortby, + includeFolders, includeCategories); + if (!results.isEmpty() && results.containsAll(expectedResults)) + { + break; + } + else + { + counter++; + } + // double wait time to not overdo solr search + waitInMilliSeconds = (waitInMilliSeconds * 2); + } + return results; + } + + /** + * Returns the property value for the given property name and nodeRef of the search results + * + * @param user + * @param term + * @param nodeRef + * @param propertyName + * @param sortby + * @param includeFolders + * @param includeCategories + * @param expectedResults + * @return String + */ + public String searchForRMContentAsUser(UserModel user, String term, String nodeRef, String propertyName, + String sortby, boolean includeFolders, boolean includeCategories, String expectedResults) + { + String result = ""; + // wait for solr indexing + int counter = 0; + int waitInMilliSeconds = 6000; + while (counter < 3) + { + synchronized (this) + { + try + { + this.wait(waitInMilliSeconds); + } + catch (InterruptedException e) + { + } + } + result = searchApi.searchForNodePropertyAsUser(user.getUsername(), user.getPassword(), nodeRef, + propertyName, term, sortby, includeFolders, includeCategories); + if (!result.isEmpty() && result.contains(expectedResults)) + { + break; + } + else + { + counter++; + } + // double wait time to not overdo solr search + waitInMilliSeconds = (waitInMilliSeconds * 2); + } + return result; + } + /** * Helper method to return site document library content model * @@ -668,17 +795,4 @@ public class BaseRMRestTest extends RestTest return documentLibrary; } - /** - * Helper method to create a Content Model - * - * @return ContentModel - * @throws Exception - */ - public ContentModel toContentModel(String nodeId) - { - ContentModel node = new ContentModel(); - node.setNodeRef(nodeId); - return node; - } - } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java index 0027a25f37..6feb052b01 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/TestData.java @@ -26,8 +26,17 @@ */ package org.alfresco.rest.rm.community.base; +import static com.google.common.collect.Sets.newHashSet; + +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_ADMIN; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_MANAGER; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_POWER_USER; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_SECURITY_OFFICER; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_USER; import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; +import java.util.Set; + /** * Test data used in tests * @@ -83,4 +92,9 @@ public interface TestData public static String NONELECTRONIC_RECORD_NAME = "Record nonelectronic" + getRandomAlphanumeric(); public static final String ALFRESCO_ADMINISTRATORS = "ALFRESCO_ADMINISTRATORS"; + /** + * The ids of the default RM roles. + */ + public static final Set RM_ROLES = newHashSet(ROLE_RM_ADMIN.roleId, ROLE_RM_MANAGER.roleId, + ROLE_RM_POWER_USER.roleId, ROLE_RM_SECURITY_OFFICER.roleId, ROLE_RM_USER.roleId); } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplans/FilePlanTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplans/FilePlanTests.java index f26b7394a2..64aa7af73c 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplans/FilePlanTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/fileplans/FilePlanTests.java @@ -488,7 +488,7 @@ public class FilePlanTests extends BaseRMRestTest children.add(recordCategory); } - getRestAPIFactory().getRMUserAPI().assignRoleToUser(managerUser.getUsername(), ROLE_RM_MANAGER); + getRestAPIFactory().getRMUserAPI().assignRoleToUser(managerUser.getUsername(), ROLE_RM_MANAGER.roleId); // Get record category children from API getRestAPIFactory().getFilePlansAPI(managerUser).getRootRecordCategories(FILE_PLAN_ALIAS) .assertThat().entriesListIsEmpty().assertThat().paginationExist(); diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java index f59ed50108..077703bca5 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/files/DeclareDocumentAsRecordTests.java @@ -28,6 +28,7 @@ package org.alfresco.rest.rm.community.files; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; +import static org.alfresco.utility.report.log.Step.STEP; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; @@ -45,6 +46,7 @@ import org.alfresco.rest.rm.community.model.record.Record; import org.alfresco.rest.rm.community.model.record.RecordProperties; import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildEntry; import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; +import org.alfresco.rest.v0.RecordsAPI; import org.alfresco.test.AlfrescoTest; import org.alfresco.utility.constants.UserRole; import org.alfresco.utility.model.FileModel; @@ -54,6 +56,7 @@ import org.alfresco.utility.model.UserModel; import org.apache.chemistry.opencmis.client.api.Document; import org.apache.chemistry.opencmis.client.api.SecondaryType; import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -69,6 +72,9 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest private SiteModel testSite; private FolderModel testFolder; + @Autowired + RecordsAPI recordsAPI; + @BeforeClass(alwaysRun=true) public void declareDocumentAsRecordSetup() throws Exception { @@ -239,6 +245,27 @@ public class DeclareDocumentAsRecordTests extends BaseRMRestTest assertStatusCode(UNPROCESSABLE_ENTITY); } + /** + * Given a file that has version declared as record + * When the file is declared as record + * Then the action is successful + */ + @Test (description = "Declaring as record a file that already has its version declared as record is successful") + @AlfrescoTest (jira = "RM-6786") + public void declareAsRecordAFileWithARecordVersion() throws Exception + { + STEP("Create a file."); + FileModel testFile = dataContent.usingSite(testSite).createContent(CMISUtil.DocumentType.TEXT_PLAIN); + + STEP("Declare file version as record and check that record is successfully created."); + recordsAPI.declareDocumentVersionAsRecord(getAdminUser().getUsername(), getAdminUser().getPassword(), testSite.getId(), + testFile.getName()); + + STEP("Declare file as record and check that record is successfully created."); + getRestAPIFactory().getFilesAPI().declareAsRecord(testFile.getNodeRefWithoutVersion()); + assertStatusCode(CREATED); + } + // @Test(description = "Create 500 documents and declare them ass records concurently.") // public void declare500DocumentsAsRecordsConcurrently() throws Exception // { diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java index 1ad927b618..d5ca79d7e3 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java @@ -221,17 +221,6 @@ public class CompleteRecordTests extends BaseRMRestTest } } - /** - * Helper method to create an RM site and assert successful creation - */ - private void createRMSite(RMSite rmSiteModel) throws Exception - { - RMSiteAPI rmSiteAPI = getRestAPIFactory().getRMSiteAPI(); - rmSiteAPI.deleteRMSite(); - rmSiteAPI.createRMSite(rmSiteModel); - assertStatusCode(CREATED); - } - /** * Helper method to create records and and assert successful creation */ diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/DeleteRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/DeleteRecordTests.java index a091b54d01..ef63360a39 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/DeleteRecordTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/DeleteRecordTests.java @@ -211,7 +211,7 @@ public class DeleteRecordTests extends BaseRMRestTest getDataUser().addUserToSite(deleteUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), SiteCollaborator); // Add RM role to user - getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER); + getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER.roleId); assertStatusCode(OK); // Try to delete newRecord @@ -242,7 +242,7 @@ public class DeleteRecordTests extends BaseRMRestTest logger.info("Test user: " + username); // Add RM role to user, RM Power User doesn't have the "Delete Record" capabilities - getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER); + getRestAPIFactory().getRMUserAPI().assignRoleToUser(username, ROLE_RM_POWER_USER.roleId); assertStatusCode(OK); // Create random folder diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/UpdateRecordsTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/UpdateRecordsTests.java index b056ff009b..2f8f3af510 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/UpdateRecordsTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/UpdateRecordsTests.java @@ -32,6 +32,7 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_SECURITY_OFFICER; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicUnfiledContainerChildModel; @@ -58,7 +59,6 @@ import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChi import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection; import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties; import org.alfresco.rest.rm.community.model.user.UserPermissions; -import org.alfresco.rest.rm.community.model.user.UserRoles; import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; @@ -240,7 +240,7 @@ public class UpdateRecordsTests extends BaseRMRestTest getDataUser().addUserToSite(updateUser, new SiteModel(getRestAPIFactory().getRMSiteAPI().getSite().getId()), UserRole.SiteCollaborator); // RM Security Officer is the lowest role with Edit Record Metadata capabilities - rmUserAPI.assignRoleToUser(updateUser.getUsername(), UserRoles.ROLE_RM_SECURITY_OFFICER); + rmUserAPI.assignRoleToUser(updateUser.getUsername(), ROLE_RM_SECURITY_OFFICER.roleId); assertStatusCode(OK); // Create random folder diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/rmroles/RMRolesTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/rmroles/RMRolesTests.java new file mode 100644 index 0000000000..1f30107cbd --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/rmroles/RMRolesTests.java @@ -0,0 +1,112 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.rm.community.rmroles; + +import static java.util.Collections.singleton; + +import static com.google.common.collect.Sets.newHashSet; + +import static org.alfresco.rest.rm.community.base.TestData.RM_ROLES; +import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_USER; +import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Set; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.user.UserCapabilities; +import org.alfresco.rest.v0.RMRolesAndActionsAPI; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.Test; + +/** + * API tests of RM roles. + * + * @author Tom Page + * @since 2.7 + */ +public class RMRolesTests extends BaseRMRestTest +{ + /** A list of capabilities. */ + private static final java.util.HashSet CAPABILITIES = newHashSet(UserCapabilities.VIEW_RECORDS_CAP, UserCapabilities.DECLARE_RECORDS_CAP); + /** The API for managing RM roles and capabilities. */ + @Autowired + private RMRolesAndActionsAPI rmRolesAndActionsAPI; + + /** Check that the roles API returns the default RM roles. */ + @Test(description = "Check the default RM roles exist.") + public void checkRMRolesExist() + { + Set configuredRoles = rmRolesAndActionsAPI + .getConfiguredRoles(getAdminUser().getUsername(), getAdminUser().getPassword()); + RM_ROLES.forEach(role -> assertTrue("Could not found role " + role, configuredRoles.contains(role))); + } + + /** Check that the RM user has the capability to view and declare records. */ + @Test(description = "Check the capabilities for the RM user.") + public void checkCapabilitiesForUser() + { + Set capabilities = rmRolesAndActionsAPI + .getCapabilitiesForRole(getAdminUser().getUsername(), getAdminUser().getPassword(), ROLE_RM_USER + .roleId); + assertEquals("Unexpected capabilities found for RM User.", capabilities, CAPABILITIES); + } + + /** Check that a new role can be created and retrieved. */ + @Test(description = "Create a new role.") + public void createNewRole() + { + String roleName = generateTestPrefix(RMRolesTests.class) + "newName"; + + // Call the endpoint under test. + rmRolesAndActionsAPI.createRole(getAdminUser().getUsername(), getAdminUser().getPassword(), roleName, + "New Role Label", CAPABILITIES); + + Set actualCapabilities = rmRolesAndActionsAPI + .getCapabilitiesForRole(getAdminUser().getUsername(), getAdminUser().getPassword(), roleName); + assertEquals("Unexpected capabilities found for RM User.", actualCapabilities, CAPABILITIES); + } + + /** Check that a role can be edited. */ + @Test(description = "Update a role.") + public void updateRole() + { + String roleName = generateTestPrefix(RMRolesTests.class) + "Name"; + rmRolesAndActionsAPI.createRole(getAdminUser().getUsername(), getAdminUser().getPassword(), roleName, "Label", + singleton(UserCapabilities.VIEW_RECORDS_CAP)); + + // Call the endpoint under test. + rmRolesAndActionsAPI.updateRole(getAdminUser().getUsername(), getAdminUser().getPassword(), roleName, + "Updated Label", singleton(UserCapabilities.DECLARE_RECORDS_CAP)); + + Set actualCapabilities = rmRolesAndActionsAPI + .getCapabilitiesForRole(getAdminUser().getUsername(), getAdminUser().getPassword(), roleName); + assertEquals("Unexpected capabilities for edited RM User.", actualCapabilities, singleton(UserCapabilities.DECLARE_RECORDS_CAP)); + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java index 13ac18a8ec..841b6b194b 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/site/RMSiteTests.java @@ -238,9 +238,7 @@ public class RMSiteTests extends BaseRMRestTest createRMSiteIfNotExists(); // Create RM site model - RMSite rmSiteToUpdate = RMSite.builder().build(); - rmSiteToUpdate.setTitle(NEW_TITLE); - rmSiteToUpdate.setDescription(NEW_DESCRIPTION); + RMSite rmSiteToUpdate = RMSite.builder().title(NEW_TITLE).description(NEW_DESCRIPTION).build(); // Create the RM site getRestAPIFactory().getRMSiteAPI(getDataUser().createRandomTestUser("testUser")).updateRMSite(rmSiteToUpdate); diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/CoreUtil.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/CoreUtil.java index 58daa6faa5..cc0c7c5bfd 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/CoreUtil.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/CoreUtil.java @@ -27,6 +27,7 @@ package org.alfresco.rest.rm.community.utils; import org.alfresco.rest.model.RestNodeBodyMoveCopyModel; +import org.alfresco.utility.model.ContentModel; /** * Utility class for core components models @@ -53,4 +54,17 @@ public class CoreUtil moveDestinationInfo.setTargetParentId(nodeId); return moveDestinationInfo; } + + /** + * Helper method to create a Content Model + * + * @return ContentModel + * @throws Exception + */ + public static ContentModel toContentModel(String nodeId) + { + ContentModel node = new ContentModel(); + node.setNodeRef(nodeId); + return node; + } } diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/RMSiteUtil.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/RMSiteUtil.java index 0e53a12810..b3574a0fcb 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/RMSiteUtil.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/RMSiteUtil.java @@ -60,10 +60,7 @@ public class RMSiteUtil */ public static RMSite createRMSiteModel(RMSiteCompliance compliance, String title, String description) { - RMSite rmSiteModel = RMSite.builder().compliance(compliance).build(); - rmSiteModel.setTitle(title); - rmSiteModel.setDescription(description); - return rmSiteModel; + return RMSite.builder().compliance(compliance).title(title).description(description).build(); } /** diff --git a/rm-community/documentation/PatchService.md b/rm-community/documentation/PatchService.md new file mode 100644 index 0000000000..3472dde7fe --- /dev/null +++ b/rm-community/documentation/PatchService.md @@ -0,0 +1,14 @@ +# RM Patch Service + +The RM Patch service operates independently of the Core Patch service & behaves differently. + +Schema numbering is sequential, it’s a 4 digit number, prefixed with the major/minor version number, e.g. schema from a 2.4 version will be 24xx. This is a different policy to the core numbering (which bumps the schema number by 10 for each release). + +Patches run in a single transaction. They may process data in batches, but it’s all wrapped in a single transaction, which is rolled back if the patch fails or is interrupted. AbstractModulePatch#245. When we implement applyInternal within a patch, that whole method runs inside a transaction. + +DB Schema numbers update only after every patch runs. This means if a patch fails, earlier patches will re run. (see: ModulePatchExecuterImpl.executeInternal#140). This behaviour is different than core’s behaviour, which updates the schema number after each successful patch. + +DB Schema number is stored in the attribute service (key: “module-schema”) against the RM’s module ID. This is not exposed in the UI. Nor in a REST API. The attribute service stores it directly in the DB, so isn’t even accessible via the node browser. +If a customer wants to determine the schema number for a running system, they’ll need to execute a DB query. + +It's possible to configure a patch not to run if being upgraded from a earlier schema version by setting `fixesFromSchema` in the patch config xml. diff --git a/rm-community/documentation/README.md b/rm-community/documentation/README.md index 2ad570b4f3..b4b7911330 100644 --- a/rm-community/documentation/README.md +++ b/rm-community/documentation/README.md @@ -2,3 +2,28 @@ * [Enterprise Technical Documentation](../../rm-enterprise/documentation/README.md) (the link will only work if this repository contains the enterprise code) * [Overview of the design of RM](overview.md) +* Records Management + * File Plan + * List of Values + * Records + * EMail Records + * Filed and Unfiled Records + * Easy Access Records + * Physical Records + * Record Import and Export + * Version Records + * Retention + * [Destruction](./destruction) + * Retention Schedules and Events + * Transfer and Accession +* Security + * [Extended permission service](extendedPermissionService.md) + * Roles, Capabilities and Permissions +* Discovery + * Governance Search + * Legal Holds +* Compliance + * Governance Audit + * Governance Rules +* Core Module Services + * [RM Patch Service](./PatchService.md) diff --git a/rm-community/documentation/destruction/README.md b/rm-community/documentation/destruction/README.md new file mode 100644 index 0000000000..b43c16bf18 --- /dev/null +++ b/rm-community/documentation/destruction/README.md @@ -0,0 +1,60 @@ +## Destruction + +### Purpose + +Ensure the immediate and permanent destruction of sensitive content. + +This includes: + + * Records + * Classified content + +### Overview + +Sensitive content is immediately deleted from the content store. It does not get added to the trashcan or any other recoverable location and as such should not be recoverable. + +It is possible to configure the component to include a cleansing step prior to content deletion. This allows the binary content to be repeatedly overwritten prior to deletion to make it harder to forensically recover the binary data. + +Recorded content can be explicitly destroyed whilst maintaining the original node and associated meta-data. This is configured as a characteristic of the destruction step within a retention schedule. + +### Artifacts and Guidance + +* Source Code Link: [GitLab](https://gitlab.alfresco.com/records-management/records-management/tree/master) +* License: Alfresco Community +* Issue Tracker Link: [JIRA RM](https://issues.alfresco.com/jira/projects/RM/summary) +* Contribution Model: Alfresco Closed Source +* Documentation: [docs.alfresco.com (Records Management)](http://docs.alfresco.com/rm2.4/concepts/welcome-rm.html) + +*** + +### Design + +#### Component Model + +#### Content Model + +* uri - http://www.alfresco.org/model/recordsmanagement/1.0 +* prefix - rma +* rma:ghosted - aspect that indicates that a records content has been destroyed, but the records meta-data is still available. + +#### Flows + +![Alfresco Destruction Flow Diagram](./resource/sequence/destruction-sequence.png) + +#### Class Diagram + +![Alfresco Destruction Class Diagram](./resource/class/destruction-class.png) + +*** + +### Interfaces and APIs + +*** + +### Configuration + +*** + +### Considerations + +*** diff --git a/rm-community/documentation/destruction/resource/class/destruction-class.png b/rm-community/documentation/destruction/resource/class/destruction-class.png new file mode 100644 index 0000000000..1b8b0e3832 Binary files /dev/null and b/rm-community/documentation/destruction/resource/class/destruction-class.png differ diff --git a/rm-community/documentation/destruction/resource/class/destruction-class.puml b/rm-community/documentation/destruction/resource/class/destruction-class.puml new file mode 100644 index 0000000000..f2377c0ffb --- /dev/null +++ b/rm-community/documentation/destruction/resource/class/destruction-class.puml @@ -0,0 +1,43 @@ +@startuml + +DestroyAction --> ContentDestructionComponent +ContentDestructionComponent <|-- ExtendedContentDestructionComponent +ContentDestructionComponent --> EagerContentStoreCleaner +EagerContentStoreCleaner --> ContentCleanser +ContentCleanser <|-- ContentCleanser522022M +ContentCleanser +-- OverwriteOperation + +class DestroyAction { + + boolean ghostingEnabled +} + +class ContentDestructionComponent { + + boolean cleansingEnabled + + void destroyContent(NodeRef nodeRef) + + void destroyContent(NodeRef nodeRef, boolean includeRenditions) + + void registerAllContentForDestruction(NodeRef nodeRef, boolean clearContentProperty) +} + +class ExtendedContentDestructionComponent { + + void onBeforeNodeDelete(NodeRef nodeRef) +} + +class EagerContentStoreCleaner { + + void registerOrphanedContentUrlForCleansing(String contentUrl) + # boolean deleteFromStore(String contentUrl, ContentStore store) +} + +abstract class ContentCleanser { + # OverwriteOperation overwriteZeros + # OverwriteOperation overwriteOnes + # OverwriteOperation overwriteRandom + + {abstract} void cleanse(File file) + # void overwrite(File file, OverwriteOperation overwriteOperation) +} + +abstract class OverwriteOperation { + + {abstract} void operation(OutputStream os) throws IOException +} + +@enduml + diff --git a/rm-community/documentation/destruction/resource/sequence/destruction-sequence.png b/rm-community/documentation/destruction/resource/sequence/destruction-sequence.png new file mode 100644 index 0000000000..06d4fb4426 Binary files /dev/null and b/rm-community/documentation/destruction/resource/sequence/destruction-sequence.png differ diff --git a/rm-community/documentation/destruction/resource/sequence/destruction-sequence.puml b/rm-community/documentation/destruction/resource/sequence/destruction-sequence.puml new file mode 100644 index 0000000000..3dc04692c2 --- /dev/null +++ b/rm-community/documentation/destruction/resource/sequence/destruction-sequence.puml @@ -0,0 +1,40 @@ +@startuml + +Title: Content Destruction and Cleansing Flow + +participant "Repository" as R +participant "Behaviour" as B +participant "ContentDestructionComponent" as CDC +participant "EagerContentStoreCleaner" as ECSC +participant ConentCleanser as CC +participant ContentStore as CS + +R->B:beforeNodeDelete +activate B + +note right of B: sensitive content +B->CDC:registerAllContentForDestruction +deactivate B +activate CDC +note right of CDC: cleansing enabled + +CDC->ECSC:registerOrphanedContentUrlForCleansing +deactivate CDC +activate ECSC + +ECSC->ECSC: registerOrphanedContentUrl + +R->ECSC:afterCommit + +ECSC->CC:cleanse +activate CC +CC->ECSC +deactivate CC + +ECSC->CS:delete +activate CS +CS->ECSC +deactivate CS +deactivate ECSC + +@enduml \ No newline at end of file diff --git a/rm-community/documentation/extendedPermissionService.md b/rm-community/documentation/extendedPermissionService.md new file mode 100644 index 0000000000..abc7d4a7d5 --- /dev/null +++ b/rm-community/documentation/extendedPermissionService.md @@ -0,0 +1,68 @@ +## Alfresco Governance Services' Extended Permission Service + +![Completeness Badge](https://img.shields.io/badge/Document_Level-Complete-green.svg?style=flat-square) + +![Version Badge](https://img.shields.io/badge/Version-Current-blue.svg?style=flat-square) + +### Purpose + +When working on the Records Management module, we needed additional functionality around permissions, and therefore +introduced the [ExtendedPermissionService](../../rm-community/rm-community-repo/source/java/org/alfresco/repo/security/permissions/impl/ExtendedPermissionServiceImpl.java). + +### Overview + +The ExtendedPermissionService is wired in, via [Spring config](../../rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/extended-repository-context.xml), +to extend Alfresco's core PermissionService, and adds support for: +* the [RMPermissionModel](../../rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMPermissionModel.java), which defines the available permissions capabilities. +* the [PermissionProcessorRegistry](../../rm-community/rm-community-repo/source/java/org/alfresco/repo/security/permissions/processor/PermissionProcessorRegistry.java), which introduces pre- and post- processors. +* other minor method extensions (e.g. to setInheritParentPermissions) + +### Permission Processor Registry + +This was added in RM 2.4 to support the requirements around the additional security classifications and markings. + +The registry is simply two array lists, one for pre-processors and one for post-processors, which are iterated around +before / after (respectively) the wrapped call PermissionService.hasPermission + +Out of the box, a system with the RM module installed will have the following permissions processors defined: + +#### Community: + +##### Pre-processors: +* None. + +##### Post-processors: +* [RecordsManagementPermissionPostProcessor](../../rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordsManagementPermissionPostProcessor.java) + * If the node is an RM node (i.e. it has the [RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT](../../rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java) marker aspect) and the + core permissions evaluates to DENIED, then this post processor allows read/writes if the appropriate read/file + permissions are present. + +#### Enterprise: +(links only work in clones of Enterprise repos) + +##### Pre-processors: +* [SecurityMarksPermissionPreProcessor](../../rm-enterprise/rm-enterprise-repo/src/main/java/org/alfresco/module/org_alfresco_module_rm/securitymarks/permission/SecurityMarksPermissionPreProcessor.java) + * For all content: denies the result if the required security clearance rules (for classification or marks) are not satisfied. (uses +[securityClearanceService.isClearedFor](../../rm-enterprise/rm-enterprise-repo/src/main/java/org/alfresco/module/org_alfresco_module_rm/securitymarks/SecurityClearanceServiceImpl.java)) + +##### Post-processors: +* None. + + +### Configuration and Extension points + +Additional processors can be defined by extending either [PermissionPreProcessorBaseImpl](../../rm-community/rm-community-repo/source/java/org/alfresco/repo/security/permissions/processor/impl/PermissionPreProcessorBaseImpl.java) +or [PermissionPostProcessorBaseImpl](../../rm-community/rm-community-repo/source/java/org/alfresco/repo/security/permissions/processor/impl/PermissionPostProcessorBaseImpl.java) +which call the add method on the appropriate list during init. + +### Performance Implications + +There is certainly a performance overhead when adding additional processing to permission checks. This is most noticeable + in the SecurityMarksPermissionPreProcessor where we need to call out to an external service. This has been profiled + heavily and optimised during 2.5 and 2.6 development. + + ###TODO: + Not yet documented (in related areas of the code) are: + * Capabilities (see rm-capabilities-*.xml, declarativeCapability.java and DeclarativeCompositeCapability.java) + * RM's permission system has an any allow allows policy unlike alfresco which policy is any deny denies + \ No newline at end of file diff --git a/rm-community/documentation/overview.md b/rm-community/documentation/overview.md index ed79baa2d3..d80c026457 100644 --- a/rm-community/documentation/overview.md +++ b/rm-community/documentation/overview.md @@ -65,6 +65,11 @@ We use standard Alfresco [data modelling](http://docs.alfresco.com/5.2/reference *** +### Component Overview +![Information Governance Component Overview](./resource/component/ig-component.png) + +*** + ### Design Decisions | Decision | Rationale | Date | diff --git a/rm-community/documentation/resource/component/ig-component.png b/rm-community/documentation/resource/component/ig-component.png new file mode 100644 index 0000000000..788642c604 Binary files /dev/null and b/rm-community/documentation/resource/component/ig-component.png differ diff --git a/rm-community/documentation/resource/component/ig-component.puml b/rm-community/documentation/resource/component/ig-component.puml new file mode 100644 index 0000000000..1c446ccb46 --- /dev/null +++ b/rm-community/documentation/resource/component/ig-component.puml @@ -0,0 +1,58 @@ +@startuml + +skinparam componentArrowColor white + +' IG Component Breakdown +rectangle "Information Governance" as IG { + + rectangle "Records Management" as RM { + + component "File Plan" as FP + + rectangle "Records" as Rec { + component "Filed and Unfiled Records" + component "Easy Access Records" + component "Version Records" + component "Physical Records" + component "Email Records" + component "Record Import and Export" + } + + rectangle "Retention" as Ret { + component "Retention Schedules and Events" + component "Transfer and Accession" + component "Destruction" + } + component "List of Values" as LOV + } + + rectangle "Security" as Sec { + component "Roles, Capabilities and Permissions" + component "Security Marks" + component "Content Classification" + } + + rectangle "Discovery" as Dis { + component "Search" + component "Legal Holds" + } + + rectangle "Compliance" as Comp { + component "Audit" + component "DoD 5015.2" + } + + rectangle "Automation" as Auto { + component "Rules" + } +} + +' Fomatting +RM -[hidden]---- Sec +RM -[hidden]---- Dis +Dis -[hidden]- Comp +Rec -[hidden]-- Ret +FP -[hidden]- LOV +Sec -[hidden]-- Auto + +@enduml \ No newline at end of file diff --git a/rm-community/pom.xml b/rm-community/pom.xml index bc274e7bc8..98eaca587f 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.6.3-SNAPSHOT + 2.7.3-SNAPSHOT diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties index 8fdbf91cbd..0df8fa38c8 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/alfresco-global.properties @@ -17,6 +17,7 @@ imap.server.attachments.extraction.enabled=false # audit.enabled=true audit.rm.enabled=true +audit.rm.viewLog.maxSize=100 #audit.rm.runas=admin #audit.filter.alfresco-access.transaction.user=~null;.* diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json index 3e2a07df1b..41a44c405e 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/bootstrap/content/rmEventConfigBootstrap.json @@ -70,7 +70,12 @@ "eventType" : "rmEventType.simple", "eventName" : "case_complete", "eventDisplayLabel" : "rmevent.case_complete" - } + }, + { + "eventType": "rmEventType.simple", + "eventName": "declassification_review", + "eventDisplayLabel": "rmevent.declassification_review" + } ] } diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml index d7d9999f20..1dd621974b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/content-context.xml @@ -18,6 +18,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_es.properties index c03ceb5d35..e77499dd40 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_es.properties @@ -3,8 +3,8 @@ dod_dod5015.description=Modelo de contenido de DOD5015 dod_dod5015.type.dod_site.title=Sitio de DOD5015 dod_dod5015.type.dod_site.description=Sitio de DOD5015 -dod_dod5015.type.dod_filePlan.title=Plan de ficheros DOD5015 -dod_dod5015.type.dod_filePlan.description=Plan de ficheros DOD5015 +dod_dod5015.type.dod_filePlan.title=Cuadro de clasificaci\u00f3n DOD5015 +dod_dod5015.type.dod_filePlan.description=Cuadro de clasificaci\u00f3n DOD5015 dod_dod5015.type.dod_recordSeries.title=Serie de documentos de archivo (depreciada) dod_dod5015.type.dod_recordSeries.description=Serie de documentos de archivo (depreciada) @@ -95,3 +95,4 @@ dod_dod5015.property.dod_contact.title=Contacto dod_dod5015.property.dod_contact.description=Contacto dod_dod5015.property.dod_contentManagementSystem.title=Sistema de gesti\u00f3n de contenidos dod_dod5015.property.dod_contentManagementSystem.description=Sistema de gesti\u00f3n de contenidos + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_ja.properties index 883893e12f..09e26fdff0 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015-model_ja.properties @@ -11,8 +11,8 @@ dod_dod5015.type.dod_recordSeries.description=\u30ec\u30b3\u30fc\u30c9\u30b7\u30 dod_dod5015.aspect.dod_dod5015record.title=DOD5015\u30ec\u30b3\u30fc\u30c9 dod_dod5015.aspect.dod_dod5015record.description=DOD5015\u30ec\u30b3\u30fc\u30c9 -dod_dod5015.property.dod_publicationDate.title=\u767a\u884c\u65e5 -dod_dod5015.property.dod_publicationDate.decription=\u767a\u884c\u65e5 +dod_dod5015.property.dod_publicationDate.title=\u516c\u958b\u65e5 +dod_dod5015.property.dod_publicationDate.decription=\u516c\u958b\u65e5 dod_dod5015.property.dod_originator.title=\u767a\u4fe1\u5143 dod_dod5015.property.dod_originator.decription=\u767a\u4fe1\u5143 dod_dod5015.property.dod_originatingOrganization.title=\u767a\u4fe1\u5143\u7d44\u7e54 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015_ja.properties index 64c65a711a..4d6d84dd6b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/dod5015/messages/dod5015_ja.properties @@ -5,4 +5,4 @@ capability.CreateModifyDestroyClassificationGuides.title=\u5206\u985e\u30ac\u30a capability.UpgradeDowngradeAndDeclassifyRecords.title=\u30c0\u30a6\u30f3\u30b0\u30ec\u30fc\u30c9\u306e\u66f4\u65b0\u3068\u30ec\u30b3\u30fc\u30c9\u306e\u5206\u985e\u89e3\u9664 capability.UpdateExemptionCategories.title=\u9664\u5916\u30ab\u30c6\u30b4\u30ea\u306e\u66f4\u65b0 capability.MapClassificationGuideMetadata.title=\u5206\u985e\u30ac\u30a4\u30c9\u30e1\u30bf\u30c7\u30fc\u30bf\u306e\u30de\u30c3\u30d4\u30f3\u30b0 -capability.CreateModifyDestroyTimeframes.title=\u30bf\u30a4\u30e0\u30d5\u30ec\u30fc\u30e0\u306e\u4f5c\u6210/\u5909\u66f4/\u7834\u68c4 +capability.CreateModifyDestroyTimeframes.title=\u671f\u9593\u306e\u4f5c\u6210/\u5909\u66f4/\u7834\u68c4 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties index 76742abc10..f5ecef752f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties @@ -56,4 +56,5 @@ log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info # Job debug # #log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug -log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info \ No newline at end of file +log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info +log4j.logger.org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAOImpl=info \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties index 43372c3d11..06a16b7eb5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties @@ -37,7 +37,7 @@ rm.action.event-not-undone=No puede deshacer el evento {0} porque no se ha defin rm.action.node-not-record-category=No puede crear una planificaci\u00f3n de retenci\u00f3n para ({0}) porque no es una categor\u00eda de documento de archivo. rm.action.parameter-not-supplied=A\u00f1ada un ''{0}'' para continuar. rm.action.delete-not-hold-type=No se pudo eliminar el bloqueo porque {1} no es del tipo {0}. -rm.action.cast-to-rm-type=No puede cargar un tipo de carpeta personalizada al plan de ficheros de Records Management. +rm.action.cast-to-rm-type=No puede cargar un tipo de carpeta personalizada al cuadro de clasificaci\u00f3n de Records Management. rm.action.record-folder-create=No puede crear una carpeta de documentos de archivo en otra carpeta de documentos de archivo. rm.action.unique.child.type-error-message=No puede crear elementos m\u00faltiples de este tipo aqu\u00ed. rm.action.multiple.children.type-error-message=No puede crear {0} aqu\u00ed. diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties index daacd7bfb1..8be3966fa5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties @@ -36,7 +36,7 @@ rm.action.records_only_undeclared=\u5b8c\u4e86\u3067\u304d\u308b\u306e\u306f\u30 rm.action.event-not-undone=\u30a4\u30d9\u30f3\u30c8 ''{0}'' \u306f\u4fdd\u7ba1\u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u306a\u3044\u305f\u3081\u3001\u5143\u306b\u623b\u3059\u64cd\u4f5c\u306f\u884c\u3048\u307e\u305b\u3093\u3002 rm.action.node-not-record-category=''{0}'' \u306f\u30ec\u30b3\u30fc\u30c9\u30ab\u30c6\u30b4\u30ea\u3067\u306f\u306a\u3044\u305f\u3081\u3001\u4fdd\u7ba1\u30b9\u30b1\u30b8\u30e5\u30fc\u30eb\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002 rm.action.parameter-not-supplied=''{0}'' \u3092\u8ffd\u52a0\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -rm.action.delete-not-hold-type=''{1}'' \u306f\u30bf\u30a4\u30d7 ''{0}'' \u3067\u306f\u306a\u3044\u305f\u3081\u3001\u30db\u30fc\u30eb\u30c9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f. +rm.action.delete-not-hold-type=''{1}'' \u306f\u30bf\u30a4\u30d7 ''{0}'' \u3067\u306f\u306a\u3044\u305f\u3081\u3001\u30db\u30fc\u30eb\u30c9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 rm.action.cast-to-rm-type=\u30ab\u30b9\u30bf\u30e0\u306e\u30d5\u30a9\u30eb\u30c0\u30bf\u30a4\u30d7\u306f\u3001\u30ec\u30b3\u30fc\u30c9\u7ba1\u7406\u306e\u6574\u7406\u4fdd\u7ba1\u30d7\u30e9\u30f3\u306b\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3002 rm.action.record-folder-create=\u5225\u306e\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u5185\u306b\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002 rm.action.unique.child.type-error-message=\u3053\u3053\u3067\u306f\u3001\u3053\u306e\u30bf\u30a4\u30d7\u306e\u30a2\u30a4\u30c6\u30e0\u3092\u8907\u6570\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/actions_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/actions_es.properties index 763236b1f2..86300201bd 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/actions_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/actions_es.properties @@ -35,7 +35,7 @@ hasDispositionAction.description=Los documentos de archivo y las carpetas tienen # Are kind isKind.title=Tipo de elemento de Records Management -isKind.description=Los elementos son una clase de componente del plan de ficheros +isKind.description=Los elementos son una clase de componente del cuadro de clasificaci\u00f3n isKind.kind.display-label=Clase # Are Record Type @@ -49,12 +49,12 @@ isRecordType.description=Los documentos de archivo tienen un tipo de documento d # Declare As Record create-record.title=Declarar como documento de archivo create-record.description=Declara el fichero como un documento de archivo -create-record.file-plan.display-label=Plan de ficheros +create-record.file-plan.display-label=Cuadro de clasificaci\u00f3n create-record.hide-record.display-label=Ocultar documento de archivo # Declare As Version Record declare-as-version-record.title=Declarar la versi\u00f3n como documento de archivo declare-as-version-record.description=Declara esta versi\u00f3n del fichero como un documento de archivo -declare-as-version-record.file-plan.display-label=Plan de ficheros +declare-as-version-record.file-plan.display-label=Cuadro de clasificaci\u00f3n # Complete record declareRecord.title=Documento de archivo completo declareRecord.description=Completa un documento de archivo diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties index 7e55d72cc7..e5e7289396 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Delete Object rm.audit.login-succeeded=Login Successful rm.audit.login-failed=Login Unsuccessful rm.audit.create-person=Create User +rm.audit.delete-person=Delete User +rm.audit.create-userGroup=Create User Group +rm.audit.delete-userGroup=Delete User Group +rm.audit.addMember=Add To User Group +rm.audit.removeMember=Remove From User Group rm.audit.linkTo=Link to rm.audit.moveTo=Move to rm.audit.copyTo=Copy to diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_de.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_de.properties index 347b9d0c0a..6510945861 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_de.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_de.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Objekt gel\u00f6scht rm.audit.login-succeeded=Anmeldung erfolgreich rm.audit.login-failed=Anmeldung fehlgeschlagen rm.audit.create-person=Benutzer anlegen +rm.audit.delete-person=Benutzer l\u00f6schen +rm.audit.create-userGroup=Benutzergruppe erstellen +rm.audit.delete-userGroup=Benutzergruppe l\u00f6schen +rm.audit.addMember=Zu Benutzergruppe hinzuf\u00fcgen +rm.audit.removeMember=Von Benutzergruppe entfernen rm.audit.linkTo=Link zu rm.audit.moveTo=Verschieben nach rm.audit.copyTo=Kopieren nach diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_es.properties index f1b82aad91..c1d33f0594 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_es.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Eliminar objeto rm.audit.login-succeeded=Inicio de sesi\u00f3n correcto rm.audit.login-failed=Error de inicio de sesi\u00f3n rm.audit.create-person=Crear usuario +rm.audit.delete-person=Eliminar usuario +rm.audit.create-userGroup=Crear grupo de usuarios +rm.audit.delete-userGroup=Eliminar grupo de usuarios +rm.audit.addMember=A\u00f1adir a grupo de usuarios +rm.audit.removeMember=Eliminar de grupo de usuarios rm.audit.linkTo=Enlace a rm.audit.moveTo=Mover a rm.audit.copyTo=Copiar a diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_fr.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_fr.properties index 9cf8b38137..7ea74aa30c 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_fr.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_fr.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Supprimer l'objet rm.audit.login-succeeded=La connexion a abouti rm.audit.login-failed=La connexion a \u00e9chou\u00e9 rm.audit.create-person=Cr\u00e9er un utilisateur +rm.audit.delete-person=Supprimer un utilisateur +rm.audit.create-userGroup=Cr\u00e9er un groupe d'utilisateur +rm.audit.delete-userGroup=Supprimer le groupe d'utilisateur +rm.audit.addMember=Ajouter au groupe d'utilisateur +rm.audit.removeMember=Supprimer du groupe d'utilisateur rm.audit.linkTo=Lier \u00e0 rm.audit.moveTo=D\u00e9placer vers... rm.audit.copyTo=Copier vers... diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_it.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_it.properties index 282fe8557c..592d2ccaf5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_it.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_it.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Elimina oggetto rm.audit.login-succeeded=Login riuscito rm.audit.login-failed=Login non riuscito rm.audit.create-person=Crea utente +rm.audit.delete-person=Elimina utente +rm.audit.create-userGroup=Crea gruppo utenti +rm.audit.delete-userGroup=Elimina gruppo utenti +rm.audit.addMember=Aggiungi al gruppo utenti +rm.audit.removeMember=Rimuovi dal gruppo utenti rm.audit.linkTo=Collega a rm.audit.moveTo=Sposta in rm.audit.copyTo=Copia in diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ja.properties index 6bfdfc7e5c..3a81eedbb8 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ja.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u524a\u9664 rm.audit.login-succeeded=\u30ed\u30b0\u30a4\u30f3\u6210\u529f rm.audit.login-failed=\u30ed\u30b0\u30a4\u30f3\u5931\u6557 rm.audit.create-person=\u30e6\u30fc\u30b6\u30fc\u306e\u4f5c\u6210 +rm.audit.delete-person=\u30e6\u30fc\u30b6\u30fc\u306e\u524a\u9664 +rm.audit.create-userGroup=\u30e6\u30fc\u30b6\u30fc\u30b0\u30eb\u30fc\u30d7\u306e\u4f5c\u6210 +rm.audit.delete-userGroup=\u30e6\u30fc\u30b6\u30fc\u30b0\u30eb\u30fc\u30d7\u306e\u524a\u9664 +rm.audit.addMember=\u30e6\u30fc\u30b6\u30fc\u30b0\u30eb\u30fc\u30d7\u306b\u8ffd\u52a0 +rm.audit.removeMember=\u30e6\u30fc\u30b6\u30fc\u30b0\u30eb\u30fc\u30d7\u304b\u3089\u524a\u9664 rm.audit.linkTo=\u30ea\u30f3\u30af\u5148 rm.audit.moveTo=\u79fb\u52d5\u5148 rm.audit.copyTo=\u30b3\u30d4\u30fc\u5148 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nb.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nb.properties index 295c293b7c..c0189944e5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nb.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nb.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Slett element rm.audit.login-succeeded=Vellykket p\u00e5logging rm.audit.login-failed=Mislykket p\u00e5logging rm.audit.create-person=Opprett bruker +rm.audit.delete-person=Slett bruker +rm.audit.create-userGroup=Opprett brukergruppe +rm.audit.delete-userGroup=Slett brukergruppe +rm.audit.addMember=Legg til i brukergruppe +rm.audit.removeMember=Fjern fra brukergruppe rm.audit.linkTo=Koble til rm.audit.moveTo=Flytt til rm.audit.copyTo=Kopier til diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nl.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nl.properties index f547c80dce..57ea711c49 100755 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nl.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_nl.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Object verwijderen rm.audit.login-succeeded=Aanmelden geslaagd rm.audit.login-failed=Aanmelden niet geslaagd rm.audit.create-person=Gebruiker maken +rm.audit.delete-person=Gebruiker verwijderen +rm.audit.create-userGroup=Gebruikersgroep maken +rm.audit.delete-userGroup=Gebruikersgroep verwijderen +rm.audit.addMember=Toevoegen aan gebruikersgroep +rm.audit.removeMember=Verwijderen uit gebruikersgroep rm.audit.linkTo=Koppelen naar rm.audit.moveTo=Verplaatsen naar rm.audit.copyTo=Kopi\u00ebren naar diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_pt_BR.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_pt_BR.properties index f55fc7b90f..8b95f07838 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_pt_BR.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_pt_BR.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=Excluir objeto rm.audit.login-succeeded=Login bem-sucedido rm.audit.login-failed=Login malsucedido rm.audit.create-person=Criar usu\u00e1rio +rm.audit.delete-person=Excluir usu\u00e1rio +rm.audit.create-userGroup=Criar grupo de usu\u00e1rios +rm.audit.delete-userGroup=Excluir grupo de usu\u00e1rios +rm.audit.addMember=Adicionar ao grupo de usu\u00e1rios +rm.audit.removeMember=Remover do grupo de usu\u00e1rios rm.audit.linkTo=Vincular a rm.audit.moveTo=Mover para rm.audit.copyTo=Copiar para diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ru.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ru.properties index 9db574317f..74c68ee760 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ru.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_ru.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0431\u rm.audit.login-succeeded=\u0412\u0445\u043e\u0434 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d rm.audit.login-failed=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 rm.audit.create-person=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f +rm.audit.delete-person=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f +rm.audit.create-userGroup=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 +rm.audit.delete-userGroup=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 +rm.audit.addMember=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0433\u0440\u0443\u043f\u043f\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 +rm.audit.removeMember=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 rm.audit.linkTo=\u0421\u0432\u044f\u0437\u0430\u0442\u044c \u0441 rm.audit.moveTo=\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 rm.audit.copyTo=\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_zh_CN.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_zh_CN.properties index 38be513e1c..494dd6ff77 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_zh_CN.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/audit-service_zh_CN.properties @@ -4,6 +4,11 @@ rm.audit.delete-object=\u5220\u9664\u5bf9\u8c61 rm.audit.login-succeeded=\u767b\u5f55\u6210\u529f rm.audit.login-failed=\u767b\u5f55\u5931\u8d25 rm.audit.create-person=\u521b\u5efa\u7528\u6237 +rm.audit.delete-person=\u5220\u9664\u7528\u6237 +rm.audit.create-userGroup=\u521b\u5efa\u7528\u6237\u7ec4 +rm.audit.delete-userGroup=\u5220\u9664\u7528\u6237\u7ec4 +rm.audit.addMember=\u52a0\u5230\u7528\u6237\u7ec4 +rm.audit.removeMember=\u4ece\u7528\u6237\u7ec4\u79fb\u9664 rm.audit.linkTo=\u94fe\u63a5\u5230 rm.audit.moveTo=\u79fb\u52a8\u5230 rm.audit.copyTo=\u590d\u5236\u5230 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/capability-service_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/capability-service_es.properties index 4dfde5c077..4531ab069f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/capability-service_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/capability-service_es.properties @@ -90,8 +90,8 @@ capability.ManageAccessRights.title=Administrar permisos # Configuration capability.group.config.title=Configuraci\u00f3n -capability.CreateModifyDestroyFileplanMetadata.title=Crear Modificar Destruir metadatos de plan de ficheros -capability.CreateModifyDestroyFileplanTypes.title=Crear Modificar Destruir tipos de plan de ficheros +capability.CreateModifyDestroyFileplanMetadata.title=Crear Modificar Destruir metadatos del cuadro de clasificaci\u00f3n +capability.CreateModifyDestroyFileplanTypes.title=Crear Modificar Destruir tipos del cuadro de clasificaci\u00f3n capability.CreateModifyDestroyRecordTypes.title=Crear Modificar Destruir tipos de documento de archivo capability.CreateAndAssociateSelectionLists.title=Crear y asociar listas de selecci\u00f3n capability.EditSelectionLists.title=Editar listas de selecci\u00f3n diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service_es.properties index ada52e23cb..2e1575538f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-management-service_es.properties @@ -4,10 +4,10 @@ rm.service.set-id=No puede cambiar el ID de {0} porque es de solo lectura. rm.service.path-node=No se pudo encontrar {0}. Trate de actualizar el navegador o p\u00f3ngase en contacto con el dep. de TI. rm.service.invalid-rm-node=El nodo de Records Management no es v\u00e1lido porque el aspecto {0} no est\u00e1 presente. rm.service.no-root=No se pudo encontrar la ra\u00edz de Records Management. Trate de archivar el documento de archivo de nuevo. -rm.service.dup-root=No puede crear un plan de ficheros aqu\u00ed porque ya hay uno creado en esta jerarqu\u00eda de carpetas. -rm.service.root-type=No se puede crear el plan de ficheros porque el tipo {0} no es un subtipo de rma:filePlan. Vuelva a intentarlo usando un tipo diferente. -rm.service.container-parent-type=Solo puede crear una categor\u00eda de documentos de archivo en el nivel superior del plan de ficheros o en otra categor\u00eda de documentos de archivo (rma:recordCategory). -rm.service.container-type=Solo puede crear una categor\u00eda de documentos de archivo en el nivel superior del plan de ficheros o en otra categor\u00eda de documentos de archivo (rma:recordsManagementContainer o subtipo). +rm.service.dup-root=No puede crear un cuadro de clasificaci\u00f3n aqu\u00ed porque ya hay uno creado en esta jerarqu\u00eda de carpetas. +rm.service.root-type=No se puede crear el cuadro de clasificaci\u00f3n porque el tipo {0} no es un subtipo de rma:filePlan. Vuelva a intentarlo usando un tipo diferente. +rm.service.container-parent-type=Solo puede crear una categor\u00eda de documentos de archivo en el nivel superior del cuadro de clasificaci\u00f3n o en otra categor\u00eda de documentos de archivo (rma:recordCategory). +rm.service.container-type=Solo puede crear una categor\u00eda de documentos de archivo en el nivel superior del cuadro de clasificaci\u00f3n o en otra categor\u00eda de documentos de archivo (rma:recordsManagementContainer o subtipo). rm.service.container-expected=Solo puede encontrar contenidos de categor\u00eda de documentos de archivo en una categor\u00eda de documentos de archivo (rma:recordCategory o subtipo). rm.service.record-folder-expected=La acci\u00f3n solo puede completarse usando una carpeta de documentos de archivo del tipo rma:recordFolder. rm.service.parent-record-folder-root=No puede crear una carpeta de documentos de archivo aqu\u00ed. Trate de crearla en una categor\u00eda de documentos de archivo. diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-model_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-model_es.properties index 822a18d2ac..742a37394b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-model_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/records-model_es.properties @@ -122,8 +122,8 @@ rma_recordsmanagement.property.rma_transferLocation.decription=Transferencia de rma_recordsmanagement.association.rma_transferred.title=Transferido rma_recordsmanagement.association.rma_transferred.decription=Transferido -rma_recordsmanagement.aspect.rma_filePlanComponent.title=Componente del plan de ficheros -rma_recordsmanagement.aspect.rma_filePlanComponent.decription=Componente del plan de ficheros +rma_recordsmanagement.aspect.rma_filePlanComponent.title=Componente del cuadro de clasificaci\u00f3n +rma_recordsmanagement.aspect.rma_filePlanComponent.decription=Componente del cuadro de clasificaci\u00f3n rma_recordsmanagement.property.rma_rootNodeRef.title=Nodo ra\u00edz rma_recordsmanagement.property.rma_rootNodeRef.decription=Nodo ra\u00edz diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events.properties index ea0094fc86..329c771aa4 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events.properties @@ -19,4 +19,5 @@ rmevent.obsolete=Obsolete rmevent.all_allowances_granted_are_terminated=All Allowances Granted are Terminated rmevent.WGI_action_complete=WGI Action Complete rmevent.separation=Separation -rmevent.case_complete=Case Complete \ No newline at end of file +rmevent.case_complete=Case Complete +rmevent.declassification_review=Declassification Review \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_de.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_de.properties index c32122a8c3..f17930380b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_de.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_de.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Alle einger\u00e4umten Berechtigun rmevent.WGI_action_complete=WGI-Aktion abschlie\u00dfen rmevent.separation=Trennung rmevent.case_complete=Fall abgeschlossen +rmevent.declassification_review=Deklassifizierungs\u00fcberpr\u00fcfung diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_es.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_es.properties index 56c41fb451..e752aac231 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_es.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_es.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Todas las provisiones otorgadas ha rmevent.WGI_action_complete=Acci\u00f3n WGI completa rmevent.separation=Separaci\u00f3n rmevent.case_complete=Caso completo +rmevent.declassification_review=Revisi\u00f3n de la desclasificaci\u00f3n diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_fr.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_fr.properties index 4e0f2a72a9..9a7633d558 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_fr.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_fr.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Toutes les autorisations accord\u0 rmevent.WGI_action_complete=Action WGI termin\u00e9e rmevent.separation=S\u00e9paration rmevent.case_complete=Cas termin\u00e9 +rmevent.declassification_review=V\u00e9rification de d\u00e9classification diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_it.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_it.properties index ccdec8f989..6264aa3c26 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_it.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_it.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Tutte le concessioni sono state te rmevent.WGI_action_complete=Azione WGI completata rmevent.separation=Separazione rmevent.case_complete=Caso completato +rmevent.declassification_review=Esame di declassificazione diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ja.properties index 50309fb68f..86d0efa00f 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ja.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=\u8a31\u53ef\u3055\u308c\u3066\u30 rmevent.WGI_action_complete=WGI \u51e6\u7406\u5b8c\u4e86 rmevent.separation=\u5206\u96e2 rmevent.case_complete=\u30b1\u30fc\u30b9\u5b8c\u4e86 +rmevent.declassification_review=\u5206\u985e\u89e3\u9664\u306e\u30ec\u30d3\u30e5\u30fc diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nb.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nb.properties index e44b8d4bfa..a408568d85 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nb.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nb.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Alle tillatelser som er gitt, er a rmevent.WGI_action_complete=WGI-handling fullf\u00f8rt rmevent.separation=Separasjon rmevent.case_complete=Sak fullf\u00f8rt +rmevent.declassification_review=Gjennomgang av deklassifisering diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nl.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nl.properties index 69c6ecfc3a..f1bf21b428 100755 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nl.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_nl.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Alle toegekende rechten zijn be\u0 rmevent.WGI_action_complete=WGI-actie afgerond rmevent.separation=Scheiding rmevent.case_complete=Geval afgerond +rmevent.declassification_review=Classificatieopheffingsrevisie diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_pt_BR.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_pt_BR.properties index 75e88fd6fa..4a23145836 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_pt_BR.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_pt_BR.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=Todas as bonifica\u00e7\u00f5es co rmevent.WGI_action_complete=A\u00e7\u00e3o de WGI conclu\u00edda rmevent.separation=Separa\u00e7\u00e3o rmevent.case_complete=Caso conclu\u00eddo +rmevent.declassification_review=Revis\u00e3o de desclassifica\u00e7\u00e3o diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ru.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ru.properties index 8dfbe318c5..fee3d91ea9 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ru.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_ru.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=\u0412\u0441\u0435 \u043f\u0440\u0 rmevent.WGI_action_complete=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 WGI \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e rmevent.separation=\u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 rmevent.case_complete=\u0421\u043b\u0443\u0447\u0430\u0439 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d +rmevent.declassification_review=\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0441\u0441\u0435\u043a\u0440\u0435\u0447\u0438\u0432\u0430\u043d\u0438\u044f diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_zh_CN.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_zh_CN.properties index 628aef053e..dc778c1a1b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_zh_CN.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-events_zh_CN.properties @@ -20,3 +20,4 @@ rmevent.all_allowances_granted_are_terminated=\u6240\u6709\u6388\u4e88\u7684\u96 rmevent.WGI_action_complete=WGI \u64cd\u4f5c\u5b8c\u6210 rmevent.separation=\u5206\u79bb rmevent.case_complete=\u6848\u4f8b\u5b8c\u6210 +rmevent.declassification_review=\u53d6\u6d88\u5206\u7c7b\u7684\u5ba1\u67e5 diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-system_ja.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-system_ja.properties index 2675f26ad5..c1109705d4 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-system_ja.properties +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/rm-system_ja.properties @@ -19,7 +19,7 @@ rm.savedsearch.cutoffRecordsName=\u30ab\u30c3\u30c8\u30aa\u30d5\u5bfe\u8c61\u306 rm.savedsearch.cutoffRecordsDesc=\u73fe\u5728\u30ab\u30c3\u30c8\u30aa\u30d5\u306e\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u3068\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u3002 rm.savedsearch.transferRecordsName=\u8ee2\u9001\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u30ec\u30b3\u30fc\u30c9\u304a\u3088\u3073\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0 rm.savedsearch.transferRecordsDesc=\u73fe\u5728\u8ee2\u9001\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u3068\u30ec\u30b3\u30fc\u30c9\u3002 -rm.savedsearch.destructionRecordsName=\u5ec3\u68c4\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u30ec\u30b3\u30fc\u30c9\u3068\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0 -rm.savedsearch.destructionRecordsDesc=\u73fe\u5728\u5ec3\u68c4\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u3002 +rm.savedsearch.destructionRecordsName=\u7834\u68c4\u53ef\u80fd\u306a\u30ec\u30b3\u30fc\u30c9\u3068\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0 +rm.savedsearch.destructionRecordsDesc=\u73fe\u5728\u7834\u68c4\u6761\u4ef6\u3092\u6e80\u305f\u3057\u3066\u3044\u308b\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u3002 rm.savedsearch.frozenRecordsName=\u30db\u30fc\u30eb\u30c9\u4e2d\u306e\u30ec\u30b3\u30fc\u30c9\u304a\u3088\u3073\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0 rm.savedsearch.frozenRecordsDesc=\u73fe\u5728\u30db\u30fc\u30eb\u30c9\u4e2d\u306e\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u3068\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u3002 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 911576a784..83031dcb57 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 @@ -242,6 +242,12 @@ + + Disposition Evaluator Combination + d:boolean + false + + rma:filePlanComponent @@ -1130,6 +1136,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/model/recordsPermissionModel.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml index d545517f44..9f9dc70b2e 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsPermissionModel.xml @@ -490,8 +490,8 @@ - - + + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 63242f2ab1..1ee6caf7e9 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -248,6 +248,12 @@ + + + + + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-context.xml index 7ca1fc7bab..6d4c60152e 100755 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-context.xml @@ -46,6 +46,7 @@ + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml index d6ebbeca54..766842f0a8 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml @@ -3,10 +3,10 @@ - - - - + + + + @@ -17,11 +17,14 @@ - - - + + + + + + + + + + + - select - count( distinct assoc.child_node_id ) - from - alf_child_assoc assoc - left join alf_node_properties childProp on assoc.child_node_id = childProp.node_id - where - assoc.parent_node_id = #{parentId} - and childProp.qname_id = #{propertyQnameId} - and childProp.string_value in - - '${item}' - - - - \ No newline at end of file + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml index 8144e44bf6..fd97f207cb 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMapConfig.xml @@ -2,6 +2,11 @@ + + + + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml index 61c53f3c6b..b7c7ce514e 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml @@ -53,10 +53,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index ddd303c982..a3a01f47b0 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -143,6 +143,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index de5519decd..32ea68a220 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -933,6 +933,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml index 3c12a64720..2cc9710263 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml @@ -20,6 +20,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml index 91c2f622ea..f9f7a7e409 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml @@ -16,6 +16,7 @@ + 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 0215c04957..05a062c994 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 @@ -466,8 +466,19 @@ + + + + + + + + + + + + + ${audit.rm.viewLog.maxSize} + diff --git a/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl index 765b823b15..b936241f81 100644 --- a/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl +++ b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/rma/dispositionactiondefinition.lib.ftl @@ -37,6 +37,7 @@ <#if action.period??>"period": "${action.period}", <#if action.periodProperty??>"periodProperty": "${action.periodProperty}", <#if action.location??>"location": "${action.location}", + <#if action.combineDispositionStepConditions??>"combineDispositionStepConditions": "${action.combineDispositionStepConditions?string}", <#if action.events??>"events": [<#list action.events as event>"${event}"<#if event_has_next>,], "eligibleOnFirstCompleteEvent": ${action.eligibleOnFirstCompleteEvent?string} } diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml index b53480d311..0b7c2d62bc 100644 --- a/rm-community/rm-community-repo/pom.xml +++ b/rm-community/rm-community-repo/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-rm-community - 2.6.3-SNAPSHOT + 2.7.3-SNAPSHOT @@ -25,8 +25,6 @@ alfresco-rm-community-repo true ${project.build.directory}/solr/home - - 5.1.1 1.4 @@ -364,7 +362,7 @@ ${alfresco.groupId} alfresco-repository - ${alfresco.h2scripts.version} + ${alfresco.version} h2scripts @@ -615,7 +613,7 @@ ${alfresco.groupId} alfresco-repository - ${alfresco.h2scripts.version} + ${alfresco.version} h2scripts diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java index de2bda630d..9e68395cef 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java @@ -253,7 +253,7 @@ public class BroadcastDispositionActionDefinitionUpdateAction extends RMActionEx { NodeRef dispositionedNode = getNodeService().getPrimaryParent(nextAction.getNodeRef()).getParentRef(); DispositionActionDefinition definition = nextAction.getDispositionActionDefinition(); - Date newAsOfDate = getDispositionService().calculateAsOfDate(dispositionedNode, definition, false); + Date newAsOfDate = getDispositionService().calculateAsOfDate(dispositionedNode, definition); if (logger.isDebugEnabled()) { diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java index ee08ee4c73..bf18587423 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java @@ -876,15 +876,11 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas } String lovConstraintQNameAsString = newLovConstraint.toPrefixString(getNamespaceService()); - // Add the constraint - if it isn't already there. - String refOfExistingConstraint = null; + // Add the constraint - if it isn't already there (there should only be one constraint). + String refOfExistingConstraint = (targetProp.getConstraints().isEmpty() ? + null : + targetProp.getConstraints().get(0).getRef()); - for (M2Constraint c : targetProp.getConstraints()) - { - // There should only be one constraint. - refOfExistingConstraint = c.getRef(); - break; - } if (refOfExistingConstraint != null) { targetProp.removeConstraintRef(refOfExistingConstraint); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java index 2f79064e2d..8af59833ee 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImpl.java @@ -27,6 +27,14 @@ package org.alfresco.module.org_alfresco_module_rm.audit; +import static org.alfresco.model.ContentModel.PROP_AUTHORITY_DISPLAY_NAME; +import static org.alfresco.model.ContentModel.PROP_AUTHORITY_NAME; +import static org.alfresco.model.ContentModel.PROP_USERNAME; +import static org.alfresco.module.org_alfresco_module_rm.audit.event.UserGroupMembershipUtils.PARENT_GROUP; +import static org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model.TYPE_DOD_5015_SITE; +import static org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType.DEFAULT_SITE_NAME; +import static org.apache.commons.lang3.StringUtils.isBlank; + import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; @@ -43,6 +51,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import javax.transaction.SystemException; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -72,6 +81,8 @@ import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -186,6 +197,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean private DictionaryService dictionaryService; private TransactionService transactionService; private NodeService nodeService; + private SiteService siteService; private ContentService contentService; private AuditComponent auditComponent; private AuditService auditService; @@ -237,6 +249,13 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean this.nodeService = nodeService; } + /** + * Set the site service (used to check the type of RM site created). + * + * @param siteService The site service. + */ + public void setSiteService(SiteService siteService) { this.siteService = siteService; } + /** * Sets the ContentService instance */ @@ -713,7 +732,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean * * @param auditedNodes details of the nodes that were modified */ - private void auditInTxn(Set auditedNodes) throws Throwable + private void auditInTxn(Set auditedNodes) throws SystemException { // Go through all the audit information and audit it boolean auditedSomething = false; @@ -820,7 +839,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean * @param writer Writer to write the audit trail * @param reportFormat Format to write the audit trail in, ignored if writer is null */ - private void getAuditTrailImpl( + protected void getAuditTrailImpl( final RecordsManagementAuditQueryParameters params, final List results, final Writer writer, @@ -967,9 +986,10 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean return true; } - if(nodeRef != null && nodeService.exists(nodeRef) && - !AccessStatus.ALLOWED.equals( - capabilityService.getCapabilityAccessState(nodeRef, ACCESS_AUDIT_CAPABILITY))) + if (nodeRef != null && nodeService.exists(nodeRef) && + filePlanService.isFilePlanComponent(nodeRef) && + !AccessStatus.ALLOWED.equals( + capabilityService.getCapabilityAccessState(nodeRef, ACCESS_AUDIT_CAPABILITY))) { return true; } @@ -1087,11 +1107,31 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean } else if (params.getEvent() != null) { - auditQueryParams.addSearchKey(RM_AUDIT_DATA_EVENT_NAME, params.getEvent()); + if (params.getEvent().equalsIgnoreCase(RM_AUDIT_EVENT_LOGIN_SUCCESS)) + { + auditQueryParams.addSearchKey(RM_AUDIT_DATA_LOGIN_FULLNAME, null); + } + else if (params.getEvent().equalsIgnoreCase(RM_AUDIT_EVENT_LOGIN_FAILURE)) + { + auditQueryParams.addSearchKey(RM_AUDIT_DATA_LOGIN_ERROR, null); + } + else + { + auditQueryParams.addSearchKey(RM_AUDIT_DATA_EVENT_NAME, params.getEvent()); + } } // Get audit entries - auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + SiteInfo siteInfo = siteService.getSite(DEFAULT_SITE_NAME); + if (siteInfo != null) + { + QName siteType = nodeService.getType(siteInfo.getNodeRef()); + if (siteType.equals(TYPE_DOD_5015_SITE)) + { + auditService.auditQuery(callback, dod5015AuditQueryParams, maxEntries); + } + } + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). auditService.auditQuery(callback, auditQueryParams, maxEntries); // finish off the audit trail report @@ -1114,7 +1154,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean * @param date The date for which the start should be calculated. * @return Returns the start of the given date. */ - private Date getStartOfDay(Date date) + protected Date getStartOfDay(Date date) { return DateUtils.truncate(date == null ? new Date() : date, Calendar.DATE); } @@ -1479,22 +1519,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean json.put("fullName", entry.getFullName() == null ? "": entry.getFullName()); json.put("nodeRef", entry.getNodeRef() == null ? "": entry.getNodeRef()); - // TODO: Find another way for checking the event - if (entry.getEvent().equals("Create Person") && entry.getNodeRef() != null) - { - NodeRef nodeRef = entry.getNodeRef(); - String userName = null; - if(nodeService.exists(nodeRef)) - { - userName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME); - } - json.put("nodeName", userName == null ? "": userName); - json.put("createPerson", true); - } - else - { - json.put("nodeName", entry.getNodeName() == null ? "": entry.getNodeName()); - } + setNodeName(entry, json); // TODO: Find another way for checking the event if (entry.getEvent().equals("Delete RM Object")) @@ -1544,7 +1569,6 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean } json.put("changedValues", changedValues); - writer.write(json.toString()); } catch (JSONException je) @@ -1554,6 +1578,81 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean } } + /** + * Update a JSON object with a node name for an audit event. + * + * @param entry The audit event. + * @param json The object to update. + * @throws JSONException If there is a problem updating the JSON. + */ + private void setNodeName(RecordsManagementAuditEntry entry, JSONObject json) throws JSONException + { + String nodeName = null; + if (entry.getNodeRef() != null) + { + // TODO: Find another way for checking the event + switch (entry.getEvent()) + { + case "Create Person": + nodeName = getNodeName(entry.getAfterProperties(), PROP_USERNAME); + // This is needed as older audit events (pre-2.7) were created without PROP_USERNAME being set. + NodeRef nodeRef = entry.getNodeRef(); + if (nodeName == null && nodeService.exists(nodeRef)) + { + nodeName = (String) nodeService.getProperty(nodeRef, PROP_USERNAME); + } + json.put("createPerson", true); + break; + + case "Delete Person": + nodeName = getNodeName(entry.getBeforeProperties(), PROP_USERNAME); + json.put("deletePerson", true); + break; + + case "Create User Group": + nodeName = getNodeName(entry.getAfterProperties(), PROP_AUTHORITY_DISPLAY_NAME, PROP_AUTHORITY_NAME); + break; + + case "Delete User Group": + nodeName = getNodeName(entry.getBeforeProperties(), PROP_AUTHORITY_DISPLAY_NAME, PROP_AUTHORITY_NAME); + break; + + case "Add To User Group": + nodeName = getNodeName(entry.getAfterProperties(), PARENT_GROUP); + break; + + case "Remove From User Group": + nodeName = getNodeName(entry.getBeforeProperties(), PARENT_GROUP); + break; + + default: + nodeName = entry.getNodeName(); + break; + } + } + json.put("nodeName", nodeName == null ? "" : nodeName); + } + + /** + * Get a node name using the first non-blank value from a properties object using a list of property names. + * + * @param properties The properties object. + * @param propertyNames The names of the properties to use. Return the first value that is not empty. + * @return The value of the property, or null if it's not there. + */ + private String getNodeName(Map properties, QName... propertyNames) + { + for (QName propertyName : propertyNames) + { + String nodeName = (properties != null ? (String) properties.get(propertyName) : null); + if (!isBlank(nodeName)) + { + return nodeName; + } + } + return null; + } + /** * Helper method to convert value to MLText * diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToUserGroupAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToUserGroupAuditEvent.java new file mode 100644 index 0000000000..79f46cf913 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToUserGroupAuditEvent.java @@ -0,0 +1,74 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import static org.alfresco.module.org_alfresco_module_rm.audit.event.UserGroupMembershipUtils.makePropertiesMap; +import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Add an authority to a user group. + * + * @author Tom Page + * @since 2.7 + */ +@BehaviourBean(defaultType = "cm:authorityContainer") +public class AddToUserGroupAuditEvent extends AuditEvent implements OnCreateChildAssociationPolicy +{ + /** Node Service */ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** Behaviour to audit adding an authority to a user group. */ + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION, notificationFrequency = EVERY_EVENT, assocType = "cm:member") + public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) + { + Map auditProperties = makePropertiesMap(childAssocRef, nodeService); + recordsManagementAuditService.auditEvent(childAssocRef.getChildRef(), getName(), null, auditProperties, true); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreatePersonAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreatePersonAuditEvent.java index 8c6e3dd6e1..b25404ab37 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreatePersonAuditEvent.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreatePersonAuditEvent.java @@ -27,11 +27,18 @@ package org.alfresco.module.org_alfresco_module_rm.audit.event; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; /** * Audits person creation. @@ -42,17 +49,30 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; @BehaviourBean public class CreatePersonAuditEvent extends AuditEvent implements OnCreateNodePolicy { + /** Node Service */ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + /** * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) */ @Override - @Behaviour - ( - kind = BehaviourKind.CLASS, - type = "cm:person" - ) + @Behaviour(kind = BehaviourKind.CLASS, type = "cm:person") public void onCreateNode(ChildAssociationRef childAssocRef) { - recordsManagementAuditService.auditEvent(childAssocRef.getChildRef(), getName()); + Map auditProperties = new HashMap<>(); + auditProperties.put(ContentModel.PROP_USERNAME, + nodeService.getProperty(childAssocRef.getChildRef(), ContentModel.PROP_USERNAME)); + + recordsManagementAuditService.auditEvent(childAssocRef.getChildRef(), getName(), null, auditProperties); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateUserGroupAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateUserGroupAuditEvent.java new file mode 100644 index 0000000000..24ebf27420 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateUserGroupAuditEvent.java @@ -0,0 +1,75 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Audits user group creation. + * + * @author Tom Page + * @since 2.7 + */ +@BehaviourBean +public class CreateUserGroupAuditEvent extends AuditEvent implements OnCreateNodePolicy +{ + /** Node Service */ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** Behaviour to audit user group creation. */ + @Override + @Behaviour(kind = BehaviourKind.CLASS, type = "cm:authorityContainer") + public void onCreateNode(ChildAssociationRef childAssocRef) + { + Map auditProperties = new HashMap<>(); + auditProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, + nodeService.getProperty(childAssocRef.getChildRef(), ContentModel.PROP_AUTHORITY_DISPLAY_NAME)); + + recordsManagementAuditService.auditEvent(childAssocRef.getChildRef(), getName(), null, auditProperties); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeletePersonAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeletePersonAuditEvent.java new file mode 100644 index 0000000000..a3aeab1e96 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeletePersonAuditEvent.java @@ -0,0 +1,88 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Audits person deletion. + * + * @author Rodica Sutu + * @since 2.7 + */ +@BehaviourBean +public class DeletePersonAuditEvent extends AuditEvent implements BeforeDeleteNodePolicy +{ + /** Node Service*/ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Behaviour that will audit user deletion + * + * @param nodeRef the node to be deleted + * + */ + + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + type = "cm:person" + ) + public void beforeDeleteNode(NodeRef nodeRef) + { + //retrieve the username property to be audited + Map userName = new HashMap<>(); + userName.put(ContentModel.PROP_USERNAME, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); + + //audit the property values before the delete event + recordsManagementAuditService.auditEvent(nodeRef, getName(), userName, null, true, false); + } + + +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteUserGroupAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteUserGroupAuditEvent.java new file mode 100644 index 0000000000..392b7e50f2 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteUserGroupAuditEvent.java @@ -0,0 +1,82 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Audits user group deletion. + * + * @author Tom Page + * @since 2.7 + */ +@BehaviourBean +public class DeleteUserGroupAuditEvent extends AuditEvent implements BeforeDeleteNodePolicy +{ + /** Node Service */ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Behaviour that will audit user group deletion + * + * @param nodeRef the node to be deleted + */ + @Override + @Behaviour(kind = BehaviourKind.CLASS, type = "cm:authorityContainer") + public void beforeDeleteNode(NodeRef nodeRef) + { + // Retrieve the authority name property to be audited + Map auditProperties = new HashMap<>(); + auditProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, + nodeService.getProperty(nodeRef, ContentModel.PROP_AUTHORITY_DISPLAY_NAME)); + + //audit the property values before the delete event + recordsManagementAuditService.auditEvent(nodeRef, getName(), auditProperties, null, true, false); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromUserGroupAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromUserGroupAuditEvent.java new file mode 100644 index 0000000000..952a1d09cb --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromUserGroupAuditEvent.java @@ -0,0 +1,74 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import static org.alfresco.module.org_alfresco_module_rm.audit.event.UserGroupMembershipUtils.makePropertiesMap; +import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.node.NodeServicePolicies.OnDeleteChildAssociationPolicy; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Remove an authority from a user group. + * + * @author Tom Page + * @since 2.7 + */ +@BehaviourBean(defaultType = "cm:authorityContainer") +public class RemoveFromUserGroupAuditEvent extends AuditEvent implements OnDeleteChildAssociationPolicy +{ + /** Node Service */ + private NodeService nodeService; + + /** + * Sets the node service + * + * @param nodeService nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** Behaviour to audit removing an authority from a user group. */ + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION, notificationFrequency = EVERY_EVENT, assocType = "cm:member") + public void onDeleteChildAssociation(ChildAssociationRef childAssocRef) + { + Map auditProperties = makePropertiesMap(childAssocRef, nodeService); + recordsManagementAuditService.auditEvent(childAssocRef.getChildRef(), getName(), auditProperties, null, true); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/UserGroupMembershipUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/UserGroupMembershipUtils.java new file mode 100644 index 0000000000..15fa678b9a --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/UserGroupMembershipUtils.java @@ -0,0 +1,92 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit.event; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +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; + +/** + * Utility class for creating audit events about user group membership. + * + * @author Tom Page + * @since 2.7 + */ +public class UserGroupMembershipUtils +{ + /** A QName to display for the parent group's name. */ + public static final QName PARENT_GROUP = QName.createQName(RecordsManagementModel.RM_URI, "Parent Group"); + /** A QName to display for a child group's name. */ + private static final QName CHILD_GROUP = QName.createQName(RecordsManagementModel.RM_URI, "Child Group"); + + /** + * Create a properties map from the given cm:member association. + * + * @param childAssocRef The association to use. + * @param nodeService The node service. + * @return A map containing the names of the parent and child. + */ + public static Map makePropertiesMap(ChildAssociationRef childAssocRef, NodeService nodeService) + { + Map auditProperties = new HashMap<>(); + // Set exactly one of the child group property or the child user name property. + String childGroupName = getUserGroupName(childAssocRef.getChildRef(), nodeService); + if (!isBlank(childGroupName)) + { + auditProperties.put(CHILD_GROUP, childGroupName); + } + String childUserName = (String) nodeService.getProperty(childAssocRef.getChildRef(), ContentModel.PROP_USERNAME); + if (!isBlank(childUserName)) + { + auditProperties.put(ContentModel.PROP_USERNAME, childUserName); + } + // Set the parent group name. + auditProperties.put(PARENT_GROUP, getUserGroupName(childAssocRef.getParentRef(), nodeService)); + return auditProperties; + } + + /** Get a name that can be displayed for the user group. */ + private static String getUserGroupName(NodeRef nodeRef, NodeService nodeService) + { + String groupName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_AUTHORITY_DISPLAY_NAME); + if (isBlank(groupName)) + { + groupName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_AUTHORITY_NAME); + } + return groupName; + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java index 27dedaef46..aa25dac7f3 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityService.java @@ -151,4 +151,12 @@ public interface CapabilityService * @param group The group which should be removed */ void removeGroup(Group group); + + /** + * Check if the current user has the given capability. + * + * @param capabilityName + * @return + */ + boolean hasCapability(NodeRef nodeRef, String capabilityName); } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java index ae8ae6ff34..0d130d8a55 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/CapabilityServiceImpl.java @@ -282,4 +282,24 @@ public class CapabilityServiceImpl implements CapabilityService groups.remove(group.getId()); } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService#hasCapability(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + @Override + public boolean hasCapability(NodeRef nodeRef, String capabilityName) + { + Capability capability = getCapability(capabilityName); + if (capability != null) + { + AccessStatus accessStatus = getCapabilityAccessState(nodeRef, capabilityName); + + if (accessStatus.equals(AccessStatus.ALLOWED)) + { + return true; + } + } + + return false; + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java index e929879a32..264915f9c6 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java @@ -37,6 +37,13 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; +import net.sf.acegisecurity.AccessDeniedException; +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.ConfigAttribute; +import net.sf.acegisecurity.ConfigAttributeDefinition; +import net.sf.acegisecurity.afterinvocation.AfterInvocationProvider; +import net.sf.acegisecurity.vote.AccessDecisionVoter; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.search.SimpleResultSetMetaData; @@ -62,13 +69,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; -import net.sf.acegisecurity.AccessDeniedException; -import net.sf.acegisecurity.Authentication; -import net.sf.acegisecurity.ConfigAttribute; -import net.sf.acegisecurity.ConfigAttributeDefinition; -import net.sf.acegisecurity.afterinvocation.AfterInvocationProvider; -import net.sf.acegisecurity.vote.AccessDecisionVoter; - /** * RM After Invocation Provider */ @@ -285,11 +285,8 @@ public class RMAfterInvocationProvider extends RMSecurityCommon { for (ConfigAttributeDefintion cad : supportedDefinitions) { - if (cad.parent && parentResult == AccessDecisionVoter.ACCESS_DENIED) - { - throw new AccessDeniedException("Access Denied"); - } - else if (!cad.parent && childResult == AccessDecisionVoter.ACCESS_DENIED) + if ((cad.parent && parentResult == AccessDecisionVoter.ACCESS_DENIED) + || (!cad.parent && childResult == AccessDecisionVoter.ACCESS_DENIED)) { throw new AccessDeniedException("Access Denied"); } @@ -358,11 +355,8 @@ public class RMAfterInvocationProvider extends RMSecurityCommon continue; } - if (cad.parent && parentReadCheck != AccessDecisionVoter.ACCESS_GRANTED) - { - throw new AccessDeniedException("Access Denied"); - } - else if (childReadCheck != AccessDecisionVoter.ACCESS_GRANTED) + if ((cad.parent && parentReadCheck != AccessDecisionVoter.ACCESS_GRANTED) + || (childReadCheck != AccessDecisionVoter.ACCESS_GRANTED)) { throw new AccessDeniedException("Access Denied"); } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java index 91c0802be9..894b618119 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/MayBeScheduledCapabilityCondition.java @@ -94,17 +94,8 @@ public class MayBeScheduledCapabilityCondition extends AbstractCapabilityConditi */ private boolean checkDispositionLevel(NodeRef nodeRef, DispositionSchedule dispositionSchedule) { - boolean result = false; boolean isRecordLevelDisposition = dispositionSchedule.isRecordLevelDisposition(); - if (recordService.isRecord(nodeRef) && isRecordLevelDisposition) - { - result = true; - } - else if (recordFolderService.isRecordFolder(nodeRef) && !isRecordLevelDisposition) - - { - result = true; - } - return result; + return (recordService.isRecord(nodeRef) && isRecordLevelDisposition) + || (recordFolderService.isRecordFolder(nodeRef) && !isRecordLevelDisposition); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java index fb77f3efb6..b86b1b0c2a 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/ContentDestructionComponent.java @@ -33,6 +33,7 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -63,6 +64,9 @@ public class ContentDestructionComponent /** behaviour filter */ private BehaviourFilter behaviourFilter; + /** Utility class for duplicating content */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** indicates whether cleansing is enabled or not */ private boolean cleansingEnabled = false; @@ -138,6 +142,15 @@ public class ContentDestructionComponent this.behaviourFilter = behaviourFilter; } + /** + * Setter for content duplication utility class + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * @param cleansingEnabled true if cleansing enabled, false otherwise */ @@ -214,16 +227,19 @@ public class ContentDestructionComponent // get content data ContentData dataContent = (ContentData)entry.getValue(); - // if enabled cleanse content - if (isCleansingEnabled()) + if (!contentBinDuplicationUtility.hasAtLeastOneOtherReference(nodeRef)) { - // register for cleanse then immediate destruction - getEagerContentStoreCleaner().registerOrphanedContentUrlForCleansing(dataContent.getContentUrl()); - } - else - { - // register for immediate destruction - getEagerContentStoreCleaner().registerOrphanedContentUrl(dataContent.getContentUrl(), true); + // if enabled cleanse content + if (isCleansingEnabled()) + { + // register for cleanse then immediate destruction + getEagerContentStoreCleaner().registerOrphanedContentUrlForCleansing(dataContent.getContentUrl()); + } + else + { + // register for immediate destruction + getEagerContentStoreCleaner().registerOrphanedContentUrl(dataContent.getContentUrl(), true); + } } // clear the property diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/cleanser/ContentCleanser.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/cleanser/ContentCleanser.java index 587b5b8534..fc79ab3d23 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/cleanser/ContentCleanser.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/content/cleanser/ContentCleanser.java @@ -34,6 +34,8 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Random; +import org.alfresco.error.AlfrescoRuntimeException; + /** * Content cleanser base implementation. * @@ -41,22 +43,22 @@ import java.util.Random; * @since 2.4.a */ public abstract class ContentCleanser -{ +{ /** * Cleanse file * * @param file file to cleanse */ public abstract void cleanse(File file); - + /** * Overwrite files bytes with provided overwrite operation * - * @param file file + * @param file file * @param overwriteOperation overwrite operation */ protected void overwrite(File file, OverwriteOperation overwriteOperation) - { + { // get the number of bytes long bytes = file.length(); try @@ -74,18 +76,18 @@ public abstract class ContentCleanser catch (IOException ioException) { // re-throw - throw new RuntimeException("Unable to overwrite file", ioException); + throw new AlfrescoRuntimeException("Unable to overwrite file", ioException); } } - + /** - * Overwrite operation + * Overwrite operation */ protected abstract class OverwriteOperation { public abstract void operation(OutputStream os) throws IOException; } - + /** * Overwrite with zeros operation */ @@ -96,7 +98,7 @@ public abstract class ContentCleanser os.write(0); } }; - + /** * Overwrite with ones operation */ @@ -107,14 +109,14 @@ public abstract class ContentCleanser os.write(0xff); } }; - + /** * Overwrite with random operation */ protected OverwriteOperation overwriteRandom = new OverwriteOperation() { private Random random = new Random(); - + public void operation(OutputStream os) throws IOException { byte[] randomByte = new byte[1]; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java index 6717718bc5..d12e072d5e 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/dataset/DataSetServiceImpl.java @@ -291,7 +291,7 @@ public class DataSetServiceImpl implements DataSetService, RecordsManagementMode } catch (Exception ex) { - throw new RuntimeException("Unexpected exception thrown. Please refer to the log files for details.", ex); + throw new AlfrescoRuntimeException("Unexpected exception thrown. Please refer to the log files for details.", ex); } finally { @@ -304,7 +304,7 @@ public class DataSetServiceImpl implements DataSetService, RecordsManagementMode } catch (IOException ex) { - throw new RuntimeException("Failed to close the input stream!", ex); + throw new AlfrescoRuntimeException("Failed to close the input stream!", ex); } } } 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 3d5b7e7700..31f0f88d4d 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/disposition/DispositionService.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java index dccc56df6b..1f221f9436 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java @@ -268,11 +268,9 @@ public interface DispositionService * * @param nodeRef The node which the schedule applies to. * @param dispositionActionDefinition The definition of the disposition action. - * @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property. * @return The new "disposition as of" date. */ - Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, - boolean allowContextFromAsOf); + Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition); /** * Gets the origin disposition schedule for the record, not the calculated one diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index e044026c75..6c74379f8c 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -132,7 +132,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl /** * Set the dictionary service * - * @param dictionaryServic the dictionary service + * @param dictionaryService the dictionary service */ @Override public void setDictionaryService(DictionaryService dictionaryService) @@ -231,7 +231,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl DispositionActionDefinition nextDispositionActionDefinition = dispositionActionDefinitions.get(0); // initialise the details of the next disposition action - initialiseDispositionAction(nodeRef, nextDispositionActionDefinition, true); + initialiseDispositionAction(nodeRef, nextDispositionActionDefinition); } } } @@ -436,7 +436,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (this.nodeService.hasAspect(nodeRef, ASPECT_SCHEDULED)) { List childAssocs = this.nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); - if (childAssocs.size() != 0) + if (!childAssocs.isEmpty()) { ChildAssociationRef firstChildAssocRef = childAssocs.get(0); result = firstChildAssocRef.getChildRef(); @@ -459,7 +459,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nodeService.exists(dsNodeRef)) { List assocs = this.nodeService.getParentAssocs(dsNodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); - if (assocs.size() != 0) + if (!assocs.isEmpty()) { if (assocs.size() != 1) { @@ -519,7 +519,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl * * @param isRecordLevelDisposition * @param rmContainer - * @param root * @return */ private List getDisposableItemsImpl(boolean isRecordLevelDisposition, NodeRef rmContainer) @@ -583,13 +582,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Check whether there is already a disposition schedule object present List assocs = nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); - if (assocs.size() == 0) + if (assocs.isEmpty()) { DispositionSchedule currentDispositionSchdule = getDispositionSchedule(nodeRef); if (currentDispositionSchdule != null) { List items = getDisposableItemsImpl(currentDispositionSchdule.isRecordLevelDisposition(), nodeRef); - if (items.size() != 0) + if (!items.isEmpty()) { throw new AlfrescoRuntimeException("Can not create a retention schedule if there are disposable items already under the control of an other retention schedule"); } @@ -671,7 +670,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl * Updates the given disposition action definition belonging to the given disposition * schedule. * - * @param schedule The DispositionSchedule the action belongs to * @param actionDefinition The DispositionActionDefinition to update * @param actionDefinitionParams Map of parameters to use to update the action definition * @return The updated DispositionActionDefinition @@ -698,12 +696,11 @@ public class DispositionServiceImpl extends ServiceBaseImpl * * @param nodeRef node reference * @param dispositionActionDefinition disposition action definition - * @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property. */ - private DispositionAction initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, boolean allowContextFromAsOf) + private DispositionAction initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition) { List childAssocs = nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, ASSOC_NEXT_DISPOSITION_ACTION, 1, true); - if (childAssocs != null && childAssocs.size() > 0) + if (childAssocs != null && !childAssocs.isEmpty()) { return new DispositionActionImpl(serviceRegistry, childAssocs.get(0).getChildRef()); } @@ -711,7 +708,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Create the properties Map props = new HashMap(10); - Date asOfDate = calculateAsOfDate(nodeRef, dispositionActionDefinition, allowContextFromAsOf); + Date asOfDate = calculateAsOfDate(nodeRef, dispositionActionDefinition); // Set the property values props.put(PROP_DISPOSITION_ACTION_ID, dispositionActionDefinition.getId()); @@ -745,12 +742,10 @@ public class DispositionServiceImpl extends ServiceBaseImpl * * @param nodeRef The node which the schedule applies to. * @param dispositionActionDefinition The definition of the disposition action. - * @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property. * @return The new "disposition as of" date. */ @Override - public Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, - boolean allowContextFromAsOf) + public Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition) { // Calculate the asOf date Date asOfDate = null; @@ -761,12 +756,27 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Get the period properties value QName periodProperty = dispositionActionDefinition.getPeriodProperty(); - if (periodProperty != null && (allowContextFromAsOf - || !RecordsManagementModel.PROP_DISPOSITION_AS_OF.equals(periodProperty))) + if (periodProperty != null) { - // doesn't matter if the period property isn't set ... the asOfDate will get updated later - // when the value of the period property is set - contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty); + if (RecordsManagementModel.PROP_DISPOSITION_AS_OF.equals(periodProperty)) + { + DispositionAction lastCompletedDispositionAction = getLastCompletedDispostionAction(nodeRef); + if (lastCompletedDispositionAction != null) + { + contextDate = lastCompletedDispositionAction.getCompletedAt(); + } + else + { + contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty); + } + + } + else + { + // doesn't matter if the period property isn't set ... the asOfDate will get updated later + // when the value of the period property is set + contextDate = (Date)this.nodeService.getProperty(nodeRef, periodProperty); + } } else { @@ -798,63 +808,77 @@ public class DispositionServiceImpl extends ServiceBaseImpl public boolean isNextDispositionActionEligible(NodeRef nodeRef) { boolean result = false; - // Get the disposition instructions DispositionSchedule di = getDispositionSchedule(nodeRef); - NodeRef nextDa = getNextDispositionActionNodeRef(nodeRef); + DispositionAction nextDa = getNextDispositionAction(nodeRef); if (di != null && this.nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE) && nextDa != null) { - // If it has an asOf date and it is greater than now the action is eligible - Date asOf = (Date)this.nodeService.getProperty(nextDa, PROP_DISPOSITION_AS_OF); - if (asOf != null && - asOf.before(new Date())) + // for accession step we can have also AND between step conditions + Boolean combineSteps = false; + if (nextDa.getName().equals("accession")) { - result = true; - } - - if (!result) - { - DispositionAction da = new DispositionActionImpl(serviceRegistry, nextDa); - DispositionActionDefinition dad = da.getDispositionActionDefinition(); - if (dad != null) - { - boolean firstComplete = dad.eligibleOnFirstCompleteEvent(); - - List assocs = this.nodeService.getChildAssocs(nextDa, ASSOC_EVENT_EXECUTIONS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef assoc : assocs) + NodeRef accessionNodeRef = di.getDispositionActionDefinitionByName("accession").getNodeRef(); + if (accessionNodeRef != null) { + Boolean combineStepsProp = (Boolean)this.nodeService.getProperty(accessionNodeRef, PROP_COMBINE_DISPOSITION_STEP_CONDITIONS); + if (combineStepsProp != null) { - NodeRef eventExecution = assoc.getChildRef(); - Boolean isCompleteValue = (Boolean)this.nodeService.getProperty(eventExecution, PROP_EVENT_EXECUTION_COMPLETE); - boolean isComplete = false; - if (isCompleteValue != null) - { - isComplete = isCompleteValue.booleanValue(); + combineSteps = combineStepsProp; + } + } + } + Date asOf = (Date)this.nodeService.getProperty(nextDa.getNodeRef(), PROP_DISPOSITION_AS_OF); + Boolean asOfDateInPast = false; + if (asOf != null) + { + asOfDateInPast = asOf.before(new Date()); + } + if (asOfDateInPast && !combineSteps) + { + return true; + } + else if(!asOfDateInPast && combineSteps) + { + return false; + } + DispositionAction da = new DispositionActionImpl(serviceRegistry, nextDa.getNodeRef()); + DispositionActionDefinition dad = da.getDispositionActionDefinition(); + if (dad != null) + { + boolean firstComplete = dad.eligibleOnFirstCompleteEvent(); - // implement AND and OR combination of event completions - if (isComplete) + List assocs = this.nodeService.getChildAssocs(nextDa.getNodeRef(), ASSOC_EVENT_EXECUTIONS, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef eventExecution = assoc.getChildRef(); + Boolean isCompleteValue = (Boolean)this.nodeService.getProperty(eventExecution, PROP_EVENT_EXECUTION_COMPLETE); + boolean isComplete = false; + if (isCompleteValue != null) + { + isComplete = isCompleteValue.booleanValue(); + + // implement AND and OR combination of event completions + if (isComplete) + { + result = true; + if (firstComplete) { - result = true; - if (firstComplete) - { - break; - } + break; } - else + } + else + { + result = false; + if (!firstComplete) { - result = false; - if (!firstComplete) - { - break; - } + break; } } } } } } - return result; } @@ -868,7 +892,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { NodeRef result = null; List assocs = nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, ASSOC_NEXT_DISPOSITION_ACTION, 1, true); - if (assocs.size() != 0) + if (!assocs.isEmpty()) { result = assocs.get(0).getChildRef(); } @@ -989,7 +1013,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nodeService.hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE)) { List assocs = nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, ASSOC_NEXT_DISPOSITION_ACTION); - if (assocs.size() > 0) + if (!assocs.isEmpty()) { currentDispositionAction = assocs.get(0).getChildRef(); } @@ -1044,7 +1068,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl nodeService.addAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE, null); } - initialiseDispositionAction(nodeRef, nextDispositionActionDefinition, false); + initialiseDispositionAction(nodeRef, nextDispositionActionDefinition); } } @@ -1117,14 +1141,14 @@ public class DispositionServiceImpl extends ServiceBaseImpl { DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dispositionSchedule); List assocs = nodeService.getChildAssocs(dispositionSchedule); - if (assocs != null && assocs.size() > 0) + if (assocs != null && !assocs.isEmpty()) { for (ChildAssociationRef assoc : assocs) { if (assoc != null && assoc.getQName().getLocalName().contains(dispositionActionName)) { DispositionActionDefinition actionDefinition = ds.getDispositionActionDefinition(assoc.getChildRef().getId()); - return calculateAsOfDate(record, actionDefinition, true); + return calculateAsOfDate(record, actionDefinition); } } } @@ -1339,14 +1363,14 @@ public class DispositionServiceImpl extends ServiceBaseImpl { recordOrFolder = folder; } - DispositionAction firstDispositionAction = initialiseDispositionAction(recordOrFolder, firstDispositionActionDef, true); + DispositionAction firstDispositionAction = initialiseDispositionAction(recordOrFolder, firstDispositionActionDef); newAction = firstDispositionAction.getNodeRef(); newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); } else if (firstDispositionActionDef.getPeriod() != null) { - Date firstActionDate = calculateAsOfDate(record, firstDispositionActionDef, true); + Date firstActionDate = calculateAsOfDate(record, firstDispositionActionDef); if (firstActionDate == null || (newDispositionActionDateAsOf != null && newDispositionActionDateAsOf.before(firstActionDate))) { diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java index a315160fa5..152562712c 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java @@ -32,6 +32,7 @@ import static org.apache.commons.lang3.BooleanUtils.isNotTrue; import java.io.Serializable; import java.util.Date; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -258,25 +259,9 @@ public class DispositionProperty extends BaseBehaviourBean */ private boolean isPropertyUpdated(Map before, Map after) { - boolean result = false; - Serializable beforeValue = before.get(propertyName); Serializable afterValue = after.get(propertyName); - if (beforeValue == null && afterValue != null) - { - result = true; - } - else if (beforeValue != null && afterValue == null) - { - result = true; - } - else if (beforeValue != null && afterValue != null && - !beforeValue.equals(afterValue)) - { - result = true; - } - - return result; + return !Objects.equals(beforeValue, afterValue); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java index 51839efb8d..ae6113deed 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracter.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -69,7 +70,13 @@ public class RFC822MetadataExtracter extends org.alfresco.repo.content.metadata. protected void filterSystemProperties(Map systemProperties, Map targetProperties) { NodeRef nodeRef = getNodeRef(targetProperties); - if (nodeRef == null || !nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD)) + if(nodeRef == null) + { + return; + } + + // Remove record properties from non-record nodes + if (!nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD)) { // Remove all rm namespace properties from the system map Map clone = new HashMap(systemProperties); @@ -81,6 +88,20 @@ public class RFC822MetadataExtracter extends org.alfresco.repo.content.metadata. } } } + + // Remove dod5015 properties from non-dod5015 nodes + if (!nodeService.hasAspect(nodeRef, DOD5015Model.ASPECT_DOD_5015_RECORD)) + { + // Remove all dod5015 namespace properties from the system map + Map clone = new HashMap<>(systemProperties); + for (QName propName : clone.keySet()) + { + if (DOD5015Model.DOD_URI.equals(propName.getNamespaceURI())) + { + systemProperties.remove(propName); + } + } + } } /** diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java index d20ca31db7..04ca0201a0 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java @@ -28,6 +28,8 @@ package org.alfresco.module.org_alfresco_module_rm.jscript.app; import static org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel.READ_RECORDS; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION; import static org.alfresco.service.cmr.security.AccessStatus.ALLOWED; import java.util.ArrayList; @@ -38,6 +40,9 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.capability.impl.ViewRecordsCapability; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; @@ -99,6 +104,11 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS /** site service */ private SiteService siteService; + /** + * Disposition service + */ + private DispositionService dispositionService; + /** Indicators */ private List indicators = new ArrayList(); @@ -154,6 +164,22 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS this.capabilityService = capabilityService; } + /** + * @return the filePlanService + */ + protected FilePlanService getFileplanService() + { + return this.filePlanService; + } + + /** + * @return the capabilityService + */ + protected CapabilityService getCapabilityService() + { + return this.capabilityService; + } + /** * @param dictionaryService dictionary service */ @@ -214,6 +240,14 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS this.jsonConversionComponentCache = jsonConversionComponentCache; } + /** + * @param dispositionService the disposition service + */ + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + /** * The initialise method */ @@ -440,6 +474,23 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS // Set the actions array setActions(rmNodeValues, nodeRef); + + //Add details of the next incomplete event in the disposition schedule + if(dispositionService.getNextDispositionAction(nodeRef) != null) + { + for(EventCompletionDetails details: dispositionService.getNextDispositionAction(nodeRef).getEventCompletionDetails()) + { + if(!details.isEventComplete()) + { + HashMap properties = ((HashMap) rmNodeValues.get("properties")); + properties.put("combineDispositionStepConditions", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS)); + properties.put("incompleteDispositionEvent", details.getEventName()); + properties.put("dispositionEventCombination", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION)); + break; + } + } + } + return rmNodeValues; } 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 e1ac53d9a5..7e6fc45ca3 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 @@ -99,6 +99,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel QName PROP_DISPOSITION_PERIOD_PROPERTY = QName.createQName(RM_URI, "dispositionPeriodProperty"); QName PROP_DISPOSITION_EVENT = QName.createQName(RM_URI, "dispositionEvent"); QName PROP_DISPOSITION_EVENT_COMBINATION = QName.createQName(RM_URI, "dispositionEventCombination"); + QName PROP_COMBINE_DISPOSITION_STEP_CONDITIONS = QName.createQName(RM_URI, "combineDispositionStepConditions"); QName PROP_DISPOSITION_LOCATION = QName.createQName(RM_URI, "dispositionLocation"); QName PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY = QName.createQName(RM_URI, "dispositionActionGhostOnDestroy"); @@ -246,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/model/rma/aspect/RecordAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java index 441af631bb..cf5e19cf26 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspect.java @@ -38,6 +38,7 @@ import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; @@ -94,6 +95,9 @@ public class RecordAspect extends AbstractDisposableItem /** quickShare service */ private QuickShareService quickShareService; + /** Utility class for duplicating content */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** I18N */ private static final String MSG_CANNOT_UPDATE_RECORD_CONTENT = "rm.service.update-record-content"; @@ -130,6 +134,15 @@ public class RecordAspect extends AbstractDisposableItem this.quickShareService = quickShareService; } + /** + * Setter for content duplication utility class + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * Behaviour to ensure renditions have the appropriate extended security. * @@ -356,7 +369,7 @@ public class RecordAspect extends AbstractDisposableItem extendedSecurityService.remove(targetNodeRef); //create a new content URL for the copy - createNewContentURL(targetNodeRef); + contentBinDuplicationUtility.duplicate(targetNodeRef); } } @@ -401,20 +414,7 @@ public class RecordAspect extends AbstractDisposableItem if (!nodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).isEmpty() || !nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).isEmpty()) { - //disable versioning and auditing - behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); - try - { - //create a new content URL for the copy/original node - createNewContentURL(nodeRef); - } - finally - { - //enable versioning and auditing - behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); - } + contentBinDuplicationUtility.duplicate(nodeRef); } return null; diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordComponentIdentifierAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordComponentIdentifierAspect.java index cdacc53990..d6d7b5db7d 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordComponentIdentifierAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordComponentIdentifierAspect.java @@ -63,8 +63,7 @@ import org.springframework.extensions.surf.util.I18NUtil; ) public class RecordComponentIdentifierAspect extends BaseBehaviourBean implements NodeServicePolicies.OnUpdatePropertiesPolicy, - NodeServicePolicies.BeforeDeleteNodePolicy, - CopyServicePolicies.OnCopyCompletePolicy + NodeServicePolicies.BeforeDeleteNodePolicy { /** I18N */ private static final String MSG_SET_ID = "rm.service.set-id"; @@ -177,31 +176,6 @@ public class RecordComponentIdentifierAspect extends BaseBehaviourBean return new DoNothingCopyBehaviourCallback(); } - @SuppressWarnings("rawtypes") - @Override - @Behaviour - ( - kind = BehaviourKind.CLASS, - notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT - ) - public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef targetNodeRef, boolean copyToNewNode, Map copyMap) - { - //Generate the id for the copy - String id = identifierService.generateIdentifier( - nodeService.getType(nodeService.getPrimaryParent(targetNodeRef).getParentRef()), - nodeService.getPrimaryParent(targetNodeRef).getParentRef()); - - //We need to allow the id to be overwritten disable the policy protecting changes to the id - behaviourFilter.disableBehaviour(); - try - { - nodeService.setProperty(targetNodeRef, PROP_IDENTIFIER, id); - } - finally - { - behaviourFilter.enableBehaviour(); - } - } /** * Updates the uniqueness check using the values provided. If the after value is null diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java index f4712a8995..c41e937aa1 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/VersionRecordAspect.java @@ -30,8 +30,10 @@ package org.alfresco.module.org_alfresco_module_rm.model.rma.aspect; import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.relationship.Relationship; import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; @@ -63,6 +65,11 @@ public class VersionRecordAspect extends BaseBehaviourBean /** relationship service */ private RelationshipService relationshipService; + /** + * Utility class for duplicating content + */ + private ContentBinDuplicationUtility contentBinDuplicationUtility; + /** * @param recordableVersionService recordable version service */ @@ -79,6 +86,16 @@ public class VersionRecordAspect extends BaseBehaviourBean this.relationshipService = relationshipService; } + /** + * Setter for content duplication utility class + * + * @param contentBinDuplicationUtility ContentBinDuplicationUtility + */ + public void setContentBinDuplicationUtility(ContentBinDuplicationUtility contentBinDuplicationUtility) + { + this.contentBinDuplicationUtility = contentBinDuplicationUtility; + } + /** * If the record is a version record then delete the associated version entry * @@ -143,7 +160,11 @@ public class VersionRecordAspect extends BaseBehaviourBean @Behaviour(kind = BehaviourKind.CLASS, notificationFrequency = NotificationFrequency.FIRST_EVENT) public void beforeAddAspect(NodeRef nodeRef, QName qName) { - //create a new content URL for the version record - createNewContentURL(nodeRef); + // if the node is the originating one the behaviour shouldn't be triggered + if (!nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_RECORD_ORIGINATING_DETAILS)) + { + //create a new content URL for the version record + contentBinDuplicationUtility.duplicate(nodeRef); + } } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java index 67a3ea526e..3d65ed4828 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java @@ -27,12 +27,9 @@ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; -import static org.alfresco.module.org_alfresco_module_rm.record.RecordUtils.generateRecordIdentifier; - import java.io.Serializable; import java.util.Map; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; @@ -203,18 +200,10 @@ public class RecordFolderType extends AbstractDisposableItem @Override public boolean getMustCopy(QName classQName, CopyDetails copyDetails) { - boolean result = true; - - if (nodeService.getType(copyDetails.getTargetParentNodeRef()).equals(TYPE_RECORD_FOLDER)) - { - result = false; - } - else if (ArrayUtils.contains(unwantedAspects, classQName)) - { - result = false; - } - - return result; + boolean targetParentIsRecordFolder = nodeService.getType(copyDetails.getTargetParentNodeRef()) + .equals(TYPE_RECORD_FOLDER); + boolean containsUnwantedAspect = ArrayUtils.contains(unwantedAspects, classQName); + return !(targetParentIsRecordFolder || containsUnwantedAspect); } }; } @@ -253,7 +242,7 @@ public class RecordFolderType extends AbstractDisposableItem } } - /** + /** * On transaction commit * * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/patch/compatibility/ModulePatchComponent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/patch/compatibility/ModulePatchComponent.java index 3a38dd40a9..86f2769f96 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/patch/compatibility/ModulePatchComponent.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/patch/compatibility/ModulePatchComponent.java @@ -27,12 +27,20 @@ package org.alfresco.module.org_alfresco_module_rm.patch.compatibility; +import static org.alfresco.repo.module.ModuleVersionNumber.VERSION_ZERO; + +import java.io.Serializable; + import org.alfresco.module.org_alfresco_module_rm.patch.ModulePatchExecuterImpl; +import org.alfresco.repo.admin.registry.RegistryKey; +import org.alfresco.repo.admin.registry.RegistryService; import org.alfresco.repo.module.AbstractModuleComponent; +import org.alfresco.repo.module.ModuleComponentHelper; +import org.alfresco.repo.module.ModuleVersionNumber; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -44,8 +52,12 @@ import org.apache.commons.logging.LogFactory; @Deprecated public abstract class ModulePatchComponent extends AbstractModuleComponent { + private static final String REGISTRY_PATH_MODULES = "modules"; + private static final String REGISTRY_PROPERTY_INSTALLED_VERSION = "installedVersion"; + private static final String REGISTRY_PROPERTY_CURRENT_VERSION = "currentVersion"; + /** logger */ - protected static final Log LOGGER = LogFactory.getLog(ModulePatchComponent.class); + protected static final Logger LOGGER = LoggerFactory.getLogger(ModulePatchComponent.class); /** Retrying transaction helper */ protected RetryingTransactionHelper retryingTransactionHelper; @@ -56,6 +68,9 @@ public abstract class ModulePatchComponent extends AbstractModuleComponent /** module patch executer */ protected ModulePatchExecuterImpl modulePatchExecuter; + /** Registry service */ + protected RegistryService registryService; + /** * @param retryingTransactionHelper retrying transaction helper */ @@ -80,6 +95,14 @@ public abstract class ModulePatchComponent extends AbstractModuleComponent this.modulePatchExecuter = modulePatchExecuter; } + /** + * @param registryService Registry service + */ + public void setRegistryService(RegistryService registryService) + { + this.registryService = registryService; + } + /** * Init method */ @@ -96,47 +119,107 @@ public abstract class ModulePatchComponent extends AbstractModuleComponent @Override protected void executeInternal() { - try + //Get the new module version + String moduleId = modulePatchExecuter.getModuleId(); + ModuleVersionNumber moduleNewVersionNumber = moduleService.getModule(moduleId).getModuleVersionNumber(); + + // Get the module details from the registry + ModuleVersionNumber moduleCurrentVersionNumber = getModuleVersionNumber(REGISTRY_PROPERTY_CURRENT_VERSION, + moduleId); + // Get the module patch component name + String moduleName = getName(); + if (moduleCurrentVersionNumber.equals(VERSION_ZERO) || + moduleCurrentVersionNumber.equals(moduleNewVersionNumber)) // No previous record of it { - if (LOGGER.isInfoEnabled()) + LOGGER.info("Module patch component '{}' is skipped, no previous version found.", moduleName); + } + else + { + if (isVersionLaterThan(moduleCurrentVersionNumber, moduleNewVersionNumber)) { - LOGGER.info("Module patch component '" + getName() + "' is executing ..."); + LOGGER.info("Module patch component '{}' is skipped for upgrade from version {} to version {}", + moduleName, moduleCurrentVersionNumber, moduleNewVersionNumber); } - - // execute path within an isolated transaction - retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + else { - @Override - public Void execute() + try { - behaviourFilter.disableBehaviour(); - try + LOGGER.info("Module patch component '{}' is executing ...", moduleName); + + // execute path within an isolated transaction + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { - executePatch(); - } - finally - { - behaviourFilter.enableBehaviour(); - } - return null; + @Override + public Void execute() + { + behaviourFilter.disableBehaviour(); + try + { + executePatch(); + } + finally + { + behaviourFilter.enableBehaviour(); + } + return null; + } + + }, false, true); + + LOGGER.info(" ... completed module patch '{}'", moduleName); + + } + catch (Exception exception) + { + // record the exception otherwise it gets swallowed + LOGGER.info(" ... error encountered. {}", exception.getMessage(), exception); + throw exception; } - - }, false, true); - - if (LOGGER.isInfoEnabled()) - { - LOGGER.info(" ... completed module patch '" + getName() + "'"); } } - catch (Exception exception) + } + + /** + * Helper method to get the ModuleVersionNumber. + */ + private ModuleVersionNumber getModuleVersionNumber(String registryProperty, String moduleId) + { + RegistryKey moduleKeyVersion = new RegistryKey(ModuleComponentHelper.URI_MODULES_1_0, + new String[]{REGISTRY_PATH_MODULES, moduleId, registryProperty}); + Serializable moduleVersion = this.registryService.getProperty(moduleKeyVersion); + + return new ModuleVersionNumber(moduleVersion.toString()); + } + + /** + * Helper method to determine if this is an upgrade from a version that already includes the early (v2.0, v2.1) + * patches. + * + */ + private boolean isVersionLaterThan(ModuleVersionNumber moduleCurrentVersionNumber, + ModuleVersionNumber moduleNewVersionNumber) + { + // assume that the v2.0 and v2.1 patches should be run + boolean versionLaterThan = false; + + // if this is an upgrade as opposed to a fresh install + if (moduleCurrentVersionNumber.compareTo(moduleNewVersionNumber) < 0) { - // record the exception otherwise it gets swallowed - if (LOGGER.isInfoEnabled()) + // if the installed version is later than the minimum version number of this patch + ModuleVersionNumber minVersion = this.getAppliesFromVersionNumber(); + if (moduleCurrentVersionNumber.compareTo(minVersion) >= 0) { - LOGGER.info(" ... error encountered. " + exception.getMessage(), exception); + versionLaterThan = true; } - throw exception; } + // v2.0 and v2.1 patches should not be run when both the current and the new version numbers are equals + else + { + versionLaterThan =true; + } + + return versionLaterThan; } /** diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/PropertyValuesOfChildrenQueryParams.java similarity index 79% rename from rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java rename to rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/PropertyValuesOfChildrenQueryParams.java index c5b5651e44..3eefd6b6bd 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/PropertyValuesOfChildrenQueryParams.java @@ -26,19 +26,16 @@ */ package org.alfresco.module.org_alfresco_module_rm.query; -import java.util.Collection; - /** - * Select parameter for select_CountChildrenWithPropertyValues. + * Select parameter for select_GetStringPropertyValuesOfChildren. * * @author Ana Manolache * @since 2.6 */ -public class ChildrenWithPropertyValuesQueryParams +public class PropertyValuesOfChildrenQueryParams { private Long parentId; private Long propertyQnameId; - private Collection propertyValues; public Long getParentId() { @@ -60,14 +57,5 @@ public class ChildrenWithPropertyValuesQueryParams this.propertyQnameId = propertyQnameId; } - public Collection getPropertyValues() - { - return propertyValues; - } - - public void setPropertyValues(Collection propertyValues) - { - this.propertyValues = propertyValues; - } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java index ea3a61c6dc..6810d68687 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java @@ -27,13 +27,14 @@ package org.alfresco.module.org_alfresco_module_rm.query; - import java.util.List; +import java.util.Set; import org.alfresco.service.cmr.repository.NodeRef; import java.util.Collection; import org.alfresco.service.namespace.QName; + /** * Records management query DAO * @@ -66,13 +67,17 @@ public interface RecordsManagementQueryDAO /** * Returns whether a given node contains children with one of the given values for the given property + * Returns distinct property values from children for the given property * * @param parent the parent to evaluate * @param property the QName of the property to evaluate - * @param propertyValues the list of values to look for - * @return true if there is at least one child with one of the values from the list set on the given property - * false otherwise + * @return list of distinct property values */ - public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues); + public Set getChildrenStringPropertyValues(NodeRef parent, QName property); + /** + * @param contentUrl the URL of the content url entity + * @return Set a set of nodes that reference the given content url + */ + Set getNodeRefsWhichReferenceContentUrl(String contentUrl); } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java index 7c7ac5fc6c..36131f95fa 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java @@ -27,20 +27,28 @@ package org.alfresco.module.org_alfresco_module_rm.query; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.domain.contentdata.ContentUrlEntity; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.version.Version2Model; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.mybatis.spring.SqlSessionTemplate; /** @@ -51,29 +59,43 @@ import org.mybatis.spring.SqlSessionTemplate; */ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, RecordsManagementModel { + /** + * logger + */ + @SuppressWarnings("unused") + private static final Log logger = LogFactory.getLog(RecordsManagementQueryDAOImpl.class); + + /** + * query names + */ private static final String COUNT_IDENTIFIER = "alfresco.query.rm.select_CountRMIndentifier"; + private static final String GET_CHILDREN_PROPERTY_VALUES = "select_GetStringPropertyValuesOfChildren"; + private static final String SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL = "select_NodeIdsWhichReferenceContentUrl"; private static final String SCHEDULED_FOLDERS = "alfresco.query.rm.select_RecordFoldersWithSchedules"; private static final String SCHEDULED_FOLDERS_COUNT = "alfresco.query.rm.select_RecordFoldersWithSchedulesCount"; - private static final String COUNT_CHILDREN_WITH_PROPERTY_VALUES = "select_CountChildrenWithPropertyValues"; - /** SQL session template */ + /** + * SQL session template + */ protected SqlSessionTemplate template; - - /** QName DAO */ + + /** + * QName DAO + */ protected QNameDAO qnameDAO; protected NodeDAO nodeDAO; protected TenantService tenantService; - + /** - * @param sqlSessionTemplate SQL session template + * @param sqlSessionTemplate SQL session template */ - public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) + public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.template = sqlSessionTemplate; } - + /** - * @param qnameDAO qname DAO + * @param qnameDAO qname DAO */ public final void setQnameDAO(QNameDAO qnameDAO) { @@ -97,28 +119,143 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, public int getCountRmaIdentifier(String identifierValue) { int result = 0; - + // lookup the id of the identifier property qname Pair pair = qnameDAO.getQName(PROP_IDENTIFIER); if (pair != null) - { + { // create query params - Map params = new HashMap(2); + Map params = new HashMap<>(2); params.put("qnameId", pair.getFirst()); params.put("idValue", identifierValue); - + // return the number of rma identifiers found that match the passed value - Integer count = (Integer)template.selectOne(COUNT_IDENTIFIER, params); - + Integer count = (Integer) template.selectOne(COUNT_IDENTIFIER, params); + if (count != null) { result = count; } } - + return result; } + @Override + public Set getChildrenStringPropertyValues(NodeRef parent, QName property) + { + PropertyValuesOfChildrenQueryParams queryParams = new PropertyValuesOfChildrenQueryParams(); + + // Set the parent node id + Pair nodePair = nodeDAO.getNodePair(tenantService.getName(parent)); + if (nodePair == null) + { + throw new InvalidNodeRefException("The parent node does not exist.", parent); + } + Long parentNodeId = nodePair.getFirst(); + queryParams.setParentId(parentNodeId); + + // Set the property qname id + Pair pair = qnameDAO.getQName(property); + if (pair == null) + { + return Collections.emptySet(); + } + queryParams.setPropertyQnameId(pair.getFirst()); + + // Perform the query + return new HashSet<>(template.selectList(GET_CHILDREN_PROPERTY_VALUES, queryParams)); + + } + + /** + * Get a set of node reference which reference the provided content URL + * + * @param String contentUrl content URL + * @return Set set of nodes that reference the provided content URL + */ + @Override + public Set getNodeRefsWhichReferenceContentUrl(String contentUrl) + { + if (logger.isDebugEnabled()) + { + logger.debug("Getting nodes that reference content URL = " + contentUrl); + } + + // create the content URL entity used to query for nodes + ContentUrlEntity contentUrlEntity = new ContentUrlEntity(); + contentUrlEntity.setContentUrl(contentUrl.toLowerCase()); + + if (logger.isDebugEnabled()) + { + logger.debug("Executing query " + SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL); + } + + // Get all the node ids which reference the given content url + List nodeIds = template.selectList(SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL, contentUrlEntity); + + if (logger.isDebugEnabled()) + { + logger.debug("Query " + SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL + " returned " + nodeIds.size() + " results"); + } + + // create a set of uuids which reference the content url + Set nodesReferencingContentUrl = new HashSet(nodeIds.size()); + for (Long nodeId : nodeIds) + { + StringBuilder logMessage = null; + NodeRef nodeRefToAdd; + + if (nodeId != null && nodeDAO.exists(nodeId)) + { + if (logger.isDebugEnabled()) + { + logMessage = new StringBuilder("Adding noderef "); + } + + // if the referencing node is a version2Store reference to the content url, add the version 2 frozen node ref + NodeRef version2FrozenNodeRef = (NodeRef) nodeDAO.getNodeProperty(nodeId, Version2Model.PROP_QNAME_FROZEN_NODE_REF); + if (version2FrozenNodeRef != null && nodeDAO.exists(version2FrozenNodeRef)) + { + nodeRefToAdd = version2FrozenNodeRef; + + if (logger.isDebugEnabled()) + { + logMessage.append(nodeRefToAdd) + .append(" (from version)"); + } + } + + // add the node ref of the referencing node + else + { + nodeRefToAdd = nodeDAO.getNodeIdStatus(nodeId) + .getNodeRef(); + if (logger.isDebugEnabled()) + { + logMessage.append(nodeRefToAdd); + } + } + + nodesReferencingContentUrl.add(nodeRefToAdd); + + if (logger.isDebugEnabled()) + { + logger.debug(logMessage.toString()); + } + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Not adding " + nodeId + " (exist==false)"); + } + } + } + + return nodesReferencingContentUrl; + } + /** * @see org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO#getRecordFoldersWithSchedules(Long, Long) */ @@ -147,40 +284,4 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, return results; } - @Override - public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues) - { - if(propertyValues.isEmpty()) - { - return false; - } - - ChildrenWithPropertyValuesQueryParams queryParams = new ChildrenWithPropertyValuesQueryParams(); - - // Set the parent node id - Pair nodePair = nodeDAO.getNodePair(tenantService.getName(parent)); - if (nodePair == null) - { - throw new InvalidNodeRefException("The parent node does not exist.", parent); - } - Long parentNodeId = nodePair.getFirst(); - queryParams.setParentId(parentNodeId); - - // Set the property qname id - Pair pair = qnameDAO.getQName(property); - if (pair == null) - { - return false; - } - queryParams.setPropertyQnameId(pair.getFirst()); - - - // Set the property values - queryParams.setPropertyValues(propertyValues); - - // Perform the query - Long count = template.selectOne(COUNT_CHILDREN_WITH_PROPERTY_VALUES, queryParams); - return count > 0; - } - } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java index bb6882482d..0d9b536a89 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java @@ -45,6 +45,7 @@ import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.util.FileUtils; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authority.RMAuthority; import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; @@ -60,6 +61,8 @@ import org.apache.commons.lang.StringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -71,6 +74,8 @@ import org.springframework.extensions.surf.util.I18NUtil; public class FilePlanRoleServiceImpl implements FilePlanRoleService, RecordsManagementModel { + /** Logger for the class. */ + private static final Logger LOGGER = LoggerFactory.getLogger(FilePlanRoleServiceImpl.class); /** I18N */ private static final String MSG_ALL_ROLES = "rm.role.all"; @@ -269,7 +274,7 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, { throw new AlfrescoRuntimeException("Could not load default bootstrap roles configuration"); } - array = new JSONArray(convertStreamToString(is)); + array = new JSONArray(FileUtils.convertStreamToString(is)); } catch (IOException ioe) { @@ -366,40 +371,6 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, }, AuthenticationUtil.getSystemUserName()); } - /** - * Helper method to convert a stream to a string. - * - * @param is input stream - * @return {@link String} string - * @throws IOException - */ - public String convertStreamToString(InputStream is) throws IOException - { - /* - * To convert the InputStream to String we use the BufferedReader.readLine() - * method. We iterate until the BufferedReader return null which means - * there's no more data to read. Each line will appended to a StringBuilder - * and returned as String. - */ - BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); - StringBuilder sb = new StringBuilder(); - - String line = null; - try - { - while ((line = reader.readLine()) != null) - { - sb.append(line + "\n"); - } - } - finally - { - try {is.close();} catch (IOException e) {} - } - - return sb.toString(); - } - /** * Helper method to check whether the current authority is a system role or not * diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java index a73df97db2..9e880b37bd 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/AuditLogGet.java @@ -57,6 +57,7 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript private static final String PARAM_EXPORT = "export"; private static final String ACCESS_AUDIT_CAPABILITY = "AccessAudit"; + private static final int DEFAULT_VIEW_LOG_MAX_SIZE = 100; /** Content Streamer */ protected ContentStreamer contentStreamer; @@ -66,6 +67,9 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript /** File plan service */ protected FilePlanService filePlanService; + + /** Maximum number of entries to be displayed in View Audit Log */ + private int viewLogMaxSize; /** * @param contentStreamer @@ -86,13 +90,22 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript /** * - * @param capabilityService Capability Service + * @param filePlanService File Plan Service */ public void setFilePlanService(FilePlanService filePlanService) { this.filePlanService = filePlanService; } + /** + * + * @param viewLogMaxSize Maximum number of entries to be displayed in View Audit Log + */ + public void setViewLogMaxSize(int viewLogMaxSize) + { + this.viewLogMaxSize = (viewLogMaxSize <= 0 ? DEFAULT_VIEW_LOG_MAX_SIZE: viewLogMaxSize); + } + @Override public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { @@ -100,7 +113,6 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript try { - RecordsManagementAuditQueryParameters queryParams = parseQueryParameters(req); ReportFormat reportFormat = parseReportFormat(req); @@ -108,6 +120,13 @@ public class AuditLogGet extends BaseAuditRetrievalWebScript { throw new WebScriptException(Status.STATUS_FORBIDDEN, "Access denied because the user does not have the Access Audit capability"); } + + // limit the number of audit log entries to be returned + if (queryParams.getMaxEntries() <= 0 || queryParams.getMaxEntries() > viewLogMaxSize) + { + queryParams.setMaxEntries(viewLogMaxSize); + } + // parse the parameters and get a file containing the audit trail auditTrail = this.rmAuditService.getAuditTrailFile(queryParams, reportFormat); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java index 946dd9902c..ff6a5540e9 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionAbstractBase.java @@ -27,6 +27,8 @@ package org.alfresco.module.org_alfresco_module_rm.script; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -49,6 +51,8 @@ import org.springframework.extensions.webscripts.WebScriptRequest; */ public class DispositionAbstractBase extends AbstractRmWebScript { + + public final static String COMBINE_DISPOSITION_STEP_CONDITIONS = "combineDispositionStepConditions"; /** * Parses the request and providing it's valid returns the DispositionSchedule object. * @@ -165,6 +169,11 @@ public class DispositionAbstractBase extends AbstractRmWebScript model.put("events", eventNames); } + if(getNodeService().getProperty(actionDef.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS) != null) + { + model.put("combineDispositionStepConditions", getNodeService().getProperty(actionDef.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS)); + } + return model; } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java index 141f008c50..54f689fd93 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPost.java @@ -133,6 +133,12 @@ public class DispositionActionDefinitionPost extends DispositionAbstractBase json.getBoolean("eligibleOnFirstCompleteEvent") ? "or" : "and"); } + if (json.has(COMBINE_DISPOSITION_STEP_CONDITIONS)) + { + props.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS, + json.getBoolean(COMBINE_DISPOSITION_STEP_CONDITIONS)); + } + if (json.has("location")) { props.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java index 828171087d..6171f0cb1f 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/DispositionActionDefinitionPut.java @@ -96,7 +96,6 @@ public class DispositionActionDefinitionPut extends DispositionAbstractBase * * @param actionDef The action definition to update * @param json The JSON to use to create the action definition - * @param schedule The DispositionSchedule the action definition belongs to * @return The updated DispositionActionDefinition */ protected DispositionActionDefinition updateActionDefinition(DispositionActionDefinition actionDef, @@ -132,6 +131,12 @@ public class DispositionActionDefinitionPut extends DispositionAbstractBase json.getBoolean("eligibleOnFirstCompleteEvent") ? "or" : "and"); } + if (json.has(COMBINE_DISPOSITION_STEP_CONDITIONS)) + { + props.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS, + json.getBoolean(COMBINE_DISPOSITION_STEP_CONDITIONS)); + } + if (json.has("location")) { props.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, 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..ad9a50e6bc --- /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 - 2019 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 9ef7af64fe..4b5f18819f 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; @@ -53,6 +55,8 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.Status; @@ -66,6 +70,8 @@ import org.springframework.extensions.webscripts.WebScriptRequest; */ public class RMSearchGet extends DeclarativeWebScript { + /** Logger for the class. */ + private static final Logger LOGGER = LoggerFactory.getLogger(RMSearchGet.class); /** URL Parameters */ private static final String PARAM_QUERY = "query"; private static final String PARAM_SORTBY = "sortby"; @@ -96,6 +102,12 @@ public class RMSearchGet extends DeclarativeWebScript /** Person data cache */ private Map personDataCache = null; + /** Utility class for record categories */ + private RecordCategoryUtil recordCategoryUtil; + + /** Utility class for classification reasons (enterprise only) */ + private ClassificationReasonsUtil classificationReasonsUtil; + /** * @param recordsManagementSearchService records management search service */ @@ -144,6 +156,19 @@ public class RMSearchGet extends DeclarativeWebScript this.permissionService = permissionService; } + /** + * @param recordCategoryUtil utility class for record categories + */ + public void setRecordCategoryUtil(RecordCategoryUtil recordCategoryUtil) + { + this.recordCategoryUtil = recordCategoryUtil; + } + + public void setClassificationReasonsUtil(ClassificationReasonsUtil classificationReasonsUtil) + { + this.classificationReasonsUtil = classificationReasonsUtil; + } + /** * @param personService person service */ @@ -183,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); @@ -211,7 +242,10 @@ public class RMSearchGet extends DeclarativeWebScript Item item = new Item(pair.getFirst(), pair.getSecond()); items.add(item); } - catch(Exception e) {} + catch(Exception e) + { + LOGGER.debug("Ignoring failed attempt to add item to search results.", e); + } } // Return model @@ -240,6 +274,7 @@ public class RMSearchGet extends DeclarativeWebScript private String createdBy; private Map nodeProperties; private Map properties; + private String recordCategoryId; public Item(NodeRef parent, NodeRef nodeRef) { @@ -333,6 +368,7 @@ public class RMSearchGet extends DeclarativeWebScript properties.put(prefixName, entry.getValue()); } } + properties.put("rma_recordCategoryIdentifier", recordCategoryUtil.getCategoryIdFromNodeId(nodeRef, false)); } private String getDisplayName(String userName) @@ -438,5 +474,15 @@ public class RMSearchGet extends DeclarativeWebScript { return properties; } + + public String getRecordCategoryId() + { + return recordCategoryId; + } + + public void setRecordCategoryId(String recordCategoryId) + { + this.recordCategoryId = recordCategoryId; + } } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtil.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtil.java new file mode 100644 index 0000000000..cf1dec0d02 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtil.java @@ -0,0 +1,86 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_IDENTIFIER; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_CATEGORY; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_FOLDER; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.Path.ChildAssocElement; + +/** + * Utility class for record categories + * @author Ross Gale + * @since 2.7 + */ +public class RecordCategoryUtil +{ + /** + * Node service + */ + private NodeService nodeService; + + /** + * Setter for the node service + * @param nodeService Node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Return the record category id for a file plan element + * @param nodeRef the node reference of the file plan element + * @param includeFolders return an id for records, folders and categories + * @return Record category identifier + */ + public String getCategoryIdFromNodeId(NodeRef nodeRef, boolean includeFolders) + { + if(!includeFolders) + { + if (nodeService.getType(nodeRef).equals(TYPE_RECORD_FOLDER) || nodeService.getType(nodeRef).equals(TYPE_RECORD_CATEGORY)) + { + return null; + } + } + //Search for the first category from the end of the path to save time + Path path = nodeService.getPath(nodeRef); + for(int x = path.size()-1; x >= 0; x--) + { + NodeRef ref = ((ChildAssocElement) path.get(x)).getRef().getChildRef(); + if (nodeService.getType(ref).equals(TYPE_RECORD_CATEGORY)) + { + return nodeService.getProperty(ref, PROP_IDENTIFIER).toString(); + } + } + return null; + } +} 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..5917d48c90 --- /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 - 2019 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/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java new file mode 100644 index 0000000000..68a0bab6c3 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtility.java @@ -0,0 +1,142 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.util; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; + +import java.util.Set; + +/** + * Utility class to duplicate the content of a node without triggering the audit or versioning behaviours + * @author Ross Gale + * @since 2.7.2 + */ +public class ContentBinDuplicationUtility extends ServiceBaseImpl +{ + + /** + * Behaviour filter + */ + private BehaviourFilter behaviourFilter; + + /** + * Provides methods for accessing and transforming content. + */ + private ContentService contentService; + + /** Records Management Query DAO */ + private RecordsManagementQueryDAO recordsManagementQueryDAO; + + /** + * Setter for behaviour filter + * @param behaviourFilter BehaviourFilter + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + /** + * Setter for content service + * @param contentService ContentService + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Setter for the Records Management QueryDAO + * + * @param recordsManagementQueryDAO The RM query DAO to set + */ + public void setRecordsManagementQueryDAO(RecordsManagementQueryDAO recordsManagementQueryDAO) + { + this.recordsManagementQueryDAO = recordsManagementQueryDAO; + } + + /** + * Determines whether the bin file for a given node has at least one other reference to it + * Will return true if the binary exists and is referenced by at least one other node + * @param nodeRef Node with the binary in question + * @return boolean for if the bin has at least one other reference to it + */ + public boolean hasAtLeastOneOtherReference(NodeRef nodeRef) + { + boolean hasAtLeastOneOtherReference = false; + String contentUrl = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT).getContentUrl(); + + Set referencesToContentNode = recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(contentUrl); + if (referencesToContentNode.size() > 1) + { + hasAtLeastOneOtherReference = true; + } + return hasAtLeastOneOtherReference; + } + + /** + * Duplicate the content of a node without triggering the audit or versioning behaviours + * + * @param nodeRef The node with the content to duplicate + */ + public void duplicate(NodeRef nodeRef) + { + //disabling versioning and auditing + behaviourFilter.disableBehaviour(); + try + { + //create a new content URL for the copy/original node + updateContentProperty(nodeRef); + } + finally + { + //enable versioning and auditing + behaviourFilter.enableBehaviour(); + } + } + + /** + * Helper to update the content property for the node + * + * @param nodeRef the node + */ + private void updateContentProperty(NodeRef nodeRef) + { + ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (reader != null) + { + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.putContent(reader); + } + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/FileUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/FileUtils.java new file mode 100644 index 0000000000..aff77ddb61 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/FileUtils.java @@ -0,0 +1,64 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +/** + * Utility class for working with files. + * + * @author Roxana Lucanu + * @since 2.7 + */ +public class FileUtils +{ + /** + * Helper method to convert a stream to a string. + * + * @param is input stream + * @return {@link String} string + * @throws IOException + */ + public static String convertStreamToString(InputStream is) throws IOException + { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")))) + { + StringBuilder sb = new StringBuilder(); + String line = null; + + while ((line = reader.readLine()) != null) + { + sb.append(line + "\n"); + } + return sb.toString(); + } + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMCollectionUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMCollectionUtils.java index 026275a5ab..a1828d6ad7 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMCollectionUtils.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMCollectionUtils.java @@ -39,6 +39,8 @@ import java.util.Set; import com.google.common.collect.ImmutableSet; +import org.apache.commons.collections.CollectionUtils; + /** * Various common helper methods for Collections. This class is probably only appropriate for use with relatively * small collections as it has not been optimised for dealing with large collections. @@ -226,4 +228,21 @@ public final class RMCollectionUtils } return ImmutableSet.copyOf(collection); } + + /** + * Check if a property is null or an empty collection. Note that this is the same as + * {org.apache.commons.collections.CollectionUtils.isEmpty(Collection)}, except that it takes a Serializable rather + * than a Collection. This avoids awkward casting exceptions when working with properties. + * + * @param value The (probably Collection) value to check. + * @return true if the supplied value is null or an empty collection. + */ + public static boolean isEmpty(Serializable value) + { + if (value instanceof Collection) + { + return CollectionUtils.isEmpty((Collection) value); + } + return (value == null); + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java index 8fc5094b05..f7a58ea1ff 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; -import org.alfresco.model.ContentModel; + import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; @@ -39,9 +39,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; @@ -554,30 +552,4 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte result.add(nodeService.getType(nodeRef)); return result; } - - /** - * Helper to update the given content property for the node - * - * @param nodeRef the node - * @param contentProperty the property to be updated - */ - protected void updateContentProperty(NodeRef nodeRef, QName contentProperty) - { - ContentReader reader = contentService.getReader(nodeRef, contentProperty); - if (reader != null) - { - ContentWriter writer = contentService.getWriter(nodeRef, contentProperty, true); - writer.putContent(reader); - } - } - - /** - * Helper to create a new content URL for the node - * - * @param nodeRef the node - */ - protected void createNewContentURL(NodeRef nodeRef) - { - updateContentProperty(nodeRef, ContentModel.PROP_CONTENT); - } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java index e5082d7f0e..b9abf212ac 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java @@ -358,7 +358,7 @@ public class DynamicAuthoritiesGet extends AbstractWebScript implements RecordsM res.getWriter().write(")"); } } - catch (Throwable e) + catch (Exception e) { if (logger.isDebugEnabled()) { @@ -416,7 +416,7 @@ public class DynamicAuthoritiesGet extends AbstractWebScript implements RecordsM protected Long getBatchSizeParameter(WebScriptRequest req) { String batchSizeStr = req.getParameter(BATCH_SIZE); - Long size = 0L; + Long size; if (StringUtils.isBlank(batchSizeStr)) { logger.info(MESSAGE_BATCHSIZE_IS_MANDATORY); 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 9cae31c6e3..52eab95b0a 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 @@ -27,6 +27,7 @@ package org.alfresco.rm.rest.api.impl; +import static org.alfresco.model.ContentModel.TYPE_FOLDER; import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; import static org.alfresco.util.ParameterCheck.mandatory; @@ -126,6 +127,8 @@ public class FilePlanComponentsApiUtils public static final String UNFILED_ALIAS = "-unfiled-"; public static final String HOLDS_ALIAS = "-holds-"; public static final String RM_SITE_ID = "rm"; + public static final List CONTAINERS_FOR_CLASSIFIABLE_CHILDREN_ALIAS = Arrays.asList( + FILE_PLAN_ALIAS, UNFILED_ALIAS); //public static String PARAM_RELATIVE_PATH = "relativePath"; // excluded properties @@ -258,9 +261,71 @@ public class FilePlanComponentsApiUtils ParameterCheck.mandatoryString("nodeId", nodeId); ParameterCheck.mandatory("expectedNodeType", expectedNodeType); - /* - * Lookup by placeholder - */ + NodeRef nodeRef = lookupByPlaceholder(nodeId); + + QName nodeType = nodeService.getType(nodeRef); + if (!nodeType.equals(expectedNodeType)) + { + throw new InvalidArgumentException("The given id:'" + nodeId + "' (nodeType:" + nodeType.toString() + + ") is not valid for this endpoint. Expected nodeType is:" + expectedNodeType.toString()); + } + + if(StringUtils.isNotBlank(relativePath)) + { + nodeRef = lookupAndValidateRelativePath(nodeRef, relativePath, readOnlyRelativePath, expectedNodeType); + } + return nodeRef; + } + + /** + * look up node by id and validate node type is suitable container + * + * @param nodeId + * @return + */ + public NodeRef validateAndLookUpContainerNode(String nodeId, List allowedPlaceholders) + { + ParameterCheck.mandatoryString("nodeId", nodeId); + + NodeRef nodeRef = lookupByAllowedPlaceholders(nodeId, allowedPlaceholders); + QName nodeType = nodeService.getType(nodeRef); + if(!dictionaryService.isSubClass(nodeType, TYPE_FOLDER)) + { + throw new InvalidArgumentException("The given id:'" + nodeId + "' (nodeType:" + nodeType.toString() + + ") is not valid for this endpoint. Expected nodeType is:" + TYPE_FOLDER.toString()); + } + + return nodeRef; + } + /** + * Lookup node by placeholder from allowed placeholder list + * + * @param nodeId + * @param allowedPlaceholders + * @return NodeRef for corresponding id + */ + public NodeRef lookupByAllowedPlaceholders(String nodeId, List allowedPlaceholders) + { + NodeRef nodeRef; + if (allowedPlaceholders.contains(nodeId)) + { + nodeRef = lookupByPlaceholder(nodeId); + } + else + { + nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId); + } + return nodeRef; + } + + /** + * Lookup node by placeholder + * + * @param nodeId + * @return NodeRef for corresponding id + */ + public NodeRef lookupByPlaceholder(String nodeId) + { NodeRef nodeRef; if (nodeId.equals(FILE_PLAN_ALIAS)) { @@ -314,18 +379,7 @@ public class FilePlanComponentsApiUtils { nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId); } - - QName nodeType = nodeService.getType(nodeRef); - if (!nodeType.equals(expectedNodeType)) - { - throw new InvalidArgumentException("The given id:'" + nodeId + "' (nodeType:" + nodeType.toString() - + ") is not valid for this endpoint. Expected nodeType is:" + expectedNodeType.toString()); - } - - if(StringUtils.isNotBlank(relativePath)) - { - nodeRef = lookupAndValidateRelativePath(nodeRef, relativePath, readOnlyRelativePath, expectedNodeType); - } + return nodeRef; } @@ -584,7 +638,7 @@ public class FilePlanComponentsApiUtils } /** * Helper method that converts a map of String properties into a map of QName properties - * @param props + * @param properties * @return a map of properties */ public Map mapToNodeProperties(Map properties) @@ -931,7 +985,7 @@ public class FilePlanComponentsApiUtils } String pathStr = null; - if (pathElements.size() > 0) + if (!pathElements.isEmpty()) { StringBuilder sb = new StringBuilder(120); for (PathInfo.ElementInfo e : pathElements) diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4804Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4804Test.java index e6ed70983b..5de7ee766c 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4804Test.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4804Test.java @@ -46,8 +46,6 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.springframework.extensions.webscripts.GUID; -import javax.xml.soap.Node; - /** * Integration test for RM-4804 * diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM5225Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM5225Test.java new file mode 100644 index 0000000000..7d73d4ba71 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM5225Test.java @@ -0,0 +1,80 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.issue; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.GUID; + +/** + * Integration test for RM-5225 + * + * Copy of record from a RM site does not append the correct id at the end of the record name. + */ +public class RM5225Test extends BaseRMTestCase +{ + /** + * Given the RM site, a record category created in the fileplan, a record folder containing a record + * When we create a copy from the existing record + * Then the created record name contains both the name of the record from which it was created and the unique identifier of the current record. + */ + public void testCopyToRecord() + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef record; + private NodeRef copiedRecord; + + public void given() + { + + /** Create record category. */ + NodeRef recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate()); + + /** Create record folder. */ + NodeRef recordFolder = recordFolderService.createRecordFolder(recordCategory, GUID.generate()); + + /** File record. */ + record = utils.createRecord(recordFolder, GUID.generate()); + } + + public void when() + { + /** Create a copy of the original record */ + copiedRecord = recordService.createRecordFromCopy(filePlan, record); + } + + public void then() + { + /** Check if the copied record contains the name of the record from which is copied. */ + assertTrue(nodeService.getProperty(copiedRecord, PROP_NAME).toString().contains(nodeService.getProperty(record, PROP_NAME).toString())); + /** Check if the copied record name contains its unique id. */ + assertTrue(nodeService.getProperty(copiedRecord, PROP_NAME).toString().contains(nodeService.getProperty(copiedRecord, PROP_IDENTIFIER).toString())); + } + }); + } +} diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java index 39b7cad51b..b8e5ec7120 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementAuditServiceImplTest.java @@ -33,6 +33,8 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditEntry; @@ -78,18 +80,18 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase @Override public Void run() throws Exception { - // test start time recorded - testStartTime = new Date(); + // test start time recorded + testStartTime = new Date(); - // Stop and clear the log - rmAuditService.stopAuditLog(filePlan); - rmAuditService.clearAuditLog(filePlan); - rmAuditService.startAuditLog(filePlan); + // Stop and clear the log + rmAuditService.stopAuditLog(filePlan); + rmAuditService.clearAuditLog(filePlan); + rmAuditService.startAuditLog(filePlan); - // check that audit service is started - assertTrue(rmAuditService.isAuditLogEnabled(filePlan)); + // check that audit service is started + assertTrue(rmAuditService.isAuditLogEnabled(filePlan)); - return null; + return null; } }); } @@ -198,7 +200,7 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase } }); } - + /** * Test getAuditTrail method and parameter filters. */ @@ -459,6 +461,116 @@ public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase assertTrue("Expected to hit successful login attempt for Charles Dickons (cdickons)", found); } + /** + * Given I have deleted a user + * When I will get the RM audit filter by delete user event + * Then there will be an entry for the deleted user + * And the audit entry has the username property value audited + * @throws Exception + */ + @org.junit.Test + public void testAuditForDeletedUser() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + final static String DELETE_USER_AUDIT_EVENT = "Delete Person"; + String userName = "auditDeleteUser"; + NodeRef user; + List entry; + + @Override + public void given() throws Exception + { + // create a user + user = createPerson(userName); + personService.deletePerson(userName); + } + + @Override + public void when() throws Exception + { + // set the audit wuery param + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setEvent(DELETE_USER_AUDIT_EVENT); + + // get the audit events for "Delete Person" + entry = getAuditTrail(params, 1, ADMIN_USER); + } + + @Override + public void then() throws Exception + { + assertEquals("Delete user event is not audited.", DELETE_USER_AUDIT_EVENT, entry.get(0).getEvent()); + assertEquals(user.getId(), entry.get(0).getNodeName()); + assertEquals("Unexpected nr of properties audited for cm:person type when deleting a user.", + 1, entry.get(0).getBeforeProperties().size()); + assertEquals("Wrong value for username property is audited", + userName, entry.get(0).getBeforeProperties().get(ContentModel.PROP_USERNAME)); + } + @Override + public void after() + { + // Stop and delete all entries + rmAuditService.stopAuditLog(filePlan); + rmAuditService.clearAuditLog(filePlan); + } + + }); + } + + /** + * Given I have created a user + * When I will get the RM audit filter by create user event + * Then there will be an entry for the created user + * + * @throws Exception + */ + @org.junit.Test + public void testAuditForCreateUser() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + final static String CREATE_USER_AUDIT_EVENT = "Create Person"; + String userName = "auditCreateUser"; + NodeRef user; + List entry; + + @Override + public void given() throws Exception + { + // create a user + user = createPerson(userName); + } + + @Override + public void when() throws Exception + { + // set the audit query param + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setEvent(CREATE_USER_AUDIT_EVENT); + + // get the audit events for "Create Person" + entry = getAuditTrail(params, 1, ADMIN_USER); + } + + @Override + public void then() throws Exception + { + assertEquals("Create user event is not audited.", + CREATE_USER_AUDIT_EVENT, entry.get(0).getEvent()); + } + + @Override + public void after() + { + // Stop and delete all entries + rmAuditService.stopAuditLog(filePlan); + rmAuditService.clearAuditLog(filePlan); + } + + }); + } + /** === Helper methods === */ private List getAuditTrail(String asUser) diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java index 556b497f5a..66f1155424 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java @@ -27,8 +27,9 @@ package org.alfresco.module.org_alfresco_module_rm.test.legacy.service; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; @@ -94,12 +95,12 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements } /** - * Given a folder containing 3 files with the descriptions set - * When I check if the folder contains children having the description of file2 or file2 - * Then the answer is positive + * Given a folder containing 3 files with distinct descriptions set + * When I get the children property values + * Then the answer contains all distinct property values set */ @org.junit.Test - public void testHasChildrenWithPropertyValues_someChildrenWithValues() throws Exception + public void testGetChildrenWithPropertyValues_childrenWithValues() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() { @@ -110,8 +111,7 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements String propValue1 = "descr1"; // set on file1 String propValue2 = "descr2"; // set on file2 String propValue3 = "descr3"; // set on file3 - String propValue4 = "descr4"; // not set on any file - Boolean result; + Set propertyValues; @Override public void given() throws Exception @@ -130,13 +130,72 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements @Override public void when() throws Exception { - result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList(propValue1, propValue2, propValue4)); + propertyValues = queryDAO.getChildrenStringPropertyValues(parentFolder, PROP_DESCRIPTION); } @Override public void then() throws Exception { - assertTrue(result); + Set expectedValues = ImmutableSet.of(propValue1, propValue2, propValue3); + assertEquals(propertyValues.size(), expectedValues.size()); + assertTrue(propertyValues.containsAll(expectedValues)); + } + + @Override + public void after() throws Exception + { + if (parentFolder != null && nodeService.exists(parentFolder)) + { + nodeService.deleteNode(parentFolder); + } + } + }); + } + + /** + * Given a folder containing 3 files only some have description set + * When I get the children property values + * Then the answer contains only the descriptions set + */ + @org.junit.Test + public void testGetChildrenWithPropertyValues_someChildrenWithValues() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + NodeRef parentFolder; + NodeRef file1; + NodeRef file2; + NodeRef file3; + String propValue1 = "descr1"; // set on file1 + String propValue2 = "descr2"; // set on file2 + Set propertyValues; + + + @Override + public void given() throws Exception + { + setupCollaborationSiteTestDataImpl(); + parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); + file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); + file2 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); + file3 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); + + nodeService.setProperty(file1, PROP_DESCRIPTION, propValue1); + nodeService.setProperty(file2, PROP_DESCRIPTION, propValue2); + } + + @Override + public void when() throws Exception + { + propertyValues = queryDAO.getChildrenStringPropertyValues(parentFolder, PROP_DESCRIPTION); + } + + @Override + public void then() throws Exception + { + Set expectedValues = ImmutableSet.of(propValue1, propValue2); + assertEquals(propertyValues.size(), expectedValues.size()); + assertTrue(propertyValues.containsAll(expectedValues)); } @Override @@ -152,11 +211,11 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements /** * Given a folder containing 3 files with the descriptions unset - * When I check if the folder contains children having certain descriptions - * Then the answer is negative + * When I get the children property values + * Then empty list is returned */ @org.junit.Test - public void testHasChildrenWithPropertyValues_propertyNotSetOnChildren() throws Exception + public void testGetChildrenWithPropertyValues_propertyNotSetOnChildren() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() { @@ -164,7 +223,7 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements NodeRef file1; NodeRef file2; NodeRef file3; - Boolean result; + Set propertyValues; @Override public void given() throws Exception @@ -179,13 +238,14 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements @Override public void when() throws Exception { - result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList("descr1", "descr2", "descr3")); + propertyValues = queryDAO.getChildrenStringPropertyValues(parentFolder, PROP_DESCRIPTION); + } @Override public void then() throws Exception { - assertFalse(result); + assertTrue(propertyValues.isEmpty()); } @Override @@ -201,17 +261,17 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements /** * Given a folder with no children but the property set on itself - * When I check if the folder contains children having certain descriptions - * Then the answer is negative + * When get the children property values + * Then the list is empty */ @org.junit.Test - public void testHasChildrenWithPropertyValues_noChildren() throws Exception + public void testGetChildrenWithPropertyValues_noChildren() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() { NodeRef folder; String propValue = "descr"; - Boolean result; + Set propertyValues; @Override public void given() throws Exception @@ -224,13 +284,13 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements @Override public void when() throws Exception { - result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, Arrays.asList("descr")); + propertyValues = queryDAO.getChildrenStringPropertyValues(folder, PROP_DESCRIPTION); } @Override public void then() throws Exception { - assertFalse(result); + assertTrue(propertyValues.isEmpty()); } @Override @@ -246,17 +306,17 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements /** * Given a folder with children and an unused property - * When I check if the folder contains children having the unused property - * Then the answer is negative + * When I get the property values for the unused property + * Then empty list is returned */ @org.junit.Test - public void testHasChildrenWithPropertyValues_propertyNotUsed() throws Exception + public void testGetChildrenWithPropertyValues_propertyNotUsed() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() { NodeRef parentFolder; QName property; - Boolean result; + Set propertyValues; @Override public void given() throws Exception @@ -271,13 +331,13 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements @Override public void when() throws Exception { - result = queryDAO.hasChildrenWithPropertyValues(folder, property, Arrays.asList("descr")); + propertyValues = queryDAO.getChildrenStringPropertyValues(folder, property); } @Override public void then() throws Exception { - assertFalse(result); + assertTrue(propertyValues.isEmpty()); } @Override @@ -291,50 +351,4 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements }); } - /** - * Given any folder and any property - * When I pass an empty array to the hasChildrenWithPropertyValues method - * Then the answer is negative - */ - @org.junit.Test - public void testHasChildrenWithPropertyValues_emptyArray() throws Exception - { - doBehaviourDrivenTest(new BehaviourDrivenTest() - { - NodeRef parentFolder; - NodeRef file1; - Boolean result; - - @Override - public void given() throws Exception - { - setupCollaborationSiteTestDataImpl(); - parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); - file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef(); - - nodeService.setProperty(file1, PROP_DESCRIPTION, "descr1"); - } - - @Override - public void when() throws Exception - { - result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, new ArrayList()); - } - - @Override - public void then() throws Exception - { - assertFalse(result); - } - - @Override - public void after() throws Exception - { - if (folder != null && nodeService.exists(folder)) - { - nodeService.deleteNode(folder); - } - } - }); - } } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index 4eeb564b49..06dc185a44 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -54,7 +54,6 @@ import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderServi import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipService; import org.alfresco.module.org_alfresco_module_rm.report.ReportService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; -import org.alfresco.module.org_alfresco_module_rm.role.Role; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService; diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateActionUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateActionUnitTest.java index b0129ba6ec..275ffe4f82 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateActionUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateActionUnitTest.java @@ -102,7 +102,7 @@ public class BroadcastDispositionActionDefinitionUpdateActionUnitTest when(mockAction.getName()).thenReturn("mockAction"); // Set up the disposition service to return a known "disposition as of" date. Date asOfDate = new Date(); - when(mockDispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, false)) + when(mockDispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition)) .thenReturn(asOfDate); // Call the method under test. @@ -160,7 +160,7 @@ public class BroadcastDispositionActionDefinitionUpdateActionUnitTest when(mockAction.getParameterValue(CHANGED_PROPERTIES)).thenReturn((Serializable) asList(PROP_DISPOSITION_PERIOD_PROPERTY)); // Set up the expected "as of" date. Date newAsOfDate = new Date(123456789000L); - when(mockDispositionService.calculateAsOfDate(recordNode, mockActionDefinition, false)).thenReturn(newAsOfDate); + when(mockDispositionService.calculateAsOfDate(recordNode, mockActionDefinition)).thenReturn(newAsOfDate); // Call the method under test. action.executeImpl(mockAction, definitionNode); diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java new file mode 100644 index 0000000000..1e957087ce --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/RecordsManagementAuditServiceImplUnitTest.java @@ -0,0 +1,200 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.audit; + +import static java.util.Collections.emptyList; + +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService.ReportFormat.JSON; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.DOD5015_AUDIT_APPLICATION_NAME; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.RM_AUDIT_APPLICATION_NAME; +import static org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditServiceImpl.RM_AUDIT_PATH_ROOT; +import static org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model.TYPE_DOD_5015_SITE; +import static org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService.DEFAULT_RM_SITE_ID; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RM_SITE; +import static org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType.DEFAULT_SITE_NAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; + +import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.repo.audit.AuditComponent; +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** + * Unit tests for {@link RecordsManagementAuditServiceImpl}. + * + * @author Tom Page + * @since 2.7 + */ +public class RecordsManagementAuditServiceImplUnitTest +{ + /** The maximum entries to return in the audit query. */ + private static final int MAX_ENTRIES = 10; + /** A node representing the file plan root. */ + private static final NodeRef FILE_PLAN_NODE = new NodeRef("file://plan/node"); + /** A node representing the RM site. */ + private static final NodeRef RM_SITE_NODE = new NodeRef("rm://site/node"); + /** The class under test. */ + @InjectMocks + private RecordsManagementAuditServiceImpl recordsManagementAuditServiceImpl; + @Mock + private NodeService mockNodeService; + @Mock + private SiteService mockSiteService; + @Mock + private AuditService mockAuditService; + @Mock + private FilePlanService mockFilePlanService; + @Mock + AuditComponent mockAuditComponent; + @Mock + private Writer mockWriter; + @Mock + private SiteInfo mockSiteInfo; + @Captor + private ArgumentCaptor queryParamsCaptor; + + /** Set up the mocks. */ + @Before + public void setUp() + { + initMocks(this); + + when(mockFilePlanService.getFilePlanBySiteId(DEFAULT_RM_SITE_ID)).thenReturn(FILE_PLAN_NODE); + when(mockSiteService.getSite(DEFAULT_SITE_NAME)).thenReturn(mockSiteInfo); + when(mockSiteInfo.getNodeRef()).thenReturn(RM_SITE_NODE); + + recordsManagementAuditServiceImpl.setIgnoredAuditProperties(emptyList()); + } + + /** + * Check that if the RM site is not a DOD site then the audit trail doesn't make a query for DOD events. + * + * @throws IOException Unexpected. + */ + @Test + public void testAuditWithoutDOD() throws IOException + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setMaxEntries(MAX_ENTRIES); + List results = new ArrayList<>(); + // Return a standard site type. + when(mockNodeService.getType(RM_SITE_NODE)).thenReturn(TYPE_RM_SITE); + + // Call the method under test. + recordsManagementAuditServiceImpl.getAuditTrailImpl(params, results, mockWriter, JSON); + + // Check that exactly one audit query was performed. + verify(mockAuditService, times(1)) + .auditQuery(any(AuditService.AuditQueryCallback.class), queryParamsCaptor.capture(), + eq(MAX_ENTRIES)); + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). + assertEquals("The application name should be the standard RM application", RM_AUDIT_APPLICATION_NAME, + queryParamsCaptor.getValue().getApplicationName()); + // Check that the event of viewing the audit log was itself audited. + verify(mockAuditComponent).recordAuditValues(eq(RM_AUDIT_PATH_ROOT), any(Map.class)); + } + + /** + * Check that if the RM site is a DOD site then the audit trail makes a query for DOD events and the standard events. + * + * @throws IOException Unexpected. + */ + @Test + public void testAuditWithDOD() throws IOException + { + RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters(); + params.setMaxEntries(MAX_ENTRIES); + List results = new ArrayList<>(); + // Return a DOD site type. + when(mockNodeService.getType(RM_SITE_NODE)).thenReturn(TYPE_DOD_5015_SITE); + + // Call the method under test. + recordsManagementAuditServiceImpl.getAuditTrailImpl(params, results, mockWriter, JSON); + + // Check that two audit queries were performed (one for DOD events and one for standard events). + verify(mockAuditService, times(2)) + .auditQuery(any(AuditService.AuditQueryCallback.class), queryParamsCaptor.capture(), + eq(MAX_ENTRIES)); + Set apps = queryParamsCaptor.getAllValues().stream().map(AuditQueryParameters::getApplicationName) + .collect(Collectors.toSet()); + // We always need to make the standard query - regardless of the type of RM site (to get events like RM site created). + assertEquals("Expected the standard audit query and the DOD audit query.", + Sets.newHashSet(RM_AUDIT_APPLICATION_NAME, DOD5015_AUDIT_APPLICATION_NAME), apps); + // Check that the event of viewing the audit log was itself audited. + verify(mockAuditComponent).recordAuditValues(eq(RM_AUDIT_PATH_ROOT), any(Map.class)); + } + + /** Check that passing null to getStartOfDay doesn't result in null being returned. */ + @Test + public void testGetStartOfDay_null() + { + Date startOfDay = recordsManagementAuditServiceImpl.getStartOfDay(null); + assertNotNull("Expected date to be created by method.", startOfDay); + } + + /** Check that any time component passed to getStartOfDay is not included in the response. */ + @Test + public void testGetStartOfDay_timeDiscarded() throws Exception + { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); + Date date = format.parse("2001-02-03 04:05:06.789"); + + // Call the method under test. + Date startOfDay = recordsManagementAuditServiceImpl.getStartOfDay(date); + + assertEquals("Unexpected date truncation.", format.parse("2001-02-03 00:00:00.000"), startOfDay); + } +} diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImplUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImplUnitTest.java index 8ba2f1ac61..99d61053ec 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImplUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImplUnitTest.java @@ -82,7 +82,7 @@ public class DispositionServiceImplUnitTest when(mockPeriod.getNextDate(createdDate)).thenReturn(nextDate); // Call the method under test. - Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, true); + Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition); assertEquals("Unexpected calculation for 'as of' date", nextDate, asOfDate); } @@ -95,7 +95,7 @@ public class DispositionServiceImplUnitTest when(mockDispositionActionDefinition.getPeriod()).thenReturn(null); // Call the method under test. - Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition, true); + Date asOfDate = dispositionService.calculateAsOfDate(CONTENT_NODE_REF, mockDispositionActionDefinition); assertNull("It should not be possible to determine the 'as of' date.", asOfDate); } diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracterUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracterUnitTest.java new file mode 100644 index 0000000000..7aca0c6679 --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/email/RFC822MetadataExtracterUnitTest.java @@ -0,0 +1,169 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.email; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.junit.Test; +import org.mockito.InjectMocks; + +/** + * Unit test for RFC822MetadataExtracter + * + * @author Ana Manolache + * @since 2.7 + */ +public class RFC822MetadataExtracterUnitTest extends BaseUnitTest +{ + @InjectMocks + private RFC822MetadataExtracter metadataExtracter; + + private static final Map COMMON_PROPERTIES = ImmutableMap.of( + ContentModel.PROP_NAME, "Name", + ContentModel.PROP_TITLE, "Title"); + private static final Map RECORD_PROPERTIES = ImmutableMap.of( + RecordsManagementModel.PROP_DECLARED_BY, "DeclaredBy", + RecordsManagementModel.PROP_DECLARED_AT, new Date()); + private static final Map DOD_PROPERTIES = ImmutableMap.of( + DOD5015Model.PROP_ORIGINATOR, "DODOriginator", + DOD5015Model.PROP_ADDRESS, "Title"); + + /** + * Given a node that is not a record nor a dod record + * and has record and dod record properties + * When the method is called + * Then the record properties and dod properties are filtered out + */ + @Test + public void testRemoveSensitivePropertiesFromCommonNodes() + { + // Given + NodeRef node = generateNodeRef(); + when(mockedNodeService.hasAspect(node, RecordsManagementModel.ASPECT_RECORD)).thenReturn(false); + when(mockedNodeService.hasAspect(node, DOD5015Model.ASPECT_DOD_5015_RECORD)).thenReturn(false); + + // When + Map systemProperties = new HashMap<>(COMMON_PROPERTIES); + systemProperties.putAll(RECORD_PROPERTIES); + systemProperties.putAll(DOD_PROPERTIES); + metadataExtracter.filterSystemProperties(systemProperties, generateTargetProperties(node)); + + // Then + assertTrue("Sensitive properties were not properly filtered out.", + systemProperties.keySet().equals(COMMON_PROPERTIES.keySet())); + } + + /** + * Given a node that is a record + * and has record properties and dod properties + * When the method is called + * Then the DOD properties are filtered out + * and common and record properties are preserved + */ + @Test + public void testRemoveDodPropertiesFromRecordNodes() + { + // Given + NodeRef node = generateNodeRef(); + when(mockedNodeService.hasAspect(node, RecordsManagementModel.ASPECT_RECORD)).thenReturn(true); + when(mockedNodeService.hasAspect(node, DOD5015Model.ASPECT_DOD_5015_RECORD)).thenReturn(false); + + // When + Map systemProperties = new HashMap<>(COMMON_PROPERTIES); + systemProperties.putAll(RECORD_PROPERTIES); + systemProperties.putAll(DOD_PROPERTIES); + metadataExtracter.filterSystemProperties(systemProperties, generateTargetProperties(node)); + + // Then + assertTrue("Common properties should not be filtered out from record nodes.", + systemProperties.keySet().containsAll(COMMON_PROPERTIES.keySet())); + assertTrue("Record properties should not be filtered out from record nodes.", + systemProperties.keySet().containsAll(RECORD_PROPERTIES.keySet())); + assertFalse("Sensitive DOD properties were not properly filtered out from record nodes.", + systemProperties.keySet().removeAll(DOD_PROPERTIES.keySet())); + } + + /** + * Given a node that is a dod record + * and has record properties and dod properties + * When the method is called + * Then the record properties are filtered out + * and common and DOD properties are preserved + */ + @Test + public void testRemoveRecordPropertiesFromDodNodes() + { + // Given + NodeRef node = generateNodeRef(); + when(mockedNodeService.hasAspect(node, RecordsManagementModel.ASPECT_RECORD)).thenReturn(false); + when(mockedNodeService.hasAspect(node, DOD5015Model.ASPECT_DOD_5015_RECORD)).thenReturn(true); + + // When + Map systemProperties = new HashMap<>(COMMON_PROPERTIES); + systemProperties.putAll(RECORD_PROPERTIES); + systemProperties.putAll(DOD_PROPERTIES); + metadataExtracter.filterSystemProperties(systemProperties, generateTargetProperties(node)); + + // Then + assertTrue("Common properties should not be filtered out from DOD nodes.", + systemProperties.keySet().containsAll(COMMON_PROPERTIES.keySet())); + assertTrue("DOD properties should not be filtered out from DOD nodes.", + systemProperties.keySet().containsAll(DOD_PROPERTIES.keySet())); + assertFalse("Sensitive record properties were not properly filtered out from DOD nodes.", + systemProperties.keySet().removeAll(RECORD_PROPERTIES.keySet())); + } + + /** + * Helper method that generates target properties such as the given node is retrieved from them + * + * @param node the node to represent in the properties + * @return the list of properties containing the node's information + */ + private Map generateTargetProperties(NodeRef node) + { + Map targetProperties = new HashMap<>(); + targetProperties.put(ContentModel.PROP_STORE_PROTOCOL, node.getStoreRef().getProtocol()); + targetProperties.put(ContentModel.PROP_STORE_IDENTIFIER, node.getStoreRef().getIdentifier()); + targetProperties.put(ContentModel.PROP_NODE_UUID, node.getId()); + return targetProperties; + } +} diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java index 730da904f3..1c14788f89 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/aspect/RecordAspectUnitTest.java @@ -30,8 +30,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASPECT_RECORD; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -39,11 +37,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; -import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.module.org_alfresco_module_rm.util.ContentBinDuplicationUtility; import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.junit.Before; @@ -70,15 +65,9 @@ public class RecordAspectUnitTest @Mock private NodeService mockNodeService; @Mock - private BehaviourFilter mockBehaviorFilter; - @Mock - private ContentService mockContentService; - @Mock - private ContentReader mockContentReader; - @Mock - private ContentWriter mockContentWriter; - @Mock private ExtendedSecurityService mockExtendedSecurityService; + @Mock + private ContentBinDuplicationUtility mockContentBinDuplicationUtility; @Before public void setUp() @@ -91,12 +80,10 @@ public class RecordAspectUnitTest public void testDuplicateBinBeforeAddingAspectForFileWithCopy() { when(mockNodeService.getSourceAssocs(NODE_REF, ContentModel.ASSOC_ORIGINAL)).thenReturn(asList(SOURCE_ASSOC_REF)); - when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); - when(mockContentService.getWriter(NODE_REF, ContentModel.PROP_CONTENT, true)).thenReturn(mockContentWriter); recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(1); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(NODE_REF); } /** Check that the bin is duplicated before adding the aspect if the file is a copy. */ @@ -104,29 +91,10 @@ public class RecordAspectUnitTest public void testDuplicateBinBeforeAddingAspectForCopy() { when(mockNodeService.getTargetAssocs(NODE_REF, ContentModel.ASSOC_ORIGINAL)).thenReturn(asList(TARGET_ASSOC_REF)); - when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); - when(mockContentService.getWriter(NODE_REF, ContentModel.PROP_CONTENT, true)).thenReturn(mockContentWriter); recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(1); - } - - /** Check that no content bin is created if the file does not have content. */ - @Test - public void testBeforeAddAspectOnFileWithNoContent() - { - when(mockNodeService.getTargetAssocs(NODE_REF, ContentModel.ASSOC_ORIGINAL)).thenReturn(asList(TARGET_ASSOC_REF)); - when(mockContentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(null); - - recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - - verify(mockBehaviorFilter, times(1)).disableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(1)).disableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); - verify(mockContentService, times(1)).getReader(NODE_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, never()).getWriter(NODE_REF, ContentModel.PROP_CONTENT, true); - verify(mockBehaviorFilter, times(1)).enableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(1)).enableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(NODE_REF); } /** Check that the bin is not duplicated before adding the aspect if the node has no copies. */ @@ -138,7 +106,7 @@ public class RecordAspectUnitTest recordAspect.beforeAddAspect(NODE_REF, ASPECT_RECORD); - verifyBeforeAddAspectMethodsInvocations(0); + verify(mockContentBinDuplicationUtility, times(0)).duplicate(NODE_REF); } /** Check that the bin is duplicated when copying a record. */ @@ -147,30 +115,10 @@ public class RecordAspectUnitTest { when(mockNodeService.exists(COPY_REF)).thenReturn(true); when(mockNodeService.hasAspect(COPY_REF, ASPECT_RECORD)).thenReturn(true); - when(mockContentService.getReader(COPY_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentReader); - when(mockContentService.getWriter(COPY_REF, ContentModel.PROP_CONTENT, true)).thenReturn(mockContentWriter); recordAspect.onCopyComplete(null, NODE_REF, COPY_REF, true, null); verify(mockExtendedSecurityService, times(1)).remove(COPY_REF); - verify(mockContentService, times(1)).getReader(COPY_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, times(1)).getWriter(COPY_REF, ContentModel.PROP_CONTENT, true); - verify(mockContentWriter, times(1)).putContent(mockContentReader); - } - - /** - * Helper to verify beforeAddAspect methods invocations - * - * @param wantedNumberOfInvocations wanted number of invocations for each method - */ - private void verifyBeforeAddAspectMethodsInvocations(int wantedNumberOfInvocations) - { - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).disableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).disableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); - verify(mockContentService, times(wantedNumberOfInvocations)).getReader(NODE_REF, ContentModel.PROP_CONTENT); - verify(mockContentService, times(wantedNumberOfInvocations)).getWriter(NODE_REF, ContentModel.PROP_CONTENT, true); - verify(mockContentWriter, times(wantedNumberOfInvocations)).putContent(mockContentReader); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).enableBehaviour(eq(ContentModel.ASPECT_AUDITABLE)); - verify(mockBehaviorFilter, times(wantedNumberOfInvocations)).enableBehaviour(eq(ContentModel.ASPECT_VERSIONABLE)); + verify(mockContentBinDuplicationUtility, times(1)).duplicate(COPY_REF); } } 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..0f46199e00 --- /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 - 2019 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-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtilUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtilUnitTest.java new file mode 100644 index 0000000000..6d2e505125 --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RecordCategoryUtilUnitTest.java @@ -0,0 +1,132 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_IDENTIFIER; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_CATEGORY; +import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_FOLDER; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.when; + +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.cmr.repository.Path; +import org.alfresco.service.cmr.repository.Path.ChildAssocElement; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for methods in the RecordsCategoryUtil class + * + * @author Ross Gale + * @since 2.7 + */ +public class RecordCategoryUtilUnitTest +{ + @Mock + private NodeService nodeService; + + @Mock + private ChildAssocElement element; + + @Mock + private ChildAssociationRef childAssociationRef; + + @InjectMocks + private RecordCategoryUtil recordCategoryUtil; + + private Path path; + + private NodeRef recordNodeRef; + + private NodeRef recordFolderNodeRef; + + private NodeRef categoryNodeRef; + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + recordNodeRef = new NodeRef("test://recordNode/"); + recordFolderNodeRef = new NodeRef("test://recordFolderNode/"); + categoryNodeRef = new NodeRef("test://categoryNode/"); + path = new Path(); + path.append(element); + when(nodeService.getType(recordFolderNodeRef)).thenReturn(TYPE_RECORD_FOLDER); + when(nodeService.getType(recordNodeRef)).thenReturn(TYPE_NON_ELECTRONIC_DOCUMENT); + when(nodeService.getPath(recordNodeRef)).thenReturn(path); + when(nodeService.getPath(recordFolderNodeRef)).thenReturn(path); + when(element.getRef()).thenReturn(childAssociationRef); + when(childAssociationRef.getChildRef()).thenReturn(categoryNodeRef); + when(nodeService.getType(categoryNodeRef)).thenReturn(TYPE_RECORD_CATEGORY); + when(nodeService.getProperty(categoryNodeRef, PROP_IDENTIFIER)).thenReturn("RecordCategoryId"); + } + + /** + * Tests an id is returned from a valid node ref + */ + @Test + public void testGetIdFromNodeRef() + { + assertEquals("RecordCategoryId",recordCategoryUtil.getCategoryIdFromNodeId(recordNodeRef,false)); + } + + /** + * Tests an id can be returned for a non record with the correct option selected + */ + @Test + public void testGetIdFromNodeRefReturnsForNonRecordWhenOptionSelected() + { + assertEquals("RecordCategoryId", recordCategoryUtil.getCategoryIdFromNodeId(recordFolderNodeRef, true)); + } + + /** + * Tests no id is returned for a folder if option isn't selected + */ + @Test + public void testGetIdFromNodeRefReturnsNullForNonRecordWhenOptionSelected() + { + assertNull(recordCategoryUtil.getCategoryIdFromNodeId(recordFolderNodeRef,false)); + } + + /** + * Tests no id is returned when a categories isn't found on the path + */ + @Test + public void testGetIdFromNodeRefReturnsNullWithNoCategory() + { + when(nodeService.getPath(recordNodeRef)).thenReturn(new Path()); + assertNull(recordCategoryUtil.getCategoryIdFromNodeId(recordNodeRef, false)); + } +} diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/MockAuthenticationUtilHelper.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/MockAuthenticationUtilHelper.java index 8785ca8daf..73b31c79bd 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/MockAuthenticationUtilHelper.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/MockAuthenticationUtilHelper.java @@ -45,6 +45,10 @@ import org.mockito.stubbing.Answer; */ public class MockAuthenticationUtilHelper { + public static final String SYSTEM_USER = "system"; + public static final String ADMIN_USER = "admin"; + public static final String GUEST_USER = "guest"; + /** * Set up a Mockito mock AuthenticationUtil so that it executes all methods assuming the user has * permissions. If the mock is asked for details about the user then it assumes the currently authenticated user is @@ -95,10 +99,10 @@ public class MockAuthenticationUtilHelper }).when(mockAuthenticationUtil). runAs(any(RunAsWork.class), anyString()); - when(mockAuthenticationUtil.getAdminUserName()).thenReturn("admin"); + when(mockAuthenticationUtil.getAdminUserName()).thenReturn(ADMIN_USER); when(mockAuthenticationUtil.getFullyAuthenticatedUser()).thenReturn(fullyAuthenticatedUser); when(mockAuthenticationUtil.getRunAsUser()).thenReturn(fullyAuthenticatedUser); - when(mockAuthenticationUtil.getSystemUserName()).thenReturn("system"); - when(mockAuthenticationUtil.getGuestUserName()).thenReturn("guest"); + when(mockAuthenticationUtil.getSystemUserName()).thenReturn(SYSTEM_USER); + when(mockAuthenticationUtil.getGuestUserName()).thenReturn(GUEST_USER); } } diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java new file mode 100644 index 0000000000..b8f2727d4e --- /dev/null +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/util/ContentBinDuplicationUtilityUnitTest.java @@ -0,0 +1,168 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.util; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Test class for the ContentBinDuplicationUtility + * @author Ross Gale + * @since 2.7.2 + */ +public class ContentBinDuplicationUtilityUnitTest +{ + private final static NodeRef NODE_REF = new NodeRef("some://test/noderef"); + private final static NodeRef NODE_REF2 = new NodeRef("some://test/anothernoderef"); + private final static String CONTENT_URL = "someContentUrl"; + + @Mock + private ContentService contentService; + + @Mock + private BehaviourFilter behaviourFilter; + + @Mock + private ContentReader contentReader; + + @Mock + private ContentWriter contentWriter; + + @Mock + private RecordsManagementQueryDAO recordsManagementQueryDAO; + + @InjectMocks + private ContentBinDuplicationUtility contentBinDuplicationUtility; + + @Before + public void setUp() + { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests that the requests are made to disable and re-enable the audit and versioning and to update the content bin + */ + @Test + public void testContentUrlIsUpdated() + { + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(contentReader); + when(contentService.getWriter(NODE_REF, ContentModel.PROP_CONTENT, true)).thenReturn(contentWriter); + contentBinDuplicationUtility.duplicate(NODE_REF); + verify(contentWriter, times(1)).putContent(contentReader); + checkBehaviours(1); + } + + /** + * Test content duplication doesn't happen when node has no content + */ + @Test + public void testDuplicationDoesntHappenWithNoContent() + { + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(null); + contentBinDuplicationUtility.duplicate(NODE_REF); + verify(contentWriter, times(0)).putContent(contentReader); + checkBehaviours(1); + } + + /** + * Test hasAtLeastOneOtherReference returns true when node has another reference to it + */ + @Test + public void testHasAtLeastOneOtherReference() + { + Set multipleReferences = new HashSet<>(); + Collections.addAll(multipleReferences, NODE_REF, NODE_REF2); + + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(contentReader); + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(multipleReferences); + + assertTrue(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + + /** + * Test hasAtLeastOneOtherReference returns false when node has no other reference to it other than its own content ref + */ + @Test + public void testHasNoOtherReference() + { + Set singleReference = Collections.singleton(NODE_REF); + + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(contentReader); + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(singleReference); + + assertFalse(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + + /** + * Test hasAtLeastOneOtherReference returns false when node has no references to it at all + */ + @Test + public void testHasNoReferences() + { + Set noReferences = Collections. emptySet(); + + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(contentReader); + when(contentService.getReader(NODE_REF, ContentModel.PROP_CONTENT).getContentUrl()).thenReturn(CONTENT_URL); + when(recordsManagementQueryDAO.getNodeRefsWhichReferenceContentUrl(CONTENT_URL)).thenReturn(noReferences); + + assertFalse(contentBinDuplicationUtility.hasAtLeastOneOtherReference(NODE_REF)); + } + + /** + * Check that the behaviours are disabled and re-enabled the correct number of times + * @param times the times the behaviours should be called + */ + private void checkBehaviours(int times) + { + verify(behaviourFilter, times(times)).disableBehaviour(); + verify(behaviourFilter, times(times)).enableBehaviour(); + } +} diff --git a/rm-community/rm-community-rest-api-explorer/pom.xml b/rm-community/rm-community-rest-api-explorer/pom.xml index 53c3e3bede..d3f6be7580 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 - 2.6.3-SNAPSHOT + 2.7.3-SNAPSHOT