diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..4c5105fe68
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,181 @@
+version: 2
+registries:
+ maven-repository-artifacts-alfresco-com-nexus-content-groups-int:
+ type: maven-repository
+ url: https://artifacts.alfresco.com/nexus/content/groups/internal
+ username: ${{secrets.NEXUS_USERNAME}}
+ password: ${{secrets.NEXUS_PASSWORD}}
+updates:
+- package-ecosystem: maven
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "22:00"
+ timezone: Africa/Abidjan
+ open-pull-requests-limit: 99
+ ignore:
+ - dependency-name: com.google.code.gson:gson
+ versions:
+ - "> 2.8.6"
+ - dependency-name: io.fabric8:fabric8-maven-plugin
+ versions:
+ - "> 4.4.0"
+ - dependency-name: javax.servlet:javax.servlet-api
+ versions:
+ - "> 3.0.1"
+ - dependency-name: org.acegisecurity:acegi-security
+ versions:
+ - "> 0.8.2_patched"
+ - dependency-name: org.activiti:activiti-engine
+ versions:
+ - "> 5.23.0"
+ - dependency-name: org.activiti:activiti-engine
+ versions:
+ - ">= 7.1.a, < 7.2"
+ - dependency-name: org.activiti:activiti-spring
+ versions:
+ - "> 5.23.0"
+ - dependency-name: org.activiti:activiti-spring
+ versions:
+ - ">= 7.1.a, < 7.2"
+ - dependency-name: org.apache.camel:camel-activemq
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-amqp
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-direct
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-directvm
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-jackson
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-mock
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.camel:camel-spring
+ versions:
+ - "> 3.7.1"
+ - dependency-name: org.apache.chemistry.opencmis:chemistry-opencmis-client-impl
+ versions:
+ - "> 1.0.0"
+ - dependency-name: org.apache.chemistry.opencmis:chemistry-opencmis-commons-impl
+ versions:
+ - "> 1.0.0"
+ - dependency-name: org.apache.chemistry.opencmis:chemistry-opencmis-server-bindings
+ versions:
+ - "> 1.0.0"
+ - dependency-name: org.apache.chemistry.opencmis:chemistry-opencmis-test-tck
+ versions:
+ - "> 1.0.0"
+ - dependency-name: org.freemarker:freemarker
+ versions:
+ - "> 2.3.20-alfresco-patched-20200421"
+ - dependency-name: org.keycloak:keycloak-adapter-core
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.keycloak:keycloak-adapter-spi
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.keycloak:keycloak-authz-client
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.keycloak:keycloak-common
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.keycloak:keycloak-core
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.keycloak:keycloak-servlet-adapter-spi
+ versions:
+ - "> 12.0.2"
+ - dependency-name: org.eclipse.jetty:jetty-server
+ versions:
+ - 9.4.38.v20210224
+ - dependency-name: org.alfresco.tas:cmis
+ versions:
+ - "1.28"
+ - dependency-name: org.springframework:spring-webmvc
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-web
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-tx
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-orm
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-test
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-jms
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-jdbc
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-expression
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-core
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-context-support
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-context
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-beans
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.springframework:spring-aop
+ versions:
+ - 5.3.4
+ - 5.3.5
+ - dependency-name: org.alfresco.tas:restapi
+ versions:
+ - "1.55"
+ - dependency-name: org.eclipse.jetty:jetty-security
+ versions:
+ - 11.0.1
+ - dependency-name: org.alfresco.aos-module:alfresco-vti-bin
+ versions:
+ - 1.4.0-M1
+ - dependency-name: org.alfresco.aos-module:alfresco-aos-module-distributionzip
+ versions:
+ - 1.4.0-M1
+ - dependency-name: org.alfresco.aos-module:alfresco-aos-module
+ versions:
+ - 1.4.0-M1
+ - dependency-name: org.alfresco.surf:spring-webscripts-api
+ versions:
+ - "8.16"
+ - dependency-name: org.alfresco.surf:spring-webscripts:tests
+ versions:
+ - "8.16"
+ - dependency-name: org.alfresco.surf:spring-webscripts
+ versions:
+ - "8.16"
+ - dependency-name: org.alfresco.surf:spring-surf-core-configservice
+ versions:
+ - "8.16"
+ registries:
+ - maven-repository-artifacts-alfresco-com-nexus-content-groups-int
diff --git a/.travis.yml b/.travis.yml
index fab7f4a49e..5ab883176c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,7 +55,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext01TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext02TestSuite"
@@ -70,7 +70,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext04TestSuite"
@@ -78,7 +78,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext05TestSuite"
@@ -97,7 +97,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext06TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContextExtraTestSuite"
@@ -105,7 +105,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContextExtraTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - MiscContextTestSuite"
@@ -113,7 +113,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl repository -Dtest=MiscContextTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - SearchTestSuite"
@@ -145,7 +145,7 @@ jobs:
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- name: "Repository - MariaDB 10.6 tests"
- if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
+ if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/ OR commit_message =~ /\[latest db\]/
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.6 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
@@ -159,7 +159,7 @@ jobs:
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - MySQL 8 tests"
- if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
+ if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/ OR commit_message =~ /\[latest db\]/
before_script:
- docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:8 --transaction-isolation='READ-COMMITTED'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
@@ -210,7 +210,7 @@ jobs:
- name: "Repository - PostgreSQL 13.3 tests"
# We only run DB tests on the latest version of PostgreSQL on feature branches
- if: commit_message !~ /\[skip db\]/
+ if: commit_message !~ /\[skip db\]/ OR commit_message =~ /\[latest db\]/
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
@@ -234,7 +234,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext02TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext03TestSuite"
@@ -242,7 +242,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext04TestSuite"
@@ -250,7 +250,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.2
+ - docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.5.3
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContextExtraTestSuite"
diff --git a/amps/ags/pom.xml b/amps/ags/pom.xml
index 900d3abd42..61bf76c197 100644
--- a/amps/ags/pom.xml
+++ b/amps/ags/pom.xml
@@ -7,7 +7,7 @@
org.alfrescoalfresco-community-repo-amps
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT
diff --git a/amps/ags/rm-automation/pom.xml b/amps/ags/rm-automation/pom.xml
index 6a0f22a16c..6cdbbd76bf 100644
--- a/amps/ags/rm-automation/pom.xml
+++ b/amps/ags/rm-automation/pom.xml
@@ -7,7 +7,7 @@
org.alfrescoalfresco-governance-services-community-parent
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml
index a1cbc8779c..c34fb1464f 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml
@@ -7,7 +7,7 @@
org.alfrescoalfresco-governance-services-automation-community-repo
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java
index 4616e7d474..b9fda4f696 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/record/RecordProperties.java
@@ -55,6 +55,7 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_ACTION_AS_OF;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_ACTION_NAME;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_AUTHORITY;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS_ELIGIBLE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_INSTRUCTIONS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_RECORD_SEARCH_DISPOSITION_PERIOD;
@@ -248,6 +249,9 @@ public class RecordProperties extends TestModel
@JsonProperty (PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS_ELIGIBLE)
private Boolean recordSearchDispositionEventsEligible;
+ @JsonProperty (PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS)
+ private List recordSearchDispositionEvents;
+
@JsonProperty (PROPERTIES_RECORD_SEARCH_DISPOSITION_INSTRUCTIONS)
private String recordSearchDispositionInstructions;
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java
index 9494bf37c9..c3b28748ec 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryChildProperties.java
@@ -47,6 +47,7 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR;
import java.util.Date;
+import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@@ -134,7 +135,7 @@ public class RecordCategoryChildProperties extends TestModel
private String recordSearchDispositionInstructions;
@JsonProperty (PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS)
- private Boolean recordSearchDispositionEvents;
+ private List recordSearchDispositionEvents;
@JsonProperty (PROPERTIES_OWNER)
private Owner owner;
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java
new file mode 100644
index 0000000000..12d322fdb2
--- /dev/null
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java
@@ -0,0 +1,103 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.v0;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.alfresco.rest.core.v0.BaseAPI;
+import org.apache.http.HttpResponse;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.springframework.stereotype.Component;
+
+/**
+ * Methods to make API requests using v0 API for Exporting Items
+ *
+ * @author Shubham Jain
+ * @since 7.1.0
+ */
+
+@Component
+public class ExportAPI extends BaseAPI
+{
+ /**
+ * The URI to export an item
+ */
+ private static final String EXPORT_API = "{0}rma/admin/export";
+
+ /**
+ * Export a single Record/Record Folder/Record Category using V0 Export API
+ *
+ * @param user User performing the export
+ * @param password User's Password
+ * @param expectedStatusCode Expected Response Code
+ * @param nodeID ID of the Node(Record/RecordFolder) to be exported
+ * @return HTTP Response
+ */
+ public HttpResponse exportRMNode(String user, String password, int expectedStatusCode, String nodeID)
+ {
+ return export(user, password, expectedStatusCode, Collections.singletonList(getNodeRefSpacesStore() + nodeID));
+ }
+
+ /**
+ * Export a list of nodes using V0 Export API
+ *
+ * @param user User performing the export
+ * @param password User's Password
+ * @param expectedStatusCode Expected Response Code
+ * @param nodeIDList List of the nodes to be exported
+ * @return HTTP Response
+ */
+ public HttpResponse exportRMNodes(String user, String password, int expectedStatusCode, List nodeIDList)
+ {
+
+ List nodeRefs =
+ nodeIDList.stream().map(nodeID -> getNodeRefSpacesStore() + nodeID).collect(Collectors.toList());
+
+ return export(user, password, expectedStatusCode, nodeRefs);
+ }
+
+ /**
+ * Export API function to perform Export Operation on items with given noderefs using V0 Export Rest API
+ *
+ * @param user User performing the export
+ * @param password User's Password
+ * @param expectedStatusCode Expected Response Code
+ * @param nodeRefs list of the noderefs for the items to be exported
+ * @return Rest API Post Request
+ */
+ public HttpResponse export(String user, String password, int expectedStatusCode, List nodeRefs)
+ {
+ final JSONObject requestParams = new JSONObject();
+
+ requestParams.put("nodeRefs", new JSONArray(nodeRefs));
+
+ return doPostJsonRequest(user, password, expectedStatusCode, requestParams, EXPORT_API);
+ }
+}
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
index 31ffe7a5ee..83e9bdf0c4 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
@@ -619,11 +619,27 @@ public class BaseRMRestTest extends RestTest
* @return
*/
public List searchForContentAsUser(UserModel user, String term)
+ {
+ String query = "cm:name:*" + term + "*";
+ return searchForContentAsUser(user,query,"afts");
+ }
+
+ /**
+ * Returns search results for the given search term
+ *
+ * @param user
+ * @param term
+ * @param query language
+ * @return
+ * @throws Exception
+ */
+ public List searchForContentAsUser(UserModel user, String q, String queryLanguage)
{
getRestAPIFactory().getRmRestWrapper().authenticateUser(user);
RestRequestQueryModel queryReq = new RestRequestQueryModel();
SearchRequest query = new SearchRequest(queryReq);
- queryReq.setQuery("cm:name:*" + term + "*");
+ queryReq.setQuery(q);
+ queryReq.setLanguage(queryLanguage);
List names = new ArrayList<>();
// wait for solr indexing
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java
index 62926a3930..f36627241e 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/hold/PreventActionsOnFrozenContentTests.java
@@ -67,7 +67,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
-
+import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
/**
* API tests to check actions on frozen content
*
@@ -309,11 +309,11 @@ public class PreventActionsOnFrozenContentTests extends BaseRMRestTest
STEP("Execute the retain action");
rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getPassword(), record.getName(),
- RM_ACTIONS.END_RETENTION);
+ RM_ACTIONS.END_RETENTION, null, SC_INTERNAL_SERVER_ERROR);
STEP("Check the record search disposition properties");
Record recordUpdated = getRestAPIFactory().getRecordsAPI().getRecord(record.getId());
- assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName().contains(RM_ACTIONS.DESTROY.getAction()));
+ assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName().contains(RM_ACTIONS.END_RETENTION.getAction()));
assertTrue(recordUpdated.getProperties().getRecordSearchDispositionPeriod().contains("immediately"));
}
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/MoveRecCategoriesWithRSTests.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/MoveRecCategoriesWithRSTests.java
new file mode 100644
index 0000000000..6f904b527d
--- /dev/null
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/recordcategories/MoveRecCategoriesWithRSTests.java
@@ -0,0 +1,268 @@
+/*-
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 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.recordcategories;
+
+import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CREATED_DATE;
+import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CUT_OFF_DATE;
+import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.DATE_FILED;
+import static org.alfresco.rest.rm.community.utils.CoreUtil.createBodyForMoveCopy;
+import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
+import static org.alfresco.utility.data.RandomData.getRandomName;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.springframework.http.HttpStatus.OK;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.alfresco.rest.core.v0.BaseAPI.RM_ACTIONS;
+import org.alfresco.rest.core.v0.RMEvents;
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.v0.service.DispositionScheduleService;
+import org.alfresco.test.AlfrescoTest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for moving record categories between record categories with different retention schedule
+ */
+public class MoveRecCategoriesWithRSTests extends BaseRMRestTest
+{
+ private RecordCategory rootCategory, rootCategory2;
+ private Record elRecord, nonElRecord;
+ @Autowired
+ private DispositionScheduleService dispositionScheduleService;
+
+ /**
+ * Create two root categories with some retention schedules on record level
+ */
+ @BeforeMethod
+ private void setUpMoveRecCategoriesWithRSTests()
+ {
+ STEP("Create record category with retention schedule and apply it to records.");
+ rootCategory = createRootCategory(getRandomName("rootCategory1"));
+ dispositionScheduleService.createCategoryRetentionSchedule(rootCategory.getName(), true);
+
+ STEP("Create record category with retention schedule and apply it to records.");
+ rootCategory2 = createRootCategory(getRandomName("rootCategory2"));
+ dispositionScheduleService.createCategoryRetentionSchedule(rootCategory2.getName(), true);
+ }
+
+ /**
+ * Given following structure is created:
+ * rootCategory1 with RS applied on record level with cut off and destroy after 1 day
+ * - subCategory1 without RS
+ * - recFolder
+ * - incomplete electronic record
+ * - complete non-electronic record
+ * rootCategory2 with RS with retain and destroy both after 2 day
+ * When moving subcategory1 within rootCategory2
+ * Then the records will inherit the RS from rootCategory2
+ */
+ @Test
+ @AlfrescoTest (jira = "APPS-1005")
+ public void testInheritWhenMoveToDifferentRSStep() throws Exception
+ {
+ STEP("Add retention schedule cut off step after 1 day period.");
+ dispositionScheduleService.addCutOffAfterPeriodStep(rootCategory.getName(), "day|1", CREATED_DATE);
+
+ STEP("Add retention schedule destroy step after 1 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory.getName(), "day|1", CUT_OFF_DATE);
+
+ STEP("Create a subcategory with a record folder and records.");
+ RecordCategoryChild subCategory = createSubCategoryWithRecords();
+
+ STEP("Add retention schedule retain step after 2 day period.");
+ dispositionScheduleService.addRetainAfterPeriodStep(rootCategory2.getName(), "day|2");
+
+ STEP("Add retention schedule destroy step after 2 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory2.getName(), "day|2", DATE_FILED);
+
+ STEP("Move the subcategory within the rootCategory2.");
+ getRestAPIFactory().getNodeAPI(toContentModel(subCategory.getId())).move(createBodyForMoveCopy(rootCategory2.getId()));
+ assertStatusCode(OK);
+
+ STEP("Check that both records inherit rootCategory2 retention schedule");
+ elRecord = getRestAPIFactory().getRecordsAPI().getRecord(elRecord.getId());
+ nonElRecord = getRestAPIFactory().getRecordsAPI().getRecord(nonElRecord.getId());
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.END_RETENTION.getAction()),
+ "Disposition action should be retain");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("day"),
+ "Disposition period property should be day");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("2"),
+ "Disposition period expression should be 2");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.END_RETENTION.getAction()),
+ "Disposition action should be retain");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("day"),
+ "Disposition period property should be day");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("2"),
+ "Disposition period expression should be 2");
+ }
+
+ /**
+ * Given following structure is created:
+ * rootCategory1 with RS applied on record level with retain and destroy after 1 day
+ * - subCategory without RS
+ * - recFolder
+ * - incomplete electronic record
+ * - complete non-electronic record
+ * rootCategory2 with RS with cut off on event case closed and destroy both after 2 day
+ * When moving subcategory within rootCategory2
+ * Then the records will inherit the RS from rootCategory2
+ */
+ @Test
+ @AlfrescoTest (jira = "APPS-1004")
+ public void testInheritWhenMoveToDifferentRSStepOnEventBase() throws Exception
+ {
+ STEP("Add retention schedule retain step after 1 day period.");
+ dispositionScheduleService.addRetainAfterPeriodStep(rootCategory.getName(), "day|1");
+
+ STEP("Add retention schedule destroy step after 1 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory.getName(), "day|1", CUT_OFF_DATE);
+
+ STEP("Create a subcategory with a record folder and records.");
+ RecordCategoryChild subCategory = createSubCategoryWithRecords();
+
+ STEP("Add retention schedule cut off step on event case closed.");
+ dispositionScheduleService.addCutOffAfterEventStep(rootCategory2.getName(), RMEvents.CASE_CLOSED.getEventName());
+
+ STEP("Add retention schedule destroy step after 1 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory2.getName(), "day|2", DATE_FILED);
+
+ STEP("Move the subcategory within the rootCategory2.");
+ getRestAPIFactory().getNodeAPI(toContentModel(subCategory.getId())).move(createBodyForMoveCopy(rootCategory2.getId()));
+ assertStatusCode(OK);
+
+ STEP("Check that both records inherit rootCategory2 retention schedule");
+ elRecord = getRestAPIFactory().getRecordsAPI().getRecord(elRecord.getId());
+ nonElRecord = getRestAPIFactory().getRecordsAPI().getRecord(nonElRecord.getId());
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.CUT_OFF.getAction()),
+ "Disposition action should be cut off");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("none"),
+ "Disposition period property should none");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("0"),
+ "Disposition period expression should be 0");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.CASE_CLOSED.getEventName()),
+ "Disposition event list doesn't contain case closed event");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.CUT_OFF.getAction()),
+ "Disposition action should be cut off");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("none"),
+ "Disposition period property should be none");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("0"),
+ "Disposition period expression should be 0");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.CASE_CLOSED.getEventName()),
+ "Disposition event list doesn't contain case closed event");
+ }
+
+ /**
+ * Given following structure is created:
+ * rootCategory1 with RS applied on record level with cut off on event case closed and destroy after 1 day
+ * - subCategory2 without RS
+ * - recFolder
+ * - incomplete electronic record
+ * - complete non-electronic record
+ * rootCategory2 with cut off on event Obsolete and destroy both after 2 day
+ * When moving subcategory2 within rootCategory2
+ * Then the records will inherit the RS from rootCategory2
+ */
+ @Test
+ @AlfrescoTest (jira = "APPS-1004")
+ public void testInheritWhenMoveToSameStepDifferentEvent() throws Exception
+ {
+ STEP("Add retention schedule cut off on case closed.");
+ dispositionScheduleService.addCutOffAfterEventStep(rootCategory.getName(), RMEvents.CASE_CLOSED.getEventName());
+
+ STEP("Add retention schedule destroy step after 1 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory.getName(), "day|1", CUT_OFF_DATE);
+
+ STEP("Create a subcategory with a record folder and records.");
+ RecordCategoryChild subCategory = createSubCategoryWithRecords();
+
+ STEP("Add retention schedule cut off step on event separation.");
+ dispositionScheduleService.addCutOffAfterEventStep(rootCategory2.getName(), RMEvents.OBSOLETE.getEventName());
+
+ STEP("Add retention schedule destroy step after 2 Day period.");
+ dispositionScheduleService.addDestroyWithGhostingAfterPeriodStep(rootCategory2.getName(), "day|2", DATE_FILED);
+
+ STEP("Move the subcategory within the rootCategory2.");
+ getRestAPIFactory().getNodeAPI(toContentModel(subCategory.getId())).move(createBodyForMoveCopy(rootCategory2.getId()));
+ assertStatusCode(OK);
+
+ STEP("Check that both records inherit rootCategory2 retention schedule");
+ elRecord = getRestAPIFactory().getRecordsAPI().getRecord(elRecord.getId());
+ nonElRecord = getRestAPIFactory().getRecordsAPI().getRecord(nonElRecord.getId());
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.CUT_OFF.getAction()),
+ "Disposition action should be cut off");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("none"),
+ "Disposition period property should be none");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("0"),
+ "Disposition period expression should be 0");
+ assertFalse(elRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.CASE_CLOSED.getEventName()),
+ "Event list contain the event from the previous RS ");
+ assertTrue(elRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.OBSOLETE.getEventName()),
+ "Event list doesn't contain the event from the current RS ");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionActionName().equalsIgnoreCase(RM_ACTIONS.CUT_OFF.getAction()),
+ "Disposition action should be cut off");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriod().equalsIgnoreCase("none"),
+ "Disposition period property should be none");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionPeriodExpression().equalsIgnoreCase("0"),
+ "Disposition period expression should be 0");
+ assertFalse(nonElRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.CASE_CLOSED.getEventName()),
+ "Event list contain the event from the previous RS ");
+ assertTrue(nonElRecord.getProperties().getRecordSearchDispositionEvents().contains(RMEvents.OBSOLETE.getEventName()),
+ "Event list doesn't contain the event from the current RS ");
+ }
+
+ @AfterMethod (alwaysRun = true)
+ public void cleanupMoveRecCategoriesWithRSTests()
+ {
+ getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(rootCategory.getId());
+ getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(rootCategory2.getId());
+ }
+
+ /**
+ * Helper method to create a sub-category with a folder, an incomplete electronic record and a complete
+ * electronic record
+ * @return
+ */
+ private RecordCategoryChild createSubCategoryWithRecords()
+ {
+ STEP("Create a subcategory with a record folder");
+ RecordCategoryChild subCategory = createRecordCategory(rootCategory.getId(), getRandomName("subCategory"));
+ RecordCategoryChild recFolder = createFolder(subCategory.getId(), getRandomName("recFolder"));
+
+ STEP("Create 2 records in the record folder. Complete one of them.");
+ elRecord = createElectronicRecord(recFolder.getId(), getRandomName("elRecord"));
+ nonElRecord = createNonElectronicRecord(recFolder.getId(), getRandomName("nonElRecord"));
+ getRestAPIFactory().getRecordsAPI().completeRecord(nonElRecord.getId());
+ return subCategory;
+ }
+
+}
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java
new file mode 100644
index 0000000000..d3e7bde53f
--- /dev/null
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java
@@ -0,0 +1,136 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 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.records;
+
+import static java.util.Arrays.asList;
+
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE;
+import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile;
+import static org.alfresco.utility.data.RandomData.getRandomName;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.apache.http.HttpStatus.SC_OK;
+
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.v0.ExportAPI;
+import org.alfresco.test.AlfrescoTest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains tests for testing the Export functionality on RM site
+ *
+ * @author Shubham Jain
+ * @since 7.1.0
+ */
+public class ExportRecordsTests extends BaseRMRestTest
+{
+ private RecordCategory rootCategory;
+
+ private RecordCategoryChild recordFolder;
+
+ @Autowired
+ private ExportAPI exportAPI;
+
+ @BeforeClass (alwaysRun = true)
+ public void exportRecordsTestsBeforeClass()
+ {
+ STEP("Create root level category");
+ rootCategory = createRootCategory(getRandomName("Category"));
+
+ STEP("Create the record folder inside the rootCategory");
+ recordFolder = createRecordFolder(rootCategory.getId(), getRandomName("Folder"));
+
+ }
+
+ @DataProvider (name = "CreateRMNodes")
+ public Object[][] getRMNodeID()
+ {
+ return new String[][] {
+ { createRecord("Record_4MB", 4).getId() },
+ { createRecord("Record_200MB", 200).getId() },
+ { recordFolder.getId() }
+ };
+ }
+
+ /**
+ * Given a record with size > 4 MB
+ * When I export the record using API
+ * Then the request is successful
+ */
+ @Test (description = "Testing the RM Export functionality for records of size >4MB and Record " +
+ "Folder containing records with size >4MB",
+ dataProvider = "CreateRMNodes")
+ @AlfrescoTest (jira = "APPS-986")
+ public void exportRMNodeTest(String nodeID)
+ {
+ STEP("Export the created record/record folder with size greater than 4 MB and verifying the expected response" +
+ " code");
+ exportAPI.exportRMNode(getAdminUser().getUsername(), getAdminUser().getPassword(), SC_OK, nodeID);
+ }
+
+ /**
+ * I would change this to
+ * Given a list of records with a size > 4MB
+ * When I export the records
+ * Then the request is succesfull
+ */
+ @Test (description = "Testing the RM Export functionality using API for a list of Records at once with " +
+ "collective size of more than 4MB")
+ public void exportRecordsTest()
+ {
+ STEP("Export all the created records at once and verifying the expected response code");
+ exportAPI.exportRMNodes(getAdminUser().getUsername(), getAdminUser().getPassword(),
+ SC_OK, asList(createRecord("Record_2MB", 2).getId(), createRecord("Record_3MB", 3).getId()));
+ }
+
+ /**
+ * Create a Record with a specific size in RM Site inside already created Record Folder
+ *
+ * @param recordName Name of the record to be created
+ * @param sizeInMegaBytes Size of the record to be created in MegaBytes
+ * @return Created record with defined size
+ */
+ public Record createRecord(String recordName, int sizeInMegaBytes)
+ {
+ return getRestAPIFactory().getRecordFolderAPI().createRecord(Record.builder().name(recordName)
+ .nodeType(CONTENT_TYPE).build(), recordFolder.getId(),
+ createTempFile("TempFile", sizeInMegaBytes));
+ }
+
+ @AfterClass (alwaysRun = true)
+ public void exportRecordsTestsAfter()
+ {
+ STEP("Delete the created rootCategory along with corresponding record folders/records present in it");
+ getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(rootCategory.getId());
+ }
+}
diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
index 56dd5da2bb..fe91643217 100644
--- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
+++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java
@@ -41,6 +41,7 @@ import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
+import java.io.RandomAccessFile;
import org.alfresco.rest.rm.community.model.record.Record;
import org.alfresco.rest.rm.community.model.record.RecordProperties;
@@ -66,13 +67,19 @@ public class FilePlanComponentsUtil
// Intentionally blank
}
- /** Name of the image resource file to be used for records body */
+ /**
+ * Name of the image resource file to be used for records body
+ */
public static final String IMAGE_FILE = "money.JPG";
- /** Title prefix for record category children */
+ /**
+ * Title prefix for record category children
+ */
public static final String TITLE_PREFIX = "Title for ";
- /** Description prefix for record category children */
+ /**
+ * Description prefix for record category children
+ */
public static final String DESCRIPTION_PREFIX = "This is the description for";
@@ -87,7 +94,7 @@ public class FilePlanComponentsUtil
}
/**
- * Creates a record model with the given type and a random name (with "Record " prefix)
+ * Creates a record model with the given type and a random name (with "Record " prefix)
*
* @param nodeType The node type
* @return The {@link Record} with for the given node type
@@ -95,9 +102,9 @@ public class FilePlanComponentsUtil
private static Record createRecordModel(String nodeType)
{
return Record.builder()
- .name("Record " + getRandomAlphanumeric())
- .nodeType(nodeType)
- .build();
+ .name("Record " + getRandomAlphanumeric())
+ .nodeType(nodeType)
+ .build();
}
/**
@@ -133,22 +140,22 @@ public class FilePlanComponentsUtil
/**
* Creates an unfiled records container child record model with the given name and type
*
- * @param name The name of the unfiled records container child
+ * @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
public static UnfiledContainerChild createUnfiledContainerChildRecordModel(String name, String nodeType)
{
return UnfiledContainerChild.builder()
- .name(name)
- .nodeType(nodeType)
- .build();
+ .name(name)
+ .nodeType(nodeType)
+ .build();
}
/**
* Creates a nonElectronic container child record model with all available properties for the non electronic records
*
- * @param name The name of the unfiled records container child
+ * @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
@@ -156,19 +163,19 @@ public class FilePlanComponentsUtil
String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
{
return UnfiledContainerChild.builder()
- .name(name)
- .nodeType(NON_ELECTRONIC_RECORD_TYPE)
- .properties(UnfiledContainerChildProperties.builder()
- .title(title)
- .description(description)
- .box(box)
- .file(file)
- .shelf(shelf)
- .storageLocation(storageLocation)
- .numberOfCopies(numberOfCopies)
- .physicalSize(physicalSize)
- .build())
- .build();
+ .name(name)
+ .nodeType(NON_ELECTRONIC_RECORD_TYPE)
+ .properties(UnfiledContainerChildProperties.builder()
+ .title(title)
+ .description(description)
+ .box(box)
+ .file(file)
+ .shelf(shelf)
+ .storageLocation(storageLocation)
+ .numberOfCopies(numberOfCopies)
+ .physicalSize(physicalSize)
+ .build())
+ .build();
}
/**
@@ -190,110 +197,110 @@ public class FilePlanComponentsUtil
String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
{
return Record.builder()
- .name(name)
- .nodeType(NON_ELECTRONIC_RECORD_TYPE)
- .properties(RecordProperties.builder()
- .title(title)
- .description(description)
- .box(box)
- .file(file)
- .shelf(shelf)
- .storageLocation(storageLocation)
- .numberOfCopies(numberOfCopies)
- .physicalSize(physicalSize)
- .build())
- .build();
+ .name(name)
+ .nodeType(NON_ELECTRONIC_RECORD_TYPE)
+ .properties(RecordProperties.builder()
+ .title(title)
+ .description(description)
+ .box(box)
+ .file(file)
+ .shelf(shelf)
+ .storageLocation(storageLocation)
+ .numberOfCopies(numberOfCopies)
+ .physicalSize(physicalSize)
+ .build())
+ .build();
}
/**
* Creates a record model with the given name, description and title
*
- * @param name The name of the record
+ * @param name The name of the record
* @param description The description of the record
- * @param title The title of the record
+ * @param title The title of the record
* @return The {@link Record} with the given details
*/
public static Record createRecordModel(String name, String description, String title)
{
return Record.builder()
- .name(name)
- .properties(RecordProperties.builder()
- .description(description)
- .title(title)
- .build())
- .build();
+ .name(name)
+ .properties(RecordProperties.builder()
+ .description(description)
+ .title(title)
+ .build())
+ .build();
}
/**
* Creates a record category child model with the given name and type
*
- * @param name The name of the record category child
+ * @param name The name of the record category child
* @param nodeType The type of the record category child
* @return The {@link RecordCategoryChild} with the given details
*/
public static RecordCategoryChild createRecordCategoryChildModel(String name, String nodeType)
{
return RecordCategoryChild.builder()
- .name(name)
- .nodeType(nodeType)
- .properties(RecordCategoryChildProperties.builder()
- .title(TITLE_PREFIX + name)
- .build())
- .build();
+ .name(name)
+ .nodeType(nodeType)
+ .properties(RecordCategoryChildProperties.builder()
+ .title(TITLE_PREFIX + name)
+ .build())
+ .build();
}
/**
* Creates a record category model with the given name and title
*
- * @param name The name of the record category
+ * @param name The name of the record category
* @param title The title of the record category
* @return The {@link RecordCategory} with the given details
*/
public static RecordCategory createRecordCategoryModel(String name, String title)
{
return RecordCategory.builder()
- .name(name)
- .nodeType(RECORD_CATEGORY_TYPE)
- .properties(RecordCategoryProperties.builder()
- .title(title)
- .build())
- .build();
+ .name(name)
+ .nodeType(RECORD_CATEGORY_TYPE)
+ .properties(RecordCategoryProperties.builder()
+ .title(title)
+ .build())
+ .build();
}
/**
* Creates a record folder model with the given name and title
*
- * @param name The name of the record folder
+ * @param name The name of the record folder
* @param title The title of the record folder
* @return The {@link RecordFolder} with the given details
*/
public static RecordFolder createRecordFolderModel(String name, String title)
{
return RecordFolder.builder()
- .name(name)
- .nodeType(RECORD_FOLDER_TYPE)
- .properties(RecordFolderProperties.builder()
- .title(title)
- .build())
- .build();
+ .name(name)
+ .nodeType(RECORD_FOLDER_TYPE)
+ .properties(RecordFolderProperties.builder()
+ .title(title)
+ .build())
+ .build();
}
/**
* Creates an unfiled records container child model with the given name and type
*
- * @param name The name of the unfiled records container child
+ * @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
public static UnfiledContainerChild createUnfiledContainerChildModel(String name, String nodeType)
{
return UnfiledContainerChild.builder()
- .name(name)
- .nodeType(nodeType)
- .properties(UnfiledContainerChildProperties.builder()
- .title(TITLE_PREFIX + name)
- .build())
- .build();
+ .name(name)
+ .nodeType(nodeType)
+ .properties(UnfiledContainerChildProperties.builder()
+ .title(TITLE_PREFIX + name)
+ .build())
+ .build();
}
/**
@@ -324,6 +331,32 @@ public class FilePlanComponentsUtil
}
}
+ /**
+ * Method to create a temporary file with specific size
+ *
+ * @param name file name
+ * @param sizeInMegaBytes size
+ * @return temporary file
+ */
+ public static File createTempFile(final String name, long sizeInMegaBytes)
+ {
+ try
+ {
+ // Create file
+ final File file = File.createTempFile(name, ".txt");
+
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ raf.setLength(sizeInMegaBytes * 1024 * 1024);
+ raf.close();
+
+ return file;
+ }
+ catch (Exception exception)
+ {
+ throw new RuntimeException("Unable to create test file.", exception);
+ }
+ }
+
/**
* Helper method to verify all properties of a nonElectronic record
*
diff --git a/amps/ags/rm-community/pom.xml b/amps/ags/rm-community/pom.xml
index daefc515f0..03926c8098 100644
--- a/amps/ags/rm-community/pom.xml
+++ b/amps/ags/rm-community/pom.xml
@@ -7,7 +7,7 @@
org.alfrescoalfresco-governance-services-community-parent
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT
diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env
index ace4179284..469ec19f29 100644
--- a/amps/ags/rm-community/rm-community-repo/.env
+++ b/amps/ags/rm-community/rm-community-repo/.env
@@ -1,4 +1,4 @@
-TRANSFORMERS_TAG=2.5.2
+TRANSFORMERS_TAG=2.5.3
SOLR6_TAG=2.0.2-RC1
POSTGRES_TAG=13.3
ACTIVEMQ_TAG=5.16.1
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties
index eeac19f2e6..dae1c555e2 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=You can't create items i
rm.action.create.transfer.child-error-message=You can't create items in Transfer Folders.
rm.action.create.record.folder.child-error-message=You can only create records in record folders and this was a {0}.
rm.action.transfer-non-editable=You can't edit transfer folder or container metadata.
-
+rm.action.node.frozen.error-message=Unable to perform action {0} because the node is frozen or has frozen children.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_cs.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_cs.properties
index 170edacbd7..f35b08ebfa 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_cs.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_cs.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Nelze vytv\u00e1\u0159et
rm.action.create.transfer.child-error-message=Nelze vytv\u00e1\u0159et polo\u017eky ve slo\u017ece p\u0159enosu.
rm.action.create.record.folder.child-error-message=Z\u00e1znamy je mo\u017en\u00e9 vytv\u00e1\u0159et pouze ve slo\u017ek\u00e1ch z\u00e1znam\u016f a toto bylo {0}.
rm.action.transfer-non-editable=Nelze upravovat metadata kontejneru nebo slo\u017eky pro p\u0159enos.
-
+rm.action.node.frozen.error-message=Akci {0} nelze prov\u00e9st, proto\u017ee uzel je zablokovan\u00fd nebo m\u00e1 zablokovan\u00e9 pod\u0159\u00edzen\u00e9 prvky.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_da.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_da.properties
index a28e721da7..9964ea265f 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_da.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_da.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Du kan ikke oprette elem
rm.action.create.transfer.child-error-message=Du kan ikke oprette elementer i Overf\u00f8r-mapper.
rm.action.create.record.folder.child-error-message=Du kan kun oprette poster i postmapper, og dette var en {0}.
rm.action.transfer-non-editable=Du kan ikke redigere overf\u00f8rselsmappe- eller container-metadata.
-
+rm.action.node.frozen.error-message=Handlingen kan ikke udf\u00f8res {0}, fordi noden er l\u00e5st eller har l\u00e5ste underordnede noder.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_de.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_de.properties
index 03b0890712..82932abbbf 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_de.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_de.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Sie k\u00f6nnen keine El
rm.action.create.transfer.child-error-message=Sie k\u00f6nnen keine Elemente in \u00dcbertragungsordnern erstellen.
rm.action.create.record.folder.child-error-message=In Record-Ordnern k\u00f6nnen Sie nur Records erstellen. Das war aber ein {0}.
rm.action.transfer-non-editable=Sie k\u00f6nnen Metadaten von \u00dcbertragungsordnern oder -containern nicht bearbeiten.
-
+rm.action.node.frozen.error-message=Die Aktion ''{0}'' kann nicht ausgef\u00fchrt werden, da der Knoten oder untergeordnete Elemente von ihm festgefahren ist bzw. sind.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties
index 5ea2ca053c..070323ce99 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_es.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=No se pueden crear eleme
rm.action.create.transfer.child-error-message=No se pueden crear elementos en las carpetas de transferencia.
rm.action.create.record.folder.child-error-message=Solo puede crear documentos de archivo en carpetas de documentos de archivo. {0} no se puede crear aqu\u00ed.
rm.action.transfer-non-editable=No se puede editar una carpeta de transferencia ni los metadatos de un contenedor.
-
+rm.action.node.frozen.error-message=No se puede realizar la acci\u00f3n {0} porque el nodo est\u00e1 congelado o tiene elementos secundarios congelados.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fi.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fi.properties
index 2fb1c281ab..d255b6ec22 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fi.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fi.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Siirtos\u00e4ili\u00f6\u
rm.action.create.transfer.child-error-message=Siirtokansioihin ei voi luoda kohteita.
rm.action.create.record.folder.child-error-message=Tietuekansioihin voi luoda ainoastaan tietueita, mutta t\u00e4m\u00e4 oli {0}.
rm.action.transfer-non-editable=Siirtokansion tai -s\u00e4ili\u00f6n metatietoja ei voi muokata.
-
+rm.action.node.frozen.error-message=Toimintoa {0} ei voitu suorittaa, koska solmu on j\u00e4\u00e4dytetty tai sill\u00e4 on j\u00e4\u00e4dytettyj\u00e4 alatasoja.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fr.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fr.properties
index d7d4a3ab52..c2b2bd947a 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fr.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_fr.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Impossible de cr\u00e9er
rm.action.create.transfer.child-error-message=Impossible de cr\u00e9er des \u00e9l\u00e9ments dans les dossiers de transfert.
rm.action.create.record.folder.child-error-message=Vous ne pouvez cr\u00e9er des documents d''archives que dans les dossiers d''archives, dans le cas pr\u00e9sent {0}.
rm.action.transfer-non-editable=Impossible de modifier les m\u00e9tadonn\u00e9es de dossier de transfert ou de contenant.
-
+rm.action.node.frozen.error-message=Impossible d''effectuer l''action {0} car le n\u0153ud ou ses enfants sont gel\u00e9s.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_it.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_it.properties
index 81c2032be7..8fd5091f30 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_it.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_it.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Impossibile creare eleme
rm.action.create.transfer.child-error-message=Impossibile creare elementi nelle cartelle Trasferimento.
rm.action.create.record.folder.child-error-message=I record possono essere creati solo nelle cartelle dei record e questa era {0}.
rm.action.transfer-non-editable=Impossibile modificare la cartella di trasferimento o i metadati dei contenitori.
-
+rm.action.node.frozen.error-message=Impossibile eseguire l''azione {0} poich\u00e9 il nodo \u00e8 congelato o presenta nodi figlio congelati.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties
index 8be3966fa5..679424bcc4 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ja.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=\u8ee2\u9001\u30b3\u30f3
rm.action.create.transfer.child-error-message=\u8ee2\u9001\u30d5\u30a9\u30eb\u30c0\u5185\u3067\u306f\u30a2\u30a4\u30c6\u30e0\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3002
rm.action.create.record.folder.child-error-message=\u30ec\u30b3\u30fc\u30c9\u3092\u4f5c\u6210\u3067\u304d\u308b\u306e\u306f\u30ec\u30b3\u30fc\u30c9\u30d5\u30a9\u30eb\u30c0\u5185\u306e\u307f\u3067\u3001\u3053\u308c\u306f {0} \u3067\u3059\u3002
rm.action.transfer-non-editable=\u8ee2\u9001\u30d5\u30a9\u30eb\u30c0\u307e\u305f\u306f\u30b3\u30f3\u30c6\u30ca\u30e1\u30bf\u30c7\u30fc\u30bf\u306f\u7de8\u96c6\u3067\u304d\u307e\u305b\u3093\u3002
-
+rm.action.node.frozen.error-message=\u30ce\u30fc\u30c9\u304c\u30d5\u30ea\u30fc\u30ba\u3057\u3066\u3044\u308b\u304b\u3001\u307e\u305f\u306f\u5b50\u304c\u51cd\u7d50\u3057\u3066\u3044\u308b\u305f\u3081\u3001\u30a2\u30af\u30b7\u30e7\u30f3 {0}\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nb.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nb.properties
index 423c2f79c9..e5946ca805 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nb.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nb.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Elementer kan ikke oppre
rm.action.create.transfer.child-error-message=Elementer kan ikke opprettes i overf\u00f8ringsmapper.
rm.action.create.record.folder.child-error-message=Oppf\u00f8ringer kan bare opprettes i oppf\u00f8ringsmapper, og dette er en {0}.
rm.action.transfer-non-editable=Overf\u00f8ringsmapper eller beholdermetadata kan ikke redigeres.
-
+rm.action.node.frozen.error-message=Kan ikke utf\u00f8re handlingen {0} fordi noden er frossen eller har frosne underordnede elementer.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nl.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nl.properties
index 397f2ef9aa..70aeaf3026 100755
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nl.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_nl.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=U kunt geen onderdelen m
rm.action.create.transfer.child-error-message=U kunt geen onderdelen maken in overdrachtsmappen.
rm.action.create.record.folder.child-error-message=U kunt alleen archiefstukken maken in archiefmappen en dit was een {0}.
rm.action.transfer-non-editable=U kunt metagegevens in een overdrachtsmap of -container niet bewerken.
-
+rm.action.node.frozen.error-message=Kan de actie {0} niet uitvoeren omdat de node geblokkeerd is of geblokkeerde onderliggende elementen heeft.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pl.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pl.properties
index 56e2d2966f..755f8afee9 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pl.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pl.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Nie mo\u017cna utworzy\u
rm.action.create.transfer.child-error-message=Nie mo\u017cna utworzy\u0107 pozycji w folderach przesy\u0142ania.
rm.action.create.record.folder.child-error-message=W folderach rekord\u00f3w mo\u017cna tworzy\u0107 tylko rekordy, a to by\u0142o {0}.
rm.action.transfer-non-editable=Nie mo\u017cna edytowa\u0107 folderu przesy\u0142ania ani metadanych kontenera.
-
+rm.action.node.frozen.error-message=Nie mo\u017cna wykona\u0107 czynno\u015bci {0}, poniewa\u017c w\u0119ze\u0142 jest zablokowany lub ma zablokowane elementy podrz\u0119dne.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pt_BR.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pt_BR.properties
index b02afa69cd..b07b94ad1b 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pt_BR.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_pt_BR.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=N\u00e3o \u00e9 poss\u00
rm.action.create.transfer.child-error-message=N\u00e3o \u00e9 poss\u00edvel criar itens nas pastas de Transfer\u00eancia.
rm.action.create.record.folder.child-error-message=\u00c9 poss\u00edvel apenas criar documentos arquiv\u00edsticos em pastas de documentos arquiv\u00edsticos; esta foi {0}.
rm.action.transfer-non-editable=N\u00e3o \u00e9 poss\u00edvel editar os metadados das pastas de transfer\u00eancia ou cont\u00eainer.
-
+rm.action.node.frozen.error-message=N\u00e3o \u00e9 poss\u00edvel executar a a\u00e7\u00e3o {0} porque o n\u00f3 est\u00e1 congelado ou tem filhos congelados.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ru.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ru.properties
index 01174e4738..be30f4cfd8 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ru.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_ru.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=\u041d\u0435\u0432\u043e
rm.action.create.transfer.child-error-message=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0432 \u043f\u0430\u043f\u043a\u0430\u0445 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438.
rm.action.create.record.folder.child-error-message=\u0417\u0430\u043f\u0438\u0441\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043f\u0430\u043f\u043a\u0430\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439, \u0430 \u044d\u0442\u043e {0}.
rm.action.transfer-non-editable=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0438\u043b\u0438 \u043f\u0430\u043f\u043a\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438.
-
+rm.action.node.frozen.error-message=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 {0}, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0443\u0437\u0435\u043b \u0437\u0430\u043a\u0440\u0435\u043f\u043b\u0435\u043d \u0438\u043b\u0438 \u0438\u043c\u0435\u0435\u0442 \u0437\u0430\u043a\u0440\u0435\u043f\u043b\u0435\u043d\u043d\u044b\u0435 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_sv.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_sv.properties
index 6f22da4f88..9e5ea4b8f1 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_sv.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_sv.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=Du kan inte skapa objekt
rm.action.create.transfer.child-error-message=Du kan inte skapa objekt i \u00d6verf\u00f6ringsmappar.
rm.action.create.record.folder.child-error-message=Du kan endast skapa handlingar i handlingsmappar och detta var en {0}.
rm.action.transfer-non-editable=Du kan inte redigera \u00f6verf\u00f6ringsmappen eller beh\u00e5llarens metadata.
-
+rm.action.node.frozen.error-message=Det gick inte att utf\u00f6ra \u00e5tg\u00e4rd {0} eftersom noden \u00e4r frusen eller har frysta underordnade.
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_zh_CN.properties b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_zh_CN.properties
index 4ff55da979..326bde95fa 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_zh_CN.properties
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/messages/action-service_zh_CN.properties
@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=\u60a8\u65e0\u6cd5\u5728
rm.action.create.transfer.child-error-message=\u60a8\u65e0\u6cd5\u5728 Transfer \u6587\u4ef6\u5939\u4e2d\u521b\u5efa\u9879\u76ee\u3002
rm.action.create.record.folder.child-error-message=\u60a8\u53ea\u80fd\u5728\u8bb0\u5f55\u6587\u4ef6\u5939\u4e2d\u521b\u5efa\u8bb0\u5f55\uff0c\u4e14\u8fd9\u662f\u4e00\u4e2a {0}\u3002
rm.action.transfer-non-editable=\u60a8\u65e0\u6cd5\u7f16\u8f91\u4f20\u8f93\u6587\u4ef6\u5939\u6216\u5bb9\u5668\u5143\u6570\u636e\u3002
-
+rm.action.node.frozen.error-message=\u65e0\u6cd5\u6267\u884c\u64cd\u4f5c{0}\uff0c\u56e0\u4e3a\u8282\u70b9\u5df2\u51bb\u7ed3\u6216\u51bb\u7ed3\u7684\u5b50\u8282\u70b9\u3002
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml
index 8dd01c5ac8..2f76bdf22a 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-job-context.xml
@@ -80,9 +80,7 @@
-
-
-
+
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml
index 8218bd49d0..23b950f2ea 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml
@@ -102,9 +102,11 @@
+
+
diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
index 95b668b250..ae80740b66 100644
--- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
+++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
@@ -695,6 +695,13 @@
init-method="init" depends-on="org_alfresco_module_rm_resourceBundles">
+
+
+
+ retain
+
+
+
@@ -899,6 +906,8 @@
+
+
@@ -944,6 +953,7 @@
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.hasFrozenChildren=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.getFreezeDate=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.getFreezeInitiator=RM_ALLOW
+ org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.isFrozenOrHasFrozenChildren=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.*=RM_DENY
]]>
diff --git a/amps/ags/rm-community/rm-community-repo/docker-compose.yml b/amps/ags/rm-community/rm-community-repo/docker-compose.yml
index bdd8d01456..6cae53e5b7 100644
--- a/amps/ags/rm-community/rm-community-repo/docker-compose.yml
+++ b/amps/ags/rm-community/rm-community-repo/docker-compose.yml
@@ -5,7 +5,7 @@ version: "3"
services:
alfresco:
# acs repo community image with ags repo community amp applied
- image: alfresco/alfresco-governance-repository-community:latest
+ image: alfresco/alfresco-governance-repository-community-base:latest
environment:
CATALINA_OPTS : "
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml
index b273a2aff5..30db11b4ca 100644
--- a/amps/ags/rm-community/rm-community-repo/pom.xml
+++ b/amps/ags/rm-community/rm-community-repo/pom.xml
@@ -8,13 +8,13 @@
org.alfrescoalfresco-governance-services-community-repo-parent
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT${project.build.directory}/${project.build.finalName}-war
- alfresco/alfresco-governance-repository-community
+ alfresco/alfresco-governance-repository-community-base
@@ -141,6 +141,11 @@
spring-testtest
+
+ org.projectlombok
+ lombok
+ provided
+
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java
index 69ecc6585d..6dbdc424be 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/RecordsManagementActionServiceImpl.java
@@ -35,17 +35,17 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRMActionExecution;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRMActionExecution;
+import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.util.PoliciesUtil;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
@@ -53,14 +53,13 @@ import org.springframework.extensions.surf.util.I18NUtil;
*
* @author Roy Wetherall
*/
+@Slf4j
public class RecordsManagementActionServiceImpl implements RecordsManagementActionService
{
/** I18N */
private static final String MSG_NOT_DEFINED = "rm.action.not-defined";
private static final String MSG_NO_IMPLICIT_NODEREF = "rm.action.no-implicit-noderef";
-
- /** Logger */
- private static Log logger = LogFactory.getLog(RecordsManagementActionServiceImpl.class);
+ private static final String MSG_NODE_FROZEN = "rm.action.node.frozen.error-message";
/** Registered records management actions */
private Map rmActions = new HashMap<>(13);
@@ -78,6 +77,16 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
private ClassPolicyDelegate beforeRMActionExecutionDelegate;
private ClassPolicyDelegate onRMActionExecutionDelegate;
+ /**
+ * Freeze Service
+ */
+ private FreezeService freezeService;
+
+ /**
+ * list of retention actions to automatically execute
+ */
+ private List retentionActions;
+
/**
* @return Policy component
*/
@@ -94,6 +103,19 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
return this.nodeService;
}
+ /**
+ * @param freezeService freeze service
+ */
+ public void setFreezeService(FreezeService freezeService)
+ {
+ this.freezeService = freezeService;
+ }
+
+ public void setRetentionActions(List retentionActions)
+ {
+ this.retentionActions = retentionActions;
+ }
+
/**
* Set the policy component
*
@@ -267,21 +289,23 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
*/
public RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name, Map parameters)
{
- if (logger.isDebugEnabled())
- {
- logger.debug("Executing record management action on " + nodeRef);
- logger.debug(" actionName = " + name);
- logger.debug(" parameters = " + parameters);
- }
+ log.debug("Executing record management action on " + nodeRef);
+ log.debug(" actionName = " + name);
+ log.debug(" parameters = " + parameters);
RecordsManagementAction rmAction = this.rmActions.get(name);
if (rmAction == null)
{
String msg = I18NUtil.getMessage(MSG_NOT_DEFINED, name);
- if (logger.isWarnEnabled())
- {
- logger.warn(msg);
- }
+ log.warn(msg);
+ throw new AlfrescoRuntimeException(msg);
+ }
+
+ if (retentionActions.contains(name.toLowerCase()) && freezeService.isFrozenOrHasFrozenChildren(nodeRef))
+ {
+ String msg = I18NUtil.getMessage(MSG_NODE_FROZEN, name);
+ log.debug(msg);
+
throw new AlfrescoRuntimeException(msg);
}
@@ -307,10 +331,7 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
if (implicitTargetNode == null)
{
String msg = I18NUtil.getMessage(MSG_NO_IMPLICIT_NODEREF, name);
- if (logger.isWarnEnabled())
- {
- logger.warn(msg);
- }
+ log.warn(msg);
throw new AlfrescoRuntimeException(msg);
}
else
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java
index cf5c8d36b0..d8ebc610eb 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java
@@ -306,7 +306,7 @@ public class RMAfterInvocationProvider extends RMSecurityCommon
}
}
- private boolean isUnfiltered(NodeRef nodeRef)
+ protected boolean isUnfiltered(NodeRef nodeRef)
{
return !nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT);
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java
index b28ff08326..9c6897139d 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java
@@ -45,7 +45,6 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.property.Dispositi
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
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.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
@@ -59,7 +58,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
-import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java
index eb9f8a1c7a..bbd84ea295 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeService.java
@@ -150,4 +150,12 @@ public interface FreezeService
*/
@Deprecated
Set getHolds(NodeRef filePlan);
+
+ /**
+ * Check given node or its children are frozen
+ * The node should be record or record folder for retention schedule
+ *
+ * @param nodeRef
+ */
+ boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef);
}
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java
index 9f77ed6121..191f0309ef 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/freeze/FreezeServiceImpl.java
@@ -43,6 +43,8 @@ import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -75,6 +77,32 @@ public class FreezeServiceImpl extends ServiceBaseImpl
/** Hold service */
private HoldService holdService;
+ /**
+ * Record Folder Service
+ */
+ private RecordFolderService recordFolderService;
+
+ /**
+ * Record Service
+ */
+ private RecordService recordService;
+
+ /**
+ * @param recordFolderService record folder service
+ */
+ public void setRecordFolderService(RecordFolderService recordFolderService)
+ {
+ this.recordFolderService = recordFolderService;
+ }
+
+ /**
+ * @param recordService record service
+ */
+ public void setRecordService(RecordService recordService)
+ {
+ this.recordService = recordService;
+ }
+
/**
* @return File plan service
*/
@@ -392,4 +420,24 @@ public class FreezeServiceImpl extends ServiceBaseImpl
// create hold
return getHoldService().createHold(filePlan, holdName, reason, null);
}
+
+ /**
+ * Helper method to determine if a node is frozen or has frozen children
+ *
+ * @param nodeRef Node to be checked
+ * @return true if the node is frozen or has frozen children, false otherwise
+ */
+ @Override
+ public boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef)
+ {
+ if (recordFolderService.isRecordFolder(nodeRef))
+ {
+ return isFrozen(nodeRef) || hasFrozenChildren(nodeRef);
+ }
+ else if (recordService.isRecord(nodeRef))
+ {
+ return isFrozen(nodeRef);
+ }
+ return Boolean.FALSE;
+ }
}
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java
index f450bb9fb5..4b1081deae 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java
@@ -30,15 +30,14 @@ package org.alfresco.module.org_alfresco_module_rm.job;
import static org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK;
import java.io.Serializable;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
+
import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
-import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
-import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -48,8 +47,8 @@ import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.surf.util.I18NUtil;
+
/**
* The Disposition Lifecycle Job Finds all disposition action nodes which are for disposition actions specified Where
@@ -58,14 +57,14 @@ import org.apache.commons.logging.LogFactory;
* @author mrogers
* @author Roy Wetherall
*/
+@Slf4j
public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecuter
{
- /** logger */
- private static Log logger = LogFactory.getLog(DispositionLifecycleJobExecuter.class);
/** batching properties */
private int batchSize;
public static final int DEFAULT_BATCH_SIZE = 500;
+ private static final String MSG_NODE_FROZEN = "rm.action.node.frozen.error-message";
/** list of disposition actions to automatically execute */
private List dispositionActions;
@@ -88,11 +87,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** freeze service */
private FreezeService freezeService;
- /** record service */
- private RecordService recordService;
-
- /** record folder service */
- private RecordFolderService recordFolderService;
+ /**
+ * @param freezeService freeze service
+ */
+ public void setFreezeService(FreezeService freezeService)
+ {
+ this.freezeService = freezeService;
+ }
/**
* List of disposition actions to automatically execute when eligible.
@@ -133,30 +134,6 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
this.searchService = searchService;
}
- /**
- * @param freezeService freeze service
- */
- public void setFreezeService(FreezeService freezeService)
- {
- this.freezeService = freezeService;
- }
-
- /**
- * @param recordService record service
- */
- public void setRecordService(RecordService recordService)
- {
- this.recordService = recordService;
- }
-
- /**
- * @param recordFolderService record folder service
- */
- public void setRecordFolderService(RecordFolderService recordFolderService)
- {
- this.recordFolderService = recordFolderService;
- }
-
/**
* Get the search query string.
*
@@ -207,11 +184,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{
try
{
- logger.debug("Job Starting");
+ log.debug("Job Starting");
if (dispositionActions == null || dispositionActions.isEmpty())
{
- logger.debug("Job Finished as disposition action is empty");
+ log.debug("Job Finished as disposition action is empty");
return;
}
@@ -220,16 +197,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (batchSize < 1)
{
- if (logger.isDebugEnabled())
- {
- logger.debug("Invalid value for batch size: " + batchSize + " default value used instead.");
- }
+ log.debug("Invalid value for batch size: " + batchSize + " default value used instead.");
batchSize = DEFAULT_BATCH_SIZE;
}
- if (logger.isTraceEnabled())
- {
- logger.trace("Using batch size of " + batchSize);
- }
+
+ log.trace("Using batch size of " + batchSize);
while (hasMore)
{
@@ -247,10 +219,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
skipCount += resultNodes.size(); // increase by page size
results.close();
- if (logger.isDebugEnabled())
- {
- logger.debug("Processing " + resultNodes.size() + " nodes");
- }
+ log.debug("Processing " + resultNodes.size() + " nodes");
// process search results
if (!resultNodes.isEmpty())
@@ -258,14 +227,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
executeAction(resultNodes);
}
}
- logger.debug("Job Finished");
+ log.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
- if (logger.isDebugEnabled())
- {
- logger.debug(exception);
- }
+ log.debug(exception.getMessage());
}
}
@@ -299,12 +265,9 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
Map props = Map.of(PARAM_NO_ERROR_CHECK, false);
- if (isFrozenOrHasFrozenChildren(parent.getParentRef()))
+ if (freezeService.isFrozenOrHasFrozenChildren(parent.getParentRef()))
{
- if (logger.isDebugEnabled()) {
- logger.debug("unable to perform action " + dispAction +
- " because node is frozen or has frozen children");
- }
+ log.debug(I18NUtil.getMessage(MSG_NODE_FROZEN, dispAction));
continue;
}
@@ -314,17 +277,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
recordsManagementActionService
.executeRecordsManagementAction(parent.getParentRef(), dispAction, props);
- if (logger.isDebugEnabled())
- {
- logger.debug("Processed action: " + dispAction + "on" + parent);
- }
+ log.debug("Processed action: " + dispAction + "on" + parent);
+
}
catch (AlfrescoRuntimeException exception)
{
- if (logger.isDebugEnabled())
- {
- logger.debug(exception);
- }
+ log.debug(exception.getMessage());
+
}
}
return Boolean.TRUE;
@@ -332,25 +291,6 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
retryingTransactionHelper.doInTransaction(processTranCB, false, true);
}
- /**
- * Helper method to determine if a node is frozen or has frozen children
- *
- * @param nodeRef Node to be checked
- * @return true if the node is frozen or has frozen children, false otherwise
- */
- private boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef)
- {
- if (recordFolderService.isRecordFolder(nodeRef))
- {
- return freezeService.isFrozen(nodeRef) || freezeService.hasFrozenChildren(nodeRef);
- }
- if (recordService.isRecord(nodeRef))
- {
- return freezeService.isFrozen(nodeRef);
- }
- throw new AlfrescoRuntimeException("The nodeRef '" + nodeRef + "' is neither a record nor a record folder.");
- }
-
public PersonService getPersonService()
{
return personService;
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/AbstractDisposableItem.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/AbstractDisposableItem.java
index bd0d9e17dd..a064d97c38 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/AbstractDisposableItem.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/AbstractDisposableItem.java
@@ -30,6 +30,10 @@ package org.alfresco.module.org_alfresco_module_rm.model.behaviour;
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.model.BaseBehaviourBean;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+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;
@@ -52,7 +56,13 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
/** disposition service */
protected DispositionService dispositionService;
-
+
+ /** record service */
+ protected RecordService recordService;
+
+ /** record folder service */
+ protected RecordFolderService recordFolderService;
+
/**
* @param dispositionService disposition service
*/
@@ -60,6 +70,22 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
{
this.dispositionService = dispositionService;
}
+
+ /**
+ * @param recordService record service
+ */
+ public void setRecordService(RecordService recordService)
+ {
+ this.recordService = recordService;
+ }
+
+ /**
+ * @param recordFolderService record folder service
+ */
+ public void setRecordFolderService(RecordFolderService recordFolderService)
+ {
+ this.recordFolderService = recordFolderService;
+ }
/**
* Removes unwanted aspects
@@ -86,4 +112,35 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
}
}
+ /**
+ * Cleans and re-initiates the containing records
+ *
+ * @param childAssociationRef
+ */
+ protected void reinitializeRecordFolder(ChildAssociationRef childAssociationRef)
+ {
+
+ NodeRef newNodeRef = childAssociationRef.getChildRef();
+
+ AuthenticationUtil.runAs(() -> {
+ // clean record folder
+ cleanDisposableItem(nodeService, newNodeRef);
+
+ // re-initialise the record folder
+ recordFolderService.setupRecordFolder(newNodeRef);
+
+ // sort out the child records
+ for (NodeRef record : recordService.getRecords(newNodeRef))
+ {
+ // clean record
+ cleanDisposableItem(nodeService, record);
+
+ // Re-initiate the records in the new folder.
+ recordService.file(record);
+ }
+
+ return null;
+ }, AuthenticationUtil.getSystemUserName());
+ }
+
}
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java
index 500aec6f6d..9a09513f15 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java
@@ -27,13 +27,14 @@
package org.alfresco.module.org_alfresco_module_rm.model.rma.type;
+import static org.alfresco.model.ContentModel.TYPE_CONTENT;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.alfresco.model.ContentModel;
-import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
-import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
+import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem;
import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService;
import org.alfresco.repo.copy.CopyBehaviourCallback;
@@ -49,6 +50,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
/**
* rma:recordCategory behaviour bean
@@ -60,9 +62,10 @@ import org.alfresco.service.namespace.QName;
(
defaultType = "rma:recordCategory"
)
-public class RecordCategoryType extends BaseBehaviourBean
+public class RecordCategoryType extends AbstractDisposableItem
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
- NodeServicePolicies.OnCreateNodePolicy
+ NodeServicePolicies.OnCreateNodePolicy,
+ NodeServicePolicies.OnMoveNodePolicy
{
private final static List ACCEPTED_UNIQUE_CHILD_TYPES = new ArrayList<>();
private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_RECORD_CATEGORY, TYPE_RECORD_FOLDER);
@@ -73,9 +76,6 @@ public class RecordCategoryType extends BaseBehaviourBean
/** file plan permission service */
protected FilePlanPermissionService filePlanPermissionService;
- /** record folder service */
- private RecordFolderService recordFolderService;
-
/**
* @param vitalRecordService vital record service
*/
@@ -92,14 +92,6 @@ public class RecordCategoryType extends BaseBehaviourBean
this.filePlanPermissionService = filePlanPermissionService;
}
- /**
- * @param recordFolderService record folder service
- */
- public void setRecordFolderService(RecordFolderService recordFolderService)
- {
- this.recordFolderService = recordFolderService;
- }
-
/**
* On every event
*
@@ -204,6 +196,53 @@ public class RecordCategoryType extends BaseBehaviourBean
}
+ /**
+ * Record Category move behaviour
+ *
+ * @see org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy#onMoveNode(org.alfresco.service.cmr.repository.ChildAssociationRef, org.alfresco.service.cmr.repository.ChildAssociationRef)
+ */
+ @Override
+ @Behaviour
+ (
+ kind = BehaviourKind.CLASS,
+ notificationFrequency = NotificationFrequency.FIRST_EVENT
+ )
+ public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
+ {
+ // clean the child folders and records only if the old parent category has a disposition schedule set
+ // if it doesn't, then there are no old properties on the child nodes that have to be cleaned in order
+ // for new ones to be set
+ if (nodeService.getType(newChildAssocRef.getChildRef()).equals(TYPE_RECORD_CATEGORY)
+ && dispositionService.getDispositionSchedule(oldChildAssocRef.getParentRef()) != null)
+ {
+ reinitializeRecordFolders(newChildAssocRef);
+ }
+ }
+
+ /**
+ * Recursively reinitialize each folder in a structure of categories
+ * Unwanted aspects will be removed from the child records and the records will be re-filed
+ * Disposition schedule aspects and properties will be inherited from the new parent category
+ *
+ * @param childAssociationRef
+ */
+ private void reinitializeRecordFolders(ChildAssociationRef childAssociationRef)
+ {
+ for (ChildAssociationRef newChildRef : nodeService.getChildAssocs(childAssociationRef.getChildRef(),
+ ContentModel.ASSOC_CONTAINS,
+ RegexQNamePattern.MATCH_ALL))
+ {
+ if (nodeService.getType(newChildRef.getChildRef()).equals(TYPE_RECORD_CATEGORY))
+ {
+ reinitializeRecordFolders(newChildRef);
+ }
+ else if (!nodeService.getType(newChildRef.getChildRef()).equals(TYPE_CONTENT))
+ {
+ reinitializeRecordFolder(newChildRef);
+ }
+ }
+ }
+
/**
* Copy callback for record category
*/
diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java
index f9eb29dbfb..f6dee11ea2 100644
--- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java
+++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordFolderType.java
@@ -34,8 +34,6 @@ 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;
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.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
@@ -68,11 +66,6 @@ public class RecordFolderType extends AbstractDisposableItem
implements NodeServicePolicies.OnMoveNodePolicy,
NodeServicePolicies.OnCreateChildAssociationPolicy
{
- /** record service */
- private RecordService recordService;
-
- /** record folder service */
- private RecordFolderService recordFolderService;
/** vital record service */
protected VitalRecordService vitalRecordService;
@@ -85,22 +78,6 @@ public class RecordFolderType extends AbstractDisposableItem
private static final String MSG_CANNOT_CREATE_CHILDREN_IN_CLOSED_RECORD_FOLDER = "rm.service.add-children-to-closed-record-folder";
- /**
- * @param recordService record service
- */
- public void setRecordService(RecordService recordService)
- {
- this.recordService = recordService;
- }
-
- /**
- * @param recordFolderService record folder service
- */
- public void setRecordFolderService(RecordFolderService recordFolderService)
- {
- this.recordFolderService = recordFolderService;
- }
-
/**
* @param vitalRecordService vital record service
*/
@@ -131,31 +108,7 @@ public class RecordFolderType extends AbstractDisposableItem
{
if (!oldChildAssocRef.getParentRef().equals(newChildAssocRef.getParentRef()))
{
- final NodeRef newNodeRef = newChildAssocRef.getChildRef();
-
- AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork
+
+
+
+
+
@@ -987,6 +992,7 @@
+
@@ -1011,9 +1017,9 @@
+
-
@@ -1085,12 +1091,21 @@
+
+
+
+
+
+
+
+
+
@@ -1145,6 +1160,7 @@
+
+
@@ -1443,6 +1460,7 @@
+
diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js
index c7610cae61..c9312b48c2 100644
--- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js
+++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-common.lib.js
@@ -40,7 +40,7 @@ var Admin = Admin || {};
var toolInfo = {};
// collect the tools required for the Admin Console
- var tools = utils.findWebScripts("AdminConsole");
+ var tools = utils.findWebScriptsForCurrentUser("AdminConsole");
// process each tool and generate the data so that a label+link can
// be output by the component template for each tool required
diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml
index 55445b39d4..016f57de71 100644
--- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml
+++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-communitysummary.get.desc.xml
@@ -7,7 +7,7 @@
AdminConsole:Edition:Communityargument
- admin
+ sysadmininternalrequired
-
\ No newline at end of file
+
diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml
index 26816eeb20..2b7ae51945 100644
--- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml
+++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/admin-root.get.desc.xml
@@ -5,7 +5,7 @@
/admin/AdminConsoleHelperargument
- admin
+ sysadmininternalrequired
-
\ No newline at end of file
+
diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml
index 39eceeb809..b1179e5ebe 100644
--- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml
+++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/jmxdump.get.desc.xml
@@ -5,7 +5,7 @@
/api/admin/jmxdumpAdminConsoleHelper
- admin
+ sysadmininternal
-
\ No newline at end of file
+
diff --git a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml
index e852951f6c..fb3fea15ae 100644
--- a/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml
+++ b/remote-api/src/main/resources/alfresco/templates/webscripts/org/alfresco/repository/admin/usage.post.desc.xml
@@ -3,8 +3,8 @@
Update and retrieve repository usage/api/admin/usage
- admin
+ sysadminrequiredAdmininternal
-
\ No newline at end of file
+
diff --git a/remote-api/src/test/java/org/alfresco/AppContext01TestSuite.java b/remote-api/src/test/java/org/alfresco/AppContext01TestSuite.java
index 699b999070..5a857d0e9d 100644
--- a/remote-api/src/test/java/org/alfresco/AppContext01TestSuite.java
+++ b/remote-api/src/test/java/org/alfresco/AppContext01TestSuite.java
@@ -42,6 +42,7 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.TestPublicApiAtomPub10TCK.class,
org.alfresco.rest.api.tests.TestPublicApiAtomPub11TCK.class,
org.alfresco.rest.api.tests.TestPublicApiBrowser11TCK.class,
+ org.alfresco.repo.web.scripts.bulkimport.BulkImportParametersExtractorTest.class
})
public class AppContext01TestSuite
{
diff --git a/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java b/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java
index dfde2647c1..0ed1f3bf22 100644
--- a/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java
+++ b/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -47,6 +47,7 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.ActivitiesPostingTest.class,
org.alfresco.rest.api.tests.AuthenticationsTest.class,
org.alfresco.rest.api.tests.DiscoveryApiTest.class,
+ org.alfresco.rest.api.discovery.DiscoveryApiWebscriptUnitTest.class,
org.alfresco.rest.api.tests.GroupsTest.class,
org.alfresco.rest.api.tests.ModulePackagesApiTest.class,
org.alfresco.rest.api.tests.NodeApiTest.class,
diff --git a/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java b/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java
index 9545280e76..4548e38d4c 100644
--- a/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java
+++ b/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -76,6 +76,7 @@ import org.junit.runners.Suite;
org.alfresco.repo.web.scripts.site.SurfConfigTest.class,
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
+ org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
})
public class AppContext04TestSuite
{
diff --git a/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java b/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java
index c0610d25e8..4a9d6fe556 100644
--- a/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java
+++ b/remote-api/src/test/java/org/alfresco/repo/web/scripts/admin/AdminWebScriptTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -31,10 +31,12 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.Facetable;
import org.alfresco.repo.dictionary.IndexTokenisationMode;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authority.AuthorityServiceImpl;
import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.admin.RepoUsage;
@@ -48,11 +50,18 @@ import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.i18n.MessageLookup;
import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.AuthorityService;
+import org.alfresco.service.cmr.security.MutableAuthenticationService;
+import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.service.license.LicenseDescriptor;
import org.alfresco.service.namespace.QName;
import org.alfresco.test_category.OwnJVMTestsCategory;
+import org.alfresco.util.PropertyMap;
+import org.apache.commons.lang3.RandomStringUtils;
import org.json.JSONObject;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
@@ -74,29 +83,46 @@ import static org.mockito.Mockito.when;
@Category(OwnJVMTestsCategory.class)
public class AdminWebScriptTest extends BaseWebScriptTest
{
- private ApplicationContext ctx;
- private RepoAdminService repoAdminService;
- private DescriptorService descriptorService;
+ private RepoAdminService repoAdminService;
+ private DescriptorService descriptorService;
+ private PersonService personService;
+ private MutableAuthenticationService authenticationService;
+
private String admin;
private String guest;
+ private String user1_sysAdmin;
+ private String user2;
@Override
protected void setUp() throws Exception
{
super.setUp();
- ctx = getServer().getApplicationContext();
- repoAdminService = (RepoAdminService) ctx.getBean("RepoAdminService");
- descriptorService = (DescriptorService) ctx.getBean("DescriptorService");
+ ApplicationContext ctx = getServer().getApplicationContext();
+ repoAdminService = ctx.getBean("RepoAdminService", RepoAdminService.class);
+ descriptorService = ctx.getBean("DescriptorService", DescriptorService.class);
+ personService = ctx.getBean("PersonService", PersonService.class);
+ authenticationService = ctx.getBean("AuthenticationService", MutableAuthenticationService.class);
+ AuthorityService authorityService = ctx.getBean("AuthorityService", AuthorityService.class);
admin = AuthenticationUtil.getAdminUserName();
guest = AuthenticationUtil.getGuestUserName();
AuthenticationUtil.setFullyAuthenticatedUser(admin);
+
+ user1_sysAdmin = RandomStringUtils.randomAlphabetic(10);
+ String user1_password = RandomStringUtils.randomAlphabetic(10);
+ createUser(user1_sysAdmin, user1_password);
+ authorityService.addAuthority(AuthorityServiceImpl.GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, user1_sysAdmin);
+
+ user2 = RandomStringUtils.randomAlphabetic(10);
+ String user2_password = RandomStringUtils.randomAlphabetic(10);
+ createUser(user2, user2_password);
}
@Override
protected void tearDown() throws Exception
{
super.tearDown();
+ AuthenticationUtil.clearCurrentSecurityContext();
}
public void testGetRestrictions() throws Exception
@@ -227,6 +253,129 @@ public class AdminWebScriptTest extends BaseWebScriptTest
assertTrue(property.getResidual());
}
+ public void testSysAdminAccess() throws Exception
+ {
+ AuthenticationUtil.clearCurrentSecurityContext();
+
+ String url = "/admin/admin-communitysummary";
+ TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url);
+
+ Response response = sendRequest(req, Status.STATUS_OK, user1_sysAdmin);
+ Document doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("System Summary"));
+
+ // Super Admin should still have access to all the scripts
+ response = sendRequest(req, Status.STATUS_OK, admin);
+ doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("System Summary"));
+ }
+
+ public void testSysAdminAccess_nodeBrowser() throws Exception
+ {
+ AuthenticationUtil.clearCurrentSecurityContext();
+
+ String nodeBrowserUrl = "/admin/admin-nodebrowser";
+
+ // test the get webscript of the node browser
+ TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(nodeBrowserUrl);
+ // The node browser is only accessible to admins, not sysAdmins
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // test the post webscript of the node browser too
+ TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(nodeBrowserUrl, "",
+ "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA");
+ // The node browser is only accessible to admins, not sysAdmins
+ sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // Normal user shouldn't have access either
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2);
+
+ // Admin should have access to everything
+ Response response = sendRequest(getReq, Status.STATUS_OK, admin);
+ Document doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("Node Browser"));
+ }
+
+ public void testSysAdminAccess_repoConsole() throws Exception
+ {
+ String repoConsoleUrl = "/admin/admin-repoconsole";
+
+ // test the get webscript of the repo console
+ TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(repoConsoleUrl);
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // test the post webscript of the repo console too
+ TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(repoConsoleUrl, "",
+ "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA");
+ sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // Normal user shouldn't have access either
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2);
+
+ // Admin should have access to everything
+ Response response = sendRequest(getReq, Status.STATUS_OK, admin);
+ Document doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("Model and Messages Console"));
+ }
+
+ public void testSysAdminAccess_tenantConsole() throws Exception
+ {
+ String tenantConsoleUrl = "/admin/admin-tenantconsole";
+ // test the get webscript of the tenant console
+ TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(tenantConsoleUrl);
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // test the post webscript of the tenant console too
+ TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(tenantConsoleUrl, "",
+ "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA");
+ sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // Normal user shouldn't have access either
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2);
+
+ // Admin should have access to everything
+ Response response = sendRequest(getReq, Status.STATUS_OK, admin);
+ Document doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("Tenant Admin Console"));
+ }
+
+ public void testSysAdminAccess_workflowConsole() throws Exception
+ {
+ String workflowConsoleUrl = "/admin/admin-workflowconsole";
+ // test the get webscript of the workflow console
+ TestWebScriptServer.GetRequest getReq = new TestWebScriptServer.GetRequest(workflowConsoleUrl);
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // test the post webscript of the workflow console too
+ TestWebScriptServer.PostRequest postReq = new TestWebScriptServer.PostRequest(workflowConsoleUrl, "",
+ "multipart/form-data; boundary=----WebKitFormBoundaryjacWCXfJ3KjtRenA");
+ sendRequest(postReq, Status.STATUS_UNAUTHORIZED, user1_sysAdmin);
+
+ // Normal user shouldn't have access either
+ sendRequest(getReq, Status.STATUS_UNAUTHORIZED, user2);
+
+ // Admin should have access to everything
+ Response response = sendRequest(getReq, Status.STATUS_OK, admin);
+ Document doc = Jsoup.parse(response.getContentAsString());
+ assertNotNull(doc.title());
+ assertTrue(doc.title().contains("Workflow Admin Console"));
+ }
+
+ public void testNonSysAdminAccess() throws Exception
+ {
+ AuthenticationUtil.clearCurrentSecurityContext();
+
+ String url = "/admin/admin-communitysummary";
+ TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url);
+
+ sendRequest(req, Status.STATUS_UNAUTHORIZED, user2);
+ }
+
private class SimplePropertyDefinition implements PropertyDefinition
{
private boolean isAspect;
@@ -350,4 +499,19 @@ public class AdminWebScriptTest extends BaseWebScriptTest
return null;
}
}
+
+ private void createUser(String username, String password)
+ {
+ if (!personService.personExists(username))
+ {
+ this.authenticationService.createAuthentication(username, password.toCharArray());
+
+ PropertyMap personProps = new PropertyMap();
+ personProps.put(ContentModel.PROP_USERNAME, username);
+ personProps.put(ContentModel.PROP_FIRSTNAME, "testFirstName");
+ personProps.put(ContentModel.PROP_LASTNAME, "testLastName");
+ personProps.put(ContentModel.PROP_EMAIL, username + "@email.com");
+ this.personService.createPerson(personProps);
+ }
+ }
}
diff --git a/remote-api/src/test/java/org/alfresco/repo/web/scripts/bulkimport/BulkImportParametersExtractorTest.java b/remote-api/src/test/java/org/alfresco/repo/web/scripts/bulkimport/BulkImportParametersExtractorTest.java
new file mode 100644
index 0000000000..c01089cf1c
--- /dev/null
+++ b/remote-api/src/test/java/org/alfresco/repo/web/scripts/bulkimport/BulkImportParametersExtractorTest.java
@@ -0,0 +1,252 @@
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.web.scripts.bulkimport;
+
+import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_BATCH_SIZE;
+import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_DISABLE_RULES;
+import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_NUM_THREADS;
+import static org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.PARAMETER_TARGET_NODEREF;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Map;
+
+import org.alfresco.repo.bulkimport.BulkImportParameters;
+import org.alfresco.repo.bulkimport.BulkImportParameters.ExistingFileMode;
+import org.alfresco.repo.web.scripts.bulkimport.AbstractBulkFileSystemImportWebScript.BulkImportParametersExtractor;
+import org.alfresco.service.cmr.model.FileNotFoundException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.junit.Test;
+import org.springframework.extensions.webscripts.WebScriptException;
+
+public class BulkImportParametersExtractorTest
+{
+ private static final String TEST_NODE_REF = "workspace://SpacesStore/this-is-just-a-test-ref";
+ private static final String TEST_MISSING_NODE_REF = "workspace://SpacesStore/this-is-just-a-not-existing-test-ref";
+ private static final Integer DEFAULT_BATCH_SIZE = 1234;
+ private static final Integer DEFAULT_NUMBER_OF_THREADS = 4321;
+
+ @Test
+ public void shouldExtractTargetRef() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertNotNull(params);
+ assertNotNull(params.getTarget());
+ assertEquals(TEST_NODE_REF, params.getTarget().toString());
+ }
+
+ @Test
+ public void shouldFallbackToDefaultValues() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertEquals(DEFAULT_BATCH_SIZE, params.getBatchSize());
+ assertEquals(DEFAULT_NUMBER_OF_THREADS, params.getNumThreads());
+ assertFalse(params.isDisableRulesService());
+ assertEquals(ExistingFileMode.SKIP, params.getExistingFileMode());
+ assertNull(params.getLoggingInterval());
+ }
+
+ @Test
+ public void shouldExtractDisableFolderRulesFlagWhenSetToTrue() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_DISABLE_RULES, "true"
+ ));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertTrue(params.isDisableRulesService());
+ }
+
+ @Test
+ public void shouldExtractDisableFolderRulesFlagWhenSetToFalse() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_DISABLE_RULES, "false"
+ ));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertFalse(params.isDisableRulesService());
+ }
+
+ @Test
+ public void shouldExtractDisableFolderRulesFlagWhenSetToNotBooleanValue() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_DISABLE_RULES, "unknown"
+ ));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertFalse(params.isDisableRulesService());
+ }
+
+ @Test
+ public void shouldPropagateFileNotFoundExceptionWhenTargetIsNotFound()
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_MISSING_NODE_REF));
+
+ assertThrows(FileNotFoundException.class, extractor::extract);
+ }
+
+ @Test
+ public void shouldExtractValidBatchSize() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_BATCH_SIZE, "1"));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertEquals(Integer.valueOf(1), params.getBatchSize());
+ }
+
+ @Test
+ public void shouldFailWithWebScriptExceptionWhenInvalidBatchSizeIsRequested() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_BATCH_SIZE, "not-a-number"));
+
+ try
+ {
+ extractor.extract();
+ } catch (WebScriptException e)
+ {
+ assertNotNull(e.getMessage());
+ assertTrue(e.getMessage().contains(PARAMETER_BATCH_SIZE));
+ return;
+ }
+
+ fail("Expected exception to be thrown.");
+ }
+
+ @Test
+ public void shouldFailWithWebScriptExceptionWhenNegativeBatchSizeIsRequested() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_BATCH_SIZE, "-1"));
+
+ try
+ {
+ extractor.extract();
+ } catch (WebScriptException e)
+ {
+ assertNotNull(e.getMessage());
+ assertTrue(e.getMessage().contains(PARAMETER_BATCH_SIZE));
+ return;
+ }
+
+ fail("Expected exception to be thrown.");
+ }
+
+ @Test
+ public void shouldExtractValidNumberOfThreads() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_NUM_THREADS, "1"));
+
+ final BulkImportParameters params = extractor.extract();
+
+ assertEquals(Integer.valueOf(1), params.getNumThreads());
+ }
+
+ @Test
+ public void shouldFailWithWebScriptExceptionWhenInvalidNumberOfThreadsIsRequested() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_NUM_THREADS, "not-a-number"));
+
+ try
+ {
+ extractor.extract();
+ } catch (WebScriptException e)
+ {
+ assertNotNull(e.getMessage());
+ assertTrue(e.getMessage().contains(PARAMETER_NUM_THREADS));
+ return;
+ }
+
+ fail("Expected exception to be thrown.");
+ }
+
+ @Test
+ public void shouldFailWithWebScriptExceptionWhenNegativeNumberOfThreadsIsRequested() throws FileNotFoundException
+ {
+ final BulkImportParametersExtractor extractor = givenExtractor(Map.of(
+ PARAMETER_TARGET_NODEREF, TEST_NODE_REF,
+ PARAMETER_NUM_THREADS, "-1"));
+
+ try
+ {
+ extractor.extract();
+ } catch (WebScriptException e)
+ {
+ assertNotNull(e.getMessage());
+ assertTrue(e.getMessage().contains(PARAMETER_NUM_THREADS));
+ return;
+ }
+
+ fail("Expected exception to be thrown.");
+ }
+
+ private BulkImportParametersExtractor givenExtractor(Map params)
+ {
+
+ return new BulkImportParametersExtractor(params::get, this::testRefCreator, DEFAULT_BATCH_SIZE, DEFAULT_NUMBER_OF_THREADS);
+ }
+
+ private NodeRef testRefCreator(String nodeRef, String path) throws FileNotFoundException
+ {
+ if (TEST_MISSING_NODE_REF.equals(nodeRef))
+ {
+ throw new FileNotFoundException(new NodeRef(nodeRef));
+ }
+ return new NodeRef(nodeRef);
+ }
+
+}
\ No newline at end of file
diff --git a/remote-api/src/test/java/org/alfresco/rest/DeletedNodesTest.java b/remote-api/src/test/java/org/alfresco/rest/DeletedNodesTest.java
index fcbe066ffc..b1d2f36d44 100644
--- a/remote-api/src/test/java/org/alfresco/rest/DeletedNodesTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/DeletedNodesTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2020 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -78,8 +78,6 @@ public class DeletedNodesTest extends AbstractSingleNetworkSiteTest
protected static final String URL_DELETED_NODES = "deleted-nodes";
private static final String URL_RENDITIONS = "renditions";
-
- private final static long DELAY_IN_MS = 500;
@Override
public void setup() throws Exception
@@ -704,6 +702,73 @@ public class DeletedNodesTest extends AbstractSingleNetworkSiteTest
assertNull("We don't show the parent id for a deleted node",aNode.getParentId());
}
+ @Test
+ public void testRequestArchivedContentDirectUrl() throws Exception
+ {
+ setRequestContext(user1);
+
+ String myNodeId = getMyNodeId();
+
+ String fileName = "TestDocumentToArchive.txt";
+ Document testDocumentToArchive = new Document();
+ testDocumentToArchive.setName(fileName);
+ testDocumentToArchive.setNodeType(TYPE_CM_CONTENT);
+
+ // create *empty* text file
+ HttpResponse response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(testDocumentToArchive), 201);
+ Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+
+ final String contentNodeId = document.getId();
+ deleteNode(contentNodeId);
+
+ // Check the upload response
+ assertEquals(fileName, document.getName());
+ ContentInfo contentInfo = document.getContent();
+ assertNotNull(contentInfo);
+ assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
+
+ HttpResponse dauResponse = post(getRequestArchivedContentDirectUrl(contentNodeId), null, null, null, null, 501);
+ }
+
+ @Test
+ public void testRequestArchivedRenditionDirectUrl() throws Exception
+ {
+ setRequestContext(user1);
+
+ // Create a folder within the site document's library
+ Date now = new Date();
+ String folder1 = "folder" + now.getTime() + "_1";
+ Folder createdFolder = createFolder(tDocLibNodeId, folder1, null);
+ assertNotNull(createdFolder);
+ String f1Id = createdFolder.getId();
+
+ // Create multipart request using an existing file
+ String fileName = "quick.pdf";
+ File file = getResourceFile(fileName);
+ MultiPartBuilder multiPartBuilder = MultiPartBuilder.create().setFileData(new MultiPartBuilder.FileData(fileName, file));
+ MultiPartBuilder.MultiPartRequest reqBody = multiPartBuilder.build();
+
+ // Upload quick.pdf file into 'folder'
+ HttpResponse response = post(getNodeChildrenUrl(f1Id), reqBody.getBody(), null, reqBody.getContentType(), 201);
+ Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+ String contentNodeId = document.getId();
+
+ Rendition rendition = createAndGetRendition(contentNodeId, "doclib");
+ assertNotNull(rendition);
+ String renditionID = rendition.getId();
+ assertEquals(Rendition.RenditionStatus.CREATED, rendition.getStatus());
+
+ deleteNode(contentNodeId);
+
+ HttpResponse dauResponse = post(getRequestArchivedRenditonContentDirectUrl(contentNodeId, renditionID), null, null, null, null, 501);
+ }
+
+ private String addToDocumentLibrary(String name, String nodeType, String userId) throws Exception
+ {
+ String parentId = getSiteContainerNodeId(Nodes.PATH_MY, "documentLibrary");
+ return createNode(parentId, name, nodeType, null).getId();
+ }
+
private String getDeletedNodeRenditionsUrl(String nodeId)
{
return URL_DELETED_NODES + "/" + nodeId + "/" + URL_RENDITIONS;
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscriptUnitTest.java b/remote-api/src/test/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscriptUnitTest.java
new file mode 100644
index 0000000000..394c13a8f1
--- /dev/null
+++ b/remote-api/src/test/java/org/alfresco/rest/api/discovery/DiscoveryApiWebscriptUnitTest.java
@@ -0,0 +1,87 @@
+/*
+ * #%L
+ * Alfresco Remote API
+ * %%
+ * Copyright (C) 2005 - 2021 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.api.discovery;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+/**
+ * @author Mikołaj Brzeziński
+ */
+public class DiscoveryApiWebscriptUnitTest
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+ private static final Boolean DISABLED = Boolean.FALSE;
+
+ private DiscoveryApiWebscript discoveryApiWebscript = mock(DiscoveryApiWebscript.class);
+ private RestApiDirectUrlConfig restApiDirectUrlConfig = mock(RestApiDirectUrlConfig.class);
+ private ContentService contentService = mock(ContentService.class);
+
+ public void mockedAsserts(boolean restEnabled, boolean systemwideEnabled)
+ {
+ when(contentService.isContentDirectUrlEnabled()).thenReturn(systemwideEnabled);
+ when(restApiDirectUrlConfig.isEnabled()).thenReturn(restEnabled);
+ assertEquals(systemwideEnabled, contentService.isContentDirectUrlEnabled());
+ assertEquals(restEnabled, restApiDirectUrlConfig.isEnabled());
+ when(discoveryApiWebscript.isContentDirectUrlEnabled()).thenReturn(restEnabled && systemwideEnabled);
+ }
+
+ @Test
+ public void testEnabledConfig_RestEnabledAndSystemwideEnabled()
+ {
+ mockedAsserts(ENABLED,ENABLED);
+ assertTrue("Direct Acess URLs are enabled",discoveryApiWebscript.isContentDirectUrlEnabled());
+ }
+
+ @Test
+ public void testDisabledConfig_RestEnabledAndSystemwideDisabled()
+ {
+ mockedAsserts(ENABLED,DISABLED);
+ assertFalse("Direct Access URLs are disabled system-wide",discoveryApiWebscript.isContentDirectUrlEnabled());
+ }
+
+ @Test
+ public void testDisabledConfig_RestDisabledAndSystemwideDisabled()
+ {
+ mockedAsserts(DISABLED,DISABLED);
+ assertFalse("REST API Direct Access URLs are disabled and Direct Access URLs are disabled system-wide ",discoveryApiWebscript.isContentDirectUrlEnabled());
+ }
+
+ @Test
+ public void testDisabledConfig_RestDisabledAndSystemwideEnabled()
+ {
+ mockedAsserts(DISABLED,ENABLED);
+ assertFalse("REST API direct access URLs are disabled",discoveryApiWebscript.isContentDirectUrlEnabled());
+ }
+
+}
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/RestApiDirectUrlConfigUnitTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/RestApiDirectUrlConfigUnitTest.java
new file mode 100644
index 0000000000..e04c07338f
--- /dev/null
+++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/RestApiDirectUrlConfigUnitTest.java
@@ -0,0 +1,132 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.api.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
+import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for REST API direct access URL configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public class RestApiDirectUrlConfigUnitTest
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+ private static final Boolean DISABLED = Boolean.FALSE;
+
+ private static final Long DEFAULT_EXPIRY_TIME_IN_SECS = 20L;
+
+ private RestApiDirectUrlConfig restApiDirectUrlConfig;
+
+ @Before
+ public void setup()
+ {
+ this.restApiDirectUrlConfig = new RestApiDirectUrlConfig();
+ SystemWideDirectUrlConfig sysConfig = new SystemWideDirectUrlConfig();
+ sysConfig.setEnabled(ENABLED);
+ sysConfig.setDefaultExpiryTimeInSec(30L);
+ sysConfig.setMaxExpiryTimeInSec(300L);
+ restApiDirectUrlConfig.setSystemWideDirectUrlConfig(sysConfig);
+ }
+
+ @Test
+ public void testValidConfig_RemainsEnabled()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ restApiDirectUrlConfig.validate();
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testValidConfig_RemainsDisabled()
+ {
+ setupDirectAccessConfig(DISABLED, DEFAULT_EXPIRY_TIME_IN_SECS);
+
+ assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
+ restApiDirectUrlConfig.validate();
+ assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testValidConfig_DefaultExpiryTimeMissing()
+ {
+ setupDirectAccessConfig(ENABLED, null);
+
+ assertNull("Expected REST API default expiry time to be null", restApiDirectUrlConfig.getDefaultExpiryTimeInSec());
+ restApiDirectUrlConfig.validate();
+ Long expectedDefaultExpiryTime = restApiDirectUrlConfig.getSysWideDefaultExpiryTimeInSec();
+ assertEquals("Expected REST API default expiry time to be set to the system-wide default", expectedDefaultExpiryTime, restApiDirectUrlConfig.getDefaultExpiryTimeInSec());
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeZero()
+ {
+ setupDirectAccessConfig(ENABLED, 0L);
+
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ restApiDirectUrlConfig.validate();
+ assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeNegative()
+ {
+ setupDirectAccessConfig(ENABLED, -1L);
+
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ restApiDirectUrlConfig.validate();
+ assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsSystemMax()
+ {
+ Long systemMax = restApiDirectUrlConfig.getSysWideMaxExpiryTimeInSec();
+ setupDirectAccessConfig(ENABLED, systemMax + 1);
+
+ assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
+ restApiDirectUrlConfig.validate();
+ assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
+ }
+
+ /* Helper method to set system-wide direct access url configuration settings */
+ private void setupDirectAccessConfig(Boolean isEnabled, Long defaultExpiryTime)
+ {
+ restApiDirectUrlConfig.setEnabled(isEnabled);
+ restApiDirectUrlConfig.setDefaultExpiryTimeInSec(defaultExpiryTime);
+ }
+}
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java
index 4938ecf42e..c6f0d16d77 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2020 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -96,6 +96,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
private static final String RESOURCE_PREFIX = "publicapi/upload/";
protected static final String URL_NODES = "nodes";
+ protected static final String URL_DELETED_NODES = "deleted-nodes";
protected static final String URL_RENDITIONS = "renditions";
protected static final String URL_VERSIONS = "versions";
@@ -136,6 +137,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
protected static PersonService personService;
protected final String RUNID = System.currentTimeMillis()+"";
+
+ private static final String REQUEST_DIRECT_ACCESS_URL = "request-direct-access-url";
@Override
@Before
@@ -211,6 +214,21 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
setRequestContext(null);
}
+ protected String getRequestContentDirectUrl(String nodeId)
+ {
+ return URL_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL;
+ }
+
+ protected String getRequestArchivedContentDirectUrl(String nodeId)
+ {
+ return URL_DELETED_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL;
+ }
+
+ protected String getRequestArchivedRenditonContentDirectUrl(String nodeId, String renditionID)
+ {
+ return URL_DELETED_NODES + "/" + nodeId + "/" + URL_RENDITIONS + "/" + renditionID + "/" + REQUEST_DIRECT_ACCESS_URL;
+ }
+
/**
* The api scope. either public or private
*
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/BufferedResponseTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/BufferedResponseTest.java
index 39c86ed916..2eb0f15df9 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/BufferedResponseTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/BufferedResponseTest.java
@@ -26,23 +26,23 @@
*/
package org.alfresco.rest.api.tests;
-import org.alfresco.repo.web.scripts.BufferedResponse;
-import org.alfresco.repo.web.scripts.TempOutputStream;
-import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
-import org.alfresco.util.TempFileProvider;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.util.Arrays;
+import java.util.function.Supplier;
import java.util.stream.Stream;
+import org.alfresco.repo.web.scripts.BufferedResponse;
+import org.alfresco.repo.web.scripts.TempOutputStream;
+import org.alfresco.util.TempFileProvider;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
/**
* Test that BufferedResponse uses a temp file instead of buffering the entire output stream in memory
*
@@ -82,17 +82,25 @@ public class BufferedResponseTest
public void testOutputStream() throws IOException
{
File bufferTempDirectory = TempFileProvider.getTempDir(TEMP_DIRECTORY_NAME);
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false,true);
- BufferedResponse response = new BufferedResponse(null, 0, streamFactory);
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory,
+ MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
- long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX );
- copyFileToOutputStream(response);
- long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
-
- response.getOutputStream().close();
+ final long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
- Assert.assertEquals(countBefore + 1, countAfter);
+ try (BufferedResponse response = new BufferedResponse(null, 0, streamFactory))
+ {
+ copyFileToOutputStream(response);
+ final long countBeforeClose = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
+ response.getOutputStream().close();
+ final long countAfterClose = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
+
+ Assert.assertEquals(countBefore + 1, countBeforeClose);
+ Assert.assertEquals(countBefore + 1, countAfterClose);
+ }
+
+ final long countAfterDestroy = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
+ Assert.assertEquals(countBefore, countAfterDestroy);
}
private void copyFileToOutputStream(BufferedResponse response) throws IOException
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/DiscoveryApiTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/DiscoveryApiTest.java
index 6cfbdb9885..f244401350 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/DiscoveryApiTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/DiscoveryApiTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -185,6 +185,7 @@ public class DiscoveryApiTest extends AbstractSingleNetworkSiteTest
assertTrue(statusInfo.getIsAuditEnabled());
assertTrue(statusInfo.getIsQuickShareEnabled());
assertTrue(statusInfo.getIsThumbnailGenerationEnabled());
+ assertFalse(statusInfo.getIsDirectAccessUrlEnabled());
// Check modules
List modulePackageList = repositoryInfo.getModules();
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java
index e84ee463e1..99c57462dd 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java
@@ -836,13 +836,13 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
// Get network admin's groups by explicit ID.
{
ListResponse groups = groupsProxy.getGroupsByPersonId(networkAdmin, null, "Couldn't get user's groups", 200);
- assertEquals(6L, (long) groups.getPaging().getTotalItems());
+ assertEquals(7L, (long) groups.getPaging().getTotalItems());
}
// test -me- alias (as network admin)
{
ListResponse groups = groupsProxy.getGroupsByPersonId("-me-", null, "Couldn't get user's groups", 200);
- assertEquals(6L, (long) groups.getPaging().getCount());
+ assertEquals(7L, (long) groups.getPaging().getCount());
Iterator it = groups.getList().iterator();
assertEquals("GROUP_ALFRESCO_ADMINISTRATORS", it.next().getId());
}
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/NodeApiTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/NodeApiTest.java
index 94762872c0..8acf9683f2 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/NodeApiTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/NodeApiTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -82,6 +82,7 @@ import org.alfresco.rest.api.tests.client.data.Folder;
import org.alfresco.rest.api.tests.client.data.Node;
import org.alfresco.rest.api.tests.client.data.PathInfo;
import org.alfresco.rest.api.tests.client.data.PathInfo.ElementInfo;
+import org.alfresco.rest.api.tests.client.data.Rendition;
import org.alfresco.rest.api.tests.client.data.SiteRole;
import org.alfresco.rest.api.tests.client.data.UserInfo;
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
@@ -6355,5 +6356,120 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertFalse((Boolean) constraintParameters.get("requiresMatch"));
}
+ @Test
+ public void testRequestContentDirectUrl() throws Exception
+ {
+ setRequestContext(user1);
+
+ // Use existing test file
+ String fileName = "quick-1.txt";
+ File file = getResourceFile(fileName);
+
+ MultiPartBuilder multiPartBuilder = MultiPartBuilder.create().setFileData(new MultiPartBuilder.FileData(fileName, file));
+ MultiPartBuilder.MultiPartRequest reqBody = multiPartBuilder.build();
+
+ // Upload text content
+ HttpResponse response = post(getNodeChildrenUrl(Nodes.PATH_MY), reqBody.getBody(), null, reqBody.getContentType(), 201);
+ Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+
+ final String contentNodeId = document.getId();
+
+ // Check the upload response
+ assertEquals(fileName, document.getName());
+ ContentInfo contentInfo = document.getContent();
+ assertNotNull(contentInfo);
+ assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
+
+ HttpResponse dauResponse = post(getRequestContentDirectUrl(contentNodeId), null, null, null, null, 501);
+ }
+
+ @Test
+ public void testRequestVersionsContentDirectUrl() throws Exception
+ {
+ setRequestContext(user1);
+
+ String myNodeId = getMyNodeId();
+
+ Document d1 = new Document();
+ d1.setName("d1.txt");
+ d1.setNodeType(TYPE_CM_CONTENT);
+
+ // create *empty* text file - as of now, versioning is not enabled by default
+ HttpResponse response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1), 201);
+ Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+
+ String docId = documentResp.getId();
+ assertFalse(documentResp.getAspectNames().contains("cm:versionable"));
+ assertNull(documentResp.getProperties()); // no properties (ie. no "cm:versionLabel")
+
+ int majorVersion = 1;
+ int minorVersion = 0;
+
+ String content = "The quick brown fox jumps over the lazy dog ";
+
+ Map params = new HashMap<>();
+ params.put("comment", "my version ");
+
+ documentResp = updateTextFile(docId, content, params);
+ assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
+ assertNotNull(documentResp.getProperties());
+ assertEquals(majorVersion+"."+minorVersion, documentResp.getProperties().get("cm:versionLabel"));
+
+ final String contentNodeId = documentResp.getId();
+
+ // Check the upload response
+ assertNotNull(documentResp.getProperties());
+ assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
+ ContentInfo contentInfo = documentResp.getContent();
+ assertNotNull(contentInfo);
+ assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
+
+ HttpResponse dauResponse = post(getRequestContentDirectUrl(contentNodeId), null, null, null, null, 501);
+ }
+
+ @Test
+ public void testRequestRenditionContentDirectUrl() throws Exception
+ {
+ setRequestContext(user1);
+
+ RepoService.TestNetwork networkN1;
+ RepoService.TestPerson userOneN1;
+ Site userOneN1Site;
+
+ networkN1 = repoService.createNetworkWithAlias("ping", true);
+ networkN1.create();
+ userOneN1 = networkN1.createUser();
+
+ setRequestContext(networkN1.getId(), userOneN1.getId(), null);
+
+ String siteTitle = "RandomSite" + System.currentTimeMillis();
+ userOneN1Site = createSite(siteTitle, SiteVisibility.PRIVATE);
+
+ // Create a folder within the site document's library
+ String folderName = "folder" + System.currentTimeMillis();
+ String parentId = getSiteContainerNodeId(userOneN1Site.getId(), "documentLibrary");
+ String folder_Id = createNode(parentId, folderName, TYPE_CM_FOLDER, null).getId();
+
+ // Create multipart request - pdf file
+ String renditionName = "doclib";
+ String fileName = "quick.pdf";
+ File file = getResourceFile(fileName);
+ MultiPartRequest reqBody = MultiPartBuilder.create()
+ .setFileData(new FileData(fileName, file))
+ .setRenditions(Collections.singletonList(renditionName))
+ .build();
+
+ // Upload quick.pdf file into 'folder' - including request to create 'doclib' thumbnail
+ HttpResponse response = post(getNodeChildrenUrl(folder_Id), reqBody.getBody(), null, reqBody.getContentType(), 201);
+ Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+ String contentNodeId = document.getId();
+
+ // wait and check that rendition is created ...
+ Rendition rendition = waitAndGetRendition(contentNodeId, null, renditionName);
+ assertNotNull(rendition);
+ assertEquals(Rendition.RenditionStatus.CREATED, rendition.getStatus());
+
+ HttpResponse dauResponse = post(getRequestContentDirectUrl(contentNodeId), null, null, null, null, 501);
+ }
}
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/TempOutputStreamTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/TempOutputStreamTest.java
index ae7b475f99..653e9256d8 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/TempOutputStreamTest.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/TempOutputStreamTest.java
@@ -33,11 +33,11 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.Arrays;
+import java.util.function.Supplier;
import java.util.stream.Stream;
import org.alfresco.repo.content.ContentLimitViolationException;
import org.alfresco.repo.web.scripts.TempOutputStream;
-import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
import org.alfresco.util.TempFileProvider;
import org.junit.Assert;
import org.junit.Test;
@@ -57,11 +57,12 @@ public class TempOutputStreamTest
@Test
public void testInMemoryStream() throws IOException
{
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory,
+ MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD - 1024L);
{
- TempOutputStream outputStream = streamFactory.createOutputStream();
+ TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -83,8 +84,8 @@ public class TempOutputStreamTest
{
// Create stream factory that doesn't delete temp file on stream close
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
- TempOutputStream outputStream = streamFactory.createOutputStream();
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
+ TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -107,26 +108,6 @@ public class TempOutputStreamTest
Assert.assertEquals(countBefore, countAfter);
}
- {
- // Create stream factory that deletes temp file on stream close
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, true);
- TempOutputStream outputStream = streamFactory.createOutputStream();
-
- long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
-
- StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
-
- // Check that temp file was created
- long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
- Assert.assertEquals(countBefore + 1, countAfter);
-
- outputStream.close();
-
- // Check that file was deleted on close
- countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
- Assert.assertEquals(countBefore, countAfter);
- }
-
file.delete();
}
@@ -140,9 +121,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(contentSize);
- // Create stream factory that deletes temp file on stream close
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
- TempOutputStream outputStream = streamFactory.createOutputStream();
+ // Create stream factory that deletes the temp file when the max Size is reached
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false);
+ TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -156,7 +137,7 @@ public class TempOutputStreamTest
// Expected
}
- // Check that file was already deleted on close
+ // Check that file was already deleted on error
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore, countAfter);
@@ -170,9 +151,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(contentSize);
- // Create stream factory that deletes temp file on stream close
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
- TempOutputStream outputStream = streamFactory.createOutputStream();
+ // Create stream factory that deletes the temp file when the max Size is reached
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false);
+ TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -186,7 +167,7 @@ public class TempOutputStreamTest
// Expected
}
- // Check that file was already deleted on close
+ // Check that file was already deleted on error
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore, countAfter);
@@ -200,9 +181,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD + 1024L);
// Create stream factory that doesn't delete temp file on stream close
- TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, true, false);
+ Supplier streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, true);
- TempOutputStream outputStream = streamFactory.createOutputStream();
+ TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -220,7 +201,7 @@ public class TempOutputStreamTest
// Compare content
String contentWriten = StreamUtils.copyToString(new BufferedInputStream(new FileInputStream(file)), Charset.defaultCharset());
- String contentRead = StreamUtils.copyToString(outputStream.getInputStream(), Charset.defaultCharset());
+ String contentRead = StreamUtils.copyToString(outputStream.toNewInputStream(), Charset.defaultCharset());
Assert.assertEquals(contentWriten, contentRead);
outputStream.destroy();
diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/TestCMIS.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/TestCMIS.java
index 8c3ec87f43..94ebd5b135 100644
--- a/remote-api/src/test/java/org/alfresco/rest/api/tests/TestCMIS.java
+++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/TestCMIS.java
@@ -60,8 +60,11 @@ import org.alfresco.cmis.client.impl.AlfrescoObjectFactoryImpl;
import org.alfresco.cmis.client.type.AlfrescoType;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
+import org.alfresco.opencmis.CMISDispatcher;
import org.alfresco.opencmis.CMISDispatcherRegistry.Binding;
+import org.alfresco.opencmis.CMISServletDispatcher;
import org.alfresco.opencmis.PublicApiAlfrescoCmisServiceFactory;
+import org.alfresco.opencmis.PublicApiBrowserCMISDispatcher;
import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService;
import org.alfresco.opencmis.dictionary.QNameFilter;
import org.alfresco.opencmis.dictionary.QNameFilterImpl;
@@ -99,6 +102,7 @@ import org.alfresco.rest.api.tests.client.data.NodeRating.Aggregate;
import org.alfresco.rest.api.tests.client.data.Person;
import org.alfresco.rest.api.tests.client.data.SiteRole;
import org.alfresco.rest.api.tests.client.data.Tag;
+import org.alfresco.rest.framework.core.exceptions.JsonpCallbackNotAllowedException;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -153,6 +157,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.surf.util.URLEncoder;
public class TestCMIS extends EnterpriseTestApi
@@ -517,6 +522,73 @@ public class TestCMIS extends EnterpriseTestApi
assertEquals(200, response.getStatusCode());
}
+ /**
+ * MNT-22428 Check the return from http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root&callback= when jsonp callback is disabled
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBrowserDisabledJSONPCallback() throws Exception
+ {
+ // disables unsecure callback jsonp
+ final PublicApiBrowserCMISDispatcher dispatcher = ctx.getBean(PublicApiBrowserCMISDispatcher.class);
+ dispatcher.setAllowUnsecureCallbackJSONP(false);
+
+ final TestNetwork network1 = getTestFixture().getRandomNetwork();
+ Iterator personIt = network1.getPersonIds().iterator();
+ final String personId = personIt.next();
+ assertNotNull(personId);
+ Person person = repoService.getPerson(personId);
+ assertNotNull(person);
+
+ publicApiClient.setRequestContext(new RequestContext(network1.getId(), personId));
+
+ // request with a callback parameter
+ HttpResponse response;
+ final Map params = Map.of("callback", "");
+ response = publicApiClient.get(network1.getId() + "/public/cmis/versions/1.1/browser/root", params);
+ assertEquals(403, response.getStatusCode());
+
+ String exceptionMessage = I18NUtil.getMessage(JsonpCallbackNotAllowedException.DEFAULT_MESSAGE_ID, params);
+ assertTrue(response.getResponse().endsWith(exceptionMessage));
+
+ // request without a callback parameter
+ response = publicApiClient.get(network1.getId() + "/public/cmis/versions/1.1/browser/root", null);
+ assertEquals(200, response.getStatusCode());
+ }
+
+ /*
+ * MNT-22428 Check the return from http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root&callback= when jsonp callback is enabled
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBrowserEnabledJSONPCallback() throws Exception
+ {
+ // enables unsecure callback jsonp
+ final PublicApiBrowserCMISDispatcher dispatcher = ctx.getBean(PublicApiBrowserCMISDispatcher.class);
+ dispatcher.setAllowUnsecureCallbackJSONP(true);
+
+ final TestNetwork network1 = getTestFixture().getRandomNetwork();
+ Iterator personIt = network1.getPersonIds().iterator();
+ final String personId = personIt.next();
+ assertNotNull(personId);
+ Person person = repoService.getPerson(personId);
+ assertNotNull(person);
+
+ publicApiClient.setRequestContext(new RequestContext(network1.getId(), personId));
+
+ // request with a callback parameter
+ HttpResponse response;
+ final Map params = Map.of("callback", "someFunction");
+ response = publicApiClient.get(network1.getId() + "/public/cmis/versions/1.1/browser/root", params);
+ assertEquals(200, response.getStatusCode());
+
+ // request without a callback parameter
+ response = publicApiClient.get(network1.getId() + "/public/cmis/versions/1.1/browser/root", null);
+ assertEquals(200, response.getStatusCode());
+ }
+
/**
* REPO-2041 / MNT-16236 Upload via cmis binding atom and browser files with different maxContentSize
*/
diff --git a/repository/pom.xml b/repository/pom.xml
index 3ad0b97b31..54bc8aaf71 100644
--- a/repository/pom.xml
+++ b/repository/pom.xml
@@ -7,7 +7,7 @@
org.alfrescoalfresco-community-repo
- 11.94-SNAPSHOT
+ 14.7-SNAPSHOT
@@ -718,7 +718,7 @@
org.alfrescoalfresco-transform-model
- ${dependency.transform.model.version}
+ ${dependency.alfresco-transform-model.version}
@@ -740,7 +740,7 @@
org.alfrescoalfresco-transform-model
- ${dependency.transform.model.version}
+ ${dependency.alfresco-transform-model.version}teststest
diff --git a/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java b/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java
index 2ce6f1e241..eb97162334 100644
--- a/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2019 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,12 @@
*/
package org.alfresco.repo.content;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
@@ -38,6 +44,8 @@ import org.alfresco.repo.content.ContentServicePolicies.OnContentPropertyUpdateP
import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
+import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
+import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.ClassPolicyDelegate;
@@ -54,6 +62,7 @@ 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.DirectAccessUrl;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.MimetypeServiceAware;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -82,7 +91,7 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
public class ContentServiceImpl implements ContentService, ApplicationContextAware
{
- private static Log logger = LogFactory.getLog(ContentServiceImpl.class);
+ private static final Log logger = LogFactory.getLog(ContentServiceImpl.class);
private DictionaryService dictionaryService;
private NodeService nodeService;
@@ -99,6 +108,8 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
/** Should we consider zero byte content to be the same as no content? */
private boolean ignoreEmptyContent;
+ private SystemWideDirectUrlConfig systemWideDirectUrlConfig;
+
/**
* The policy component
*/
@@ -140,7 +151,12 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
{
this.store = store;
}
-
+
+ public void setSystemWideDirectUrlConfig(SystemWideDirectUrlConfig systemWideDirectUrlConfig)
+ {
+ this.systemWideDirectUrlConfig = systemWideDirectUrlConfig;
+ }
+
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
@@ -451,16 +467,15 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
{
return getWriter(nodeRef,propertyQName, update, null);
}
-
+
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update,
- StorageClassSet storageClassSet)
+ StorageClassSet storageClassSet)
{
if (!isStorageClassesSupported(storageClassSet))
{
throw new UnsupportedStorageClassException(store, storageClassSet,
- "The supplied storage classes are not supported");
+ "The supplied storage classes are not supported");
}
-
if (nodeRef == null)
{
ContentContext ctx = new ContentContext(null, null, storageClassSet);
@@ -477,18 +492,18 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
if (storageClassSet != null)
{
- if (existingContentReader != null &&
- existingContentReader.getContentData() != null &&
+ if (existingContentReader != null &&
+ existingContentReader.getContentData() != null &&
existingContentReader.getContentData().getContentUrl() != null)
{
Set currentStorageClasses = findStorageClasses(nodeRef);
- if (currentStorageClasses != null &&
+ if (currentStorageClasses != null &&
!currentStorageClasses.equals(storageClassSet))
{
Set possibleTransitions = findStorageClassesTransitions(nodeRef)
.get(currentStorageClasses);
- if (possibleTransitions == null ||
+ if (possibleTransitions == null ||
!possibleTransitions.contains(storageClassSet))
{
throw new UnsupportedStorageClassException(store, storageClassSet,
@@ -501,11 +516,10 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
}
}
}
-
+
// get the content using the (potentially) existing content - the new content
// can be wherever the store decides.
- ContentContext ctx = new NodeContentContext(existingContentReader, null, nodeRef,
- propertyQName, storageClassSet);
+ ContentContext ctx = new NodeContentContext(existingContentReader, null, nodeRef, propertyQName);
ContentWriter writer = store.getWriter(ctx);
// Register the new URL for rollback cleanup
eagerContentStoreCleaner.registerNewContentUrl(writer.getContentUrl());
@@ -550,25 +564,6 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
return tempStore.getWriter(ContentContext.NULL_CONTEXT);
}
- @Override
- public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt)
- {
- ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
-
- // check that the URL is available
- if (contentData == null || contentData.getContentUrl() == null)
- {
- throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
- }
-
- if (store.isDirectAccessSupported())
- {
- return store.getDirectAccessUrl(contentData.getContentUrl(), expiresAt);
- }
-
- return null;
- }
-
/**
* Ensures that, upon closure of the output stream, the node is updated with
* the latest URL of the content to which it refers.
@@ -627,6 +622,106 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isContentDirectUrlEnabled()
+ {
+ return systemWideDirectUrlConfig.isEnabled() && store.isContentDirectUrlEnabled();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isContentDirectUrlEnabled(NodeRef nodeRef)
+ {
+ boolean contentDirectUrlEnabled = false;
+
+ // TODO: update this
+ if (systemWideDirectUrlConfig.isEnabled())
+ {
+ ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
+
+ // check that the URL is available
+ if (contentData == null || contentData.getContentUrl() == null)
+ {
+ throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
+ }
+
+ contentDirectUrlEnabled = (store.isContentDirectUrlEnabled(getContentUrl(nodeRef)));
+ }
+
+ return contentDirectUrlEnabled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor)
+ {
+ if (!systemWideDirectUrlConfig.isEnabled())
+ {
+ throw new DirectAccessUrlDisabledException("Direct access url isn't available.");
+ }
+
+ String contentUrl = getContentUrl(nodeRef);
+ String fileName = getFileName(nodeRef);
+ validFor = adjustValidFor(validFor);
+
+ DirectAccessUrl directAccessUrl = null;
+ if (store.isContentDirectUrlEnabled())
+ {
+ try
+ {
+ directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
+ }
+ catch (UnsupportedOperationException ex)
+ {
+ // expected exception
+ }
+ }
+ return directAccessUrl;
+ }
+
+ protected String getContentUrl(NodeRef nodeRef)
+ {
+ ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
+
+ // check that the URL is available
+ if (contentData == null || contentData.getContentUrl() == null)
+ {
+ throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
+ }
+
+ return contentData.getContentUrl();
+ }
+
+ protected String getFileName(NodeRef nodeRef)
+ {
+ String fileName = null;
+
+ try
+ {
+ fileName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
+ }
+ catch (InvalidNodeRefException ex)
+ {
+ }
+
+ return fileName;
+ }
+
+ private Long adjustValidFor(Long validFor)
+ {
+ if (validFor == null || validFor > systemWideDirectUrlConfig.getDefaultExpiryTimeInSec())
+ {
+ validFor = systemWideDirectUrlConfig.getDefaultExpiryTimeInSec();
+ }
+ return validFor;
+ }
+
@Override
public boolean isStorageClassesSupported(StorageClassSet storageClassSet)
{
@@ -691,4 +786,4 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
return store.findStorageClassesTransitions(contentData.getContentUrl());
}
-}
\ No newline at end of file
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java
index 7031a1cd8e..1fd9c9ba7d 100644
--- a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java
+++ b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -44,6 +44,7 @@ import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentStreamListener;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
+import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
@@ -106,7 +107,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
{
eventPublisher.publishEvent(new CachingContentStoreCreatedEvent(this));
}
-
+
@Override
public boolean isContentUrlSupported(String contentUrl)
{
@@ -140,7 +141,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
/**
* {@inheritDoc}
*
- * For {@link #SPOOF_PROTOCOL spoofed} URLs, the URL always exists.
+ * For {@link FileContentStore#SPOOF_PROTOCOL spoofed} URLs, the URL always exists.
*/
@Override
public boolean exists(String contentUrl)
@@ -481,14 +482,36 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
return this.beanName;
}
- public boolean isDirectAccessSupported()
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isContentDirectUrlEnabled()
{
- return backingStore.isDirectAccessSupported();
+ return backingStore.isContentDirectUrlEnabled();
}
- public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt)
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isContentDirectUrlEnabled(String contentUrl)
{
- return backingStore.getDirectAccessUrl(contentUrl, expiresAt);
+ return backingStore.isContentDirectUrlEnabled(contentUrl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName)
+ {
+ return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor)
+ {
+ return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
}
@Override
@@ -502,13 +525,13 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
{
return backingStore.getSupportedStorageClasses();
}
-
+
@Override
public void updateStorageClasses(String contentUrl, StorageClassSet storageClassSet, Map parameters)
{
backingStore.updateStorageClasses(contentUrl, storageClassSet, parameters);
}
-
+
@Override
public StorageClassSet findStorageClasses(String contentUrl)
{
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/AbstractDirectUrlConfig.java b/repository/src/main/java/org/alfresco/repo/content/directurl/AbstractDirectUrlConfig.java
new file mode 100644
index 0000000000..db6ebef9e6
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/AbstractDirectUrlConfig.java
@@ -0,0 +1,81 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+/**
+ * Direct Access Url configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public abstract class AbstractDirectUrlConfig implements DirectUrlConfig
+{
+ /** System-wide direct access URL configuration */
+ private SystemWideDirectUrlConfig systemWideDirectUrlConfig;
+
+ /** Direct access URL configuration settings */
+ private Boolean enabled;
+ private Long defaultExpiryTimeInSec;
+
+ public void setSystemWideDirectUrlConfig(SystemWideDirectUrlConfig systemWideDirectUrlConfig)
+ {
+ this.systemWideDirectUrlConfig = systemWideDirectUrlConfig;
+ }
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public void setDefaultExpiryTimeInSec(Long defaultExpiryTimeInSec)
+ {
+ this.defaultExpiryTimeInSec = defaultExpiryTimeInSec;
+ }
+
+ protected Boolean isSysWideEnabled()
+ {
+ return systemWideDirectUrlConfig.isEnabled();
+ }
+
+ public Long getSysWideDefaultExpiryTimeInSec()
+ {
+ return systemWideDirectUrlConfig.getDefaultExpiryTimeInSec();
+ }
+
+ public Long getSysWideMaxExpiryTimeInSec()
+ {
+ return systemWideDirectUrlConfig.getMaxExpiryTimeInSec();
+ }
+
+ public Boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public Long getDefaultExpiryTimeInSec()
+ {
+ return defaultExpiryTimeInSec;
+ }
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfig.java b/repository/src/main/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfig.java
new file mode 100644
index 0000000000..f6a390dbd6
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfig.java
@@ -0,0 +1,135 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Content store direct access URL configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public class ContentStoreDirectUrlConfig extends AbstractDirectUrlConfig
+{
+ private static final Log logger = LogFactory.getLog(ContentStoreDirectUrlConfig.class);
+
+ private Long maxExpiryTimeInSec;
+
+ public void setMaxExpiryTimeInSec(Long maxExpiryTimeInSec)
+ {
+ this.maxExpiryTimeInSec = maxExpiryTimeInSec;
+ }
+
+ public Long getMaxExpiryTimeInSec()
+ {
+ return maxExpiryTimeInSec;
+ }
+
+ /**
+ * Configuration initialise
+ */
+ public void init()
+ {
+ validate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void validate()
+ {
+ // Disable direct access URLs for the content store if any error found in the content store direct access URL config
+ try
+ {
+ validateDirectAccessUrlConfig();
+ }
+ catch (InvalidDirectAccessUrlConfigException ex)
+ {
+ logger.error("Disabling content store direct access URLs due to configuration error: " + ex.getMessage());
+ setEnabled(false);
+ }
+ logger.info("Content store direct access URLs are " + (isEnabled() ? "enabled" : "disabled"));
+ }
+
+ /* Helper method to validate the content direct access url configuration settings */
+ private void validateDirectAccessUrlConfig() throws InvalidDirectAccessUrlConfigException
+ {
+ if (isEnabled())
+ {
+ if (getMaxExpiryTimeInSec() == null)
+ {
+ logger.warn(String.format("Maximum expiry time property is missing: setting to system-wide maximum [%s].", getSysWideMaxExpiryTimeInSec()));
+ setMaxExpiryTimeInSec(getSysWideMaxExpiryTimeInSec());
+ }
+ else if (getMaxExpiryTimeInSec() > getSysWideMaxExpiryTimeInSec())
+ {
+ String errorMsg = String.format("Content store direct access URL maximum expiry time [%s] exceeds system-wide maximum expiry time [%s].",
+ getMaxExpiryTimeInSec(), getSysWideMaxExpiryTimeInSec());
+ throw new InvalidDirectAccessUrlConfigException(errorMsg);
+ }
+
+ if (getDefaultExpiryTimeInSec() == null)
+ {
+ logger.warn(String.format("Default expiry time property is missing: setting to system-wide default [%s].", getSysWideDefaultExpiryTimeInSec()));
+ setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
+ }
+ else if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
+ {
+ logger.warn(String.format("Default expiry time property [%s] exceeds maximum expiry time for content store [%s]: setting to system-wide default [%s].",
+ getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec(), getSysWideDefaultExpiryTimeInSec()));
+ setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
+ }
+ else if (getDefaultExpiryTimeInSec() > getSysWideDefaultExpiryTimeInSec())
+ {
+ logger.warn(String.format("Default expiry time property [%s] exceeds system-wide default expiry time [%s]: setting to system-wide default.",
+ getDefaultExpiryTimeInSec(), getSysWideDefaultExpiryTimeInSec()));
+ setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
+ }
+
+ if (getDefaultExpiryTimeInSec() < 1)
+ {
+ String errorMsg = String.format("Content store direct access URL default expiry time [%s] is invalid.", getDefaultExpiryTimeInSec());
+ throw new InvalidDirectAccessUrlConfigException(errorMsg);
+ }
+
+ if (getDefaultExpiryTimeInSec() > getSysWideMaxExpiryTimeInSec())
+ {
+ String errorMsg = String.format("Content store direct access URL default expiry time [%s] exceeds system-wide maximum expiry time [%s].",
+ getDefaultExpiryTimeInSec(), getSysWideMaxExpiryTimeInSec());
+ throw new InvalidDirectAccessUrlConfigException(errorMsg);
+ }
+
+ if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
+ {
+ String errorMsg = String.format("Content store direct access URL default expiry time [%s] exceeds content store maximum expiry time [%s].",
+ getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec());
+ throw new InvalidDirectAccessUrlConfigException(errorMsg);
+ }
+ }
+ }
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java
new file mode 100644
index 0000000000..d0702b398d
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java
@@ -0,0 +1,44 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Runtime exception thrown when direct access URLs are disabled.
+ *
+ * @author Sara Aspery
+ */
+public class DirectAccessUrlDisabledException extends AlfrescoRuntimeException
+{
+
+ private static final long serialVersionUID = -6506082117146782993L;
+
+ public DirectAccessUrlDisabledException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/DirectUrlConfig.java b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectUrlConfig.java
new file mode 100644
index 0000000000..68ed3a84da
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectUrlConfig.java
@@ -0,0 +1,41 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import org.alfresco.api.AlfrescoPublicApi;
+
+/**
+ * Direct Access Url configuration settings interface.
+ *
+ * @author Sara Aspery
+ */
+@AlfrescoPublicApi
+public interface DirectUrlConfig
+{
+ Boolean isEnabled();
+ Long getDefaultExpiryTimeInSec();
+ void validate();
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/InvalidDirectAccessUrlConfigException.java b/repository/src/main/java/org/alfresco/repo/content/directurl/InvalidDirectAccessUrlConfigException.java
new file mode 100644
index 0000000000..5c6445ed5f
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/InvalidDirectAccessUrlConfigException.java
@@ -0,0 +1,44 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Runtime exception thrown when the direct access URL configuration settings are invalid.
+ *
+ * @author Sara Aspery
+ */
+public class InvalidDirectAccessUrlConfigException extends AlfrescoRuntimeException
+{
+ private static final long serialVersionUID = -6318313836484979887L;
+
+ public InvalidDirectAccessUrlConfigException(String msg)
+ {
+ super(msg);
+ }
+}
+
diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfig.java b/repository/src/main/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfig.java
new file mode 100644
index 0000000000..aa1a632742
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfig.java
@@ -0,0 +1,125 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * System-wide direct access URL configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public class SystemWideDirectUrlConfig implements DirectUrlConfig
+{
+ private static final Log logger = LogFactory.getLog(SystemWideDirectUrlConfig.class);
+
+ /** Direct access url configuration settings */
+ private Boolean enabled;
+ private Long defaultExpiryTimeInSec;
+ private Long maxExpiryTimeInSec;
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public void setDefaultExpiryTimeInSec(Long defaultExpiryTimeInSec)
+ {
+ this.defaultExpiryTimeInSec = defaultExpiryTimeInSec;
+ }
+
+ public void setMaxExpiryTimeInSec(Long maxExpiryTimeInSec)
+ {
+ this.maxExpiryTimeInSec = maxExpiryTimeInSec;
+ }
+
+ public Boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public Long getDefaultExpiryTimeInSec()
+ {
+ return defaultExpiryTimeInSec;
+ }
+
+ public Long getMaxExpiryTimeInSec()
+ {
+ return maxExpiryTimeInSec;
+ }
+
+ /**
+ * Configuration initialise
+ */
+ public void init()
+ {
+ validate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void validate()
+ {
+ // Disable direct access URLs system-wide if any error found in the system-wide direct access URL config
+ try
+ {
+ validateSystemDirectAccessUrlConfig();
+ }
+ catch (InvalidDirectAccessUrlConfigException ex)
+ {
+ logger.error("Disabling system-wide direct access URLs due to configuration error: " + ex.getMessage());
+ setEnabled(false);
+ }
+ logger.info("System-wide direct access URLs are " + (isEnabled() ? "enabled" : "disabled"));
+ }
+
+ /* Helper method to validate the system-wide direct access url configuration settings */
+ private void validateSystemDirectAccessUrlConfig() throws InvalidDirectAccessUrlConfigException
+ {
+ if (isEnabled())
+ {
+ if (getDefaultExpiryTimeInSec() == null || getDefaultExpiryTimeInSec() < 1)
+ {
+ throw new InvalidDirectAccessUrlConfigException("System-wide direct access URL default expiry time is missing or invalid.");
+ }
+
+ if (getMaxExpiryTimeInSec() == null || getMaxExpiryTimeInSec() < 1)
+ {
+ throw new InvalidDirectAccessUrlConfigException("System-wide direct access URL maximum expiry time is missing or invalid.");
+ }
+
+ if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
+ {
+ String errorMsg = String.format("System-wide direct access URL default expiry time [%s] exceeds maximum expiry time [%s].",
+ getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec());
+ throw new InvalidDirectAccessUrlConfigException(errorMsg);
+ }
+ }
+ }
+}
diff --git a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java
index 0ca5a94451..01d336de39 100644
--- a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java
+++ b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -44,6 +44,7 @@ import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
+import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -67,10 +68,9 @@ import org.apache.commons.logging.LogFactory;
* @see CachingContentStore
*/
public class AggregatingContentStore extends AbstractContentStore
-{
+{
private static final Log logger = LogFactory.getLog(AggregatingContentStore.class);
- private static final String REPLICATING_CONTENT_STORE_NOT_INITIALISED = "ReplicatingContentStore not initialised";
-
+
private ContentStore primaryStore;
private List secondaryStores;
@@ -138,7 +138,7 @@ public class AggregatingContentStore extends AbstractContentStore
{
if (primaryStore == null)
{
- throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
+ throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
}
// get a read lock so that we are sure that no replication is underway
@@ -173,12 +173,11 @@ public class AggregatingContentStore extends AbstractContentStore
}
}
- @Override
public boolean exists(String contentUrl)
{
if (primaryStore == null)
{
- throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
+ throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
}
// get a read lock so that we are sure that no replication is underway
@@ -244,7 +243,6 @@ public class AggregatingContentStore extends AbstractContentStore
}
}
- @Override
public ContentWriter getWriter(ContentContext ctx)
{
// get the writer
@@ -259,7 +257,6 @@ public class AggregatingContentStore extends AbstractContentStore
*
* @return Returns the value returned by the delete on the primary store.
*/
- @Override
public boolean delete(String contentUrl) throws ContentIOException
{
// delete on the primary store
@@ -273,39 +270,62 @@ public class AggregatingContentStore extends AbstractContentStore
}
/**
- * @return Returns true if at least one store supports direct access
+ * @return Returns {@code true} if at least one store supports direct access URLs
*/
- @Override
- public boolean isDirectAccessSupported()
+ public boolean isContentDirectUrlEnabled()
{
// Check the primary store
- boolean isDirectAccessSupported = primaryStore.isDirectAccessSupported();
+ boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled();
- if (!isDirectAccessSupported)
+ if (!isContentDirectUrlEnabled)
{
// Direct access is not supported by the primary store so we have to check the
// other stores
for (ContentStore store : secondaryStores)
{
+ isContentDirectUrlEnabled = store.isContentDirectUrlEnabled();
- isDirectAccessSupported = store.isDirectAccessSupported();
-
- if (isDirectAccessSupported)
+ if (isContentDirectUrlEnabled)
{
break;
}
}
}
- return isDirectAccessSupported;
+ return isContentDirectUrlEnabled;
}
- @Override
- public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt)
+ /**
+ * @return Returns {@code true} if at least one store supports direct access URL for node
+ */
+ public boolean isContentDirectUrlEnabled(String contentUrl)
+ {
+ // Check the primary store
+ boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled(contentUrl);
+
+ if (!isContentDirectUrlEnabled)
+ {
+ // Direct access is not supported by the primary store so we have to check the
+ // other stores
+ for (ContentStore store : secondaryStores)
+ {
+ isContentDirectUrlEnabled = store.isContentDirectUrlEnabled(contentUrl);
+
+ if (isContentDirectUrlEnabled)
+ {
+ break;
+ }
+ }
+ }
+
+ return isContentDirectUrlEnabled;
+ }
+
+ public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor)
{
if (primaryStore == null)
{
- throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
+ throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
}
// get a read lock so that we are sure that no replication is underway
@@ -321,13 +341,13 @@ public class AggregatingContentStore extends AbstractContentStore
// Check the primary store
try
{
- directAccessUrl = primaryStore.getDirectAccessUrl(contentUrl, expiresAt);
+ directAccessUrl = primaryStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
}
catch (UnsupportedOperationException e)
{
// The store does not support direct access URL
directAccessUrlSupported = false;
- }
+ }
catch (UnsupportedContentUrlException e)
{
// The store can't handle the content URL
@@ -344,7 +364,7 @@ public class AggregatingContentStore extends AbstractContentStore
{
try
{
- directAccessUrl = store.getDirectAccessUrl(contentUrl, expiresAt);
+ directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
}
catch (UnsupportedOperationException e)
{
@@ -389,7 +409,7 @@ public class AggregatingContentStore extends AbstractContentStore
public boolean isStorageClassesSupported(StorageClassSet storageClassesSet)
{
// We only need to provide info about the primary store,
- // because the aggregating CS only allows to be written in the primary
+ // because the aggregating CS only allows to be written in the primary
return primaryStore.isStorageClassesSupported(storageClassesSet);
}
@@ -463,7 +483,7 @@ public class AggregatingContentStore extends AbstractContentStore
// The content URL was not supported
throw new UnsupportedContentUrlException(this, contentUrl);
}
-
+
return storageClassesSet;
}
finally
@@ -479,7 +499,7 @@ public class AggregatingContentStore extends AbstractContentStore
// because the aggregating CS only allows to be written in the primary
return primaryStore.getStorageClassesTransitions();
}
-
+
@Override
public Map> findStorageClassesTransitions(String contentUrl)
{
diff --git a/repository/src/main/java/org/alfresco/repo/content/transform/LocalCombinedConfig.java b/repository/src/main/java/org/alfresco/repo/content/transform/LocalCombinedConfig.java
deleted file mode 100644
index f8a331acef..0000000000
--- a/repository/src/main/java/org/alfresco/repo/content/transform/LocalCombinedConfig.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2019 - 2021 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.repo.content.transform;
-
-import org.alfresco.transform.client.model.config.Transformer;
-import org.alfresco.transform.client.registry.CombinedConfig;
-import org.alfresco.transform.client.registry.TransformServiceRegistryImpl;
-import org.apache.commons.logging.Log;
-
-import java.util.List;
-
-import static org.alfresco.repo.content.transform.LocalTransformServiceRegistry.LOCAL_TRANSFORMER;
-import static org.alfresco.repo.content.transform.LocalTransformServiceRegistry.URL;
-import static org.alfresco.util.EqualsHelper.nullSafeEquals;
-
-/**
- * Extends the standard CombinedConfig to add in removal of overridden or invalid transforms.
- *
- * @author adavis
- */
-public class LocalCombinedConfig extends CombinedConfig
-{
- public LocalCombinedConfig(Log log)
- {
- super(log);
- }
-
- /**
- * Discards a transformer that is invalid (e.g. T-Engines with the same name, baseUrl has not been specified on a
- * T-Engine transform) or overridden an earlier transform with the same name). If the overridden transform is from
- * a T-Engine and the overriding transform is not a pipeline or a failover, we also copy the {@code baseUrl} from
- * the overridden transform so that the original T-Engine will still be called.
- *
- * @param i the current transform's index into combinedTransformers.
- * @param combinedTransformers the full list of transformers in the order they were read.
- * @param registry that wil hold the transforms.
- * @param transformAndItsOrigin the current combinedTransformers element.
- * @param transformer the current transformer.
- * @param name the current transformer's name.
- * @param readFrom where the current transformer was read from.
- * @param isPipeline if the current transform is a pipeline.
- * @param isFailover if the current transform is a failover.
- *
- * @returns the index of a transform to be removed. {@code -1} is returned if there should not be a remove.
- * @throws IllegalArgumentException if the current transform has a problem and should be removed.
- */
- @Override
- protected int removeInvalidTransformer(int i, List combinedTransformers,
- TransformServiceRegistryImpl registry,
- TransformAndItsOrigin transformAndItsOrigin, Transformer transformer,
- String name, String readFrom, boolean isPipeline, boolean isFailover)
- {
- int indexToRemove = -1;
-
- if (name == null || "".equals(name.trim()))
- {
- throw new IllegalArgumentException("Local transformer names may not be null. Read from " + readFrom);
- }
-
- // Get the baseUrl - test code might change it
- String baseUrl = transformAndItsOrigin.getBaseUrl();
- String testBaseUrl = ((LocalTransformServiceRegistry)registry).getBaseUrlIfTesting(name, baseUrl);
- if (!nullSafeEquals(baseUrl, testBaseUrl))
- {
- baseUrl = testBaseUrl;
- transformAndItsOrigin = new TransformAndItsOrigin(transformer, baseUrl, readFrom);
- combinedTransformers.set(i, transformAndItsOrigin);
- }
-
- boolean isOneStepTransform = !isPipeline && !isFailover && !name.equals(LocalPassThroughTransform.NAME);
-
- // Check to see if the name has been used before.
- int j = lastIndexOf(name, combinedTransformers, i);
- if (j >= 0)
- {
- if (baseUrl != null) // If a T-Engine, else it is an override
- {
- throw new IllegalArgumentException("Local T-Engine transformer " + transformerName(name) +
- " must be a unique name. Read from " + readFrom);
- }
-
- if (isOneStepTransform)
- {
- // We need to set the baseUrl of the original transform in the one overriding,
- // so we can talk to its T-Engine
- TransformAndItsOrigin overriddenTransform = combinedTransformers.get(j);
- String overriddenBaseUrl = overriddenTransform.getBaseUrl();
- Transformer overriddenTransformTransform = transformAndItsOrigin.getTransformer();
- TransformAndItsOrigin overridingTransform = new TransformAndItsOrigin(
- overriddenTransformTransform, overriddenBaseUrl, readFrom);
- combinedTransformers.set(i, overridingTransform);
- }
- indexToRemove = j;
- }
- else if (isOneStepTransform && baseUrl == null)
- {
- throw new IllegalArgumentException("Local T-Engine transformer " + transformerName(name) +
- " must have its baseUrl set in " + LOCAL_TRANSFORMER + name + URL + " Read from " +
- readFrom);
- }
- return indexToRemove;
- }
-
- private static int lastIndexOf(String name, List combinedTransformers, int toIndex)
- {
- // Lists are short (< 100) entries and this is not a frequent or time critical step, so walking the list
- // should be okay.
- for (int j = toIndex-1; j >=0; j--)
- {
- TransformAndItsOrigin transformAndItsOrigin = combinedTransformers.get(j);
- Transformer transformer = transformAndItsOrigin.getTransformer();
- String transformerName = transformer.getTransformerName();
- if (name.equals(transformerName))
- {
- return j;
- }
- }
- return -1;
- }
-}
diff --git a/repository/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java b/repository/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java
index bc4daa6ed7..ff1ab924ae 100644
--- a/repository/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java
+++ b/repository/src/main/java/org/alfresco/repo/content/transform/LocalTransformServiceRegistry.java
@@ -133,7 +133,7 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl
@Override
public boolean readConfig() throws IOException
{
- CombinedConfig combinedConfig = new LocalCombinedConfig(getLog());
+ CombinedConfig combinedConfig = new CombinedConfig(getLog());
List urls = getTEngineUrls();
boolean successReadingConfig = combinedConfig.addRemoteConfig(urls, "T-Engine");
successReadingConfig &= combinedConfig.addLocalConfig("alfresco/transforms");
@@ -315,12 +315,6 @@ public class LocalTransformServiceRegistry extends TransformServiceRegistryImpl
options;
}
- // When testing, we need to be able to set the baseUrl when reading from a file.
- public String getBaseUrlIfTesting(String name, String baseUrl)
- {
- return baseUrl;
- }
-
@Override
protected Log getLog()
{
diff --git a/repository/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java b/repository/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java
index 8837f3648e..d09833b74e 100644
--- a/repository/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java
+++ b/repository/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java
@@ -27,7 +27,6 @@ package org.alfresco.repo.event2;
import java.util.ArrayDeque;
import java.util.Deque;
-
import org.alfresco.repo.event.v1.model.ChildAssociationResource;
import org.alfresco.repo.event.v1.model.DataAttributes;
import org.alfresco.repo.event.v1.model.EventData;
@@ -117,8 +116,9 @@ public class ChildAssociationEventConsolidator implements ChildAssociationEventS
{
String parentId = childAssociationRef.getParentRef().getId();
String childId = childAssociationRef.getChildRef().getId();
+ String assocQName = helper.getQNamePrefixString(childAssociationRef.getQName());
String assocType = helper.getQNamePrefixString(childAssociationRef.getTypeQName());
- return new ChildAssociationResource(parentId, childId, assocType);
+ return new ChildAssociationResource(parentId, childId, assocType, assocQName);
}
/**
diff --git a/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java b/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
index 7ce7e40d21..9212f2a1b8 100644
--- a/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
+++ b/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
@@ -46,6 +46,7 @@ import org.alfresco.repo.event2.filter.NodePropertyFilter;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -148,6 +149,7 @@ public class NodeResourceHelper implements InitializingBean
.setModifiedByUser(getUserInfo((String) properties.get(ContentModel.PROP_MODIFIER), mapUserCache))
.setModifiedAt(getZonedDateTime((Date)properties.get(ContentModel.PROP_MODIFIED)))
.setContent(getContentInfo(properties))
+ .setPrimaryAssocQName(getPrimaryAssocQName(nodeRef))
.setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false))
.setProperties(mapToNodeProperties(properties))
.setAspectNames(getMappedAspects(nodeRef));
@@ -158,6 +160,23 @@ public class NodeResourceHelper implements InitializingBean
return dictionaryService.isSubClass(className, ofClassQName);
}
+ private String getPrimaryAssocQName(NodeRef nodeRef)
+ {
+ String result = null;
+ try
+ {
+ ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef);
+ if(primaryParent != null && primaryParent.getQName() != null)
+ {
+ result = primaryParent.getQName().getPrefixedQName(namespaceService).getPrefixString();
+ }
+ } catch (NamespaceException namespaceException)
+ {
+ LOGGER.error("Cannot return a valid primary association QName: " + namespaceException.getMessage());
+ }
+ return result;
+ }
+
private UserInfo getUserInfo(String userName, Map mapUserCache)
{
UserInfo userInfo = mapUserCache.get(userName);
diff --git a/repository/src/main/java/org/alfresco/repo/management/SafeApplicationEventMulticaster.java b/repository/src/main/java/org/alfresco/repo/management/SafeApplicationEventMulticaster.java
index baae82464e..cb0fb3588d 100644
--- a/repository/src/main/java/org/alfresco/repo/management/SafeApplicationEventMulticaster.java
+++ b/repository/src/main/java/org/alfresco/repo/management/SafeApplicationEventMulticaster.java
@@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
+import java.util.function.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -152,6 +153,16 @@ public class SafeApplicationEventMulticaster implements ApplicationEventMulticas
}
}
+ @Override
+ public void removeApplicationListeners(Predicate> predicate)
+ {
+ }
+
+ @Override
+ public void removeApplicationListenerBeans(Predicate predicate)
+ {
+ }
+
public void removeAllListeners()
{
synchronized (this.defaultRetriever)
diff --git a/repository/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java b/repository/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java
index 281b76adf3..60388f9be4 100644
--- a/repository/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java
+++ b/repository/src/main/java/org/alfresco/repo/rendition2/LocalSynchronousTransformClient.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2019 Alfresco Software Limited
+ * Copyright (C) 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -120,21 +120,21 @@ public class LocalSynchronousTransformClient implements SynchronousTransformClie
if (logger.isDebugEnabled())
{
- logger.debug(TRANSFORM + " requested " + renditionName);
+ logger.debug(TRANSFORM + "requested " + renditionName);
}
transform.transform(reader, writer, actualOptions, renditionName, sourceNodeRef);
if (logger.isDebugEnabled())
{
- logger.debug(TRANSFORM + " created " + renditionName);
+ logger.debug(TRANSFORM + "created " + renditionName);
}
}
catch (Exception e)
{
if (logger.isDebugEnabled())
{
- logger.debug(TRANSFORM + " failed " + renditionName, e);
+ logger.debug(TRANSFORM + "failed " + renditionName, e);
}
throw e;
}
diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java b/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java
index fd98d9bc77..abb7c40efe 100644
--- a/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java
+++ b/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java
@@ -1,28 +1,28 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2005 - 2016 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%
- */
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 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.repo.search.impl.querymodel.impl.db;
import java.util.ArrayList;
@@ -74,6 +74,10 @@ public class DBQuery extends BaseQuery implements DBQueryBuilderComponent
Set selectorGroup;
+ private int limit = 0;
+
+ private int offset = 0;
+
/**
* @param source Source
* @param constraint Constraint
@@ -133,6 +137,22 @@ public class DBQuery extends BaseQuery implements DBQueryBuilderComponent
this.sinceTxId = sinceTxId;
}
+ public int getLimit() {
+ return limit;
+ }
+
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
public List getJoins()
{
HashMap singleJoins = new HashMap();
diff --git a/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java b/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java
index 612f958a65..96ca208c73 100644
--- a/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java
+++ b/repository/src/main/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java
@@ -76,8 +76,10 @@ 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.apache.ibatis.executor.result.DefaultResultContext;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
/**
@@ -89,7 +91,11 @@ public class DBQueryEngine implements QueryEngine
protected static final Log logger = LogFactory.getLog(DBQueryEngine.class);
protected static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
-
+
+ private static final int DEFAULT_MIN_PAGING_BATCH_SIZE = 2500;
+
+ private static final int DEFAULT_MAX_PAGING_BATCH_SIZE = 10000;
+
protected SqlSessionTemplate template;
protected QNameDAO qnameDAO;
@@ -114,6 +120,12 @@ public class DBQueryEngine implements QueryEngine
private boolean maxPermissionCheckEnabled;
+ private boolean usePagingQuery = false;
+
+ private int minPagingBatchSize = DEFAULT_MIN_PAGING_BATCH_SIZE;
+
+ private int maxPagingBatchSize = DEFAULT_MAX_PAGING_BATCH_SIZE;
+
protected EntityLookupCache nodesCache;
private List> stores;
@@ -149,7 +161,31 @@ public class DBQueryEngine implements QueryEngine
{
this.permissionService = permissionService;
}
-
+
+ public boolean isUsePagingQuery() {
+ return usePagingQuery;
+ }
+
+ public void setUsePagingQuery(boolean usePagingQuery) {
+ this.usePagingQuery = usePagingQuery;
+ }
+
+ public int getMinPagingBatchSize() {
+ return minPagingBatchSize;
+ }
+
+ public void setMinPagingBatchSize(int minPagingBatchSize) {
+ this.minPagingBatchSize = minPagingBatchSize;
+ }
+
+ public int getMaxPagingBatchSize() {
+ return maxPagingBatchSize;
+ }
+
+ public void setMaxPagingBatchSize(int maxPagingBatchSize) {
+ this.maxPagingBatchSize = maxPagingBatchSize;
+ }
+
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
{
this.metadataIndexCheck2 = metadataIndexCheck2;
@@ -331,7 +367,7 @@ public class DBQueryEngine implements QueryEngine
int requiredNodes = computeRequiredNodesCount(options);
logger.debug("- query sent to the database");
- template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler()
+ performTmdqSelect(pickQueryTemplate(options, dbQuery), dbQuery, requiredNodes, new ResultHandler()
{
@Override
public void handleResult(ResultContext extends Node> context)
@@ -399,6 +435,54 @@ public class DBQueryEngine implements QueryEngine
return frs;
}
+ private void performTmdqSelect(String statement, DBQuery dbQuery, int requiredNodes, ResultHandler handler)
+ {
+ if (usePagingQuery)
+ {
+ performTmdqSelectPaging(statement, dbQuery, requiredNodes, handler);
+ }
+ else
+ {
+ performTmdqSelectStreaming(statement, dbQuery, handler);
+ }
+ }
+
+ private void performTmdqSelectStreaming(String statement, DBQuery dbQuery, ResultHandler handler)
+ {
+ template.select(statement, dbQuery, handler);
+ }
+
+ private void performTmdqSelectPaging(String statement, DBQuery dbQuery, int requiredNodes, ResultHandler handler)
+ {
+ int batchStart = 0;
+ int batchSize = requiredNodes * 2;
+ batchSize = Math.min(Math.max(batchSize, minPagingBatchSize), maxPagingBatchSize);
+ DefaultResultContext resultCtx = new DefaultResultContext<>();
+ while (!resultCtx.isStopped())
+ {
+ dbQuery.setOffset(batchStart);
+ dbQuery.setLimit(batchSize);
+ List batch = template.selectList(statement, dbQuery);
+ for (Node node : batch)
+ {
+ resultCtx.nextResultObject(node);
+ handler.handleResult(resultCtx);
+ if (resultCtx.isStopped())
+ {
+ break;
+ }
+ }
+ if (batch.size() < batchSize)
+ {
+ resultCtx.stop();
+ }
+ else
+ {
+ batchStart += batchSize;
+ }
+ }
+ }
+
private DBResultSet createResultSet(QueryOptions options, List nodes, int numberFound)
{
DBResultSet dbResultSet = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
diff --git a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java
index 84bafac0a8..0d4ecf8e2a 100644
--- a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java
@@ -1,61 +1,61 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2005 - 2016 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%
- */
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 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.repo.security.authority;
-import java.util.AbstractSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.alfresco.model.ContentModel;
-import org.alfresco.query.PagingRequest;
-import org.alfresco.query.PagingResults;
-import org.alfresco.repo.policy.ClassPolicyDelegate;
-import org.alfresco.repo.policy.PolicyComponent;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityAddedToGroup;
-import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityRemovedFromGroup;
-import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnGroupDeleted;
-import org.alfresco.repo.security.permissions.PermissionServiceSPI;
-import org.alfresco.repo.security.person.UserNameMatcher;
-import org.alfresco.repo.tenant.TenantService;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.AuthenticationService;
-import org.alfresco.service.cmr.security.AuthorityService;
-import org.alfresco.service.cmr.security.AuthorityType;
-import org.alfresco.service.cmr.security.PermissionService;
-import org.alfresco.service.cmr.security.PersonService;
-import org.alfresco.util.Pair;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.extensions.surf.util.ParameterCheck;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.query.PagingRequest;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.policy.ClassPolicyDelegate;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityAddedToGroup;
+import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityRemovedFromGroup;
+import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnGroupDeleted;
+import org.alfresco.repo.security.permissions.PermissionServiceSPI;
+import org.alfresco.repo.security.person.UserNameMatcher;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.cmr.security.AuthorityService;
+import org.alfresco.service.cmr.security.AuthorityType;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.util.Pair;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.extensions.surf.util.ParameterCheck;
/**
* The default implementation of the authority service.
@@ -64,6 +64,8 @@ import org.springframework.extensions.surf.util.ParameterCheck;
*/
public class AuthorityServiceImpl implements AuthorityService, InitializingBean
{
+ public static final String GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX + "ALFRESCO_SYSTEM_ADMINISTRATORS";
+
private static Set DEFAULT_ZONES = new HashSet();
static
@@ -83,11 +85,11 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
private Set guestSet = Collections.singleton(PermissionService.GUEST_AUTHORITY);
private Set allSet = Collections.singleton(PermissionService.ALL_AUTHORITIES);
private Set adminGroups = Collections.emptySet();
- private Set guestGroups = Collections.emptySet();
-
- private ClassPolicyDelegate onAuthorityAddedToGroups;
- private ClassPolicyDelegate onAuthorityRemovedFromGroup;
- private ClassPolicyDelegate onGroupDeletedDelegate;
+ private Set guestGroups = Collections.emptySet();
+
+ private ClassPolicyDelegate onAuthorityAddedToGroups;
+ private ClassPolicyDelegate onAuthorityRemovedFromGroup;
+ private ClassPolicyDelegate onGroupDeletedDelegate;
private PolicyComponent policyComponent;
public AuthorityServiceImpl()
@@ -133,18 +135,18 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
public void setGuestGroups(Set guestGroups)
{
this.guestGroups = guestGroups;
- }
-
- public void setPolicyComponent(PolicyComponent policyComponent)
- {
- this.policyComponent = policyComponent;
- }
-
- public void init()
- {
- onAuthorityAddedToGroups = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityAddedToGroup.class);
- onAuthorityRemovedFromGroup = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityRemovedFromGroup.class);
- onGroupDeletedDelegate = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnGroupDeleted.class);
+ }
+
+ public void setPolicyComponent(PolicyComponent policyComponent)
+ {
+ this.policyComponent = policyComponent;
+ }
+
+ public void init()
+ {
+ onAuthorityAddedToGroups = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityAddedToGroup.class);
+ onAuthorityRemovedFromGroup = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityRemovedFromGroup.class);
+ onGroupDeletedDelegate = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnGroupDeleted.class);
}
@Override
@@ -482,12 +484,12 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
*/
public void addAuthority(Collection parentNames, String childName)
{
- authorityDAO.addAuthority(parentNames, childName);
-
- OnAuthorityAddedToGroup policy = onAuthorityAddedToGroups.get(ContentModel.TYPE_AUTHORITY);
- for (String parentGroup : parentNames)
- {
- policy.onAuthorityAddedToGroup(parentGroup, childName);
+ authorityDAO.addAuthority(parentNames, childName);
+
+ OnAuthorityAddedToGroup policy = onAuthorityAddedToGroups.get(ContentModel.TYPE_AUTHORITY);
+ for (String parentGroup : parentNames)
+ {
+ policy.onAuthorityAddedToGroup(parentGroup, childName);
}
}
@@ -565,18 +567,18 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
}
}
authorityDAO.deleteAuthority(name);
- permissionServiceSPI.deletePermissions(name);
-
- if (isGroup(type))
- {
- OnGroupDeleted onGroupDelete = onGroupDeletedDelegate.get(ContentModel.TYPE_AUTHORITY);
- onGroupDelete.onGroupDeleted(name, cascade);
+ permissionServiceSPI.deletePermissions(name);
+
+ if (isGroup(type))
+ {
+ OnGroupDeleted onGroupDelete = onGroupDeletedDelegate.get(ContentModel.TYPE_AUTHORITY);
+ onGroupDelete.onGroupDeleted(name, cascade);
}
- }
-
- private boolean isGroup(AuthorityType authorityType)
- {
- return AuthorityType.GROUP == authorityType || AuthorityType.EVERYONE == authorityType;
+ }
+
+ private boolean isGroup(AuthorityType authorityType)
+ {
+ return AuthorityType.GROUP == authorityType || AuthorityType.EVERYONE == authorityType;
}
/**
@@ -622,9 +624,9 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
@Override
public void removeAuthority(String parentName, String childName)
{
- authorityDAO.removeAuthority(parentName, childName);
-
- OnAuthorityRemovedFromGroup policy = onAuthorityRemovedFromGroup.get(ContentModel.TYPE_AUTHORITY);
+ authorityDAO.removeAuthority(parentName, childName);
+
+ OnAuthorityRemovedFromGroup policy = onAuthorityRemovedFromGroup.get(ContentModel.TYPE_AUTHORITY);
policy.onAuthorityRemovedFromGroup(parentName, childName);
}
@@ -770,6 +772,16 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean
return authorityDAO.getShortName(name);
}
+ @Override
+ public boolean hasSysAdminAuthority()
+ {
+ final String currentUserName = AuthenticationUtil.getRunAsUser();
+ if (currentUserName == null)
+ {
+ return false;
+ }
+ return getAuthoritiesForUser(currentUserName).contains(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY);
+ }
/**
* Lazy load set of authorities. Try not to iterate or ask for the size. Needed for the case where there
diff --git a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java
index a2edb0249f..80fb2fc9b9 100644
--- a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java
+++ b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2019 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,7 @@
*/
package org.alfresco.service.cmr.repository;
+
import java.util.Collections;
import java.util.Date;
import java.util.Map;
@@ -183,7 +184,7 @@ public interface ContentService
*/
@Auditable(parameters = {"nodeRef", "propertyQName", "update", "storageClasses"})
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update,
- StorageClassSet storageClassSet) throws InvalidNodeRefException, InvalidTypeException;
+ StorageClassSet storageClassSet) throws InvalidNodeRefException, InvalidTypeException;
/**
* Gets a writer to a temporary location. The longevity of the stored
@@ -195,32 +196,46 @@ public interface ContentService
public ContentWriter getTempWriter();
/**
- * Gets a presigned URL to directly access a binary content. It is up to the
- * content store if it can fulfil this request with an expiry time (in
- * milliseconds) or not.
+ * Checks if the system and at least one store supports the retrieving of direct access URLs.
*
- * @param nodeRef
- * a reference to a node having a content property
- * @param expiresAt
- * an optional expiry date, so the direct access url would become
- * invalid when the expiry date is reached
- * @return A direct access URL object for a binary content or returns null if not supported
- * @throws IllegalArgumentException if there is no binary content for the node
+ * @return {@code true} if direct access URLs retrieving is supported, {@code false} otherwise
*/
- @Auditable(parameters = {"nodeRef", "expiresAt"})
- public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt);
+ boolean isContentDirectUrlEnabled();
/**
- * Checks whether or not the current {@link ContentService} supports the provided {@link Set} storage classes
+ * Checks if the system and store supports the retrieving of a direct access {@code URL} for the given node.
*
- * @param storageClassSet The storage classes that will be checked whether or not are supported
- * @return true if the storage classes are supported, false otherwise.
+ * @return {@code true} if direct access URLs retrieving is supported for the node, {@code false} otherwise
*/
- default boolean isStorageClassesSupported(StorageClassSet storageClassSet)
+ boolean isContentDirectUrlEnabled(NodeRef nodeRef);
+
+ /**
+ * Gets a presigned URL to directly access the content. It is up to the actual store
+ * implementation if it can fulfil this request with an expiry time or not.
+ *
+ * @param nodeRef Node ref for which to obtain the direct access {@code URL}.
+ * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}.
+ * @return A direct access {@code URL} object for the content.
+ * @throws UnsupportedOperationException if the store is unable to provide the information.
+ */
+ default DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment)
{
- return false;
+ return requestContentDirectUrl(nodeRef, attachment, null);
}
+ /**
+ * Gets a presigned URL to directly access the content. It is up to the actual store
+ * implementation if it can fulfil this request with an expiry time or not.
+ *
+ * @param nodeRef Node ref for which to obtain the direct access {@code URL}.
+ * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}.
+ * @param validFor The time at which the direct access {@code URL} will expire.
+ * @return A direct access {@code URL} object for the content.
+ * @throws UnsupportedOperationException if the store is unable to provide the information.
+ */
+ @Auditable(parameters = {"nodeRef", "validFor"})
+ DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor);
+
/**
* @return Returns the complete {@link Set} of supported storage classes by this {@link ContentService}
*/
diff --git a/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java b/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java
index 6d6964b62d..5094302539 100644
--- a/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java
+++ b/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java
@@ -1,28 +1,28 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * Copyright (C) 2005 - 2016 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%
- */
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 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.service.cmr.security;
import java.util.Collection;
@@ -511,4 +511,19 @@ public interface AuthorityService
*/
@Auditable(parameters = {"type"})
public Set findAuthorities(AuthorityType type, String parentAuthority, boolean immediate, String displayNamePattern, String zoneName);
-}
+
+ /**
+ * Check the current user has system administration authority.
+ *
+ * @return true if the currently authenticated user has the system administration authority, otherwise false
+ * @throws UnsupportedOperationException if the implementing class (i.e. external clients) doesn't provide an implementation for the {@code hasSysAdminAuthority} operation
+ *
+ * @since 7.1
+ */
+ @Auditable
+ // See PRODMAN-493 -> REPO-5659
+ default boolean hasSysAdminAuthority()
+ {
+ throw new UnsupportedOperationException("hasSysAdminAuthority");
+ }
+}
diff --git a/repository/src/main/java/org/alfresco/transform/client/registry/CombinedConfig.java b/repository/src/main/java/org/alfresco/transform/client/registry/CombinedConfig.java
index 8234ad14d6..1b208c5f61 100644
--- a/repository/src/main/java/org/alfresco/transform/client/registry/CombinedConfig.java
+++ b/repository/src/main/java/org/alfresco/transform/client/registry/CombinedConfig.java
@@ -30,10 +30,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.transform.LocalPassThroughTransform;
import org.alfresco.service.cmr.repository.MimetypeService;
-import org.alfresco.transform.client.model.config.SupportedSourceAndTarget;
import org.alfresco.transform.client.model.config.TransformConfig;
-import org.alfresco.transform.client.model.config.TransformOption;
-import org.alfresco.transform.client.model.config.TransformStep;
import org.alfresco.transform.client.model.config.Transformer;
import org.alfresco.util.ConfigFileFinder;
import org.apache.commons.logging.Log;
@@ -47,58 +44,22 @@ import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static org.alfresco.repo.content.metadata.AsynchronousExtractor.isMetadataEmbedMimetype;
-import static org.alfresco.repo.content.metadata.AsynchronousExtractor.isMetadataExtractMimetype;
/**
- * This class reads multiple T-Engine config and local files and registers them all with a registry as if they were all
+ * This class reads multiple T-Engine config and local files and registers as if they were all
* in one file. Transform options are shared between all sources.
*
- * The caller should make calls to {@link #addRemoteConfig(List, String)} and {@link #addLocalConfig(String)} followed
- * by a call to {@link #register(TransformServiceRegistryImpl)}.
+ * The caller should make calls to {@link #addRemoteConfig(List, String)}, {@link #addLocalConfig(String)} or
+ * {@link #addTransformConfig(TransformConfig, String, String)} followed by a call to
+ * {@link #register(TransformServiceRegistryImpl)}.
*
* @author adavis
*/
-public class CombinedConfig
+public class CombinedConfig extends CombinedTransformConfig
{
private final Log log;
- public static class TransformAndItsOrigin
- {
- final Transformer transformer;
- final String baseUrl;
- final String readFrom;
-
- public TransformAndItsOrigin(Transformer transformer, String baseUrl, String readFrom)
- {
- this.transformer = transformer;
- this.baseUrl = baseUrl;
- this.readFrom = readFrom;
- }
-
- public Transformer getTransformer()
- {
- return transformer;
- }
-
- public String getBaseUrl()
- {
- return baseUrl;
- }
- }
-
- Map> combinedTransformOptions = new HashMap<>();
- List combinedTransformers = new ArrayList<>();
-
private ObjectMapper jsonObjectMapper = new ObjectMapper();
private ConfigFileFinder configFileFinder;
private int tEngineCount;
@@ -113,9 +74,7 @@ public class CombinedConfig
protected void readJson(JsonNode jsonNode, String readFrom, String baseUrl)
{
TransformConfig transformConfig = jsonObjectMapper.convertValue(jsonNode, TransformConfig.class);
- transformConfig.getTransformOptions().forEach((key, map) -> combinedTransformOptions.put(key, map));
- transformConfig.getTransformers().forEach(transformer -> combinedTransformers.add(
- new TransformAndItsOrigin(transformer, baseUrl, readFrom)));
+ addTransformConfig(transformConfig, readFrom, baseUrl);
}
};
}
@@ -255,6 +214,12 @@ public class CombinedConfig
return message;
}
+ @Override
+ protected boolean isPassThroughTransformName(String name)
+ {
+ return name.equals(LocalPassThroughTransform.NAME);
+ }
+
/**
* Adds a PassThrough transform where the source and target mimetypes are identical, or transforms to "text/plain"
* from selected text based types.
@@ -273,277 +238,7 @@ public class CombinedConfig
data.setTEngineCount(tEngineCount);
data.setFileCount(configFileFinder.getFileCount());
- combinedTransformers = removeInvalidTransformers(combinedTransformers, registry);
- combinedTransformers = sortTransformers(combinedTransformers, registry);
- addWildcardSupportedSourceAndTarget(combinedTransformers);
-
- combinedTransformers.forEach(transformer ->
- registry.register(transformer.transformer, combinedTransformOptions,
- transformer.baseUrl, transformer.readFrom));
- }
-
- /**
- * Discards transformers that are invalid (e.g. transformers that have both pipeline and failover sections). Calls
- * {@link #removeInvalidTransformer(int, List, TransformServiceRegistryImpl, TransformAndItsOrigin, Transformer,
- * String, String, boolean, boolean)} for each transform, so that subclasses (LocalCombinedConfig), may also
- * discard invalid transforms or overridden transforms.
- *
- * @param combinedTransformers the full list of transformers in the order they were read.
- * @param registry that wil hold the transforms.
- */
- private List removeInvalidTransformers(List combinedTransformers,
- TransformServiceRegistryImpl registry)
- {
- for (int i=0; i pipeline = transformer.getTransformerPipeline();
- List failover = transformer.getTransformerFailover();
- boolean isPipeline = pipeline != null && !pipeline.isEmpty();
- boolean isFailover = failover != null && !failover.isEmpty();
-
- if (isPipeline && isFailover)
- {
- throw new IllegalArgumentException("Transformer " + transformerName(name) +
- " cannot have pipeline and failover sections. Read from " + readFrom);
- }
-
- // Local transforms may override each other or be invalid
- int indexToRemove = removeInvalidTransformer(i, combinedTransformers, registry, transformAndItsOrigin,
- transformer, name, readFrom, isPipeline, isFailover);
-
- // If required remove the requested transform
- if (indexToRemove >= 0)
- {
- combinedTransformers.remove(indexToRemove);
- // this may also require the current index i to be changed so we don't skip one.
- if (i >= indexToRemove)
- {
- i--;
- }
- }
- }
- catch (IllegalArgumentException e)
- {
- String msg = e.getMessage();
- registry.logError(msg);
- combinedTransformers.remove(i--);
- }
- }
- return combinedTransformers;
- }
-
- protected int removeInvalidTransformer(int i, List combinedTransformers,
- TransformServiceRegistryImpl registry,
- TransformAndItsOrigin transformAndItsOrigin, Transformer transformer,
- String name, String readFrom, boolean isPipeline, boolean isFailover)
- {
- return -1;
- }
-
- protected static String transformerName(String name)
- {
- return name == null ? " without a name" : "\"" + name + "\"";
- }
-
- // Sort transformers so there are no forward references, if that is possible.
- private List sortTransformers(List original, TransformServiceRegistryImpl registry)
- {
- List transformers = new ArrayList<>(original.size());
- List todo = new ArrayList<>(original.size());
- Set transformerNames = new HashSet<>();
- boolean added;
- do
- {
- added = false;
- for (TransformAndItsOrigin entry : original)
- {
- String name = entry.transformer.getTransformerName();
- List pipeline = entry.transformer.getTransformerPipeline();
- Set referencedTransformerNames = new HashSet<>();
- boolean addEntry = true;
- if (pipeline != null)
- {
- for (TransformStep step : pipeline)
- {
- String stepName = step.getTransformerName();
- referencedTransformerNames.add(stepName);
- }
- }
- List failover = entry.transformer.getTransformerFailover();
- if (failover != null)
- {
- referencedTransformerNames.addAll(failover);
- }
-
- for (String referencedTransformerName : referencedTransformerNames)
- {
- if (!transformerNames.contains(referencedTransformerName))
- {
- todo.add(entry);
- addEntry = false;
- break;
- }
- }
-
- if (addEntry)
- {
- transformers.add(entry);
- added = true;
- if (name != null)
- {
- transformerNames.add(name);
- }
- }
- }
- original.clear();
- original.addAll(todo);
- todo.clear();
- }
- while (added && !original.isEmpty());
-
- transformers.addAll(todo);
-
- for (TransformAndItsOrigin transformAndItsOrigin : original)
- {
- String name = transformAndItsOrigin.getTransformer().getTransformerName();
- registry.logError("Transformer " + transformerName(name) +
- " ignored as step transforms do not exist. Read from " + transformAndItsOrigin.readFrom);
- }
-
- return transformers;
- }
-
- private void addWildcardSupportedSourceAndTarget(List combinedTransformers)
- {
- Map transformers = new HashMap<>();
- combinedTransformers.forEach(ct -> transformers.put(ct.transformer.getTransformerName(), ct.transformer));
-
- combinedTransformers.forEach(transformAndItsOrigin ->
- {
- Transformer transformer = transformAndItsOrigin.transformer;
-
- // If there are no SupportedSourceAndTarget, then work out all the wildcard combinations.
- if (transformer.getSupportedSourceAndTargetList().isEmpty())
- {
- List pipeline = transformer.getTransformerPipeline();
- List failover = transformer.getTransformerFailover();
- boolean isPipeline = pipeline != null && !pipeline.isEmpty();
- boolean isFailover = failover != null && !failover.isEmpty();
- if (isFailover)
- {
- // Copy all SupportedSourceAndTarget values from each step transformer
- Set supportedSourceAndTargets = failover.stream().flatMap(
- name -> transformers.get(name).getSupportedSourceAndTargetList().stream()).
- collect(Collectors.toSet());
- transformer.setSupportedSourceAndTargetList(supportedSourceAndTargets);
- }
- else if (isPipeline)
- {
- // Build up SupportedSourceAndTarget values. The list of source types and max sizes will come from the
- // initial step transformer that have a target mimetype that matches the first intermediate mimetype.
- // We then step through all intermediate transformers checking the next intermediate type is supported.
- // When we get to the last step transformer, it provides all the target mimetypes based on the previous
- // intermediate mimeype. Any combinations supported by the first transformer are excluded.
- boolean first = true;
- String sourceMediaType = null;
- Set sourceMediaTypesAndMaxSizes = null;
- Set firstTransformOptions = null;
- for (TransformStep step : pipeline)
- {
- String name = step.getTransformerName();
- Transformer stepTransformer = transformers.get(name);
- if (stepTransformer == null)
- {
- break;
- }
-
- String stepTrg = step.getTargetMediaType();
- if (first)
- {
- first = false;
- sourceMediaTypesAndMaxSizes = stepTransformer.getSupportedSourceAndTargetList().stream().
- filter(s -> stepTrg.equals(s.getTargetMediaType())).
- collect(Collectors.toSet());
- sourceMediaType = stepTrg;
- firstTransformOptions = stepTransformer.getTransformOptions();
- }
- else
- {
- final String src = sourceMediaType;
- if (stepTrg == null) // if final step
- {
- // Create a cartesian product of sourceMediaType,MaxSourceSize and TargetMediaType where
- // the source matches the last intermediate.
- Set supportedSourceAndTargets = sourceMediaTypesAndMaxSizes.stream().
- flatMap(s -> stepTransformer.getSupportedSourceAndTargetList().stream().
- filter(st ->
- {
- String targetMimetype = st.getTargetMediaType();
- return st.getSourceMediaType().equals(src) &&
- !(isMetadataExtractMimetype(targetMimetype) ||
- isMetadataEmbedMimetype(targetMimetype));
- }).
- map(t -> t.getTargetMediaType()).
- map(trg -> SupportedSourceAndTarget.builder().
- withSourceMediaType(s.getSourceMediaType()).
- withMaxSourceSizeBytes(s.getMaxSourceSizeBytes()).
- withPriority(s.getPriority()).
- withTargetMediaType(trg).build())).
- collect(Collectors.toSet());
-
- // Exclude duplicates with the first transformer, if it has the same options.
- // There is no point doing more work.
- Set transformOptions = transformer.getTransformOptions();
- if (sameOptions(transformOptions, firstTransformOptions))
- {
- supportedSourceAndTargets.removeAll(sourceMediaTypesAndMaxSizes);
- }
-
- transformer.setSupportedSourceAndTargetList(supportedSourceAndTargets);
- }
- else // if intermediate step
- {
- // Check source to target is supported (it normally is)
- if (!stepTransformer.getSupportedSourceAndTargetList().stream().
- anyMatch(st -> st.getSourceMediaType().equals(src) &&
- st.getTargetMediaType().equals(stepTrg)))
- {
- break;
- }
-
- sourceMediaType = stepTrg;
- }
- }
- }
- }
- }
- });
- }
-
- private boolean sameOptions(Set transformOptionNames1, Set transformOptionNames2)
- {
- // They have the same names
- if (transformOptionNames1.equals(transformOptionNames2))
- {
- return true;
- }
-
- // Check the actual options.
- Set transformOptions1 = getTransformOptions(transformOptionNames1);
- Set transformOptions2 = getTransformOptions(transformOptionNames2);
- return transformOptions1.equals(transformOptions2);
- }
-
- private Set getTransformOptions(Set transformOptionNames)
- {
- Set transformOptions = new HashSet<>();
- transformOptionNames.forEach(name->transformOptions.addAll(combinedTransformOptions.get(name)));
- return transformOptions;
+ combineTransformerConfig(registry);
+ registerCombinedTransformers(registry);
}
}
diff --git a/repository/src/main/java/org/alfresco/transform/client/registry/TransformServiceRegistryImpl.java b/repository/src/main/java/org/alfresco/transform/client/registry/TransformServiceRegistryImpl.java
index 2467a924ff..72cca58777 100644
--- a/repository/src/main/java/org/alfresco/transform/client/registry/TransformServiceRegistryImpl.java
+++ b/repository/src/main/java/org/alfresco/transform/client/registry/TransformServiceRegistryImpl.java
@@ -175,6 +175,12 @@ public abstract class TransformServiceRegistryImpl extends AbstractTransformRegi
getLog().error(msg);
}
+ @Override
+ protected void logWarn(String msg)
+ {
+ getLog().warn(msg);
+ }
+
@Override
public String findTransformerName(final String sourceMimetype, final long sourceSizeInBytes,
final String targetMimetype, final Map actualOptions,
diff --git a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml
index 0e327634ac..50f259bf56 100644
--- a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml
+++ b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml
@@ -63,6 +63,17 @@
GROUP_ALFRESCO_MODEL_ADMINISTRATORS
+
+
+
+
+
+
+ GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS
+ GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS
+ GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS
+
+
@@ -110,6 +121,11 @@
view:pathref="${system.authorities_container.childname}/cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS"
view:childName="cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" />
+
+
+
@@ -146,10 +162,15 @@
view:pathref="${system.authorities_container.childname}/cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS"
view:childName="cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" />
+
+
+
-
\ No newline at end of file
+
diff --git a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml
index 9cdf49e989..b9b2a6cd9e 100644
--- a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml
+++ b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml
@@ -40,6 +40,15 @@
view:childName="cm:${alfresco_user_store.adminusername}" />
+
+
+
+
+
+
+
+
@@ -66,4 +75,4 @@
-
\ No newline at end of file
+
diff --git a/repository/src/main/resources/alfresco/content-services-context.xml b/repository/src/main/resources/alfresco/content-services-context.xml
index aed14cdd9e..dfbdf56633 100644
--- a/repository/src/main/resources/alfresco/content-services-context.xml
+++ b/repository/src/main/resources/alfresco/content-services-context.xml
@@ -1,359 +1,376 @@
-
-
-
-
-
-
- false
-
-
- ContentStore
-
-
-
- ${filecontentstore.subsystem.name}
-
-
-
- manager
-
-
-
-
-
-
-
- true
-
-
- ContentStore
-
-
- unencrypted
-
-
-
- managed
- unencrypted
-
-
-
-
-
-
-
-
-
-
- fileContentStore
-
-
-
- org.alfresco.repo.content.ContentStore
- org.alfresco.repo.content.ContentStoreCaps
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${dir.contentstore.deleted}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${system.content.orphanProtectDays}
-
-
- ${system.content.deletionFailureAction}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${system.content.cleanerBatchSize}
-
-
-
-
-
- ${system.content.eagerOrphanCleanup}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${policy.content.update.ignoreEmpty}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- UTF-8
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- classpath:alfresco/mimetype/mimetype-map.xml
- classpath:alfresco/mimetype/mimetype-map-openoffice.xml
- classpath*:alfresco/module/*/mimetype-map*.xml
- classpath*:alfresco/extension/mimetype/*-map.xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- classpath:alfresco/ml/content-filter-lang.xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- EEE, d MMM yyyy HH:mm:ss Z
- EEE, d MMM yy HH:mm:ss Z
- d MMM yyyy HH:mm:ss Z
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.alfresco.repo.content.transform.TransformerConfigMBean
-
-
-
-
-
-
-
-
-
-
- transformerDebugLog
-
-
-
- org.apache.commons.logging.Log
-
-
-
-
-
-
-
-
-
-
- transformerLog
-
-
-
- org.apache.commons.logging.Log
-
-
-
-
-
-
+
+
+
+
+
+
+ false
+
+
+ ContentStore
+
+
+
+ ${filecontentstore.subsystem.name}
+
+
+
+ manager
+
+
+
+
+
+
+
+ true
+
+
+ ContentStore
+
+
+ unencrypted
+
+
+
+ managed
+ unencrypted
+
+
+
+
+
+
+
+
+
+
+ fileContentStore
+
+
+
+ org.alfresco.repo.content.ContentStore
+ org.alfresco.repo.content.ContentStoreCaps
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${dir.contentstore.deleted}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${system.content.orphanProtectDays}
+
+
+ ${system.content.deletionFailureAction}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${system.content.cleanerBatchSize}
+
+
+
+
+
+ ${system.content.eagerOrphanCleanup}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${policy.content.update.ignoreEmpty}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ classpath:alfresco/mimetype/mimetype-map.xml
+ classpath:alfresco/mimetype/mimetype-map-openoffice.xml
+ classpath*:alfresco/module/*/mimetype-map*.xml
+ classpath*:alfresco/extension/mimetype/*-map.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ classpath:alfresco/ml/content-filter-lang.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EEE, d MMM yyyy HH:mm:ss Z
+ EEE, d MMM yy HH:mm:ss Z
+ d MMM yyyy HH:mm:ss Z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.alfresco.repo.content.transform.TransformerConfigMBean
+
+
+
+
+
+
+
+
+
+
+ transformerDebugLog
+
+
+
+ org.apache.commons.logging.Log
+
+
+
+
+
+
+
+
+
+
+ transformerLog
+
+
+
+ org.apache.commons.logging.Log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repository/src/main/resources/alfresco/dbscripts/upgrade/7.1.0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/remove-alf_server-table.sql b/repository/src/main/resources/alfresco/dbscripts/upgrade/7.1.0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/remove-alf_server-table.sql
index 357afd3339..cb5679598b 100644
--- a/repository/src/main/resources/alfresco/dbscripts/upgrade/7.1.0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/remove-alf_server-table.sql
+++ b/repository/src/main/resources/alfresco/dbscripts/upgrade/7.1.0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/remove-alf_server-table.sql
@@ -12,6 +12,7 @@ ALTER TABLE alf_transaction
DROP COLUMN IF EXISTS server_id;
DROP TABLE IF EXISTS alf_server;
+DROP SEQUENCE IF EXISTS alf_server_seq;
--
-- Record script finish
diff --git a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/metadata-query-common-SqlMap.xml b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/metadata-query-common-SqlMap.xml
index 8230226816..ecc97b3451 100644
--- a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/metadata-query-common-SqlMap.xml
+++ b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/metadata-query-common-SqlMap.xml
@@ -177,7 +177,8 @@
-
-
+
+ limit #{offset}, #{limit}
+
\ No newline at end of file
diff --git a/repository/src/main/resources/alfresco/messages/content-model_da.properties b/repository/src/main/resources/alfresco/messages/content-model_da.properties
index d0cc7d281d..fc0f5bda22 100644
--- a/repository/src/main/resources/alfresco/messages/content-model_da.properties
+++ b/repository/src/main/resources/alfresco/messages/content-model_da.properties
@@ -125,7 +125,7 @@ cm_contentmodel.aspect.cm_auditable.title=Kan overv\u00e5ges
cm_contentmodel.aspect.cm_auditable.description=Kan overv\u00e5ges
cm_contentmodel.property.cm_created.title=Oprettelsesdato
cm_contentmodel.property.cm_created.description=Oprettelsesdato
-cm_contentmodel.property.cm_creator.title=Oprettet af
+cm_contentmodel.property.cm_creator.title=Opretter
cm_contentmodel.property.cm_creator.description=Den person, der oprettede elementet
cm_contentmodel.property.cm_modified.title=\u00c6ndringsdato
cm_contentmodel.property.cm_modified.description=Tidspunkt for, hvorn\u00e5r elementet senest blev \u00e6ndret
diff --git a/repository/src/main/resources/alfresco/messages/iptc-model_da.properties b/repository/src/main/resources/alfresco/messages/iptc-model_da.properties
index 3be0ce09bc..a74ab303f5 100644
--- a/repository/src/main/resources/alfresco/messages/iptc-model_da.properties
+++ b/repository/src/main/resources/alfresco/messages/iptc-model_da.properties
@@ -93,7 +93,7 @@ iptcxmp_iptcmodel.property.Iptc4xmpExt_DigitalSourcefileType.title=Originalfotos
iptcxmp_iptcmodel.property.Iptc4xmpExt_DigitalSourcefileType.description=Kildens digitale filtype.
iptcxmp_iptcmodel.property.Iptc4xmpExt_DigitalSourceType.title=Kildetype til dette foto
iptcxmp_iptcmodel.property.Iptc4xmpExt_DigitalSourceType.description=Kildetype til dette digitale billede
-iptcxmp_iptcmodel.property.Iptc4xmpExt_Event.title=Event
+iptcxmp_iptcmodel.property.Iptc4xmpExt_Event.title=H\u00e6ndelse
iptcxmp_iptcmodel.property.Iptc4xmpExt_Event.description=Navngiver eller beskriver den specifikke begivenhed indholdet relaterer til.
iptcxmp_iptcmodel.property.plus_ImageSupplierID.title=Billedes leverand\u00f8r-ID
iptcxmp_iptcmodel.property.plus_ImageSupplierID.description=Identificerer emnets den nyeste leverand\u00f8r, som ikke n\u00f8dvendigvis er ejer eller opretter.
@@ -125,7 +125,7 @@ iptcxmp_iptcmodel.property.plus_LicensorCity.title=By
iptcxmp_iptcmodel.property.plus_LicensorCity.description=By for en person eller virksomhed, der skal kontaktes for at f\u00e5 en licens til brug af emnet, eller som har licens p\u00e5 emnet.
iptcxmp_iptcmodel.property.plus_LicensorCountry.title=Land
iptcxmp_iptcmodel.property.plus_LicensorCountry.description=Land for en person eller virksomhed, der skal kontaktes for at f\u00e5 en licens til brug af emnet, eller som har licens p\u00e5 emnet.
-iptcxmp_iptcmodel.property.plus_LicensorEmail.title=e-mail
+iptcxmp_iptcmodel.property.plus_LicensorEmail.title=E-mailadresse
iptcxmp_iptcmodel.property.plus_LicensorEmail.description=E-mail for en person eller virksomhed, der skal kontaktes for at f\u00e5 en licens til brug af emnet, eller som har licens p\u00e5 emnet.
iptcxmp_iptcmodel.property.plus_LicensorExtendedAddress.title=Udviddet adresse
iptcxmp_iptcmodel.property.plus_LicensorExtendedAddress.description=Udvidet adresse for en person eller virksomhed, der skal kontaktes for at f\u00e5 en licens til brug af emnet, eller som har licens p\u00e5 emnet.
diff --git a/repository/src/main/resources/alfresco/messages/patch-service.properties b/repository/src/main/resources/alfresco/messages/patch-service.properties
index ef0c0c90c0..51701e3a01 100644
--- a/repository/src/main/resources/alfresco/messages/patch-service.properties
+++ b/repository/src/main/resources/alfresco/messages/patch-service.properties
@@ -406,4 +406,4 @@ patch.db-V6.3-add-indexes-node-transaction.description=Create additional indexes
patch.db-V7.1.0-remove-alf_server-table.description=Removes alf_server table and constraints
-
+patch.alfrescoSystemAdministrators.description=Adds the 'GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS' group
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_de.properties b/repository/src/main/resources/alfresco/messages/system-messages_de.properties
index 46705571d4..dc2ee67561 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_de.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_de.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=Die folgenden Probleme werden behoben, sobald das lange Ausf\u00fchrungspatch ''{0}'' ausgef\u00fchrt wurde.
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_es.properties b/repository/src/main/resources/alfresco/messages/system-messages_es.properties
index f78a5a9f07..9ccf8bb2a3 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_es.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_es.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=Los siguientes problemas se resolver\u00e1n una vez que se haya ejecutado el parche de larga duraci\u00f3n {0}
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_fr.properties b/repository/src/main/resources/alfresco/messages/system-messages_fr.properties
index 16501e5c0d..290ebbaa74 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_fr.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_fr.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=Les probl\u00e8mes suivants seront r\u00e9solus une fois la longue ex\u00e9cution du correctif {0} termin\u00e9e
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_it.properties b/repository/src/main/resources/alfresco/messages/system-messages_it.properties
index b9ed640c4c..71ba9be8b6 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_it.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_it.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=I problemi seguenti verranno risolti al termine dell''esecuzione della patch di lunga durata {0}
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_ja.properties b/repository/src/main/resources/alfresco/messages/system-messages_ja.properties
index a367d3aa8f..1d8a8427f5 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_ja.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_ja.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=\u9577\u6642\u9593\u30d1\u30b9{0}\u3092\u5b9f\u884c\u3059\u308b\u3068\u3001\u6b21\u306e\u554f\u984c\u304c\u89e3\u6c7a\u3055\u308c\u307e\u3059
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_nb.properties b/repository/src/main/resources/alfresco/messages/system-messages_nb.properties
index 2f2a08c4f4..c681d4042b 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_nb.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_nb.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=F\u00f8lgende problemer vil bli l\u00f8st s\u00e5 snart den lengekj\u00f8rende oppdateringen {0} har blitt kj\u00f8rt
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_nl.properties b/repository/src/main/resources/alfresco/messages/system-messages_nl.properties
index dca3bf4a2f..35ce63c7a6 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_nl.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_nl.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=De volgende problemen worden opgelost zodra de langlopende patch {0} is uitgevoerd
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_pt_BR.properties b/repository/src/main/resources/alfresco/messages/system-messages_pt_BR.properties
index 3fa3581af7..a69825e0f8 100644
--- a/repository/src/main/resources/alfresco/messages/system-messages_pt_BR.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_pt_BR.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=Os seguintes problemas ser\u00e3o resolvidos ap\u00f3s o patch de longa execu\u00e7\u00e3o {0} ter sido executado
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_ru.properties b/repository/src/main/resources/alfresco/messages/system-messages_ru.properties
index 6333cd4d9f..1ff9c950bb 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_ru.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_ru.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0448\u0435\u043d\u044b \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0433\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f {0}
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/messages/system-messages_zh_CN.properties b/repository/src/main/resources/alfresco/messages/system-messages_zh_CN.properties
index 791476f720..89cc58c2d1 100755
--- a/repository/src/main/resources/alfresco/messages/system-messages_zh_CN.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages_zh_CN.properties
@@ -38,7 +38,7 @@ system.schema_comp.index_columns_validator=Number of columns in index doesn''t m
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
# Optional long running patch messages...
-system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
+system.schema_comp.patch_run_suggestion=\u4e00\u65e6\u8fd0\u884c\u4e86\u957f\u65f6\u95f4\u8fd0\u884c\u7684\u4fee\u8865\u7a0b\u5e8f{0}\uff0c\u4ee5\u4e0b\u95ee\u9898\u5c06\u5f97\u5230\u89e3\u51b3
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/main/resources/alfresco/patch/patch-services-context.xml b/repository/src/main/resources/alfresco/patch/patch-services-context.xml
index 49a5b90d25..e3daaeef11 100644
--- a/repository/src/main/resources/alfresco/patch/patch-services-context.xml
+++ b/repository/src/main/resources/alfresco/patch/patch-services-context.xml
@@ -1424,7 +1424,37 @@
${system.remove-alf_server-table-from-db.ignored}
-
+
+
+ patch.alfrescoSystemAdministrators
+ patch.alfrescoSystemAdministrators.description
+ 0
+ 15001
+ 15002
+
+
+
+
+
+
+
+
+
+
+
+ ALFRESCO_SYSTEM_ADMINISTRATORS
+
+
+ ALFRESCO_SYSTEM_ADMINISTRATORS
+
+
+
+ APP.DEFAULT
+ AUTH.ALF
+
+
+
+
+
diff --git a/repository/src/main/resources/alfresco/public-services-security-context.xml b/repository/src/main/resources/alfresco/public-services-security-context.xml
index 8b3ba3eb0d..caded70ecf 100644
--- a/repository/src/main/resources/alfresco/public-services-security-context.xml
+++ b/repository/src/main/resources/alfresco/public-services-security-context.xml
@@ -490,15 +490,16 @@
org.alfresco.service.cmr.repository.ContentService.getStoreTotalSpace=ACL_ALLOW
- org.alfresco.service.cmr.repository.ContentService.getStoreFreeSpace=ACL_ALLOW
+ org.alfresco.service.cmr.repository.ContentService.getStoreFreeSpace=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.getSupportedStorageClasses=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.isStorageClassesSupported=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.getStorageClassesTransitions=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.getRawReader=ACL_METHOD.ROLE_ADMINISTRATOR
org.alfresco.service.cmr.repository.ContentService.getReader=ACL_NODE.0.sys:base.ReadContent
org.alfresco.service.cmr.repository.ContentService.getWriter=ACL_NODE.0.sys:base.WriteContent
- org.alfresco.service.cmr.repository.ContentService.getDirectAccessUrl=ACL_NODE.0.sys:base.ReadContent
org.alfresco.service.cmr.repository.ContentService.getTempWriter=ACL_ALLOW
+ org.alfresco.service.cmr.repository.ContentService.requestContentDirectUrl=ACL_NODE.0.sys:base.ReadContent
+ org.alfresco.service.cmr.repository.ContentService.isContentDirectUrlEnabled=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.findStorageClasses=ACL_NODE.0.sys:base.ReadContent
org.alfresco.service.cmr.repository.ContentService.updateStorageClasses=ACL_ALLOW
org.alfresco.service.cmr.repository.ContentService.*=ACL_DENY
@@ -796,6 +797,7 @@
org.alfresco.service.cmr.security.AuthorityService.hasAdminAuthority=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.hasGuestAuthority=ACL_ALLOW
+ org.alfresco.service.cmr.security.AuthorityService.hasSysAdminAuthority=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.isAdminAuthority=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.isGuestAuthority=ACL_ALLOW
org.alfresco.service.cmr.security.AuthorityService.countUsers=ACL_ALLOW
diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties
index ef6bade8a1..44e2cf2d90 100644
--- a/repository/src/main/resources/alfresco/repository.properties
+++ b/repository/src/main/resources/alfresco/repository.properties
@@ -1,1247 +1,1313 @@
-# Repository configuration
-
-repository.name=Main Repository
-
-# Schema number
-version.schema=15001
-
-# Directory configuration
-
-dir.root=./alf_data
-
-dir.contentstore=${dir.root}/contentstore
-dir.contentstore.deleted=${dir.root}/contentstore.deleted
-dir.contentstore.bucketsPerMinute=0
-
-# ContentStore subsystem: default choice
-filecontentstore.subsystem.name=unencryptedContentStore
-
-# The location of cached content
-dir.cachedcontent=${dir.root}/cachedcontent
-
-# The value for the maximum permitted size in bytes of all content.
-# No value (or a negative long) will be taken to mean that no limit should be applied.
-# See content-services-context.xml
-system.content.maximumFileSizeLimit=
-
-#
-# The server mode. Set value in alfresco-global.properties
-# UNKNOWN | TEST | BACKUP | PRODUCTION
-#
-system.serverMode=UNKNOWN
-
-# The location for lucene index files
-dir.indexes=${dir.root}/lucene-indexes
-
-# The location for index backups
-dir.indexes.backup=${dir.root}/backup-lucene-indexes
-
-# The location for lucene index locks
-dir.indexes.lock=${dir.indexes}/locks
-
-#Directory to find external license
-dir.license.external=.
-# Spring resource location of external license files
-location.license.external=file://${dir.license.external}/*.lic
-# Spring resource location of embedded license files
-location.license.embedded=/WEB-INF/alfresco/license/*.lic
-# Spring resource location of license files on shared classpath
-location.license.shared=classpath*:/alfresco/extension/license/*.lic
-
-# WebDAV initialization properties
-system.webdav.servlet.enabled=true
-system.webdav.url.path.prefix=
-system.webdav.storeName=${protocols.storeName}
-system.webdav.rootPath=${protocols.rootPath}
-# File name patterns that trigger rename shuffle detection
-# pattern is used by move - tested against full path after it has been lower cased.
-system.webdav.renameShufflePattern=(.*/\\..*)|(.*[a-f0-9]{8}+$)|(.*\\.tmp$)|(.*atmp[0-9]+$)|(.*\\.wbk$)|(.*\\.bak$)|(.*\\~$)|(.*backup.*\\.do[ct]{1}[x]?[m]?$)|(.*\\.sb\\-\\w{8}\\-\\w{6}$)
-system.webdav.activities.enabled=false
-
-
-system.workflow.jbpm.comment.property.max.length=-1
-system.workflow.comment.property.max.length=4000
-
-#Determines if Activiti definitions are visible
-system.workflow.engine.activiti.definitions.visible=true
-
-
-# Determines if the Activiti engine is enabled
-system.workflow.engine.activiti.enabled=true
-system.workflow.engine.activiti.idblocksize=100
-system.workflow.engine.activiti.taskvariableslimit=20000
-
-# Determines if the workflows that are deployed to the activiti engine should
-# be deployed in the tenant-context of the thread IF the tenant-service is enabled
-# If set to false, all workflows deployed will be shared among tenants. Recommended
-# setting is true unless there is a good reason to not allow deploy tenant-specific
-# worklfows when a MT-environment is set up.
-system.workflow.deployWorkflowsInTenant=true
-#Determines if historic process instance are retained in case of canceling a process instance
-system.workflow.engine.activiti.retentionHistoricProcessInstance=false
-
-# The maximum number of groups to check for pooled tasks. For performance
-# reasons, this is limited to 500 by default.
-system.workflow.maxAuthoritiesForPooledTasks=500
-
-# The maximum number of pooled tasks to return in a query. It may be necessary
-# to limit this depending on UI limitations.
-system.workflow.maxPooledTasks=-1
-
-# The maximum number of reviewers for "Group Review and Approve" workflow.
-# Use '0' for unlimited.
-system.workflow.maxGroupReviewers=0
-
-index.subsystem.name=noindex
-
-# ######################################### #
-# Index Tracking Configuration #
-# ######################################### #
-#
-# Index tracking information of a certain age is cleaned out by a scheduled job.
-# Any clustered system that has been offline for longer than this period will need to be seeded
-# with a more recent backup of the Lucene indexes or the indexes will have to be fully rebuilt.
-# Use -1 to disable purging. This can be switched on at any stage.
-index.tracking.minRecordPurgeAgeDays=30
-# Unused transactions will be purged in chunks determined by commit time boundaries. 'index.tracking.purgeSize' specifies the size
-# of the chunk (in ms). Default is a couple of hours.
-index.tracking.purgeSize=7200000
-
-# Change the failure behaviour of the configuration checker
-system.bootstrap.config_check.strict=true
-
-
-#
-# How long should shutdown wait to complete normally before
-# taking stronger action and calling System.exit()
-# in ms, 10,000 is 10 seconds
-#
-shutdown.backstop.timeout=10000
-shutdown.backstop.enabled=false
-
-# Server Single User Mode
-# note:
-# only allow named user (note: if blank or not set then will allow all users)
-# assuming maxusers is not set to 0
-#server.singleuseronly.name=admin
-
-# Server Max Users - limit number of users with non-expired tickets
-# note:
-# -1 allows any number of users, assuming not in single-user mode
-# 0 prevents further logins, including the ability to enter single-user mode
-server.maxusers=-1
-
-#
-# Disable all shared caches (mutable and immutable)
-# These properties are used for diagnostic purposes
-system.cache.disableMutableSharedCaches=false
-system.cache.disableImmutableSharedCaches=false
-
-# The maximum capacity of the parent assocs cache (the number of nodes whose parents can be cached)
-system.cache.parentAssocs.maxSize=130000
-
-# The average number of parents expected per cache entry. This parameter is multiplied by the above
-# value to compute a limit on the total number of cached parents, which will be proportional to the
-# cache's memory usage. The cache will be pruned when this limit is exceeded to avoid excessive
-# memory usage.
-system.cache.parentAssocs.limitFactor=8
-
-#
-# Properties to limit resources spent on individual searches
-#
-# The maximum time spent pruning results
-system.acl.maxPermissionCheckTimeMillis=10000
-# The maximum number of search results to perform permission checks against
-system.acl.maxPermissionChecks=1000
-system.acl.maxPermissionCheckEnabled=false
-
-# The maximum number of filefolder list results
-system.filefolderservice.defaultListMaxResults=5000
-# DEPRECATED: Use 'system.auditableData.preserve'
-system.preserve.modificationData=false
-# The default to preserve all cm:auditable data on a node when the process is not directly driven by a user action
-system.auditableData.preserve=${system.preserve.modificationData}
-# Specific control of how the FileFolderService treats cm:auditable data when performing moves
-system.auditableData.FileFolderService=${system.auditableData.preserve}
-# Specific control of whether ACL changes on a node trigger the cm:auditable aspect
-system.auditableData.ACLs=${system.auditableData.preserve}
-
-# Properties to control read permission evaluation for acegi
-system.readpermissions.optimise=true
-system.readpermissions.bulkfetchsize=1000
-
-#
-# Manually control how the system handles maximum string lengths.
-# Any zero or negative value is ignored.
-# Only change this after consulting support or reading the appropriate Javadocs for
-# org.alfresco.repo.domain.schema.SchemaBootstrap for V2.1.2.
-# Before database migration, the string value storage may need to be adjusted using the scheduled job
-system.maximumStringLength=-1
-system.maximumStringLength.jobCronExpression=* * * * * ? 2099
-system.maximumStringLength.jobQueryRange=10000
-system.maximumStringLength.jobThreadCount=4
-
-#
-# Limit hibernate session size by trying to amalgamate events for the L2 session invalidation
-# - hibernate works as is up to this size
-# - after the limit is hit events that can be grouped invalidate the L2 cache by type and not instance
-# events may not group if there are post action listener registered (this is not the case with the default distribution)
-system.hibernateMaxExecutions=20000
-
-#
-# Determine if modification timestamp propagation from child to parent nodes is respected or not.
-# Even if 'true', the functionality is only supported for child associations that declare the
-# 'propagateTimestamps' element in the dictionary definition.
-system.enableTimestampPropagation=true
-
-#
-# Enable system model integrity checking.
-# WARNING: Changing this is unsupported; bugs may corrupt data
-system.integrity.enabled=true
-# Do integrity violations fail transactions
-# WARNING: Changing this is unsupported; bugs may corrupt data
-system.integrity.failOnViolation=true
-# The number of errors to report when violations are detected
-system.integrity.maxErrorsPerTransaction=5
-# Add call stacks to integrity events so that errors are logged with possible causes
-# WARNING: This is expensive and should only be switched on for diagnostic purposes
-system.integrity.trace=false
-
-#
-# Decide if content should be removed from the system immediately after being orphaned.
-# Do not change this unless you have examined the impact it has on your backup procedures.
-system.content.eagerOrphanCleanup=false
-# The number of days to keep orphaned content in the content stores.
-# This has no effect on the 'deleted' content stores, which are not automatically emptied.
-system.content.orphanProtectDays=14
-# The action to take when a store or stores fails to delete orphaned content
-# IGNORE: Just log a warning. The binary remains and the record is expunged
-# KEEP_URL: Log a warning and create a URL entry with orphan time 0. It won't be processed or removed.
-system.content.deletionFailureAction=IGNORE
-# The CRON expression to trigger the deletion of resources associated with orphaned content.
-system.content.orphanCleanup.cronExpression=0 0 4 * * ?
-# The batch size user by the content store cleaner
-system.content.cleanerBatchSize=1000
-
-# The CRON expression to trigger the cleanup of deleted nodes and dangling transactions that are old enough
-system.nodeServiceCleanup.cronExpression=0 0 21 * * ?
-
-# When transforming archive files (.zip etc) into text representations (such as
-# for full text indexing), should the files within the archive be processed too?
-# If enabled, transformation takes longer, but searches of the files find more.
-transformer.Archive.includeContents=false
-
-# Database configuration
-db.schema.name=
-db.schema.stopAfterSchemaBootstrap=false
-db.schema.update=true
-db.schema.update.lockRetryCount=24
-db.schema.update.lockRetryWaitSeconds=5
-db.driver=org.gjt.mm.mysql.Driver
-db.name=alfresco
-db.url=jdbc:mysql:///${db.name}
-db.username=alfresco
-db.password=alfresco
-db.pool.initial=10
-db.pool.max=275
-db.txn.isolation=-1
-db.pool.statements.enable=true
-db.pool.statements.max=40
-db.pool.min=10
-db.pool.idle=10
-db.pool.wait.max=5000
-
-db.pool.validate.query=
-db.pool.evict.interval=600000
-db.pool.evict.idle.min=1800000
-#
-# note: for 'db.pool.evict.num.tests' see http://commons.apache.org/dbcp/configuration.html (numTestsPerEvictionRun)
-# and also following extract from "org.apache.commons.pool.impl.GenericKeyedObjectPool" (1.5.5)
-#
-# * The number of objects to examine during each run of the idle object evictor thread (if any).
-# * When a negative value is supplied, ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})
-# * tests will be run. I.e., when the value is -n, roughly one nth of the
-# * idle objects will be tested per run.
-#
-db.pool.evict.num.tests=-1
-
-db.pool.evict.validate=false
-db.pool.validate.borrow=true
-db.pool.validate.return=false
-
-db.pool.abandoned.detect=false
-db.pool.abandoned.time=300
-#
-# db.pool.abandoned.log=true (logAbandoned) adds overhead (http://commons.apache.org/dbcp/configuration.html)
-# and also requires db.pool.abandoned.detect=true (removeAbandoned)
-#
-db.pool.abandoned.log=false
-
-
-# Audit configuration
-audit.enabled=true
-audit.tagging.enabled=true
-audit.alfresco-access.enabled=false
-audit.alfresco-access.sub-actions.enabled=false
-audit.cmischangelog.enabled=false
-audit.dod5015.enabled=false
-# Setting this flag to true will force startup failure when invalid audit configurations are detected
-audit.config.strict=false
-# Audit map filter for AccessAuditor - restricts recorded events to user driven events
-audit.filter.alfresco-access.default.enabled=false
-audit.filter.alfresco-access.transaction.user=~System;~null;.*
-audit.filter.alfresco-access.transaction.type=cm:folder;cm:content;st:site
-audit.filter.alfresco-access.transaction.path=~/sys:archivedItem;~/ver:;.*
-
-
-# System Configuration
-system.store=system://system
-system.descriptor.childname=sys:descriptor
-system.descriptor.current.childname=sys:descriptor-current
-
-# User config
-alfresco_user_store.store=user://alfrescoUserStore
-alfresco_user_store.system_container.childname=sys:system
-alfresco_user_store.user_container.childname=sys:people
-
-# note: default admin username - should not be changed after installation
-alfresco_user_store.adminusername=admin
-
-# Initial password - editing this will not have any effect once the repository is installed
-alfresco_user_store.adminpassword=209c6174da490caeb422f3fa5a7ae634
-
-# note: default guest username - should not be changed after installation
-alfresco_user_store.guestusername=guest
-
-# Used to move home folders to a new location
-home_folder_provider_synchronizer.enabled=false
-home_folder_provider_synchronizer.override_provider=
-home_folder_provider_synchronizer.keep_empty_parents=false
-
-# Spaces Archive Configuration
-spaces.archive.store=archive://SpacesStore
-
-# Spaces Configuration
-spaces.store=workspace://SpacesStore
-spaces.company_home.childname=app:company_home
-spaces.guest_home.childname=app:guest_home
-spaces.dictionary.childname=app:dictionary
-spaces.templates.childname=app:space_templates
-spaces.imap_attachments.childname=cm:Imap Attachments
-spaces.imap_home.childname=cm:Imap Home
-spaces.imapConfig.childname=app:imap_configs
-spaces.imap_templates.childname=app:imap_templates
-spaces.scheduled_actions.childname=cm:Scheduled Actions
-spaces.emailActions.childname=app:email_actions
-spaces.searchAction.childname=cm:search
-spaces.templates.content.childname=app:content_templates
-spaces.templates.email.childname=app:email_templates
-spaces.templates.email.invite1.childname=app:invite_email_templates
-spaces.templates.email.notify.childname=app:notify_email_templates
-spaces.templates.email.following.childname=app:following
-spaces.templates.rss.childname=app:rss_templates
-spaces.savedsearches.childname=app:saved_searches
-spaces.scripts.childname=app:scripts
-spaces.content_forms.childname=app:forms
-spaces.user_homes.childname=app:user_homes
-spaces.user_homes.regex.key=userName
-spaces.user_homes.regex.pattern=
-spaces.user_homes.regex.group_order=
-spaces.sites.childname=st:sites
-spaces.templates.email.invite.childname=cm:invite
-spaces.templates.email.activities.childname=cm:activities
-spaces.rendition.rendering_actions.childname=app:rendering_actions
-spaces.replication.replication_actions.childname=app:replication_actions
-spaces.transfers.childname=app:transfers
-spaces.transfer_groups.childname=app:transfer_groups
-spaces.transfer_temp.childname=app:temp
-spaces.inbound_transfer_records.childname=app:inbound_transfer_records
-spaces.webscripts.childname=cm:webscripts
-spaces.extension_webscripts.childname=cm:extensionwebscripts
-spaces.models.childname=app:models
-spaces.workflow.definitions.childname=app:workflow_defs
-spaces.templates.email.workflowemailnotification.childname=cm:workflownotification
-spaces.nodetemplates.childname=app:node_templates
-spaces.shared.childname=app:shared
-spaces.solr_facets.root.childname=srft:facets
-spaces.smartfolders.childname=app:smart_folders
-spaces.smartdownloads.childname=app:smart_downloads
-spaces.transfer_summary_report.location=/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.inbound_transfer_records.childname}
-spaces.quickshare.link_expiry_actions.childname=app:quick_share_link_expiry_actions
-
-
-# ADM VersionStore Configuration
-version.store.initialVersion=true
-version.store.enableAutoVersioning=true
-version.store.enableAutoVersionOnUpdateProps=false
-version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore
-version.store.version2Store=workspace://version2Store
-
-# Optional Comparator class name to sort versions.
-# Set to: org.alfresco.repo.version.common.VersionLabelComparator
-# if upgrading from a version that used unordered sequences in a cluster.
-version.store.versionComparatorClass=
-
-# Folders for storing people
-system.system_container.childname=sys:system
-system.people_container.childname=sys:people
-system.authorities_container.childname=sys:authorities
-system.zones_container.childname=sys:zones
-
-# Folders for storing workflow related info
-system.workflow_container.childname=sys:workflow
-
-# Folder for storing shared remote credentials
-system.remote_credentials_container.childname=sys:remote_credentials
-
-# Folder for storing syncset definitions
-system.syncset_definition_container.childname=sys:syncset_definitions
-
-# Folder for storing download archives
-system.downloads_container.childname=sys:downloads
-
-# Folder for storing IdP's certificate definitions
-system.certificate_container.childname=sys:samlcertificate
-
-# Are user names case sensitive?
-user.name.caseSensitive=false
-domain.name.caseSensitive=false
-domain.separator=
-
-#Format caption extracted from the XML Schema.
-xforms.formatCaption=true
-
-# ECM content usages/quotas
-system.usages.enabled=false
-system.usages.clearBatchSize=0
-system.usages.updateBatchSize=50
-
-# Repository endpoint - used by Activity Service
-repo.remote.endpoint=/service
-
-# Some authentication mechanisms may need to create people
-# in the repository on demand. This enables that feature.
-# If disabled an error will be generated for missing
-# people. If enabled then a person will be created and
-# persisted.
-create.missing.people=${server.transaction.allow-writes}
-
-# Create home folders (unless disabled, see next property) as people are created (true) or create them lazily (false)
-home.folder.creation.eager=true
-# Disable home folder creation - if true then home folders are not created (neither eagerly nor lazily)
-home.folder.creation.disabled=false
-
-# Should we consider zero byte content to be the same as no content when firing
-# content update policies? Prevents 'premature' firing of inbound content rules
-# for some clients such as Mac OS X Finder
-policy.content.update.ignoreEmpty=true
-
-# Default value of alfresco.rmi.services.host is 0.0.0.0 which means 'listen on all adapters'.
-# This allows connections to JMX both remotely and locally.
-#
-alfresco.rmi.services.port=50500
-alfresco.rmi.services.external.host=localhost
-alfresco.rmi.services.host=0.0.0.0
-
-# If the RMI address is in-use, how many retries should be done before aborting
-# Default value of alfresco.rmi.services.retries is 0 which means 'Don't retry if the address is in-use'
-alfresco.rmi.services.retries=4
-# How long in milliseconds to wait after a failed server socket bind, before retrying
-alfresco.rmi.services.retryInterval=250
-
-# RMI service ports for the individual services.
-# These eight services are available remotely.
-#
-# Assign individual ports for each service for best performance
-# or run several services on the same port, you can even run everything on 50500 if
-# running through a firewall.
-#
-# Specify 0 to use a random unused port.
-#
-monitor.rmi.service.port=50508
-
-#
-# enable or disable individual RMI services
-#
-monitor.rmi.service.enabled=false
-
-
-# Should the Mbean server bind to an existing server. Set to true for most application servers.
-# false for WebSphere clusters.
-mbean.server.locateExistingServerIfPossible=true
-
-# Rendition Service 2
-renditionService2.enabled=true
-
-# Thumbnail Service
-system.thumbnail.generate=true
-
-# when creating doc via CMIS - optionally configure set of renditions names to request async
-cmis.create.doc.request.renditions.set=
-
-# Default thumbnail limits
-# When creating thumbnails, only use the first pageLimit pages
-system.thumbnail.definition.default.timeoutMs=-1
-system.thumbnail.definition.default.readLimitTimeMs=-1
-system.thumbnail.definition.default.maxSourceSizeKBytes=-1
-system.thumbnail.definition.default.readLimitKBytes=-1
-system.thumbnail.definition.default.pageLimit=-1
-system.thumbnail.definition.default.maxPages=-1
-
-# Max mimetype sizes to create thumbnail icons
-system.thumbnail.mimetype.maxSourceSizeKBytes.pdf=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.txt=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.docx=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.xlsx=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.pptx=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.odt=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.ods=-1
-system.thumbnail.mimetype.maxSourceSizeKBytes.odp=-1
-
-# Configuration for handling of failing thumbnails.
-# See NodeEligibleForRethumbnailingEvaluator's javadoc for details.
-#
-# Retry periods limit the frequency with which the repository will attempt to create Share thumbnails
-# for content nodes which have previously failed in their thumbnail attempts.
-# These periods are in seconds.
-#
-# 604800s = 60s * 60m * 24h * 7d = 1 week
-system.thumbnail.retryPeriod=60
-system.thumbnail.retryCount=2
-system.thumbnail.quietPeriod=604800
-system.thumbnail.quietPeriodRetriesEnabled=true
-system.thumbnail.redeployStaticDefsOnStartup=true
-
-content.metadata.async.extract.enabled=true
-content.metadata.async.embed.enabled=true
-
-# The default timeout for metadata mapping extracters
-content.metadataExtracter.default.timeoutMs=20000
-
-# Local transformer urls to T-engines to service transform requests via http. Enabled by default.
-localTransform.core-aio.url=http://localhost:8090/
-
-# When a local transformer .url is set, this value indicates the amount of time to wait after a connection failure
-# before retrying the connection to allow a docker container to (re)start.
-localTransform.core-aio.startupRetryPeriodSeconds=60
-
-# Property to enable upgrade from 2.1-A
-V2.1-A.fixes.to.schema=0
-#V2.1-A.fixes.to.schema=82
-
-# The default authentication chain
-authentication.chain=alfrescoNtlm1:alfrescoNtlm
-
-# Do authentication tickets expire or live for ever?
-authentication.ticket.ticketsExpire=true
-
-# If ticketsEpire is true then how they should expire?
-# Valid values are: AFTER_INACTIVITY, AFTER_FIXED_TIME, DO_NOT_EXPIRE
-# The default is AFTER_FIXED_TIME
-authentication.ticket.expiryMode=AFTER_INACTIVITY
-
-# If authentication.ticket.ticketsExpire is true and
-# authentication.ticket.expiryMode is AFTER_FIXED_TIME or AFTER_INACTIVITY,
-# this controls the minimum period for which tickets are valid.
-# The default is PT1H for one hour.
-authentication.ticket.validDuration=PT1H
-
-# Use one ticket for all user sessions
-# For the pre 4.2 behaviour of one ticket per session set this to false.
-authentication.ticket.useSingleTicketPerUser=true
-
-authentication.alwaysAllowBasicAuthForAdminConsole.enabled=true
-authentication.getRemoteUserTimeoutMilliseconds=10000
-
-# FTP access
-ftp.enabled=false
-
-# Default root path for protocols
-protocols.storeName=${spaces.store}
-protocols.rootPath=/${spaces.company_home.childname}
-
-# OpenCMIS
-opencmis.connector.default.store=${spaces.store}
-opencmis.connector.default.rootPath=/${spaces.company_home.childname}
-opencmis.connector.default.typesDefaultMaxItems=500
-opencmis.connector.default.typesDefaultDepth=-1
-opencmis.connector.default.objectsDefaultMaxItems=10000
-opencmis.connector.default.objectsDefaultDepth=100
-opencmis.connector.default.contentChangesDefaultMaxItems=10000
-opencmis.connector.default.openHttpSession=false
-opencmis.activities.enabled=true
-opencmis.bulkUpdateProperties.maxItemsSize=1000
-opencmis.bulkUpdateProperties.batchSize=20
-opencmis.bulkUpdateProperties.workerThreads=2
-opencmis.maxContentSizeMB=4096
-opencmis.memoryThresholdKB=4096
-
-# URL generation overrides
-
-# if true, the context path of OpenCMIS generated urls will be set to "opencmis.context.value", otherwise it will be taken from the request url
-opencmis.context.override=false
-opencmis.context.value=
-# if true, the servlet path of OpenCMIS generated urls will be set to "opencmis.servletpath.value", otherwise it will be taken from the request url
-opencmis.servletpath.override=false
-opencmis.servletpath.value=
-opencmis.server.override=false
-opencmis.server.value=
-
-# IMAP
-imap.server.enabled=false
-imap.server.port=143
-imap.server.attachments.extraction.enabled=true
-
-# Default IMAP mount points
-imap.config.home.store=${spaces.store}
-imap.config.home.rootPath=/${spaces.company_home.childname}
-imap.config.home.folderPath=${spaces.imap_home.childname}
-imap.config.server.mountPoints=AlfrescoIMAP
-imap.config.server.mountPoints.default.mountPointName=IMAP
-imap.config.server.mountPoints.default.modeName=ARCHIVE
-imap.config.server.mountPoints.default.store=${spaces.store}
-imap.config.server.mountPoints.default.rootPath=${protocols.rootPath}
-imap.config.server.mountPoints.value.AlfrescoIMAP.mountPointName=Alfresco IMAP
-imap.config.server.mountPoints.value.AlfrescoIMAP.modeName=MIXED
-
-#Imap extraction settings
-#imap.attachments.mode:
-# SEPARATE -- All attachments for each email will be extracted to separate folder.
-# COMMON -- All attachments for all emails will be extracted to one folder.
-# SAME -- Attachments will be extracted to the same folder where email lies.
-imap.attachments.mode=SEPARATE
-imap.attachments.folder.store=${spaces.store}
-imap.attachments.folder.rootPath=/${spaces.company_home.childname}
-imap.attachments.folder.folderPath=${spaces.imap_attachments.childname}
-
-# Activities Feed - refer to subsystem
-
-# Feed max ID range to limit maximum number of entries
-activities.feed.max.idRange=1000000
-# Feed max size (number of entries)
-activities.feed.max.size=200
-# Feed max age (eg. 44640 mins => 31 days)
-activities.feed.max.ageMins=44640
-
-activities.feed.generator.jsonFormatOnly=true
-activities.feed.fetchBatchSize=250
-activities.feedNotifier.batchSize=200
-activities.feedNotifier.numThreads=2
-
-# Subsystem unit test values. Will not have any effect on production servers
-subsystems.test.beanProp.default.longProperty=123456789123456789
-subsystems.test.beanProp.default.anotherStringProperty=Global Default
-subsystems.test.beanProp=inst1,inst2,inst3
-subsystems.test.beanProp.value.inst2.boolProperty=true
-subsystems.test.beanProp.value.inst3.anotherStringProperty=Global Instance Default
-subsystems.test.simpleProp2=true
-subsystems.test.simpleProp3=Global Default3
-
-# Default Async Action Thread Pool
-default.async.action.threadPriority=1
-default.async.action.corePoolSize=8
-default.async.action.maximumPoolSize=20
-
-# Deployment Service
-deployment.service.numberOfSendingThreads=5
-deployment.service.corePoolSize=2
-deployment.service.maximumPoolSize=3
-deployment.service.threadPriority=5
-# How long to wait in mS before refreshing a target lock - detects shutdown servers
-deployment.service.targetLockRefreshTime=60000
-# How long to wait in mS from the last communication before deciding that deployment has failed, possibly
-# the destination is no longer available?
-deployment.service.targetLockTimeout=3600000
-# Deployment method used to deploy this Alfresco instance (DEFAULT, INSTALLER, DOCKER_COMPOSE, HELM_CHART, ANSIBLE, ZIP, QUICK_START)
-deployment.method=DEFAULT
-
-#Invitation Service
-# Should send emails as part of invitation process.
-notification.email.siteinvite=true
-# Moderated invite Activiti workflow
-site.invite.moderated.workflowId=activiti$activitiInvitationModerated
-# Add intneral users Activiti workflow (use activiti$activitiInvitationNominated to revert to requiring accept of invite for internal users)
-site.invite.nominated.workflowId=activiti$activitiInvitationNominatedAddDirect
-# Add external users Activiti workflow
-site.invite.nominatedExternal.workflowId=activiti$activitiInvitationNominated
-
-# Replication Service
-replication.enabled=false
-
-# Transfer Service
-transferservice.receiver.enabled=false
-transferservice.receiver.stagingDir=${java.io.tmpdir}/alfresco-transfer-staging
-#
-# How long to wait in mS before refreshing a transfer lock - detects shutdown servers
-# Default 1 minute.
-transferservice.receiver.lockRefreshTime=60000
-#
-# How many times to attempt retry the transfer lock
-transferservice.receiver.lockRetryCount=3
-# How long to wait, in mS, before retrying the transfer lock
-transferservice.receiver.lockRetryWait=100
-#
-# How long to wait, in mS, since the last contact with from the client before
-# timing out a transfer. Needs to be long enough to cope with network delays and "thinking
-# time" for both source and destination. Default 5 minutes.
-transferservice.receiver.lockTimeOut=300000
-
-# OrphanReaper
-orphanReaper.lockRefreshTime=60000
-orphanReaper.lockTimeOut=3600000
-
-
-# security
-security.anyDenyDenies=true
-# Whether to post-process denies. Only applies to solr4+ when anyDenyDenies is true.
-security.postProcessDenies=false
-
-#
-# Encryption properties
-#
-# default keystores location
-dir.keystore=classpath:alfresco/keystore
-
-# general encryption parameters
-encryption.keySpec.class=org.alfresco.encryption.DESEDEKeyGenerator
-encryption.keyAlgorithm=AES
-encryption.cipherAlgorithm=AES/CBC/PKCS5Padding
-
-# secret key keystore configuration
-encryption.keystore.location=${dir.keystore}/keystore
-# configuration via metadata is deprecated
-encryption.keystore.keyMetaData.location=
-encryption.keystore.provider=
-encryption.keystore.type=pkcs12
-
-# backup secret key keystore configuration
-encryption.keystore.backup.location=${dir.keystore}/backup-keystore
-# configuration via metadata is deprecated
-encryption.keystore.backup.keyMetaData.location=
-encryption.keystore.backup.provider=
-encryption.keystore.backup.type=pkcs12
-
-# Should encryptable properties be re-encrypted with new encryption keys on botstrap?
-encryption.bootstrap.reencrypt=false
-
-# mac/md5 encryption
-encryption.mac.messageTimeout=30000
-encryption.mac.algorithm=HmacSHA1
-
-# ssl encryption
-encryption.ssl.keystore.location=${dir.keystore}/ssl.keystore
-encryption.ssl.keystore.provider=
-encryption.ssl.keystore.type=JCEKS
-# configuration via metadata is deprecated
-encryption.ssl.keystore.keyMetaData.location=
-encryption.ssl.truststore.location=${dir.keystore}/ssl.truststore
-encryption.ssl.truststore.provider=
-encryption.ssl.truststore.type=JCEKS
-# configuration via metadata is deprecated
-encryption.ssl.truststore.keyMetaData.location=
-
-# Re-encryptor properties
-encryption.reencryptor.chunkSize=100
-encryption.reencryptor.numThreads=2
-
-# SOLR connection details (e.g. for JMX)
-solr.host=localhost
-solr.port=8983
-solr.port.ssl=8984
-solr.solrUser=solr
-solr.solrPassword=solr
-# none, https
-solr.secureComms=https
-solr.sharedSecret=
-solr.sharedSecret.header=X-Alfresco-Search-Secret
-solr.cmis.alternativeDictionary=DEFAULT_DICTIONARY
-
-solr.max.total.connections=40
-solr.max.host.connections=40
-
-# Solr connection timeouts
-# solr connect timeout in ms
-solr.solrConnectTimeout=5000
-
-# cron expression defining how often the Solr Admin client (used by JMX) pings Solr if it goes away
-solr.solrPingCronExpression=0 0/5 * * * ? *
-
-
-#Default SOLR store mappings mappings
-solr.store.mappings=solrMappingAlfresco,solrMappingArchive
-solr.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
-solr.store.mappings.value.solrMappingAlfresco.baseUrl=/solr/alfresco
-solr.store.mappings.value.solrMappingAlfresco.protocol=workspace
-solr.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
-solr.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
-solr.store.mappings.value.solrMappingArchive.baseUrl=/solr/archive
-solr.store.mappings.value.solrMappingArchive.protocol=archive
-solr.store.mappings.value.solrMappingArchive.identifier=SpacesStore
-
-#Default SOLR 4 store mappings mappings
-solr4.store.mappings=solrMappingAlfresco,solrMappingArchive
-solr4.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
-solr4.store.mappings.value.solrMappingAlfresco.baseUrl=/solr4/alfresco
-solr4.store.mappings.value.solrMappingAlfresco.protocol=workspace
-solr4.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
-solr4.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
-solr4.store.mappings.value.solrMappingArchive.baseUrl=/solr4/archive
-solr4.store.mappings.value.solrMappingArchive.protocol=archive
-solr4.store.mappings.value.solrMappingArchive.identifier=SpacesStore
-
-#Default SOLR 6 store mappings mappings
-solr6.store.mappings=solrMappingAlfresco,solrMappingArchive,solrMappingHistory
-solr6.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
-solr6.store.mappings.value.solrMappingAlfresco.baseUrl=/solr/alfresco
-solr6.store.mappings.value.solrMappingAlfresco.protocol=workspace
-solr6.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
-solr6.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
-solr6.store.mappings.value.solrMappingArchive.baseUrl=/solr/archive
-solr6.store.mappings.value.solrMappingArchive.protocol=archive
-solr6.store.mappings.value.solrMappingArchive.identifier=SpacesStore
-solr6.store.mappings.value.solrMappingHistory.httpClientFactory=solrHttpClientFactory
-solr6.store.mappings.value.solrMappingHistory.baseUrl=/solr/history
-solr6.store.mappings.value.solrMappingHistory.protocol=workspace
-solr6.store.mappings.value.solrMappingHistory.identifier=history
-
-#
-# URL Shortening Properties
-#
-urlshortening.bitly.username=brianalfresco
-urlshortening.bitly.api.key=R_ca15c6c89e9b25ccd170bafd209a0d4f
-urlshortening.bitly.url.length=20
-
-#
-# Bulk Filesystem Importer
-#
-
-# The number of threads to employ in a batch import
-bulkImport.batch.numThreads=4
-
-# The size of a batch in a batch import i.e. the number of files to import in a
-# transaction/thread
-bulkImport.batch.batchSize=20
-
-
-#
-# Caching Content Store
-#
-system.content.caching.cacheOnInbound=true
-system.content.caching.maxDeleteWatchCount=1
-# Clean up every day at 3 am
-system.content.caching.contentCleanup.cronExpression=0 0 3 * * ?
-system.content.caching.minFileAgeMillis=60000
-system.content.caching.maxUsageMB=4096
-# maxFileSizeMB - 0 means no max file size.
-system.content.caching.maxFileSizeMB=0
-# When the CachingContentStore is about to write a cache file but the disk usage is in excess of panicThresholdPct
-# (default 90%) then the cache file is not written and the cleaner is started (if not already running) in a new thread.
-system.content.caching.panicThresholdPct=90
-# When a cache file has been written that results in cleanThresholdPct (default 80%) of maxUsageBytes
-# being exceeded then the cached content cleaner is invoked (if not already running) in a new thread.
-system.content.caching.cleanThresholdPct=80
-# An aggressive cleaner is run till the targetUsagePct (default 70%) of maxUsageBytes is achieved
-system.content.caching.targetUsagePct=70
-# Threshold in seconds indicating a minimal gap between normal cleanup starts
-system.content.caching.normalCleanThresholdSec=0
-
-mybatis.useLocalCaches=false
-
-fileFolderService.checkHidden.enabled=true
-
-
-ticket.cleanup.cronExpression=0 0 * * * ?
-
-#
-# Download Service Cleanup
-#
-download.cleaner.startDelayMilliseconds=3600000
-# 1 hour
-download.cleaner.repeatIntervalMilliseconds=3600000
-download.cleaner.maxAgeMins=60
-# -1 or 0 for not using batches
-download.cleaner.batchSize=1000
-
-# you could set this to false for new installations greater then ACS 6.2
-# see MNT-20212
-download.cleaner.cleanAllSysDownloadFolders=true
-
-#
-# Download Service Limits, in bytes
-#
-download.maxContentSize=2152852358
-
-# Max size of view trashcan files
-#
-trashcan.MaxSize=1000
-
-#
-# Use bridge tables for caching authority evaluation.
-#
-authority.useBridgeTable=true
-
-# Limit the number of results from findAuthority query
-authority.findAuthorityLimit=10000
-
-# enable QuickShare - if false then the QuickShare-specific REST APIs will return 403 Forbidden
-system.quickshare.enabled=true
-system.quickshare.email.from.default=noreply@alfresco.com
-# By default the difference between the quick share expiry date and the current time must be at least 1 day (24 hours).
-# However, this can be changed to at least 1 hour or 1 minute for testing purposes. For example,
-# setting the value to MINUTES, means the service will calculate the difference between NOW and the given expiry date
-# in terms of minutes and checks for the difference to be greater than 1 minute.
-# DAYS | HOURS | MINUTES
-system.quickshare.expiry_date.enforce.minimum.period=DAYS
-
-# Oubound Mail
-mail.service.corePoolSize=8
-mail.service.maximumPoolSize=20
-
-nodes.bulkLoad.cachingThreshold=10
-
-# Multi-Tenancy
-
-# if "dir.contentstore.tenants" is set then
-# tenants are not co-mingled and all content roots will appear below this container (in sub-folder)
-# and when creating a tenant the "contentRootPath" (root content store directory for a given tenant) will be ignored
-dir.contentstore.tenants=
-
-# Gateway Authentication
-# gateway authentication is disabled if empty host is specified
-alfresco.authentication.gateway.host=
-alfresco.authentication.gateway.protocol=https
-alfresco.authentication.gateway.port=443
-alfresco.authentication.gateway.outboundHeaders=Authorization,key
-alfresco.authentication.gateway.inboundHeaders=X-Alfresco-Authenticator-Key,X-Alfresco-Remote-User
-alfresco.authentication.gateway.prefixUrl=/publicapi
-alfresco.authentication.gateway.bufferSize=2048
-alfresco.authentication.gateway.connectTimeout=10000
-alfresco.authentication.gateway.readTimeout=120000
-alfresco.authentication.gateway.httpTcpNodelay=true
-alfresco.authentication.gateway.httpConnectionStalecheck=true
-
-# webscripts config
-webscripts.encryptTempFiles=false
-webscripts.tempDirectoryName=Alfresco-WebScripts
-# 4mb
-webscripts.memoryThreshold=4194304
-# 4gb
-webscripts.setMaxContentSize=5368709120
-
-# Property to enable index upgrade for metadata query (MDQ)
-#
-# The indexes are not added unless this value is changed
-# Adding each the supporting indexes may take several hours depending on the size of the database.
-# The required indexes may be added in stages.
-# See: classpath:alfresco/dbscripts/upgrade/4.2/${db.script.dialect}/metadata-query-indexes.sql
-# See: classpath:alfresco/dbscripts/upgrade/5.1/${db.script.dialect}/metadata-query-indexes-2.sql
-system.metadata-query-indexes.ignored=true
-system.metadata-query-indexes-more.ignored=true
-
-#
-# Do we defer running the shared folder patch?
-#
-system.patch.sharedFolder.deferred=false
-# Default value is run new years day 2030 i.e. not run.
-system.patch.sharedFolder.cronExpression=0 0 0 ? 1 1 2030
-
-#
-# Default values for deferring the running of the addUnmovableAspect patch
-#
-system.patch.addUnmovableAspect.deferred=false
-system.patch.addUnmovableAspect.cronExpression=0 0 0 ? 1 1 2030
-
-# Property to enable removal of all JBPM related data from the database
-#
-# The tables are not removed from the databasen unless explicitly requested by setting this property to false.
-# See: classpath:alfresco/dbscripts/upgrade/5.2/${db.script.dialect}/remove-jbpm-tables-from-db.sql
-system.remove-jbpm-tables-from-db.ignored=true
-
-#
-# Use a canned query when requested to search for people if " [hint:useCQ]" is provided in search term
-#
-people.search.honor.hint.useCQ=true
-
-# Delays cron jobs after bootstrap to allow server to fully come up before jobs start
-system.cronJob.startDelayMilliseconds=60000
-
-# Schedule for reading mimetype config definitions dynamically. Initially checks every 10 seconds and then switches to
-# every hour after the configuration is read successfully. If there is a error later reading the config, the
-# checks return to every 10 seconds.
-mimetype.config.cronExpression=0 30 0/1 * * ?
-mimetype.config.initialAndOnError.cronExpression=0/10 * * * * ?
-
-# Optional property to specify an external file or directory that will be read for mimetype definitions from YAML
-# files (possibly added to a volume via k8 ConfigMaps).
-mimetype.config.dir=shared/classes/alfresco/extension/mimetypes
-
-# Schedule for reading rendition config definitions dynamically. Initially checks every 10 seconds and then switches to
-# every hour after the configuration is read successfully. If there is a error later reading the config, the
-# checks return to every 10 seconds.
-rendition.config.cronExpression=2 30 0/1 * * ?
-rendition.config.initialAndOnError.cronExpression=0/10 * * * * ?
-
-# Optional property to specify an external file or directory that will be read for rendition definitions from YAML
-# files (possibly added to a volume via k8 ConfigMaps).
-rendition.config.dir=shared/classes/alfresco/extension/transform/renditions
-
-# Optional property to specify an external file or directory that will be read for transformer json config.
-local.transform.pipeline.config.dir=shared/classes/alfresco/extension/transform/pipelines
-
-# Used to disable transforms locally.
-local.transform.service.enabled=true
-
-# Schedule for reading local transform config, so that T-Engines and local pipeline config is dynamically
-# picked up, or reintegrated after an outage. Initially checks every 10 seconds and then switches to every hour
-# after the configuration is read successfully. If there is a error later reading the config, the checks return to
-# every 10 seconds.
-local.transform.service.cronExpression=4 30 0/1 * * ?
-local.transform.service.initialAndOnError.cronExpression=0/10 * * * * ?
-
-#
-# Check that the declared mimetype (of the Node) is the same as the derived
-# mimetype of the content (via Tika) before a transformation takes place.
-# Only files in the repository (not intermediate files in a transformer
-# pipeline) are checked. This property provides a trade off between a
-# security check and a relatively expensive (Tika) operation.
-#
-# There are a few issues with the Tika mimetype detection. So that transformations
-# still take place where the detected mimetype is not the same as the declared mimetype,
-# another property (transformer.strict.mimetype.check.whitelist.mimetypes) contains pairs
-# of declared and detected mimetypes that should be allowed. This parameter value is a
-# sequence of ; separated pairs. The declared and derived mimetypes are also ; separated.
-#
-transformer.strict.mimetype.check=true
-
-# A white list of declared and detected mimetypes, that don't match, but should still be transformed.
-transformer.strict.mimetype.check.whitelist.mimetypes=application/eps;application/postscript;application/illustrator;application/pdf;application/x-tar;application/x-gtar;application/acp;application/zip;application/vnd.stardivision.math;application/x-tika-msoffice
-
-#
-# Enable transformation retrying if the file has MIME type differ than file extension.
-# Ignored if transformer.strict.mimetype.check is true as these transformations
-# will not take place.
-#
-content.transformer.retryOn.different.mimetype=true
-
-# Debug and Log buffer sizes
-transformer.debug.entries=0
-transformer.log.entries=50
-
-#
-# Lock timeout configuration
-#
-system.lockTryTimeout=100
-system.lockTryTimeout.DictionaryDAOImpl=10000
-system.lockTryTimeout.MessageServiceImpl=${system.lockTryTimeout}
-system.lockTryTimeout.PolicyComponentImpl=${system.lockTryTimeout}
-
-
-# Scheduled job to clean up unused properties from the alf_prop_xxx tables.
-# Default setting of "0 0 3 ? * SAT" is to run every Saturday at 3am.
-attributes.propcleaner.cronExpression=0 0 3 ? * SAT
-
-# Control Alfresco JMX connectivity
-alfresco.jmx.connector.enabled=false
-
-# Dissallow Attribute Service Entries with "Serializable" objects in key Segments
-# Please, see MNT-11895 for details.
-system.propval.uniquenessCheck.enabled=true
-
-# Requests for ephemeral (in-memory) locks with expiry times (in seconds) greater
-# than this value will result in persistent locks being created instead. By default
-# this value is equal to the maximum allowed expiry for ephemeral locks, therefore
-# this feature is disabled by default. Setting this to -1 would mean that ALL
-# requests for ephemeral locks would result in persistent locks being created.
-alfresco.ephemeralLock.expiryThresh=172800
-
-# SurfConfigFolder Patch
-#
-# Do we defer running the surf-config folder patch?
-#
-system.patch.surfConfigFolder.deferred=false
-# Default value. i.e. never run. It can be triggered using JMX
-system.patch.surfConfigFolder.cronExpression=* * * * * ? 2099
-
-#
-# Solr Facets Config Properties
-#
-solr_facets.root.path=/app:company_home/app:dictionary
-solr_facets.root=${solr_facets.root.path}/${spaces.solr_facets.root.childname}
-solr_facets.inheritanceHierarchy=default,custom
-
-models.enforceTenantInNamespace=false
-
-# Allowed protocols for links
-links.protocosl.white.list=http,https,ftp,mailto
-
-# Fixed ACLs
-# Required for fixing MNT-15368 - Time Consumed for Updating Folder Permission
-# ADMAccessControlListDAO.setFixedAcls called on a large folder hierarchy will take a long time for its execution.
-# For this reason now method can also be called asynchronously if transaction reaches system.fixedACLs.maxTransactionTime.
-# In this case setFixedAcls method recursion will be stopped and unfinished nodes will be marked with ASPECT_PENDING_FIX_ACL.
-# Pending nodes will be processed by FixedAclUpdater, programmatically called but also configured as a scheduled job.
-system.fixedACLs.maxTransactionTime=10000
-# fixedACLsUpdater - lock time to live
-system.fixedACLsUpdater.lockTTL=10000
-# fixedACLsUpdater - maximum number of nodes to process per execution
-system.fixedACLsUpdater.maxItemBatchSize=100
-# fixedACLsUpdater - the number of threads to use
-system.fixedACLsUpdater.numThreads=4
-# fixedACLsUpdater - Force shared ACL to propagate through children even if there is an unexpected ACL
-system.fixedACLsUpdater.forceSharedACL=false
-# fixedACLsUpdater cron expression - fire at midnight every day
-system.fixedACLsUpdater.cronExpression=0 0 0 * * ?
-
-cmis.disable.hidden.leading.period.files=false
-
-#Smart Folders Config Properties
-smart.folders.enabled=false
-
-#Smart reference config
-smart.reference.classpath.hash=${smart.folders.config.vanilla.processor.classpath}->1,${smart.folders.config.system.templates.classpath}->2
-
-#Smart store config
-
-#Company home relative download associations of smart entries
-smart.download.associations.folder=${spaces.dictionary.childname}/${spaces.smartdownloads.childname}
-
-#Generic virtualization methods config
-
-#Vanilla JSON templates javascript processor classpath. A java script processor used to
-#covert JSON templates to internal smart folder definitions.
-
-smart.folders.config.vanilla.processor.classpath=/org/alfresco/repo/virtual/node/vanilla.js
-
-#System virtualization method config
-
-#System virtualization method aspect.
-smart.folders.config.system.aspect=smf:systemConfigSmartFolder
-#System virtualization method aspect defined template location property.
-smart.folders.config.system.aspect.template.location.property=smf:system-template-location
-#Classpath to be explored for *.json entries defining system templates.
-smart.folders.config.system.templates.classpath=/org/alfresco/repo/virtual/node
-#A company home relative name or qname path location of repository system templates.
-smart.folders.config.system.templates.path=${spaces.dictionary.childname}/${spaces.smartfolders.childname}
-#Content sub type of repository system templates.
-smart.folders.config.system.templates.template.type=smf:smartFolderTemplate
-
-#Custom virtualization method config
-
-#Custom virtualization method aspect.
-smart.folders.config.custom.aspect=smf:customConfigSmartFolder
-#Custom virtualization method aspect template content association.
-smart.folders.config.custom.aspect.template.association=smf:custom-template-association
-
-
-#Type virtualization method config
-
-#A company home relative name or qname path location of the type mapped templates.
-smart.folders.config.type.templates.path=${spaces.dictionary.childname}/${spaces.smartfolders.childname}
-#Type and aspect qname regular expression filter.
-smart.folders.config.type.templates.qname.filter=none
-
-# Preferred password encoding, md4, sha256, bcrypt10
-system.preferred.password.encoding=md4
-
-# Upgrade Password Hash Job
-system.upgradePasswordHash.jobBatchSize=100
-system.upgradePasswordHash.jobQueryRange=10000
-system.upgradePasswordHash.jobThreadCount=4
-system.upgradePasswordHash.jobCronExpression=* * * * * ? 2099
-
-system.api.discovery.enabled=true
-
-# Maximum query size for category/tag fetch when not explicitly set by paging parameters
-category.queryFetchSize=5000
-
-# Brute force protection
-authentication.protection.enabled=true
-authentication.protection.limit=10
-authentication.protection.periodSeconds=6
-
-system.email.sender.default=noreply@alfresco.com
-# reset password workflow will expire in an hour
-system.reset-password.endTimer=PT1H
-system.reset-password.sendEmailAsynchronously=true
-
-# HeartBeat
-heartbeat.target.url=
-heartbeat.enabled=true
-
-# CSRF filter overrides
-csrf.filter.enabled=true
-csrf.filter.referer=
-csrf.filter.referer.always=false
-csrf.filter.origin=
-csrf.filter.origin.always=false
-
-# CORS settings
-cors.enabled=false
-cors.allowed.origins=
-cors.allowed.methods=GET,POST,HEAD,OPTIONS,PUT,DELETE
-cors.allowed.headers=Authorization,Content-Type,Cache-Control,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,X-CSRF-Token
-cors.exposed.headers=Access-Control-Allow-Origin,Access-Control-Allow-Credentials
-cors.support.credentials=true
-cors.preflight.maxage=10
-
-# Alfresco Rest Api-Explorer
-api-explorer.url=
-
-# Events subsystem
-events.subsystem.autoStart=false
-# Messaging subsystem
-messaging.subsystem.autoStart=true
-
-
-# Raw events
-acs.repo.rendition.events.endpoint=jms:acs-repo-rendition-events?jmsMessageType=Text
-
-# Transform request events
-acs.repo.transform.request.endpoint=jms:acs-repo-transform-request?jmsMessageType=Text
-
-# If enabled doesn't allow to set content properties via NodeService
-contentPropertyRestrictions.enabled=true
-contentPropertyRestrictions.whitelist=
-
-# Repo events2
-# Type and aspect filters which should be excluded
-# Note: System folders node types are added by default
-repo.event2.filter.nodeTypes=sys:*, fm:*, cm:thumbnail, cm:failedThumbnail, cm:rating, rma:rmsite include_subtypes
-repo.event2.filter.nodeAspects=sys:*
-repo.event2.filter.childAssocTypes=rn:rendition
-# Comma separated list of users which should be excluded
-# Note: username's case-sensitivity depends on the {user.name.caseSensitive} setting
-repo.event2.filter.users=
-# Topic name
-repo.event2.topic.endpoint=amqp:topic:alfresco.repo.event2
-# Thread pool for async enqueue of repo events
-repo.event2.queue.enqueueThreadPool.priority=1
-repo.event2.queue.enqueueThreadPool.coreSize=8
-repo.event2.queue.enqueueThreadPool.maximumSize=10
-# Thread pool for async dequeue and delivery of repo events
-repo.event2.queue.dequeueThreadPool.priority=1
-repo.event2.queue.dequeueThreadPool.coreSize=1
-repo.event2.queue.dequeueThreadPool.maximumSize=1
-
-
-# MNT-21083
-# --DELETE_NOT_EXISTS - default settings
-system.delete_not_exists.batchsize=100000
-system.delete_not_exists.delete_batchsize=1000
-system.delete_not_exists.read_only=false
-system.delete_not_exists.timeout_seconds=-1
-system.prop_table_cleaner.algorithm=V2
-
-# Configure the expiration time of the direct access url. This is the length of time in seconds that the link is valid for.
-# Note: It is up to the actual ContentStore implementation if it can fulfil this request or not.
-alfresco.content.directAccessUrl.lifetimeInSec=300
-
-# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories.
-system.new-node-transaction-indexes.ignored=true
-
-# Allows the configuration of maximum limits of the temp files to be deleted or the maximum time allowed to run for the job
-system.tempFileCleaner.maxFilesToDelete=
-system.tempFileCleaner.maxTimeToRun=
-
-# Property to long running migration to remove alf_server in v7+ patch.db-V7.1.0-remove-alf_server-table
-system.remove-alf_server-table-from-db.ignored=true
+# Repository configuration
+
+repository.name=Main Repository
+
+# Schema number
+version.schema=16000
+
+# Directory configuration
+
+dir.root=./alf_data
+
+dir.contentstore=${dir.root}/contentstore
+dir.contentstore.deleted=${dir.root}/contentstore.deleted
+dir.contentstore.bucketsPerMinute=0
+
+# ContentStore subsystem: default choice
+filecontentstore.subsystem.name=unencryptedContentStore
+
+# The location of cached content
+dir.cachedcontent=${dir.root}/cachedcontent
+
+# The value for the maximum permitted size in bytes of all content.
+# No value (or a negative long) will be taken to mean that no limit should be applied.
+# See content-services-context.xml
+system.content.maximumFileSizeLimit=
+
+#
+# The server mode. Set value in alfresco-global.properties
+# UNKNOWN | TEST | BACKUP | PRODUCTION
+#
+system.serverMode=UNKNOWN
+
+# The location for lucene index files
+dir.indexes=${dir.root}/lucene-indexes
+
+# The location for index backups
+dir.indexes.backup=${dir.root}/backup-lucene-indexes
+
+# The location for lucene index locks
+dir.indexes.lock=${dir.indexes}/locks
+
+#Directory to find external license
+dir.license.external=.
+# Spring resource location of external license files
+location.license.external=file://${dir.license.external}/*.lic
+# Spring resource location of embedded license files
+location.license.embedded=/WEB-INF/alfresco/license/*.lic
+# Spring resource location of license files on shared classpath
+location.license.shared=classpath*:/alfresco/extension/license/*.lic
+
+# WebDAV initialization properties
+system.webdav.servlet.enabled=true
+system.webdav.url.path.prefix=
+system.webdav.storeName=${protocols.storeName}
+system.webdav.rootPath=${protocols.rootPath}
+# File name patterns that trigger rename shuffle detection
+# pattern is used by move - tested against full path after it has been lower cased.
+system.webdav.renameShufflePattern=(.*/\\..*)|(.*[a-f0-9]{8}+$)|(.*\\.tmp$)|(.*atmp[0-9]+$)|(.*\\.wbk$)|(.*\\.bak$)|(.*\\~$)|(.*backup.*\\.do[ct]{1}[x]?[m]?$)|(.*\\.sb\\-\\w{8}\\-\\w{6}$)
+system.webdav.activities.enabled=false
+
+
+system.workflow.jbpm.comment.property.max.length=-1
+system.workflow.comment.property.max.length=4000
+
+#Determines if Activiti definitions are visible
+system.workflow.engine.activiti.definitions.visible=true
+
+
+# Determines if the Activiti engine is enabled
+system.workflow.engine.activiti.enabled=true
+system.workflow.engine.activiti.idblocksize=100
+system.workflow.engine.activiti.taskvariableslimit=20000
+
+# Determines if the workflows that are deployed to the activiti engine should
+# be deployed in the tenant-context of the thread IF the tenant-service is enabled
+# If set to false, all workflows deployed will be shared among tenants. Recommended
+# setting is true unless there is a good reason to not allow deploy tenant-specific
+# worklfows when a MT-environment is set up.
+system.workflow.deployWorkflowsInTenant=true
+#Determines if historic process instance are retained in case of canceling a process instance
+system.workflow.engine.activiti.retentionHistoricProcessInstance=false
+
+# The maximum number of groups to check for pooled tasks. For performance
+# reasons, this is limited to 500 by default.
+system.workflow.maxAuthoritiesForPooledTasks=500
+
+# The maximum number of pooled tasks to return in a query. It may be necessary
+# to limit this depending on UI limitations.
+system.workflow.maxPooledTasks=-1
+
+# The maximum number of reviewers for "Group Review and Approve" workflow.
+# Use '0' for unlimited.
+system.workflow.maxGroupReviewers=0
+
+index.subsystem.name=noindex
+
+# ######################################### #
+# Index Tracking Configuration #
+# ######################################### #
+#
+# Index tracking information of a certain age is cleaned out by a scheduled job.
+# Any clustered system that has been offline for longer than this period will need to be seeded
+# with a more recent backup of the Lucene indexes or the indexes will have to be fully rebuilt.
+# Use -1 to disable purging. This can be switched on at any stage.
+index.tracking.minRecordPurgeAgeDays=30
+# Unused transactions will be purged in chunks determined by commit time boundaries. 'index.tracking.purgeSize' specifies the size
+# of the chunk (in ms). Default is a couple of hours.
+index.tracking.purgeSize=7200000
+
+# Change the failure behaviour of the configuration checker
+system.bootstrap.config_check.strict=true
+
+
+#
+# How long should shutdown wait to complete normally before
+# taking stronger action and calling System.exit()
+# in ms, 10,000 is 10 seconds
+#
+shutdown.backstop.timeout=10000
+shutdown.backstop.enabled=false
+
+# Server Single User Mode
+# note:
+# only allow named user (note: if blank or not set then will allow all users)
+# assuming maxusers is not set to 0
+#server.singleuseronly.name=admin
+
+# Server Max Users - limit number of users with non-expired tickets
+# note:
+# -1 allows any number of users, assuming not in single-user mode
+# 0 prevents further logins, including the ability to enter single-user mode
+server.maxusers=-1
+
+#
+# Disable all shared caches (mutable and immutable)
+# These properties are used for diagnostic purposes
+system.cache.disableMutableSharedCaches=false
+system.cache.disableImmutableSharedCaches=false
+
+# The maximum capacity of the parent assocs cache (the number of nodes whose parents can be cached)
+system.cache.parentAssocs.maxSize=130000
+
+# The average number of parents expected per cache entry. This parameter is multiplied by the above
+# value to compute a limit on the total number of cached parents, which will be proportional to the
+# cache's memory usage. The cache will be pruned when this limit is exceeded to avoid excessive
+# memory usage.
+system.cache.parentAssocs.limitFactor=8
+
+#
+# Properties to limit resources spent on individual searches
+#
+# The maximum time spent pruning results
+system.acl.maxPermissionCheckTimeMillis=10000
+# The maximum number of search results to perform permission checks against
+system.acl.maxPermissionChecks=1000
+system.acl.maxPermissionCheckEnabled=false
+
+# The maximum number of filefolder list results
+system.filefolderservice.defaultListMaxResults=5000
+# DEPRECATED: Use 'system.auditableData.preserve'
+system.preserve.modificationData=false
+# The default to preserve all cm:auditable data on a node when the process is not directly driven by a user action
+system.auditableData.preserve=${system.preserve.modificationData}
+# Specific control of how the FileFolderService treats cm:auditable data when performing moves
+system.auditableData.FileFolderService=${system.auditableData.preserve}
+# Specific control of whether ACL changes on a node trigger the cm:auditable aspect
+system.auditableData.ACLs=${system.auditableData.preserve}
+
+# Properties to control read permission evaluation for acegi
+system.readpermissions.optimise=true
+system.readpermissions.bulkfetchsize=1000
+
+#
+# Manually control how the system handles maximum string lengths.
+# Any zero or negative value is ignored.
+# Only change this after consulting support or reading the appropriate Javadocs for
+# org.alfresco.repo.domain.schema.SchemaBootstrap for V2.1.2.
+# Before database migration, the string value storage may need to be adjusted using the scheduled job
+system.maximumStringLength=-1
+system.maximumStringLength.jobCronExpression=* * * * * ? 2099
+system.maximumStringLength.jobQueryRange=10000
+system.maximumStringLength.jobThreadCount=4
+
+#
+# Limit hibernate session size by trying to amalgamate events for the L2 session invalidation
+# - hibernate works as is up to this size
+# - after the limit is hit events that can be grouped invalidate the L2 cache by type and not instance
+# events may not group if there are post action listener registered (this is not the case with the default distribution)
+system.hibernateMaxExecutions=20000
+
+#
+# Determine if modification timestamp propagation from child to parent nodes is respected or not.
+# Even if 'true', the functionality is only supported for child associations that declare the
+# 'propagateTimestamps' element in the dictionary definition.
+system.enableTimestampPropagation=true
+
+#
+# Enable system model integrity checking.
+# WARNING: Changing this is unsupported; bugs may corrupt data
+system.integrity.enabled=true
+# Do integrity violations fail transactions
+# WARNING: Changing this is unsupported; bugs may corrupt data
+system.integrity.failOnViolation=true
+# The number of errors to report when violations are detected
+system.integrity.maxErrorsPerTransaction=5
+# Add call stacks to integrity events so that errors are logged with possible causes
+# WARNING: This is expensive and should only be switched on for diagnostic purposes
+system.integrity.trace=false
+
+#
+# Decide if content should be removed from the system immediately after being orphaned.
+# Do not change this unless you have examined the impact it has on your backup procedures.
+system.content.eagerOrphanCleanup=false
+# The number of days to keep orphaned content in the content stores.
+# This has no effect on the 'deleted' content stores, which are not automatically emptied.
+system.content.orphanProtectDays=14
+# The action to take when a store or stores fails to delete orphaned content
+# IGNORE: Just log a warning. The binary remains and the record is expunged
+# KEEP_URL: Log a warning and create a URL entry with orphan time 0. It won't be processed or removed.
+system.content.deletionFailureAction=IGNORE
+# The CRON expression to trigger the deletion of resources associated with orphaned content.
+system.content.orphanCleanup.cronExpression=0 0 4 * * ?
+# The batch size user by the content store cleaner
+system.content.cleanerBatchSize=1000
+
+# The CRON expression to trigger the cleanup of deleted nodes and dangling transactions that are old enough
+system.nodeServiceCleanup.cronExpression=0 0 21 * * ?
+
+# When transforming archive files (.zip etc) into text representations (such as
+# for full text indexing), should the files within the archive be processed too?
+# If enabled, transformation takes longer, but searches of the files find more.
+transformer.Archive.includeContents=false
+
+# Database configuration
+db.schema.name=
+db.schema.stopAfterSchemaBootstrap=false
+db.schema.update=true
+db.schema.update.lockRetryCount=24
+db.schema.update.lockRetryWaitSeconds=5
+db.driver=org.gjt.mm.mysql.Driver
+db.name=alfresco
+db.url=jdbc:mysql:///${db.name}
+db.username=alfresco
+db.password=alfresco
+db.pool.initial=10
+db.pool.max=275
+db.txn.isolation=-1
+db.pool.statements.enable=true
+db.pool.statements.max=40
+db.pool.min=10
+db.pool.idle=10
+db.pool.wait.max=5000
+
+db.pool.validate.query=
+db.pool.evict.interval=600000
+db.pool.evict.idle.min=1800000
+#
+# note: for 'db.pool.evict.num.tests' see http://commons.apache.org/dbcp/configuration.html (numTestsPerEvictionRun)
+# and also following extract from "org.apache.commons.pool.impl.GenericKeyedObjectPool" (1.5.5)
+#
+# * The number of objects to examine during each run of the idle object evictor thread (if any).
+# * When a negative value is supplied, ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})
+# * tests will be run. I.e., when the value is -n, roughly one nth of the
+# * idle objects will be tested per run.
+#
+db.pool.evict.num.tests=-1
+
+db.pool.evict.validate=false
+db.pool.validate.borrow=true
+db.pool.validate.return=false
+
+db.pool.abandoned.detect=false
+db.pool.abandoned.time=300
+#
+# db.pool.abandoned.log=true (logAbandoned) adds overhead (http://commons.apache.org/dbcp/configuration.html)
+# and also requires db.pool.abandoned.detect=true (removeAbandoned)
+#
+db.pool.abandoned.log=false
+
+
+# Audit configuration
+audit.enabled=true
+audit.tagging.enabled=true
+audit.alfresco-access.enabled=false
+audit.alfresco-access.sub-actions.enabled=false
+audit.cmischangelog.enabled=false
+audit.dod5015.enabled=false
+# Setting this flag to true will force startup failure when invalid audit configurations are detected
+audit.config.strict=false
+# Audit map filter for AccessAuditor - restricts recorded events to user driven events
+audit.filter.alfresco-access.default.enabled=false
+audit.filter.alfresco-access.transaction.user=~System;~null;.*
+audit.filter.alfresco-access.transaction.type=cm:folder;cm:content;st:site
+audit.filter.alfresco-access.transaction.path=~/sys:archivedItem;~/ver:;.*
+
+
+# System Configuration
+system.store=system://system
+system.descriptor.childname=sys:descriptor
+system.descriptor.current.childname=sys:descriptor-current
+
+# User config
+alfresco_user_store.store=user://alfrescoUserStore
+alfresco_user_store.system_container.childname=sys:system
+alfresco_user_store.user_container.childname=sys:people
+
+# note: default admin username - should not be changed after installation
+alfresco_user_store.adminusername=admin
+
+# Initial password - editing this will not have any effect once the repository is installed
+alfresco_user_store.adminpassword=209c6174da490caeb422f3fa5a7ae634
+
+# note: default guest username - should not be changed after installation
+alfresco_user_store.guestusername=guest
+
+# Used to move home folders to a new location
+home_folder_provider_synchronizer.enabled=false
+home_folder_provider_synchronizer.override_provider=
+home_folder_provider_synchronizer.keep_empty_parents=false
+
+# Spaces Archive Configuration
+spaces.archive.store=archive://SpacesStore
+
+# Spaces Configuration
+spaces.store=workspace://SpacesStore
+spaces.company_home.childname=app:company_home
+spaces.guest_home.childname=app:guest_home
+spaces.dictionary.childname=app:dictionary
+spaces.templates.childname=app:space_templates
+spaces.imap_attachments.childname=cm:Imap Attachments
+spaces.imap_home.childname=cm:Imap Home
+spaces.imapConfig.childname=app:imap_configs
+spaces.imap_templates.childname=app:imap_templates
+spaces.scheduled_actions.childname=cm:Scheduled Actions
+spaces.emailActions.childname=app:email_actions
+spaces.searchAction.childname=cm:search
+spaces.templates.content.childname=app:content_templates
+spaces.templates.email.childname=app:email_templates
+spaces.templates.email.invite1.childname=app:invite_email_templates
+spaces.templates.email.notify.childname=app:notify_email_templates
+spaces.templates.email.following.childname=app:following
+spaces.templates.rss.childname=app:rss_templates
+spaces.savedsearches.childname=app:saved_searches
+spaces.scripts.childname=app:scripts
+spaces.content_forms.childname=app:forms
+spaces.user_homes.childname=app:user_homes
+spaces.user_homes.regex.key=userName
+spaces.user_homes.regex.pattern=
+spaces.user_homes.regex.group_order=
+spaces.sites.childname=st:sites
+spaces.templates.email.invite.childname=cm:invite
+spaces.templates.email.activities.childname=cm:activities
+spaces.rendition.rendering_actions.childname=app:rendering_actions
+spaces.replication.replication_actions.childname=app:replication_actions
+spaces.transfers.childname=app:transfers
+spaces.transfer_groups.childname=app:transfer_groups
+spaces.transfer_temp.childname=app:temp
+spaces.inbound_transfer_records.childname=app:inbound_transfer_records
+spaces.webscripts.childname=cm:webscripts
+spaces.extension_webscripts.childname=cm:extensionwebscripts
+spaces.models.childname=app:models
+spaces.workflow.definitions.childname=app:workflow_defs
+spaces.templates.email.workflowemailnotification.childname=cm:workflownotification
+spaces.nodetemplates.childname=app:node_templates
+spaces.shared.childname=app:shared
+spaces.solr_facets.root.childname=srft:facets
+spaces.smartfolders.childname=app:smart_folders
+spaces.smartdownloads.childname=app:smart_downloads
+spaces.transfer_summary_report.location=/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.inbound_transfer_records.childname}
+spaces.quickshare.link_expiry_actions.childname=app:quick_share_link_expiry_actions
+
+
+# ADM VersionStore Configuration
+version.store.initialVersion=true
+version.store.enableAutoVersioning=true
+version.store.enableAutoVersionOnUpdateProps=false
+version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore
+version.store.version2Store=workspace://version2Store
+
+# Optional Comparator class name to sort versions.
+# Set to: org.alfresco.repo.version.common.VersionLabelComparator
+# if upgrading from a version that used unordered sequences in a cluster.
+version.store.versionComparatorClass=
+
+# Folders for storing people
+system.system_container.childname=sys:system
+system.people_container.childname=sys:people
+system.authorities_container.childname=sys:authorities
+system.zones_container.childname=sys:zones
+
+# Folders for storing workflow related info
+system.workflow_container.childname=sys:workflow
+
+# Folder for storing shared remote credentials
+system.remote_credentials_container.childname=sys:remote_credentials
+
+# Folder for storing syncset definitions
+system.syncset_definition_container.childname=sys:syncset_definitions
+
+# Folder for storing download archives
+system.downloads_container.childname=sys:downloads
+
+# Folder for storing IdP's certificate definitions
+system.certificate_container.childname=sys:samlcertificate
+
+# Are user names case sensitive?
+user.name.caseSensitive=false
+domain.name.caseSensitive=false
+domain.separator=
+
+#Format caption extracted from the XML Schema.
+xforms.formatCaption=true
+
+# ECM content usages/quotas
+system.usages.enabled=false
+system.usages.clearBatchSize=0
+system.usages.updateBatchSize=50
+
+# Repository endpoint - used by Activity Service
+repo.remote.endpoint=/service
+
+# Some authentication mechanisms may need to create people
+# in the repository on demand. This enables that feature.
+# If disabled an error will be generated for missing
+# people. If enabled then a person will be created and
+# persisted.
+create.missing.people=${server.transaction.allow-writes}
+
+# Create home folders (unless disabled, see next property) as people are created (true) or create them lazily (false)
+home.folder.creation.eager=true
+# Disable home folder creation - if true then home folders are not created (neither eagerly nor lazily)
+home.folder.creation.disabled=false
+
+# Should we consider zero byte content to be the same as no content when firing
+# content update policies? Prevents 'premature' firing of inbound content rules
+# for some clients such as Mac OS X Finder
+policy.content.update.ignoreEmpty=true
+
+# Default value of alfresco.rmi.services.host is 0.0.0.0 which means 'listen on all adapters'.
+# This allows connections to JMX both remotely and locally.
+#
+alfresco.rmi.services.port=50500
+alfresco.rmi.services.external.host=localhost
+alfresco.rmi.services.host=0.0.0.0
+
+# If the RMI address is in-use, how many retries should be done before aborting
+# Default value of alfresco.rmi.services.retries is 0 which means 'Don't retry if the address is in-use'
+alfresco.rmi.services.retries=4
+# How long in milliseconds to wait after a failed server socket bind, before retrying
+alfresco.rmi.services.retryInterval=250
+
+# RMI service ports for the individual services.
+# These eight services are available remotely.
+#
+# Assign individual ports for each service for best performance
+# or run several services on the same port, you can even run everything on 50500 if
+# running through a firewall.
+#
+# Specify 0 to use a random unused port.
+#
+monitor.rmi.service.port=50508
+
+#
+# enable or disable individual RMI services
+#
+monitor.rmi.service.enabled=false
+
+
+# Should the Mbean server bind to an existing server. Set to true for most application servers.
+# false for WebSphere clusters.
+mbean.server.locateExistingServerIfPossible=true
+
+# Rendition Service 2
+renditionService2.enabled=true
+
+# Thumbnail Service
+system.thumbnail.generate=true
+
+# when creating doc via CMIS - optionally configure set of renditions names to request async
+cmis.create.doc.request.renditions.set=
+
+# Default thumbnail limits
+# When creating thumbnails, only use the first pageLimit pages
+system.thumbnail.definition.default.timeoutMs=-1
+system.thumbnail.definition.default.readLimitTimeMs=-1
+system.thumbnail.definition.default.maxSourceSizeKBytes=-1
+system.thumbnail.definition.default.readLimitKBytes=-1
+system.thumbnail.definition.default.pageLimit=-1
+system.thumbnail.definition.default.maxPages=-1
+
+# Max mimetype sizes to create thumbnail icons
+system.thumbnail.mimetype.maxSourceSizeKBytes.pdf=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.txt=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.docx=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.xlsx=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.pptx=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.odt=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.ods=-1
+system.thumbnail.mimetype.maxSourceSizeKBytes.odp=-1
+
+# Configuration for handling of failing thumbnails.
+# See NodeEligibleForRethumbnailingEvaluator's javadoc for details.
+#
+# Retry periods limit the frequency with which the repository will attempt to create Share thumbnails
+# for content nodes which have previously failed in their thumbnail attempts.
+# These periods are in seconds.
+#
+# 604800s = 60s * 60m * 24h * 7d = 1 week
+system.thumbnail.retryPeriod=60
+system.thumbnail.retryCount=2
+system.thumbnail.quietPeriod=604800
+system.thumbnail.quietPeriodRetriesEnabled=true
+system.thumbnail.redeployStaticDefsOnStartup=true
+
+content.metadata.async.extract.enabled=true
+content.metadata.async.embed.enabled=true
+
+# The default timeout for metadata mapping extracters
+content.metadataExtracter.default.timeoutMs=20000
+
+# Local transformer urls to T-engines to service transform requests via http. Enabled by default.
+localTransform.core-aio.url=http://localhost:8090/
+
+# When a local transformer .url is set, this value indicates the amount of time to wait after a connection failure
+# before retrying the connection to allow a docker container to (re)start.
+localTransform.core-aio.startupRetryPeriodSeconds=60
+
+# Property to enable upgrade from 2.1-A
+V2.1-A.fixes.to.schema=0
+#V2.1-A.fixes.to.schema=82
+
+# The default authentication chain
+authentication.chain=alfrescoNtlm1:alfrescoNtlm
+
+# Do authentication tickets expire or live for ever?
+authentication.ticket.ticketsExpire=true
+
+# If ticketsEpire is true then how they should expire?
+# Valid values are: AFTER_INACTIVITY, AFTER_FIXED_TIME, DO_NOT_EXPIRE
+# The default is AFTER_FIXED_TIME
+authentication.ticket.expiryMode=AFTER_INACTIVITY
+
+# If authentication.ticket.ticketsExpire is true and
+# authentication.ticket.expiryMode is AFTER_FIXED_TIME or AFTER_INACTIVITY,
+# this controls the minimum period for which tickets are valid.
+# The default is PT1H for one hour.
+authentication.ticket.validDuration=PT1H
+
+# Use one ticket for all user sessions
+# For the pre 4.2 behaviour of one ticket per session set this to false.
+authentication.ticket.useSingleTicketPerUser=true
+
+authentication.alwaysAllowBasicAuthForAdminConsole.enabled=true
+authentication.getRemoteUserTimeoutMilliseconds=10000
+
+# FTP access
+ftp.enabled=false
+
+# Default root path for protocols
+protocols.storeName=${spaces.store}
+protocols.rootPath=/${spaces.company_home.childname}
+
+# OpenCMIS
+opencmis.connector.default.store=${spaces.store}
+opencmis.connector.default.rootPath=/${spaces.company_home.childname}
+opencmis.connector.default.typesDefaultMaxItems=500
+opencmis.connector.default.typesDefaultDepth=-1
+opencmis.connector.default.objectsDefaultMaxItems=10000
+opencmis.connector.default.objectsDefaultDepth=100
+opencmis.connector.default.contentChangesDefaultMaxItems=10000
+opencmis.connector.default.openHttpSession=false
+opencmis.activities.enabled=true
+opencmis.bulkUpdateProperties.maxItemsSize=1000
+opencmis.bulkUpdateProperties.batchSize=20
+opencmis.bulkUpdateProperties.workerThreads=2
+opencmis.maxContentSizeMB=4096
+opencmis.memoryThresholdKB=4096
+
+# URL generation overrides
+
+# if true, the context path of OpenCMIS generated urls will be set to "opencmis.context.value", otherwise it will be taken from the request url
+opencmis.context.override=false
+opencmis.context.value=
+# if true, the servlet path of OpenCMIS generated urls will be set to "opencmis.servletpath.value", otherwise it will be taken from the request url
+opencmis.servletpath.override=false
+opencmis.servletpath.value=
+opencmis.server.override=false
+opencmis.server.value=
+
+# IMAP
+imap.server.enabled=false
+imap.server.port=143
+imap.server.attachments.extraction.enabled=true
+
+# Default IMAP mount points
+imap.config.home.store=${spaces.store}
+imap.config.home.rootPath=/${spaces.company_home.childname}
+imap.config.home.folderPath=${spaces.imap_home.childname}
+imap.config.server.mountPoints=AlfrescoIMAP
+imap.config.server.mountPoints.default.mountPointName=IMAP
+imap.config.server.mountPoints.default.modeName=ARCHIVE
+imap.config.server.mountPoints.default.store=${spaces.store}
+imap.config.server.mountPoints.default.rootPath=${protocols.rootPath}
+imap.config.server.mountPoints.value.AlfrescoIMAP.mountPointName=Alfresco IMAP
+imap.config.server.mountPoints.value.AlfrescoIMAP.modeName=MIXED
+
+#Imap extraction settings
+#imap.attachments.mode:
+# SEPARATE -- All attachments for each email will be extracted to separate folder.
+# COMMON -- All attachments for all emails will be extracted to one folder.
+# SAME -- Attachments will be extracted to the same folder where email lies.
+imap.attachments.mode=SEPARATE
+imap.attachments.folder.store=${spaces.store}
+imap.attachments.folder.rootPath=/${spaces.company_home.childname}
+imap.attachments.folder.folderPath=${spaces.imap_attachments.childname}
+
+# Activities Feed - refer to subsystem
+
+# Feed max ID range to limit maximum number of entries
+activities.feed.max.idRange=1000000
+# Feed max size (number of entries)
+activities.feed.max.size=200
+# Feed max age (eg. 44640 mins => 31 days)
+activities.feed.max.ageMins=44640
+
+activities.feed.generator.jsonFormatOnly=true
+activities.feed.fetchBatchSize=250
+activities.feedNotifier.batchSize=200
+activities.feedNotifier.numThreads=2
+
+# Subsystem unit test values. Will not have any effect on production servers
+subsystems.test.beanProp.default.longProperty=123456789123456789
+subsystems.test.beanProp.default.anotherStringProperty=Global Default
+subsystems.test.beanProp=inst1,inst2,inst3
+subsystems.test.beanProp.value.inst2.boolProperty=true
+subsystems.test.beanProp.value.inst3.anotherStringProperty=Global Instance Default
+subsystems.test.simpleProp2=true
+subsystems.test.simpleProp3=Global Default3
+
+# Default Async Action Thread Pool
+default.async.action.threadPriority=1
+default.async.action.corePoolSize=8
+default.async.action.maximumPoolSize=20
+
+# Deployment Service
+deployment.service.numberOfSendingThreads=5
+deployment.service.corePoolSize=2
+deployment.service.maximumPoolSize=3
+deployment.service.threadPriority=5
+# How long to wait in mS before refreshing a target lock - detects shutdown servers
+deployment.service.targetLockRefreshTime=60000
+# How long to wait in mS from the last communication before deciding that deployment has failed, possibly
+# the destination is no longer available?
+deployment.service.targetLockTimeout=3600000
+# Deployment method used to deploy this Alfresco instance (DEFAULT, INSTALLER, DOCKER_COMPOSE, HELM_CHART, ANSIBLE, ZIP, QUICK_START)
+deployment.method=DEFAULT
+
+#Invitation Service
+# Should send emails as part of invitation process.
+notification.email.siteinvite=true
+# Moderated invite Activiti workflow
+site.invite.moderated.workflowId=activiti$activitiInvitationModerated
+# Add intneral users Activiti workflow (use activiti$activitiInvitationNominated to revert to requiring accept of invite for internal users)
+site.invite.nominated.workflowId=activiti$activitiInvitationNominatedAddDirect
+# Add external users Activiti workflow
+site.invite.nominatedExternal.workflowId=activiti$activitiInvitationNominated
+
+# Replication Service
+replication.enabled=false
+
+# Transfer Service
+transferservice.receiver.enabled=false
+transferservice.receiver.stagingDir=${java.io.tmpdir}/alfresco-transfer-staging
+#
+# How long to wait in mS before refreshing a transfer lock - detects shutdown servers
+# Default 1 minute.
+transferservice.receiver.lockRefreshTime=60000
+#
+# How many times to attempt retry the transfer lock
+transferservice.receiver.lockRetryCount=3
+# How long to wait, in mS, before retrying the transfer lock
+transferservice.receiver.lockRetryWait=100
+#
+# How long to wait, in mS, since the last contact with from the client before
+# timing out a transfer. Needs to be long enough to cope with network delays and "thinking
+# time" for both source and destination. Default 5 minutes.
+transferservice.receiver.lockTimeOut=300000
+
+# OrphanReaper
+orphanReaper.lockRefreshTime=60000
+orphanReaper.lockTimeOut=3600000
+
+
+# security
+security.anyDenyDenies=true
+# Whether to post-process denies. Only applies to solr4+ when anyDenyDenies is true.
+security.postProcessDenies=false
+
+#
+# Encryption properties
+#
+# default keystores location
+dir.keystore=classpath:alfresco/keystore
+
+# general encryption parameters
+encryption.keySpec.class=org.alfresco.encryption.DESEDEKeyGenerator
+encryption.keyAlgorithm=AES
+encryption.cipherAlgorithm=AES/CBC/PKCS5Padding
+
+# secret key keystore configuration
+encryption.keystore.location=${dir.keystore}/keystore
+# configuration via metadata is deprecated
+encryption.keystore.keyMetaData.location=
+encryption.keystore.provider=
+encryption.keystore.type=pkcs12
+
+# backup secret key keystore configuration
+encryption.keystore.backup.location=${dir.keystore}/backup-keystore
+# configuration via metadata is deprecated
+encryption.keystore.backup.keyMetaData.location=
+encryption.keystore.backup.provider=
+encryption.keystore.backup.type=pkcs12
+
+# Should encryptable properties be re-encrypted with new encryption keys on botstrap?
+encryption.bootstrap.reencrypt=false
+
+# mac/md5 encryption
+encryption.mac.messageTimeout=30000
+encryption.mac.algorithm=HmacSHA1
+
+# ssl encryption
+encryption.ssl.keystore.location=${dir.keystore}/ssl.keystore
+encryption.ssl.keystore.provider=
+encryption.ssl.keystore.type=JCEKS
+# configuration via metadata is deprecated
+encryption.ssl.keystore.keyMetaData.location=
+encryption.ssl.truststore.location=${dir.keystore}/ssl.truststore
+encryption.ssl.truststore.provider=
+encryption.ssl.truststore.type=JCEKS
+# configuration via metadata is deprecated
+encryption.ssl.truststore.keyMetaData.location=
+
+# Re-encryptor properties
+encryption.reencryptor.chunkSize=100
+encryption.reencryptor.numThreads=2
+
+# SOLR connection details (e.g. for JMX)
+solr.host=localhost
+solr.port=8983
+solr.port.ssl=8984
+solr.solrUser=solr
+solr.solrPassword=solr
+# none, https
+solr.secureComms=https
+solr.sharedSecret=
+solr.sharedSecret.header=X-Alfresco-Search-Secret
+solr.cmis.alternativeDictionary=DEFAULT_DICTIONARY
+
+solr.max.total.connections=40
+solr.max.host.connections=40
+
+# Solr connection timeouts
+# solr connect timeout in ms
+solr.solrConnectTimeout=5000
+
+# cron expression defining how often the Solr Admin client (used by JMX) pings Solr if it goes away
+solr.solrPingCronExpression=0 0/5 * * * ? *
+
+
+#Default SOLR store mappings mappings
+solr.store.mappings=solrMappingAlfresco,solrMappingArchive
+solr.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
+solr.store.mappings.value.solrMappingAlfresco.baseUrl=/solr/alfresco
+solr.store.mappings.value.solrMappingAlfresco.protocol=workspace
+solr.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
+solr.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
+solr.store.mappings.value.solrMappingArchive.baseUrl=/solr/archive
+solr.store.mappings.value.solrMappingArchive.protocol=archive
+solr.store.mappings.value.solrMappingArchive.identifier=SpacesStore
+
+#Default SOLR 4 store mappings mappings
+solr4.store.mappings=solrMappingAlfresco,solrMappingArchive
+solr4.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
+solr4.store.mappings.value.solrMappingAlfresco.baseUrl=/solr4/alfresco
+solr4.store.mappings.value.solrMappingAlfresco.protocol=workspace
+solr4.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
+solr4.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
+solr4.store.mappings.value.solrMappingArchive.baseUrl=/solr4/archive
+solr4.store.mappings.value.solrMappingArchive.protocol=archive
+solr4.store.mappings.value.solrMappingArchive.identifier=SpacesStore
+
+#Default SOLR 6 store mappings mappings
+solr6.store.mappings=solrMappingAlfresco,solrMappingArchive,solrMappingHistory
+solr6.store.mappings.value.solrMappingAlfresco.httpClientFactory=solrHttpClientFactory
+solr6.store.mappings.value.solrMappingAlfresco.baseUrl=/solr/alfresco
+solr6.store.mappings.value.solrMappingAlfresco.protocol=workspace
+solr6.store.mappings.value.solrMappingAlfresco.identifier=SpacesStore
+solr6.store.mappings.value.solrMappingArchive.httpClientFactory=solrHttpClientFactory
+solr6.store.mappings.value.solrMappingArchive.baseUrl=/solr/archive
+solr6.store.mappings.value.solrMappingArchive.protocol=archive
+solr6.store.mappings.value.solrMappingArchive.identifier=SpacesStore
+solr6.store.mappings.value.solrMappingHistory.httpClientFactory=solrHttpClientFactory
+solr6.store.mappings.value.solrMappingHistory.baseUrl=/solr/history
+solr6.store.mappings.value.solrMappingHistory.protocol=workspace
+solr6.store.mappings.value.solrMappingHistory.identifier=history
+
+#
+# URL Shortening Properties
+#
+urlshortening.bitly.username=brianalfresco
+urlshortening.bitly.api.key=R_ca15c6c89e9b25ccd170bafd209a0d4f
+urlshortening.bitly.url.length=20
+
+#
+# Bulk Filesystem Importer
+#
+
+# The number of threads to employ in a batch import
+bulkImport.batch.numThreads=4
+
+# The size of a batch in a batch import i.e. the number of files to import in a
+# transaction/thread
+bulkImport.batch.batchSize=20
+
+
+#
+# Caching Content Store
+#
+system.content.caching.cacheOnInbound=true
+system.content.caching.maxDeleteWatchCount=1
+# Clean up every day at 3 am
+system.content.caching.contentCleanup.cronExpression=0 0 3 * * ?
+system.content.caching.minFileAgeMillis=60000
+system.content.caching.maxUsageMB=4096
+# maxFileSizeMB - 0 means no max file size.
+system.content.caching.maxFileSizeMB=0
+# When the CachingContentStore is about to write a cache file but the disk usage is in excess of panicThresholdPct
+# (default 90%) then the cache file is not written and the cleaner is started (if not already running) in a new thread.
+system.content.caching.panicThresholdPct=90
+# When a cache file has been written that results in cleanThresholdPct (default 80%) of maxUsageBytes
+# being exceeded then the cached content cleaner is invoked (if not already running) in a new thread.
+system.content.caching.cleanThresholdPct=80
+# An aggressive cleaner is run till the targetUsagePct (default 70%) of maxUsageBytes is achieved
+system.content.caching.targetUsagePct=70
+# Threshold in seconds indicating a minimal gap between normal cleanup starts
+system.content.caching.normalCleanThresholdSec=0
+
+mybatis.useLocalCaches=false
+
+fileFolderService.checkHidden.enabled=true
+
+
+ticket.cleanup.cronExpression=0 0 * * * ?
+
+#
+# Download Service Cleanup
+#
+download.cleaner.startDelayMilliseconds=3600000
+# 1 hour
+download.cleaner.repeatIntervalMilliseconds=3600000
+download.cleaner.maxAgeMins=60
+# -1 or 0 for not using batches
+download.cleaner.batchSize=1000
+
+# you could set this to false for new installations greater then ACS 6.2
+# see MNT-20212
+download.cleaner.cleanAllSysDownloadFolders=true
+
+#
+# Download Service Limits, in bytes
+#
+download.maxContentSize=2152852358
+
+# Max size of view trashcan files
+#
+trashcan.MaxSize=1000
+
+#
+# Use bridge tables for caching authority evaluation.
+#
+authority.useBridgeTable=true
+
+# Limit the number of results from findAuthority query
+authority.findAuthorityLimit=10000
+
+# enable QuickShare - if false then the QuickShare-specific REST APIs will return 403 Forbidden
+system.quickshare.enabled=true
+system.quickshare.email.from.default=noreply@alfresco.com
+# By default the difference between the quick share expiry date and the current time must be at least 1 day (24 hours).
+# However, this can be changed to at least 1 hour or 1 minute for testing purposes. For example,
+# setting the value to MINUTES, means the service will calculate the difference between NOW and the given expiry date
+# in terms of minutes and checks for the difference to be greater than 1 minute.
+# DAYS | HOURS | MINUTES
+system.quickshare.expiry_date.enforce.minimum.period=DAYS
+
+# Oubound Mail
+mail.service.corePoolSize=8
+mail.service.maximumPoolSize=20
+
+nodes.bulkLoad.cachingThreshold=10
+
+# Multi-Tenancy
+
+# if "dir.contentstore.tenants" is set then
+# tenants are not co-mingled and all content roots will appear below this container (in sub-folder)
+# and when creating a tenant the "contentRootPath" (root content store directory for a given tenant) will be ignored
+dir.contentstore.tenants=
+
+# Gateway Authentication
+# gateway authentication is disabled if empty host is specified
+alfresco.authentication.gateway.host=
+alfresco.authentication.gateway.protocol=https
+alfresco.authentication.gateway.port=443
+alfresco.authentication.gateway.outboundHeaders=Authorization,key
+alfresco.authentication.gateway.inboundHeaders=X-Alfresco-Authenticator-Key,X-Alfresco-Remote-User
+alfresco.authentication.gateway.prefixUrl=/publicapi
+alfresco.authentication.gateway.bufferSize=2048
+alfresco.authentication.gateway.connectTimeout=10000
+alfresco.authentication.gateway.readTimeout=120000
+alfresco.authentication.gateway.httpTcpNodelay=true
+alfresco.authentication.gateway.httpConnectionStalecheck=true
+
+# webscripts config
+webscripts.encryptTempFiles=false
+webscripts.tempDirectoryName=Alfresco-WebScripts
+# 4mb
+webscripts.memoryThreshold=4194304
+# 4gb
+webscripts.setMaxContentSize=5368709120
+
+# Property to enable index upgrade for metadata query (MDQ)
+#
+# The indexes are not added unless this value is changed
+# Adding each the supporting indexes may take several hours depending on the size of the database.
+# The required indexes may be added in stages.
+# See: classpath:alfresco/dbscripts/upgrade/4.2/${db.script.dialect}/metadata-query-indexes.sql
+# See: classpath:alfresco/dbscripts/upgrade/5.1/${db.script.dialect}/metadata-query-indexes-2.sql
+system.metadata-query-indexes.ignored=true
+system.metadata-query-indexes-more.ignored=true
+
+#
+# Do we defer running the shared folder patch?
+#
+system.patch.sharedFolder.deferred=false
+# Default value is run new years day 2030 i.e. not run.
+system.patch.sharedFolder.cronExpression=0 0 0 ? 1 1 2030
+
+#
+# Default values for deferring the running of the addUnmovableAspect patch
+#
+system.patch.addUnmovableAspect.deferred=false
+system.patch.addUnmovableAspect.cronExpression=0 0 0 ? 1 1 2030
+
+# Property to enable removal of all JBPM related data from the database
+#
+# The tables are not removed from the databasen unless explicitly requested by setting this property to false.
+# See: classpath:alfresco/dbscripts/upgrade/5.2/${db.script.dialect}/remove-jbpm-tables-from-db.sql
+system.remove-jbpm-tables-from-db.ignored=true
+
+#
+# Use a canned query when requested to search for people if " [hint:useCQ]" is provided in search term
+#
+people.search.honor.hint.useCQ=true
+
+# Delays cron jobs after bootstrap to allow server to fully come up before jobs start
+system.cronJob.startDelayMilliseconds=60000
+
+# Schedule for reading mimetype config definitions dynamically. Initially checks every 10 seconds and then switches to
+# every hour after the configuration is read successfully. If there is a error later reading the config, the
+# checks return to every 10 seconds.
+mimetype.config.cronExpression=0 30 0/1 * * ?
+mimetype.config.initialAndOnError.cronExpression=0/10 * * * * ?
+
+# Optional property to specify an external file or directory that will be read for mimetype definitions from YAML
+# files (possibly added to a volume via k8 ConfigMaps).
+mimetype.config.dir=shared/classes/alfresco/extension/mimetypes
+
+# Schedule for reading rendition config definitions dynamically. Initially checks every 10 seconds and then switches to
+# every hour after the configuration is read successfully. If there is a error later reading the config, the
+# checks return to every 10 seconds.
+rendition.config.cronExpression=2 30 0/1 * * ?
+rendition.config.initialAndOnError.cronExpression=0/10 * * * * ?
+
+# Optional property to specify an external file or directory that will be read for rendition definitions from YAML
+# files (possibly added to a volume via k8 ConfigMaps).
+rendition.config.dir=shared/classes/alfresco/extension/transform/renditions
+
+# Optional property to specify an external file or directory that will be read for transformer json config.
+local.transform.pipeline.config.dir=shared/classes/alfresco/extension/transform/pipelines
+
+# Used to disable transforms locally.
+local.transform.service.enabled=true
+
+# Schedule for reading local transform config, so that T-Engines and local pipeline config is dynamically
+# picked up, or reintegrated after an outage. Initially checks every 10 seconds and then switches to every hour
+# after the configuration is read successfully. If there is a error later reading the config, the checks return to
+# every 10 seconds.
+local.transform.service.cronExpression=4 30 0/1 * * ?
+local.transform.service.initialAndOnError.cronExpression=0/10 * * * * ?
+
+#
+# Check that the declared mimetype (of the Node) is the same as the derived
+# mimetype of the content (via Tika) before a transformation takes place.
+# Only files in the repository (not intermediate files in a transformer
+# pipeline) are checked. This property provides a trade off between a
+# security check and a relatively expensive (Tika) operation.
+#
+# There are a few issues with the Tika mimetype detection. So that transformations
+# still take place where the detected mimetype is not the same as the declared mimetype,
+# another property (transformer.strict.mimetype.check.whitelist.mimetypes) contains pairs
+# of declared and detected mimetypes that should be allowed. This parameter value is a
+# sequence of ; separated pairs. The declared and derived mimetypes are also ; separated.
+#
+transformer.strict.mimetype.check=true
+
+# A white list of declared and detected mimetypes, that don't match, but should still be transformed.
+transformer.strict.mimetype.check.whitelist.mimetypes=application/eps;application/postscript;application/illustrator;application/pdf;application/x-tar;application/x-gtar;application/acp;application/zip;application/vnd.stardivision.math;application/x-tika-msoffice
+
+#
+# Enable transformation retrying if the file has MIME type differ than file extension.
+# Ignored if transformer.strict.mimetype.check is true as these transformations
+# will not take place.
+#
+content.transformer.retryOn.different.mimetype=true
+
+# Debug and Log buffer sizes
+transformer.debug.entries=0
+transformer.log.entries=50
+
+#
+# Lock timeout configuration
+#
+system.lockTryTimeout=100
+system.lockTryTimeout.DictionaryDAOImpl=10000
+system.lockTryTimeout.MessageServiceImpl=${system.lockTryTimeout}
+system.lockTryTimeout.PolicyComponentImpl=${system.lockTryTimeout}
+
+
+# Scheduled job to clean up unused properties from the alf_prop_xxx tables.
+# Default setting of "0 0 3 ? * SAT" is to run every Saturday at 3am.
+attributes.propcleaner.cronExpression=0 0 3 ? * SAT
+
+# Control Alfresco JMX connectivity
+alfresco.jmx.connector.enabled=false
+
+# Dissallow Attribute Service Entries with "Serializable" objects in key Segments
+# Please, see MNT-11895 for details.
+system.propval.uniquenessCheck.enabled=true
+
+# Requests for ephemeral (in-memory) locks with expiry times (in seconds) greater
+# than this value will result in persistent locks being created instead. By default
+# this value is equal to the maximum allowed expiry for ephemeral locks, therefore
+# this feature is disabled by default. Setting this to -1 would mean that ALL
+# requests for ephemeral locks would result in persistent locks being created.
+alfresco.ephemeralLock.expiryThresh=172800
+
+# SurfConfigFolder Patch
+#
+# Do we defer running the surf-config folder patch?
+#
+system.patch.surfConfigFolder.deferred=false
+# Default value. i.e. never run. It can be triggered using JMX
+system.patch.surfConfigFolder.cronExpression=* * * * * ? 2099
+
+#
+# Solr Facets Config Properties
+#
+solr_facets.root.path=/app:company_home/app:dictionary
+solr_facets.root=${solr_facets.root.path}/${spaces.solr_facets.root.childname}
+solr_facets.inheritanceHierarchy=default,custom
+
+models.enforceTenantInNamespace=false
+
+# Allowed protocols for links
+links.protocosl.white.list=http,https,ftp,mailto
+
+# Fixed ACLs
+# Required for fixing MNT-15368 - Time Consumed for Updating Folder Permission
+# ADMAccessControlListDAO.setFixedAcls called on a large folder hierarchy will take a long time for its execution.
+# For this reason now method can also be called asynchronously if transaction reaches system.fixedACLs.maxTransactionTime.
+# In this case setFixedAcls method recursion will be stopped and unfinished nodes will be marked with ASPECT_PENDING_FIX_ACL.
+# Pending nodes will be processed by FixedAclUpdater, programmatically called but also configured as a scheduled job.
+system.fixedACLs.maxTransactionTime=10000
+# fixedACLsUpdater - lock time to live
+system.fixedACLsUpdater.lockTTL=10000
+# fixedACLsUpdater - maximum number of nodes to process per execution
+system.fixedACLsUpdater.maxItemBatchSize=100
+# fixedACLsUpdater - the number of threads to use
+system.fixedACLsUpdater.numThreads=4
+# fixedACLsUpdater - Force shared ACL to propagate through children even if there is an unexpected ACL
+system.fixedACLsUpdater.forceSharedACL=false
+# fixedACLsUpdater cron expression - fire at midnight every day
+system.fixedACLsUpdater.cronExpression=0 0 0 * * ?
+
+cmis.disable.hidden.leading.period.files=false
+
+#Smart Folders Config Properties
+smart.folders.enabled=false
+
+#Smart reference config
+smart.reference.classpath.hash=${smart.folders.config.vanilla.processor.classpath}->1,${smart.folders.config.system.templates.classpath}->2
+
+#Smart store config
+
+#Company home relative download associations of smart entries
+smart.download.associations.folder=${spaces.dictionary.childname}/${spaces.smartdownloads.childname}
+
+#Generic virtualization methods config
+
+#Vanilla JSON templates javascript processor classpath. A java script processor used to
+#covert JSON templates to internal smart folder definitions.
+
+smart.folders.config.vanilla.processor.classpath=/org/alfresco/repo/virtual/node/vanilla.js
+
+#System virtualization method config
+
+#System virtualization method aspect.
+smart.folders.config.system.aspect=smf:systemConfigSmartFolder
+#System virtualization method aspect defined template location property.
+smart.folders.config.system.aspect.template.location.property=smf:system-template-location
+#Classpath to be explored for *.json entries defining system templates.
+smart.folders.config.system.templates.classpath=/org/alfresco/repo/virtual/node
+#A company home relative name or qname path location of repository system templates.
+smart.folders.config.system.templates.path=${spaces.dictionary.childname}/${spaces.smartfolders.childname}
+#Content sub type of repository system templates.
+smart.folders.config.system.templates.template.type=smf:smartFolderTemplate
+
+#Custom virtualization method config
+
+#Custom virtualization method aspect.
+smart.folders.config.custom.aspect=smf:customConfigSmartFolder
+#Custom virtualization method aspect template content association.
+smart.folders.config.custom.aspect.template.association=smf:custom-template-association
+
+
+#Type virtualization method config
+
+#A company home relative name or qname path location of the type mapped templates.
+smart.folders.config.type.templates.path=${spaces.dictionary.childname}/${spaces.smartfolders.childname}
+#Type and aspect qname regular expression filter.
+smart.folders.config.type.templates.qname.filter=none
+
+# Preferred password encoding, md4, sha256, bcrypt10
+system.preferred.password.encoding=md4
+
+# Upgrade Password Hash Job
+system.upgradePasswordHash.jobBatchSize=100
+system.upgradePasswordHash.jobQueryRange=10000
+system.upgradePasswordHash.jobThreadCount=4
+system.upgradePasswordHash.jobCronExpression=* * * * * ? 2099
+
+system.api.discovery.enabled=true
+
+# Maximum query size for category/tag fetch when not explicitly set by paging parameters
+category.queryFetchSize=5000
+
+# Brute force protection
+authentication.protection.enabled=true
+authentication.protection.limit=10
+authentication.protection.periodSeconds=6
+
+system.email.sender.default=noreply@alfresco.com
+# reset password workflow will expire in an hour
+system.reset-password.endTimer=PT1H
+system.reset-password.sendEmailAsynchronously=true
+
+# HeartBeat
+heartbeat.target.url=
+heartbeat.enabled=true
+
+# CSRF filter overrides
+csrf.filter.enabled=true
+csrf.filter.referer=
+csrf.filter.referer.always=false
+csrf.filter.origin=
+csrf.filter.origin.always=false
+
+# CORS settings
+cors.enabled=false
+cors.allowed.origins=
+cors.allowed.methods=GET,POST,HEAD,OPTIONS,PUT,DELETE
+cors.allowed.headers=Authorization,Content-Type,Cache-Control,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,X-CSRF-Token
+cors.exposed.headers=Access-Control-Allow-Origin,Access-Control-Allow-Credentials
+cors.support.credentials=true
+cors.preflight.maxage=10
+
+# Alfresco Rest Api-Explorer
+api-explorer.url=
+
+# Events subsystem
+events.subsystem.autoStart=false
+# Messaging subsystem
+messaging.subsystem.autoStart=true
+
+
+# Raw events
+acs.repo.rendition.events.endpoint=jms:acs-repo-rendition-events?jmsMessageType=Text
+
+# Transform request events
+acs.repo.transform.request.endpoint=jms:acs-repo-transform-request?jmsMessageType=Text
+
+# If enabled doesn't allow to set content properties via NodeService
+contentPropertyRestrictions.enabled=true
+contentPropertyRestrictions.whitelist=
+
+# Repo events2
+# Type and aspect filters which should be excluded
+# Note: System folders node types are added by default
+repo.event2.filter.nodeTypes=sys:*, fm:*, cm:thumbnail, cm:failedThumbnail, cm:rating, rma:rmsite include_subtypes
+repo.event2.filter.nodeAspects=sys:*
+repo.event2.filter.childAssocTypes=rn:rendition
+# Comma separated list of users which should be excluded
+# Note: username's case-sensitivity depends on the {user.name.caseSensitive} setting
+repo.event2.filter.users=
+# Topic name
+repo.event2.topic.endpoint=amqp:topic:alfresco.repo.event2
+# Thread pool for async enqueue of repo events
+repo.event2.queue.enqueueThreadPool.priority=1
+repo.event2.queue.enqueueThreadPool.coreSize=8
+repo.event2.queue.enqueueThreadPool.maximumSize=10
+# Thread pool for async dequeue and delivery of repo events
+repo.event2.queue.dequeueThreadPool.priority=1
+repo.event2.queue.dequeueThreadPool.coreSize=1
+repo.event2.queue.dequeueThreadPool.maximumSize=1
+
+
+# MNT-21083
+# --DELETE_NOT_EXISTS - default settings
+system.delete_not_exists.batchsize=100000
+system.delete_not_exists.delete_batchsize=1000
+system.delete_not_exists.read_only=false
+system.delete_not_exists.timeout_seconds=-1
+system.prop_table_cleaner.algorithm=V2
+
+# Configure the system-wide (ACS) settings for direct access urls.
+#
+# For Direct Access URLs to be usable on the service-layer, the feature must be enabled both system-wide and on the
+# content-store(s). For the feature to be usable through REST (outside the JVM) the rest-api configuration must also be
+# enabled.
+#
+# The system.directAccessUrl.enabled property is the main switch of the feature. If this is set to false ALL
+# Direct Access URLs are disabled.
+#
+# The next configuration that controls specific Direct Access URLs is the content store one.
+# The connector.s3.directAccessUrl.enabled property controls whether Direct Access URLs are enabled for that specific store.
+#
+# Whether or not a client can request a Direct Access URL by using a REST endpoint is controlled by the
+# restApi.directAccessUrl.enabled property. If the REST endpoint is disabled, but the feature is enabled
+# system-wide and on the content-store, then the direct access URLs will only be usable by Java clients (only
+# service-level requests will be possible).
+
+# Controls whether this feature is available, system wide.
+# For direct access urls to work, the feature needs to be enabled both system-wide and on the individual content-store.
+system.directAccessUrl.enabled=false
+# Sets the default expiry time for the direct access url across all Content Stores.
+# Its value cannot exceed the system-wide max expiry time, it can only be equal or lower (all DAUs disabled otherwise).
+# This property is mandatory if direct access urls are enabled system-wide - (all DAUs disabled otherwise).
+system.directAccessUrl.defaultExpiryTimeInSec=30
+# Sets the upper limit for the direct access urls expiry time, meaning a Content Store will be able to override this
+# value but not exceed it, and the same goes for the clients.
+# A service (Java Interface) client will be able to request a direct access url for a custom expiry time but that time
+# can’t exceed this value.
+# If the requested time exceeds the max value, the expiry time reverts to the default configured one.
+# This property is mandatory if direct access urls are enabled system-wide - (all DAUs disabled otherwise).
+system.directAccessUrl.maxExpiryTimeInSec=300
+
+# Configure the common S3 storage connector content store settings for direct access urls.
+#
+# Note: When multiple S3 buckets are used for storage in Alfresco, each S3 Content Stores can be configured with either
+# the default (common) S3 Connector-specific properties (i.e. connector.s3.directAccessUrl.enabled etc.) OR new separate
+# properties could be defined for each and every store (e.g.
+# connector.s3store1.directAccessUrl.enabled,
+# connector.s3store2.directAccessUrl.enabled etc.).
+
+# Controls whether DAUs are enabled on this specific content store.
+# For direct access urls to work, the feature needs to be enabled both system-wide and on the individual content-store.
+connector.s3.directAccessUrl.enabled=false
+# Sets the expiry time for the direct access url in this store, by overriding the system-wide config.
+# If this value exceeds the content store upper limit or the system-wide default it will fallback to the system-wide
+# default configuration.
+# Its value cannot exceed the system-wide max expiry time, it can only be equal or lower (DAUs for the specific content
+# store disabled otherwise).
+# If not set, the default system-wide setting will be used.
+connector.s3.directAccessUrl.defaultExpiryTimeInSec=30
+# The maximum expiry time interval that can be requested by clients - content-store specific setting.
+# Its value cannot exceed the system-wide configuration, it can only be equal or lower (DAUs for the specific content
+# store disabled otherwise).
+# If not set, the default system-wide setting will be used.
+connector.s3.directAccessUrl.maxExpiryTimeInSec=300
+
+# Configure the REST API configuration settings for direct access urls.
+#
+# Controls whether direct access url requests via the REST API are enabled.
+restApi.directAccessUrl.enabled=false
+# Sets the expiry time for all the direct access urls requested via a REST call.
+# Its value cannot exceed the system-wide max expiry time configuration, it can only be equal or lower (REST API DAUs
+# disabled otherwise).
+# If not set, the default system-wide setting will be used.
+# Direct Access Url REST API calls cannot request an explicit expiry time.
+restApi.directAccessUrl.defaultExpiryTimeInSec=30
+
+# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories.
+system.new-node-transaction-indexes.ignored=true
+
+# Allows the configuration of maximum limits of the temp files to be deleted or the maximum time allowed to run for the job
+system.tempFileCleaner.maxFilesToDelete=
+system.tempFileCleaner.maxTimeToRun=
+
+# Property to long running migration to remove alf_server in v7+ patch.db-V7.1.0-remove-alf_server-table
+system.remove-alf_server-table-from-db.ignored=true
+
+# When using JSONP, allows unsecure usage of "callback" functions. Disabled by default for security reasons
+allow.unsecure.callback.jsonp=false
diff --git a/repository/src/main/resources/alfresco/subsystems/Search/common-search-context.xml b/repository/src/main/resources/alfresco/subsystems/Search/common-search-context.xml
index 7410e27f2b..932c4a06ef 100644
--- a/repository/src/main/resources/alfresco/subsystems/Search/common-search-context.xml
+++ b/repository/src/main/resources/alfresco/subsystems/Search/common-search-context.xml
@@ -105,8 +105,35 @@
-
-
+
+
+
+ search.dbQueryEngineImpl.#bean.dialect#
+
+
+ org.alfresco.repo.search.impl.querymodel.QueryEngine
+
+
+ org.alfresco.repo.domain.dialect.Dialect
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repository/src/test/java/org/alfresco/AllDBTestsTestSuite.java b/repository/src/test/java/org/alfresco/AllDBTestsTestSuite.java
index 6dddc58bbe..1737976f0e 100644
--- a/repository/src/test/java/org/alfresco/AllDBTestsTestSuite.java
+++ b/repository/src/test/java/org/alfresco/AllDBTestsTestSuite.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -77,7 +77,10 @@ import org.junit.runners.Suite;
// From MiscContextTestSuite
org.alfresco.repo.domain.query.CannedQueryDAOTest.class,
- // REPO-2963 : Tests causing a cascade of failures in AllDBTestsTestSuite on PostgreSQL/MySQL
+ // ACS-1907
+ org.alfresco.repo.search.impl.querymodel.impl.db.ACS1907Test.class,
+
+ // REPO-2963 : Tests causing a cascade of failures in AllDBTestsTestSuite on PostgreSQL/MySQL
// Moved at the bottom of the suite because DbNodeServiceImplTest.testNodeCleanupRegistry() takes a long time on a clean DB.
org.alfresco.repo.node.db.DbNodeServiceImplTest.class,
diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
index ba6cc05354..26b5908cf8 100644
--- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
+++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
@@ -180,6 +180,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.audit.AuditableAnnotationTest.class,
org.alfresco.repo.audit.PropertyAuditFilterTest.class,
org.alfresco.repo.audit.access.NodeChangeTest.class,
+ org.alfresco.repo.content.ContentServiceImplUnitTest.class,
+ org.alfresco.repo.content.directurl.SystemWideDirectUrlConfigUnitTest.class,
+ org.alfresco.repo.content.directurl.ContentStoreDirectUrlConfigUnitTest.class,
org.alfresco.repo.content.LimitedStreamCopierTest.class,
org.alfresco.repo.content.filestore.FileIOTest.class,
org.alfresco.repo.content.filestore.SpoofedTextContentReaderTest.class,
@@ -233,7 +236,6 @@ import org.junit.runners.Suite;
org.alfresco.repo.events.ClientUtilTest.class,
org.alfresco.repo.rendition2.RenditionService2Test.class,
org.alfresco.repo.rendition2.TransformationOptionsConverterTest.class,
- org.alfresco.transform.client.registry.TransformServiceRegistryConfigTest.class,
org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
diff --git a/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java b/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java
new file mode 100644
index 0000000000..63e6530715
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java
@@ -0,0 +1,160 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.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;
+import static org.mockito.MockitoAnnotations.openMocks;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
+import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.DirectAccessUrl;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for content service implementation.
+ *
+ * @author Sara Aspery
+ */
+public class ContentServiceImplUnitTest
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+ private static final Boolean DISABLED = Boolean.FALSE;
+
+ private static final Long SYS_DEFAULT_EXPIRY_TIME_IN_SECS = 30L;
+ private static final Long SYS_MAX_EXPIRY_TIME_IN_SECS = 300L;
+
+ private static final NodeRef NODE_REF = new NodeRef("content://Node/Ref");
+
+ @InjectMocks
+ private ContentServiceImpl contentService;
+
+ @Mock
+ private ContentStore mockContentStore;
+
+ @Mock
+ private NodeService mockNodeService;
+
+ @Mock
+ private ContentData mockContentData;
+
+ @Before
+ public void setup()
+ {
+ openMocks(this);
+ when(mockNodeService.getProperty(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentData);
+ when(mockContentData.getContentUrl()).thenReturn("someContentUrl");
+ when(mockNodeService.getProperty(NODE_REF, ContentModel.PROP_NAME)).thenReturn("someFilename");
+ }
+
+ @Test
+ public void testIsContentDirectUrlEnabled_SystemWideIsDisabled()
+ {
+ setupSystemWideDirectAccessConfig(DISABLED);
+ assertFalse("Expected contentDirectUrl to be disabled", contentService.isContentDirectUrlEnabled());
+ verify(mockContentStore, never()).isContentDirectUrlEnabled();
+ }
+
+ @Test
+ public void testIsContentDirectUrlEnabled_SystemWideIsEnabledButStoreIsDisabled()
+ {
+ setupSystemWideDirectAccessConfig(ENABLED);
+ when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(DISABLED);
+ assertFalse("Expected contentDirectUrl to be disabled", contentService.isContentDirectUrlEnabled());
+ }
+
+ @Test
+ public void testIsContentDirectUrlEnabled_SystemWideIsEnabledAndStoreIsEnabled()
+ {
+ setupSystemWideDirectAccessConfig(ENABLED);
+ when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(ENABLED);
+ assertTrue("Expected contentDirectUrl to be enabled", contentService.isContentDirectUrlEnabled());
+ }
+
+ @Test
+ public void testRequestContentDirectUrl_SystemWideIsDisabled()
+ {
+ setupSystemWideDirectAccessConfig(DISABLED);
+ try
+ {
+ contentService.requestContentDirectUrl(NODE_REF, true, 20L);
+ fail("Expected DirectAccessUrlDisabledException");
+ }
+ catch (DirectAccessUrlDisabledException ex)
+ {
+ verify(mockContentStore, never()).isContentDirectUrlEnabled();
+ }
+ }
+
+ @Test
+ public void testRequestContentDirectUrl_SystemWideIsEnabledButStoreIsDisabled()
+ {
+ setupSystemWideDirectAccessConfig(ENABLED);
+ when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(DISABLED);
+
+ DirectAccessUrl directAccessUrl = contentService.requestContentDirectUrl(NODE_REF, true, 20L);
+ assertNull(directAccessUrl);
+ verify(mockContentStore, never()).requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong());
+ }
+
+ @Test
+ public void testRequestContentDirectUrl_StoreIsEnabledButNotImplemented()
+ {
+ setupSystemWideDirectAccessConfig(ENABLED);
+ when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(ENABLED);
+
+ DirectAccessUrl directAccessUrl = contentService.requestContentDirectUrl(NODE_REF, true, 20L);
+ assertNull(directAccessUrl);
+ verify(mockContentStore, times(1)).requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong());
+ }
+
+ /* Helper method to set system-wide direct access url configuration settings */
+ private void setupSystemWideDirectAccessConfig(Boolean isEnabled)
+ {
+ SystemWideDirectUrlConfig sysConfig = new SystemWideDirectUrlConfig();
+ sysConfig.setEnabled(isEnabled);
+ sysConfig.setDefaultExpiryTimeInSec(SYS_DEFAULT_EXPIRY_TIME_IN_SECS);
+ sysConfig.setMaxExpiryTimeInSec(SYS_MAX_EXPIRY_TIME_IN_SECS);
+ sysConfig.validate();
+ contentService.setSystemWideDirectUrlConfig(sysConfig);
+ }
+}
diff --git a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java
index d379db4b10..47b2fa14c2 100644
--- a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java
+++ b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java
@@ -36,6 +36,7 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -494,21 +495,21 @@ public class CachingContentStoreTest
}
@Test
- public void isDirectAccessSupported()
+ public void isContentDirectUrlSupported()
{
- assertFalse(cachingStore.isDirectAccessSupported());
+ assertFalse(cachingStore.isContentDirectUrlEnabled());
- when(backingStore.isDirectAccessSupported()).thenReturn(true);
- assertTrue(cachingStore.isDirectAccessSupported());
+ when(backingStore.isContentDirectUrlEnabled()).thenReturn(true);
+ assertTrue(cachingStore.isContentDirectUrlEnabled());
}
@Test
- public void getDirectAccessUrlUnsupported()
+ public void getRequestContentDirectUrlUnsupported()
{
try
{
- when(backingStore.getDirectAccessUrl(anyString(), any())).thenThrow(new UnsupportedOperationException());
- cachingStore.getDirectAccessUrl("url", null);
+ when(backingStore.requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong())).thenThrow(new UnsupportedOperationException());
+ cachingStore.requestContentDirectUrl("url", true,"someFile", 30L);
fail();
}
catch (UnsupportedOperationException e)
@@ -518,10 +519,10 @@ public class CachingContentStoreTest
}
@Test
- public void getDirectAccessUrl()
+ public void getRequestContentDirectUrl()
{
- when(backingStore.getDirectAccessUrl(anyString(), any())).thenReturn(new DirectAccessUrl());
- cachingStore.getDirectAccessUrl("url", null);
+ when(backingStore.requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong())).thenReturn(new DirectAccessUrl());
+ cachingStore.requestContentDirectUrl("url", true,"someFile", 30L);
}
@Test
diff --git a/repository/src/test/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfigUnitTest.java b/repository/src/test/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfigUnitTest.java
new file mode 100644
index 0000000000..b32ed63be8
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/repo/content/directurl/ContentStoreDirectUrlConfigUnitTest.java
@@ -0,0 +1,234 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for content store direct access URL configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public class ContentStoreDirectUrlConfigUnitTest
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+ private static final Boolean DISABLED = Boolean.FALSE;
+
+ private static final Long DEFAULT_EXPIRY_TIME_IN_SECS = 10L;
+ private static final Long MAX_EXPIRY_TIME_IN_SECS = 20L;
+
+ private static final Long SYS_DEFAULT_EXPIRY_TIME_IN_SECS = 30L;
+ private static final Long SYS_MAX_EXPIRY_TIME_IN_SECS = 300L;
+
+ private ContentStoreDirectUrlConfig contentStoreDirectUrlConfig;
+
+ @Before
+ public void setup()
+ {
+ this.contentStoreDirectUrlConfig = new ContentStoreDirectUrlConfig();
+ setupSystemWideDirectAccessConfig();
+ }
+
+ @Test
+ public void testValidConfig_RemainsEnabled()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertTrue("Expected REST API direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testValidConfig_RemainsDisabled()
+ {
+ setupDirectAccessConfig(DISABLED, DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeMissing_ValidReplacement()
+ {
+ Long maxExpiryTimeInSecs = SYS_DEFAULT_EXPIRY_TIME_IN_SECS + 1;
+ setupDirectAccessConfig(ENABLED, null, maxExpiryTimeInSecs);
+
+ verifyDirectAccessConfig(ENABLED, null, maxExpiryTimeInSecs);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(ENABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, maxExpiryTimeInSecs);
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeMissing_ReplacementExceedsMax()
+ {
+ setupDirectAccessConfig(ENABLED, null, MAX_EXPIRY_TIME_IN_SECS);
+
+ verifyDirectAccessConfig(ENABLED, null, MAX_EXPIRY_TIME_IN_SECS);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(DISABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeZero()
+ {
+ setupDirectAccessConfig(ENABLED, 0L, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeNegative()
+ {
+ setupDirectAccessConfig(ENABLED, -1L, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsSystemMax()
+ {
+ Long defaultExpiryTimeInSecs = SYS_MAX_EXPIRY_TIME_IN_SECS + 1;
+ setupDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsStoreMax_ValidReplacement()
+ {
+ Long maxExpiryTimeInSecs = SYS_DEFAULT_EXPIRY_TIME_IN_SECS + 1;
+ Long defaultExpiryTimeInSecs = maxExpiryTimeInSecs + 1;
+ setupDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, maxExpiryTimeInSecs);
+
+ verifyDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, maxExpiryTimeInSecs);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(ENABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, maxExpiryTimeInSecs);
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsStoreMax_ReplacementExceedsStoreMax()
+ {
+ Long defaultExpiryTimeInSecs = MAX_EXPIRY_TIME_IN_SECS + 1;
+ setupDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, MAX_EXPIRY_TIME_IN_SECS);
+
+ verifyDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, MAX_EXPIRY_TIME_IN_SECS);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(DISABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+ }
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsSystemDefault_ValidReplacement()
+ {
+ Long defaultExpiryTimeInSecs = SYS_DEFAULT_EXPIRY_TIME_IN_SECS + 1;
+ Long maxExpiryTimeInSecs = SYS_MAX_EXPIRY_TIME_IN_SECS;
+ setupDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, maxExpiryTimeInSecs);
+
+ verifyDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, maxExpiryTimeInSecs);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(ENABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, maxExpiryTimeInSecs);
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsSystemDefault_ReplacementExceedsStoreMax()
+ {
+ Long defaultExpiryTimeInSecs = SYS_DEFAULT_EXPIRY_TIME_IN_SECS + 1;
+ setupDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, MAX_EXPIRY_TIME_IN_SECS);
+
+ verifyDirectAccessConfig(ENABLED, defaultExpiryTimeInSecs, MAX_EXPIRY_TIME_IN_SECS);
+ contentStoreDirectUrlConfig.validate();
+ verifyDirectAccessConfig(DISABLED, SYS_DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeZero()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, 0L);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeNegative()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, -1L);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeExceedsSystemMax()
+ {
+ Long maxExpiryTimeInSec = contentStoreDirectUrlConfig.getSysWideMaxExpiryTimeInSec() + 1;
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, maxExpiryTimeInSec);
+
+ assertTrue("Expected content store direct URLs to be enabled", contentStoreDirectUrlConfig.isEnabled());
+ contentStoreDirectUrlConfig.validate();
+ assertFalse("Expected content store direct URLs to be disabled", contentStoreDirectUrlConfig.isEnabled());
+ }
+
+ /* Helper method to set content store direct access url configuration settings */
+ private void setupDirectAccessConfig(Boolean isEnabled, Long defaultExpiryTime, Long maxExpiryTime)
+ {
+ contentStoreDirectUrlConfig.setEnabled(isEnabled);
+ contentStoreDirectUrlConfig.setDefaultExpiryTimeInSec(defaultExpiryTime);
+ contentStoreDirectUrlConfig.setMaxExpiryTimeInSec(maxExpiryTime);
+ }
+
+ /* Helper method to verify content store direct access url configuration settings */
+ private void verifyDirectAccessConfig(Boolean isEnabled, Long defaultExpiryTime, Long maxExpiryTime)
+ {
+ assertEquals("Expected content store direct URLs to be enabled = " + isEnabled, isEnabled, contentStoreDirectUrlConfig.isEnabled());
+ assertEquals("Expected default expiry time to match " + defaultExpiryTime, defaultExpiryTime, contentStoreDirectUrlConfig.getDefaultExpiryTimeInSec());
+ assertEquals("Expected maximum expiry time to match " + maxExpiryTime, maxExpiryTime, contentStoreDirectUrlConfig.getMaxExpiryTimeInSec());
+ }
+
+ /* Helper method to set system-wide direct access url configuration settings */
+ private void setupSystemWideDirectAccessConfig()
+ {
+ SystemWideDirectUrlConfig sysConfig = new SystemWideDirectUrlConfig();
+ sysConfig.setEnabled(ENABLED);
+ sysConfig.setDefaultExpiryTimeInSec(SYS_DEFAULT_EXPIRY_TIME_IN_SECS);
+ sysConfig.setMaxExpiryTimeInSec(SYS_MAX_EXPIRY_TIME_IN_SECS);
+ sysConfig.validate();
+ contentStoreDirectUrlConfig.setSystemWideDirectUrlConfig(sysConfig);
+ }
+}
diff --git a/repository/src/test/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfigUnitTest.java b/repository/src/test/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfigUnitTest.java
new file mode 100644
index 0000000000..d498fac30b
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/repo/content/directurl/SystemWideDirectUrlConfigUnitTest.java
@@ -0,0 +1,153 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.content.directurl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for system-wide direct access URL configuration settings.
+ *
+ * @author Sara Aspery
+ */
+public class SystemWideDirectUrlConfigUnitTest
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+ private static final Boolean DISABLED = Boolean.FALSE;
+
+ private static final Long DEFAULT_EXPIRY_TIME_IN_SECS = 30L;
+ private static final Long MAX_EXPIRY_TIME_IN_SECS = 300L;
+
+ private SystemWideDirectUrlConfig systemWideDirectUrlConfig;
+
+ @Before
+ public void setup()
+ {
+ this.systemWideDirectUrlConfig = new SystemWideDirectUrlConfig();
+ }
+
+ @Test
+ public void testValidConfig_RemainsEnabled()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testValidConfig_RemainsDisabled()
+ {
+ setupDirectAccessConfig(DISABLED, DEFAULT_EXPIRY_TIME_IN_SECS, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeMissing()
+ {
+ setupDirectAccessConfig(ENABLED, null, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeZero()
+ {
+ setupDirectAccessConfig(ENABLED, 0L, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeNegative()
+ {
+ setupDirectAccessConfig(ENABLED, -1L, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeMissing()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, null);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeZero()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, 0L);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_MaxExpiryTimeNegative()
+ {
+ setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS, -1L);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ @Test
+ public void testInvalidConfig_DefaultExpiryTimeExceedsMax()
+ {
+ setupDirectAccessConfig(ENABLED, MAX_EXPIRY_TIME_IN_SECS + 1, MAX_EXPIRY_TIME_IN_SECS);
+
+ assertTrue("Expected system-wide direct URLs to be enabled", systemWideDirectUrlConfig.isEnabled());
+ systemWideDirectUrlConfig.validate();
+ assertFalse("Expected system-wide direct URLs to be disabled", systemWideDirectUrlConfig.isEnabled());
+ }
+
+ /* Helper method to set system-wide direct access url configuration settings */
+ private void setupDirectAccessConfig(Boolean isEnabled, Long defaultExpiryTime, Long maxExpiryTime)
+ {
+ systemWideDirectUrlConfig.setEnabled(isEnabled);
+ systemWideDirectUrlConfig.setDefaultExpiryTimeInSec(defaultExpiryTime);
+ systemWideDirectUrlConfig.setMaxExpiryTimeInSec(maxExpiryTime);
+ }
+}
+
diff --git a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java
index 72f0307c95..2a3224851a 100644
--- a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java
+++ b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -63,6 +63,18 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
/**
@@ -88,9 +100,12 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
ContentStore primaryStoreMock;
@Mock
ContentStore secondaryStoreMock;
-
+ @Mock
AggregatingContentStore aggregatingContentStoreMock;
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
@Before
public void before() throws Exception
{
@@ -201,51 +216,53 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
}
@Test
- public void testDirectAccessUnsupportedByDefault()
+ public void testIsContentDirectUrlEnabled()
{
+ // Create the aggregating store
+ AggregatingContentStore aggStore = new AggregatingContentStore();
+ aggStore.setPrimaryStore(primaryStoreMock);
+ aggStore.setSecondaryStores(List.of(secondaryStoreMock));
+
// By default it is unsupported
- assertFalse(aggregatingContentStoreMock.isDirectAccessSupported());
- verify(primaryStoreMock, times(1)).isDirectAccessSupported();
- verify(secondaryStoreMock, times(1)).isDirectAccessSupported();
+ assertFalse(aggStore.isContentDirectUrlEnabled());
+
+ // Supported if at least one store supports direct access
+ {
+ when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(false);
+ when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true);
+ assertTrue(aggStore.isContentDirectUrlEnabled());
+
+ when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true);
+ when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true);
+ assertTrue(aggStore.isContentDirectUrlEnabled());
+
+ when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true);
+ when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(false);
+ assertTrue(aggStore.isContentDirectUrlEnabled());
+ }
}
@Test
- public void testIsDirectAccessSupportedByPrimaryStore()
+ public void testRequestContentDirectUrl()
{
- when(primaryStoreMock.isDirectAccessSupported()).thenReturn(false);
- when(secondaryStoreMock.isDirectAccessSupported()).thenReturn(true);
+ // Create the aggregating store
+ AggregatingContentStore aggStore = new AggregatingContentStore();
+ aggStore.setPrimaryStore(primaryStoreMock);
+ aggStore.setSecondaryStores(List.of(secondaryStoreMock));
- assertTrue(aggregatingContentStoreMock.isDirectAccessSupported());
- verify(primaryStoreMock, times(1)).isDirectAccessSupported();
- verify(secondaryStoreMock, times(1)).isDirectAccessSupported();
- }
-
- @Test
- public void testIsDirectAccessSupportedBySecondaryStore()
- {
- when(primaryStoreMock.isDirectAccessSupported()).thenReturn(true);
-
- assertTrue(aggregatingContentStoreMock.isDirectAccessSupported());
- verify(primaryStoreMock, times(1)).isDirectAccessSupported();
- verifyNoInteractions(secondaryStoreMock);
- }
-
- @Test
- public void testGetDirectAccessUrl()
- {
UnsupportedOperationException unsupportedExc = new UnsupportedOperationException();
- UnsupportedContentUrlException unsupportedContentUrlExc = new UnsupportedContentUrlException(aggregatingContentStoreMock, "");
+ UnsupportedContentUrlException unsupportedContentUrlExc = new UnsupportedContentUrlException(aggStore, "");
// By default it is unsupported
- DirectAccessUrl directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("url", null);
+ DirectAccessUrl directAccessUrl = aggStore.requestContentDirectUrl("url", true, "anyfilename", 30L);
assertNull(directAccessUrl);
// Direct access not supported
try
{
- when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc);
- aggregatingContentStoreMock.getDirectAccessUrl("urlDANotSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ aggStore.requestContentDirectUrl(eq("urlDANotSupported"), true, "anyfilename", 30L);
fail();
}
catch (UnsupportedOperationException e)
@@ -255,9 +272,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
try
{
- when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedContentUrlExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc);
- aggregatingContentStoreMock.getDirectAccessUrl("urlDANotSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ aggStore.requestContentDirectUrl("urlDANotSupported", true, "anyfilename", 30L);
fail();
}
catch (UnsupportedOperationException e)
@@ -267,9 +284,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
try
{
- when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedContentUrlExc);
- aggregatingContentStoreMock.getDirectAccessUrl("urlDANotSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ aggStore.requestContentDirectUrl("urlDANotSupported", true, "anyfilename", 30L);
fail();
}
catch (UnsupportedOperationException e)
@@ -280,9 +297,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
// Content url not supported
try
{
- when(primaryStoreMock.getDirectAccessUrl(eq("urlNotSupported"), any())).thenThrow(unsupportedContentUrlExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlNotSupported"), any())).thenThrow(unsupportedContentUrlExc);
- aggregatingContentStoreMock.getDirectAccessUrl("urlNotSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlNotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlNotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ aggStore.requestContentDirectUrl("urlNotSupported", true, "anyfilename", 30L);
fail();
}
catch (UnsupportedContentUrlException e)
@@ -290,29 +307,31 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
// Expected
}
- when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl());
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlPriSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
- when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl());
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlPriSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
- when(primaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenThrow(unsupportedExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl());
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlSecSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenThrow(unsupportedExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
- when(primaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenThrow(unsupportedContentUrlExc);
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl());
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlSecSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc);
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
- when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl());
- when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl());
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlPriSupported", null);
+ when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl());
+ directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
- directAccessUrl = aggregatingContentStoreMock.getDirectAccessUrl("urlSecSupported", null);
+ directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L);
assertNotNull(directAccessUrl);
}
@@ -321,7 +340,7 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
{
final StorageClassSet sc = new StorageClassSet("a-certain-storage-class");
when(primaryStoreMock.isStorageClassesSupported(sc)).thenReturn(true);
-
+
assertTrue(aggregatingContentStoreMock.isStorageClassesSupported(sc));
verify(primaryStoreMock, times(1)).isStorageClassesSupported(sc);
verifyNoInteractions(secondaryStoreMock);
@@ -332,7 +351,7 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
{
final StorageClassSet sc = new StorageClassSet("a-certain-storage-class");
when(primaryStoreMock.isStorageClassesSupported(sc)).thenReturn(false);
-
+
assertFalse(aggregatingContentStoreMock.isStorageClassesSupported(sc));
verify(primaryStoreMock, times(1)).isStorageClassesSupported(sc);
verifyNoInteractions(secondaryStoreMock);
@@ -428,7 +447,7 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes
.thenThrow(new UnsupportedContentUrlException(aggregatingContentStoreMock, ""));
aggregatingContentStoreMock.findStorageClassesTransitions("contentUrl");
-
+
verify(primaryStoreMock, times(1)).findStorageClassesTransitions("contentUrl");
verifyNoInteractions(secondaryStoreMock);
}
diff --git a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
index c5f3f9248f..995bec4cfe 100644
--- a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
+++ b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
@@ -36,6 +36,7 @@ import java.util.List;
import javax.jms.ConnectionFactory;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.dictionary.NamespaceDAO;
import org.alfresco.repo.event.v1.model.ChildAssociationResource;
import org.alfresco.repo.event.v1.model.DataAttributes;
import org.alfresco.repo.event.v1.model.EventType;
@@ -114,6 +115,9 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
@Qualifier("eventGeneratorQueue")
protected EventGeneratorQueue eventQueue;
+ @Autowired
+ private NamespaceDAO namespaceDAO;
+
protected NodeRef rootNodeRef;
@BeforeClass
@@ -152,9 +156,19 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
return nodeService.getRootNode(storeRef);
});
+ initTestNamespacePrefixMapping();
+
flushSpuriousEvents();
}
+ protected void initTestNamespacePrefixMapping() {
+ if(namespaceDAO.getNamespaceURI("ce") == null)
+ {
+ namespaceDAO.addURI(TEST_NAMESPACE);
+ namespaceDAO.addPrefix("ce", TEST_NAMESPACE);
+ }
+ }
+
/*
* When running with an empty database some events related to the creation may
* creep up here making the test fails. After attempting several other
@@ -207,11 +221,16 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
}
protected NodeRef createNode(QName contentType, PropertyMap propertyMap)
+ {
+ return createNode(contentType, GUID.generate(), propertyMap);
+ }
+
+ protected NodeRef createNode(QName contentType, String localName, PropertyMap propertyMap)
{
return retryingTransactionHelper.doInTransaction(() -> nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
- QName.createQName(TEST_NAMESPACE, GUID.generate()),
+ QName.createQName(TEST_NAMESPACE, localName),
contentType,
propertyMap).getChildRef());
}
diff --git a/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java
index d7db0e007a..11b6bcf77d 100644
--- a/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java
+++ b/repository/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java
@@ -50,6 +50,7 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent
@Test
public void testAddChildAssociation()
{
+ String assocLocalName = GUID.generate();
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
@@ -64,11 +65,11 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent
resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
- nodeService.addChild(
- parentNodeRef,
- childNodeRef,
- ContentModel.ASSOC_CONTAINS,
- QName.createQName(TEST_NAMESPACE, GUID.generate())));
+ nodeService.addChild(
+ parentNodeRef,
+ childNodeRef,
+ ContentModel.ASSOC_CONTAINS,
+ QName.createQName(TEST_NAMESPACE, assocLocalName)));
List childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getChildAssocs(parentNodeRef));
@@ -101,6 +102,7 @@ public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent
assertEquals("Wrong parent", parentNodeRef.getId(), childAssociationResource.getParent().getId());
assertEquals("Wrong child", childNodeRef.getId(), childAssociationResource.getChild().getId());
assertEquals("Wrong assoc type", "cm:contains", childAssociationResource.getAssocType());
+ assertEquals("Wrong assoc name", "ce:" + assocLocalName, childAssociationResource.getAssocQName());
}
@Test
diff --git a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java
index db6b592c70..1e801ad206 100644
--- a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java
+++ b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java
@@ -53,16 +53,17 @@ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent
@Autowired
private NodeDAO nodeDAO;
-
+
@Test
public void testCreateEvent()
{
// Create a node without content
final String name = "TestFile-" + System.currentTimeMillis() + ".txt";
+ String localName = GUID.generate();
PropertyMap propertyMap = new PropertyMap();
propertyMap.put(ContentModel.PROP_TITLE, "test title");
propertyMap.put(ContentModel.PROP_NAME, name);
- final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, propertyMap);
+ final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, localName, propertyMap);
final RepoEvent> resultRepoEvent = getRepoEvent(1);
// Repo event attributes
@@ -103,6 +104,7 @@ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent
assertEquals("Wrong node modifier display name.", "Administrator",
nodeResource.getModifiedByUser().getDisplayName());
assertNotNull("Missing modifiedAt property.", nodeResource.getModifiedAt());
+ assertEquals("Wrong primaryAssocQName prefix.", "ce:" + localName, nodeResource.getPrimaryAssocQName());
}
@Test
diff --git a/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java
index 52a69434af..d7115c6677 100644
--- a/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java
+++ b/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java
@@ -45,9 +45,10 @@ public class DeleteRepoEventIT extends AbstractContextAwareRepoEvent
@Test
public void testDeleteContent()
{
+ String localName = GUID.generate();
PropertyMap propertyMap = new PropertyMap();
propertyMap.put(ContentModel.PROP_TITLE, "test title");
- NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, propertyMap);
+ NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, localName, propertyMap);
NodeResource createdResource = getNodeResource(1);
@@ -64,6 +65,7 @@ public class DeleteRepoEventIT extends AbstractContextAwareRepoEvent
assertEquals("Repo event type:", EventType.NODE_DELETED.getType(), resultRepoEvent.getType());
assertEquals(createdResource.getId(), getNodeResource(resultRepoEvent).getId());
+ assertEquals("Wrong primaryAssocQName prefix.", "ce:" + localName, createdResource.getPrimaryAssocQName());
// There should be no resourceBefore
EventData eventData = getEventData(resultRepoEvent);
diff --git a/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
index 413fa7d395..6075c2fd0c 100644
--- a/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
+++ b/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
@@ -33,7 +33,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.M2Model;
@@ -59,6 +58,7 @@ import org.junit.Test;
*/
public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
{
+
@Test
public void testUpdateNodeResourceContent()
{
@@ -138,6 +138,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
@@ -198,6 +199,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
@@ -274,6 +276,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
@@ -539,6 +542,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
@@ -574,6 +578,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
NodeResource nodeResource = getNodeResource(resultRepoEvent);
assertEquals("Incorrect node type was found", "cm:dictionaryModel", nodeResource.getNodeType());
+ initTestNamespacePrefixMapping();
final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT);
// old node's type
assertEquals(ContentModel.TYPE_CONTENT, nodeService.getType(nodeRef));
@@ -613,7 +618,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
-
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
@@ -660,6 +665,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getProperties());
assertNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getPrimaryHierarchy());
+ assertNull(resourceBefore.getPrimaryAssocQName());
}
@Test
diff --git a/repository/src/test/java/org/alfresco/repo/search/impl/querymodel/impl/db/ACS1907Test.java b/repository/src/test/java/org/alfresco/repo/search/impl/querymodel/impl/db/ACS1907Test.java
new file mode 100644
index 0000000000..50232978e9
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/repo/search/impl/querymodel/impl/db/ACS1907Test.java
@@ -0,0 +1,260 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 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.repo.search.impl.querymodel.impl.db;
+
+import junit.framework.TestCase;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.cache.TransactionalCache;
+import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
+import org.alfresco.repo.security.permissions.AccessControlList;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.*;
+import org.alfresco.service.cmr.security.MutableAuthenticationService;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.test_category.OwnJVMTestsCategory;
+import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.util.testing.category.DBTests;
+import org.junit.experimental.categories.Category;
+import org.springframework.context.ApplicationContext;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@Category({OwnJVMTestsCategory.class, DBTests.class})
+public class ACS1907Test extends TestCase
+{
+
+ private ApplicationContext ctx;
+
+ private NodeService nodeService;
+ private AuthenticationComponent authenticationComponent;
+ private MutableAuthenticationService authenticationService;
+ private MutableAuthenticationDao authenticationDAO;
+ private SearchService pubSearchService;
+ private PermissionService pubPermissionService;
+ private TransactionService transactionService;
+ private RetryingTransactionHelper txnHelper;
+ private DBQueryEngine queryEngine;
+
+ private TransactionalCache aclCache;
+ private TransactionalCache aclEntityCache;
+ private TransactionalCache permissionEntityCache;
+
+ private NodeRef rootNodeRef;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ setupServices();
+ this.authenticationComponent.setSystemUserAsCurrentUser();
+ rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
+ setupTestUsers();
+ setupTestContent();
+ dropCaches();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ authenticationComponent.clearCurrentSecurityContext();
+ }
+
+ private void setupServices()
+ {
+ ctx = ApplicationContextHelper.getApplicationContext();
+ nodeService = (NodeService) ctx.getBean("dbNodeService");
+ authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent");
+ authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService");
+ authenticationDAO = (MutableAuthenticationDao) ctx.getBean("authenticationDao");
+ pubSearchService = (SearchService) ctx.getBean("SearchService");
+ pubPermissionService = (PermissionService) ctx.getBean("PermissionService");
+ transactionService = (TransactionService) ctx.getBean("TransactionService");
+ aclCache = (TransactionalCache) ctx.getBean("aclCache");
+ aclEntityCache = (TransactionalCache) ctx.getBean("aclEntityCache");
+ permissionEntityCache = (TransactionalCache) ctx.getBean("permissionEntityCache");
+ SwitchableApplicationContextFactory searchContextFactory = (SwitchableApplicationContextFactory) ctx.getBean("Search");
+ ApplicationContext searchCtx = searchContextFactory.getApplicationContext();
+ queryEngine = (DBQueryEngine) searchCtx.getBean("search.dbQueryEngineImpl");
+ txnHelper = new RetryingTransactionHelper();
+ txnHelper.setTransactionService(transactionService);
+ txnHelper.setReadOnly(false);
+ txnHelper.setMaxRetries(1);
+ txnHelper.setMinRetryWaitMs(1);
+ txnHelper.setMaxRetryWaitMs(10);
+ txnHelper.setRetryWaitIncrementMs(1);
+ }
+
+ private void setupTestUser(String userName)
+ {
+ if (!authenticationDAO.userExists(userName))
+ {
+ authenticationService.createAuthentication(userName, userName.toCharArray());
+ }
+ }
+
+ private void setupTestUsers()
+ {
+ txnHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() {
+ @Override
+ public Object execute() throws Throwable {
+ setupTestUser("userA");
+ setupTestUser("userB");
+ setupTestUser(AuthenticationUtil.getAdminUserName());
+ return null;
+ }
+ }, false, false);
+ }
+
+ private void setupTestContent()
+ {
+ for(int f = 0; f < 5; f++)
+ {
+ final int ff = f;
+ txnHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() {
+ @Override
+ public Object execute() throws Throwable {
+ Map testFolderProps = new HashMap<>();
+ testFolderProps.put(ContentModel.PROP_NAME, "folder"+ff);
+ NodeRef testFolder = nodeService.createNode(
+ rootNodeRef,
+ ContentModel.ASSOC_CHILDREN,
+ QName.createQName("https://example.com/test", "folder"+ff),
+ ContentModel.TYPE_FOLDER,
+ testFolderProps
+ ).getChildRef();
+ for(int c = 0; c < 5; c++)
+ {
+ Map testContentProps = new HashMap<>();
+ testContentProps.put(ContentModel.PROP_NAME, "content"+c);
+ NodeRef testContent = nodeService.createNode(
+ testFolder,
+ ContentModel.ASSOC_CONTAINS,
+ QName.createQName("https://example.com/test", "content"+c),
+ ContentModel.TYPE_CONTENT,
+ testContentProps
+ ).getChildRef();
+ String user = c % 2 == 0 ? "userA" : "userB";
+ pubPermissionService.setPermission(testContent, user, "Read", true);
+ }
+ return null;
+ }
+ }, false, false);
+ }
+ }
+
+ private void dropCaches()
+ {
+ aclCache.clear();
+ aclEntityCache.clear();
+ permissionEntityCache.clear();
+ }
+
+ public void testACS1907()
+ {
+ txnHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() {
+ @Override
+ public Object execute() throws Throwable {
+ AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() {
+ @Override
+ public Object doWork() throws Exception {
+ SearchParameters sp = new SearchParameters();
+ sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
+ sp.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
+ sp.setQueryConsistency(QueryConsistency.TRANSACTIONAL);
+ sp.setQuery("TYPE:\"cm:content\"");
+ ResultSet rs = pubSearchService.query(sp);
+ int cnt = 0;
+ for (ResultSetRow row : rs)
+ {
+ assertNotNull(row.getValue(ContentModel.PROP_NAME));
+ cnt++;
+ }
+ return null;
+ }
+ }, "userA");
+ return null;
+ }
+ }, false, false);
+ }
+
+ public void testPaging()
+ {
+ HashSet resultPageSize2 = queryNodes(2);
+ HashSet resultPageSize5 = queryNodes(5);
+ HashSet resultPageSize10 = queryNodes(10);
+ HashSet resultPageSizeAll = queryNodes(10000);
+ // all result sets must be equal, independent of page size used to retrieve them
+ assertTrue(resultPageSize2.size() >= 25);
+ assertTrue(resultPageSize5.size() >= 25);
+ assertTrue(resultPageSize10.size() >= 25);
+ assertTrue(resultPageSizeAll.size() >= 25);
+ assertTrue(resultPageSize2.containsAll(resultPageSize5));
+ assertTrue(resultPageSize2.containsAll(resultPageSize10));
+ assertTrue(resultPageSize2.containsAll(resultPageSizeAll));
+ assertTrue(resultPageSize5.containsAll(resultPageSize2));
+ assertTrue(resultPageSize5.containsAll(resultPageSize10));
+ assertTrue(resultPageSize5.containsAll(resultPageSizeAll));
+ assertTrue(resultPageSize10.containsAll(resultPageSize2));
+ assertTrue(resultPageSize10.containsAll(resultPageSize5));
+ assertTrue(resultPageSize10.containsAll(resultPageSizeAll));
+ assertTrue(resultPageSizeAll.containsAll(resultPageSize2));
+ assertTrue(resultPageSizeAll.containsAll(resultPageSize5));
+ assertTrue(resultPageSizeAll.containsAll(resultPageSize10));
+ // reset
+ queryEngine.setMinPagingBatchSize(2500);
+ queryEngine.setMaxPagingBatchSize(10000);
+ }
+
+ HashSet queryNodes(int pageSize)
+ {
+ queryEngine.setMinPagingBatchSize(pageSize);
+ queryEngine.setMaxPagingBatchSize(pageSize);
+ HashSet result = new HashSet<>();
+ SearchParameters sp = new SearchParameters();
+ sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
+ sp.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
+ sp.setQueryConsistency(QueryConsistency.TRANSACTIONAL);
+ sp.setQuery("TYPE:\"cm:content\"");
+ ResultSet rs = pubSearchService.query(sp);
+ int cnt = 0;
+ for (ResultSetRow row : rs)
+ {
+ result.add(row.getNodeRef());
+ }
+ return result;
+ }
+
+}
diff --git a/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java b/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java
index 568a9dd526..adbaa5f8c1 100644
--- a/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java
+++ b/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java
@@ -25,6 +25,7 @@
*/
package org.alfresco.repo.security.authority;
+import static org.alfresco.repo.security.authority.AuthorityServiceImpl.GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -117,9 +118,10 @@ public class AuthorityServiceTest extends TestCase
private static final int DEFAULT_SITE_GRP_CNT = 5; // default number of groups per site
private static final int DEFAULT_SITE_ROOT_GRP_CNT = 1; // default number of root groups per site
- private static final int DEFAULT_GRP_CNT = 5; // default (non-site) bootstrap groups -
+ private static final int DEFAULT_GRP_CNT = 6; // default (non-site) bootstrap groups -
// eg. GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_SITE_ADMINISTRATORS,
- // GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS
+ // GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS,
+ // GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS
private int SITE_CNT = 0;
private int GRP_CNT = 0;
@@ -467,8 +469,8 @@ public class AuthorityServiceTest extends TestCase
assertTrue(authorityService.hasAdminAuthority());
assertTrue(pubAuthorityService.hasAdminAuthority());
Set authorities = authorityService.getAuthorities();
- // 6 => [GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_EVERYONE, GROUP_SITE_ADMINISTRATORS, ROLE_ADMINISTRATOR, GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS]
- assertEquals("Unexpected result: " + authorities, 7 + (SITE_CNT*2), authorityService.getAuthorities().size());
+ // 8 => [GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_EVERYONE, GROUP_SITE_ADMINISTRATORS, ROLE_ADMINISTRATOR, GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS, GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS]
+ assertEquals("Unexpected result: " + authorities, 8 + (SITE_CNT*2), authorityService.getAuthorities().size());
}
public void testNoUser()
@@ -1773,6 +1775,46 @@ public class AuthorityServiceTest extends TestCase
personService.deletePerson(username);
}
+ public void testAdminHasSysAdminAuthority()
+ {
+ authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
+ assertTrue(authorityService.hasAdminAuthority());
+ assertTrue("By default, Admin should be member of Alfresco_System_Administrators group.",
+ pubAuthorityService.hasSysAdminAuthority());
+ }
+
+ public void testSysAdminGroup()
+ {
+ personService.getPerson("andy");
+ // Make sure Andy is not part of ALFRESCO_ADMINISTRATORS group
+ String adminGroup = authorityService.getName(AuthorityType.GROUP, "ALFRESCO_ADMINISTRATORS");
+ authorityService.removeAuthority(adminGroup, "andy");
+ assertFalse(authorityService.isAdminAuthority("andy"));
+
+ // Set the current authentication to Andy, so we can check the runAsUser
+ authenticationComponent.setCurrentUser("andy");
+ assertFalse("Andy hasn't been added to the Alfresco_System_Administrators group yet.",
+ pubAuthorityService.hasSysAdminAuthority());
+
+ // Set the current authentication to admin in order to add Andy to the group
+ authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
+ pubAuthorityService.addAuthority(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, "andy");
+
+ // Set the current authentication to Andy, so we can check the runAsUser
+ authenticationComponent.setCurrentUser("andy");
+ assertTrue("Andy is a member of the Alfresco_System_Administrators group",
+ pubAuthorityService.hasSysAdminAuthority());
+
+ // Set the current authentication to admin in order to remove Andy from the group
+ authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
+ pubAuthorityService.removeAuthority(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, "andy");
+
+ // Set the current authentication to Andy, so we can check the runAsUser
+ authenticationComponent.setCurrentUser("andy");
+ assertFalse("Andy has been removed from the Alfresco_System_Administrators group.",
+ pubAuthorityService.hasSysAdminAuthority());
+ }
+
private T createClassPolicy(Class policyInterface, QName policyQName, QName triggerOnClass)
{
T policy = mock(policyInterface);
diff --git a/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java
index fdaa0f353f..3296d98227 100644
--- a/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java
+++ b/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -28,8 +28,12 @@ package org.alfresco.repo.version;
import java.util.Date;
import java.util.Set;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.openMocks;
+
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.ContentStore;
+import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -40,6 +44,9 @@ import org.alfresco.test_category.OwnJVMTestsCategory;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.annotation.Transactional;
@@ -51,7 +58,9 @@ import org.springframework.transaction.annotation.Transactional;
@Category(OwnJVMTestsCategory.class)
@Transactional
public class ContentServiceImplTest extends BaseVersionStoreTest
-{
+{
+ private static final Boolean ENABLED = Boolean.TRUE;
+
/**
* Test content data
*/
@@ -60,9 +69,14 @@ public class ContentServiceImplTest extends BaseVersionStoreTest
/**
* The version content store
*/
+ @InjectMocks
private ContentService contentService;
+
private ContentStore contentStore;
+ @Mock
+ private SystemWideDirectUrlConfig mockSystemWideDirectUrlConfig;
+
@Before
public void before() throws Exception
{
@@ -133,15 +147,17 @@ public class ContentServiceImplTest extends BaseVersionStoreTest
}
@Test
- public void testWhenGetDirectAccessUrlIsNotSupported()
+ public void testWhenRequestContentDirectUrlIsNotSupported()
{
- assertFalse(contentStore.isDirectAccessSupported());
+ openMocks(this);
+ when(mockSystemWideDirectUrlConfig.isEnabled()).thenReturn(ENABLED);
+ when(mockSystemWideDirectUrlConfig.getDefaultExpiryTimeInSec()).thenReturn(30L);
+ when(mockSystemWideDirectUrlConfig.getMaxExpiryTimeInSec()).thenReturn(300L);
+
+ assertFalse(contentStore.isContentDirectUrlEnabled());
// Set the presigned URL to expire after one minute.
- Date expiresAt = new Date();
- long expTimeMillis = expiresAt.getTime();
- expTimeMillis += 1000 * 60;
- expiresAt.setTime(expTimeMillis);
+ Long validFor = 60L;
try
{
@@ -149,7 +165,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest
NodeRef nodeRef = this.dbNodeService
.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}MyNoContentNode"), TEST_TYPE_QNAME, this.nodeProperties).getChildRef();
- assertEquals(null, contentService.getDirectAccessUrl(nodeRef, expiresAt));
+ assertNull(contentService.requestContentDirectUrl(nodeRef, true, validFor));
fail("nodeRef has no content");
}
catch (IllegalArgumentException e)
@@ -159,7 +175,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest
try
{
- assertEquals(null, contentService.getDirectAccessUrl(null, null));
+ assertNull(contentService.requestContentDirectUrl(null, true, null));
fail("nodeRef is null");
}
catch (IllegalArgumentException e)
@@ -170,8 +186,8 @@ public class ContentServiceImplTest extends BaseVersionStoreTest
// Create a node with content
NodeRef nodeRef = createNewVersionableNode();
- assertEquals(null, contentService.getDirectAccessUrl(nodeRef, null));
- assertEquals(null, contentService.getDirectAccessUrl(nodeRef, expiresAt));
+ assertNull(contentService.requestContentDirectUrl(nodeRef, true, null));
+ assertNull(contentService.requestContentDirectUrl(nodeRef, true, validFor));
}
@Test
diff --git a/repository/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java b/repository/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java
index a30f2e1399..866afecb63 100644
--- a/repository/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java
+++ b/repository/src/test/java/org/alfresco/transform/client/registry/LocalTransformServiceRegistryConfigTest.java
@@ -28,16 +28,19 @@ package org.alfresco.transform.client.registry;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.AbstractLocalTransform;
-import org.alfresco.repo.content.transform.LocalCombinedConfig;
import org.alfresco.repo.content.transform.LocalPipelineTransform;
import org.alfresco.repo.content.transform.LocalTransformImpl;
import org.alfresco.repo.content.transform.LocalTransformServiceRegistry;
import org.alfresco.repo.content.transform.TransformerDebug;
import org.alfresco.transform.client.model.config.SupportedSourceAndTarget;
import org.alfresco.transform.client.model.config.TransformOption;
+import org.alfresco.transform.client.model.config.TransformOptionGroup;
+import org.alfresco.transform.client.model.config.TransformOptionValue;
import org.alfresco.transform.client.model.config.Transformer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -48,6 +51,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -55,7 +59,9 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static java.util.Collections.emptyMap;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -63,11 +69,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
- * Extends the {@link TransformServiceRegistryConfigTest} (used to test the config received from the Transform Service)
- * so that configuration for the local transformations may be tested. This includes pipelines and options specific
- * transform steps.
+ * Testing LocalTransformServiceRegistry.
*/
-public class LocalTransformServiceRegistryConfigTest extends TransformServiceRegistryConfigTest
+public class LocalTransformServiceRegistryConfigTest extends TransformRegistryTest
{
public static final String HARD_CODED_VALUE = "hard coded value";
@@ -135,6 +139,12 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
super.logError(msg);
}
+ @Override
+ protected void logWarn(String msg)
+ {
+ logError(msg);
+ }
+
public Data assertDataChanged(Data prevData, String msg)
{
// If the data changes, there has been a read
@@ -169,10 +179,14 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
private static Log log = LogFactory.getLog(LocalTransformServiceRegistry.class);
+ public static final String PNG = "image/png";
+ public static final String TIFF = "image/tiff";
+
+ public static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper();
+
private static final String LOCAL_TRANSFORM_SERVICE_CONFIG = "alfresco/local-transform-service-config-test.json";
private static final String LOCAL_TRANSFORM_SERVICE_CONFIG_PIPELINE = "alfresco/local-transform-service-config-pipeline-test.json";
- private static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper();
private static final String LOCAL_TRANSFORM = "localTransform.";
private static final String URL = ".url";
@@ -202,8 +216,10 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
initTestData();
super.setUp();
+ LogManager.getLogger(LocalTransformServiceRegistryConfigTest.class).setLevel(Level.DEBUG);
}
+ @Override
protected LocalTransformServiceRegistry buildTransformServiceRegistryImpl() throws Exception
{
registry = new TestLocalTransformServiceRegistry();
@@ -222,19 +238,6 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
return (System.currentTimeMillis() - startMs) + "ms: ";
}
- @Override
- protected String getTransformServiceConfig()
- {
- return LOCAL_TRANSFORM_SERVICE_CONFIG;
- }
-
- @Override
- protected String getTransformServiceConfigPipeline()
- {
- return LOCAL_TRANSFORM_SERVICE_CONFIG_PIPELINE;
- }
-
- @Override
protected int getExpectedTransformsForTestJsonPipeline()
{
// imagemagick
@@ -259,7 +262,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
*/
private void retrieveLocalTransformList(String path)
{
- CombinedConfig combinedConfig = new LocalCombinedConfig(log);
+ CombinedConfig combinedConfig = new CombinedConfig(log);
combinedConfig.addLocalConfig(path);
combinedConfig.register(registry);
mapOfTransformOptions = combinedConfig.combinedTransformOptions;
@@ -379,11 +382,166 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
return i;
}
+ private void register(String path) throws IOException
+ {
+ CombinedConfig combinedConfig = new CombinedConfig(log);
+ combinedConfig.addLocalConfig(path);
+ combinedConfig.register((TransformServiceRegistryImpl)registry);
+ }
+
@Test
- @Override
public void testJsonConfig() throws IOException
{
- internalTestJsonConfig(64, 70);
+ register(LOCAL_TRANSFORM_SERVICE_CONFIG);
+
+ // Check the count of transforms supported
+ assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?",
+ 64, countSupportedTransforms(true));
+ assertEquals("The number of source to target mimetypes transforms has changed. " +
+ "There may be multiple transformers for the same combination. Config change?",
+ 70, countSupportedTransforms(false));
+
+ // Check a supported transform for each transformer.
+ assertSupported(DOC, 1234, PDF, emptyMap(), null, ""); // libreoffice
+ assertSupported(DOC, 1234, PDF, emptyMap(), null, ""); // libreoffice
+ assertSupported(PDF, 1234, PNG, emptyMap(), null, ""); // pdfrenderer
+ assertSupported(JPEG,1234, GIF, emptyMap(), null, ""); // imagemagick
+ assertSupported(MSG, 1234, TXT, emptyMap(), null, ""); // tika
+ assertSupported(MSG, 1234, GIF, emptyMap(), null, ""); // officeToImageViaPdf
+
+ Map invalidPdfOptions = new HashMap<>();
+ invalidPdfOptions.put("allowEnlargement", "false");
+ assertSupported(DOC, 1234, PDF, invalidPdfOptions, null, "Invalid as there is a extra option");
+ }
+
+ @Test
+ public void testJsonPipeline() throws IOException
+ {
+ register(LOCAL_TRANSFORM_SERVICE_CONFIG_PIPELINE);
+
+ // Check the count of transforms supported
+ int expectedTransforms = getExpectedTransformsForTestJsonPipeline();
+ assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?",
+ expectedTransforms, countSupportedTransforms(true));
+ assertEquals("The number of source to target mimetypes transforms has changed. " +
+ "There may be multiple transformers for the same combination. Config change?",
+ expectedTransforms, countSupportedTransforms(false));
+
+ // Check required and optional default correctly
+ Map> transformsToWord =
+ registry.getData().getTransforms().get(DOC);
+ List supportedTransforms = transformsToWord.get(GIF);
+ SupportedTransform supportedTransform = supportedTransforms.get(0);
+
+ Set transformOptionsSet = supportedTransform.getTransformOptions().getTransformOptions();
+
+ Iterator iterator = transformOptionsSet.iterator();
+ assertTrue("Expected transform values", iterator.hasNext());
+ // Because Set is unordered we don't know which TransformOptionGroup we retrieve
+ TransformOptionGroup transformOptions1 = (TransformOptionGroup)iterator.next();
+
+ assertTrue("Expected transform values", iterator.hasNext());
+ TransformOptionGroup transformOptions2 = (TransformOptionGroup)iterator.next();
+
+ TransformOptionGroup imagemagick;
+ TransformOptionGroup pdf;
+
+ if(containsTransformOptionValueName(transformOptions1, "alphaRemove"))
+ {
+ imagemagick = transformOptions1;
+ pdf = transformOptions2;
+ }
+ else
+ {
+ imagemagick = transformOptions2;
+ pdf = transformOptions1;
+ }
+
+ TransformOptionValue alphaRemove = (TransformOptionValue)retrieveTransformOptionByPropertyName(imagemagick, "alphaRemove", "TransformOptionValue");
+ TransformOptionGroup crop = (TransformOptionGroup)retrieveTransformOptionByPropertyName(imagemagick, "crop", "TransformOptionGroup");
+ TransformOptionValue cropGravity = (TransformOptionValue)retrieveTransformOptionByPropertyName(crop, "cropGravity", "TransformOptionValue");
+ TransformOptionValue cropWidth = (TransformOptionValue)retrieveTransformOptionByPropertyName(crop, "cropWidth", "TransformOptionValue");
+
+ assertTrue("The holding group should be required", supportedTransform.getTransformOptions().isRequired());
+ assertFalse("imagemagick should be optional as it is not set", imagemagick.isRequired());
+ assertFalse("pdf should be optional as required is not set", pdf.isRequired());
+ assertEquals("alphaRemove", alphaRemove.getName());
+ assertEquals("cropGravity", cropGravity.getName());
+ assertEquals("cropWidth", cropWidth.getName());
+ assertFalse("alphaRemove should be optional as required is not set", alphaRemove.isRequired());
+ assertFalse("crop should be optional as required is not set", crop.isRequired());
+ assertTrue("cropGravity should be required as it is set", cropGravity.isRequired());
+ assertFalse("cropWidth should be optional as required is not set", cropWidth.isRequired());
+
+ // Check a supported transform for each transformer.
+ assertSupported(DOC,1234, GIF, emptyMap(), null, "");
+ assertSupported(DOC,1234, PNG, emptyMap(), null, "");
+ assertSupported(DOC,1234, JPEG, emptyMap(), null, "");
+ assertSupported(DOC,1234, TIFF, emptyMap(), null, "");
+
+ Map actualOptions = new HashMap<>();
+ actualOptions.put("thumbnail", "true");
+ actualOptions.put("resizeWidth", "100");
+ actualOptions.put("resizeHeight", "100");
+ actualOptions.put("allowEnlargement", "false");
+ actualOptions.put("maintainAspectRatio", "true");
+ assertSupported(DOC,1234, PNG, actualOptions, null, "");
+ }
+
+ private TransformOption retrieveTransformOptionByPropertyName (TransformOptionGroup transformOptionGroup, String propertyName, String propertyType)
+ {
+ Iterator iterator = transformOptionGroup.getTransformOptions().iterator();
+
+ List transformOptionsList = new ArrayList<>();
+ while(iterator.hasNext())
+ {
+ transformOptionsList.add(iterator.next());
+ }
+
+ for (TransformOption t : transformOptionsList)
+ {
+ if (t instanceof TransformOptionValue)
+ {
+ TransformOptionValue value = (TransformOptionValue) t;
+ if (propertyType.equalsIgnoreCase("TransformOptionValue"))
+ {
+ if (value.getName().equalsIgnoreCase(propertyName))
+ return value;
+ }
+ else
+ {
+ if (value.getName().contains(propertyName))
+ return transformOptionGroup;
+ }
+ }
+ else
+ {
+ TransformOption result = retrieveTransformOptionByPropertyName((TransformOptionGroup)t, propertyName, propertyType);
+ if (result != null)
+ return result;
+ }
+ }
+ return null;
+ }
+
+ private boolean containsTransformOptionValueName (TransformOptionGroup transformOptionGroup, String propertyName)
+ {
+ return retrieveTransformOptionByPropertyName(transformOptionGroup, propertyName, "TransformOptionValue") != null;
+ }
+
+ private int countSupportedTransforms(boolean unique)
+ {
+ int count = 0;
+ int uniqueCount = 0;
+ for (Map> targetMap : registry.getData().getTransforms().values())
+ {
+ for (List supportedTransforms : targetMap.values())
+ {
+ uniqueCount++;
+ count += supportedTransforms.size();
+ }
+ }
+ return unique ? uniqueCount : count;
}
@Test
@@ -635,7 +793,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
public void testNoName()
{
retrieveLocalTransformList("alfresco/local-transform-service-config-no-name-test.json");
- registry.assertErrorLogged("Local transformer names may not be null.*no-name-test.*");
+ registry.assertErrorLogged("Transformer names may not be null.*no-name-test.*");
}
@Test
@@ -649,7 +807,7 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
public void testTEngineDuplicateNames()
{
retrieveLocalTransformList("alfresco/local-transform-service-config-dup-name-test.json");
- registry.assertErrorLogged("Local T-Engine transformer .* must be a unique name.*dup-name.*");
+ registry.assertErrorLogged("Transformer \"pdfrenderer\" must be a unique name.*dup-name.*");
}
@Test
@@ -657,21 +815,22 @@ public class LocalTransformServiceRegistryConfigTest extends TransformServiceReg
{
registry.setResetBaseUrl(false);
retrieveLocalTransformList("alfresco/local-transform-service-config-no-base-url-test.json");
- registry.assertErrorLogged("Local T-Engine transformer .* must have its baseUrl set .*no-base-url.*");
+ registry.assertErrorLogged("Single step transformers \\(such as \"pdfrenderer\"\\) must be defined in a " +
+ "T-Engine rather than in a pipeline file, unless they are overriding an existing single step definition.*no-base-url.*");
}
@Test
public void testPipelineMissingStepTransform()
{
retrieveLocalTransformList("alfresco/transform-service-config-pipeline-missing-step-test.json");
- registry.assertErrorLogged("Transformer .* ignored as step transforms do not exist.*pipeline-missing-step.*");
+ registry.assertErrorLogged("Transformer \"missingPdfrenderer\" ignored as step transforms \\(\"pdfrenderer\"\\) do not exist.*pipeline-missing-step-test.*");
}
@Test
public void testFailoverMissingStepTransform()
{
retrieveLocalTransformList("alfresco/transform-service-config-failover-missing-step-test.json");
- registry.assertErrorLogged("Transformer .* ignored as step transforms do not exist.*failover-missing-step.*");
+ registry.assertErrorLogged("Transformer \"missingPdfrenderer\" ignored as step transforms \\(\"pdfrenderer\"\\) do not exist.*failover-missing-step-test.*");
}
@Test
diff --git a/repository/src/test/java/org/alfresco/transform/client/registry/TransformServiceRegistryConfigTest.java b/repository/src/test/java/org/alfresco/transform/client/registry/TransformServiceRegistryConfigTest.java
deleted file mode 100644
index 2ccccada69..0000000000
--- a/repository/src/test/java/org/alfresco/transform/client/registry/TransformServiceRegistryConfigTest.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * #%L
- * Alfresco Repository
- * %%
- * 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.transform.client.registry;
-
-import static java.util.Collections.emptyMap;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.alfresco.transform.client.model.config.TransformOption;
-import org.alfresco.transform.client.model.config.TransformOptionGroup;
-import org.alfresco.transform.client.model.config.TransformOptionValue;
-import org.alfresco.transform.client.registry.SupportedTransform;
-import org.alfresco.transform.client.registry.TransformRegistryTest;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * Test the config received from the Transform Service about what it supports.
- *
- * @author adavis
- */
-public class TransformServiceRegistryConfigTest extends TransformRegistryTest
-{
- private static Log log = LogFactory.getLog(TransformServiceRegistryConfigTest.class);
-
- public static final String PNG = "image/png";
- public static final String TIFF = "image/tiff";
-
- private static final String TRANSFORM_SERVICE_CONFIG = "alfresco/transform-service-config-test.json";
- private static final String TRANSFORM_SERVICE_CONFIG_PIPELINE = "alfresco/transform-service-config-pipeline-test.json";
-
- public static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper();
-
- @Before
- @Override
- public void setUp() throws Exception
- {
- super.setUp();
- LogManager.getLogger(TransformServiceRegistryConfigTest.class).setLevel(Level.DEBUG);
- }
-
- @Override
- protected TransformServiceRegistryImpl buildTransformServiceRegistryImpl() throws Exception
- {
- TransformServiceRegistryImpl registry = new TransformServiceRegistryImpl()
- {
- @Override
- public boolean readConfig() throws IOException
- {
- return true;
- }
-
- @Override
- protected Log getLog()
- {
- return log;
- }
- };
- registry.setJsonObjectMapper(JSON_OBJECT_MAPPER);
- registry.setCronExpression(null); // just read once
- registry.afterPropertiesSet();
- return registry;
- }
-
- @After
- public void tearDown()
- {
- // shut down
- }
-
- protected String getTransformServiceConfig()
- {
- return TRANSFORM_SERVICE_CONFIG;
- }
-
- protected String getTransformServiceConfigPipeline()
- {
- return TRANSFORM_SERVICE_CONFIG_PIPELINE;
- }
-
- private void register(String path) throws IOException
- {
- CombinedConfig combinedConfig = new CombinedConfig(log);
- combinedConfig.addLocalConfig(path);
- combinedConfig.register((TransformServiceRegistryImpl)registry);
- }
-
- @Test
- public void testJsonConfig() throws IOException
- {
- internalTestJsonConfig(60, 60);
- }
-
- protected void internalTestJsonConfig(int expectedSourceTargetUniqueCount, int expectedSourceTargetCount) throws IOException
- {
- register(getTransformServiceConfig());
-
- // Check the count of transforms supported
- assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?",
- expectedSourceTargetUniqueCount, countSupportedTransforms(true));
- assertEquals("The number of source to target mimetypes transforms has changed. " +
- "There may be multiple transformers for the same combination. Config change?",
- expectedSourceTargetCount, countSupportedTransforms(false));
-
- // Check a supported transform for each transformer.
- assertSupported(DOC, 1234, PDF, emptyMap(), null, ""); // libreoffice
- assertSupported(DOC, 1234, PDF, emptyMap(), null, ""); // libreoffice
- assertSupported(PDF, 1234, PNG, emptyMap(), null, ""); // pdfrenderer
- assertSupported(JPEG,1234, GIF, emptyMap(), null, ""); // imagemagick
- assertSupported(MSG, 1234, TXT, emptyMap(), null, ""); // tika
- assertSupported(MSG, 1234, GIF, emptyMap(), null, ""); // officeToImageViaPdf
-
- Map invalidPdfOptions = new HashMap<>();
- invalidPdfOptions.put("allowEnlargement", "false");
- assertSupported(DOC, 1234, PDF, invalidPdfOptions, null, "Invalid as there is a extra option");
- }
-
- @Test
- public void testJsonPipeline() throws IOException
- {
- register(getTransformServiceConfigPipeline());
-
- // Check the count of transforms supported
- int expectedTransforms = getExpectedTransformsForTestJsonPipeline();
- assertEquals("The number of UNIQUE source to target mimetypes transforms has changed. Config change?",
- expectedTransforms, countSupportedTransforms(true));
- assertEquals("The number of source to target mimetypes transforms has changed. " +
- "There may be multiple transformers for the same combination. Config change?",
- expectedTransforms, countSupportedTransforms(false));
-
- // Check required and optional default correctly
- Map> transformsToWord =
- registry.getData().getTransforms().get(DOC);
- List supportedTransforms = transformsToWord.get(GIF);
- SupportedTransform supportedTransform = supportedTransforms.get(0);
-
- Set transformOptionsSet = supportedTransform.getTransformOptions().getTransformOptions();
-
- Iterator iterator = transformOptionsSet.iterator();
- assertTrue("Expected transform values", iterator.hasNext());
- // Because Set is unordered we don't know which TransformOptionGroup we retrieve
- TransformOptionGroup transformOptions1 = (TransformOptionGroup)iterator.next();
-
- assertTrue("Expected transform values", iterator.hasNext());
- TransformOptionGroup transformOptions2 = (TransformOptionGroup)iterator.next();
-
- TransformOptionGroup imagemagick;
- TransformOptionGroup pdf;
-
- if(containsTransformOptionValueName(transformOptions1, "alphaRemove"))
- {
- imagemagick = transformOptions1;
- pdf = transformOptions2;
- }
- else
- {
- imagemagick = transformOptions2;
- pdf = transformOptions1;
- }
-
- TransformOptionValue alphaRemove = (TransformOptionValue)retrieveTransformOptionByPropertyName(imagemagick, "alphaRemove", "TransformOptionValue");
- TransformOptionGroup crop = (TransformOptionGroup)retrieveTransformOptionByPropertyName(imagemagick, "crop", "TransformOptionGroup");
- TransformOptionValue cropGravity = (TransformOptionValue)retrieveTransformOptionByPropertyName(crop, "cropGravity", "TransformOptionValue");
- TransformOptionValue cropWidth = (TransformOptionValue)retrieveTransformOptionByPropertyName(crop, "cropWidth", "TransformOptionValue");
-
- assertTrue("The holding group should be required", supportedTransform.getTransformOptions().isRequired());
- assertFalse("imagemagick should be optional as it is not set", imagemagick.isRequired());
- assertFalse("pdf should be optional as required is not set", pdf.isRequired());
- assertEquals("alphaRemove", alphaRemove.getName());
- assertEquals("cropGravity", cropGravity.getName());
- assertEquals("cropWidth", cropWidth.getName());
- assertFalse("alphaRemove should be optional as required is not set", alphaRemove.isRequired());
- assertFalse("crop should be optional as required is not set", crop.isRequired());
- assertTrue("cropGravity should be required as it is set", cropGravity.isRequired());
- assertFalse("cropWidth should be optional as required is not set", cropWidth.isRequired());
-
- // Check a supported transform for each transformer.
- assertSupported(DOC,1234, GIF, emptyMap(), null, "");
- assertSupported(DOC,1234, PNG, emptyMap(), null, "");
- assertSupported(DOC,1234, JPEG, emptyMap(), null, "");
- assertSupported(DOC,1234, TIFF, emptyMap(), null, "");
-
- Map actualOptions = new HashMap<>();
- actualOptions.put("thumbnail", "true");
- actualOptions.put("resizeWidth", "100");
- actualOptions.put("resizeHeight", "100");
- actualOptions.put("allowEnlargement", "false");
- actualOptions.put("maintainAspectRatio", "true");
- assertSupported(DOC,1234, PNG, actualOptions, null, "");
- }
-
- private TransformOption retrieveTransformOptionByPropertyName (TransformOptionGroup transformOptionGroup, String propertyName, String propertyType)
- {
- Iterator iterator = transformOptionGroup.getTransformOptions().iterator();
-
- List transformOptionsList = new ArrayList<>();
- while(iterator.hasNext())
- {
- transformOptionsList.add(iterator.next());
- }
-
- for (TransformOption t : transformOptionsList)
- {
- if (t instanceof TransformOptionValue)
- {
- TransformOptionValue value = (TransformOptionValue) t;
- if (propertyType.equalsIgnoreCase("TransformOptionValue"))
- {
- if (value.getName().equalsIgnoreCase(propertyName))
- return value;
- }
- else
- {
- if (value.getName().contains(propertyName))
- return transformOptionGroup;
- }
- }
- else
- {
- TransformOption result = retrieveTransformOptionByPropertyName((TransformOptionGroup)t, propertyName, propertyType);
- if (result != null)
- return result;
- }
- }
- return null;
- }
-
- private boolean containsTransformOptionValueName (TransformOptionGroup transformOptionGroup, String propertyName)
- {
- return retrieveTransformOptionByPropertyName(transformOptionGroup, propertyName, "TransformOptionValue") != null;
- }
-
- protected int getExpectedTransformsForTestJsonPipeline()
- {
-// {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" },
-// {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"},
-// {"sourceMediaType": "application/msword", "targetMediaType": "image/png" },
-// {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"}
- return 4;
- }
-
- private int countSupportedTransforms(boolean unique)
- {
- int count = 0;
- int uniqueCount = 0;
- for (Map> targetMap : registry.getData().getTransforms().values())
- {
- for (List supportedTransforms : targetMap.values())
- {
- uniqueCount++;
- count += supportedTransforms.size();
- }
- }
- return unique ? uniqueCount : count;
- }
-}
\ No newline at end of file
diff --git a/repository/src/test/resources/alfresco/transform-service-config-pipeline-test.json b/repository/src/test/resources/alfresco/transform-service-config-pipeline-test.json
deleted file mode 100644
index 5160301fd9..0000000000
--- a/repository/src/test/resources/alfresco/transform-service-config-pipeline-test.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "transformOptions": {
- "imageMagickOptions": [
- {"value": {"name": "alphaRemove"}},
- {"value": {"name": "autoOrient"}},
- {"value": {"name": "startPage"}},
- {"value": {"name": "endPage"}},
- {"group": {"transformOptions": [
- {"value": {"required": "true", "name": "cropGravity"}},
- {"value": {"name": "cropWidth"}},
- {"value": {"name": "cropHeight"}},
- {"value": {"name": "cropPercentage"}},
- {"value": {"name": "cropXOffset"}},
- {"value": {"name": "cropYOffset"}}
- ]}},
- {"group": {"transformOptions": [
- {"value": {"name": "thumbnail"}},
- {"value": {"name": "resizeHeight"}},
- {"value": {"name": "resizeWidth"}},
- {"value": {"name": "resizePercentage"}},
- {"value": {"name": "allowEnlargement"}},
- {"value": {"name": "maintainAspectRatio"}}
- ]}}
- ],
- "pdfRendererOptions": [
- {"value": {"name": "page"}},
- {"value": {"name": "width"}},
- {"value": {"name": "height"}},
- {"value": {"name": "allowPdfEnlargement"}},
- {"value": {"name": "maintainPdfAspectRatio"}}
- ]
- },
- "transformers": [
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/msword", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"}
- ],
- "transformOptions": [
- "pdfRendererOptions",
- "imageMagickOptions"
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/repository/src/test/resources/alfresco/transform-service-config-test.json b/repository/src/test/resources/alfresco/transform-service-config-test.json
deleted file mode 100644
index 9b021fddf0..0000000000
--- a/repository/src/test/resources/alfresco/transform-service-config-test.json
+++ /dev/null
@@ -1,144 +0,0 @@
-{
- "transformOptions": {
- "imageMagickOptions": [
- {"value": {"name": "alphaRemove"}},
- {"value": {"name": "autoOrient"}},
- {"value": {"name": "startPage"}},
- {"value": {"name": "endPage"}},
- {"group": {"transformOptions": [
- {"value": {"name": "cropGravity"}},
- {"value": {"name": "cropWidth"}},
- {"value": {"name": "cropHeight"}},
- {"value": {"name": "cropPercentage"}},
- {"value": {"name": "cropXOffset"}},
- {"value": {"name": "cropYOffset"}}
- ]}},
- {"group": {"transformOptions": [
- {"value": {"name": "thumbnail"}},
- {"value": {"name": "resizeHeight"}},
- {"value": {"name": "resizeWidth"}},
- {"value": {"name": "resizePercentage"}},
- {"value": {"name": "allowEnlargement"}},
- {"value": {"name": "maintainAspectRatio"}}
- ]}}
- ],
- "tikaOptions": [
- {"value": {"name": "transform"}},
- {"value": {"name": "includeContents"}},
- {"value": {"name": "notExtractBookmarksText"}},
- {"value": {"name": "targetMimetype"}},
- {"value": {"name": "targetEncoding"}}
- ],
- "pdfRendererOptions": [
- {"value": {"name": "page"}},
- {"value": {"name": "width"}},
- {"value": {"name": "height"}},
- {"value": {"name": "allowPdfEnlargement"}},
- {"value": {"name": "maintainPdfAspectRatio"}}
- ]
- },
- "transformers": [
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "image/gif", "targetMediaType": "image/gif" },
- {"sourceMediaType": "image/gif", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "image/gif", "targetMediaType": "image/png" },
- {"sourceMediaType": "image/gif", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "image/jpeg", "targetMediaType": "image/gif" },
- {"sourceMediaType": "image/jpeg", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "image/jpeg", "targetMediaType": "image/png" },
- {"sourceMediaType": "image/jpeg", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "image/png", "targetMediaType": "image/gif" },
- {"sourceMediaType": "image/png", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "image/png", "targetMediaType": "image/png" },
- {"sourceMediaType": "image/png", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "image/tiff", "targetMediaType": "image/gif" },
- {"sourceMediaType": "image/tiff", "targetMediaType": "image/tiff"}
- ],
- "transformOptions": [
- "imageMagickOptions"
- ]
- },
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "application/pdf", "maxSourceSizeBytes": 26214400, "targetMediaType": "text/plain" },
- {"sourceMediaType": "application/msword", "targetMediaType": "text/plain"},
- {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "text/plain" },
- {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "text/plain" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "text/plain"},
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "text/plain" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "text/plain" },
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "text/plain"}
- ],
- "transformOptions": [
- "tikaOptions"
- ]
- },
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "application/pdf", "targetMediaType": "image/png" }
- ],
- "transformOptions": [
- "pdfRendererOptions"
- ]
- },
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "application/msword", "targetMediaType": "application/msword" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "application/msword" },
- {"sourceMediaType": "application/msword", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf" },
- {"sourceMediaType": "application/vnd.ms-excel", "maxSourceSizeBytes": 10485760, "targetMediaType": "application/pdf"},
- {"sourceMediaType": "application/vnd.ms-powerpoint", "maxSourceSizeBytes": 6291456, "targetMediaType": "application/pdf" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "maxSourceSizeBytes": 786432, "targetMediaType": "application/pdf" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "maxSourceSizeBytes": 1572864, "targetMediaType": "application/pdf"},
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "maxSourceSizeBytes": 4194304, "targetMediaType": "application/pdf" },
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "application/pdf"}
- ]
- },
- {
- "supportedSourceAndTargetList": [
- {"sourceMediaType": "application/msword", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/msword", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/msword", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/msword", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.ms-excel", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.ms-powerpoint", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "targetMediaType": "image/tiff"},
-
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/gif" },
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/jpeg"},
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/png" },
- {"sourceMediaType": "application/vnd.ms-outlook", "targetMediaType": "image/tiff"}
- ],
- "transformOptions": [
- "pdfRendererOptions",
- "imageMagickOptions"
- ]
- }
- ]
-}
\ No newline at end of file