mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			371 Commits
		
	
	
		
			20.136
			...
			APPS-2000_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3ffe06391b | ||
|  | 92ff7151d8 | ||
|  | d7722e4f25 | ||
|  | 6a87008665 | ||
|  | 6d14548119 | ||
|  | 67c4751676 | ||
|  | d4a60a1d24 | ||
|  | 4388a75924 | ||
|  | 5982ec7172 | ||
|  | 7f26658eba | ||
|  | 88273f3073 | ||
|  | 7218864a44 | ||
|  | 133dd2b1c1 | ||
|  | ade270743e | ||
|  | 59b50f0c67 | ||
|  | 8837c15a67 | ||
|  | bac818baf2 | ||
|  | 2f6c845865 | ||
|  | 347627d0fd | ||
|  | a495dae1db | ||
|  | a07da7308f | ||
|  | 8b472e89f5 | ||
|  | a3aaaedb0e | ||
|  | 044d07af86 | ||
|  | 110ccd5574 | ||
|  | 8fbe444266 | ||
|  | 5ef0bbb840 | ||
|  | ba5720b494 | ||
|  | 866adc16f3 | ||
|  | 577788a8c6 | ||
|  | 6b02b4dc05 | ||
|  | fc847ac7a5 | ||
|  | 25e8f0c6f8 | ||
|  | 499cf08ecd | ||
|  | f0c191d7ca | ||
|  | a1faf97fc5 | ||
|  | d372ff6f5e | ||
|  | 095bf0e8d8 | ||
|  | c23db31ca4 | ||
|  | 6cfcc8b207 | ||
|  | 20582dfcf9 | ||
|  | fb6b1ef4ab | ||
|  | 7fc184ccf3 | ||
|  | 732d4e4733 | ||
|  | bd94060fea | ||
|  | d46ae1634f | ||
|  | 7bd31eca5f | ||
|  | 8938aaff09 | ||
|  | 18dadfe110 | ||
|  | 4e9fb76404 | ||
|  | 295c3ba58e | ||
|  | 195e0e927c | ||
|  | b0adc1a6bb | ||
|  | 828c98d03f | ||
|  | 1d4c97cfb7 | ||
|  | e0d5d0691f | ||
|  | 96942ceeb9 | ||
|  | 2d16c28859 | ||
|  | 51fe0275df | ||
|  | 4786a0fb0b | ||
|  | 3f91a6001f | ||
|  | 1f1b534a28 | ||
|  | 689e6a23fa | ||
|  | 562c83bb9c | ||
|  | 4ff91be9f4 | ||
|  | f3190133c3 | ||
|  | 8325cd7c66 | ||
|  | aeb931b018 | ||
|  | 6679a33412 | ||
|  | aea504f911 | ||
|  | fb235e1682 | ||
|  | 4065400dbd | ||
|  | 02aa423087 | ||
|  | 0dbd2fb3d0 | ||
|  | c30c96de47 | ||
|  | fa9ffbe71a | ||
|  | 826db75625 | ||
|  | 04df3385fd | ||
|  | cc12b34c7f | ||
|  | c110e2b76c | ||
|  | 3d1ef4883b | ||
|  | afa2bbf325 | ||
|  | 1d665f4e50 | ||
|  | 6a527068dc | ||
|  | a45c04679f | ||
|  | bc6398c038 | ||
|  | 43f1100040 | ||
|  | 8bade25669 | ||
|  | 1e6e87f24a | ||
|  | cac97dc1e7 | ||
|  | 3bcef29c30 | ||
|  | fb98bb09f0 | ||
|  | ad723a700a | ||
|  | 1bc5bed838 | ||
|  | beaf83a008 | ||
|  | 8734eec068 | ||
|  | 1e506659d2 | ||
|  | b834ab9245 | ||
|  | 402c0661e0 | ||
|  | 763f0932a0 | ||
|  | 0403fcde69 | ||
|  | 1866e64d07 | ||
|  | 47b793b922 | ||
|  | 8ebaf16a45 | ||
|  | c3dcf8891e | ||
|  | 5cdb5f2922 | ||
|  | 2bac9bd9d1 | ||
|  | fffef168d9 | ||
|  | 0f89c3ce8a | ||
|  | 497b197c12 | ||
|  | 2b8ea3bb50 | ||
|  | 5e7394a37d | ||
|  | 89329d7984 | ||
|  | 3f94b1632b | ||
|  | d0f6867887 | ||
|  | 49c56e9a9d | ||
|  | 78b54e65c4 | ||
|  | b93212f3bf | ||
|  | 636d63e34f | ||
|  | ad934e511d | ||
|  | 16db159092 | ||
|  | fd8e44e365 | ||
|  | ed924094ff | ||
|  | 3181d2b8bf | ||
|  | 2891a3ba61 | ||
|  | 92f27bbcd7 | ||
|  | 80d4db2e81 | ||
|  | 6bd598fadf | ||
|  | c1b4b30262 | ||
|  | f0e5dcaa67 | ||
|  | 07c7342032 | ||
|  | b0af817377 | ||
|  | e8cf0f7d12 | ||
|  | b8e621d3d9 | ||
|  | a62db22ecb | ||
|  | 5fcee3ec55 | ||
|  | 8a6274b37e | ||
|  | d279b3e3c3 | ||
|  | da5505a21f | ||
|  | e476334998 | ||
|  | 7990991453 | ||
|  | 9289fc7f89 | ||
|  | 20af2a5dd9 | ||
|  | 59069ae38f | ||
|  | ea4eac5a77 | ||
|  | 83bc0dcee1 | ||
|  | 5af7a1e284 | ||
|  | 11333eb1e5 | ||
|  | e901ffb751 | ||
|  | 7dbe25d754 | ||
|  | 1f764b446e | ||
|  | 4c256ab546 | ||
|  | 8fe2f5b251 | ||
|  | 841826fcdd | ||
|  | 1a98715d35 | ||
|  | 334126ec72 | ||
|  | e02c334362 | ||
|  | 269cc165e0 | ||
|  | b724203f5b | ||
|  | 5912726372 | ||
|  | eb85205df1 | ||
|  | 88616a2c48 | ||
|  | 90e9764d63 | ||
|  | 50e91a40bc | ||
|  | 391ba106c4 | ||
|  | 46942b9e77 | ||
|  | 4ff76617a9 | ||
|  | 82c671c07a | ||
|  | 06ce348cdd | ||
|  | 099cbb614c | ||
|  | 0acc6d31ef | ||
|  | a218cfa0ed | ||
|  | e00959a089 | ||
|  | a9ac83704b | ||
|  | 603f334c46 | ||
|  | 2de971dad3 | ||
|  | 31f347e42f | ||
|  | e88b4e96ee | ||
|  | 6e408235f2 | ||
|  | 182211ee32 | ||
|  | 380b63d63b | ||
|  | 08312a5153 | ||
|  | 9dafa748af | ||
|  | fec31aed1c | ||
|  | 92af01368b | ||
|  | 019b3d4de3 | ||
|  | c2728a4239 | ||
|  | 9f68abcfda | ||
|  | 390e533107 | ||
|  | a7e0491532 | ||
|  | 434e4d3dc3 | ||
|  | c88d1802a1 | ||
|  | 490c87ec9a | ||
|  | ae479351c2 | ||
|  | 44cf210b27 | ||
|  | 99a1c05c39 | ||
|  | 4f6786ee95 | ||
|  | 5d9dca1872 | ||
|  | bf7f64add4 | ||
|  | 7d15862b69 | ||
|  | 2e8ec4c226 | ||
|  | 24752d0409 | ||
|  | ff8804e7db | ||
|  | 02a62cb5f7 | ||
|  | e1afe15055 | ||
|  | 0fcdf3b00b | ||
|  | fa63cef4f7 | ||
|  | 27f41e11b3 | ||
|  | affa20e996 | ||
|  | 0df9f562e6 | ||
|  | 9a3ec69b27 | ||
|  | 7126406bf3 | ||
|  | edc43ed5b9 | ||
|  | 907aed826a | ||
|  | eaa96607b9 | ||
|  | 0d5d9ff0b4 | ||
|  | 5266304805 | ||
|  | 63e77b6f86 | ||
|  | 9cf74e4dc4 | ||
|  | 4107a0fd0e | ||
|  | 8554fe0d74 | ||
|  | 2ed88cd1be | ||
|  | 8f4f4e938c | ||
|  | dc76b15aa6 | ||
|  | 0109db978f | ||
|  | 6461629024 | ||
|  | c35dbb0481 | ||
|  | 637523c2c8 | ||
|  | f1a54895f6 | ||
|  | 7f6d9443e2 | ||
|  | 297d72c246 | ||
|  | 7208ad9b62 | ||
|  | 31ca2726a8 | ||
|  | 6f43286441 | ||
|  | aec50a991f | ||
|  | f62b937ded | ||
|  | 66d3d72f7c | ||
|  | 0b4b9fd47a | ||
|  | 2b955d21f9 | ||
|  | 6a853f3b92 | ||
|  | c293ace4e0 | ||
|  | 86dfdd8df4 | ||
|  | abd73e820c | ||
|  | 09bec28721 | ||
|  | 0d1a6c8a0a | ||
|  | 4b9c052f0d | ||
|  | 6fb0bb3042 | ||
|  | 78f3d58c46 | ||
|  | 1913258e84 | ||
|  | d95e6c8e34 | ||
|  | 958d217339 | ||
|  | 4417e28412 | ||
|  | 58653cb523 | ||
|  | 7af935d43a | ||
|  | f5d843446a | ||
|  | 817901e74e | ||
|  | c492683113 | ||
|  | e2305d053f | ||
|  | 749768457e | ||
|  | 118f2ecbab | ||
|  | a4ac93df7a | ||
|  | 1e1fbe8207 | ||
|  | e78f9ced98 | ||
|  | 72221c777d | ||
|  | 25f3f33594 | ||
|  | f83328f7b0 | ||
|  | a177f391db | ||
|  | 784557afcd | ||
|  | 8fb7b0d224 | ||
|  | 011ec9d7e6 | ||
|  | 3900f589d9 | ||
|  | def7a0d432 | ||
|  | 7371c5d7ff | ||
|  | 90406cef03 | ||
|  | 862e6d5596 | ||
|  | 27be717df5 | ||
|  | 28ad071daf | ||
|  | 06cc5e3499 | ||
|  | df8b36350d | ||
|  | 552abc9bd4 | ||
|  | 00cab8e6b5 | ||
|  | 6f4069790c | ||
|  | 146b59a4a8 | ||
|  | 2c3845bf9d | ||
|  | dd05f3d338 | ||
|  | c2338bdeb2 | ||
|  | 589e14a0b1 | ||
|  | 0f753c11c7 | ||
|  | c8fea93298 | ||
|  | d7f881ce0c | ||
|  | b5fabb1290 | ||
|  | f6d3ff4b15 | ||
|  | 2eb5bb7b7a | ||
|  | 53909dc086 | ||
|  | 4fedf0bada | ||
|  | 9f6edc648e | ||
|  | 22298eaa46 | ||
|  | 2e9db406d4 | ||
|  | 8565c9413e | ||
|  | 0d411cd759 | ||
|  | ca3bbf5226 | ||
|  | e83cd86c4d | ||
|  | 7da314cd97 | ||
|  | d39401a7ec | ||
|  | 05df8a7582 | ||
|  | 85f22cb6e4 | ||
|  | ce77b1ff42 | ||
|  | 29a7de55d2 | ||
|  | cb3cb85694 | ||
|  | 2fcf1bd2d5 | ||
|  | 4c687f670e | ||
|  | dad0094a46 | ||
|  | 06fdc4302e | ||
|  | b1466915af | ||
|  | 6be773ba18 | ||
|  | fbcfe68c99 | ||
|  | e50118115e | ||
|  | 3a2136b886 | ||
|  | 86464427a0 | ||
|  | 7e9e0e1ad4 | ||
|  | 32c3a5ad90 | ||
|  | e983e4ed22 | ||
|  | ba197fcf70 | ||
|  | 18e6965a61 | ||
|  | c0757f45a2 | ||
|  | 7bdbc3a91a | ||
|  | 25fa97bdd8 | ||
|  | 7592b637cc | ||
|  | 70c490f26c | ||
|  | 93f7d06cf0 | ||
|  | 657318c6ae | ||
|  | 4f4d8210ef | ||
|  | 14ba7f5d55 | ||
|  | c4a4aedad7 | ||
|  | be60d67377 | ||
|  | 806131c21c | ||
|  | b0f767c1ae | ||
|  | baccde2663 | ||
|  | e048278a27 | ||
|  | 8586d22b95 | ||
|  | 3dec621b15 | ||
|  | 64202ee9d5 | ||
|  | 2b92022b5b | ||
|  | 9a56a052e8 | ||
|  | 1f38c24bbd | ||
|  | 0bcb96e51e | ||
|  | df522a38b9 | ||
|  | 1458ef3311 | ||
|  | 2bdca1a4f5 | ||
|  | 07cc9aa86e | ||
|  | 7db550cee1 | ||
|  | 937d80327f | ||
|  | 8abff868cc | ||
|  | ffae8a62a1 | ||
|  | 0350c966df | ||
|  | 833a7675bc | ||
|  | 8aa263f377 | ||
|  | 73f30b7ef2 | ||
|  | 4bf58cc99c | ||
|  | d53881d87f | ||
|  | 937601020d | ||
|  | 429b389b08 | ||
|  | 0612d74c68 | ||
|  | eb6157ed2a | ||
|  | 72a573c090 | ||
|  | 13e3369a7f | ||
|  | 444de08f91 | ||
|  | 5f5c377532 | ||
|  | f1561504cd | ||
|  | 653f733887 | ||
|  | 623782dc97 | 
							
								
								
									
										54
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,6 +23,7 @@ env: | ||||
|   MAVEN_USERNAME: ${{ secrets.NEXUS_USERNAME }} | ||||
|   QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} | ||||
|   QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }} | ||||
|   CI_WORKSPACE: ${{ github.workspace }} | ||||
|   TAS_ENVIRONMENT: ./packaging/tests/environment | ||||
|   TAS_SCRIPTS: ../alfresco-community-repo/packaging/tests/scripts | ||||
|  | ||||
| @@ -64,7 +65,21 @@ jobs: | ||||
|           srcclr-api-token: ${{ secrets.SRCCLR_API_TOKEN }} | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|          | ||||
|  | ||||
|   pmd_scan: | ||||
|     name: "PMD Scan" | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [prepare] | ||||
|     if: > | ||||
|       github.event_name == 'pull_request' && | ||||
|       !contains(github.event.head_commit.message, '[skip pmd]') && | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force]') | ||||
|     steps: | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/pmd@v2.5.0 | ||||
|         with: | ||||
|           fail-on-new-issues: "false" | ||||
|  | ||||
|   all_unit_tests_suite: | ||||
|     name: "Core, Data-Model, Repository - AllUnitTestsSuite - Build and test" | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -316,7 +331,8 @@ jobs: | ||||
|           - testSuite: MTLSTestSuite | ||||
|             compose-profile: with-mtls-transform-core-aio | ||||
|             mtls: true | ||||
|             mvn-options: '-Dencryption.ssl.keystore.location=${GITHUB_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${GITHUB_WORKSPACE}/keystores/alfresco/alfresco.truststore' | ||||
|             disabledHostnameVerification: false | ||||
|             mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0 | ||||
| @@ -328,8 +344,14 @@ jobs: | ||||
|       - name: "Generate Keystores and Truststores for Mutual TLS configuration" | ||||
|         if: ${{ matrix.mtls }} | ||||
|         run: | | ||||
|          git clone -b "master" --depth=1 "https://${{ secrets.BOT_GITHUB_USERNAME }}:${{ secrets.BOT_GITHUB_TOKEN }}@github.com/Alfresco/alfresco-ssl-generator.git" | ||||
|          bash ./scripts/ci/generate_keystores.sh | ||||
|           git clone -b "master" --depth=1 "https://${{ secrets.BOT_GITHUB_USERNAME }}:${{ secrets.BOT_GITHUB_TOKEN }}@github.com/Alfresco/alfresco-ssl-generator.git" | ||||
|           if ${{ matrix.disabledHostnameVerification }} ; then | ||||
|             bash ${{ env.CI_WORKSPACE }}/alfresco-ssl-generator/scripts/ci/generate_keystores_wrong_hostnames.sh | ||||
|             echo "HOSTNAME_VERIFICATION_DISABLED=true" >> "$GITHUB_ENV" | ||||
|           else | ||||
|             bash ${{ env.CI_WORKSPACE }}/alfresco-ssl-generator/scripts/ci/generate_keystores.sh | ||||
|             echo "HOSTNAME_VERIFICATION_DISABLED=false" >> "$GITHUB_ENV" | ||||
|           fi | ||||
|       - name: "Set up the environment" | ||||
|         run: | | ||||
|           if [ -e ./scripts/ci/tests/${{ matrix.testSuite }}-setup.sh ]; then | ||||
| @@ -539,3 +561,27 @@ jobs: | ||||
|           aws s3 cp --acl private ./amps/ags/rm-automation/rm-automation-community-rest-api/target/reports/rm-automation-community-rest-api.log s3://ags-travis-artifacts/community/${{ github.run_number }}/AGS-Community-Rest-API-Tests/rm-automation-community-rest-api.log | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
|   ags_start_api_explorer: | ||||
|     name: "Test Tomcat deployment of api explorer" | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [ prepare ] | ||||
|     if: > | ||||
|       (((github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request' ) && | ||||
|       !contains(github.event.head_commit.message, '[skip ags]')) || | ||||
|       contains(github.event.head_commit.message, '[ags]')) && | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force]') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.33.0 | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
|           bash ./scripts/ci/init.sh | ||||
|           bash ./scripts/ci/build.sh | ||||
|       - name: "Test Tomcat deployment" | ||||
|         run: | | ||||
|           mvn verify -Pags,start-api-explorer -DskipTests & | ||||
|           ${{ env.TAS_SCRIPTS }}/wait-for-alfresco-start.sh "http://localhost:8085/api-explorer" | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <build> | ||||
| @@ -82,7 +82,7 @@ | ||||
|       <dependency> | ||||
|          <groupId>com.github.docker-java</groupId> | ||||
|          <artifactId>docker-java</artifactId> | ||||
|          <version>3.2.13</version> | ||||
|          <version>3.3.2</version> | ||||
|       </dependency> | ||||
|    </dependencies> | ||||
| </project> | ||||
|   | ||||
| @@ -512,7 +512,12 @@ public abstract class BaseAPI | ||||
|         try | ||||
|         { | ||||
|             HttpResponse httpResponse = doRequestJson(HttpPost.class, requestUrl, adminUser, adminPassword, requestParams); | ||||
|             assertEquals("POST request to " + requestUrl + " was not successful.", expectedStatusCode, httpResponse.getStatusLine().getStatusCode()); | ||||
|             if (httpResponse.getStatusLine().getStatusCode() != expectedStatusCode) | ||||
|             { | ||||
|                 // It's only possible to stream the response body once, so ensure we only do this if the test has failed. | ||||
|                 JSONObject responseJson = responseBodyToJson(httpResponse); | ||||
|                 assertEquals("POST request to " + requestUrl + " was not successful. Response: " + responseJson, expectedStatusCode, httpResponse.getStatusLine().getStatusCode()); | ||||
|             } | ||||
|             return httpResponse; | ||||
|         } | ||||
|         catch (InstantiationException | IllegalAccessException error) | ||||
| @@ -521,6 +526,32 @@ public abstract class BaseAPI | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Try to convert the response body to a JSON object. | ||||
|      * | ||||
|      * @param response The response. | ||||
|      * @return The JSON object or null if it was not possible to convert the response. | ||||
|      */ | ||||
|     private JSONObject responseBodyToJson(HttpResponse response) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 return new JSONObject(EntityUtils.toString(response.getEntity())); | ||||
|             } | ||||
|             catch (JSONException error) | ||||
|             { | ||||
|                 LOGGER.error("Converting message body to JSON failed. Body: {}", response.getEntity().getContent(), error); | ||||
|             } | ||||
|         } | ||||
|         catch (ParseException | IOException error) | ||||
|         { | ||||
|             LOGGER.error("Parsing message body failed.", error); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Helper method for handling generic HTTP requests | ||||
|      * @param requestType request type (a subclass of {@link HttpRequestBase}) | ||||
| @@ -558,18 +589,7 @@ public abstract class BaseAPI | ||||
|             HttpResponse response = client.execute(adminUser, adminPassword, request); | ||||
|             LOGGER.info("Response: {}", response.getStatusLine()); | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 responseBody = new JSONObject(EntityUtils.toString(response.getEntity())); | ||||
|             } | ||||
|             catch (JSONException error) | ||||
|             { | ||||
|                 LOGGER.error("Converting message body to JSON failed. Body: {}", responseBody, error); | ||||
|             } | ||||
|             catch (ParseException | IOException error) | ||||
|             { | ||||
|                 LOGGER.error("Parsing message body failed.", error); | ||||
|             } | ||||
|             responseBody = responseBodyToJson(response); | ||||
|  | ||||
|             switch (response.getStatusLine().getStatusCode()) | ||||
|             { | ||||
|   | ||||
| @@ -50,6 +50,7 @@ import org.apache.http.HttpResponse; | ||||
| import org.apache.http.HttpStatus; | ||||
| import org.apache.http.util.EntityUtils; | ||||
| import org.json.JSONObject; | ||||
| import org.junit.Ignore; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.testng.AssertJUnit; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| @@ -135,192 +136,195 @@ public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest { | ||||
|      * <p> | ||||
|      * <p/> TestRail Test C775<p/> | ||||
|      **/ | ||||
|     @Test | ||||
|     @AlfrescoTest(jira = "RM-1622") | ||||
|     public void dispositionScheduleLinkedRecords() throws UnsupportedEncodingException { | ||||
|         STEP("Create record category"); | ||||
|         Category1 = createRootCategory(categoryRM3077); | ||||
| //    @Ignore("ACS-5020") | ||||
|     //    @Test | ||||
| //    @AlfrescoTest(jira = "RM-1622") | ||||
| //    public void dispositionScheduleLinkedRecords() throws UnsupportedEncodingException { | ||||
| //        STEP("Create record category"); | ||||
| //        Category1 = createRootCategory(categoryRM3077); | ||||
| // | ||||
| //        //create retention schedule | ||||
| //        dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false); | ||||
| // | ||||
| //        // add cut off step | ||||
| //        dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE); | ||||
| // | ||||
| //        //create a copy of the category recordsCategory | ||||
| //        String CopyCategoryId = copyCategory(getAdminUser(),Category1.getId(), copyCategoryRM3077); | ||||
| // | ||||
| //        // create folders in both categories | ||||
| //        CatFolder = createRecordFolder(Category1.getId(), folderRM3077); | ||||
| //        CopyCatFolder = createRecordFolder(CopyCategoryId, copyFolderRM3077); | ||||
| // | ||||
| //        // create record  files | ||||
| //        String electronicRecord = "RM-2801 electronic record"; | ||||
| //        Record elRecord = createElectronicRecord(CatFolder.getId(), electronicRecord); | ||||
| //        String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //            getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), electronicRecord); | ||||
| // | ||||
| //        String nonElectronicRecord = "RM-2801 non-electronic record"; | ||||
| //        Record nonElRecord = createNonElectronicRecord(CatFolder.getId(), nonElectronicRecord); | ||||
| //        String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //            getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), nonElectronicRecord); | ||||
| // | ||||
| //        // link the records to copy folder, then complete them | ||||
| //        List<String> recordLists = new ArrayList<>(); | ||||
| //        recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + elRecord.getId()); | ||||
| //        recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + nonElRecord.getId()); | ||||
| // | ||||
| //        linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,copyCategoryRM3077 + "/" + | ||||
| //                copyFolderRM3077, recordLists); | ||||
| //        recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), elRecordFullName); | ||||
| //        recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), nonElRecordFullName); | ||||
| // | ||||
| //        // edit disposition date | ||||
| //        recordFoldersAPI.postFolderAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),editDispositionDateJson(),CatFolder.getName()); | ||||
| // | ||||
| //        // cut off the Folder | ||||
| //        recordFoldersAPI.postFolderAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),CatFolder.getName()); | ||||
| // | ||||
| //        // Verify the Content | ||||
| //        Node electronicNode = getNode(elRecord.getId()); | ||||
| //        assertTrue("The content of " + electronicRecord + " is available", | ||||
| //            StringUtils.isEmpty(electronicNode.getNodeContent().getResponse().getBody().asString())); | ||||
| // | ||||
| //        // verify the Properties | ||||
| //        AssertJUnit.assertNull("The properties are present even after cutting off the record.", elRecord.getProperties().getTitle()); | ||||
| // | ||||
| //        // delete precondition | ||||
| //        deleteRecordCategory(Category1.getId()); | ||||
| //        deleteRecordCategory(CopyCategoryId); | ||||
| //    } | ||||
| //    /** | ||||
| //     * Test covering RM-3060 | ||||
| //     * Check the disposition steps for a record can be executed | ||||
| //     * When the record is linked to a folder with the same disposition schedule | ||||
| //     * */ | ||||
| //    @Ignore("ACS-5020") | ||||
| ////    @Test | ||||
| //    @AlfrescoTest (jira = "RM-3060") | ||||
| //    public void sameDispositionScheduleLinkedRecords() throws UnsupportedEncodingException { | ||||
| // | ||||
| //        // create a category with retention applied on records level | ||||
| //        RecordCategory recordCategory = getRestAPIFactory().getFilePlansAPI(rmAdmin) | ||||
| //            .createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(), | ||||
| //                RecordCategory.DEFAULT_FILE_PLAN_ALIAS); | ||||
| //        dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true); | ||||
| //        dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "week|1", DATE_FILED); | ||||
| //        dispositionScheduleService.addTransferAfterEventStep(firstCategoryRM3060, TRANSFER_LOCATION, RMEvents.CASE_CLOSED.getEventName()); | ||||
| //        dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(firstCategoryRM3060, "week|1", CUT_OFF_DATE); | ||||
| // | ||||
| //        // make a copy of the category created | ||||
| //        String categorySecondId = copyCategory(getAdminUser(), recordCategory.getId(), secondCategoryRM3060); | ||||
| // | ||||
| //        // create a folder on the category firstCategoryRM3060 with a complete electronic record | ||||
| //        RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(recordCategory.getId(),firstFolderRM3060); | ||||
| //        Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060); | ||||
| // | ||||
| //        String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060); | ||||
| //        String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //            getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060); | ||||
| // | ||||
| //        recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(), elRecordFullName); | ||||
| // | ||||
| //        // create a folder on the category secondCategoryRM3060 with a non electronic record | ||||
| //        RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(categorySecondId,secondFolderRM3060); | ||||
| //        Record secondNonElectronicRecord = createNonElectronicRecord(secondFolderRecordCategoryChild.getId(),nonElectronicRecordRM3060); | ||||
| // | ||||
| //        // link the nonElectronicRecordRM3060 to firstFolderRM3060 | ||||
| //        List<String> recordLists = new ArrayList<>(); | ||||
| //        recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId()); | ||||
| // | ||||
| //        linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" + | ||||
| //                secondFolderRM3060, recordLists); | ||||
| //        String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //            getDataUser().usingAdmin().getAdminUser().getPassword(), secondFolderRM3060, secondNonElectronicRecord.getName()); | ||||
| //        String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //            getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060); | ||||
| // | ||||
| //        // complete records and cut them off | ||||
| //        recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(), nonElRecordFullName); | ||||
| // | ||||
| //        // edit the disposition date | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef); | ||||
| // | ||||
| //        // cut off the record | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef); | ||||
| // | ||||
| //        //check the record is cut off | ||||
| //        AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT)); | ||||
| // | ||||
| //        // link the electronic record to secondFolderRM3060 | ||||
| //        recordLists.clear(); | ||||
| //        recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId()); | ||||
| //        linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
| //            getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" + | ||||
| //                secondFolderRM3060, recordLists); | ||||
| // | ||||
| //        // edit the disposition date and cut off the record | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef); | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef); | ||||
| // | ||||
| //        AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT)); | ||||
| // | ||||
| //        // open the record and complete the disposition schedule event | ||||
| //        rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(), elRecordFullName, RMEvents.CASE_CLOSED, Instant.now()); | ||||
| //        rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(), nonElRecordFullName, RMEvents.CASE_CLOSED, Instant.now()); | ||||
| // | ||||
| //        // transfer the files & complete transfers | ||||
| //        HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //                getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060)); | ||||
| // | ||||
| //        String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef); | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId); | ||||
| // | ||||
| //        HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
| //                getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060)); | ||||
| // | ||||
| //        String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef); | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId); | ||||
| // | ||||
| //        AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully transferred", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE)); | ||||
| //        AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully transferred.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE)); | ||||
| // | ||||
| //        // edit the disposition date for nonElectronicRecordRM3060 & electronicRecordRM3060 | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef); | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef); | ||||
| // | ||||
| //        // destroy nonElectronicRecordRM3060 & electronicRecordRM3060 records | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef); | ||||
| //        recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
| //            getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef); | ||||
| // | ||||
| //        // check the file is not displayed | ||||
| //       assertNull("The file " + nonElectronicRecordRM3060 + " has not been successfully destroyed.", secondNonElectronicRecord.getContent()); | ||||
| //       assertNull("The file " + electronicRecordRM3060 + " has not been successfully destroyed.", firstElectronicRecord.getContent()); | ||||
| // | ||||
| //        // delete precondition | ||||
| //        deleteRecordCategory(recordCategory.getId()); | ||||
| //        deleteRecordCategory(categorySecondId); | ||||
| //    } | ||||
|  | ||||
|         //create retention schedule | ||||
|         dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false); | ||||
|  | ||||
|         // add cut off step | ||||
|         dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE); | ||||
|  | ||||
|         //create a copy of the category recordsCategory | ||||
|         String CopyCategoryId = copyCategory(getAdminUser(),Category1.getId(), copyCategoryRM3077); | ||||
|  | ||||
|         // create folders in both categories | ||||
|         CatFolder = createRecordFolder(Category1.getId(), folderRM3077); | ||||
|         CopyCatFolder = createRecordFolder(CopyCategoryId, copyFolderRM3077); | ||||
|  | ||||
|         // create record  files | ||||
|         String electronicRecord = "RM-2801 electronic record"; | ||||
|         Record elRecord = createElectronicRecord(CatFolder.getId(), electronicRecord); | ||||
|         String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|             getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), electronicRecord); | ||||
|  | ||||
|         String nonElectronicRecord = "RM-2801 non-electronic record"; | ||||
|         Record nonElRecord = createNonElectronicRecord(CatFolder.getId(), nonElectronicRecord); | ||||
|         String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|             getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), nonElectronicRecord); | ||||
|  | ||||
|         // link the records to copy folder, then complete them | ||||
|         List<String> recordLists = new ArrayList<>(); | ||||
|         recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + elRecord.getId()); | ||||
|         recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + nonElRecord.getId()); | ||||
|  | ||||
|         linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,copyCategoryRM3077 + "/" + | ||||
|                 copyFolderRM3077, recordLists); | ||||
|         recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), elRecordFullName); | ||||
|         recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), nonElRecordFullName); | ||||
|  | ||||
|         // edit disposition date | ||||
|         recordFoldersAPI.postFolderAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),editDispositionDateJson(),CatFolder.getName()); | ||||
|  | ||||
|         // cut off the Folder | ||||
|         recordFoldersAPI.postFolderAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),CatFolder.getName()); | ||||
|  | ||||
|         // Verify the Content | ||||
|         Node electronicNode = getNode(elRecord.getId()); | ||||
|         assertTrue("The content of " + electronicRecord + " is available", | ||||
|             StringUtils.isEmpty(electronicNode.getNodeContent().getResponse().getBody().asString())); | ||||
|  | ||||
|         // verify the Properties | ||||
|         AssertJUnit.assertNull("The properties are present even after cutting off the record.", elRecord.getProperties().getTitle()); | ||||
|  | ||||
|         // delete precondition | ||||
|         deleteRecordCategory(Category1.getId()); | ||||
|         deleteRecordCategory(CopyCategoryId); | ||||
|     } | ||||
|     /** | ||||
|      * Test covering RM-3060 | ||||
|      * Check the disposition steps for a record can be executed | ||||
|      * When the record is linked to a folder with the same disposition schedule | ||||
|      * */ | ||||
|     @Test | ||||
|     @AlfrescoTest (jira = "RM-3060") | ||||
|     public void sameDispositionScheduleLinkedRecords() throws UnsupportedEncodingException { | ||||
|  | ||||
|         // create a category with retention applied on records level | ||||
|         RecordCategory recordCategory = getRestAPIFactory().getFilePlansAPI(rmAdmin) | ||||
|             .createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(), | ||||
|                 RecordCategory.DEFAULT_FILE_PLAN_ALIAS); | ||||
|         dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true); | ||||
|         dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "week|1", DATE_FILED); | ||||
|         dispositionScheduleService.addTransferAfterEventStep(firstCategoryRM3060, TRANSFER_LOCATION, RMEvents.CASE_CLOSED.getEventName()); | ||||
|         dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(firstCategoryRM3060, "week|1", CUT_OFF_DATE); | ||||
|  | ||||
|         // make a copy of the category created | ||||
|         String categorySecondId = copyCategory(getAdminUser(), recordCategory.getId(), secondCategoryRM3060); | ||||
|  | ||||
|         // create a folder on the category firstCategoryRM3060 with a complete electronic record | ||||
|         RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(recordCategory.getId(),firstFolderRM3060); | ||||
|         Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060); | ||||
|  | ||||
|         String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060); | ||||
|         String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|             getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060); | ||||
|  | ||||
|         recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(), elRecordFullName); | ||||
|  | ||||
|         // create a folder on the category secondCategoryRM3060 with a non electronic record | ||||
|         RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(categorySecondId,secondFolderRM3060); | ||||
|         Record secondNonElectronicRecord = createNonElectronicRecord(secondFolderRecordCategoryChild.getId(),nonElectronicRecordRM3060); | ||||
|  | ||||
|         // link the nonElectronicRecordRM3060 to firstFolderRM3060 | ||||
|         List<String> recordLists = new ArrayList<>(); | ||||
|         recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId()); | ||||
|  | ||||
|         linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" + | ||||
|                 secondFolderRM3060, recordLists); | ||||
|         String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|             getDataUser().usingAdmin().getAdminUser().getPassword(), secondFolderRM3060, secondNonElectronicRecord.getName()); | ||||
|         String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|             getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060); | ||||
|  | ||||
|         // complete records and cut them off | ||||
|         recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(), nonElRecordFullName); | ||||
|  | ||||
|         // edit the disposition date | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef); | ||||
|  | ||||
|         // cut off the record | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef); | ||||
|  | ||||
|         //check the record is cut off | ||||
|         AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT)); | ||||
|  | ||||
|         // link the electronic record to secondFolderRM3060 | ||||
|         recordLists.clear(); | ||||
|         recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId()); | ||||
|         linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(), | ||||
|             getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" + | ||||
|                 secondFolderRM3060, recordLists); | ||||
|  | ||||
|         // edit the disposition date and cut off the record | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef); | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef); | ||||
|  | ||||
|         AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT)); | ||||
|  | ||||
|         // open the record and complete the disposition schedule event | ||||
|         rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(), elRecordFullName, RMEvents.CASE_CLOSED, Instant.now()); | ||||
|         rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(), nonElRecordFullName, RMEvents.CASE_CLOSED, Instant.now()); | ||||
|  | ||||
|         // transfer the files & complete transfers | ||||
|         HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|                 getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060)); | ||||
|  | ||||
|         String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef); | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId); | ||||
|  | ||||
|         HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(), | ||||
|                 getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060)); | ||||
|  | ||||
|         String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef); | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId); | ||||
|  | ||||
|         AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully transferred", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE)); | ||||
|         AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully transferred.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE)); | ||||
|  | ||||
|         // edit the disposition date for nonElectronicRecordRM3060 & electronicRecordRM3060 | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef); | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef); | ||||
|  | ||||
|         // destroy nonElectronicRecordRM3060 & electronicRecordRM3060 records | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef); | ||||
|         recordFoldersAPI.postRecordAction(getAdminUser().getUsername(), | ||||
|             getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef); | ||||
|  | ||||
|         // check the file is not displayed | ||||
|        assertNull("The file " + nonElectronicRecordRM3060 + " has not been successfully destroyed.", secondNonElectronicRecord.getContent()); | ||||
|        assertNull("The file " + electronicRecordRM3060 + " has not been successfully destroyed.", firstElectronicRecord.getContent()); | ||||
|  | ||||
|         // delete precondition | ||||
|         deleteRecordCategory(recordCategory.getId()); | ||||
|         deleteRecordCategory(categorySecondId); | ||||
|     } | ||||
|     private String copyCategory(UserModel user, String categoryId, String copyName) { | ||||
|         RepoTestModel repoTestModel = new RepoTestModel() {}; | ||||
|         repoTestModel.setNodeRef(categoryId); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.7-A2 | ||||
| SOLR6_TAG=2.0.7-A5 | ||||
| POSTGRES_TAG=14.4 | ||||
| ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8 | ||||
| ACTIVEMQ_TAG=5.17.4-jre17-rockylinux8 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
| @@ -436,7 +436,7 @@ | ||||
|                            </run> | ||||
|                         </image> | ||||
|                         <image> | ||||
|                            <name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name> | ||||
|                            <name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8</name> | ||||
|                            <run> | ||||
|                               <ports> | ||||
|                                  <port>${activemq.port1}:${activemq.port1}</port> | ||||
| @@ -507,7 +507,7 @@ | ||||
|                            </run> | ||||
|                         </image> | ||||
|                         <image> | ||||
|                            <name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name> | ||||
|                            <name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8</name> | ||||
|                            <run> | ||||
|                               <ports> | ||||
|                                  <port>${activemq.port1}:${activemq.port1}</port> | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| # | ||||
|  | ||||
| # Version label | ||||
| version.major=7 | ||||
| version.minor=4 | ||||
| version.major=23 | ||||
| version.minor=1 | ||||
| version.revision=0 | ||||
| version.label= | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
| @@ -36,24 +36,40 @@ | ||||
|          <id>start-api-explorer</id> | ||||
|          <build> | ||||
|             <plugins> | ||||
|                <plugin> | ||||
|                   <groupId>org.apache.tomcat.maven</groupId> | ||||
|                   <artifactId>tomcat7-maven-plugin</artifactId> | ||||
|                   <executions> | ||||
|                      <execution> | ||||
|                         <id>run-war</id> | ||||
|                         <goals> | ||||
|                            <goal>run-war</goal> | ||||
|                         </goals> | ||||
|                         <phase>verify</phase> | ||||
|                      </execution> | ||||
|                   </executions> | ||||
|                   <configuration> | ||||
|                      <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader> | ||||
|                      <path>/api-explorer</path> | ||||
|                      <port>8085</port> | ||||
|                   </configuration> | ||||
|                </plugin> | ||||
|                 <plugin> | ||||
|                     <groupId>org.codehaus.cargo</groupId> | ||||
|                     <artifactId>cargo-maven3-plugin</artifactId> | ||||
|                     <executions> | ||||
|                         <execution> | ||||
|                             <id>run-war</id> | ||||
|                             <phase>verify</phase> | ||||
|                             <goals> | ||||
|                                 <goal>run</goal> | ||||
|                             </goals> | ||||
|                         </execution> | ||||
|                     </executions> | ||||
|                     <configuration> | ||||
|                         <container> | ||||
|                             <containerId>tomcat9x</containerId> | ||||
|                             <type>embedded</type> | ||||
|                             <log>target/cargo.log</log> | ||||
|                         </container> | ||||
|                         <configuration> | ||||
|                             <properties> | ||||
|                                 <cargo.servlet.port>8085</cargo.servlet.port> | ||||
|                             </properties> | ||||
|                         </configuration> | ||||
|                         <deployables> | ||||
|                             <deployable> | ||||
|                                 <type>war</type> | ||||
|                                 <properties> | ||||
|                                     <context>/api-explorer</context> | ||||
|                                 </properties> | ||||
|                                 <pingURL>http://localhost:8085/api-explorer</pingURL> | ||||
|                             </deployable> | ||||
|                         </deployables> | ||||
|                     </configuration> | ||||
|                 </plugin> | ||||
|             </plugins> | ||||
|          </build> | ||||
|       </profile> | ||||
|   | ||||
| @@ -38,9 +38,7 @@ tags: | ||||
|     description: Retrieve and manage unfiled records containers | ||||
|   - name: unfiled-record-folders | ||||
|     description: Retrieve and manage unfiled record folders | ||||
|   - name: events | ||||
|     description: Retrieve and manage retention events | ||||
|      | ||||
|  | ||||
| paths: | ||||
|   ## GS sites | ||||
|   '/gs-sites': | ||||
| @@ -2094,172 +2092,8 @@ paths: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/events': | ||||
|     get: | ||||
|       tags: | ||||
|         - events | ||||
|       summary: List all available retention events | ||||
|       description: | | ||||
|         Gets the list of events that can be used by retention steps | ||||
|       operationId: getAllEvents | ||||
|       produces: | ||||
|         - application/json | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventPaging' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: value of **maxItems** or **skipCount** is invalid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|  | ||||
|     post: | ||||
|       tags: | ||||
|         - events | ||||
|       summary: Create a new retention event | ||||
|       description: | | ||||
|         Creates a new event that can be used by retention schedules. | ||||
|       operationId: createEvent | ||||
|       parameters: | ||||
|         - in: body | ||||
|           name: eventBodyCreate | ||||
|           description: The new event. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventBody' | ||||
|       consumes: | ||||
|         - application/json | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '201': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **name** or **type** is invalid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to create event | ||||
|         '409': | ||||
|           description: Cannot create event. An event with the name **name** already exists | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|    | ||||
|   '/events/{eventId}': | ||||
|     get: | ||||
|       tags: | ||||
|         - events | ||||
|       summary: Return event for given eventId | ||||
|       description: | | ||||
|         Gets information about the retention event with id **eventId**. | ||||
|       operationId: getEvent | ||||
|       produces: | ||||
|         - application/json | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/eventIdParam' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **eventId** is invalid     | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '404': | ||||
|           description: "**eventId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|     put: | ||||
|       tags: | ||||
|         - events | ||||
|       summary: Update event for given eventId | ||||
|       operationId: updateEvent | ||||
|       description: | | ||||
|         Updates retention event with id **eventId**. | ||||
|       produces: | ||||
|         - application/json | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/eventIdParam' | ||||
|         - in: body | ||||
|           name: eventBodyUpdate | ||||
|           description: The event information to update. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventBody' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: The update request is invalid or **eventId** is not a valid format or **eventBodyUpdate** is invalid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to update events | ||||
|         '404': | ||||
|           description: "**eventId** does not exist" | ||||
|         '409': | ||||
|           description: Cannot update event. An event with the name **name** already exists | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|    | ||||
|   '/event-types': | ||||
|     get: | ||||
|       tags: | ||||
|         - events | ||||
|       summary: List all the retention event types | ||||
|       description: | | ||||
|         Gets a list of all the retention event types. | ||||
|       operationId: getAllEventTypes | ||||
|       produces: | ||||
|         - application/json | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/EventTypePaging' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: value of **maxItems** or **skipCount** is invalid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error'           | ||||
| parameters: | ||||
|   ## event | ||||
|   eventIdParam: | ||||
|     name: eventId | ||||
|     in: path | ||||
|     description: The identifier of an event. | ||||
|     required: true | ||||
|     type: string | ||||
|   ## File plans | ||||
|   filePlanEntryIncludeParam: | ||||
|     name: include | ||||
| @@ -3927,92 +3761,4 @@ definitions: | ||||
|           - SiteConsumer | ||||
|           - SiteCollaborator | ||||
|           - SiteContributor | ||||
|           - SiteManager | ||||
|   EventPaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/EventEntry' | ||||
|   EventEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/Event' | ||||
|   Event: | ||||
|     type: object | ||||
|     required: | ||||
|       - id | ||||
|       - name | ||||
|       - type | ||||
|     properties: | ||||
|       id: | ||||
|         type: string | ||||
|         description: this is the id of the event | ||||
|       name: | ||||
|         type: string | ||||
|         description: This is the unique display label of the event | ||||
|       type: | ||||
|         type: string | ||||
|         description: this is event type | ||||
|   EventBody: | ||||
|     type: object | ||||
|     required: | ||||
|       - name | ||||
|     properties: | ||||
|       name: | ||||
|         type: string | ||||
|         description: This is the unique display label of the event | ||||
|       type: | ||||
|         type: string | ||||
|         description: this is event type | ||||
|         default: Simple | ||||
|   EventTypePaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/EventTypeEntry' | ||||
|   EventTypeEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/EventType' | ||||
|   EventType: | ||||
|     type: object | ||||
|     required: | ||||
|       - id | ||||
|       - name | ||||
|     properties: | ||||
|       id: | ||||
|         type: string | ||||
|         description: this is the event type id | ||||
|       name: | ||||
|         type: string | ||||
|         description: this is event type name | ||||
|       isAutomatic: | ||||
|         type: boolean | ||||
|         description: Whether events of this type need completing manually or can be completed automatically | ||||
|         default: true | ||||
|       associationName: | ||||
|         type: string | ||||
|         description: The association used to determine whether automatic events of this type are complete | ||||
|       actionOnAssociatedNode: | ||||
|         type: string | ||||
|         description: If an association name is set for this event type then it is possible to require an action to be completed on the associated node | ||||
|           - SiteManager | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -23,6 +23,10 @@ function runAction(p_params) | ||||
|       if (p_params.destNode.hasAspect("cm:lockable") && !p_params.destNode.hasAspect("trx:transferred")) | ||||
|       { | ||||
|          p_params.destNode.unlock(); | ||||
|          if(p_params.destNode.hasAspect("gd2:editingInGoogle")) | ||||
|          { | ||||
|              p_params.destNode.removeAspect("gd2:editingInGoogle"); | ||||
|          } | ||||
|       } | ||||
|  | ||||
|       var resultId = originalDoc.name, | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>20.136</version> | ||||
|       <version>23.1.0.163-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
|   | ||||
| @@ -22,18 +22,21 @@ package org.alfresco.httpclient; | ||||
| import javax.net.ssl.KeyManager; | ||||
| import javax.net.ssl.SSLContext; | ||||
| import javax.net.ssl.TrustManager; | ||||
|  | ||||
| import org.alfresco.error.AlfrescoRuntimeException; | ||||
| import org.apache.http.HttpHost; | ||||
| import org.apache.http.HttpRequestInterceptor; | ||||
| import org.apache.http.client.config.RequestConfig; | ||||
| import org.apache.http.config.RegistryBuilder; | ||||
| import org.apache.http.conn.HttpClientConnectionManager; | ||||
| import org.apache.http.conn.socket.ConnectionSocketFactory; | ||||
| import org.apache.http.conn.socket.PlainConnectionSocketFactory; | ||||
| import org.apache.http.conn.ssl.NoopHostnameVerifier; | ||||
| import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | ||||
| import org.apache.http.impl.client.HttpClientBuilder; | ||||
| import org.apache.http.impl.client.HttpClients; | ||||
| import org.apache.http.impl.client.StandardHttpRequestRetryHandler; | ||||
| import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | ||||
|  | ||||
|  | ||||
| public class HttpClient4Factory | ||||
| @@ -79,19 +82,19 @@ public class HttpClient4Factory | ||||
|                     throw new HttpClientException(msg); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory( | ||||
|                     createSSLContext(config), | ||||
|                     new String[] { TLS_V_1_2, TLS_V_1_3 }, | ||||
|                     null, | ||||
|                     config.isHostnameVerificationDisabled() ? new NoopHostnameVerifier() : SSLConnectionSocketFactory.getDefaultHostnameVerifier()); | ||||
|             clientBuilder.setSSLSocketFactory(sslConnectionSocketFactory); | ||||
|             clientBuilder.setSSLSocketFactory(getSslConnectionSocketFactory(config)); | ||||
|         } | ||||
|  | ||||
|         if(connectionManager != null) | ||||
|         if (connectionManager != null) | ||||
|         { | ||||
|             clientBuilder.setConnectionManager(connectionManager); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //Setting a connectionManager overrides these properties | ||||
|             clientBuilder.setMaxConnTotal(config.getMaxTotalConnections()); | ||||
|             clientBuilder.setMaxConnPerRoute(config.getMaxHostConnections()); | ||||
|         } | ||||
|  | ||||
|         RequestConfig requestConfig = RequestConfig.custom() | ||||
|                .setConnectTimeout(config.getConnectionTimeout()) | ||||
| @@ -100,11 +103,41 @@ public class HttpClient4Factory | ||||
|                .build(); | ||||
|  | ||||
|         clientBuilder.setDefaultRequestConfig(requestConfig); | ||||
|         clientBuilder.setMaxConnTotal(config.getMaxTotalConnections()); | ||||
|         clientBuilder.setMaxConnPerRoute(config.getMaxHostConnections()); | ||||
|  | ||||
|         clientBuilder.setRetryHandler(new StandardHttpRequestRetryHandler(5, false)); | ||||
|  | ||||
|         return clientBuilder.build(); | ||||
|     } | ||||
|  | ||||
|     private static SSLConnectionSocketFactory getSslConnectionSocketFactory(HttpClientConfig config) | ||||
|     { | ||||
|         return new SSLConnectionSocketFactory( | ||||
|                 createSSLContext(config), | ||||
|                 new String[] { TLS_V_1_2, TLS_V_1_3 }, | ||||
|                 null, | ||||
|                 config.isHostnameVerificationDisabled() ? new NoopHostnameVerifier() : SSLConnectionSocketFactory.getDefaultHostnameVerifier()); | ||||
|     } | ||||
|  | ||||
|     public static PoolingHttpClientConnectionManager createPoolingConnectionManager(HttpClientConfig config) | ||||
|     { | ||||
|         PoolingHttpClientConnectionManager poolingHttpClientConnectionManager; | ||||
|         if(config.isMTLSEnabled()) | ||||
|         { | ||||
|             poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager( | ||||
|                     RegistryBuilder.<ConnectionSocketFactory>create() | ||||
|                    .register("https", getSslConnectionSocketFactory(config)) | ||||
|                    .build()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager( | ||||
|                     RegistryBuilder.<ConnectionSocketFactory>create() | ||||
|                    .register("http", PlainConnectionSocketFactory.getSocketFactory()) | ||||
|                    .build()); | ||||
|         } | ||||
|         poolingHttpClientConnectionManager.setMaxTotal(config.getMaxTotalConnections()); | ||||
|         poolingHttpClientConnectionManager.setDefaultMaxPerRoute(config.getMaxHostConnections()); | ||||
|  | ||||
|         return poolingHttpClientConnectionManager; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
| @@ -119,7 +119,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.jibx</groupId> | ||||
|             <artifactId>jibx-run</artifactId> | ||||
|             <version>1.3.3</version> | ||||
|             <version>1.4.2</version> | ||||
|         </dependency> | ||||
|         <dependency>  | ||||
|             <groupId>com.fasterxml.jackson.core</groupId>  | ||||
| @@ -134,7 +134,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>com.fasterxml.woodstox</groupId> | ||||
|             <artifactId>woodstox-core</artifactId> | ||||
|             <version>6.4.0</version> | ||||
|             <version>6.5.1</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- the cxf libs were updated, see dependencyManagement section --> | ||||
|   | ||||
| @@ -201,6 +201,11 @@ public class SearchParameters implements BasicSearchParameters | ||||
|  | ||||
|     private String timezone; | ||||
|      | ||||
|     /** | ||||
|      * Configure the limit to track the total hits on search results | ||||
|      */ | ||||
|     private int trackTotalHits; | ||||
|  | ||||
|     /** | ||||
|      * Default constructor | ||||
|      */ | ||||
| @@ -251,6 +256,7 @@ public class SearchParameters implements BasicSearchParameters | ||||
|         sp.stats = this.stats; | ||||
|         sp.ranges = this.ranges; | ||||
|         sp.timezone = this.timezone; | ||||
|         sp.trackTotalHits = this.trackTotalHits; | ||||
|         return sp; | ||||
|     } | ||||
|      | ||||
| @@ -1641,6 +1647,21 @@ public class SearchParameters implements BasicSearchParameters | ||||
|     { | ||||
|         this.includeMetadata = includeMetadata; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     public int getTrackTotalHits() | ||||
|     { | ||||
|         return trackTotalHits; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set a maximum value for the report of total hits. The reported number of hits will never exceed this limit even | ||||
|      * if more are found. If unset, the engine’s default tracking limit is applied. To remove any limit, set to -1. | ||||
|      * | ||||
|      * @param trackTotalHits int | ||||
|      */ | ||||
|     public void setTrackTotalHits(int trackTotalHits) | ||||
|     { | ||||
|         this.trackTotalHits = trackTotalHits; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -9,6 +9,6 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
| </project> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.7-A2 | ||||
| SOLR6_TAG=2.0.7-A5 | ||||
| POSTGRES_TAG=14.4 | ||||
| ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8 | ||||
| ACTIVEMQ_TAG=5.17.4-jre17-rockylinux8 | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <organization> | ||||
| @@ -16,12 +16,12 @@ | ||||
|     </organization> | ||||
|  | ||||
|     <properties> | ||||
|         <maven.build.sourceVersion>11</maven.build.sourceVersion> | ||||
|         <maven.build.sourceVersion>17</maven.build.sourceVersion> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|         <chemistry-opencmis-commons-api>1.1.0</chemistry-opencmis-commons-api> | ||||
|         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> | ||||
|         <maven-release.version>2.5.3</maven-release.version> | ||||
|         <java.version>11</java.version> | ||||
|         <java.version>17</java.version> | ||||
|         <suiteXmlFile>${project.basedir}/src/test/resources/cmis-suite.xml</suiteXmlFile> | ||||
|         <cmis.binding /> | ||||
|         <cmis.basePath /> | ||||
| @@ -58,12 +58,6 @@ | ||||
|     </profiles> | ||||
|  | ||||
|     <dependencies> | ||||
|         <dependency> | ||||
|             <groupId>org.jboss.resteasy</groupId> | ||||
|             <artifactId>resteasy-jackson2-provider</artifactId> | ||||
|             <version>4.7.1.Final</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- alfresco tester settings --> | ||||
|         <dependency> | ||||
|             <groupId>org.alfresco.tas</groupId> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -8,20 +8,19 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|         <suiteXmlFile>${project.basedir}/src/test/resources/restapi-suite.xml</suiteXmlFile> | ||||
|         <maven.build.sourceVersion>11</maven.build.sourceVersion> | ||||
|         <maven.build.sourceVersion>17</maven.build.sourceVersion> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|         <rest.api.explorer.branch>master</rest.api.explorer.branch> | ||||
|         <httpclient-osgi-version>4.5.6</httpclient-osgi-version> | ||||
|         <org.glassfish.version>1.1.4</org.glassfish.version> | ||||
|         <commons-lang3.version>3.12.0</commons-lang3.version> | ||||
|         <scribejava-apis.version>8.3.1</scribejava-apis.version> | ||||
|         <license-maven-plugin.version>2.0.1.alfresco-2</license-maven-plugin.version> | ||||
|         <java.version>11</java.version> | ||||
|         <scribejava-apis.version>8.3.3</scribejava-apis.version> | ||||
|         <java.version>17</java.version> | ||||
|     </properties> | ||||
|  | ||||
|     <profiles> | ||||
| @@ -85,6 +84,13 @@ | ||||
|             <version>${commons-lang3.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.awaitility</groupId> | ||||
|             <artifactId>awaitility</artifactId> | ||||
|             <version>${dependency.awaitility.version}</version> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- REST ASSURED --> | ||||
|         <dependency> | ||||
|             <groupId>org.apache.httpcomponents</groupId> | ||||
| @@ -165,14 +171,14 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.codehaus.groovy</groupId> | ||||
|             <artifactId>groovy</artifactId> | ||||
|             <version>3.0.16</version> | ||||
|             <version>3.0.18</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json--> | ||||
|         <dependency> | ||||
|             <groupId>org.codehaus.groovy</groupId> | ||||
|             <artifactId>groovy-json</artifactId> | ||||
|             <version>3.0.16</version> | ||||
|             <version>3.0.18</version> | ||||
|         </dependency> | ||||
|  | ||||
|        <dependency> | ||||
|   | ||||
| @@ -52,6 +52,11 @@ This must be unique within the parent category. | ||||
|     */ | ||||
|     private long count; | ||||
|  | ||||
|     /** | ||||
|      The path to this category. | ||||
|      */ | ||||
|     private String path; | ||||
|  | ||||
|     public String getId() | ||||
|     { | ||||
|         return this.id; | ||||
| @@ -102,6 +107,14 @@ This must be unique within the parent category. | ||||
|         this.count = count; | ||||
|     } | ||||
|  | ||||
|     public String getPath() { | ||||
|         return path; | ||||
|     } | ||||
|  | ||||
|     public void setPath(String path) { | ||||
|         this.path = path; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object o) | ||||
|     { | ||||
| @@ -126,6 +139,7 @@ This must be unique within the parent category. | ||||
|                 ", parentId='" + parentId + '\'' + | ||||
|                 ", hasChildren=" + hasChildren + | ||||
|                 ", count=" + count + | ||||
|                 ", path=" + path + | ||||
|                 '}'; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,190 @@ | ||||
| package org.alfresco.rest.search; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import org.alfresco.rest.core.IRestModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| /** | ||||
|  * Generated by 'krystian' on '2023-06-12 18:46' from 'Alfresco Content Services REST API' swagger file  | ||||
|  * Generated from 'Alfresco Content Services REST API' swagger file | ||||
|  * Base Path {@linkplain /alfresco/api/-default-/public/search/versions/1} | ||||
|  */ | ||||
| public class RestRequestDefaultsModel extends TestModel implements IRestModel<RestRequestDefaultsModel> | ||||
| { | ||||
|     @JsonProperty(value = "entry") | ||||
|     RestRequestDefaultsModel model; | ||||
|  | ||||
|     @Override | ||||
|     public RestRequestDefaultsModel onModel() | ||||
|     { | ||||
|         return model; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|     A list of query fields/properties used to expand TEXT: queries. | ||||
| The default is cm:content. | ||||
| You could include all content properties using d:content or list all individual content properties or types. | ||||
| As more terms are included the query size, complexity, memory impact and query time will increase. | ||||
|  | ||||
|     */	         | ||||
|  | ||||
|     private List<String> textAttributes;	     | ||||
|     /** | ||||
|     The default way to combine query parts when AND or OR is not explicitly stated - includes ! - + | ||||
| one two three | ||||
| (one two three) | ||||
|  | ||||
|     */	         | ||||
|  | ||||
|     private String defaultFTSOperator;	     | ||||
|     /** | ||||
|     The default way to combine query parts in field query groups when AND or OR is not explicitly stated - includes ! - + | ||||
| FIELD:(one two three) | ||||
|  | ||||
|     */	         | ||||
|  | ||||
|     private String defaultFTSFieldOperator;	     | ||||
|     /** | ||||
|     The default name space to use if one is not provided | ||||
|     */	         | ||||
|  | ||||
|     private String namespace;	     | ||||
|  | ||||
|     private String defaultFieldName;	     | ||||
|  | ||||
|     public List<String> getTextAttributes() | ||||
|     { | ||||
|         return this.textAttributes; | ||||
|     } | ||||
|  | ||||
|     public void setTextAttributes(List<String> textAttributes) | ||||
|     { | ||||
|         this.textAttributes = textAttributes; | ||||
|     }				 | ||||
|  | ||||
|     public String getDefaultFTSOperator() | ||||
|     { | ||||
|         return this.defaultFTSOperator; | ||||
|     } | ||||
|  | ||||
|     public void setDefaultFTSOperator(String defaultFTSOperator) | ||||
|     { | ||||
|         this.defaultFTSOperator = defaultFTSOperator; | ||||
|     }				 | ||||
|  | ||||
|     public String getDefaultFTSFieldOperator() | ||||
|     { | ||||
|         return this.defaultFTSFieldOperator; | ||||
|     } | ||||
|  | ||||
|     public void setDefaultFTSFieldOperator(String defaultFTSFieldOperator) | ||||
|     { | ||||
|         this.defaultFTSFieldOperator = defaultFTSFieldOperator; | ||||
|     }				 | ||||
|  | ||||
|     public String getNamespace() | ||||
|     { | ||||
|         return this.namespace; | ||||
|     } | ||||
|  | ||||
|     public void setNamespace(String namespace) | ||||
|     { | ||||
|         this.namespace = namespace; | ||||
|     }				 | ||||
|  | ||||
|     public String getDefaultFieldName() | ||||
|     { | ||||
|         return this.defaultFieldName; | ||||
|     } | ||||
|  | ||||
|     public void setDefaultFieldName(String defaultFieldName) | ||||
|     { | ||||
|         this.defaultFieldName = defaultFieldName; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() | ||||
|     { | ||||
|         return "RestRequestDefaultsModel{" + "textAttributes=" + textAttributes + ", defaultFTSOperator='" + defaultFTSOperator + '\'' + ", defaultFTSFieldOperator='" | ||||
|             + defaultFTSFieldOperator + '\'' + ", namespace='" + namespace + '\'' + ", defaultFieldName='" + defaultFieldName + '\'' + '}'; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object o) | ||||
|     { | ||||
|         if (this == o) | ||||
|             return true; | ||||
|         if (o == null || getClass() != o.getClass()) | ||||
|             return false; | ||||
|         RestRequestDefaultsModel that = (RestRequestDefaultsModel) o; | ||||
|         return Objects.equals(textAttributes, that.textAttributes) && Objects.equals(defaultFTSOperator, that.defaultFTSOperator) && Objects.equals( | ||||
|             defaultFTSFieldOperator, that.defaultFTSFieldOperator) && Objects.equals(namespace, that.namespace) && Objects.equals(defaultFieldName, that.defaultFieldName); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() | ||||
|     { | ||||
|         return Objects.hash(textAttributes, defaultFTSOperator, defaultFTSFieldOperator, namespace, defaultFieldName); | ||||
|     } | ||||
|  | ||||
|     public static Builder builder() | ||||
|     { | ||||
|         return new Builder(); | ||||
|     } | ||||
|  | ||||
|     public static class Builder | ||||
|     { | ||||
|         private List<String> textAttributes; | ||||
|         private String defaultFTSOperator; | ||||
|         private String defaultFTSFieldOperator; | ||||
|         private String namespace; | ||||
|         private String defaultFieldName; | ||||
|  | ||||
|         public Builder textAttributes(List<String> textAttributes) | ||||
|         { | ||||
|             this.textAttributes = textAttributes; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder defaultFTSOperator(String defaultFTSOperator) | ||||
|         { | ||||
|             this.defaultFTSOperator = defaultFTSOperator; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder defaultFTSFieldOperator(String defaultFTSFieldOperator) | ||||
|         { | ||||
|             this.defaultFTSFieldOperator = defaultFTSFieldOperator; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder namespace(String namespace) | ||||
|         { | ||||
|             this.namespace = namespace; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder defaultFieldName(String defaultFieldName) | ||||
|         { | ||||
|             this.defaultFieldName = defaultFieldName; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public RestRequestDefaultsModel create() | ||||
|         { | ||||
|             RestRequestDefaultsModel defaults = new RestRequestDefaultsModel(); | ||||
|             defaults.setTextAttributes(this.textAttributes); | ||||
|             defaults.setDefaultFTSOperator(this.defaultFTSOperator); | ||||
|             defaults.setDefaultFTSFieldOperator(this.defaultFTSFieldOperator); | ||||
|             defaults.setNamespace(this.namespace); | ||||
|             defaults.setDefaultFieldName(this.defaultFieldName); | ||||
|  | ||||
|             return defaults; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,82 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * alfresco-tas-restapi | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2023 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.search; | ||||
|  | ||||
| import org.alfresco.rest.core.IRestModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| public class RestRequestLimitsModel extends TestModel implements IRestModel<RestRequestLimitsModel> | ||||
| { | ||||
|     @JsonProperty | ||||
|     RestRequestLimitsModel model; | ||||
|  | ||||
|     private Integer permissionEvaluationTime; | ||||
|     private Integer permissionEvaluationCount; | ||||
|     private Integer trackTotalHitsLimit; | ||||
|  | ||||
|     @Override | ||||
|     public RestRequestLimitsModel onModel() | ||||
|     { | ||||
|         return model; | ||||
|     } | ||||
|      | ||||
|     public RestRequestLimitsModel(Integer permissionEvaluationTime, Integer permissionEvaluationCount, | ||||
|             Integer trackTotalHitsLimit) | ||||
|     { | ||||
|         super(); | ||||
|         this.permissionEvaluationTime = permissionEvaluationTime; | ||||
|         this.permissionEvaluationCount = permissionEvaluationCount; | ||||
|         this.trackTotalHitsLimit = trackTotalHitsLimit; | ||||
|     } | ||||
|  | ||||
|     public Integer getPermissionEvaluationTime() | ||||
|     { | ||||
|         return permissionEvaluationTime; | ||||
|     } | ||||
|     public void setPermissionEvaluationTime(Integer permissionEvaluationTime) | ||||
|     { | ||||
|         this.permissionEvaluationTime = permissionEvaluationTime; | ||||
|     } | ||||
|     public Integer getPermissionEvaluationCount() | ||||
|     { | ||||
|         return permissionEvaluationCount; | ||||
|     } | ||||
|     public void setPermissionEvaluationCount(Integer permissionEvaluationCount) | ||||
|     { | ||||
|         this.permissionEvaluationCount = permissionEvaluationCount; | ||||
|     } | ||||
|     public Integer getTrackTotalHitsLimit() | ||||
|     { | ||||
|         return trackTotalHitsLimit; | ||||
|     } | ||||
|     public void setTrackTotalHitsLimit(Integer trackTotalHitsLimit) | ||||
|     { | ||||
|         this.trackTotalHitsLimit = trackTotalHitsLimit; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,105 @@ | ||||
| package org.alfresco.rest.search; | ||||
|  | ||||
| import java.util.Objects; | ||||
|  | ||||
| import org.alfresco.rest.core.IRestModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| /** | ||||
|  * Generated by 'krystian' on '2023-06-12 18:46' from 'Alfresco Content Services REST API' swagger file  | ||||
|  * Generated from 'Alfresco Content Services REST API' swagger file | ||||
|  * Base Path {@linkplain /alfresco/api/-default-/public/search/versions/1} | ||||
|  */ | ||||
| public class RestRequestTemplatesModel extends TestModel implements IRestModel<RestRequestTemplatesModel> | ||||
| { | ||||
|     @JsonProperty(value = "entry") | ||||
|     RestRequestTemplatesModel model; | ||||
|  | ||||
|     @Override | ||||
|     public RestRequestTemplatesModel onModel() | ||||
|     { | ||||
|         return model; | ||||
|     } | ||||
|  | ||||
|     private String name; | ||||
|  | ||||
|     private String template; | ||||
|  | ||||
|     public String getName() | ||||
|     { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     public void setName(String name) | ||||
|     { | ||||
|         this.name = name; | ||||
|     } | ||||
|  | ||||
|     public String getTemplate() | ||||
|     { | ||||
|         return template; | ||||
|     } | ||||
|  | ||||
|     public void setTemplate(String template) | ||||
|     { | ||||
|         this.template = template; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() | ||||
|     { | ||||
|         return "RestRequestTemplatesModel{" + "name='" + name + '\'' + ", template='" + template + '\'' + '}'; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object o) | ||||
|     { | ||||
|         if (this == o) | ||||
|             return true; | ||||
|         if (o == null || getClass() != o.getClass()) | ||||
|             return false; | ||||
|         RestRequestTemplatesModel that = (RestRequestTemplatesModel) o; | ||||
|         return Objects.equals(name, that.name) && Objects.equals(template, that.template); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() | ||||
|     { | ||||
|         return Objects.hash(name, template); | ||||
|     } | ||||
|  | ||||
|     public static Builder builder() | ||||
|     { | ||||
|         return new Builder(); | ||||
|     } | ||||
|  | ||||
|     public static class Builder | ||||
|     { | ||||
|         private String name; | ||||
|         private String template; | ||||
|  | ||||
|         public Builder name(String name) | ||||
|         { | ||||
|             this.name = name; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder template(String template) | ||||
|         { | ||||
|             this.template = template; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public RestRequestTemplatesModel create() | ||||
|         { | ||||
|             RestRequestTemplatesModel template = new RestRequestTemplatesModel(); | ||||
|             template.setName(this.name); | ||||
|             template.setTemplate(this.template); | ||||
|  | ||||
|             return template; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -43,14 +43,15 @@ | ||||
|  */ | ||||
| package org.alfresco.rest.search; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import org.alfresco.rest.model.RestRequestRangesModel; | ||||
| import org.alfresco.rest.model.RestRequestSpellcheckModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Search Query object. | ||||
|  * @author msuzuki | ||||
| @@ -76,6 +77,9 @@ public class SearchRequest extends TestModel | ||||
|     String facetFormat; | ||||
|     List<String> include; | ||||
|     List<SortClause> sort; | ||||
|     RestRequestDefaultsModel defaults; | ||||
|     List<RestRequestTemplatesModel> templates; | ||||
|     RestRequestLimitsModel limits; | ||||
|  | ||||
|     public SearchRequest() | ||||
|     { | ||||
| @@ -255,6 +259,26 @@ public class SearchRequest extends TestModel | ||||
|         this.fields = fields; | ||||
|     } | ||||
|  | ||||
|     public RestRequestDefaultsModel getDefaults() | ||||
|     { | ||||
|         return defaults; | ||||
|     } | ||||
|  | ||||
|     public void setDefaults(RestRequestDefaultsModel defaults) | ||||
|     { | ||||
|         this.defaults = defaults; | ||||
|     } | ||||
|  | ||||
|     public List<RestRequestTemplatesModel> getTemplates() | ||||
|     { | ||||
|         return templates; | ||||
|     } | ||||
|  | ||||
|     public void setTemplates(List<RestRequestTemplatesModel> templates) | ||||
|     { | ||||
|         this.templates = templates; | ||||
|     } | ||||
|  | ||||
|     public List<SortClause> getSort() | ||||
|     { | ||||
|         if (sort == null) | ||||
| @@ -279,4 +303,15 @@ public class SearchRequest extends TestModel | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public RestRequestLimitsModel getLimits() | ||||
|     { | ||||
|         return limits; | ||||
|     } | ||||
|  | ||||
|     public void setLimits(RestRequestLimitsModel limits) | ||||
|     { | ||||
|         this.limits = limits; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,223 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Remote API | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2023 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
|  | ||||
| package org.alfresco.rest.categories; | ||||
|  | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.springframework.http.HttpStatus.CREATED; | ||||
| import static org.springframework.http.HttpStatus.OK; | ||||
| import static org.testng.Assert.assertTrue; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.model.RestCategoryLinkBodyModel; | ||||
| import org.alfresco.rest.model.RestCategoryModel; | ||||
| import org.alfresco.rest.model.RestCategoryModelsCollection; | ||||
| import org.alfresco.utility.Utility; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.FolderModel; | ||||
| import org.alfresco.utility.model.SiteModel; | ||||
| import org.alfresco.utility.model.TestGroup; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| public class CategoriesPathTests extends CategoriesRestTest | ||||
| { | ||||
|     private FileModel file; | ||||
|     private RestCategoryModel category; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     @Override | ||||
|     public void dataPreparation() throws Exception | ||||
|     { | ||||
|         STEP("Create user and site"); | ||||
|         user = dataUser.createRandomTestUser(); | ||||
|         SiteModel site = dataSite.usingUser(user).createPublicRandomSite(); | ||||
|  | ||||
|         STEP("Create a folder, file in it and a category"); | ||||
|         FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder(); | ||||
|         file = dataContent.usingUser(user).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         category = prepareCategoryUnderRoot(); | ||||
|  | ||||
|         STEP("Wait for indexing to complete"); | ||||
|         Utility.sleep(1000, 60000, () -> restClient.authenticateUser(user) | ||||
|                 .withCoreAPI() | ||||
|                 .usingCategory(category) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .getCategory() | ||||
|                 .assertThat() | ||||
|                 .field(FIELD_PATH) | ||||
|                 .isNotNull()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Verify path for a category got by ID. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testGetCategoryById_includePath() | ||||
|     { | ||||
|         STEP("Get category and verify if path is a general path for categories"); | ||||
|         final RestCategoryModel actualCategory = restClient.authenticateUser(user) | ||||
|                 .withCoreAPI() | ||||
|                 .usingCategory(category) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .getCategory(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         actualCategory.assertThat().field(FIELD_ID).is(category.getId()); | ||||
|         actualCategory.assertThat().field(FIELD_PATH).is("/categories/General"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Verify path for category. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testGetCategories_includePath() | ||||
|     { | ||||
|         STEP("Get few categories and verify its paths"); | ||||
|         final RestCategoryModel parentCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); | ||||
|         final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user) | ||||
|                 .withCoreAPI() | ||||
|                 .usingCategory(parentCategory) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .getCategoryChildren(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         assertTrue(actualCategories.getEntries().stream() | ||||
|                 .map(RestCategoryModel::onModel) | ||||
|                 .allMatch(cat -> cat.getPath().equals("/categories/General"))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Verify path for child category. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testGetChildCategory_includePath() | ||||
|     { | ||||
|         STEP("Create parent and child categories"); | ||||
|         final RestCategoryModel parentCategory = prepareCategoryUnderRoot(); | ||||
|         final RestCategoryModel childCategory = prepareCategoryUnder(parentCategory); | ||||
|  | ||||
|         STEP("Verify path for created child categories"); | ||||
|         final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user) | ||||
|                 .withCoreAPI() | ||||
|                 .usingCategory(parentCategory) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .getCategoryChildren(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         actualCategories.getEntries().stream() | ||||
|                 .map(RestCategoryModel::onModel) | ||||
|                 .forEach(cat -> cat.assertThat().field(FIELD_PATH).is("/categories/General/" + parentCategory.getName())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create category and verify that it has a path. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testCreateCategory_includingPath() | ||||
|     { | ||||
|         STEP("Create a category under root and verify if path is a general path for categories"); | ||||
|         final String categoryName = getRandomName("Category"); | ||||
|         final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID); | ||||
|         final RestCategoryModel aCategory = createCategoryModelWithName(categoryName); | ||||
|         final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser()) | ||||
|                 .withCoreAPI() | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .usingCategory(rootCategory) | ||||
|                 .createSingleCategory(aCategory); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(CREATED); | ||||
|         createdCategory.assertThat().field(FIELD_NAME).is(categoryName); | ||||
|         createdCategory.assertThat().field(FIELD_PATH).is("/categories/General"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update category and verify that it has a path. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testUpdateCategory_includePath() | ||||
|     { | ||||
|         STEP("Update linked category and verify if path is a general path for categories"); | ||||
|         final String categoryNewName = getRandomName("NewCategoryName"); | ||||
|         final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName); | ||||
|         final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser()) | ||||
|                 .withCoreAPI() | ||||
|                 .usingCategory(category) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .updateCategory(fixedCategoryModel); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         updatedCategory.assertThat().field(FIELD_ID).is(category.getId()); | ||||
|         updatedCategory.assertThat().field(FIELD_PATH).is("/categories/General"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Link node to categories and verify that they have path. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testLinkNodeToCategories_includePath() | ||||
|     { | ||||
|         STEP("Link node to categories and verify if path is a general path"); | ||||
|         final RestCategoryLinkBodyModel categoryLinkModel = createCategoryLinkModelWithId(category.getId()); | ||||
|         final RestCategoryModel linkedCategory = restClient.authenticateUser(dataUser.getAdminUser()) | ||||
|                 .withCoreAPI() | ||||
|                 .usingNode(file) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .linkToCategory(categoryLinkModel); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(CREATED); | ||||
|         linkedCategory.assertThat().field(FIELD_ID).is(category.getId()); | ||||
|         linkedCategory.assertThat().field(FIELD_PATH).is("/categories/General"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * List categories for given node and verify that they have a path. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API }) | ||||
|     public void testListCategoriesForNode_includePath() | ||||
|     { | ||||
|         STEP("Link file to category"); | ||||
|         final RestCategoryLinkBodyModel categoryLink = createCategoryLinkModelWithId(category.getId()); | ||||
|         final RestCategoryModel linkedCategory = restClient.authenticateUser(dataUser.getAdminUser()) | ||||
|                 .withCoreAPI() | ||||
|                 .usingNode(file) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .linkToCategory(categoryLink); | ||||
|  | ||||
|         STEP("Get linked category and verify if path is a general path"); | ||||
|         final RestCategoryModelsCollection linkedCategories = restClient.authenticateUser(dataUser.getAdminUser()) | ||||
|                 .withCoreAPI() | ||||
|                 .usingNode(file) | ||||
|                 .include(INCLUDE_PATH_PARAM) | ||||
|                 .getLinkedCategories(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         linkedCategories.assertThat().entriesListCountIs(1); | ||||
|         linkedCategories.getEntries().get(0).onModel().assertThat().field(FIELD_ID).is(category.getId()); | ||||
|         linkedCategories.getEntries().get(0).onModel().assertThat().field(FIELD_PATH).is("/categories/General"); | ||||
|     } | ||||
| } | ||||
| @@ -46,6 +46,7 @@ import org.testng.annotations.BeforeClass; | ||||
| abstract class CategoriesRestTest extends RestTest | ||||
| { | ||||
|     protected static final String INCLUDE_COUNT_PARAM = "count"; | ||||
|     protected static final String INCLUDE_PATH_PARAM = "path"; | ||||
|     protected static final String ROOT_CATEGORY_ID = "-root-"; | ||||
|     protected static final String CATEGORY_NAME_PREFIX = "CategoryName"; | ||||
|     protected static final String FIELD_NAME = "name"; | ||||
| @@ -53,6 +54,7 @@ abstract class CategoriesRestTest extends RestTest | ||||
|     protected static final String FIELD_PARENT_ID = "parentId"; | ||||
|     protected static final String FIELD_HAS_CHILDREN = "hasChildren"; | ||||
|     protected static final String FIELD_COUNT = "count"; | ||||
|     protected static final String FIELD_PATH = "path"; | ||||
|  | ||||
|     protected UserModel user; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package org.alfresco.rest.renditions; | ||||
|  | ||||
| import java.time.Duration; | ||||
| import com.google.common.base.Predicates; | ||||
| import org.alfresco.rest.RestTest; | ||||
| import org.alfresco.rest.core.RestResponse; | ||||
| import org.alfresco.rest.model.RestNodeModel; | ||||
| @@ -8,6 +10,8 @@ import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.FolderModel; | ||||
| import org.alfresco.utility.model.SiteModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.awaitility.Awaitility; | ||||
| import org.awaitility.Durations; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| @@ -68,15 +72,23 @@ public abstract class RenditionIntegrationTests extends RestTest | ||||
|      */ | ||||
|     protected RestNodeModel uploadFile(String sourceFile) throws Exception | ||||
|     { | ||||
|         FolderModel folder = FolderModel.getRandomFolderModel(); | ||||
|         folder = dataContent.usingUser(user).usingSite(site).createFolder(folder); | ||||
|         FolderModel folder = Awaitility | ||||
|             .await() | ||||
|             .atMost(Duration.ofSeconds(30)) | ||||
|             .pollInterval(Durations.ONE_SECOND) | ||||
|             .ignoreExceptions() | ||||
|             .until(() -> { | ||||
|                 FolderModel randomFolderModel = FolderModel.getRandomFolderModel(); | ||||
|                 return dataContent.usingUser(user).usingSite(site).createFolder(randomFolderModel); | ||||
|             }, Predicates.notNull()); | ||||
|         restClient.authenticateUser(user).configureRequestSpec() | ||||
|                 .addMultiPart("filedata", Utility.getResourceTestDataFile(sourceFile)); | ||||
|             .addMultiPart("filedata", Utility.getResourceTestDataFile(sourceFile)); | ||||
|         RestNodeModel fileNode = restClient.authenticateUser(user).withCoreAPI().usingNode(folder).createNode(); | ||||
|  | ||||
|         Assert.assertEquals(Integer.valueOf(restClient.getStatusCode()).intValue(), HttpStatus.CREATED.value(), | ||||
|                 "Failed to created a node for rendition tests using file " + sourceFile); | ||||
|             "Failed to created a node for rendition tests using file " + sourceFile); | ||||
|  | ||||
|         return fileNode; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify admin user gets tag using REST API and status code is OK (200)") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void adminIsAbleToGetTag() throws Exception | ||||
|     public void adminIsAbleToGetTag() | ||||
|     { | ||||
|         RestTagModel returnedTag = restClient.authenticateUser(adminUserModel).withCoreAPI().getTag(documentTag); | ||||
|         restClient.assertStatusCodeIs(HttpStatus.OK); | ||||
| @@ -35,7 +35,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify user with Manager role gets tag using REST API and status code is OK (200)") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY }) | ||||
|     public void userWithManagerRoleIsAbleToGetTag() throws Exception | ||||
|     public void userWithManagerRoleIsAbleToGetTag() | ||||
|     { | ||||
|         restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)); | ||||
|  | ||||
| @@ -47,7 +47,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Collaborator role gets tag using REST API and status code is OK (200)") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void userWithCollaboratorRoleIsAbleToGetTag() throws Exception | ||||
|     public void userWithCollaboratorRoleIsAbleToGetTag() | ||||
|     { | ||||
|         restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteCollaborator)); | ||||
|         RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); | ||||
| @@ -57,7 +57,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Contributor role gets tag using REST API and status code is OK (200)") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void userWithContributorRoleIsAbleToGetTag() throws Exception | ||||
|     public void userWithContributorRoleIsAbleToGetTag() | ||||
|     { | ||||
|         restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteContributor)); | ||||
|         RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); | ||||
| @@ -67,7 +67,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|      | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, description = "Verify user with Consumer role gets tag using REST API and status code is OK (200)") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void userWithConsumerRoleIsAbleToGetTag() throws Exception | ||||
|     public void userWithConsumerRoleIsAbleToGetTag() | ||||
|     { | ||||
|         restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteConsumer)); | ||||
|         RestTagModel returnedTag = restClient.withCoreAPI().getTag(documentTag); | ||||
| @@ -78,7 +78,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Verify Manager user gets status code 401 if authentication call fails") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY }) | ||||
| //    @Bug(id="MNT-16904", description = "It fails only on environment with tenants") | ||||
|     public void managerIsNotAbleToGetTagIfAuthenticationFails() throws Exception | ||||
|     public void managerIsNotAbleToGetTagIfAuthenticationFails() | ||||
|     { | ||||
|         UserModel managerUser = dataUser.usingAdmin().createRandomTestUser(); | ||||
|         String managerPassword = managerUser.getPassword(); | ||||
| @@ -92,7 +92,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, | ||||
|             description = "Verify that if tag id is invalid status code returned is 400") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void invalidTagIdTest() throws Exception | ||||
|     public void invalidTagIdTest() | ||||
|     { | ||||
|         String tagId = documentTag.getId(); | ||||
|         documentTag.setId("random_tag_value"); | ||||
| @@ -104,7 +104,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, | ||||
|             executionType = ExecutionType.REGRESSION, description = "Check that properties filter is applied when getting tag using Manager user.") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void checkPropertiesFilterIsApplied() throws Exception | ||||
|     public void checkPropertiesFilterIsApplied() | ||||
|     { | ||||
|         RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) | ||||
|                 .withParams("properties=id,tag").withCoreAPI().getTag(documentTag); | ||||
| @@ -117,7 +117,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, | ||||
|             executionType = ExecutionType.REGRESSION, description = "Check that Manager user can get tag of a folder.") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void getTagOfAFolder() throws Exception | ||||
|     public void getTagOfAFolder() | ||||
|     { | ||||
|         RestTagModel returnedTag = restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) | ||||
|                 .withCoreAPI().getTag(folderTag); | ||||
| @@ -128,7 +128,7 @@ public class GetTagTests extends TagsDataPrep | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, | ||||
|             executionType = ExecutionType.REGRESSION, description = "Check default error model schema. Use invalid skipCount parameter.") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void checkDefaultErrorModelSchema() throws Exception | ||||
|     public void checkDefaultErrorModelSchema() | ||||
|     { | ||||
|         restClient.authenticateUser(usersWithRoles.getOneUserWithRole(UserRole.SiteManager)) | ||||
|                 .withParams("skipCount=abc").withCoreAPI().getTag(documentTag); | ||||
| @@ -140,13 +140,14 @@ public class GetTagTests extends TagsDataPrep | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Verify that count field is not present for searched tag. | ||||
|      * Verify that count field is not present for searched tag when not requested. | ||||
|      */ | ||||
|     @Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION}) | ||||
|     public void testGetTag_notIncludingCount() | ||||
|     { | ||||
|         STEP("Create single tag as admin"); | ||||
|         final RestTagModel tagModel = createTagModelWithName(getRandomName(TAG_NAME_PREFIX).toLowerCase()); | ||||
|         String tagName = getRandomName(TAG_NAME_PREFIX).toLowerCase(); | ||||
|         final RestTagModel tagModel = createTagModelWithName(tagName); | ||||
|         final RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(CREATED); | ||||
| @@ -155,8 +156,26 @@ public class GetTagTests extends TagsDataPrep | ||||
|         final RestTagModel searchedTag = restClient.withCoreAPI().getTag(createdTag); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         searchedTag.assertThat().field(FIELD_TAG).is(tagModel.getTag()) | ||||
|                 .assertThat().field(FIELD_ID).isNotEmpty() | ||||
|                 .assertThat().field(FIELD_COUNT).isNull(); | ||||
|         RestTagModel expected = RestTagModel.builder().id(createdTag.getId()).tag(tagName).create(); | ||||
|         searchedTag.assertThat().isEqualTo(expected); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check that the count field can be included. | ||||
|      */ | ||||
|     @Test (groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTag_includeCount() | ||||
|     { | ||||
|         STEP("Create unused tag as admin"); | ||||
|         String tagName = getRandomName(TAG_NAME_PREFIX).toLowerCase(); | ||||
|         RestTagModel tagModel = createTagModelWithName(tagName); | ||||
|         RestTagModel createdTag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(tagModel); | ||||
|  | ||||
|         STEP("Get a single tag with the count field"); | ||||
|         RestTagModel searchedTag = restClient.withCoreAPI().include("count").getTag(createdTag); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         RestTagModel expected = RestTagModel.builder().id(createdTag.getId()).tag(tagName).count(0).create(); | ||||
|         searchedTag.assertThat().isEqualTo(expected); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package org.alfresco.rest.tags; | ||||
|  | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||||
| import static org.springframework.http.HttpStatus.OK; | ||||
|  | ||||
| import java.util.Set; | ||||
| @@ -16,6 +17,11 @@ import org.alfresco.utility.testrail.annotation.TestRail; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import java.util.Set; | ||||
| import java.util.stream.IntStream; | ||||
|  | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
|  | ||||
| @Test(groups = {TestGroup.REQUIRE_SOLR}) | ||||
| public class GetTagsTests extends TagsDataPrep | ||||
| { | ||||
| @@ -72,6 +78,138 @@ public class GetTagsTests extends TagsDataPrep | ||||
|             .and().entriesListContains("tag", documentTagValue2); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Include count in the query parameters and ensure count is as expected for returned tags. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withIncludeCount() | ||||
|     { | ||||
|         STEP("Get tags including count filter and ensure count is as expected for returned tags"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("include=count") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|  | ||||
|         returnedCollection.getEntries().stream() | ||||
|                 .filter(e -> e.onModel().getTag().equals(folderTagValue) || e.onModel().getTag().equals(documentTagValue)) | ||||
|                 .forEach(e -> e.onModel().assertThat().field("count").is(2)); | ||||
|  | ||||
|         returnedCollection.getEntries().stream() | ||||
|                 .filter(e -> e.onModel().getTag().equals(documentTagValue2)) | ||||
|                 .forEach(e -> e.onModel().assertThat().field("count").is(1)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by count. Default sort order should be ascending | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByCountDefaultOrderShouldBeAsc() | ||||
|     { | ||||
|         STEP("Get tags and order results by count. Default sort order should be ascending"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("include=count&orderBy=count") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedAscBy("count"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by count in ascending order | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByCountAsc() | ||||
|     { | ||||
|         STEP("Get tags and order results by count in ascending order"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("include=count&orderBy=count ASC") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedAscBy("count"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by count in descending order | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByCountDesc() | ||||
|     { | ||||
|         STEP("Get tags and order results by count in descending order"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("include=count&orderBy=count DESC") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedDescBy("count"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by tag name. Default sort order should be ascending | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByTagDefaultOrderShouldBeAsc() | ||||
|     { | ||||
|         STEP("Get tags and order results by tag name. Default sort order should be ascending"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("orderBy=tag") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedAscBy("tag"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by tag name in ascending order | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByTagAsc() | ||||
|     { | ||||
|         STEP("Get tags and order results by tag name in ascending order"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("orderBy=tag ASC") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedAscBy("tag"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tags and order results by tag name in descending order | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_withOrderByTagDesc() | ||||
|     { | ||||
|         STEP("Get tags and order results by tag name in descending order"); | ||||
|         returnedCollection = restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("orderBy=tag DESC") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(OK); | ||||
|         returnedCollection.assertThat().entriesListIsSortedDescBy("tag"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Ensure that we get a 400 error when we request to order by count without also including the tag count. | ||||
|      */ | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION }) | ||||
|     public void testGetTags_orderByCountWithoutIncludeCount() | ||||
|     { | ||||
|         restClient.authenticateUser(adminUserModel) | ||||
|                 .withParams("orderBy=count") | ||||
|                 .withCoreAPI() | ||||
|                 .getTags(); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(BAD_REQUEST); | ||||
|     } | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.SANITY, description = "Failed authentication get tags call returns status code 401 with Manager role") | ||||
|     @Test(groups = { TestGroup.REST_API, TestGroup.TAGS, TestGroup.SANITY }) | ||||
| //    @Bug(id="MNT-16904", description = "It fails only on environment with tenants") | ||||
| @@ -193,8 +331,7 @@ public class GetTagsTests extends TagsDataPrep | ||||
|                 .getPagination().assertThat().field("maxItems").is(100) | ||||
|                 .and().field("hasMoreItems").is("false") | ||||
|                 .and().field("count").is("0") | ||||
|                 .and().field("skipCount").is(20000) | ||||
|                 .and().field("totalItems").is(0); | ||||
|                 .and().field("skipCount").is(20000); | ||||
|     } | ||||
|  | ||||
|     @TestRail(section = { TestGroup.REST_API, TestGroup.TAGS }, executionType = ExecutionType.REGRESSION, | ||||
|   | ||||
| @@ -22,7 +22,8 @@ public class TagsDataPrep extends RestTest | ||||
|     protected static ListUserWithRoles usersWithRoles; | ||||
|     protected static SiteModel siteModel; | ||||
|     protected static FileModel document; | ||||
|     protected static FolderModel folder; | ||||
|     protected static FolderModel folder, folder2; | ||||
|     protected static RestTagModelsCollection folder2tags; | ||||
|     protected static String documentTagValue, documentTagValue2, folderTagValue; | ||||
|     protected static RestTagModel documentTag, documentTag2, folderTag, orphanTag, returnedModel; | ||||
|     protected static RestTagModelsCollection returnedCollection; | ||||
| @@ -38,6 +39,7 @@ public class TagsDataPrep extends RestTest | ||||
|         usersWithRoles = dataUser.usingAdmin().addUsersWithRolesToSite(siteModel, UserRole.SiteManager, UserRole.SiteCollaborator, UserRole.SiteConsumer, UserRole.SiteContributor); | ||||
|         document = dataContent.usingUser(adminUserModel).usingSite(siteModel).createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         folder = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder(); | ||||
|         folder2 = dataContent.usingUser(adminUserModel).usingSite(siteModel).createFolder(); | ||||
|  | ||||
|         documentTagValue = RandomData.getRandomName("tag").toLowerCase(); | ||||
|         documentTagValue2 = RandomData.getRandomName("tag").toLowerCase(); | ||||
| @@ -48,6 +50,7 @@ public class TagsDataPrep extends RestTest | ||||
|         documentTag2 = restClient.withCoreAPI().usingResource(document).addTag(documentTagValue2); | ||||
|         folderTag = restClient.withCoreAPI().usingResource(folder).addTag(folderTagValue); | ||||
|         orphanTag = restClient.withCoreAPI().createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("orphan-tag").toLowerCase()).create()); | ||||
|         folder2tags = restClient.withCoreAPI().usingResource(folder2).addTags(folderTagValue, documentTagValue); | ||||
|  | ||||
|         // Allow indexing to complete. | ||||
|         Utility.sleep(500, 60000, () -> | ||||
|   | ||||
| @@ -305,7 +305,24 @@ public class UpdateTagTests extends TagsDataPrep | ||||
|         returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().usingTag(orphanTag).update(newTagName); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(HttpStatus.OK); | ||||
|         returnedModel.assertThat().field("tag").is(newTagName); | ||||
|         returnedModel.assertThat().field("id").isNotNull(); | ||||
|         RestTagModel expected = RestTagModel.builder().id(orphanTag.getId()).tag(newTagName).create(); | ||||
|         returnedModel.assertThat().isEqualTo(expected); | ||||
|     } | ||||
|  | ||||
|     @Test (groups = { TestGroup.REST_API, TestGroup.TAGS }) | ||||
|     public void canUpdateTagAndGetCount() | ||||
|     { | ||||
|         STEP("Create an orphaned tag"); | ||||
|         String tagName = RandomData.getRandomName("tag").toLowerCase(); | ||||
|         RestTagModel createdTag = RestTagModel.builder().tag(tagName).create(); | ||||
|         RestTagModel tag = restClient.authenticateUser(adminUserModel).withCoreAPI().createSingleTag(createdTag); | ||||
|  | ||||
|         STEP("Update tag and request the count field"); | ||||
|         String newTagName = RandomData.getRandomName("new").toLowerCase(); | ||||
|         returnedModel = restClient.authenticateUser(adminUserModel).withCoreAPI().include("count").usingTag(tag).update(newTagName); | ||||
|  | ||||
|         restClient.assertStatusCodeIs(HttpStatus.OK); | ||||
|         RestTagModel expected = RestTagModel.builder().id(tag.getId()).tag(newTagName).count(0).create(); | ||||
|         returnedModel.assertThat().isEqualTo(expected); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
| @@ -30,10 +30,6 @@ | ||||
|             <groupId>org.alfresco</groupId> | ||||
|             <artifactId>alfresco-trashcan-cleaner</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.alfresco.services</groupId> | ||||
|             <artifactId>alfresco-messaging-repo</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>xalan</groupId> | ||||
|             <artifactId>xalan</artifactId> | ||||
| @@ -140,7 +136,7 @@ | ||||
|             <plugin> | ||||
|                 <groupId>org.codehaus.mojo</groupId> | ||||
|                 <artifactId>buildnumber-maven-plugin</artifactId> | ||||
|                 <version>3.0.0</version> | ||||
|                 <version>3.1.0</version> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <phase>validate</phase> | ||||
|   | ||||
							
								
								
									
										111
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>alfresco-community-repo</artifactId> | ||||
|     <version>20.136</version> | ||||
|     <version>23.1.0.163-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|     <name>Alfresco Community Repo Parent</name> | ||||
|  | ||||
| @@ -23,8 +23,8 @@ | ||||
|     </modules> | ||||
|  | ||||
|     <properties> | ||||
|         <acs.version.major>7</acs.version.major> | ||||
|         <acs.version.minor>4</acs.version.minor> | ||||
|         <acs.version.major>23</acs.version.major> | ||||
|         <acs.version.minor>1</acs.version.minor> | ||||
|         <acs.version.revision>0</acs.version.revision> | ||||
|         <acs.version.label /> | ||||
|         <amp.min.version>${acs.version.major}.0.0</amp.min.version> | ||||
| @@ -38,48 +38,48 @@ | ||||
|         <builder.name>entitled-builder</builder.name> | ||||
|         <local.registry>127.0.0.1:5000</local.registry> | ||||
|  | ||||
|         <java.version>11</java.version> | ||||
|         <java.version>17</java.version> | ||||
|         <maven.compiler.source>${java.version}</maven.compiler.source> | ||||
|         <maven.compiler.target>${java.version}</maven.compiler.target> | ||||
|         <maven.build.sourceVersion>${java.version}</maven.build.sourceVersion> | ||||
|  | ||||
|         <dir.root>${project.build.directory}/alf_data</dir.root> | ||||
|  | ||||
|         <dependency.alfresco-hb-data-sender.version>1.0.12</dependency.alfresco-hb-data-sender.version> | ||||
|         <dependency.alfresco-trashcan-cleaner.version>2.4.1</dependency.alfresco-trashcan-cleaner.version> | ||||
|         <dependency.alfresco-jlan.version>7.4</dependency.alfresco-jlan.version> | ||||
|         <dependency.alfresco-hb-data-sender.version>1.1.1</dependency.alfresco-hb-data-sender.version> | ||||
|         <dependency.alfresco-trashcan-cleaner.version>2.4.2</dependency.alfresco-trashcan-cleaner.version> | ||||
|         <dependency.alfresco-jlan.version>7.5</dependency.alfresco-jlan.version> | ||||
|         <dependency.alfresco-server-root.version>6.0.1</dependency.alfresco-server-root.version> | ||||
|         <dependency.alfresco-messaging-repo.version>1.2.20</dependency.alfresco-messaging-repo.version> | ||||
|         <dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version> | ||||
|         <dependency.activiti.version>5.23.0</dependency.activiti.version> | ||||
|         <dependency.alfresco-transform-service.version>2.1.0-A5</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-transform-core.version>3.1.0-A5</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-greenmail.version>6.5</dependency.alfresco-greenmail.version> | ||||
|         <dependency.acs-event-model.version>0.0.18</dependency.acs-event-model.version> | ||||
|         <dependency.alfresco-transform-service.version>3.0.0</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-transform-core.version>4.0.0</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-greenmail.version>6.9</dependency.alfresco-greenmail.version> | ||||
|         <dependency.acs-event-model.version>0.0.23</dependency.acs-event-model.version> | ||||
|  | ||||
|         <dependency.spring.version>5.3.25</dependency.spring.version> | ||||
|         <dependency.spring.version>5.3.27</dependency.spring.version> | ||||
|         <dependency.antlr.version>3.5.3</dependency.antlr.version> | ||||
|         <dependency.jackson.version>2.15.0-rc1</dependency.jackson.version> | ||||
|         <dependency.jackson.version>2.15.1</dependency.jackson.version> | ||||
|         <dependency.cxf.version>3.5.5</dependency.cxf.version> | ||||
|         <dependency.opencmis.version>1.0.0</dependency.opencmis.version> | ||||
|         <dependency.webscripts.version>8.40</dependency.webscripts.version> | ||||
|         <dependency.webscripts.version>8.45</dependency.webscripts.version> | ||||
|         <dependency.bouncycastle.version>1.70</dependency.bouncycastle.version> | ||||
|         <dependency.mockito-core.version>4.9.0</dependency.mockito-core.version> | ||||
|         <dependency.assertj.version>3.24.2</dependency.assertj.version> | ||||
|         <dependency.org-json.version>20230227</dependency.org-json.version> | ||||
|         <dependency.org-json.version>20230618</dependency.org-json.version> | ||||
|         <dependency.commons-dbcp.version>2.9.0</dependency.commons-dbcp.version> | ||||
|         <dependency.commons-io.version>2.11.0</dependency.commons-io.version> | ||||
|         <dependency.gson.version>2.8.9</dependency.gson.version> | ||||
|         <dependency.guava.version>32.1.1-jre</dependency.guava.version> | ||||
|         <dependency.httpclient.version>4.5.13</dependency.httpclient.version> | ||||
|         <dependency.httpcore.version>4.4.15</dependency.httpcore.version> | ||||
|         <dependency.httpcore.version>4.4.16</dependency.httpcore.version> | ||||
|         <dependency.commons-httpclient.version>3.1-HTTPCLIENT-1265</dependency.commons-httpclient.version> | ||||
|         <dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version> | ||||
|         <dependency.slf4j.version>2.0.3</dependency.slf4j.version> | ||||
|         <dependency.log4j.version>2.19.0</dependency.log4j.version> | ||||
|         <dependency.gytheio.version>0.18</dependency.gytheio.version> | ||||
|         <dependency.groovy.version>3.0.16</dependency.groovy.version> | ||||
|         <dependency.slf4j.version>2.0.7</dependency.slf4j.version> | ||||
|         <dependency.log4j.version>2.20.0</dependency.log4j.version> | ||||
|         <dependency.gytheio.version>0.20.0-A1</dependency.gytheio.version> | ||||
|         <dependency.groovy.version>3.0.18</dependency.groovy.version> | ||||
|         <dependency.tika.version>2.4.1</dependency.tika.version> | ||||
|         <dependency.spring-security.version>5.8.2</dependency.spring-security.version> | ||||
|         <dependency.spring-security.version>5.8.3</dependency.spring-security.version> | ||||
|         <dependency.truezip.version>7.7.10</dependency.truezip.version> | ||||
|         <dependency.poi.version>5.2.2</dependency.poi.version> | ||||
|         <dependency.poi-ooxml-lite.version>5.2.3</dependency.poi-ooxml-lite.version> | ||||
| @@ -89,11 +89,11 @@ | ||||
|         <dependency.netty.qpid.version>4.1.82.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue --> | ||||
|         <dependency.netty-tcnative.version>2.0.56.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies --> | ||||
|         <dependency.activemq.version>5.17.4</dependency.activemq.version> | ||||
|         <dependency.apache-compress.version>1.22</dependency.apache-compress.version> | ||||
|         <dependency.apache-compress.version>1.23.0</dependency.apache-compress.version> | ||||
|         <dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version> | ||||
|         <dependency.awaitility.version>4.2.0</dependency.awaitility.version> | ||||
|         <dependency.swagger-ui.version>3.38.0</dependency.swagger-ui.version> | ||||
|         <dependency.swagger-parser.version>1.0.63</dependency.swagger-parser.version> | ||||
|         <dependency.swagger-parser.version>1.0.67</dependency.swagger-parser.version> | ||||
|         <dependency.maven-filtering.version>3.1.1</dependency.maven-filtering.version> | ||||
|         <dependency.maven-artifact.version>3.8.6</dependency.maven-artifact.version> | ||||
|         <dependency.jdom2.version>2.0.6.1</dependency.jdom2.version> | ||||
| @@ -108,25 +108,25 @@ | ||||
|         <dependency.jakarta-mail-api.version>1.6.5</dependency.jakarta-mail-api.version> | ||||
|         <dependency.jakarta-json-api.version>1.1.6</dependency.jakarta-json-api.version> | ||||
|         <dependency.jakarta-json-path.version>2.8.0</dependency.jakarta-json-path.version> | ||||
|         <dependency.json-smart.version>2.4.10</dependency.json-smart.version> | ||||
|         <dependency.json-smart.version>2.5.0</dependency.json-smart.version> | ||||
|         <dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version> | ||||
|  | ||||
|         <alfresco.googledrive.version>3.4.0-M1</alfresco.googledrive.version> | ||||
|         <alfresco.aos-module.version>1.6.0-A4</alfresco.aos-module.version> | ||||
|         <alfresco.api-explorer.version>7.3.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share --> | ||||
|         <alfresco.googledrive.version>3.4.2-A2</alfresco.googledrive.version> | ||||
|         <alfresco.aos-module.version>1.6.1</alfresco.aos-module.version> | ||||
|         <alfresco.api-explorer.version>23.1.0-A1</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share --> | ||||
|  | ||||
|         <alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version> | ||||
|         <license-maven-plugin.version>2.0.1.alfresco-2</license-maven-plugin.version> | ||||
|         <license-maven-plugin.version>2.0.1</license-maven-plugin.version> | ||||
|  | ||||
|         <dependency.postgresql.version>42.5.2</dependency.postgresql.version> | ||||
|         <dependency.postgresql.version>42.6.0</dependency.postgresql.version> | ||||
|         <dependency.mysql.version>8.0.30</dependency.mysql.version> | ||||
|         <dependency.mysql-image.version>8</dependency.mysql-image.version> | ||||
|         <dependency.mariadb.version>2.7.4</dependency.mariadb.version> | ||||
|         <dependency.tas-utility.version>4.0.0</dependency.tas-utility.version> | ||||
|         <dependency.rest-assured.version>5.2.0</dependency.rest-assured.version> | ||||
|         <dependency.tas-email.version>1.11</dependency.tas-email.version> | ||||
|         <dependency.tas-webdav.version>1.7</dependency.tas-webdav.version> | ||||
|         <dependency.tas-ftp.version>1.7</dependency.tas-ftp.version> | ||||
|         <dependency.tas-utility.version>4.0.4</dependency.tas-utility.version> | ||||
|         <dependency.rest-assured.version>5.3.1</dependency.rest-assured.version> | ||||
|         <dependency.tas-email.version>1.23</dependency.tas-email.version> | ||||
|         <dependency.tas-webdav.version>1.18</dependency.tas-webdav.version> | ||||
|         <dependency.tas-ftp.version>1.18</dependency.tas-ftp.version> | ||||
|         <dependency.tas-dataprep.version>2.6</dependency.tas-dataprep.version> | ||||
|  | ||||
|         <!-- AGS properties shared between community and enterprise --> | ||||
| @@ -150,7 +150,7 @@ | ||||
|         <connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection> | ||||
|         <developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection> | ||||
|         <url>https://github.com/Alfresco/alfresco-community-repo</url> | ||||
|         <tag>20.136</tag> | ||||
|         <tag>HEAD</tag> | ||||
|     </scm> | ||||
|  | ||||
|     <distributionManagement> | ||||
| @@ -297,17 +297,6 @@ | ||||
|                     </exclusion> | ||||
|                 </exclusions> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>org.alfresco.services</groupId> | ||||
|                 <artifactId>alfresco-messaging-repo</artifactId> | ||||
|                 <version>${dependency.alfresco-messaging-repo.version}</version> | ||||
|                 <exclusions> | ||||
|                     <exclusion> | ||||
|                         <groupId>org.apache.activemq</groupId> | ||||
|                         <artifactId>activemq-broker</artifactId> | ||||
|                     </exclusion> | ||||
|                 </exclusions> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>org.alfresco</groupId> | ||||
|                 <artifactId>alfresco-server-root</artifactId> | ||||
| @@ -371,7 +360,7 @@ | ||||
|             <dependency> | ||||
|                 <groupId>commons-codec</groupId> | ||||
|                 <artifactId>commons-codec</artifactId> | ||||
|                 <version>1.15</version> | ||||
|                 <version>1.16.0</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>commons-lang</groupId> | ||||
| @@ -657,7 +646,7 @@ | ||||
|             <dependency> | ||||
|                 <groupId>org.jsoup</groupId> | ||||
|                 <artifactId>jsoup</artifactId> | ||||
|                 <version>1.15.3</version> | ||||
|                 <version>1.16.1</version> | ||||
|             </dependency> | ||||
|             <!-- upgrade dependency from TIKA --> | ||||
|             <dependency> | ||||
| @@ -741,7 +730,7 @@ | ||||
|             <dependency> | ||||
|                 <groupId>joda-time</groupId> | ||||
|                 <artifactId>joda-time</artifactId> | ||||
|                 <version>2.12.1</version> | ||||
|                 <version>2.12.5</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- provided dependencies --> | ||||
| @@ -783,6 +772,11 @@ | ||||
|                 <artifactId>gson</artifactId> | ||||
|                 <version>${dependency.gson.version}</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>com.google.guava</groupId> | ||||
|                 <artifactId>guava</artifactId> | ||||
|                 <version>${dependency.guava.version}</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>org.apache.camel</groupId> | ||||
|                 <artifactId>camel-core</artifactId> | ||||
| @@ -898,7 +892,7 @@ | ||||
|             <dependency> | ||||
|                 <groupId>org.projectlombok</groupId> | ||||
|                 <artifactId>lombok</artifactId> | ||||
|                 <version>1.18.24</version> | ||||
|                 <version>1.18.28</version> | ||||
|                 <scope>provided</scope> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
| @@ -936,7 +930,7 @@ | ||||
|                 <plugin> | ||||
|                     <groupId>io.fabric8</groupId> | ||||
|                     <artifactId>docker-maven-plugin</artifactId> | ||||
|                     <version>0.42.0</version> | ||||
|                     <version>0.43.0</version> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <artifactId>maven-surefire-plugin</artifactId> | ||||
| @@ -957,21 +951,21 @@ | ||||
|                 <plugin> | ||||
|                     <groupId>org.apache.maven.plugins</groupId> | ||||
|                     <artifactId>maven-javadoc-plugin</artifactId> | ||||
|                     <version>3.4.1</version> | ||||
|                     <version>3.5.0</version> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <groupId>org.apache.maven.plugins</groupId> | ||||
|                     <artifactId>maven-resources-plugin</artifactId> | ||||
|                     <version>3.3.0</version> | ||||
|                     <version>3.3.1</version> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <groupId>org.apache.maven.plugins</groupId> | ||||
|                     <artifactId>maven-dependency-plugin</artifactId> | ||||
|                     <version>3.3.0</version> | ||||
|                     <version>3.5.0</version> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <artifactId>maven-assembly-plugin</artifactId> | ||||
|                     <version>3.4.2</version> | ||||
|                     <version>3.6.0</version> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <groupId>org.alfresco.maven.plugin</groupId> | ||||
| @@ -1015,6 +1009,11 @@ | ||||
|                         <skipDeploy>true</skipDeploy> | ||||
|                     </configuration> | ||||
|                 </plugin> | ||||
|                 <plugin> | ||||
|                     <groupId>org.codehaus.cargo</groupId> | ||||
|                     <artifactId>cargo-maven3-plugin</artifactId> | ||||
|                     <version>1.10.8</version> | ||||
|                 </plugin> | ||||
|             </plugins> | ||||
|         </pluginManagement> | ||||
|         <plugins> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
| @@ -47,7 +47,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.apache.santuario</groupId> | ||||
|             <artifactId>xmlsec</artifactId> | ||||
|             <version>3.0.0</version> | ||||
|             <version>3.0.2</version> | ||||
|         </dependency> | ||||
|         <!-- newer version, see REPO-3133 --> | ||||
|         <dependency> | ||||
| @@ -130,7 +130,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.eclipse.jetty</groupId> | ||||
|             <artifactId>jetty-server</artifactId> | ||||
|             <version>10.0.11</version> | ||||
|             <version>10.0.14</version> | ||||
|             <scope>test</scope> | ||||
|             <exclusions> | ||||
|                 <exclusion> | ||||
| @@ -154,7 +154,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.alfresco.cmis.client</groupId> | ||||
|             <artifactId>alfresco-opencmis-extension</artifactId> | ||||
|             <version>2.1</version> | ||||
|             <version>2.3</version> | ||||
|             <scope>test</scope> | ||||
|             <exclusions> | ||||
|               <!-- Duplicates classes from jakarta.transaction:jakarta.transaction-api --> | ||||
|   | ||||
| @@ -32,10 +32,8 @@ import java.util.List; | ||||
|  | ||||
| import org.alfresco.rest.api.model.Category; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
|  | ||||
| @Experimental | ||||
| public interface Categories | ||||
| { | ||||
|     Category getCategoryById(StoreRef storeRef, String id, Parameters parameters); | ||||
|   | ||||
| @@ -32,22 +32,19 @@ import java.util.List; | ||||
| import org.alfresco.rest.api.model.Tag; | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
|  | ||||
| public interface Tags | ||||
| { | ||||
|     List<Tag> addTags(String nodeId, List<Tag> tags, Parameters parameters); | ||||
|     Tag getTag(StoreRef storeRef, String tagId); | ||||
|     Tag getTag(StoreRef storeRef, String tagId, Parameters parameters); | ||||
|     void deleteTag(String nodeId, String tagId); | ||||
|     CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params); | ||||
|     Tag changeTag(StoreRef storeRef, String tagId, Tag tag); | ||||
|     Tag changeTag(StoreRef storeRef, String tagId, Tag tag, Parameters parameters); | ||||
|     CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params); | ||||
|  | ||||
|     @Experimental | ||||
|     List<Tag> createTags(StoreRef storeRef, List<Tag> tags, Parameters parameters); | ||||
|  | ||||
|     @Experimental | ||||
|     default List<Tag> createTags(List<Tag> tags, Parameters parameters) | ||||
|     { | ||||
|         return createTags(STORE_REF_WORKSPACE_SPACESSTORE, tags, parameters); | ||||
|   | ||||
| @@ -49,7 +49,6 @@ import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException; | ||||
| import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| @@ -65,7 +64,6 @@ import org.alfresco.util.TypeConstraint; | ||||
| import org.apache.commons.collections.CollectionUtils; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| @Experimental | ||||
| public class CategoriesImpl implements Categories | ||||
| { | ||||
|     static final String INCLUDE_COUNT_PARAM = "count"; | ||||
| @@ -111,6 +109,11 @@ public class CategoriesImpl implements Categories | ||||
|             category.setCount(categoriesCount.getOrDefault(category.getId(), 0)); | ||||
|         } | ||||
|  | ||||
|         if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|         { | ||||
|             category.setPath(getCategoryPath(category)); | ||||
|         } | ||||
|  | ||||
|         return category; | ||||
|     } | ||||
|  | ||||
| @@ -128,6 +131,10 @@ public class CategoriesImpl implements Categories | ||||
|                     { | ||||
|                         category.setCount(0); | ||||
|                     } | ||||
|                     if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|                     { | ||||
|                         category.setPath(getCategoryPath(category)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect(Collectors.toList()); | ||||
|     } | ||||
| @@ -136,11 +143,18 @@ public class CategoriesImpl implements Categories | ||||
|     public List<Category> getCategoryChildren(final StoreRef storeRef, final String parentCategoryId, final Parameters parameters) | ||||
|     { | ||||
|         final NodeRef parentNodeRef = getCategoryNodeRef(storeRef, parentCategoryId); | ||||
|         final List<Category> categories = nodeService.getChildAssocs(parentNodeRef).stream() | ||||
|             .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName())) | ||||
|             .map(ChildAssociationRef::getChildRef) | ||||
|             .map(this::mapToCategory) | ||||
|             .collect(Collectors.toList()); | ||||
|         final List<Category> categories = nodeService.getChildAssocs(parentNodeRef) | ||||
|                 .stream() | ||||
|                 .filter(ca -> ContentModel.ASSOC_SUBCATEGORIES.equals(ca.getTypeQName())) | ||||
|                 .map(ChildAssociationRef::getChildRef) | ||||
|                 .map(this::mapToCategory) | ||||
|                 .peek(category -> { | ||||
|                     if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|                     { | ||||
|                         category.setPath(getCategoryPath(category)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect(Collectors.toList()); | ||||
|  | ||||
|         if (parameters.getInclude().contains(INCLUDE_COUNT_PARAM)) | ||||
|         { | ||||
| @@ -170,6 +184,11 @@ public class CategoriesImpl implements Categories | ||||
|             category.setCount(categoriesCount.getOrDefault(category.getId(), 0)); | ||||
|         } | ||||
|  | ||||
|         if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|         { | ||||
|             category.setPath(getCategoryPath(category)); | ||||
|         } | ||||
|  | ||||
|         return category; | ||||
|     } | ||||
|  | ||||
| @@ -200,7 +219,16 @@ public class CategoriesImpl implements Categories | ||||
|         } | ||||
|         final Collection<NodeRef> actualCategories = DefaultTypeConverter.INSTANCE.getCollection(NodeRef.class, currentCategories); | ||||
|  | ||||
|         return actualCategories.stream().map(this::mapToCategory).collect(Collectors.toList()); | ||||
|         return actualCategories | ||||
|                 .stream() | ||||
|                 .map(this::mapToCategory) | ||||
|                 .peek(category -> { | ||||
|                     if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|                     { | ||||
|                         category.setPath(getCategoryPath(category)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect(Collectors.toList()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -230,7 +258,16 @@ public class CategoriesImpl implements Categories | ||||
|  | ||||
|         linkNodeToCategories(contentNodeRef, categoryNodeRefs); | ||||
|  | ||||
|         return categoryNodeRefs.stream().map(this::mapToCategory).collect(Collectors.toList()); | ||||
|         return categoryNodeRefs | ||||
|                 .stream() | ||||
|                 .map(this::mapToCategory) | ||||
|                 .peek(category -> { | ||||
|                     if (parameters.getInclude().contains(Nodes.PARAM_INCLUDE_PATH)) | ||||
|                     { | ||||
|                         category.setPath(getCategoryPath(category)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .collect(Collectors.toList()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -475,4 +512,16 @@ public class CategoriesImpl implements Categories | ||||
|             .stream() | ||||
|             .collect(Collectors.toMap(pair -> pair.getFirst().toString().replace(idPrefix, StringUtils.EMPTY), Pair::getSecond)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get path for a given category in human-readable form. | ||||
|      * | ||||
|      * @param category Category to provide path for. | ||||
|      * @return Path for a category in human-readable form. | ||||
|      */ | ||||
|     private String getCategoryPath(final Category category) | ||||
|     { | ||||
|         final NodeRef categoryNodeRef = nodes.getNode(category.getId()).getNodeRef(); | ||||
|         return nodeService.getPath(categoryNodeRef).toDisplayPath(nodeService, permissionService); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,12 +30,12 @@ import static java.util.stream.Collectors.toList; | ||||
| import static org.alfresco.rest.antlr.WhereClauseParser.EQUALS; | ||||
| import static org.alfresco.rest.antlr.WhereClauseParser.IN; | ||||
| import static org.alfresco.rest.antlr.WhereClauseParser.MATCHES; | ||||
| import static org.alfresco.service.cmr.repository.StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; | ||||
| import static org.alfresco.service.cmr.tagging.TaggingService.TAG_ROOT_NODE_REF; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| @@ -43,6 +43,8 @@ import java.util.Objects; | ||||
| import java.util.Optional; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.query.ListBackedPagingResults; | ||||
| import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.repo.tagging.NonExistentTagException; | ||||
| import org.alfresco.repo.tagging.TagExistsException; | ||||
| @@ -62,7 +64,6 @@ import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rest.framework.resource.parameters.where.Query; | ||||
| import org.alfresco.rest.framework.resource.parameters.where.QueryHelper; | ||||
| import org.alfresco.rest.framework.resource.parameters.where.QueryImpl; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| @@ -80,40 +81,40 @@ import org.apache.commons.collections.CollectionUtils; | ||||
|  */ | ||||
| public class TagsImpl implements Tags | ||||
| { | ||||
| 	private static final String PARAM_INCLUDE_COUNT = "count"; | ||||
| 	private static final String PARAM_WHERE_TAG = "tag"; | ||||
| 	static final String NOT_A_VALID_TAG = "An invalid parameter has been supplied"; | ||||
| 	static final String NO_PERMISSION_TO_MANAGE_A_TAG = "Current user does not have permission to manage a tag"; | ||||
|     public static final String PARAM_INCLUDE_COUNT = "count"; | ||||
|     private static final String PARAM_WHERE_TAG = "tag"; | ||||
|     static final String NOT_A_VALID_TAG = "An invalid parameter has been supplied"; | ||||
|     static final String NO_PERMISSION_TO_MANAGE_A_TAG = "Current user does not have permission to manage a tag"; | ||||
|  | ||||
|     private Nodes nodes; | ||||
| 	private NodeService nodeService; | ||||
| 	private TaggingService taggingService; | ||||
| 	private TypeConstraint typeConstraint; | ||||
| 	private AuthorityService authorityService; | ||||
|     private NodeService nodeService; | ||||
|     private TaggingService taggingService; | ||||
|     private TypeConstraint typeConstraint; | ||||
|     private AuthorityService authorityService; | ||||
|  | ||||
| 	public void setTypeConstraint(TypeConstraint typeConstraint) | ||||
| 	{ | ||||
| 		this.typeConstraint = typeConstraint; | ||||
| 	} | ||||
|  | ||||
| 	public void setNodes(Nodes nodes) | ||||
|     public void setTypeConstraint(TypeConstraint typeConstraint) | ||||
|     { | ||||
| 		this.nodes = nodes; | ||||
| 	} | ||||
| 	public void setNodeService(NodeService nodeService) | ||||
| 	{ | ||||
| 		this.nodeService = nodeService; | ||||
| 	} | ||||
| 	 | ||||
|         this.typeConstraint = typeConstraint; | ||||
|     } | ||||
|  | ||||
|     public void setNodes(Nodes nodes) | ||||
|     { | ||||
|         this.nodes = nodes; | ||||
|     } | ||||
|     public void setNodeService(NodeService nodeService) | ||||
|     { | ||||
|         this.nodeService = nodeService; | ||||
|     } | ||||
|  | ||||
|     public void setTaggingService(TaggingService taggingService) | ||||
|     { | ||||
| 		this.taggingService = taggingService; | ||||
| 	} | ||||
|         this.taggingService = taggingService; | ||||
|     } | ||||
|  | ||||
| 	public void setAuthorityService(AuthorityService authorityService) | ||||
| 	{ | ||||
| 		this.authorityService = authorityService; | ||||
| 	} | ||||
|     public void setAuthorityService(AuthorityService authorityService) | ||||
|     { | ||||
|         this.authorityService = authorityService; | ||||
|     } | ||||
|  | ||||
|     public List<Tag> addTags(String nodeId, final List<Tag> tags, final Parameters parameters) | ||||
|     { | ||||
| @@ -128,14 +129,15 @@ public class TagsImpl implements Tags | ||||
|         { | ||||
|             List<Pair<String, NodeRef>> tagNodeRefs = taggingService.addTags(nodeRef, tagValues); | ||||
|             List<Tag> ret = new ArrayList<>(tags.size()); | ||||
| 			List<Pair<String, Integer>> tagsCountPairList = taggingService.findTaggedNodesAndCountByTagName(nodeRef.getStoreRef()); | ||||
| 			Map<String, Integer> tagsCountMap = tagsCountPairList.stream().collect(Collectors.toMap(Pair::getFirst,Pair::getSecond)); | ||||
|             List<Pair<String, Integer>> tagsCountPairList = taggingService.findTaggedNodesAndCountByTagName(nodeRef.getStoreRef()); | ||||
|             Map<String, Long> tagsCountMap = tagsCountPairList.stream().collect(Collectors.toMap(Pair::getFirst, pair -> Long.valueOf(pair.getSecond()))); | ||||
|             for (Pair<String, NodeRef> pair : tagNodeRefs) | ||||
|             { | ||||
| 				Tag createdTag = new Tag(pair.getSecond(), pair.getFirst()); | ||||
| 				if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) { | ||||
| 					createdTag.setCount(Optional.ofNullable(tagsCountMap.get(createdTag.getTag())).orElse(0) + 1); | ||||
| 				} | ||||
|                 Tag createdTag = new Tag(pair.getSecond(), pair.getFirst()); | ||||
|                 if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) | ||||
|                 { | ||||
|                     createdTag.setCount(Optional.ofNullable(tagsCountMap.get(createdTag.getTag())).orElse(0L) + 1); | ||||
|                 } | ||||
|                 ret.add(createdTag); | ||||
|             } | ||||
|             return ret; | ||||
| @@ -145,105 +147,112 @@ public class TagsImpl implements Tags | ||||
|             throw new InvalidArgumentException(e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 	 | ||||
|  | ||||
|     public void deleteTag(String nodeId, String tagId) | ||||
|     { | ||||
| 		NodeRef nodeRef = nodes.validateNode(nodeId); | ||||
| 		getTag(tagId); | ||||
|     	NodeRef existingTagNodeRef = validateTag(tagId); | ||||
|     	String tagValue = taggingService.getTagName(existingTagNodeRef); | ||||
|     	taggingService.removeTag(nodeRef, tagValue); | ||||
|         NodeRef nodeRef = nodes.validateNode(nodeId); | ||||
|         getTag(STORE_REF_WORKSPACE_SPACESSTORE, tagId, null); | ||||
|         NodeRef existingTagNodeRef = validateTag(tagId); | ||||
|         String tagValue = taggingService.getTagName(existingTagNodeRef); | ||||
|         taggingService.removeTag(nodeRef, tagValue); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| 	public void deleteTagById(StoreRef storeRef, String tagId) { | ||||
| 		verifyAdminAuthority(); | ||||
|     public void deleteTagById(StoreRef storeRef, String tagId) { | ||||
|         verifyAdminAuthority(); | ||||
|  | ||||
| 		NodeRef tagNodeRef = validateTag(storeRef, tagId); | ||||
| 		String tagValue = taggingService.getTagName(tagNodeRef); | ||||
| 		taggingService.deleteTag(storeRef, tagValue); | ||||
| 	} | ||||
|         NodeRef tagNodeRef = validateTag(storeRef, tagId); | ||||
|         String tagValue = taggingService.getTagName(tagNodeRef); | ||||
|         taggingService.deleteTag(storeRef, tagValue); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CollectionWithPagingInfo<Tag> getTags(StoreRef storeRef, Parameters params) | ||||
|     { | ||||
|         Paging paging = params.getPaging(); | ||||
|         Pair<String, Boolean> sorting = !params.getSorting().isEmpty() ? new Pair<>(params.getSorting().get(0).column, params.getSorting().get(0).asc) : null; | ||||
|         Map<Integer, Collection<String>> namesFilters = resolveTagNamesQuery(params.getQuery()); | ||||
|         PagingResults<Pair<NodeRef, String>> results = taggingService.getTags(storeRef, Util.getPagingRequest(paging), namesFilters.get(EQUALS), namesFilters.get(MATCHES)); | ||||
|  | ||||
|         Integer totalItems = results.getTotalResultCount().getFirst(); | ||||
|         List<Pair<NodeRef, String>> page = results.getPage(); | ||||
|         List<Tag> tags = new ArrayList<>(page.size()); | ||||
|         for (Pair<NodeRef, String> pair : page) | ||||
|         { | ||||
|             Tag selectedTag = new Tag(pair.getFirst(), pair.getSecond()); | ||||
|             tags.add(selectedTag); | ||||
|         } | ||||
|         Map<NodeRef, Long> results = taggingService.getTags(storeRef, params.getInclude(), sorting, namesFilters.get(EQUALS), namesFilters.get(MATCHES)); | ||||
|  | ||||
|         List<Tag> tagsList = results.entrySet().stream().map(entry -> new Tag(entry.getKey(), (String)nodeService.getProperty(entry.getKey(), ContentModel.PROP_NAME))).collect(Collectors.toList()); | ||||
|  | ||||
|         if (params.getInclude().contains(PARAM_INCLUDE_COUNT)) | ||||
|         { | ||||
|             List<Pair<String, Integer>> tagsByCount = taggingService.findTaggedNodesAndCountByTagName(storeRef); | ||||
|             Map<String, Integer> tagsByCountMap = new HashMap<>(); | ||||
|             if (tagsByCount != null) | ||||
|             { | ||||
|                 for (Pair<String, Integer> tagByCountElem : tagsByCount) | ||||
|                 { | ||||
|                     tagsByCountMap.put(tagByCountElem.getFirst(), tagByCountElem.getSecond()); | ||||
|                 } | ||||
|             } | ||||
|             tags.forEach(tag -> tag.setCount(Optional.ofNullable(tagsByCountMap.get(tag.getTag())).orElse(0))); | ||||
|             tagsList.forEach(tag -> tag.setCount(results.get(tag.getNodeRef()))); | ||||
|         } | ||||
|  | ||||
|         return CollectionWithPagingInfo.asPaged(paging, tags, results.hasMoreItems(), totalItems); | ||||
|         ListBackedPagingResults listBackedPagingResults = new ListBackedPagingResults(tagsList, Util.getPagingRequest(params.getPaging())); | ||||
|  | ||||
|         return CollectionWithPagingInfo.asPaged(paging, listBackedPagingResults.getPage(), listBackedPagingResults.hasMoreItems(), (Integer) listBackedPagingResults.getTotalResultCount().getFirst()); | ||||
|     } | ||||
|  | ||||
|     public NodeRef validateTag(String tagId) | ||||
|     { | ||||
|     	NodeRef tagNodeRef = nodes.validateNode(tagId); | ||||
| 		return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef); | ||||
|         NodeRef tagNodeRef = nodes.validateNode(tagId); | ||||
|         return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef); | ||||
|     } | ||||
|      | ||||
|     public NodeRef validateTag(StoreRef storeRef, String tagId) | ||||
|     { | ||||
|     	NodeRef tagNodeRef = nodes.validateNode(storeRef, tagId); | ||||
| 		return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef); | ||||
|         NodeRef tagNodeRef = nodes.validateNode(storeRef, tagId); | ||||
|         return checkTagRootAsNodePrimaryParent(tagId, tagNodeRef); | ||||
|     } | ||||
|  | ||||
|     public Tag changeTag(StoreRef storeRef, String tagId, Tag tag) | ||||
|     /** | ||||
|      * Find the number of times the given tag is used (if requested). | ||||
|      * | ||||
|      * @param storeRef The store the tag is in. | ||||
|      * @param tagName The name of the tag. | ||||
|      * @param parameters The request parameters object containing the includes parameter. | ||||
|      * @return The number of times the tag is applied, or null if "count" wasn't in the include parameter. | ||||
|      */ | ||||
|     private Long findCountIfRequested(StoreRef storeRef, String tagName, Parameters parameters) | ||||
|     { | ||||
|     	try | ||||
|     	{ | ||||
| 	    	NodeRef existingTagNodeRef = validateTag(storeRef, tagId); | ||||
| 	    	String existingTagName = taggingService.getTagName(existingTagNodeRef); | ||||
| 	    	String newTagName = tag.getTag(); | ||||
| 	    	NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName); | ||||
| 	    	return new Tag(newTagNodeRef, newTagName); | ||||
|     	} | ||||
|     	catch(NonExistentTagException e) | ||||
|     	{ | ||||
|     		throw new NotFoundException(e.getMessage()); | ||||
|     	} | ||||
|     	catch(TagExistsException e) | ||||
|     	{ | ||||
|     		throw new ConstraintViolatedException(e.getMessage()); | ||||
|     	} | ||||
|     	catch(TaggingException e) | ||||
|     	{ | ||||
|     		throw new InvalidArgumentException(e.getMessage()); | ||||
|     	} | ||||
|         Long count = null; | ||||
|         if (parameters != null && parameters.getInclude() != null && parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) | ||||
|         { | ||||
|             count = taggingService.findCountByTagName(storeRef, tagName); | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     public Tag getTag(String tagId) | ||||
|     @Override | ||||
|     public Tag changeTag(StoreRef storeRef, String tagId, Tag tag, Parameters parameters) | ||||
|     { | ||||
|     	return getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, tagId); | ||||
|         try | ||||
|         { | ||||
|             NodeRef existingTagNodeRef = validateTag(storeRef, tagId); | ||||
|             String existingTagName = taggingService.getTagName(existingTagNodeRef); | ||||
|             Long count = findCountIfRequested(storeRef, existingTagName, parameters); | ||||
|             String newTagName = tag.getTag(); | ||||
|             NodeRef newTagNodeRef = taggingService.changeTag(storeRef, existingTagName, newTagName); | ||||
|             return Tag.builder().nodeRef(newTagNodeRef).tag(newTagName).count(count).create(); | ||||
|         } | ||||
|         catch(NonExistentTagException e) | ||||
|         { | ||||
|             throw new NotFoundException(e.getMessage()); | ||||
|         } | ||||
|         catch(TagExistsException e) | ||||
|         { | ||||
|             throw new ConstraintViolatedException(e.getMessage()); | ||||
|         } | ||||
|         catch(TaggingException e) | ||||
|         { | ||||
|             throw new InvalidArgumentException(e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Tag getTag(StoreRef storeRef, String tagId) | ||||
|     @Override | ||||
|     public Tag getTag(StoreRef storeRef, String tagId, Parameters parameters) | ||||
|     { | ||||
|     	NodeRef tagNodeRef = validateTag(storeRef, tagId); | ||||
|     	String tagValue = taggingService.getTagName(tagNodeRef); | ||||
|     	return new Tag(tagNodeRef, tagValue); | ||||
|         NodeRef tagNodeRef = validateTag(storeRef, tagId); | ||||
|         String tagName = taggingService.getTagName(tagNodeRef); | ||||
|         Long count = findCountIfRequested(storeRef, tagName, parameters); | ||||
|         return Tag.builder().nodeRef(tagNodeRef).tag(tagName).count(count).create(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CollectionWithPagingInfo<Tag> getTags(String nodeId, Parameters params) | ||||
|     { | ||||
|         NodeRef nodeRef = nodes.validateOrLookupNode(nodeId); | ||||
| @@ -259,80 +268,79 @@ public class TagsImpl implements Tags | ||||
|         return CollectionWithPagingInfo.asPaged(params.getPaging(), tags, results.hasMoreItems(), (totalItems == null ? null : totalItems.intValue())); | ||||
|     } | ||||
|  | ||||
| 	@Experimental | ||||
| 	@Override | ||||
| 	public List<Tag> createTags(final StoreRef storeRef, final List<Tag> tags, final Parameters parameters) | ||||
| 	{ | ||||
| 		verifyAdminAuthority(); | ||||
| 		final List<String> tagNames = Optional.ofNullable(tags).orElse(Collections.emptyList()).stream() | ||||
| 			.filter(Objects::nonNull) | ||||
| 			.map(Tag::getTag) | ||||
| 			.distinct() | ||||
| 			.collect(toList()); | ||||
|     @Override | ||||
|     public List<Tag> createTags(final StoreRef storeRef, final List<Tag> tags, final Parameters parameters) | ||||
|     { | ||||
|         verifyAdminAuthority(); | ||||
|         final List<String> tagNames = Optional.ofNullable(tags).orElse(Collections.emptyList()).stream() | ||||
|             .filter(Objects::nonNull) | ||||
|             .map(Tag::getTag) | ||||
|             .distinct() | ||||
|             .collect(toList()); | ||||
|  | ||||
| 		if (CollectionUtils.isEmpty(tagNames)) | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException(NOT_A_VALID_TAG); | ||||
| 		} | ||||
|         if (CollectionUtils.isEmpty(tagNames)) | ||||
|         { | ||||
|             throw new InvalidArgumentException(NOT_A_VALID_TAG); | ||||
|         } | ||||
|  | ||||
| 		return taggingService.createTags(storeRef, tagNames).stream() | ||||
| 			.map(pair -> Tag.builder().tag(pair.getFirst()).nodeRef(pair.getSecond()).create()) | ||||
| 			.peek(tag -> { | ||||
| 				if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) | ||||
| 				{ | ||||
| 					tag.setCount(0); | ||||
| 				} | ||||
| 			}).collect(toList()); | ||||
| 	} | ||||
|         return taggingService.createTags(storeRef, tagNames).stream() | ||||
|             .map(pair -> Tag.builder().tag(pair.getFirst()).nodeRef(pair.getSecond()).create()) | ||||
|             .peek(tag -> { | ||||
|                 if (parameters.getInclude().contains(PARAM_INCLUDE_COUNT)) | ||||
|                 { | ||||
|                     tag.setCount(0L); | ||||
|                 } | ||||
|             }).collect(toList()); | ||||
|     } | ||||
|  | ||||
| 	private void verifyAdminAuthority() | ||||
| 	{ | ||||
| 		if (!authorityService.hasAdminAuthority()) | ||||
| 		{ | ||||
| 			throw new PermissionDeniedException(NO_PERMISSION_TO_MANAGE_A_TAG); | ||||
| 		} | ||||
| 	} | ||||
|     private void verifyAdminAuthority() | ||||
|     { | ||||
|         if (!authorityService.hasAdminAuthority()) | ||||
|         { | ||||
|             throw new PermissionDeniedException(NO_PERMISSION_TO_MANAGE_A_TAG); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 	/** | ||||
| 	 * Method resolves where query looking for clauses: EQUALS, IN or MATCHES. | ||||
| 	 * Expected values for EQUALS and IN will be merged under EQUALS clause. | ||||
| 	 * @param namesQuery Where query with expected tag name(s). | ||||
| 	 * @return Map of expected exact and alike tag names. | ||||
| 	 */ | ||||
| 	private Map<Integer, Collection<String>> resolveTagNamesQuery(final Query namesQuery) | ||||
| 	{ | ||||
| 		if (namesQuery == null || namesQuery == QueryImpl.EMPTY) | ||||
| 		{ | ||||
| 			return Collections.emptyMap(); | ||||
| 		} | ||||
|     /** | ||||
|      * Method resolves where query looking for clauses: EQUALS, IN or MATCHES. | ||||
|      * Expected values for EQUALS and IN will be merged under EQUALS clause. | ||||
|      * @param namesQuery Where query with expected tag name(s). | ||||
|      * @return Map of expected exact and alike tag names. | ||||
|      */ | ||||
|     private Map<Integer, Collection<String>> resolveTagNamesQuery(final Query namesQuery) | ||||
|     { | ||||
|         if (namesQuery == null || namesQuery == QueryImpl.EMPTY) | ||||
|         { | ||||
|             return Collections.emptyMap(); | ||||
|         } | ||||
|  | ||||
| 		final Map<Integer, Collection<String>> properties = QueryHelper | ||||
| 			.resolve(namesQuery) | ||||
| 			.usingOrOperator() | ||||
| 			.withoutNegations() | ||||
| 			.getProperty(PARAM_WHERE_TAG) | ||||
| 			.getExpectedValuesForAnyOf(EQUALS, IN, MATCHES) | ||||
| 			.skipNegated(); | ||||
|         final Map<Integer, Collection<String>> properties = QueryHelper | ||||
|             .resolve(namesQuery) | ||||
|             .usingOrOperator() | ||||
|             .withoutNegations() | ||||
|             .getProperty(PARAM_WHERE_TAG) | ||||
|             .getExpectedValuesForAnyOf(EQUALS, IN, MATCHES) | ||||
|             .skipNegated(); | ||||
|  | ||||
| 		return properties.entrySet().stream() | ||||
| 			.collect(Collectors.groupingBy((entry) -> { | ||||
| 				if (entry.getKey() == EQUALS || entry.getKey() == IN) | ||||
| 				{ | ||||
| 					return EQUALS; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					return MATCHES; | ||||
| 				} | ||||
| 			}, Collectors.flatMapping((entry) -> entry.getValue().stream().map(String::toLowerCase), Collectors.toCollection(HashSet::new)))); | ||||
| 	} | ||||
|         return properties.entrySet().stream() | ||||
|             .collect(Collectors.groupingBy((entry) -> { | ||||
|                 if (entry.getKey() == EQUALS || entry.getKey() == IN) | ||||
|                 { | ||||
|                     return EQUALS; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return MATCHES; | ||||
|                 } | ||||
|             }, Collectors.flatMapping((entry) -> entry.getValue().stream().map(String::toLowerCase), Collectors.toCollection(HashSet::new)))); | ||||
|     } | ||||
|  | ||||
| 	private NodeRef checkTagRootAsNodePrimaryParent(String tagId, NodeRef tagNodeRef) | ||||
| 	{ | ||||
| 		if ( tagNodeRef == null || !nodeService.getPrimaryParent(tagNodeRef).getParentRef().equals(TAG_ROOT_NODE_REF)) | ||||
| 		{ | ||||
| 			throw new EntityNotFoundException(tagId); | ||||
| 		} | ||||
| 		return tagNodeRef; | ||||
| 	} | ||||
|     private NodeRef checkTagRootAsNodePrimaryParent(String tagId, NodeRef tagNodeRef) | ||||
|     { | ||||
|         if ( tagNodeRef == null || !nodeService.getPrimaryParent(tagNodeRef).getParentRef().equals(TAG_ROOT_NODE_REF)) | ||||
|         { | ||||
|             throw new EntityNotFoundException(tagId); | ||||
|         } | ||||
|         return tagNodeRef; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,6 +35,7 @@ public class Category | ||||
|     private String parentId; | ||||
|     private boolean hasChildren; | ||||
|     private Integer count; | ||||
|     private String path; | ||||
|  | ||||
|     public String getId() | ||||
|     { | ||||
| @@ -91,6 +92,14 @@ public class Category | ||||
|         this.count = count; | ||||
|     } | ||||
|  | ||||
|     public String getPath() { | ||||
|         return path; | ||||
|     } | ||||
|  | ||||
|     public void setPath(String path) { | ||||
|         this.path = path; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object o) | ||||
|     { | ||||
| @@ -100,19 +109,20 @@ public class Category | ||||
|             return false; | ||||
|         Category category = (Category) o; | ||||
|         return hasChildren == category.hasChildren && Objects.equals(id, category.id) && Objects.equals(name, category.name) && Objects.equals(parentId, category.parentId) | ||||
|             && Objects.equals(count, category.count); | ||||
|             && Objects.equals(count, category.count) && Objects.equals(path, category.path); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() | ||||
|     { | ||||
|         return Objects.hash(id, name, parentId, hasChildren, count); | ||||
|         return Objects.hash(id, name, parentId, hasChildren, count, path); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() | ||||
|     { | ||||
|         return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren + ", count=" + count + '}'; | ||||
|         return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", parentId='" + parentId + '\'' + ", hasChildren=" + hasChildren | ||||
|                 + ", count=" + count + ", path=" + path + '}'; | ||||
|     } | ||||
|  | ||||
|     public static Builder builder() | ||||
| @@ -127,6 +137,7 @@ public class Category | ||||
|         private String parentId; | ||||
|         private boolean hasChildren; | ||||
|         private Integer count; | ||||
|         private String path; | ||||
|  | ||||
|         public Builder id(String id) | ||||
|         { | ||||
| @@ -158,6 +169,12 @@ public class Category | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder path(String path) | ||||
|         { | ||||
|             this.path = path; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Category create() | ||||
|         { | ||||
|             final Category category = new Category(); | ||||
| @@ -166,6 +183,7 @@ public class Category | ||||
|             category.setParentId(parentId); | ||||
|             category.setHasChildren(hasChildren); | ||||
|             category.setCount(count); | ||||
|             category.setPath(path); | ||||
|             return category; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -42,7 +42,7 @@ public class Tag implements Comparable<Tag> | ||||
| { | ||||
| 	private NodeRef nodeRef; | ||||
| 	private String tag; | ||||
| 	private Integer count; | ||||
| 	private Long count; | ||||
|  | ||||
|     public Tag() | ||||
| 	{ | ||||
| @@ -76,13 +76,13 @@ public class Tag implements Comparable<Tag> | ||||
| 		this.tag = Optional.ofNullable(tag).map(String::toLowerCase).orElse(null); | ||||
| 	} | ||||
| 	 | ||||
| 	public Integer getCount() | ||||
| 	public Long getCount() | ||||
| 	{ | ||||
| 	 | ||||
| 	    return count; | ||||
| 	} | ||||
|  | ||||
| 	public void setCount(Integer count) | ||||
| 	public void setCount(Long count) | ||||
| 	{ | ||||
| 	    this.count = count; | ||||
| 	} | ||||
| @@ -133,7 +133,7 @@ public class Tag implements Comparable<Tag> | ||||
| 	{ | ||||
| 		private NodeRef nodeRef; | ||||
| 		private String tag; | ||||
| 		private Integer count; | ||||
| 		private Long count; | ||||
|  | ||||
| 		public Builder nodeRef(NodeRef nodeRef) | ||||
| 		{ | ||||
| @@ -147,7 +147,7 @@ public class Tag implements Comparable<Tag> | ||||
| 			return this; | ||||
| 		} | ||||
|  | ||||
| 		public Builder count(Integer count) | ||||
| 		public Builder count(Long count) | ||||
| 		{ | ||||
| 			this.count = count; | ||||
| 			return this; | ||||
|   | ||||
| @@ -823,6 +823,11 @@ public class SearchMapper | ||||
|                 sp.setLimitBy(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS); | ||||
|                 sp.setMaxPermissionCheckTimeMillis(limits.getPermissionEvaluationTime()); | ||||
|             } | ||||
|  | ||||
|             if(limits.getTrackTotalHitsLimit() != null) | ||||
|             { | ||||
|                 sp.setTrackTotalHits(limits.getTrackTotalHitsLimit()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -93,7 +93,7 @@ public class FacetField | ||||
|  | ||||
|     public String toFilterQuery(String value) | ||||
|     { | ||||
|         return field+":\""+value+"\""; | ||||
|         return ("Null".equals(value)) ? "ISNULL:\"" + field + "\"" : field + ":\"" + value + "\""; | ||||
|     } | ||||
|  | ||||
|     public String getPrefix() | ||||
|   | ||||
| @@ -37,13 +37,16 @@ public class Limits | ||||
|  | ||||
|     private final Integer permissionEvaluationTime; | ||||
|     private final Integer permissionEvaluationCount; | ||||
|     private final Integer trackTotalHitsLimit; | ||||
|  | ||||
|     @JsonCreator | ||||
|     public Limits(@JsonProperty("permissionEvaluationTime") Integer permissionEvaluationTime, | ||||
|                   @JsonProperty("permissionEvaluationCount") Integer permissionEvaluationCount) | ||||
|                   @JsonProperty("permissionEvaluationCount") Integer permissionEvaluationCount, | ||||
|                   @JsonProperty("trackTotalHitsLimit") Integer trackTotalHitsLimit) | ||||
|     { | ||||
|         this.permissionEvaluationTime = permissionEvaluationTime; | ||||
|         this.permissionEvaluationCount = permissionEvaluationCount; | ||||
|         this.trackTotalHitsLimit = trackTotalHitsLimit; | ||||
|     } | ||||
|  | ||||
|     public Integer getPermissionEvaluationTime() | ||||
| @@ -55,4 +58,9 @@ public class Limits | ||||
|     { | ||||
|         return permissionEvaluationCount; | ||||
|     } | ||||
|  | ||||
|     public Integer getTrackTotalHitsLimit() | ||||
|     { | ||||
|         return trackTotalHitsLimit; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,8 +25,8 @@ | ||||
|  */ | ||||
| package org.alfresco.rest.api.tags; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| import java.util.List; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import org.alfresco.rest.api.Tags; | ||||
| import org.alfresco.rest.api.model.Tag; | ||||
| @@ -36,7 +36,6 @@ import org.alfresco.rest.framework.resource.EntityResource; | ||||
| import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| import org.alfresco.util.ParameterCheck; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| @@ -73,19 +72,18 @@ public class TagsEntityResource implements EntityResourceAction.Read<Tag>, | ||||
|     @WebApiDescription(title="Updates a tag by unique Id") | ||||
| 	public Tag update(String id, Tag entity, Parameters parameters) | ||||
| 	{ | ||||
| 		return tags.changeTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, entity); | ||||
| 		return tags.changeTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, entity, parameters); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public Tag readById(String id, Parameters parameters) throws EntityNotFoundException | ||||
| 	{ | ||||
| 		return tags.getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id); | ||||
| 		return tags.getTag(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, id, parameters); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * POST /tags | ||||
| 	 */ | ||||
| 	@Experimental | ||||
| 	@WebApiDescription( | ||||
| 		title = "Create an orphan tag", | ||||
| 		description = "Creates a tag, which is not associated with any node", | ||||
|   | ||||
| @@ -30,11 +30,9 @@ import org.alfresco.rest.api.categories.CategoriesEntityResourceTest; | ||||
| import org.alfresco.rest.api.categories.NodesCategoryLinksRelationTest; | ||||
| import org.alfresco.rest.api.categories.SubcategoriesRelationTest; | ||||
| import org.alfresco.rest.api.impl.CategoriesImplTest; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Suite; | ||||
|  | ||||
| @Experimental | ||||
| @RunWith(Suite.class) | ||||
| @Suite.SuiteClasses({ | ||||
|     CategoriesImplTest.class, | ||||
|   | ||||
| @@ -27,11 +27,9 @@ package org.alfresco.rest.api; | ||||
|  | ||||
| import org.alfresco.rest.api.impl.TagsImplTest; | ||||
| import org.alfresco.rest.api.tags.TagsEntityResourceTest; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Suite; | ||||
|  | ||||
| @Experimental | ||||
| @RunWith(Suite.class) | ||||
| @Suite.SuiteClasses({ | ||||
|     TagsImplTest.class, | ||||
|   | ||||
| @@ -60,6 +60,7 @@ import java.util.stream.IntStream; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.transfer.PathHelper; | ||||
| import org.alfresco.rest.api.Nodes; | ||||
| import org.alfresco.rest.api.model.Category; | ||||
| import org.alfresco.rest.api.model.Node; | ||||
| @@ -71,6 +72,7 @@ import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| import org.alfresco.service.cmr.repository.Path; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| import org.alfresco.service.cmr.search.CategoryService; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| @@ -100,6 +102,9 @@ public class CategoriesImplTest | ||||
|     private static final Category CATEGORY = createDefaultCategory(); | ||||
|     private static final String CONTENT_NODE_ID = "content-node-id"; | ||||
|     private static final NodeRef CONTENT_NODE_REF = createNodeRefWithId(CONTENT_NODE_ID); | ||||
|     private static final String MOCK_ROOT_LEVEL = "/{mockRootLevel}"; | ||||
|     private static final String MOCK_CHILD_LEVEL = "/{mockChild}"; | ||||
|     private static final String MOCK_CATEGORY_PATH = "//" + MOCK_ROOT_LEVEL + "//" + MOCK_CHILD_LEVEL; | ||||
|  | ||||
|     @Mock | ||||
|     private Nodes nodesMock; | ||||
| @@ -252,6 +257,27 @@ public class CategoriesImplTest | ||||
|             .isEqualTo(1); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetCategoryById_includePath() | ||||
|     { | ||||
|         final QName categoryQName = createCmQNameOf(CATEGORY_NAME); | ||||
|         final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); | ||||
|         given(nodesMock.getNode(any())).willReturn(createNode()); | ||||
|         given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|  | ||||
|         // when | ||||
|         final Category actualCategory = objectUnderTest.getCategoryById(CATEGORY_ID, parametersMock); | ||||
|  | ||||
|         assertThat(actualCategory) | ||||
|                 .isNotNull() | ||||
|                 .extracting(Category::getPath) | ||||
|                 .isNotNull() | ||||
|                 .isEqualTo(MOCK_CATEGORY_PATH); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetCategoryById_notACategory() | ||||
|     { | ||||
| @@ -479,6 +505,36 @@ public class CategoriesImplTest | ||||
|             .isEqualTo(0); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateCategory_includePath() | ||||
|     { | ||||
|         final QName categoryQName = createCmQNameOf(CATEGORY_NAME); | ||||
|         final NodeRef categoryNodeRef = createNodeRefWithId(CATEGORY_ID); | ||||
|         final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); | ||||
|         given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); | ||||
|         given(categoryServiceMock.createCategory(parentCategoryNodeRef, CATEGORY_NAME)).willReturn(categoryNodeRef); | ||||
|         given(nodesMock.getNode(any())).willReturn(createNode()); | ||||
|         given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|         final List<Category> categoryModels = new ArrayList<>(prepareCategories()); | ||||
|  | ||||
|         // when | ||||
|         final List<Category> actualCreatedCategories = objectUnderTest.createSubcategories(PARENT_ID, categoryModels, parametersMock); | ||||
|  | ||||
|         then(categoryServiceMock).should().createCategory(any(), any()); | ||||
|         then(categoryServiceMock).shouldHaveNoMoreInteractions(); | ||||
|  | ||||
|         assertThat(actualCreatedCategories) | ||||
|                 .isNotNull() | ||||
|                 .hasSize(1) | ||||
|                 .element(0) | ||||
|                 .extracting(Category::getPath) | ||||
|                 .isNotNull() | ||||
|                 .isEqualTo(MOCK_CATEGORY_PATH); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateCategories_noPermissions() | ||||
|     { | ||||
| @@ -628,6 +684,30 @@ public class CategoriesImplTest | ||||
|             .isEqualTo(List.of(0, 2, 0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetCategoryChildren_includePath() | ||||
|     { | ||||
|         final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         given(nodesMock.validateNode(PARENT_ID)).willReturn(parentCategoryNodeRef); | ||||
|         given(nodesMock.isSubClass(parentCategoryNodeRef, ContentModel.TYPE_CATEGORY, false)).willReturn(true); | ||||
|         final int childrenCount = 3; | ||||
|         final List<ChildAssociationRef> childAssociationRefMocks = prepareChildAssocMocks(childrenCount, parentCategoryNodeRef); | ||||
|         given(nodeServiceMock.getChildAssocs(parentCategoryNodeRef)).willReturn(childAssociationRefMocks); | ||||
|         childAssociationRefMocks.forEach(this::prepareCategoryNodeMocks); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|  | ||||
|         // when | ||||
|         final List<Category> actualCategoryChildren = objectUnderTest.getCategoryChildren(PARENT_ID, parametersMock); | ||||
|  | ||||
|         assertThat(actualCategoryChildren) | ||||
|                 .isNotNull() | ||||
|                 .hasSize(3) | ||||
|                 .extracting(Category::getPath) | ||||
|                 .isNotNull() | ||||
|                 .isEqualTo(List.of(MOCK_CATEGORY_PATH, MOCK_CATEGORY_PATH, MOCK_CATEGORY_PATH)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetCategoryChildren_noChildren() | ||||
|     { | ||||
| @@ -751,6 +831,32 @@ public class CategoriesImplTest | ||||
|             .isEqualTo(1); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateCategoryById_includePath() | ||||
|     { | ||||
|         final String categoryNewName = "categoryNewName"; | ||||
|         final Category fixedCategory = createCategoryOnlyWithName(categoryNewName); | ||||
|         // simulate path provided by client to check if it will be ignored | ||||
|         fixedCategory.setPath("/test/TestCat"); | ||||
|         final QName categoryQName = createCmQNameOf(CATEGORY_NAME); | ||||
|         final NodeRef parentCategoryNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         final ChildAssociationRef parentAssociation = createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, categoryQName); | ||||
|         given(nodesMock.getNode(any())).willReturn(createNode(categoryNewName)); | ||||
|         given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); | ||||
|         given(nodeServiceMock.moveNode(any(), any(), any(), any())).willReturn(createAssociationOf(parentCategoryNodeRef, CATEGORY_NODE_REF, createCmQNameOf(categoryNewName))); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|  | ||||
|         // when | ||||
|         final Category actualCategory = objectUnderTest.updateCategoryById(CATEGORY_ID, fixedCategory, parametersMock); | ||||
|  | ||||
|         assertThat(actualCategory) | ||||
|                 .isNotNull() | ||||
|                 .extracting(Category::getPath) | ||||
|                 .isNotNull() | ||||
|                 .isEqualTo(MOCK_CATEGORY_PATH); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateCategoryById_noPermission() | ||||
|     { | ||||
| @@ -918,6 +1024,7 @@ public class CategoriesImplTest | ||||
|         then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef); | ||||
|         then(nodeServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         final List<Category> expectedLinkedCategories = List.of(CATEGORY); | ||||
|         expectedLinkedCategories.get(0).setPath(null); | ||||
|         assertThat(actualLinkedCategories) | ||||
|             .isNotNull().usingRecursiveComparison() | ||||
|             .isEqualTo(expectedLinkedCategories); | ||||
| @@ -984,6 +1091,36 @@ public class CategoriesImplTest | ||||
|             .isEqualTo(expectedLinkedCategories); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLinkNodeToCategories_includePath() | ||||
|     { | ||||
|         final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); | ||||
|         given(nodesMock.getNode(any())).willReturn(createNode()); | ||||
|         given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); | ||||
|         given(nodeServiceMock.hasAspect(any(), any())).willReturn(true); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|  | ||||
|         // when | ||||
|         final List<Category> actualLinkedCategories = objectUnderTest.linkNodeToCategories(CONTENT_NODE_ID, List.of(CATEGORY), parametersMock); | ||||
|  | ||||
|         then(nodesMock).should(times(2)).getNode(CATEGORY_ID); | ||||
|         then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); | ||||
|         then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF); | ||||
|         then(nodeServiceMock).should().getParentAssocs(CATEGORY_NODE_REF); | ||||
|         then(nodeServiceMock).should().hasAspect(CONTENT_NODE_REF, ContentModel.ASPECT_GEN_CLASSIFIABLE); | ||||
|         then(nodeServiceMock).should().getProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES); | ||||
|         final Serializable expectedCategories = (Serializable) List.of(CATEGORY_NODE_REF); | ||||
|         then(nodeServiceMock).should().setProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES, expectedCategories); | ||||
|         then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef); | ||||
|         final List<Category> expectedLinkedCategories = List.of(CATEGORY); | ||||
|         expectedLinkedCategories.get(0).setPath(MOCK_CATEGORY_PATH); | ||||
|         assertThat(actualLinkedCategories) | ||||
|                 .isNotNull().usingRecursiveComparison() | ||||
|                 .isEqualTo(expectedLinkedCategories); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLinkNodeToCategories_withPreviouslyLinkedCategories() | ||||
|     { | ||||
| @@ -1168,11 +1305,46 @@ public class CategoriesImplTest | ||||
|         then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef); | ||||
|         then(nodeServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         final List<Category> expectedCategories = List.of(CATEGORY); | ||||
|         expectedCategories.get(0).setPath(null); | ||||
|         assertThat(actualCategories) | ||||
|             .isNotNull().usingRecursiveComparison() | ||||
|             .isEqualTo(expectedCategories); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testListCategoriesForNode_includePath() | ||||
|     { | ||||
|         final NodeRef categoryParentNodeRef = createNodeRefWithId(PARENT_ID); | ||||
|         final ChildAssociationRef parentAssociation = createAssociationOf(categoryParentNodeRef, CATEGORY_NODE_REF); | ||||
|         given(nodeServiceMock.getProperty(any(), eq(ContentModel.PROP_CATEGORIES))).willReturn((Serializable) List.of(CATEGORY_NODE_REF)); | ||||
|         given(nodesMock.getNode(any())).willReturn(createNode()); | ||||
|         given(nodeServiceMock.getPrimaryParent(any())).willReturn(parentAssociation); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(Nodes.PARAM_INCLUDE_PATH)); | ||||
|         given(nodeServiceMock.getPath(any())).willReturn(mockCategoryPath()); | ||||
|  | ||||
|         // when | ||||
|         final List<Category> actualCategories = objectUnderTest.listCategoriesForNode(CONTENT_NODE_ID, parametersMock); | ||||
|  | ||||
|         then(nodesMock).should().validateOrLookupNode(CONTENT_NODE_ID); | ||||
|         then(permissionServiceMock).should().hasReadPermission(CONTENT_NODE_REF); | ||||
|         then(permissionServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         then(typeConstraint).should().matches(CONTENT_NODE_REF); | ||||
|         then(typeConstraint).shouldHaveNoMoreInteractions(); | ||||
|         then(nodesMock).should(times(2)).getNode(CATEGORY_ID); | ||||
|         then(nodesMock).shouldHaveNoMoreInteractions(); | ||||
|         then(nodeServiceMock).should().getProperty(CONTENT_NODE_REF, ContentModel.PROP_CATEGORIES); | ||||
|         then(nodeServiceMock).should().getChildAssocs(CATEGORY_NODE_REF, RegexQNamePattern.MATCH_ALL, RegexQNamePattern.MATCH_ALL, false); | ||||
|         then(nodeServiceMock).should().getPrimaryParent(CATEGORY_NODE_REF); | ||||
|         then(nodeServiceMock).should().getParentAssocs(categoryParentNodeRef); | ||||
|         then(nodeServiceMock).should().getPath(any()); | ||||
|         then(nodeServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         final List<Category> expectedCategories = List.of(CATEGORY); | ||||
|         expectedCategories.get(0).setPath(MOCK_CATEGORY_PATH); | ||||
|         assertThat(actualCategories) | ||||
|                 .isNotNull().usingRecursiveComparison() | ||||
|                 .isEqualTo(expectedCategories); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testListCategoriesForNode_withInvalidNodeId() | ||||
|     { | ||||
| @@ -1329,4 +1501,13 @@ public class CategoriesImplTest | ||||
|     { | ||||
|         return new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, parentNode, childNodeName, childNode); | ||||
|     } | ||||
|  | ||||
|     private Path mockCategoryPath() | ||||
|     { | ||||
|         final Path mockPath = new Path(); | ||||
|         mockPath.append(PathHelper.stringToPath(MOCK_ROOT_LEVEL)); | ||||
|         mockPath.append(PathHelper.stringToPath(MOCK_CHILD_LEVEL)); | ||||
|         mockPath.append(PathHelper.stringToPath("/")); | ||||
|         return mockPath; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -40,10 +40,14 @@ import static org.mockito.ArgumentMatchers.isNull; | ||||
| import static org.mockito.BDDMockito.given; | ||||
| import static org.mockito.BDDMockito.then; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.rest.api.Nodes; | ||||
| @@ -55,6 +59,7 @@ import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationE | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Paging; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rest.framework.resource.parameters.SortColumn; | ||||
| import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException; | ||||
| import org.alfresco.rest.framework.tools.RecognizedParamsExtractor; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| @@ -83,6 +88,8 @@ public class TagsImplTest | ||||
|     private static final NodeRef TAG_PARENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, PARENT_NODE_ID); | ||||
|     private static final String CONTENT_NODE_ID = "content-node-id"; | ||||
|     private static final NodeRef CONTENT_NODE_REF = new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, CONTENT_NODE_ID); | ||||
|     private static final String PARAM_INCLUDE_COUNT = "count"; | ||||
|  | ||||
|  | ||||
|     private final RecognizedParamsExtractor queryExtractor = new RecognizedParamsExtractor() {}; | ||||
|  | ||||
| @@ -115,19 +122,23 @@ public class TagsImplTest | ||||
|         given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willReturn(TAG_NODE_REF); | ||||
|         given(taggingServiceMock.getTagName(TAG_NODE_REF)).willReturn(TAG_NAME); | ||||
|         given(nodeServiceMock.getPrimaryParent(TAG_NODE_REF)).willReturn(primaryParentMock); | ||||
|         given(primaryParentMock.getParentRef()).willReturn(TAG_PARENT_NODE_REF); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTags() | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME))); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|         given(parametersMock.getInclude()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //given(taggingServiceMock.getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull())).willReturn(List.of(new Pair<>(TAG_NODE_REF, null))); | ||||
|         given(taggingServiceMock.getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull())).willReturn(Map.of(TAG_NODE_REF, 0L)); | ||||
|         given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("tag-dummy-name"); | ||||
|  | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), isNull()); | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), isNull()); | ||||
|         then(taggingServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
| @@ -137,16 +148,17 @@ public class TagsImplTest | ||||
|     public void testGetTags_verifyIfCountIsZero() | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(pagingResultsMock.getPage()).willReturn(List.of(new Pair<>(TAG_NODE_REF, TAG_NAME))); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT)); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(), any(), any(), any())).willReturn(Map.of(TAG_NODE_REF, 0L)); | ||||
|  | ||||
|         given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("tag-dummy-name"); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of("count")); | ||||
|  | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE); | ||||
|         final List<Tag> expectedTags = createTagsWithNodeRefs(List.of(TAG_NAME)).stream() | ||||
|             .peek(tag -> tag.setCount(0)) | ||||
|             .peek(tag -> tag.setCount(0L)) | ||||
|             .collect(toList()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
| @@ -157,21 +169,142 @@ public class TagsImplTest | ||||
|     { | ||||
|         NodeRef tagNodeA = new NodeRef("tag://A/"); | ||||
|         NodeRef tagNodeB = new NodeRef("tag://B/"); | ||||
|         List<Pair<NodeRef, String>> tagPairs = List.of(new Pair<>(tagNodeA, "taga"), new Pair<>(tagNodeB, "tagb")); | ||||
|  | ||||
|         given(parametersMock.getSorting()).willReturn(Collections.emptyList()); | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(pagingResultsMock.getPage()).willReturn(tagPairs); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of("count")); | ||||
|         // Only taga is included in the returned list since tagb is not in use. | ||||
|         given(taggingServiceMock.findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE)).willReturn(List.of(new Pair<>("taga", 5))); | ||||
|  | ||||
|         final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>(); | ||||
|         results.put(tagNodeA, 5L); | ||||
|         results.put(tagNodeB, 0L); | ||||
|  | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), isNull(), any(), any())).willReturn(results); | ||||
|         given(nodeServiceMock.getProperty(any(NodeRef.class), eq(ContentModel.PROP_NAME))).willReturn("taga", "tagb"); | ||||
|  | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().findTaggedNodesAndCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE); | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(), | ||||
|                                                Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0).create()); | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("tagA").nodeRef(tagNodeA).count(5L).create(), | ||||
|                                                Tag.builder().tag("tagB").nodeRef(tagNodeB).count(0L).create()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTags_orderByCountAscendingOrder() | ||||
|     { | ||||
|         NodeRef tagNodeA = new NodeRef("tag://A/"); | ||||
|         NodeRef tagNodeB = new NodeRef("tag://B/"); | ||||
|         NodeRef tagNodeC = new NodeRef("tag://C/"); | ||||
|  | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of("count")); | ||||
|         given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("count", true))); | ||||
|  | ||||
|         final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>(); | ||||
|         results.put(tagNodeB, 0L); | ||||
|         results.put(tagNodeC, 2L); | ||||
|         results.put(tagNodeA, 5L); | ||||
|  | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), eq(new Pair<>("count", true)), any(), any())).willReturn(results); | ||||
|         given(nodeServiceMock.getProperty(tagNodeA, ContentModel.PROP_NAME)).willReturn("taga"); | ||||
|         given(nodeServiceMock.getProperty(tagNodeB, ContentModel.PROP_NAME)).willReturn("tagb"); | ||||
|         given(nodeServiceMock.getProperty(tagNodeC, ContentModel.PROP_NAME)).willReturn("tagc"); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0L).create(), | ||||
|                                                Tag.builder().tag("tagc").nodeRef(tagNodeC).count(2L).create(), | ||||
|                                                Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTags_orderByCountDescendingOrder() | ||||
|     { | ||||
|         NodeRef tagNodeA = new NodeRef("tag://A/"); | ||||
|         NodeRef tagNodeB = new NodeRef("tag://B/"); | ||||
|         NodeRef tagNodeC = new NodeRef("tag://C/"); | ||||
|  | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of("count")); | ||||
|         given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("count", false))); | ||||
|  | ||||
|         final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>(); | ||||
|         results.put(tagNodeA, 5L); | ||||
|         results.put(tagNodeC, 2L); | ||||
|         results.put(tagNodeB, 0L); | ||||
|  | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), eq(List.of(PARAM_INCLUDE_COUNT)), eq(new Pair<>("count", false)), any(), any())).willReturn(results); | ||||
|         given(nodeServiceMock.getProperty(tagNodeA, ContentModel.PROP_NAME)).willReturn("taga"); | ||||
|         given(nodeServiceMock.getProperty(tagNodeB, ContentModel.PROP_NAME)).willReturn("tagb"); | ||||
|         given(nodeServiceMock.getProperty(tagNodeC, ContentModel.PROP_NAME)).willReturn("tagc"); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create(), | ||||
|                                                Tag.builder().tag("tagc").nodeRef(tagNodeC).count(2L).create(), | ||||
|                                                Tag.builder().tag("tagb").nodeRef(tagNodeB).count(0L).create()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTags_orderByTagAscendingOrder() | ||||
|     { | ||||
|         NodeRef tagApple = new NodeRef("tag://apple/"); | ||||
|         NodeRef tagBanana = new NodeRef("tag://banana/"); | ||||
|         NodeRef tagCoconut = new NodeRef("tag://coconut/"); | ||||
|  | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getInclude()).willReturn(Collections.emptyList()); | ||||
|         given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("tag", true))); | ||||
|  | ||||
|         final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>(); | ||||
|         results.put(tagApple, 0L); | ||||
|         results.put(tagBanana, 0L); | ||||
|         results.put(tagCoconut, 0L); | ||||
|  | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(), eq(new Pair<>("tag", true)), any(), any())).willReturn(results); | ||||
|         given(nodeServiceMock.getProperty(tagApple, ContentModel.PROP_NAME)).willReturn("apple"); | ||||
|         given(nodeServiceMock.getProperty(tagBanana, ContentModel.PROP_NAME)).willReturn("banana"); | ||||
|         given(nodeServiceMock.getProperty(tagCoconut, ContentModel.PROP_NAME)).willReturn("coconut"); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("apple").nodeRef(tagApple).create(), | ||||
|                                                Tag.builder().tag("banana").nodeRef(tagBanana).create(), | ||||
|                                                Tag.builder().tag("coconut").nodeRef(tagCoconut).create()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTags_orderByTagDescendingOrder() | ||||
|     { | ||||
|         NodeRef tagApple = new NodeRef("tag://apple/"); | ||||
|         NodeRef tagBanana = new NodeRef("tag://banana/"); | ||||
|         NodeRef tagCoconut = new NodeRef("tag://coconut/"); | ||||
|  | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getInclude()).willReturn(Collections.emptyList()); | ||||
|         given(parametersMock.getSorting()).willReturn(List.of(new SortColumn("tag", false))); | ||||
|  | ||||
|         final LinkedHashMap<NodeRef, Long> results = new LinkedHashMap<>(); | ||||
|         results.put(tagCoconut, 0L); | ||||
|         results.put(tagBanana, 0L); | ||||
|         results.put(tagApple, 0L); | ||||
|  | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(), eq(new Pair<>("tag", false)), any(), any())).willReturn(results); | ||||
|         given(nodeServiceMock.getProperty(tagApple, ContentModel.PROP_NAME)).willReturn("apple"); | ||||
|         given(nodeServiceMock.getProperty(tagBanana, ContentModel.PROP_NAME)).willReturn("banana"); | ||||
|         given(nodeServiceMock.getProperty(tagCoconut, ContentModel.PROP_NAME)).willReturn("coconut"); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         final List<Tag> expectedTags = List.of(Tag.builder().tag("coconut").nodeRef(tagCoconut).create(), | ||||
|                                                Tag.builder().tag("banana").nodeRef(tagBanana).create(), | ||||
|                                                Tag.builder().tag("apple").nodeRef(tagApple).create()); | ||||
|         assertEquals(expectedTags, actualTags.getCollection()); | ||||
|     } | ||||
|  | ||||
| @@ -180,13 +313,13 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName)")); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|         given(parametersMock.getInclude()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname")), isNull()); | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), eq(new ArrayList<>()), any(), eq(Set.of("expectedname")), isNull()); | ||||
|         then(taggingServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         assertThat(actualTags).isNotNull(); | ||||
|     } | ||||
| @@ -196,13 +329,13 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag IN (expectedName1, expectedName2))")); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|         given(parametersMock.getInclude()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), eq(Set.of("expectedname1", "expectedname2")), isNull()); | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE),any(), any(), eq(Set.of("expectedname1", "expectedname2")), isNull()); | ||||
|         then(taggingServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         assertThat(actualTags).isNotNull(); | ||||
|     } | ||||
| @@ -212,13 +345,13 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag MATCHES ('expectedName*'))")); | ||||
|         given(taggingServiceMock.getTags(any(StoreRef.class), any(PagingRequest.class), any(), any())).willReturn(pagingResultsMock); | ||||
|         given(pagingResultsMock.getTotalResultCount()).willReturn(new Pair<>(Integer.MAX_VALUE, 0)); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|         given(parametersMock.getInclude()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final CollectionWithPagingInfo<Tag> actualTags = objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock); | ||||
|  | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(PagingRequest.class), isNull(), eq(Set.of("expectedname*"))); | ||||
|         then(taggingServiceMock).should().getTags(eq(STORE_REF_WORKSPACE_SPACESSTORE), any(), any(), isNull(), eq(Set.of("expectedname*"))); | ||||
|         then(taggingServiceMock).shouldHaveNoMoreInteractions(); | ||||
|         assertThat(actualTags).isNotNull(); | ||||
|     } | ||||
| @@ -228,6 +361,7 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag=expectedName AND tag IN (expectedName1, expectedName2))")); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock)); | ||||
| @@ -241,6 +375,7 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(tag BETWEEN ('expectedName', 'expectedName2'))")); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock)); | ||||
| @@ -254,6 +389,7 @@ public class TagsImplTest | ||||
|     { | ||||
|         given(parametersMock.getPaging()).willReturn(pagingMock); | ||||
|         given(parametersMock.getQuery()).willReturn(queryExtractor.getWhereClause("(NOT tag=expectedName)")); | ||||
|         given(parametersMock.getSorting()).willReturn(new ArrayList<>()); | ||||
|  | ||||
|         //when | ||||
|         final Throwable actualException = catchThrowable(() -> objectUnderTest.getTags(STORE_REF_WORKSPACE_SPACESSTORE, parametersMock)); | ||||
| @@ -266,7 +402,6 @@ public class TagsImplTest | ||||
|     public void testDeleteTagById() | ||||
|     { | ||||
|         //when | ||||
|         given(primaryParentMock.getParentRef()).willReturn(TAG_PARENT_NODE_REF); | ||||
|         objectUnderTest.deleteTagById(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID); | ||||
|  | ||||
|         then(authorityServiceMock).should().hasAdminAuthority(); | ||||
| @@ -425,7 +560,7 @@ public class TagsImplTest | ||||
|         final List<Tag> actualCreatedTags = objectUnderTest.createTags(tagsToCreate, parametersMock); | ||||
|  | ||||
|         final List<Tag> expectedTags = createTagsWithNodeRefs(tagNames).stream() | ||||
|             .peek(tag -> tag.setCount(0)) | ||||
|             .peek(tag -> tag.setCount(0L)) | ||||
|             .collect(toList()); | ||||
|         assertThat(actualCreatedTags) | ||||
|             .isNotNull() | ||||
| @@ -435,8 +570,8 @@ public class TagsImplTest | ||||
|     @Test(expected = EntityNotFoundException.class) | ||||
|     public void testGetTagByIdNotFoundValidation() | ||||
|     { | ||||
|         given(primaryParentMock.getParentRef()).willReturn(TAG_NODE_REF); | ||||
|         objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE,TAG_ID); | ||||
|         given(nodesMock.validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID)).willThrow(EntityNotFoundException.class); | ||||
|         objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, null); | ||||
|         then(nodeServiceMock).shouldHaveNoInteractions(); | ||||
|         then(nodesMock).should().validateNode(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID); | ||||
|         then(nodesMock).shouldHaveNoMoreInteractions(); | ||||
| @@ -459,8 +594,8 @@ public class TagsImplTest | ||||
|  | ||||
|         List<Tag> actual = objectUnderTest.addTags(CONTENT_NODE_ID, tags, parametersMock); | ||||
|  | ||||
|         final List<Tag> expected = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5).create(), | ||||
|                 Tag.builder().tag("tagb").nodeRef(tagNodeB).count(1).create()); | ||||
|         final List<Tag> expected = List.of(Tag.builder().tag("taga").nodeRef(tagNodeA).count(5L).create(), | ||||
|                 Tag.builder().tag("tagb").nodeRef(tagNodeB).count(1L).create()); | ||||
|         assertEquals("Unexpected tags returned.", expected, actual); | ||||
|     } | ||||
|  | ||||
| @@ -508,6 +643,53 @@ public class TagsImplTest | ||||
|         objectUnderTest.getTags(CONTENT_NODE_ID, parametersMock); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testChangeTag() | ||||
|     { | ||||
|         Tag suppliedTag = Tag.builder().tag("new-name").create(); | ||||
|         given(taggingServiceMock.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME, "new-name")).willReturn(TAG_NODE_REF); | ||||
|  | ||||
|         Tag tag = objectUnderTest.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, suppliedTag, parametersMock); | ||||
|  | ||||
|         Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag("new-name").create(); | ||||
|         assertEquals("Unexpected return value", expected, tag); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testChangeTagAndGetCount() | ||||
|     { | ||||
|         Tag suppliedTag = Tag.builder().tag("new-name").create(); | ||||
|         given(taggingServiceMock.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME, "new-name")).willReturn(TAG_NODE_REF); | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT)); | ||||
|         given(taggingServiceMock.findCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME)).willReturn(3L); | ||||
|  | ||||
|         Tag tag = objectUnderTest.changeTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, suppliedTag, parametersMock); | ||||
|  | ||||
|         Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag("new-name").count(3L).create(); | ||||
|         assertEquals("Unexpected return value", expected, tag); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTag() | ||||
|     { | ||||
|         Tag tag = objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, parametersMock); | ||||
|  | ||||
|         Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag(TAG_NAME).create(); | ||||
|         assertEquals("Unexpected tag returned", expected, tag); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetTagWithCount() | ||||
|     { | ||||
|         given(parametersMock.getInclude()).willReturn(List.of(PARAM_INCLUDE_COUNT)); | ||||
|         given(taggingServiceMock.findCountByTagName(STORE_REF_WORKSPACE_SPACESSTORE, TAG_NAME)).willReturn(0L); | ||||
|  | ||||
|         Tag tag = objectUnderTest.getTag(STORE_REF_WORKSPACE_SPACESSTORE, TAG_ID, parametersMock); | ||||
|  | ||||
|         Tag expected = Tag.builder().nodeRef(TAG_NODE_REF).tag(TAG_NAME).count(0L).create(); | ||||
|         assertEquals("Unexpected tag returned", expected, tag); | ||||
|     } | ||||
|  | ||||
|     private static List<Pair<String, NodeRef>> createTagAndNodeRefPairs(final List<String> tagNames) | ||||
|     { | ||||
|         return tagNames.stream() | ||||
|   | ||||
| @@ -745,33 +745,56 @@ public class SearchMapperTests | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void fromLimits() throws Exception | ||||
|     public void fromLimits_setNull() throws Exception | ||||
|     { | ||||
|         SearchParameters searchParameters = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParameters); | ||||
|  | ||||
|         //Doesn't error | ||||
|         searchMapper.fromLimits(searchParameters, null); | ||||
|         assertEquals(500, searchParameters.getLimit()); | ||||
|         assertEquals(LimitBy.UNLIMITED, searchParameters.getLimitBy()); | ||||
|         assertEquals("LimitBy default value should be unlimited", LimitBy.UNLIMITED, searchParameters.getLimitBy()); | ||||
|         assertEquals("Limit default value should be 500", 500, searchParameters.getLimit()); | ||||
|     } | ||||
|  | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(null, null)); | ||||
|         assertEquals(LimitBy.UNLIMITED, searchParameters.getLimitBy()); | ||||
|         assertEquals(500, searchParameters.getLimit()); | ||||
|  | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(null, 34)); | ||||
|         assertEquals(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, searchParameters.getLimitBy()); | ||||
|         assertEquals(34, searchParameters.getMaxPermissionChecks()); | ||||
|         assertEquals(-1, searchParameters.getLimit()); | ||||
|         assertEquals(-1, searchParameters.getMaxPermissionCheckTimeMillis()); | ||||
|  | ||||
|         searchParameters = new SearchParameters(); | ||||
|     @Test | ||||
|     public void fromLimits_setAllLimitsAsNull() throws Exception | ||||
|     { | ||||
|         SearchParameters searchParameters = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParameters); | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(1000, null)); | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(null, null, null)); | ||||
|         assertEquals("LimitBy default value should be unlimited", LimitBy.UNLIMITED, searchParameters.getLimitBy()); | ||||
|         assertEquals("Limit default value should be 500", 500, searchParameters.getLimit()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void fromLimits_setPermissionEvaluationCount() throws Exception | ||||
|     { | ||||
|         SearchParameters searchParameters = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParameters); | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(null, 34, null)); | ||||
|         assertEquals(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, searchParameters.getLimitBy()); | ||||
|         assertEquals(1000, searchParameters.getMaxPermissionCheckTimeMillis()); | ||||
|         assertEquals(-1, searchParameters.getLimit()); | ||||
|         assertEquals(-1, searchParameters.getMaxPermissionChecks()); | ||||
|         assertEquals("MaxPermissionChecks should be set", 34, searchParameters.getMaxPermissionChecks()); | ||||
|         assertEquals("Limit should be -1", -1, searchParameters.getLimit()); | ||||
|         assertEquals("MaxPermissionCheckTimeMillis should be -1", -1, searchParameters.getMaxPermissionCheckTimeMillis()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void fromLimits_setPermissionEvaluationTime() throws Exception | ||||
|     { | ||||
|         SearchParameters searchParameters = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParameters); | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(1000, null, null)); | ||||
|         assertEquals(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, searchParameters.getLimitBy()); | ||||
|         assertEquals("MaxPermissionCheckTimeMillis should be set", 1000, searchParameters.getMaxPermissionCheckTimeMillis()); | ||||
|         assertEquals("Limit should be -1", -1, searchParameters.getLimit()); | ||||
|         assertEquals("MaxPermissionChecks should be -1", -1, searchParameters.getMaxPermissionChecks()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void fromLimits_setTrackTotalHitsLimit() throws Exception | ||||
|     { | ||||
|         SearchParameters searchParameters = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParameters); | ||||
|         searchMapper.fromLimits(searchParameters, new Limits(null, null, 10)); | ||||
|         assertEquals("TrackTotalHits should be set", 10, searchParameters.getTrackTotalHits()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>20.136</version> | ||||
|         <version>23.1.0.163-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
| @@ -126,7 +126,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>com.ibm.icu</groupId> | ||||
|             <artifactId>icu4j</artifactId> | ||||
|             <version>72.1</version> | ||||
|             <version>73.2</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.googlecode.json-simple</groupId> | ||||
| @@ -375,20 +375,9 @@ | ||||
|         <dependency> | ||||
|             <groupId>com.fasterxml.woodstox</groupId> | ||||
|             <artifactId>woodstox-core</artifactId> | ||||
|             <version>6.4.0</version> | ||||
|             <version>6.5.1</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- GData --> | ||||
|         <dependency> | ||||
|             <groupId>com.google.gdata</groupId> | ||||
|             <artifactId>gdata-core-1.0</artifactId> | ||||
|             <version>1.47.1</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.google.gdata</groupId> | ||||
|             <artifactId>gdata-media-1.0</artifactId> | ||||
|             <version>1.47.1</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.security</groupId> | ||||
|             <artifactId>spring-security-crypto</artifactId> | ||||
| @@ -747,7 +736,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>org.aspectj</groupId> | ||||
|             <artifactId>aspectjrt</artifactId> | ||||
|             <version>1.9.9.1</version> | ||||
|             <version>1.9.19</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>commons-net</groupId> | ||||
| @@ -809,6 +798,10 @@ | ||||
|             <artifactId>reflections</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>commons-lang</groupId> | ||||
|             <artifactId>commons-lang</artifactId> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -44,6 +44,7 @@ import java.util.zip.ZipException; | ||||
| import org.alfresco.error.AlfrescoRuntimeException; | ||||
| import org.alfresco.model.ApplicationModel; | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.action.ActionServiceImpl; | ||||
| import org.alfresco.repo.action.ParameterDefinitionImpl; | ||||
| import org.alfresco.repo.content.MimetypeMap; | ||||
| import org.alfresco.repo.importer.ACPImportPackageHandler; | ||||
| @@ -65,6 +66,8 @@ import org.alfresco.util.TempFileProvider; | ||||
| import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; | ||||
| import org.apache.commons.compress.archivers.zip.ZipFile; | ||||
| import org.apache.commons.compress.utils.InputStreamStatistics; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
|  | ||||
| /** | ||||
|  * Importer action executor | ||||
| @@ -73,6 +76,7 @@ import org.apache.commons.compress.utils.InputStreamStatistics; | ||||
|  */ | ||||
| public class ImporterActionExecuter extends ActionExecuterAbstractBase | ||||
| { | ||||
|     private static Log logger = LogFactory.getLog(ImporterActionExecuter.class); | ||||
|     public static final String NAME = "import"; | ||||
|     public static final String PARAM_ENCODING = "encoding"; | ||||
|     public static final String PARAM_DESTINATION_FOLDER = "destination"; | ||||
| @@ -242,17 +246,21 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase | ||||
|                        //       http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807 | ||||
|                        // We also try to use the extra encoding information if present | ||||
|                        String encoding = (String) ruleAction.getParameterValue(PARAM_ENCODING); | ||||
|                        logger.info("Encoding before: "+encoding); | ||||
|                        if (encoding == null) | ||||
|                        { | ||||
|                            encoding = "UTF-8"; | ||||
|                            logger.info("Encoding is null "); | ||||
|                            encoding = "Cp437"; | ||||
|                        } | ||||
|                        else | ||||
|                        { | ||||
|                            if (encoding.equalsIgnoreCase("default")) | ||||
|                            { | ||||
|                                encoding = null; | ||||
|                                logger.info("Encoding is default "); | ||||
|                                encoding = "Cp437"; | ||||
|                            } | ||||
|                        } | ||||
|                        logger.info("Encoding after: "+encoding); | ||||
|                        zipFile = new ZipFile(tempFile, encoding, true); | ||||
|                        // build a temp dir name based on the ID of the noderef we are importing | ||||
|                        // also use the long life temp folder as large ZIP files can take a while | ||||
|   | ||||
| @@ -1,158 +0,0 @@ | ||||
| /* | ||||
|  * #%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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.config.source; | ||||
|  | ||||
| import java.io.InputStream; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.springframework.extensions.config.ConfigException; | ||||
| import org.springframework.extensions.config.source.UrlConfigSource; | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.repo.tenant.TenantService; | ||||
| import org.alfresco.service.cmr.repository.ContentReader; | ||||
| import org.alfresco.service.cmr.repository.ContentService; | ||||
| import org.alfresco.service.cmr.repository.InvalidStoreRefException; | ||||
| 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.SearchService; | ||||
| import org.alfresco.service.namespace.NamespaceService; | ||||
|  | ||||
| /** | ||||
|  * ConfigSource that looks for a prefix to determine where to look for the config.</br> | ||||
|  * Valid prefixes are: | ||||
|  * <ul> | ||||
|  *   <li><b><storeProtocol>://<storeIdentifier></b> the location provided is a path to a repository file</li> | ||||
|  * </ul> | ||||
|  * as well as those defined in the core (UrlConfigSource) | ||||
|  * | ||||
|  * Example store URLs | ||||
|  *   <code>workspace://SpacesStore/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.webclient_extension.childname}/cm:web-client-config-custom.xml</code> | ||||
|  *   <code>workspace://SpacesStore/app:company_home/app:dictionary/app:webclient_extension/cm:web-client-config-custom.xml</code>  | ||||
|  */ | ||||
| public class RepoUrlConfigSource extends UrlConfigSource | ||||
| {    | ||||
|     private TenantService tenantService; | ||||
|     private SearchService searchService; | ||||
|     private ContentService contentService; | ||||
|     private NamespaceService namespaceService; | ||||
|     private NodeService nodeService; | ||||
|      | ||||
|  | ||||
|     public void setTenantService(TenantService tenantService)  | ||||
|     { | ||||
|         this.tenantService = tenantService; | ||||
|     } | ||||
|      | ||||
|     public void setSearchService(SearchService searchService)  | ||||
|     { | ||||
|         this.searchService = searchService; | ||||
|     } | ||||
|      | ||||
|     public void setContentService(ContentService contentService)  | ||||
|     { | ||||
|         this.contentService = contentService; | ||||
|     } | ||||
|      | ||||
|     public void setNamespaceService(NamespaceService namespaceService) | ||||
|     { | ||||
|         this.namespaceService = namespaceService; | ||||
|     } | ||||
|      | ||||
|     public void setNodeService(NodeService nodeService)  | ||||
|     { | ||||
|         this.nodeService = nodeService; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     public RepoUrlConfigSource(String sourceLocation) | ||||
|     { | ||||
|         super(sourceLocation); | ||||
|     } | ||||
|  | ||||
|     public RepoUrlConfigSource(List<String> sourceLocations) | ||||
|     { | ||||
|         super(sourceLocations); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     public InputStream getInputStream(String sourceUrl) | ||||
|     { | ||||
|         // determine the config source        | ||||
|         try | ||||
|         { | ||||
|             return super.getInputStream(sourceUrl); | ||||
|         }  | ||||
|         catch (ConfigException ce) | ||||
|         {      | ||||
|             int idx = sourceUrl.indexOf(StoreRef.URI_FILLER);  | ||||
|             if (idx != -1) | ||||
|             { | ||||
|                 // assume this is a repository location | ||||
|                 int idx2 = sourceUrl.indexOf("/", idx+3); | ||||
|                                  | ||||
|                 String store = sourceUrl.substring(0, idx2); | ||||
|                 String path = sourceUrl.substring(idx2); | ||||
|                  | ||||
|                 StoreRef storeRef = tenantService.getName(new StoreRef(store));     | ||||
|                 NodeRef rootNode = null; | ||||
|                  | ||||
|                 try  | ||||
|                 { | ||||
|                     rootNode = nodeService.getRootNode(storeRef); | ||||
|                 }  | ||||
|                 catch (InvalidStoreRefException e) | ||||
|                 { | ||||
|                     throw ce; | ||||
|                 } | ||||
|                  | ||||
|                 List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, path, null, namespaceService, false); | ||||
|                  | ||||
|                 if (nodeRefs.size() == 0) | ||||
|                 { | ||||
|                     // if none found, then simply skip | ||||
|                     return null; | ||||
|                 } | ||||
|                 else if (nodeRefs.size() > 1) | ||||
|                 { | ||||
|                     // unexpected | ||||
|                     throw new ConfigException("Found duplicate config sources in the repository " + sourceUrl); | ||||
|                 } | ||||
|  | ||||
|                 NodeRef nodeRef = nodeRefs.get(0); | ||||
|                  | ||||
|                 ContentReader cr = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); | ||||
|                                     | ||||
|                 return cr.getContentInputStream();                                                   | ||||
|             }  | ||||
|             else | ||||
|             { | ||||
|                 // not a repository url | ||||
|                 throw ce; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -23,225 +23,225 @@ | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.repo.jscript; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.service.ServiceRegistry; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| import org.alfresco.service.cmr.search.CategoryService; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.mozilla.javascript.Context; | ||||
| import org.mozilla.javascript.Scriptable; | ||||
|  | ||||
| /** | ||||
|  * Support class for finding categories, finding root nodes for categories and creating root categories.  | ||||
|  *  | ||||
|  * @author Andy Hind | ||||
|  */ | ||||
| public final class Classification extends BaseScopableProcessorExtension | ||||
| { | ||||
|     private ServiceRegistry services; | ||||
|  | ||||
|     private StoreRef storeRef; | ||||
|      | ||||
|     /** | ||||
|      * Set the default store reference | ||||
|      *  | ||||
|      * @param   storeRef the default store reference | ||||
|      */ | ||||
|     public void setStoreUrl(String storeRef) | ||||
|     { | ||||
|         this.storeRef = new StoreRef(storeRef); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Set the service registry | ||||
|      *  | ||||
|      * @param services  the service registry | ||||
|      */ | ||||
|     public void setServiceRegistry(ServiceRegistry services) | ||||
|     { | ||||
|         this.services = services; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Find all the category nodes in a given classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getAllCategoryNodes(String aspect) | ||||
|     { | ||||
|         Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories( | ||||
|                             storeRef, createQName(aspect), CategoryService.Depth.ANY)); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all the aspects that define a classification. | ||||
|      *  | ||||
|      * @return String[] | ||||
|      */ | ||||
|     public String[] getAllClassificationAspects() | ||||
|     { | ||||
|         Collection<QName> aspects = services.getCategoryService().getClassificationAspects(); | ||||
|         String[] answer = new String[aspects.size()]; | ||||
|         int i = 0; | ||||
|         for (QName qname : aspects) | ||||
|         { | ||||
|             answer[i++] = qname.toPrefixString(this.services.getNamespaceService()); | ||||
|         } | ||||
|         return answer; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Create a root category in a classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @param name String | ||||
|      */ | ||||
|     public CategoryNode createRootCategory(String aspect, String name) | ||||
|     { | ||||
|         NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name); | ||||
|         CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope()); | ||||
|  | ||||
|         return categoryNode; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get the category node from the category node reference. | ||||
|      *  | ||||
|      * @param categoryRef   category node reference | ||||
|      * @return {@link CategoryNode} category node  | ||||
|      */ | ||||
|     public CategoryNode getCategory(String categoryRef) | ||||
|     { | ||||
|         CategoryNode result = null; | ||||
|         NodeRef categoryNodeRef = new NodeRef(categoryRef); | ||||
|         if (services.getNodeService().exists(categoryNodeRef) == true && | ||||
|             services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true) | ||||
|         { | ||||
|             result = new CategoryNode(categoryNodeRef, this.services, getScope()); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the root categories in a classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getRootCategories(String aspect) | ||||
|     { | ||||
|         Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories( | ||||
|                             storeRef, createQName(aspect))); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get ordered, filtered and paged root categories in a classification. | ||||
|      *  | ||||
|      * @param aspect | ||||
|      * @param filter | ||||
|      * @param maxItems | ||||
|      * @param skipCount (offset) | ||||
|      * @return | ||||
|      */ | ||||
|     public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount) | ||||
|     { | ||||
|         PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems); | ||||
|         List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage(); | ||||
|         Object[] cats = buildCategoryNodes(rootCategories); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the category usage count. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @param maxCount int | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getCategoryUsage(String aspect, int maxCount) | ||||
|     { | ||||
|         List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount); | ||||
|         Object[] tags = new Object[topCats.size()]; | ||||
|         int i = 0; | ||||
|         for (Pair<NodeRef, Integer> topCat : topCats) | ||||
|         { | ||||
|            tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond()); | ||||
|         } | ||||
|          | ||||
|         return Context.getCurrentContext().newArray(getScope(), tags); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build category nodes. | ||||
|      *  | ||||
|      * @param cars  list of associations to category nodes | ||||
|      * @return {@link Object}[] array of category nodes | ||||
|      */ | ||||
|     private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars) | ||||
|     { | ||||
|         Object[] categoryNodes = new Object[cars.size()]; | ||||
|         int i = 0; | ||||
|         for (ChildAssociationRef car : cars) | ||||
|         { | ||||
|             categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope()); | ||||
|         } | ||||
|         return categoryNodes; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create QName from string | ||||
|      *  | ||||
|      * @param s QName string value | ||||
|      * @return {@link QName} qualified name object     | ||||
|      */ | ||||
|     private QName createQName(String s) | ||||
|     { | ||||
|         QName qname; | ||||
|         if (s.indexOf(QName.NAMESPACE_BEGIN) != -1) | ||||
|         { | ||||
|             qname = QName.createQName(s); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             qname = QName.createQName(s, this.services.getNamespaceService()); | ||||
|         } | ||||
|         return qname; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Tag class returned from getCategoryUsage(). | ||||
|      */ | ||||
|     public final class Tag | ||||
|     { | ||||
|         private CategoryNode categoryNode; | ||||
|         private int frequency = 0; | ||||
|         | ||||
|         public Tag(CategoryNode categoryNode, int frequency) | ||||
|         { | ||||
|             this.categoryNode = categoryNode; | ||||
|             this.frequency = frequency; | ||||
|         } | ||||
|         | ||||
|         public CategoryNode getCategory() | ||||
|         { | ||||
|             return categoryNode; | ||||
|         } | ||||
|         | ||||
|         public int getFrequency() | ||||
|         { | ||||
|             return frequency; | ||||
|         } | ||||
|      } | ||||
| } | ||||
| package org.alfresco.repo.jscript; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.service.ServiceRegistry; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| import org.alfresco.service.cmr.search.CategoryService; | ||||
| import org.alfresco.service.namespace.QName; | ||||
| import org.alfresco.util.Pair; | ||||
| import org.mozilla.javascript.Context; | ||||
| import org.mozilla.javascript.Scriptable; | ||||
|  | ||||
| /** | ||||
|  * Support class for finding categories, finding root nodes for categories and creating root categories.  | ||||
|  *  | ||||
|  * @author Andy Hind | ||||
|  */ | ||||
| public final class Classification extends BaseScopableProcessorExtension | ||||
| { | ||||
|     private ServiceRegistry services; | ||||
|  | ||||
|     private StoreRef storeRef; | ||||
|      | ||||
|     /** | ||||
|      * Set the default store reference | ||||
|      *  | ||||
|      * @param   storeRef the default store reference | ||||
|      */ | ||||
|     public void setStoreUrl(String storeRef) | ||||
|     { | ||||
|         this.storeRef = new StoreRef(storeRef); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Set the service registry | ||||
|      *  | ||||
|      * @param services  the service registry | ||||
|      */ | ||||
|     public void setServiceRegistry(ServiceRegistry services) | ||||
|     { | ||||
|         this.services = services; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Find all the category nodes in a given classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getAllCategoryNodes(String aspect) | ||||
|     { | ||||
|         Object[] cats = buildCategoryNodes(services.getCategoryService().getCategories( | ||||
|                             storeRef, createQName(aspect), CategoryService.Depth.ANY)); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all the aspects that define a classification. | ||||
|      *  | ||||
|      * @return String[] | ||||
|      */ | ||||
|     public String[] getAllClassificationAspects() | ||||
|     { | ||||
|         Collection<QName> aspects = services.getCategoryService().getClassificationAspects(); | ||||
|         String[] answer = new String[aspects.size()]; | ||||
|         int i = 0; | ||||
|         for (QName qname : aspects) | ||||
|         { | ||||
|             answer[i++] = qname.toPrefixString(this.services.getNamespaceService()); | ||||
|         } | ||||
|         return answer; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Create a root category in a classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @param name String | ||||
|      */ | ||||
|     public CategoryNode createRootCategory(String aspect, String name) | ||||
|     { | ||||
|         NodeRef categoryNodeRef = services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name); | ||||
|         CategoryNode categoryNode = new CategoryNode(categoryNodeRef, this.services, getScope()); | ||||
|  | ||||
|         return categoryNode; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get the category node from the category node reference. | ||||
|      *  | ||||
|      * @param categoryRef   category node reference | ||||
|      * @return {@link CategoryNode} category node  | ||||
|      */ | ||||
|     public CategoryNode getCategory(String categoryRef) | ||||
|     { | ||||
|         CategoryNode result = null; | ||||
|         NodeRef categoryNodeRef = new NodeRef(categoryRef); | ||||
|         if (services.getNodeService().exists(categoryNodeRef) == true && | ||||
|             services.getDictionaryService().isSubClass(ContentModel.TYPE_CATEGORY, services.getNodeService().getType(categoryNodeRef)) == true) | ||||
|         { | ||||
|             result = new CategoryNode(categoryNodeRef, this.services, getScope()); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the root categories in a classification. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getRootCategories(String aspect) | ||||
|     { | ||||
|         Object[] cats = buildCategoryNodes(services.getCategoryService().getRootCategories( | ||||
|                             storeRef, createQName(aspect))); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get ordered, filtered and paged root categories in a classification. | ||||
|      *  | ||||
|      * @param aspect | ||||
|      * @param filter | ||||
|      * @param maxItems | ||||
|      * @param skipCount (offset) | ||||
|      * @return | ||||
|      */ | ||||
|     public Scriptable getRootCategories(String aspect, String filter, int maxItems, int skipCount) | ||||
|     { | ||||
|         PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems); | ||||
|         List<ChildAssociationRef> rootCategories = services.getCategoryService().getRootCategories(storeRef, createQName(aspect), pagingRequest, true, filter).getPage(); | ||||
|         Object[] cats = buildCategoryNodes(rootCategories); | ||||
|         return Context.getCurrentContext().newArray(getScope(), cats); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the category usage count. | ||||
|      *  | ||||
|      * @param aspect String | ||||
|      * @param maxCount int | ||||
|      * @return Scriptable | ||||
|      */ | ||||
|     public Scriptable getCategoryUsage(String aspect, int maxCount) | ||||
|     { | ||||
|         List<Pair<NodeRef, Integer>> topCats = services.getCategoryService().getTopCategories(storeRef, createQName(aspect), maxCount); | ||||
|         Object[] tags = new Object[topCats.size()]; | ||||
|         int i = 0; | ||||
|         for (Pair<NodeRef, Integer> topCat : topCats) | ||||
|         { | ||||
|            tags[i++] = new Tag(new CategoryNode(topCat.getFirst(), this.services, getScope()), topCat.getSecond()); | ||||
|         } | ||||
|          | ||||
|         return Context.getCurrentContext().newArray(getScope(), tags); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build category nodes. | ||||
|      *  | ||||
|      * @param cars  list of associations to category nodes | ||||
|      * @return {@link Object}[] array of category nodes | ||||
|      */ | ||||
|     private Object[] buildCategoryNodes(Collection<ChildAssociationRef> cars) | ||||
|     { | ||||
|         Object[] categoryNodes = new Object[cars.size()]; | ||||
|         int i = 0; | ||||
|         for (ChildAssociationRef car : cars) | ||||
|         { | ||||
|             categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, getScope()); | ||||
|         } | ||||
|         return categoryNodes; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create QName from string | ||||
|      *  | ||||
|      * @param s QName string value | ||||
|      * @return {@link QName} qualified name object     | ||||
|      */ | ||||
|     private QName createQName(String s) | ||||
|     { | ||||
|         QName qname; | ||||
|         if (s.indexOf(QName.NAMESPACE_BEGIN) != -1) | ||||
|         { | ||||
|             qname = QName.createQName(s); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             qname = QName.createQName(s, this.services.getNamespaceService()); | ||||
|         } | ||||
|         return qname; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Tag class returned from getCategoryUsage(). | ||||
|      */ | ||||
|     public final class Tag | ||||
|     { | ||||
|         private CategoryNode categoryNode; | ||||
|         private int frequency = 0; | ||||
|         | ||||
|         public Tag(CategoryNode categoryNode, int frequency) | ||||
|         { | ||||
|             this.categoryNode = categoryNode; | ||||
|             this.frequency = frequency; | ||||
|         } | ||||
|         | ||||
|         public CategoryNode getCategory() | ||||
|         { | ||||
|             return categoryNode; | ||||
|         } | ||||
|         | ||||
|         public int getFrequency() | ||||
|         { | ||||
|             return frequency; | ||||
|         } | ||||
|      } | ||||
| } | ||||
|   | ||||
| @@ -251,15 +251,16 @@ public class TransactionBehaviourQueue implements TransactionListener | ||||
|         } | ||||
|         catch (IllegalArgumentException e) | ||||
|         { | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId(), e); | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId() + " : " + e.getMessage(), e); | ||||
|         } | ||||
|         catch (IllegalAccessException e) | ||||
|         { | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId(), e); | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId() + " : " + e.getMessage(), e); | ||||
|         } | ||||
|         catch (InvocationTargetException e) | ||||
|         { | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId(), e.getTargetException()); | ||||
|             String msg = e.getMessage() + (e.getTargetException() != null ? "(" + e.getTargetException().getMessage() + ")" : ""); | ||||
|             throw new AlfrescoRuntimeException("Failed to execute transaction-level behaviour " + context.method + " in transaction " + AlfrescoTransactionSupport.getTransactionId() + " : " + msg, e.getTargetException()); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -514,15 +514,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                         NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName); | ||||
|                         boolean createRenditionNode = renditionNode == null; | ||||
|                         boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED); | ||||
|                         boolean sourceChanges = !sourceHasAspectRenditioned || createRenditionNode || transformInputStream == null; | ||||
|                         try | ||||
|                         { | ||||
|                             if (sourceChanges) | ||||
|                             { | ||||
|                                 ruleService.disableRuleType(RuleType.UPDATE); | ||||
|                                 behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); | ||||
|                                 behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE); | ||||
|                             } | ||||
|                             ruleService.disableRuleType(RuleType.UPDATE); | ||||
|                             behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); | ||||
|                             behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE); | ||||
|  | ||||
|                             // If they do not exist create the rendition association and the rendition node. | ||||
|                             if (createRenditionNode) | ||||
| @@ -592,12 +588,9 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea | ||||
|                         } | ||||
|                         finally | ||||
|                         { | ||||
|                             if (sourceChanges) | ||||
|                             { | ||||
|                                 behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); | ||||
|                                 behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE); | ||||
|                                 ruleService.enableRuleType(RuleType.UPDATE); | ||||
|                             } | ||||
|                             behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); | ||||
|                             behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE); | ||||
|                             ruleService.enableRuleType(RuleType.UPDATE); | ||||
|                         } | ||||
|                         return null; | ||||
|                     }, false, true)); | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
|  */ | ||||
| package org.alfresco.repo.search.impl; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| @@ -50,7 +49,6 @@ import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.repo.search.IndexerAndSearcher; | ||||
| import org.alfresco.repo.search.IndexerException; | ||||
| import org.alfresco.repo.tenant.TenantService; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.dictionary.AspectDefinition; | ||||
| import org.alfresco.service.cmr.dictionary.DataTypeDefinition; | ||||
| import org.alfresco.service.cmr.dictionary.DictionaryService; | ||||
| @@ -414,34 +412,7 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService | ||||
|         int count = 0; | ||||
|         boolean moreItems = false; | ||||
|  | ||||
|         final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> { | ||||
|             final Set<ChildAssociationRef> childNodes = new HashSet<>(); | ||||
|             if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter)) | ||||
|             { | ||||
|                 // lookup in DB without filtering | ||||
|                 childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (CollectionUtils.isNotEmpty(exactNamesFilter)) | ||||
|                 { | ||||
|                     // lookup in DB filtering by name | ||||
|                     childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter)); | ||||
|                 } | ||||
|                 if (CollectionUtils.isNotEmpty(alikeNamesFilter)) | ||||
|                 { | ||||
|                     // lookup using search engin filtering by name | ||||
|                     childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Stream<ChildAssociationRef> childNodesStream = childNodes.stream(); | ||||
|             if (sortByName) | ||||
|             { | ||||
|                 childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName())); | ||||
|             } | ||||
|             return childNodesStream.collect(Collectors.toList()); | ||||
|         }; | ||||
|         final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(sortByName, exactNamesFilter, alikeNamesFilter, skipCount, maxItems); | ||||
|  | ||||
|         OUTER_LOOP: for(NodeRef nodeRef : nodeRefs) | ||||
|         { | ||||
| @@ -468,6 +439,55 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService | ||||
|         return new ListBackedPagingResults<>(associations, moreItems); | ||||
|     } | ||||
|  | ||||
|     public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter) | ||||
|     { | ||||
|         final Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectName); | ||||
|         final List<ChildAssociationRef> associations = new LinkedList<>(); | ||||
|  | ||||
|         final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = getNodeRefCollectionFunction(false, exactNamesFilter, alikeNamesFilter, 0, 10000); | ||||
|  | ||||
|         for (NodeRef nodeRef : nodeRefs) | ||||
|         { | ||||
|             Collection<ChildAssociationRef> children = childNodesSupplier.apply(nodeRef); | ||||
|             associations.addAll(children); | ||||
|         } | ||||
|  | ||||
|         return associations; | ||||
|     } | ||||
|  | ||||
|     private Function<NodeRef, Collection<ChildAssociationRef>> getNodeRefCollectionFunction(boolean sortByName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter, int skipCount, int maxItems) | ||||
|     { | ||||
|         final Function<NodeRef, Collection<ChildAssociationRef>> childNodesSupplier = (nodeRef) -> { | ||||
|             final Set<ChildAssociationRef> childNodes = new HashSet<>(); | ||||
|             if (CollectionUtils.isEmpty(exactNamesFilter) && CollectionUtils.isEmpty(alikeNamesFilter)) | ||||
|             { | ||||
|                 // lookup in DB without filtering | ||||
|                 childNodes.addAll(nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, RegexQNamePattern.MATCH_ALL)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (CollectionUtils.isNotEmpty(exactNamesFilter)) | ||||
|                 { | ||||
|                     // lookup in DB filtering by name | ||||
|                     childNodes.addAll(nodeService.getChildrenByName(nodeRef, ContentModel.ASSOC_SUBCATEGORIES, exactNamesFilter)); | ||||
|                 } | ||||
|                 if (CollectionUtils.isNotEmpty(alikeNamesFilter)) | ||||
|                 { | ||||
|                     // lookup using search engine filtering by name | ||||
|                     childNodes.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE, sortByName, alikeNamesFilter, skipCount + maxItems + 1)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Stream<ChildAssociationRef> childNodesStream = childNodes.stream(); | ||||
|             if (sortByName) | ||||
|             { | ||||
|                 childNodesStream = childNodesStream.sorted(Comparator.comparing(tag -> tag.getQName().getLocalName())); | ||||
|             } | ||||
|             return childNodesStream.collect(Collectors.toList()); | ||||
|         }; | ||||
|         return childNodesSupplier; | ||||
|     } | ||||
|  | ||||
|     public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName) | ||||
|     { | ||||
|         return getRootCategories(storeRef, aspectName, null); | ||||
| @@ -615,7 +635,6 @@ public abstract class AbstractCategoryServiceImpl implements CategoryService | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Experimental | ||||
|     public Optional<NodeRef> getRootCategoryNodeRef(final StoreRef storeRef) | ||||
|     { | ||||
|         return getRootCategoryNodeRef(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE); | ||||
|   | ||||
| @@ -504,21 +504,6 @@ public class ResetPasswordServiceImpl implements ResetPasswordService | ||||
|         return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams); | ||||
|     } | ||||
|  | ||||
|     private String getRepoBaseUrl(String url, String propName) | ||||
|     { | ||||
|         if (url == null) | ||||
|         { | ||||
|             LOGGER.warn("The url for the property [" + propName + "] is not configured."); | ||||
|             return ""; | ||||
|         } | ||||
|  | ||||
|         if (url.endsWith("/")) | ||||
|         { | ||||
|             url = url.substring(0, url.length() - 1); | ||||
|         } | ||||
|         return UrlUtil.replaceRepoBaseUrlPlaceholder(url, sysAdminParams); | ||||
|     } | ||||
|  | ||||
|     protected String getResetPasswordEmailTemplate(ClientApp clientApp) | ||||
|     { | ||||
|         return clientApp.getProperty("requestResetPasswordTemplatePath"); | ||||
| @@ -537,16 +522,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService | ||||
|         StringBuilder sb = new StringBuilder(100); | ||||
|  | ||||
|         String pageUrl = clientApp.getProperty("resetPasswordPageUrl"); | ||||
|  | ||||
|         if(!StringUtils.isEmpty(clientApp.getProperty("workspaceUrl"))) | ||||
|         { | ||||
|             String workspaceUrlPlaceholder = clientApp.getProperty("workspaceUrl"); | ||||
|             String workSpaceUrl = getRepoBaseUrl(workspaceUrlPlaceholder,""); | ||||
|             sb.append(UrlUtil.replaceWorkSpaceUrlPlaceholder(pageUrl,workSpaceUrl)); | ||||
|             LOGGER.warn("Client Name is " + clientApp.getName() + " The url used is     " + sb.toString()); | ||||
|  | ||||
|         } | ||||
|         else if(StringUtils.isEmpty(pageUrl)) | ||||
|         if (StringUtils.isEmpty(pageUrl)) | ||||
|         { | ||||
|             sb.append(UrlUtil.getShareUrl(sysAdminParams)); | ||||
|  | ||||
| @@ -559,7 +535,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService | ||||
|             sb.append(getUrl(pageUrl, "")); | ||||
|         } | ||||
|  | ||||
|             sb.append("?key=").append(key) | ||||
|         sb.append("?key=").append(key) | ||||
|                     .append("&id=").append(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id)); | ||||
|  | ||||
|         return sb.toString(); | ||||
|   | ||||
| @@ -357,8 +357,13 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer | ||||
|  | ||||
|         private ClientRegistration.Builder createBuilder(OIDCProviderMetadata metadata) | ||||
|         { | ||||
|             final String authUri = Optional.of(metadata) | ||||
|                                            .map(OIDCProviderMetadata::getAuthorizationEndpointURI) | ||||
|                                            .map(URI::toASCIIString) | ||||
|                                            .orElse(null); | ||||
|             return ClientRegistration | ||||
|                     .withRegistrationId("ids") | ||||
|                     .authorizationUri(authUri) | ||||
|                     .tokenUri(metadata.getTokenEndpointURI().toASCIIString()) | ||||
|                     .jwkSetUri(metadata.getJWKSetURI().toASCIIString()) | ||||
|                     .issuerUri(config.getIssuerUrl()) | ||||
|   | ||||
| @@ -1605,8 +1605,6 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial | ||||
|                     this.userSearchCtls = new SearchControls(); | ||||
|                     this.userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); | ||||
|                     this.userSearchCtls.setReturningAttributes(LDAPUserRegistry.this.userKeys.getFirst()); | ||||
|                     // MNT-14001 fix, set search limit to ensure that server will not return more search results then provided by paged result control | ||||
|                     this.userSearchCtls.setCountLimit(LDAPUserRegistry.this.queryBatchSize > 0 ? LDAPUserRegistry.this.queryBatchSize : 0); | ||||
|  | ||||
|                     this.next = fetchNext(); | ||||
|                 } | ||||
|   | ||||
| @@ -27,8 +27,10 @@ package org.alfresco.repo.tagging; | ||||
|  | ||||
| import static java.util.Collections.emptyMap; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.ASPECT_WORKING_COPY; | ||||
| import static org.alfresco.model.ContentModel.ASSOC_SUBCATEGORIES; | ||||
| import static org.alfresco.model.ContentModel.PROP_NAME; | ||||
| import static org.alfresco.service.cmr.search.SearchService.LANGUAGE_LUCENE; | ||||
| import static org.alfresco.service.namespace.NamespaceService.CONTENT_MODEL_1_0_URI; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| @@ -44,12 +46,16 @@ import java.util.Comparator; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.Iterator; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.Optional; | ||||
| import java.util.Set; | ||||
| import java.util.TreeMap; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.query.EmptyPagingResults; | ||||
| @@ -71,7 +77,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.TransactionListener; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.action.Action; | ||||
| import org.alfresco.service.cmr.action.ActionService; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| @@ -119,7 +124,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|  | ||||
|     private static Log logger = LogFactory.getLog(TaggingServiceImpl.class); | ||||
|      | ||||
| 	private static Collator collator = Collator.getInstance(); | ||||
|     private static Collator collator = Collator.getInstance(); | ||||
|  | ||||
|     private NodeService nodeService; | ||||
|     private NodeService nodeServiceInternal; | ||||
| @@ -136,6 +141,9 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     private static final String TAG_DETAILS_DELIMITER = "|"; | ||||
|     /** Next tag delimiter */ | ||||
|     private static final String NEXT_TAG_DELIMITER = "\n"; | ||||
|     /** Parameters Include count */ | ||||
|     private static final String PARAM_INCLUDE_COUNT = "count"; | ||||
|  | ||||
|  | ||||
|     private static Set<String> FORBIDDEN_TAGS_SEQUENCES = new HashSet<String>(Arrays.asList(new String[] {NEXT_TAG_DELIMITER, TAG_DETAILS_DELIMITER})); | ||||
|  | ||||
| @@ -333,7 +341,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     public void beforeDeleteNode(NodeRef nodeRef) | ||||
|     { | ||||
|        if (this.nodeService.exists(nodeRef) == true &&           | ||||
|            this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true && !this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) | ||||
|            this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TAGGABLE) == true && !this.nodeService.hasAspect(nodeRef, ASPECT_WORKING_COPY)) | ||||
|        { | ||||
|            updateAllScopeTags(nodeRef, Boolean.FALSE); | ||||
|        } | ||||
| @@ -492,8 +500,8 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     { | ||||
|         // Lower the case of the tag | ||||
|         tag = tag.toLowerCase(); | ||||
| 		 | ||||
| 		return getTagNodeRef(storeRef, tag, true); | ||||
|  | ||||
|         return getTagNodeRef(storeRef, tag, true); | ||||
|     }   | ||||
|  | ||||
|     /** | ||||
| @@ -522,33 +530,33 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|      | ||||
|     public NodeRef changeTag(StoreRef storeRef, String existingTag, String newTag) | ||||
|     { | ||||
|     	if (existingTag == null) | ||||
|     	{ | ||||
|     		throw new TaggingException("Existing tag cannot be null"); | ||||
|     	} | ||||
|     	 | ||||
|     	if (newTag == null || StringUtils.isBlank(newTag)) | ||||
|     	{ | ||||
|     		throw new TaggingException("New tag cannot be blank"); | ||||
|     	} | ||||
|         if (existingTag == null) | ||||
|         { | ||||
|             throw new TaggingException("Existing tag cannot be null"); | ||||
|         } | ||||
|  | ||||
|     	existingTag = existingTag.toLowerCase(); | ||||
|     	newTag = newTag.toLowerCase(); | ||||
|         if (newTag == null || StringUtils.isBlank(newTag)) | ||||
|         { | ||||
|             throw new TaggingException("New tag cannot be blank"); | ||||
|         } | ||||
|  | ||||
|     	if (existingTag.equals(newTag)) | ||||
|     	{ | ||||
|     		throw new TaggingException("New and existing tags are the same"); | ||||
|     	} | ||||
|     	 | ||||
|     	if (getTagNodeRef(storeRef, existingTag) == null) | ||||
|     	{ | ||||
|     		throw new NonExistentTagException("Tag " + existingTag + " not found"); | ||||
|     	} | ||||
|     	 | ||||
|     	if (getTagNodeRef(storeRef, newTag) != null) | ||||
|     	{ | ||||
|     		throw new TagExistsException("Tag " + newTag + " already exists"); | ||||
|     	} | ||||
|         existingTag = existingTag.toLowerCase(); | ||||
|         newTag = newTag.toLowerCase(); | ||||
|  | ||||
|         if (existingTag.equals(newTag)) | ||||
|         { | ||||
|             throw new TaggingException("New and existing tags are the same"); | ||||
|         } | ||||
|  | ||||
|         if (getTagNodeRef(storeRef, existingTag) == null) | ||||
|         { | ||||
|             throw new NonExistentTagException("Tag " + existingTag + " not found"); | ||||
|         } | ||||
|  | ||||
|         if (getTagNodeRef(storeRef, newTag) != null) | ||||
|         { | ||||
|             throw new TagExistsException("Tag " + newTag + " already exists"); | ||||
|         } | ||||
|  | ||||
|         NodeRef tagNodeRef = getTagNodeRef(storeRef, existingTag); | ||||
|         nodeService.setProperty(tagNodeRef, PROP_NAME, newTag); | ||||
| @@ -678,6 +686,21 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Map<String, Long> calculateCount(StoreRef storeRef) | ||||
|     { | ||||
|         List<Pair<String, Integer>> tagsByCount = findTaggedNodesAndCountByTagName(storeRef); | ||||
|         Map<String, Long> tagsByCountMap = new HashMap<>(); | ||||
|         if (tagsByCount != null) | ||||
|         { | ||||
|             for (Pair<String, Integer> tagByCountElem : tagsByCount) | ||||
|             { | ||||
|                 tagsByCountMap.put(tagByCountElem.getFirst(), Long.valueOf(tagByCountElem.getSecond())); | ||||
|             } | ||||
|         } | ||||
|         return tagsByCountMap; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @see TaggingService#hasTag(NodeRef, String) | ||||
|      */ | ||||
| @@ -693,12 +716,12 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public NodeRef addTag(final NodeRef nodeRef, final String tagName) | ||||
|     { | ||||
|     	NodeRef newTagNodeRef = null; | ||||
|     	 | ||||
|     	if(tagName == null) | ||||
|     	{ | ||||
|     		throw new IllegalArgumentException("Must provide a non-null tag"); | ||||
|     	} | ||||
|         NodeRef newTagNodeRef = null; | ||||
|  | ||||
|         if(tagName == null) | ||||
|         { | ||||
|             throw new IllegalArgumentException("Must provide a non-null tag"); | ||||
|         } | ||||
|  | ||||
|         updateTagBehaviour.disable(); | ||||
|         createTagBehaviour.disable(); | ||||
| @@ -748,7 +771,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|      */ | ||||
|     public List<Pair<String, NodeRef>> addTags(NodeRef nodeRef, List<String> tags) | ||||
|     { | ||||
|     	List<Pair<String, NodeRef>> ret = new ArrayList<Pair<String, NodeRef>>(); | ||||
|         List<Pair<String, NodeRef>> ret = new ArrayList<Pair<String, NodeRef>>(); | ||||
|         for (String tag : tags) | ||||
|         { | ||||
|             NodeRef tagNodeRef = addTag(nodeRef, tag); | ||||
| @@ -869,36 +892,36 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|                 int skipCount = pagingRequest.getSkipCount(); | ||||
|                 int maxItems = pagingRequest.getMaxItems(); | ||||
|                 int end = maxItems == Integer.MAX_VALUE ? totalItems : skipCount + maxItems; | ||||
|             	int size = (maxItems == Integer.MAX_VALUE ? totalItems : maxItems); | ||||
|                 int size = (maxItems == Integer.MAX_VALUE ? totalItems : maxItems); | ||||
|  | ||||
|                 final List<Pair<NodeRef, String>> sortedTags = new ArrayList<Pair<NodeRef, String>>(size); | ||||
|             	// grab all tags and sort (assume fairly low number of tags) | ||||
|             	for(NodeRef tagNode : currentTagNodes) | ||||
|             	{ | ||||
|                 // grab all tags and sort (assume fairly low number of tags) | ||||
|                 for(NodeRef tagNode : currentTagNodes) | ||||
|                 { | ||||
|                     String tag = (String)this.nodeService.getProperty(tagNode, PROP_NAME); | ||||
|                     sortedTags.add(new Pair<NodeRef, String>(tagNode, tag));            		 | ||||
|             	} | ||||
|                 } | ||||
|                 Collections.sort(sortedTags, new Comparator<Pair<NodeRef, String>>() | ||||
|                 { | ||||
| 					@Override | ||||
| 					public int compare(Pair<NodeRef, String> o1, Pair<NodeRef, String> o2) | ||||
| 					{ | ||||
| 						String tag1 = o1.getSecond(); | ||||
| 						String tag2 = o2.getSecond(); | ||||
| 						return collator.compare(tag1, tag2); | ||||
| 					} | ||||
| 				}); | ||||
|                     @Override | ||||
|                     public int compare(Pair<NodeRef, String> o1, Pair<NodeRef, String> o2) | ||||
|                     { | ||||
|                         String tag1 = o1.getSecond(); | ||||
|                         String tag2 = o2.getSecond(); | ||||
|                         return collator.compare(tag1, tag2); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 final List<Pair<NodeRef, String>> result = new ArrayList<Pair<NodeRef, String>>(size); | ||||
|             	Iterator<Pair<NodeRef, String>> it = sortedTags.iterator(); | ||||
|                 Iterator<Pair<NodeRef, String>> it = sortedTags.iterator(); | ||||
|                 for(int count = 0; count < end && it.hasNext(); count++) | ||||
|                 { | ||||
|                 	Pair<NodeRef, String> tagPair = it.next(); | ||||
|                     Pair<NodeRef, String> tagPair = it.next(); | ||||
|  | ||||
|                 	if(count < skipCount) | ||||
|                 	{ | ||||
|                 		continue; | ||||
|                 	} | ||||
|                     if(count < skipCount) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     result.add(tagPair); | ||||
|                 } | ||||
| @@ -907,30 +930,30 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|  | ||||
|                 return new PagingResults<Pair<NodeRef, String>>() | ||||
|                 { | ||||
|         			@Override | ||||
|         			public List<Pair<NodeRef, String>> getPage() | ||||
|         			{ | ||||
|         				return result; | ||||
|         			} | ||||
|                     @Override | ||||
|                     public List<Pair<NodeRef, String>> getPage() | ||||
|                     { | ||||
|                         return result; | ||||
|                     } | ||||
|  | ||||
|         			@Override | ||||
|         			public boolean hasMoreItems() | ||||
|         			{ | ||||
|         				return hasMoreItems; | ||||
|         			} | ||||
|                     @Override | ||||
|                     public boolean hasMoreItems() | ||||
|                     { | ||||
|                         return hasMoreItems; | ||||
|                     } | ||||
|  | ||||
|         			@Override | ||||
|         			public Pair<Integer, Integer> getTotalResultCount() | ||||
|         			{ | ||||
|         				Integer total = Integer.valueOf(totalItems); | ||||
|         				return new Pair<Integer, Integer>(total, total); | ||||
|         			} | ||||
|                     @Override | ||||
|                     public Pair<Integer, Integer> getTotalResultCount() | ||||
|                     { | ||||
|                         Integer total = Integer.valueOf(totalItems); | ||||
|                         return new Pair<Integer, Integer>(total, total); | ||||
|                     } | ||||
|  | ||||
|         			@Override | ||||
|         			public String getQueryExecutionId() | ||||
|         			{ | ||||
|         				return null; | ||||
|         			} | ||||
|                     @Override | ||||
|                     public String getQueryExecutionId() | ||||
|                     { | ||||
|                         return null; | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
| @@ -954,7 +977,83 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|             exactNamesFilter, alikeNamesFilter); | ||||
|  | ||||
|         return mapPagingResult(rootCategories, | ||||
|             (childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName())); | ||||
|                 (childAssociation) -> new Pair<>(childAssociation.getChildRef(), childAssociation.getQName().getLocalName())); | ||||
|     } | ||||
|  | ||||
|     public Map<NodeRef, Long> getTags(StoreRef storeRef, List<String> parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter) | ||||
|     { | ||||
|         ParameterCheck.mandatory("storeRef", storeRef); | ||||
|         Collection<ChildAssociationRef> rootCategories = categoryService.getRootCategories(storeRef, ContentModel.ASPECT_TAGGABLE, exactNamesFilter, alikeNamesFilter); | ||||
|  | ||||
|         Map<String, Long> tagsMap = new TreeMap<>(); | ||||
|         for (ChildAssociationRef childAssociation : rootCategories) | ||||
|         { | ||||
|             tagsMap.put(childAssociation.getQName().getLocalName(), 0L); | ||||
|         } | ||||
|  | ||||
|         Map<String, Long> tagsByCountMap = new HashMap<>(); | ||||
|  | ||||
|         if(parameterIncludes.contains(PARAM_INCLUDE_COUNT)) | ||||
|         { | ||||
|             tagsByCountMap = calculateCount(storeRef); | ||||
|  | ||||
|             for (Map.Entry<String, Long> entry : tagsMap.entrySet()) { | ||||
|                 entry.setValue(Optional.ofNullable(tagsByCountMap.get(entry.getKey())).orElse(0L)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //check if we should sort results. Can only sort by one parameter, default order is ascending | ||||
|         if (sorting != null) | ||||
|         { | ||||
|             if (sorting.getFirst().equals("tag")) | ||||
|             { | ||||
|                 if (!sorting.getSecond()) | ||||
|                 { | ||||
|                     Stream<Map.Entry<String,Long>> sortedTags = | ||||
|                             tagsMap.entrySet().stream() | ||||
|                                     .sorted(Collections.reverseOrder(Map.Entry.comparingByKey())); | ||||
|                     tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Stream<Map.Entry<String,Long>> sortedTags = | ||||
|                             tagsMap.entrySet().stream() | ||||
|                                     .sorted(Map.Entry.comparingByKey()); | ||||
|                     tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|                 } | ||||
|             } | ||||
|             else if (sorting.getFirst().equals(PARAM_INCLUDE_COUNT)) | ||||
|             { | ||||
|                 if (tagsByCountMap.isEmpty()) | ||||
|                 { | ||||
|                     throw new IllegalArgumentException("Tag count should be included when ordering by count"); | ||||
|                 } | ||||
|  | ||||
|                 if (!sorting.getSecond()) | ||||
|                 { | ||||
|                     Stream<Map.Entry<String, Long>> sortedTags = | ||||
|                             tagsMap.entrySet().stream() | ||||
|                                     .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); | ||||
|                     tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Stream<Map.Entry<String,Long>> sortedTags = | ||||
|                             tagsMap.entrySet().stream() | ||||
|                                     .sorted(Map.Entry.comparingByValue()); | ||||
|                     tagsMap = sortedTags.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Map<NodeRef, Long> tagNodeRefMap = new LinkedHashMap<>(); | ||||
|  | ||||
|         for (Map.Entry<String, Long> entry : tagsMap.entrySet()) | ||||
|         { | ||||
|             tagNodeRefMap.put(getTagNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, entry.getKey()), entry.getValue()); | ||||
|         } | ||||
|  | ||||
|         return tagNodeRefMap; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
| @@ -1188,15 +1287,15 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|      */ | ||||
|     private void getTagScopes(final NodeRef nodeRef, List<NodeRef> tagScopes, boolean firstOnly) | ||||
|     { | ||||
|     	Boolean hasAspect = AuthenticationUtil.runAs(new RunAsWork<Boolean>() | ||||
|     	{ | ||||
| 			@Override | ||||
| 			public Boolean doWork() throws Exception  | ||||
| 			{ | ||||
| 				return new Boolean(nodeService.hasAspect(nodeRef,  ContentModel.ASPECT_TAGSCOPE)); | ||||
| 			} | ||||
| 		}, AuthenticationUtil.getSystemUserName()); | ||||
|     	 | ||||
|         Boolean hasAspect = AuthenticationUtil.runAs(new RunAsWork<Boolean>() | ||||
|         { | ||||
|             @Override | ||||
|             public Boolean doWork() throws Exception | ||||
|             { | ||||
|                 return new Boolean(nodeService.hasAspect(nodeRef,  ContentModel.ASPECT_TAGSCOPE)); | ||||
|             } | ||||
|         }, AuthenticationUtil.getSystemUserName()); | ||||
|  | ||||
|         if (Boolean.TRUE.equals(hasAspect) == true) | ||||
|         { | ||||
|             tagScopes.add(nodeRef); | ||||
| @@ -1207,23 +1306,23 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|         } | ||||
|          | ||||
|         NodeRef parent = AuthenticationUtil.runAs(new RunAsWork<NodeRef>() | ||||
|     	{ | ||||
| 			@Override | ||||
| 			public NodeRef doWork() throws Exception  | ||||
| 			{ | ||||
| 				NodeRef result = null; | ||||
| 				ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef); | ||||
| 		        if (assoc != null) | ||||
| 		        { | ||||
| 		            result = assoc.getParentRef();                           | ||||
| 		        } | ||||
| 		        return result; | ||||
| 			} | ||||
| 		}, AuthenticationUtil.getSystemUserName()); | ||||
|         { | ||||
|             @Override | ||||
|             public NodeRef doWork() throws Exception | ||||
|             { | ||||
|                 NodeRef result = null; | ||||
|                 ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef); | ||||
|                 if (assoc != null) | ||||
|                 { | ||||
|                     result = assoc.getParentRef(); | ||||
|                 } | ||||
|                 return result; | ||||
|             } | ||||
|         }, AuthenticationUtil.getSystemUserName()); | ||||
|          | ||||
|         if (parent != null) | ||||
|         { | ||||
|         	getTagScopes(parent, tagScopes, firstOnly);             | ||||
|             getTagScopes(parent, tagScopes, firstOnly); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1241,7 +1340,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|             // Do the search for nodes | ||||
|             resultSet = this.searchService.query( | ||||
|                 storeRef,  | ||||
|                 SearchService.LANGUAGE_LUCENE,  | ||||
|                 LANGUAGE_LUCENE, | ||||
|                 "+PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\""); | ||||
|             List<NodeRef> nodeRefs = resultSet.getNodeRefs(); | ||||
|             return nodeRefs; | ||||
| @@ -1270,7 +1369,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|             // Do query | ||||
|             resultSet = this.searchService.query( | ||||
|                 storeRef,  | ||||
|                 SearchService.LANGUAGE_LUCENE,  | ||||
|                 LANGUAGE_LUCENE, | ||||
|                 "+PATH:\"" + pathString + "//*\" +PATH:\"/cm:taggable/cm:" + ISO9075.encode(tag) + "/member\""); | ||||
|             List<NodeRef> nodeRefs = resultSet.getNodeRefs(); | ||||
|             return nodeRefs; | ||||
| @@ -1538,7 +1637,7 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     public void afterCheckOut(NodeRef workingCopy) | ||||
|     { | ||||
|         if (this.nodeService.exists(workingCopy) == true && this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_TAGGABLE) == true | ||||
|                 && this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)) | ||||
|                 && this.nodeService.hasAspect(workingCopy, ASPECT_WORKING_COPY)) | ||||
|         { | ||||
|             updateAllScopeTags(workingCopy, Boolean.FALSE); | ||||
|         } | ||||
| @@ -1550,10 +1649,10 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|     @Override | ||||
|     public List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef) | ||||
|     { | ||||
|         String queryTaggeble = "ASPECT:\"" + ContentModel.ASPECT_TAGGABLE + "\"" + "-ASPECT:\"" + ContentModel.ASPECT_WORKING_COPY + "\""; | ||||
|         String queryTaggeble = "ASPECT:\"" + ContentModel.ASPECT_TAGGABLE + "\"" + "-ASPECT:\"" + ASPECT_WORKING_COPY + "\""; | ||||
|         SearchParameters sp = new SearchParameters(); | ||||
|         sp.setQuery(queryTaggeble); | ||||
|         sp.setLanguage(SearchService.LANGUAGE_LUCENE); | ||||
|         sp.setLanguage(LANGUAGE_LUCENE); | ||||
|         sp.addStore(storeRef); | ||||
|         sp.addFieldFacet(new FieldFacet("TAG")); | ||||
|  | ||||
| @@ -1573,7 +1672,32 @@ public class TaggingServiceImpl implements TaggingService, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Experimental | ||||
|     /** {@inheritDoc} */ | ||||
|     @Override | ||||
|     public long findCountByTagName(StoreRef storeRef, String name) | ||||
|     { | ||||
|         String query = "TAG:\"" + name + "\"" + "-ASPECT:\"" + ASPECT_WORKING_COPY + "\""; | ||||
|         SearchParameters sp = new SearchParameters(); | ||||
|         sp.setQuery(query); | ||||
|         sp.setLanguage(LANGUAGE_LUCENE); | ||||
|         sp.addStore(storeRef); | ||||
|  | ||||
|         ResultSet resultSet = null; | ||||
|         try | ||||
|         { | ||||
|             // Do the search for nodes | ||||
|             resultSet = this.searchService.query(sp); | ||||
|             return resultSet.getNumberFound(); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             if (resultSet != null) | ||||
|             { | ||||
|                 resultSet.close(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<Pair<String, NodeRef>> createTags(final StoreRef storeRef, final List<String> tagNames) | ||||
|     { | ||||
|   | ||||
| @@ -997,6 +997,20 @@ public class WorkflowServiceImpl implements WorkflowService | ||||
|     public WorkflowTask updateTask(String taskId, Map<QName, Serializable> properties, Map<QName, List<NodeRef>> add, | ||||
|                 Map<QName, List<NodeRef>> remove) | ||||
|     { | ||||
|     	if(properties.containsKey(WorkflowModel.PROP_STATUS)) { | ||||
|  | ||||
|             LinkedList<String> validTaskStatus = new LinkedList<>(); | ||||
|             validTaskStatus.add("Not Yet Started"); | ||||
|             validTaskStatus.add("In Progress"); | ||||
|             validTaskStatus.add("On Hold"); | ||||
|             validTaskStatus.add("Cancelled"); | ||||
|             validTaskStatus.add("Completed"); | ||||
|  | ||||
|             if (!validTaskStatus.contains(properties.get(WorkflowModel.PROP_STATUS))) { | ||||
|                 throw new WorkflowException("Invalid Value is Passed for Task Status."); | ||||
|             } | ||||
|         } | ||||
| 	 | ||||
|         String engineId = BPMEngineRegistry.getEngineId(taskId); | ||||
|         TaskComponent component = getTaskComponent(engineId); | ||||
|         // get the current assignee before updating the task | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
| package org.alfresco.service.cmr.search; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
|  | ||||
| @@ -34,7 +35,6 @@ import org.alfresco.query.EmptyPagingResults; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.service.Auditable; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.cmr.repository.ChildAssociationRef; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| @@ -150,11 +150,21 @@ public interface CategoryService | ||||
|      */ | ||||
|     @Auditable(parameters = {"storeRef", "aspectName", "pagingRequest", "sortByName", "exactNamesFilter", "alikeNamesFilter"}) | ||||
|     default PagingResults<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, PagingRequest pagingRequest, boolean sortByName, | ||||
|         Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter) | ||||
|                                                               Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter) | ||||
|     { | ||||
|         return new EmptyPagingResults<>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a collection of the root categories for an aspect/classification supporting multiple name filters. | ||||
|      */ | ||||
|     @Auditable(parameters = {"storeRef", "aspectName", "exactNamesFilter", "alikeNamesFilter"}) | ||||
|     default Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter) | ||||
|     { | ||||
|         return Collections.emptyList(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Get the root categories for an aspect/classification with names that start with filter | ||||
|      *  | ||||
| @@ -272,7 +282,6 @@ public interface CategoryService | ||||
|      * | ||||
|      * @return NodeRef for category root node | ||||
|      */ | ||||
|     @Experimental | ||||
|     @Auditable(parameters = {"storeRef"}) | ||||
|     default Optional<NodeRef> getRootCategoryNodeRef(final StoreRef storeRef) | ||||
|     { | ||||
|   | ||||
| @@ -28,13 +28,13 @@ package org.alfresco.service.cmr.tagging; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import org.alfresco.api.AlfrescoPublicApi; | ||||
| import org.alfresco.query.EmptyPagingResults; | ||||
| import org.alfresco.query.PagingRequest; | ||||
| import org.alfresco.query.PagingResults; | ||||
| import org.alfresco.service.Auditable; | ||||
| import org.alfresco.service.Experimental; | ||||
| import org.alfresco.service.NotAuditable; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| @@ -94,6 +94,18 @@ public interface TaggingService | ||||
|     { | ||||
|         return new EmptyPagingResults<>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a map of tag NodeRefs and their respective usage count filtered by name and sorted by tag name or count | ||||
|      * | ||||
|      * @param storeRef | ||||
|      * @param parameterIncludes | ||||
|      * @param sorting | ||||
|      * @param exactNamesFilter | ||||
|      * @param alikeNamesFilter | ||||
|      * @return | ||||
|      */ | ||||
|     Map<NodeRef, Long> getTags(StoreRef storeRef, List<String>parameterIncludes, Pair<String, Boolean> sorting, Collection<String> exactNamesFilter, Collection<String> alikeNamesFilter); | ||||
|      | ||||
|     /**  | ||||
|      * Get all the tags currently available that match the provided filter. | ||||
| @@ -327,8 +339,7 @@ public interface TaggingService | ||||
|      */ | ||||
|     @NotAuditable | ||||
|     Pair<List<String>, Integer> getPagedTags(StoreRef storeRef, String filter, int fromTag, int pageSize); | ||||
|      | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Get tagged nodes and count of nodes group by tag name | ||||
|      *  | ||||
| @@ -338,6 +349,16 @@ public interface TaggingService | ||||
|     @NotAuditable | ||||
|     List<Pair<String, Integer>> findTaggedNodesAndCountByTagName(StoreRef storeRef); | ||||
|  | ||||
|     /** | ||||
|      * Get the number of tagged nodes for a given tag. | ||||
|      * | ||||
|      * @param storeRef The store containing the nodes. | ||||
|      * @param name The name of the tag. | ||||
|      * @return The number of nodes tagged with the specified tag. | ||||
|      */ | ||||
|     @NotAuditable | ||||
|     long findCountByTagName(StoreRef storeRef, String name); | ||||
|  | ||||
|     /** | ||||
|      * Creates orphan tags. Tag names case will be lowered. | ||||
|      * | ||||
| @@ -346,12 +367,18 @@ public interface TaggingService | ||||
|      * @return {@link List} of {@link Pair}s of tag names and node references. | ||||
|      * @throws org.alfresco.service.cmr.repository.DuplicateChildNodeNameException if tag already exists. | ||||
|      */ | ||||
|     @Experimental | ||||
|     @Auditable(parameters = {"tagNames"}) | ||||
|     default List<Pair<String, NodeRef>> createTags(StoreRef storeRef, List<String> tagNames) | ||||
|     { | ||||
|         return Collections.emptyList(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param storeRef | ||||
|      * @return a map with each tag name and its usage count | ||||
|      */ | ||||
|     Map<String, Long> calculateCount(StoreRef storeRef); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -41,12 +41,6 @@ public class UrlUtil | ||||
|     public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}"); | ||||
|     // ${alfrescoUrl} placeholder | ||||
|     public static final Pattern REPO_PATTERN = Pattern.compile("\\$\\{alfrescoUrl\\}"); | ||||
|  | ||||
|     public static final Pattern REPOBASE_PATTERN = Pattern.compile("\\$\\{repoBaseUrl\\}"); | ||||
|  | ||||
|     public static final Pattern WORKSPACE_PATTERN = Pattern.compile("\\$\\{workspaceUrl\\}"); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Builds up the Url to Alfresco based on the settings in the  | ||||
|      *  {@link SysAdminParams}.  | ||||
| @@ -152,68 +146,4 @@ public class UrlUtil | ||||
|         url.append(context); | ||||
|         return url.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds up the Url to Adw based on the settings in the | ||||
|      *  {@link SysAdminParams}. | ||||
|      * @return Adw Url such as https://col.ab.or.ate/#/ | ||||
|      *  or http://localhost:8081/#/ | ||||
|      */ | ||||
|     public static String getWorkspaceUrl(SysAdminParams sysAdminParams) | ||||
|     { | ||||
|         return buildWorkspaceUrl( | ||||
|                 sysAdminParams.getAlfrescoProtocol(), | ||||
|                 sysAdminParams.getAlfrescoHost(), | ||||
|                 sysAdminParams.getAlfrescoPort()); | ||||
|     } | ||||
|  | ||||
|     protected static String buildWorkspaceUrl(String workSpaceProtocol, String workspaceHost, int workspacePort) { | ||||
|         StringBuilder workspaceUrl = new StringBuilder(); | ||||
|         workspaceUrl.append(workSpaceProtocol); | ||||
|         workspaceUrl.append("://"); | ||||
|         workspaceUrl.append(workspaceHost); | ||||
|         if ("http".equals(workSpaceProtocol) && workspacePort == 80) | ||||
|         { | ||||
|             // Not needed | ||||
|         } | ||||
|         else if ("https".equals(workSpaceProtocol) && workspacePort == 443) | ||||
|         { | ||||
|             // Not needed | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             workspaceUrl.append(':'); | ||||
|             workspaceUrl.append(workspacePort); | ||||
|         } | ||||
|  | ||||
|         return workspaceUrl.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Replaces the repo base url placeholder, namely {@literal ${repoBaseUrl}}, with <b>workspace</b> url. | ||||
|      * | ||||
|      * @param value          the string value which contains the repoBase url placeholder | ||||
|      * @param sysAdminParams the {@code SysAdminParams} object | ||||
|      * @return if the given {@code value} contains share url placeholder, | ||||
|      * the placeholder is replaced with share url; otherwise, the given {@code value} is simply returned | ||||
|      */ | ||||
|  | ||||
|     public static String replaceRepoBaseUrlPlaceholder(String value, SysAdminParams sysAdminParams) | ||||
|     { | ||||
|         if (value != null) | ||||
|         { | ||||
|             return REPOBASE_PATTERN.matcher(value).replaceAll(getWorkspaceUrl(sysAdminParams)); | ||||
|         } | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     public static String replaceWorkSpaceUrlPlaceholder(String pageUrl,String workspaceUrl) | ||||
|     { | ||||
|         if (pageUrl != null) | ||||
|         { | ||||
|             return WORKSPACE_PATTERN.matcher(pageUrl).replaceAll(workspaceUrl); | ||||
|         } | ||||
|         return pageUrl; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -25,27 +25,66 @@ | ||||
|  */ | ||||
| package org.alfresco.util.remote.server; | ||||
|  | ||||
| import org.springframework.remoting.rmi.RmiRegistryFactoryBean; | ||||
| import org.alfresco.util.remote.server.socket.HostConfigurableSocketFactory; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.DisposableBean; | ||||
| import org.springframework.beans.factory.FactoryBean; | ||||
|  | ||||
| import java.rmi.RemoteException; | ||||
| import java.rmi.registry.LocateRegistry; | ||||
| import java.rmi.registry.Registry; | ||||
| import java.rmi.server.RMIClientSocketFactory; | ||||
| import java.rmi.server.RMIServerSocketFactory; | ||||
| import java.rmi.server.UnicastRemoteObject; | ||||
|  | ||||
| /** | ||||
|  * This class controls the RMI connectivity via <code>alfresco.jmx.connector.enabled</code> property | ||||
|  * | ||||
|  * @author alex.mukha | ||||
|  */ | ||||
| public class AlfrescoRmiRegistryFactoryBean extends RmiRegistryFactoryBean | ||||
| public class AlfrescoRmiRegistryFactoryBean implements FactoryBean<Registry>, DisposableBean | ||||
| { | ||||
|     private static final String ERR_MSG_NOT_ENABLED = "The RMI registry factory is disabled."; | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(AlfrescoRmiRegistryFactoryBean.class); | ||||
|  | ||||
|     private boolean enabled = true; | ||||
|     private boolean created = false; | ||||
|  | ||||
|     public void setEnabled(boolean enabled) | ||||
|     { | ||||
|     private final boolean enabled; | ||||
|  | ||||
|     private final int port; | ||||
|  | ||||
|     private final Registry registry; | ||||
|  | ||||
|     public AlfrescoRmiRegistryFactoryBean(boolean enabled, int port, HostConfigurableSocketFactory socketFactory) throws Exception { | ||||
|         this.enabled = enabled; | ||||
|         this.port = port; | ||||
|         if(this.enabled) | ||||
|         { | ||||
|             this.registry = initRegistry(socketFactory); | ||||
|         } | ||||
|         else { | ||||
|             this.registry = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Registry initRegistry(HostConfigurableSocketFactory socketFactory) throws RemoteException { | ||||
|         if (LOG.isDebugEnabled()) { | ||||
|             LOG.debug("Looking for RMI registry at port '" + this.port + "', using custom socket factory"); | ||||
|         } | ||||
|         Registry registry; | ||||
|         synchronized (LocateRegistry.class) { | ||||
|             try { | ||||
|                 // Retrieve existing registry. | ||||
|                 registry = LocateRegistry.getRegistry(null, this.port, socketFactory); | ||||
|                 testRegistry(this.registry); | ||||
|             } | ||||
|             catch (RemoteException ex) { | ||||
|                 LOG.trace("RMI registry access threw exception", ex); | ||||
|                 LOG.debug("Could not detect RMI registry - creating new one"); | ||||
|                 // Assume no registry found -> create new one. | ||||
|                 this.created = true; | ||||
|                 registry = LocateRegistry.createRegistry(this.port, socketFactory, socketFactory); | ||||
|             } | ||||
|         } | ||||
|         return registry; | ||||
|     } | ||||
|  | ||||
|     public boolean isEnabled() | ||||
| @@ -54,57 +93,34 @@ public class AlfrescoRmiRegistryFactoryBean extends RmiRegistryFactoryBean | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         if (enabled) | ||||
|         { | ||||
|             super.afterPropertiesSet(); | ||||
|     public void destroy() throws Exception { | ||||
|         if (this.created) { | ||||
|             LOG.debug("Unexporting RMI registry"); | ||||
|             UnicastRemoteObject.unexportObject(this.registry, true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Registry getRegistry( | ||||
|             String registryHost, | ||||
|             int registryPort, | ||||
|             RMIClientSocketFactory clientSocketFactory, | ||||
|             RMIServerSocketFactory serverSocketFactory) throws RemoteException | ||||
|     { | ||||
|         if(enabled) | ||||
|         { | ||||
|             return super.getRegistry(registryHost, registryPort, clientSocketFactory, serverSocketFactory); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             throw new RemoteException(ERR_MSG_NOT_ENABLED); | ||||
|         } | ||||
|     public Registry getObject() throws Exception { | ||||
|         return this.registry; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Registry getRegistry( | ||||
|             int registryPort, | ||||
|             RMIClientSocketFactory clientSocketFactory, | ||||
|             RMIServerSocketFactory serverSocketFactory) throws RemoteException | ||||
|     { | ||||
|         if(enabled) | ||||
|         { | ||||
|             return super.getRegistry(registryPort, clientSocketFactory, serverSocketFactory); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             throw new RemoteException(ERR_MSG_NOT_ENABLED); | ||||
|         } | ||||
|     public Class<?> getObjectType() { | ||||
|         return (this.registry != null ? this.registry.getClass() : Registry.class); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Registry getRegistry(int registryPort) throws RemoteException | ||||
|     public boolean isCreated() | ||||
|     { | ||||
|         if(enabled) | ||||
|         { | ||||
|             return super.getRegistry(registryPort); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             throw new RemoteException(ERR_MSG_NOT_ENABLED); | ||||
|         } | ||||
|         return created; | ||||
|     } | ||||
|  | ||||
|     public int getPort() { | ||||
|         return port; | ||||
|     } | ||||
|  | ||||
|     private void testRegistry(Registry registry) throws RemoteException | ||||
|     { | ||||
|         registry.list(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,16 +20,6 @@ repo.client-app.share.resetPasswordPageUrl=${shareUrl}/page/reset-password | ||||
| repo.client-app.share.confirmResetPasswordTemplatePath= | ||||
|  | ||||
| ### Digital workspace template configurations | ||||
| #repo.client-app.workspace.inviteModeratedTemplatePath= | ||||
|  | ||||
| repo.client-app.workspace.workspaceUrl=${repoBaseUrl}/workspace | ||||
|  | ||||
| #repo.client-app.workspace.templateAssetsUrl=${workspaceUrl}/images | ||||
|  | ||||
| repo.client-app.workspace.templateAssetsUrl=alfresco/templates/reset-password-email-templates/images | ||||
| # reset password request email template path | ||||
| repo.client-app.workspace.requestResetPasswordTemplatePath=alfresco/templates/reset-password-email-templates/forgot-password-email-template.ftl | ||||
| # reset password UI page url | ||||
| repo.client-app.workspace.resetPasswordPageUrl=${workspaceUrl}/reset-password/ | ||||
| # reset password confirmation email template path | ||||
| repo.client-app.workspace.confirmResetPasswordTemplatePath= | ||||
| repo.client-app.workspace.inviteModeratedTemplatePath= | ||||
| repo.client-app.workspace.workspaceUrl=workspace | ||||
| repo.client-app.workspace.templateAssetsUrl=${alfrescoUrl}/images | ||||
|   | ||||
| @@ -75,6 +75,9 @@ | ||||
|         <property name="propertiesPersister"> | ||||
|         	<bean class="org.alfresco.config.AlfrescoPropertiesPersister"/> | ||||
|         </property> | ||||
|         <property name="order"> | ||||
|             <value>9999999</value> | ||||
|         </property> | ||||
|     </bean> | ||||
|  | ||||
|     <!-- Expand global properties and version information in bean definitions --> | ||||
| @@ -101,6 +104,18 @@ | ||||
|         </property> | ||||
|     </bean> | ||||
|  | ||||
|     <bean id="default-properties-resolver" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> | ||||
|         <property name="ignoreUnresolvablePlaceholders"> | ||||
|             <value>true</value> | ||||
|         </property> | ||||
|         <property name="searchSystemEnvironment"> | ||||
|             <value>false</value> | ||||
|         </property> | ||||
|         <property name="order"> | ||||
|             <value>2147483647</value> | ||||
|         </property> | ||||
|     </bean> | ||||
|  | ||||
|  | ||||
|     <!-- Load properties that must be shared between the        --> | ||||
|     <!-- Alfresco server and its remote clients.                --> | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| repository.name=Main Repository | ||||
|  | ||||
| # Schema number | ||||
| version.schema=18000 | ||||
| version.schema=19000 | ||||
|  | ||||
| # Directory configuration | ||||
|  | ||||
| @@ -752,12 +752,16 @@ encryption.ssl.truststore.keyMetaData.location= | ||||
| ## HttpClient config for Transform Service | ||||
| #enable mtls in HttpClientFactory for Transform Service. | ||||
| httpclient.config.transform.mTLSEnabled=false | ||||
| httpclient.config.transform.maxTotalConnections=40 | ||||
| httpclient.config.transform.maxHostConnections=40 | ||||
| httpclient.config.transform.maxTotalConnections=20 | ||||
| httpclient.config.transform.maxHostConnections=20 | ||||
| httpclient.config.transform.socketTimeout=5000 | ||||
| httpclient.config.transform.connectionRequestTimeout=5000 | ||||
| httpclient.config.transform.connectionTimeout=5000 | ||||
|  | ||||
| # Property is disabled by default for security reasons, never enable it on for production environments. | ||||
| # It will stop verification of hostnames placed in certificates returned with server responses to Repository requests towards transform services | ||||
| httpclient.config.transform.hostnameVerificationDisabled=false | ||||
|  | ||||
| # Re-encryptor properties | ||||
| encryption.reencryptor.chunkSize=100 | ||||
| encryption.reencryptor.numThreads=2 | ||||
| @@ -1345,7 +1349,7 @@ system.remove-alf_server-table-from-db.ignored=true | ||||
| allow.unsecure.callback.jsonp=false | ||||
|  | ||||
| # pre-configured allow list of media/mime types to allow inline instead of attachment (via Content-Disposition response header) | ||||
| content.nonAttach.mimetypes=application/pdf,image/jpeg,image/gif,image/png,image/tiff,image/bmp | ||||
| content.nonAttach.mimetypes=application/pdf,image/jpeg,image/gif,image/png,image/tiff,image/bmp,application/octet-stream | ||||
|  | ||||
| # Zip file compression ratio threshold as a percentage, above which the zip file will be considered a "zip bomb" and the | ||||
| # import extraction process cancelled. | ||||
|   | ||||
| @@ -1,428 +0,0 @@ | ||||
| <html> | ||||
| <head> | ||||
|    <style type="text/css"> | ||||
|       td { | ||||
|          font-family: 'Helvetica Neue', Arial, sans-serif; | ||||
|       } | ||||
|  | ||||
|       body { | ||||
|          -webkit-font-smoothing: antialiased; | ||||
|          -webkit-text-size-adjust: none; | ||||
|          width: 100% ! important; | ||||
|          height: 100% !important; | ||||
|          color: #727174; | ||||
|          font-weight: 400; | ||||
|          font-size: 18px; | ||||
|          margin: 0; | ||||
|       } | ||||
|  | ||||
|       h1 { | ||||
|          margin: 10px 0; | ||||
|       } | ||||
|  | ||||
|       h2 { | ||||
|          color: #727174; | ||||
|          font-weight: 600; | ||||
|          font-size: 22px; | ||||
|       } | ||||
|  | ||||
|       a { | ||||
|          color: #0c79bf; | ||||
|          text-decoration: underline; | ||||
|       } | ||||
|  | ||||
|       a.linkone { | ||||
|          color: #0c79bf; | ||||
|          text-decoration: underline; | ||||
|       } | ||||
|  | ||||
|       .appleLinks a { | ||||
|          color: #0c79bf; | ||||
|       } | ||||
|  | ||||
|       .appleLinksWhite a { | ||||
|          color: #0c79bf; | ||||
|       } | ||||
|  | ||||
|       .force-full-width { | ||||
|          width: 100% !important; | ||||
|       } | ||||
|  | ||||
|       .body-padding { | ||||
|          padding: 0 75px | ||||
|       } | ||||
|  | ||||
|       .force-width-80 { | ||||
|          width: 80% !important; | ||||
|       } | ||||
|  | ||||
|       p { | ||||
|          Margin-bottom: 1em; | ||||
|       } | ||||
|  | ||||
|       .button { | ||||
|          text-align: center; | ||||
|          font-size: 18px; | ||||
|          font-family: sans-serif; | ||||
|          font-weight: 400; | ||||
|          -webkit-font-smoothing: antialiased; | ||||
|          -webkit-text-size-adjust: none; | ||||
|       } | ||||
|  | ||||
|       .button a { | ||||
|          color: #ffffff; | ||||
|          text-decoration: none; | ||||
|       } | ||||
|  | ||||
|       img { | ||||
|          max-width: 600px; | ||||
|          outline: none; | ||||
|          text-decoration: none; | ||||
|          -ms-interpolation-mode: bicubic; | ||||
|       } | ||||
|  | ||||
|       a img { | ||||
|          border: none; | ||||
|       } | ||||
|  | ||||
|       table { | ||||
|          border-collapse: collapse; | ||||
|          mso-table-lspace: 0pt; | ||||
|          mso-table-rspace: 0pt; | ||||
|       } | ||||
|  | ||||
|       #outlook a { | ||||
|          padding: 0; | ||||
|       } | ||||
|  | ||||
|       .ReadMsgBody { | ||||
|          width: 100%; | ||||
|       } | ||||
|  | ||||
|       .ExternalClass { | ||||
|          width: 100%; | ||||
|       } | ||||
|  | ||||
|       .backgroundTable { | ||||
|          margin: 0 auto; | ||||
|          padding: 0; | ||||
|          width: 100% !important; | ||||
|       } | ||||
|  | ||||
|       table td { | ||||
|          border-collapse: collapse; | ||||
|       } | ||||
|  | ||||
|       .ExternalClass * { | ||||
|          line-height: 115%; | ||||
|       } | ||||
|  | ||||
|       .ExternalClass { | ||||
|          vertical-align: middle | ||||
|       } | ||||
|  | ||||
|       /*]]>*/ | ||||
|    </style> | ||||
|    <style type="text/css" media="screen"> | ||||
|       @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { { | ||||
|          @-ms-viewport { | ||||
|             width: 320px; | ||||
|          } | ||||
|          @viewport { | ||||
|             width: 320px; | ||||
|          } | ||||
|       } | ||||
|    </style> | ||||
|    <style type="text/css" media="screen"> | ||||
|       @media screen { | ||||
|          * { | ||||
|             font-family: 'Helvetica Neue', 'Arial', 'sans-serif' !important; | ||||
|          } | ||||
|  | ||||
|          .w280 { | ||||
|             width: 280px !important; | ||||
|          } | ||||
|       } | ||||
|  | ||||
|    </style> | ||||
|    <style type="text/css" media="only screen and (max-width: 480px)"> | ||||
|       @media only screen and (max-width: 480px) { | ||||
|          .full { | ||||
|             display: block; | ||||
|             width: 100%; | ||||
|          } | ||||
|  | ||||
|          table[class*="w320"] { | ||||
|             width: 320px !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="w320"] { | ||||
|             width: 280px !important; | ||||
|             padding-left: 20px !important; | ||||
|             padding-right: 20px !important; | ||||
|          } | ||||
|  | ||||
|          img[class*="w320"] { | ||||
|             width: 250px !important; | ||||
|             height: 67px !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="mobile-spacing"] { | ||||
|             padding-top: 10px !important; | ||||
|             padding-bottom: 10px !important; | ||||
|          } | ||||
|  | ||||
|          *[class*="mobile-hide"] { | ||||
|             display: none !important; | ||||
|          } | ||||
|  | ||||
|          *[class*="mobile-br"] { | ||||
|             font-size: 8px !important; | ||||
|          } | ||||
|  | ||||
|          *[class*="mobile-brh"] { | ||||
|             font-size: 1px !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="mobile-w20"] { | ||||
|             width: 20px !important; | ||||
|          } | ||||
|  | ||||
|          img[class*="mobile-w20"] { | ||||
|             width: 20px !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="mobile-center"] { | ||||
|             text-align: center !important; | ||||
|          } | ||||
|  | ||||
|          table[class*="w100p"] { | ||||
|             width: 100% !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="activate-now"] { | ||||
|             padding-right: 0 !important; | ||||
|             padding-top: 20px !important; | ||||
|          } | ||||
|  | ||||
|          td[class*="mobile-resize"] { | ||||
|             font-size: 22px !important; | ||||
|             padding-left: 15px !important; | ||||
|          } | ||||
|       } | ||||
|  | ||||
|    </style> | ||||
| </head> | ||||
| <body offset="0" class="body" style="padding:0; margin:0; display:block; background:#f3f4f4; -webkit-text-size-adjust:none; " bgcolor="#EEEEEE"> | ||||
| <table align="center" cellpadding="0" cellspacing="0" width="100%" height="100%"> | ||||
|    <tr> | ||||
|       <td align="center" valign="top" style="background-color:#f3f4f4; " width="100%"> | ||||
|          <table cellspacing="0" cellpadding="0" width="600" class="w320"> | ||||
|             <tr> | ||||
|                <td align="center" valign="top"> | ||||
|  | ||||
|                   <!--Snippet Block--> | ||||
|                   <table border="0" cellspacing="0" cellpadding="0" align="center" width="100%"> | ||||
|                      <tr> | ||||
|                         <td> | ||||
|                            <table border="0" cellspacing="0" cellpadding="10" width="100%" align="center"> | ||||
|                               <tbody> | ||||
|                               <tr> | ||||
|                                  <td align="left"> | ||||
|                                     <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0"> | ||||
|                                        <tr> | ||||
|                                           <td align="right"> | ||||
|                                              <div style="display:none;font-size:1px;color:#333333;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;"> | ||||
|                                                 - | ||||
|                                              </div> | ||||
|                                           </td> | ||||
|                                        </tr> | ||||
|                                     </table> | ||||
|                                  </td> | ||||
|                               </tr> | ||||
|                               </tbody> | ||||
|                            </table> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Header Block--> | ||||
|                   <table bgcolor="#FFFFFF" border="0" cellspacing="0" cellpadding="0" align="center" width="100%" | ||||
|                          style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; border-bottom:solid 1px #dedee4; border-top:solid 1px #dedee4; width:100%!important; min-width:100%;"> | ||||
|                      <tr style="width:100%;"> | ||||
|                         <td> | ||||
|                            <table border="0" cellspacing="0" cellpadding="10" align="center" width="100%"> | ||||
|                               <tr> | ||||
|                                  <td align="left" valign="middle"><a style="color: #ffffff; text-decoration: none;" href= | ||||
|                                     "http://go.alfresco.com/a0100K050L0000KZjI2d00U" target="_blank" | ||||
|                                     ><span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #727174;"><img | ||||
|                                                   style="border:none; display:block; border-collapse:collapse; outline:none; text-decoration:none;" | ||||
|                                                   src="${template_assets_url}/hyland_logo.png" border="0" | ||||
|                                                   alt="Alfresco" width="122" height="38"></span></a></td> | ||||
|                               </tr> | ||||
|                            </table> | ||||
|                         </td> | ||||
|                         <td align="right" bgcolor="#FFFFFF"> | ||||
|                            <table border="0" cellspacing="0" cellpadding="10" align="center" width="100%"> | ||||
|                               <tr> | ||||
|                                  <td align="right" valign="middle"><span | ||||
|                                             style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; font-weight:200; color: #727174;">Digital Workspace</span> | ||||
|                                  </td> | ||||
|                               </tr> | ||||
|                            </table> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Break--> | ||||
|                   <table width="100%"> | ||||
|                      <tr> | ||||
|                         <td height="10"> | ||||
|                            <div class="mobile-br"> </div> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Body Block 1--> | ||||
|                   <!--Banner Block--> | ||||
|                   <table cellspacing="0" cellpadding="0" width="100%" style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; "> | ||||
|                      <tr> | ||||
|                         <td style="border-collapse:collapse; "> | ||||
|                            <table cellspacing="0" cellpadding="0" width="100%" style="background-color:#0c79bf; border:none; "> | ||||
|                               <tr> | ||||
|                                  <td style="border-collapse:collapse; "> | ||||
|                                     <div class="mktEditable" id="Banner Image 1"><span | ||||
|                                                style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #ffffff;"><img | ||||
|                                                   src="${template_assets_url}/adw_logo.png" alt="Alfresco Products" style="width:100%; border:none; | ||||
| display:block; border-collapse:collapse; outline:none; text-decoration:none;"></span></div> | ||||
|                                  </td> | ||||
|                               </tr> | ||||
|                            </table> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|                   <!--Copy--> | ||||
|                   <table cellspacing="0" cellpadding="0" align="center" bgcolor="#FFFFFF" | ||||
|                          style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; border-bottom:solid 1px #dedee4; " width="100%"> | ||||
|                      <tr align="center"> | ||||
|                         <td style="background-color:#ffffff; " align="center"> | ||||
|                            <table class="force-width-80" cellspacing="0" cellpadding="12" align="center" bgcolor="#FFFFFF" width="80%"> | ||||
|                               <tbody> | ||||
|                               <tr> | ||||
|                                  <td align="left"> | ||||
|                                     <!--Headline Text--> | ||||
|                                     <div class="mktEditable" id="Headline Text 1" align="left" style="vertical-align:middle; "><span | ||||
|                                                style="color: #727174; margin: 0px; font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 22px; font-weight: 600; text-align: left; text-decoration: none; vertical-align: | ||||
| middle;"><p>${message("templates.reset-password-email.ftl.title")}</p></span></div> | ||||
|                                     <div class="mktEditable" id="Body Text 1" align="left"> | ||||
| <span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:18px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;"> | ||||
| <p>${message("templates.reset-password-email.ftl.detail")}</p> | ||||
| </span> | ||||
|  | ||||
|                                     </div> | ||||
|  | ||||
|                                     <!--Break--> | ||||
|                                     <table width="100%"> | ||||
|                                        <tr> | ||||
|                                           <td height="5"> | ||||
|                                              <div class="mobile-br"> </div> | ||||
|                                           </td> | ||||
|                                        </tr> | ||||
|                                     </table> | ||||
|  | ||||
|                                     <!--Button--> | ||||
|                                     <table style="margin:0 auto; " cellspacing="0" cellpadding="0" width="100%"> | ||||
|                                        <tr> | ||||
|                                           <td style="text-align:center; margin:0 auto; "> | ||||
|                                              <div><!--[if mso]> | ||||
|                                                 <v:rect xmlns:v="urn:schemas-microsoft-com:vml" | ||||
|                                                         xmlns:w="urn:schemas-microsoft-com:office:word" | ||||
|                                                         style="height:45px;v-text-anchor:middle;width:220px;" stroke="f" | ||||
|                                                         fillcolor="#47AA2"> | ||||
|                                                    <w:anchorlock/> | ||||
|                                                    <center> | ||||
|                                                 <![endif]--> | ||||
|                                                 <div class="mktEditable" id="Button Text"><a href="${reset_password_url}" style="background-color:#47aa42; color:#ffffff; display:inline-block; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:18px; font-weight:400; line-height:45px; text-align:center; text-decoration:none; width:220px; | ||||
| -webkit-text-size-adjust:none;">${message("templates.reset-password-email.ftl.reset_password_button")}</a></div> | ||||
|                                                 <span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:12px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;"> | ||||
| <p>${message("templates.reset-password-email.ftl.ignore_message")}</p> | ||||
| <span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:12px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;"> | ||||
| <hr> | ||||
| <p>${message("templates.reset-password-email.ftl.having_trouble_clicking_button")}</p> | ||||
| <p><a href="${reset_password_url}">${reset_password_url}</a></p> | ||||
| </span> | ||||
|                                                    <!--[if mso]> | ||||
|                                                    </center> | ||||
|                                                    </v:rect> | ||||
|                                                    <![endif]--></div> | ||||
|                                           </td> | ||||
|                                        </tr> | ||||
|                                     </table> | ||||
|  | ||||
|                                     <!--Break--> | ||||
|                                     <table width="100%"> | ||||
|                                        <tr> | ||||
|                                           <td height="5"> | ||||
|                                              <div class="mobile-br"> </div> | ||||
|                                           </td> | ||||
|                                        </tr> | ||||
|                                     </table> | ||||
|  | ||||
|                                  </td> | ||||
|                               </tr> | ||||
|                               </tbody> | ||||
|                            </table> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Break--> | ||||
|                   <table width="100%"> | ||||
|                      <tr> | ||||
|                         <td height="10"> | ||||
|                            <div class="mobile-br"> </div> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Footer Block--> | ||||
|                   <table border="0" cellspacing="0" cellpadding="0" align="center" width="100%"> | ||||
|                      <tr> | ||||
|                         <td align="center"> | ||||
|                            <a style="color: #0c79bf; text-decoration: underline;" href="http://www.alfresco.com/company/contact" | ||||
|                               target="_blank"><span | ||||
|                                       style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #0c79bf;">${message("templates.generic-email.ftl.contact_us")}</span></a> | ||||
|                            <span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, | ||||
| sans-serif; font-size: 10px; color: #b3b3b8;">© ${date?string["yyyy"]} Alfresco Software, Inc. | ||||
|                                     ${message("templates.generic-email.ftl.copy_right")}</span><br> | ||||
|                            <span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #b3b3b8;">Bridge Ave, The Place Maidenhead SL6 1AF United Kingdom</span><br> | ||||
|                            <span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #b3b3b8;">1825 S Grant St, Suite 900 San Mateo, CA 94402 USA</span><br> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                   <!--Break--> | ||||
|                   <table width="100%"> | ||||
|                      <tr> | ||||
|                         <td height="10"> | ||||
|                            <div class="mobile-br"> </div> | ||||
|                         </td> | ||||
|                      </tr> | ||||
|                   </table> | ||||
|  | ||||
|                </td> | ||||
|             </tr> | ||||
|          </table> | ||||
|       </td> | ||||
|    </tr> | ||||
| </table> | ||||
| <!--Litmus Tracking--> | ||||
| <table style="display: none;"> | ||||
|    <tbody> | ||||
|    <tr> | ||||
|       <td style="width: 0px; display: none; overflow: hidden; max-height: 0px;">{{my.Litmus_Code}}</td> | ||||
|    </tr> | ||||
|    </tbody> | ||||
| </table> | ||||
| </body> | ||||
| </html> | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 2.3 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 7.0 KiB | 
| @@ -30,6 +30,7 @@ import static org.junit.Assert.assertNotEquals; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.InputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| @@ -419,6 +420,65 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati | ||||
|         assertTrue("New rendition content hash code was not generated", isValidRenditionContentHashCode(contentHashCode3)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testModifierAfterMultipleRenditionRequests() throws InterruptedException | ||||
|     { | ||||
|         String user1 = createRandomUser(); | ||||
|         String user2 = createRandomUser(); | ||||
|         List<NodeRef> nodes = new ArrayList<NodeRef>(); | ||||
|  | ||||
|         final int TOTAL_NODES = 10; | ||||
|  | ||||
|         // Create nodes | ||||
|         for (int i = 0; i < TOTAL_NODES; i++) | ||||
|         { | ||||
|             NodeRef n = createSource(user1, "quick.jpg"); | ||||
|             render(user1, n, DOC_LIB); | ||||
|             nodes.add(n); | ||||
|         } | ||||
|  | ||||
|         // Ask rendition multiple times with 'user2' | ||||
|         for (int j = 0; j < 10; j++) | ||||
|         { | ||||
|             for (int i = 0; i < TOTAL_NODES; i++) | ||||
|             { | ||||
|                 render(user2, nodes.get(i), DOC_LIB); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (int i = 0; i < TOTAL_NODES; i++) | ||||
|         { | ||||
|             waitForRendition(user1, nodes.get(i), DOC_LIB, true); | ||||
|         } | ||||
|  | ||||
|         // Check the modifier is still user1 | ||||
|         assertEquals(TOTAL_NODES, countModifier(nodes, user1)); | ||||
|     } | ||||
|  | ||||
|     private int countModifier(List<NodeRef> nodes, String user) | ||||
|     { | ||||
|         int count = 0; | ||||
|         for (int i = 0; i < nodes.size(); i++) | ||||
|         { | ||||
|             count += compareModifier(nodes.get(i), user); | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     private int compareModifier(NodeRef nodeRef, String user) | ||||
|     { | ||||
|         int res = 0; | ||||
|         if (nodeRef != null && user != null) | ||||
|         { | ||||
|             String modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER).toString(); | ||||
|             if (user.equals(modifier)) | ||||
|             { | ||||
|                 res = 1; | ||||
|             } | ||||
|         } | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @deprecated can be removed when we remove the original RenditionService | ||||
|      */ | ||||
| @@ -622,4 +682,4 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati | ||||
|             renditionService2.setEnabled(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -91,7 +91,6 @@ public class ResetPasswordServiceImplTest | ||||
|  | ||||
|     private static TestPerson testPerson; | ||||
|     private static EmailUtil emailUtil; | ||||
|     private static TestPerson testPersonForWorkspace; | ||||
|  | ||||
|     @BeforeClass | ||||
|     public static void initStaticData() throws Exception | ||||
| @@ -115,18 +114,9 @@ public class ResetPasswordServiceImplTest | ||||
|                     .setPassword("password") | ||||
|                     .setEmail(userName + "@example.com"); | ||||
|  | ||||
|         String userNameForWorkspace = "shane.doe" + System.currentTimeMillis(); | ||||
|         testPersonForWorkspace = new TestPerson() | ||||
|                 .setUserName(userNameForWorkspace) | ||||
|                 .setFirstName("Shane") | ||||
|                 .setLastName("doe") | ||||
|                 .setPassword("password") | ||||
|                 .setEmail(userNameForWorkspace + "@example.com"); | ||||
|  | ||||
|         transactionHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> | ||||
|         { | ||||
|             createUser(testPerson); | ||||
|             createUser(testPersonForWorkspace); | ||||
|             return null; | ||||
|         }); | ||||
|  | ||||
| @@ -504,84 +494,4 @@ public class ResetPasswordServiceImplTest | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testResetPasswordForClientWorkspace() throws Exception | ||||
|     { | ||||
|         // Try the credential before change of password | ||||
|         authenticateUser(testPersonForWorkspace.userName, testPersonForWorkspace.password); | ||||
|  | ||||
|         // Make sure to run as system | ||||
|         AuthenticationUtil.clearCurrentSecurityContext(); | ||||
|         AuthenticationUtil.setRunAsUserSystem(); | ||||
|  | ||||
|         // Request password reset | ||||
|         resetPasswordService.requestReset(testPersonForWorkspace.userName, "workspace"); | ||||
|         assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount()); | ||||
|         // Check the email | ||||
|         MimeMessage msg = emailUtil.getLastEmail(); | ||||
|         assertNotNull("There should be an email.", msg); | ||||
|         assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length); | ||||
|         // Check the recipient is the person who requested the reset password | ||||
|         assertEquals(testPersonForWorkspace.email, msg.getAllRecipients()[0].toString()); | ||||
|         //Check the sender is what we set as default | ||||
|         assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString()); | ||||
|         // There should be a subject | ||||
|         assertNotNull("There should be a subject.", msg.getSubject()); | ||||
|         // Check the default email subject - (check that we are sending the right email) | ||||
|         String emailSubjectKey = getDeclaredField(SendResetPasswordEmailDelegate.class, "EMAIL_SUBJECT_KEY"); | ||||
|         assertNotNull(emailSubjectKey); | ||||
|         assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey)); | ||||
|  | ||||
|         // Check the reset password url. | ||||
|         String resetPasswordUrl = (String) emailUtil.getLastEmailTemplateModelValue("reset_password_url"); | ||||
|         assertNotNull("Wrong email is sent.", resetPasswordUrl); | ||||
|         // Get the workflow id and key | ||||
|         Pair<String, String> pair = getWorkflowIdAndKeyFromUrl(resetPasswordUrl); | ||||
|         assertNotNull("Workflow Id can't be null.", pair.getFirst()); | ||||
|         assertNotNull("Workflow Key can't be null.", pair.getSecond()); | ||||
|  | ||||
|         emailUtil.reset(); | ||||
|         // Now that we have got the email, try to reset the password | ||||
|         ResetPasswordDetails passwordDetails = new ResetPasswordDetails() | ||||
|                 .setUserId(testPersonForWorkspace.userName) | ||||
|                 .setPassword("newPassword") | ||||
|                 .setWorkflowId(pair.getFirst()) | ||||
|                 .setWorkflowKey(pair.getSecond()); | ||||
|  | ||||
|         resetPasswordService.initiateResetPassword(passwordDetails); | ||||
|         assertEquals("A reset password confirmation email should have been sent.", 1, emailUtil.getSentCount()); | ||||
|         // Check the email | ||||
|         msg = emailUtil.getLastEmail(); | ||||
|         assertNotNull("There should be an email.", msg); | ||||
|         assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length); | ||||
|         // Check the recipient is the person who requested the reset password | ||||
|         assertEquals(testPersonForWorkspace.email, msg.getAllRecipients()[0].toString()); | ||||
|         // Check the sender is what we set as default | ||||
|         assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString()); | ||||
|         // There should be a subject | ||||
|         assertNotNull("There should be a subject.", msg.getSubject()); | ||||
|         // Check the default email subject - (check that we are sending the right email) | ||||
|         emailSubjectKey = getDeclaredField(SendResetPasswordConfirmationEmailDelegate.class, "EMAIL_SUBJECT_KEY"); | ||||
|         assertNotNull(emailSubjectKey); | ||||
|         assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey)); | ||||
|  | ||||
|         // Try the old credential | ||||
|         TestHelper.assertThrows(() -> authenticateUser(testPersonForWorkspace.userName, testPersonForWorkspace.password), | ||||
|                 AuthenticationException.class, | ||||
|                 "As the user changed her password, the authentication should have failed."); | ||||
|  | ||||
|         // Try the new credential | ||||
|         authenticateUser(testPersonForWorkspace.userName, "newPassword"); | ||||
|  | ||||
|         // Make sure to run as system | ||||
|         AuthenticationUtil.clearCurrentSecurityContext(); | ||||
|         AuthenticationUtil.setRunAsUserSystem(); | ||||
|         emailUtil.reset(); | ||||
|         // Try reset again with the used workflow | ||||
|         TestHelper.assertThrows(() -> resetPasswordService.initiateResetPassword(passwordDetails), | ||||
|                 InvalidResetPasswordWorkflowException.class, | ||||
|                 "The workflow instance is not active (it has already been used)."); | ||||
|         assertEquals("No email should have been sent.", 0, emailUtil.getSentCount()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -34,7 +34,7 @@ services: | ||||
|     ports: | ||||
|       - "3307:3306" | ||||
|   activemq: | ||||
|     image: alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8 | ||||
|     image: alfresco/alfresco-activemq:5.17.4-jre17-rockylinux8 | ||||
|     ports: | ||||
|       - "5672:5672" # AMQP | ||||
|       - "61616:61616" # OpenWire | ||||
| @@ -20,7 +20,7 @@ services: | ||||
|       - "5433:5432" | ||||
|   activemq: | ||||
|     profiles: ["default", "with-transform-core-aio", "activemq", "with-mtls-transform-core-aio"] | ||||
|     image: alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8 | ||||
|     image: alfresco/alfresco-activemq:5.17.4-jre17-rockylinux8 | ||||
|     ports: | ||||
|       - "5672:5672" # AMQP | ||||
|       - "61616:61616" # OpenWire | ||||
| @@ -31,8 +31,8 @@ services: | ||||
|     ports: | ||||
|       - 8090:8090 | ||||
|     volumes: | ||||
|       - ${GITHUB_WORKSPACE}/keystores/tengineAIO/tengineAIO.truststore:/tengineAIO.truststore | ||||
|       - ${GITHUB_WORKSPACE}/keystores/tengineAIO/tengineAIO.keystore:/tengineAIO.keystore | ||||
|       - ${CI_WORKSPACE}/keystores/tengineAIO/tengineAIO.truststore:/tengineAIO.truststore | ||||
|       - ${CI_WORKSPACE}/keystores/tengineAIO/tengineAIO.keystore:/tengineAIO.keystore | ||||
|     environment: | ||||
|       ACTIVEMQ_URL: "nio://activemq:61616" | ||||
|       ACTIVEMQ_USER: "admin" | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| #! /bin/bash | ||||
| #! /bin/bash | ||||
|  | ||||
| # SETTINGS | ||||
| # Alfresco Format: "classic" / "current" is supported only from 7.0 | ||||
| ALFRESCO_FORMAT=current | ||||
|  | ||||
| #Contains directory settings | ||||
| source ${GITHUB_WORKSPACE}/alfresco-ssl-generator/ssl-tool/utils.sh | ||||
|  | ||||
| # Cleanup previous output of script | ||||
| rm -rd $CA_DIR | ||||
| rm -rd $KEYSTORES_DIR | ||||
| rm -rd $CERTIFICATES_DIR | ||||
|  | ||||
| # SETTINGS | ||||
| # Alfresco Format: "classic" / "current" is supported only from 7.0 | ||||
| ALFRESCO_FORMAT=current | ||||
|  | ||||
| #CA | ||||
| ${GITHUB_WORKSPACE}/alfresco-ssl-generator/ssl-tool/run_ca.sh -keysize 2048 -keystorepass password -certdname "/C=GB/ST=UK/L=Maidenhead/O=Alfresco Software Ltd./OU=Unknown/CN=Custom Alfresco CA" -servername localhost -validityduration 1 | ||||
| #Alfresco | ||||
| ${GITHUB_WORKSPACE}/alfresco-ssl-generator/ssl-tool/run_additional.sh -servicename alfresco -rootcapass password -keysize 2048 -keystoretype JCEKS -keystorepass password -truststoretype JCEKS -truststorepass password -certdname "/C=GB/ST=UK/L=Maidenhead/O=Alfresco Software Ltd./OU=Unknown/CN=Custom Alfresco Repository" -servername localhost -alfrescoformat $ALFRESCO_FORMAT | ||||
| #Alfresco Metadata encryption | ||||
| ${GITHUB_WORKSPACE}/alfresco-ssl-generator/ssl-tool/run_encryption.sh -subfoldername alfresco -servicename encryption -encstorepass mp6yc0UD9e -encmetadatapass oKIWzVdEdA -alfrescoformat $ALFRESCO_FORMAT | ||||
| #T-Engine AIO | ||||
| ${GITHUB_WORKSPACE}/alfresco-ssl-generator/ssl-tool/run_additional.sh -servicename tengineAIO -rootcapass password -keysize 2048 -keystoretype JCEKS -keystorepass password -truststoretype JCEKS -truststorepass password -certdname "/C=GB/ST=UK/L=Maidenhead/O=Alfresco Software Ltd./OU=Unknown/CN=T-Engine AIO" -servername localhost -alfrescoformat $ALFRESCO_FORMAT | ||||
| @@ -35,10 +35,10 @@ git add pom.xml | ||||
|  | ||||
| if [[ "${COMMIT_MESSAGE}" =~ \[force[^\]]*\] ]]; then | ||||
|   FORCE_TOKEN=$(echo "${COMMIT_MESSAGE}" | sed "s|^.*\(\[force[^]]*\]\).*$|\1|g") | ||||
|   git commit --allow-empty -m "${FORCE_TOKEN} Update upstream version to ${VERSION}" | ||||
|   git commit --allow-empty -m "${FORCE_TOKEN} Update community-repo version to ${VERSION}" | ||||
|   git push | ||||
| elif git status --untracked-files=no --porcelain | grep -q '^' ; then | ||||
|   git commit -m "Update upstream version to ${VERSION}" | ||||
|   git commit -m "Update community-repo version to ${VERSION}" | ||||
|   git push | ||||
| else | ||||
|   echo "Dependencies are already up to date." | ||||
|   | ||||
		Reference in New Issue
	
	Block a user