Compare commits

...

37 Commits

Author SHA1 Message Date
alfresco-build
576b6faac9 [maven-release-plugin][skip ci] prepare release 23.5.0.17 2025-05-23 12:25:03 +00:00
varapathijanakiram
d86415401d Backported the MNT-24776 (#3364) 2025-05-23 17:11:13 +05:30
alfresco-build
460cc1f2cd [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-21 11:36:58 +00:00
alfresco-build
370fef10fd [maven-release-plugin][skip ci] prepare release 23.5.0.16 2025-05-21 11:36:56 +00:00
varapathijanakiram
efadc239d4 Merge pull request #3360 from Alfresco/fix/MNT-24172
[fix/MNT-24172-fixRecreationOfRendition2] Backport Fix of recreation of rendition2
2025-05-21 16:25:03 +05:30
alfresco-build
de90e37578 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-20 11:27:29 +00:00
alfresco-build
6e438d2e4f [maven-release-plugin][skip ci] prepare release 23.5.0.15 2025-05-20 11:27:26 +00:00
SatyamSah5
a86fa21880 [ACS-9572] Show proper message for duplicate unzipping (#3356) 2025-05-20 15:15:42 +05:30
DurgDineshsai
db74a6e7f2 Merge pull request #3357 from Alfresco/fix/MNT-24146
Backport MNT 24146 changes
2025-05-20 11:49:28 +05:30
rrajoria
8c773ac97c fix precommit 2025-05-20 10:53:27 +05:30
vjanakiram
5a2b3cf64d [fix/MNT-24172-fixRecreationOfRendition2] Backport fix for recreation of rendition2 2025-05-19 19:45:06 +05:30
rrajoria
8ab910d2b1 Fix PreCommit 2025-05-19 12:25:24 +05:30
alfresco-build
fa70f1cd45 [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-19 05:57:38 +00:00
alfresco-build
75a2e0f901 [maven-release-plugin][skip ci] prepare release 23.5.0.14 2025-05-19 05:57:36 +00:00
bsayan2
84997bcf86 MNT-25069 help documentation url updated. backport to 23.N (#3343) 2025-05-19 10:40:00 +05:30
alfresco-build
536b3ddd6d [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 15:13:54 +00:00
alfresco-build
bb86c97b11 [maven-release-plugin][skip ci] prepare release 23.5.0.13 2025-05-16 15:13:52 +00:00
Gerard Olenski
558f117f24 ACS-9578 Improve stability in AddToHoldsBulkV1Tests (#3355)
(cherry picked from commit d163410e3d)
2025-05-16 16:31:44 +02:00
alfresco-build
bbae71658d [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 13:04:38 +00:00
alfresco-build
9ee9653463 [maven-release-plugin][skip ci] prepare release 23.5.0.12 2025-05-16 13:04:36 +00:00
Sara
44570cec8a Merge pull request #3353 from Alfresco/feature/ACS-9642_Prep_23-N_for_ACS-23-5
ACS-9642 Prepare 23.N forACS-23-5
2025-05-16 12:37:13 +01:00
alfresco-build
33eb0354fa [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 10:51:22 +00:00
alfresco-build
0d3e2dc8bb [maven-release-plugin][skip ci] prepare release 23.5.0.11 2025-05-16 10:51:20 +00:00
Gerard Olenski
ac62c52a33 ACS-9652 [Es Connector] Prepare ACS 23.N for release (with ES Connector 5.1.0) (#3354)
Co-authored-by: Belal Ansari <belal.ansari@hyland.com>
2025-05-16 12:05:27 +02:00
Sara Aspery
0d30d40d8f Merge branch 'refs/heads/release/23.N' into feature/ACS-9642_Prep_23-N_for_ACS-23-5 2025-05-16 10:16:29 +01:00
alfresco-build
87e365df7e [maven-release-plugin][skip ci] prepare for next development iteration 2025-05-16 09:02:12 +00:00
Tiago Salvado
f6a12760c9 [MNT-24992] Add method to force renditions content hash code (#3288) (#3291) 2025-05-15 22:48:15 +01:00
tathagta15
560a050af3 Bump org.springframework.security:spring-security to 6.3.8 (#3322) 2025-05-15 18:59:55 +01:00
cezary-witkowski
5ed82930d2 [MNT-24937] Fix EventTableOutbox messages flooding the logs on bootstrap failure (#3286) (#3297)
Signed-off-by: cezary-witkowski <cezary.witkowski@hyland.com>
2025-05-15 18:55:08 +01:00
Belal Ansari
f8a32022c3 PRODSEC-10029 - to bump apache camel and netty to latest safe version along with roundRobin Load Balancer upgrade 2025-05-15 18:51:50 +01:00
Belal Ansari
137df0ff4c PRODSEC-10029 - to bumping apache camel and netty to latest safe compatible version 2025-05-15 18:51:50 +01:00
Kacper Magdziarz
23bd8c064c [PRODSEC-10013] Bump JSON Smart to 2.5.2 2025-05-15 18:02:22 +01:00
purusothaman-mm
e3384eaee4 [PRODSEC-9888] Fix for cxf-core and netty-common (#3217) 2025-05-15 18:00:54 +01:00
Kacper Magdziarz
c7e5716a4a [ACS-9490] Backport: Use FixedThreadPool for ExecutorService (#3301) (#3302) 2025-05-15 17:45:34 +01:00
Belal Ansari
7fa0d30100 Fix/MNT-24891 backport to 23.3 (#3312)
Co-authored-by: Kacper Magdziarz <kacper.magdziarz@hyland.com>
2025-05-15 17:44:02 +01:00
DurgDineshsai
71849cd4ac MNT-24146 -backport fix & precommit changes 2025-05-15 17:30:20 +05:30
DurgDineshsai
686ffcb19c MNT-24146 backport fix 2025-05-15 15:04:46 +05:30
90 changed files with 5111 additions and 4840 deletions

View File

@@ -1539,7 +1539,7 @@
"filename": "repository/src/test/java/org/alfresco/repo/rendition2/AbstractRenditionIntegrationTest.java",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
"line_number": 127,
"line_number": 130,
"is_secret": false
}
],
@@ -1888,5 +1888,5 @@
}
]
},
"generated_at": "2025-05-14T14:31:49Z"
"generated_at": "2025-05-15T21:47:13Z"
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<build>

View File

@@ -26,13 +26,6 @@
*/
package org.alfresco.rest.rm.community.hold;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -44,12 +37,25 @@ import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.dataprep.ContentActions;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
@@ -71,10 +77,6 @@ import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* API tests for adding items to holds via the bulk process
@@ -82,7 +84,7 @@ import org.testng.annotations.Test;
public class AddToHoldsBulkV1Tests extends BaseRMRestTest
{
private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied. You do not have the appropriate " +
"permissions to perform this operation.";
"permissions to perform this operation.";
private static final int NUMBER_OF_FILES = 5;
private final List<FileModel> addedFiles = new ArrayList<>();
private final List<UserModel> users = new ArrayList<>();
@@ -102,8 +104,9 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
{
STEP("Create a hold.");
hold = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold);
STEP("Create test files.");
@@ -117,8 +120,8 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
for (int i = 0; i < NUMBER_OF_FILES; i++)
{
FileModel documentHeld = dataContent.usingAdmin()
.usingResource(i % 2 == 0 ? folder1 : folder2)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
.usingResource(i % 2 == 0 ? folder1 : folder2)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
addedFiles.add(documentHeld);
}
@@ -128,29 +131,37 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are searchable.");
await().atMost(30, TimeUnit.SECONDS)
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
.until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
RestRequestQueryModel ancestorReq = getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion());
SearchRequest ancestorSearchRequest = new SearchRequest();
ancestorSearchRequest.setQuery(ancestorReq);
STEP("Wait until paths are indexed.");
// to improve stability on CI - seems that sometimes during big load we need to wait longer for the condition
await().atMost(120, TimeUnit.SECONDS)
.until(() -> getRestAPIFactory().getSearchAPI(null).search(ancestorSearchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
holdBulkOperation = HoldBulkOperation.builder()
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* Then the content is added to the hold and the status of the bulk operation is DONE
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API Then the content is added to the hold and the status of the bulk operation is DONE
*/
@Test
public void addContentFromTestSiteToHoldUsingBulkAPI()
{
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -158,50 +169,49 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(20, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, holdBulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold.getId());
.getBulkStatuses(hold.getId());
assertEquals(Arrays.asList(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a folder and all subfolders to a hold using the bulk API
* Then the content is added to the hold and the status of the bulk operation is DONE
* Given a user with the add to hold capability and hold filing permission When the user adds content from a folder and all subfolders to a hold using the bulk API Then the content is added to the hold and the status of the bulk operation is DONE
*/
@Test
public void addContentFromFolderAndAllSubfoldersToHoldUsingBulkAPI()
{
hold3 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold3);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
// Get content from folder and all subfolders of the root folder
HoldBulkOperation bulkOperation = HoldBulkOperation.builder()
.query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion()))
.op(HoldBulkOperationType.ADD).build();
.query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion()))
.op(HoldBulkOperationType.ADD).build();
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(bulkOperation, hold3.getId());
.startBulkProcess(bulkOperation, hold3.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -209,43 +219,40 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(20, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, bulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold3.getId());
.getBulkStatuses(hold3.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
* Given a user without the add to hold capability
* When the user adds content from a site to a hold using the bulk API
* Then the user receives access denied error
* Given a user without the add to hold capability When the user adds content from a site to a hold using the bulk API Then the user receives access denied error
*/
@Test
public void testBulkProcessWithUserWithoutAddToHoldCapability()
{
UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole
.SiteCollaborator,
hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
UserRole.SiteCollaborator,
hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
users.add(userWithoutAddToHoldCapability);
STEP("Add content from the site to the hold using the bulk API.");
getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -253,21 +260,19 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user without the filing permission on a hold
* When the user adds content from a site to a hold using the bulk API
* Then the user receives access denied error
* Given a user without the filing permission on a hold When the user adds content from a site to a hold using the bulk API Then the user receives access denied error
*/
@Test
public void testBulkProcessWithUserWithoutFilingPermissionOnAHold()
{
// User without filing permission on a hold
UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS);
UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS);
users.add(userWithoutPermission);
STEP("Add content from the site to the hold using the bulk API.");
getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -276,68 +281,63 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user without the write permission on all the content
* When the user adds content from a site to a hold using the bulk API
* Then all processed items are marked as errors and the last error message contains access denied error
* Given a user without the write permission on all the content When the user adds content from a site to a hold using the bulk API Then all processed items are marked as errors and the last error message contains access denied error
*/
@Test
public void testBulkProcessWithUserWithoutWritePermissionOnTheContent()
{
// User without write permission on the content
UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(
testSite, UserRole.SiteConsumer,
hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
testSite, UserRole.SiteConsumer,
hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userWithoutPermission);
// Wait until permissions are reverted
SearchRequest searchRequest = new SearchRequest();
searchRequest.setQuery(holdBulkOperation.getQuery());
await().atMost(30, TimeUnit.SECONDS)
.until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
.until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination()
.getTotalItems() == NUMBER_OF_FILES);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(
userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId());
userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response.");
assertStatusCode(ACCEPTED);
await().atMost(20, TimeUnit.SECONDS).until(() ->
Objects.equals(getRestAPIFactory().getHoldsAPI(userWithoutPermission)
await().atMost(20, TimeUnit.SECONDS).until(() -> Objects.equals(getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus(), "DONE"));
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE,
holdBulkOperation);
holdBulkOperation);
}
/**
* Given a user without the write permission on one file
* When the user adds content from a site to a hold using the bulk API
* Then all processed items are added to the hold except the one that the user does not have write permission
* And the status of the bulk operation is DONE, contains the error message and the number of errors is 1
* Given a user without the write permission on one file When the user adds content from a site to a hold using the bulk API Then all processed items are added to the hold except the one that the user does not have write permission And the status of the bulk operation is DONE, contains the error message and the number of errors is 1
*/
@Test
public void testBulkProcessWithUserWithoutWritePermissionOnOneFile()
{
hold2 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold2);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(),
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteConsumer.getRoleId(), false);
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteConsumer.getRoleId(), false);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold2.getId());
.startBulkProcess(holdBulkOperation, hold2.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -345,56 +345,50 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Wait until all files are added to the hold.");
await().atMost(30, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size()
== NUMBER_OF_FILES - 1);
() -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size() == NUMBER_OF_FILES - 1);
await().atMost(30, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems()
== NUMBER_OF_FILES);
() -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
.getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId)
.toList();
assertEquals(addedFiles.stream().skip(1).map(FileModel::getNodeRefWithoutVersion).sorted().toList(),
holdChildrenNodeRefs.stream().sorted().toList());
holdChildrenNodeRefs.stream().sorted().toList());
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId());
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE, holdBulkOperation);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold2.getId());
.getBulkStatuses(hold2.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
// Revert the permissions
contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(),
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteCollaborator.getRoleId(), true);
testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(),
UserRole.SiteCollaborator.getRoleId(), true);
}
/**
* Given an unauthenticated user
* When the user adds content from a site to a hold using the bulk API
* Then the user receives unauthorized error
* Given an unauthenticated user When the user adds content from a site to a hold using the bulk API Then the user receives unauthorized error
*/
@Test
public void testBulkProcessAsUnauthenticatedUser()
{
STEP("Start bulk process as unauthenticated user");
getRestAPIFactory().getHoldsAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword"))
.startBulkProcess(holdBulkOperation, hold.getId());
.startBulkProcess(holdBulkOperation, hold.getId());
STEP("Verify the response status code.");
assertStatusCode(UNAUTHORIZED);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testBulkProcessForNonExistentHold()
@@ -407,10 +401,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* and the bulk operation is invalid
* Then the user receives bad request error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API and the bulk operation is invalid Then the user receives bad request error
*/
@Test
public void testGetBulkStatusesForInvalidOperation()
@@ -418,7 +409,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Start bulk process for non existent hold");
HoldBulkOperation invalidHoldBulkOperation = HoldBulkOperation.builder().op(null)
.query(holdBulkOperation.getQuery()).build();
.query(holdBulkOperation.getQuery()).build();
getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(invalidHoldBulkOperation, hold.getId());
STEP("Verify the response status code.");
@@ -426,10 +417,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusForNonExistentHold()
@@ -442,10 +430,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the bulk status does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the bulk status does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusForNonExistentBulkStatus()
@@ -458,10 +443,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And the hold does not exist
* Then the user receives not found error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And the hold does not exist Then the user receives not found error
*/
@Test
public void testGetBulkStatusesForNonExistentHold()
@@ -474,9 +456,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items)
* Then the user receives bad request error
* Given a user with the add to hold capability and hold filing permission When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items) Then the user receives bad request error
*/
@Test
public void testExceedingBulkOperationLimit()
@@ -486,8 +466,8 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
queryReq.setLanguage("afts");
HoldBulkOperation exceedLimitOp = HoldBulkOperation.builder()
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
.query(queryReq)
.op(HoldBulkOperationType.ADD).build();
STEP("Start bulk process to exceed the limit");
getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(exceedLimitOp, hold.getId());
@@ -497,26 +477,24 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And then the user cancels the bulk operation
* Then the user receives OK status code
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And then the user cancels the bulk operation Then the user receives OK status code
*/
@Test
public void testBulkProcessCancellationWithAllowedUser()
{
Hold hold4 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold4);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold4.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold4.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold4.getId());
.startBulkProcess(holdBulkOperation, hold4.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
@@ -524,47 +502,44 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.cancelBulkOperation(hold4.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
.cancelBulkOperation(hold4.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
// Verify the status code
assertStatusCode(OK);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And a 2nd user without the add to hold capability cancels the bulk operation
* Then the 2nd user receives access denied error
* Given a user with the add to hold capability and hold filing permission When the user adds content from a site to a hold using the bulk API And a 2nd user without the add to hold capability cancels the bulk operation Then the 2nd user receives access denied error
*/
@Test
public void testBulkProcessCancellationWithUserWithoutAddToHoldCapability()
{
Hold hold5 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(),
FILE_PLAN_ALIAS);
holds.add(hold5);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold5.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
UserRole.SiteCollaborator, hold5.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold5.getId());
.startBulkProcess(holdBulkOperation, hold5.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems());
UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole
.SiteCollaborator,
hold5.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
UserRole.SiteCollaborator,
hold5.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
users.add(userWithoutAddToHoldCapability);
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability)
.cancelBulkOperation(hold5.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
.cancelBulkOperation(hold5.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
@@ -572,7 +547,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
}
private void assertBulkProcessStatus(HoldBulkStatus holdBulkStatus, long expectedProcessedItems,
int expectedErrorsCount, String expectedErrorMessage, HoldBulkOperation holdBulkOperation)
int expectedErrorsCount, String expectedErrorMessage, HoldBulkOperation holdBulkOperation)
{
assertEquals("DONE", holdBulkStatus.getStatus());
assertEquals(expectedProcessedItems, holdBulkStatus.getTotalItems());

View File

@@ -23,7 +23,7 @@ Recorded content can be explicitly destroyed whilst maintaining the original nod
* License: Alfresco Community
* Issue Tracker Link: [JIRA RM](https://issues.alfresco.com/jira/projects/RM/summary)
* Contribution Model: Alfresco Closed Source
* Documentation: [docs.alfresco.com (Records Management)](http://docs.alfresco.com/rm2.4/concepts/welcome-rm.html)
* Documentation: [docs.alfresco.com (Records Management)](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
***

View File

@@ -21,18 +21,18 @@ RM is split into two main parts - a repository integration and a Share integrati
* [Community License](../LICENSE.txt)
* [Enterprise License](../../rm-enterprise/LICENSE.txt) (this file will only be present in clones of the Enterprise repository)
* [Issue Tracker Link](https://issues.alfresco.com/jira/projects/RM)
* [Community Documentation Link](http://docs.alfresco.com/rm-community/concepts/welcome-rm.html)
* [Enterprise Documentation Link](http://docs.alfresco.com/rm/concepts/welcome-rm.html)
* [Community Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services-Community-Edition/23.4/Alfresco-Governance-Services-Community-Edition/Introduction)
* [Enterprise Documentation Link](https://support.hyland.com/r/Alfresco/Alfresco-Governance-Services/23.4/Alfresco-Governance-Services/Introduction)
* [Contribution Model](../../CONTRIBUTING.md)
***
### Prerequisite Knowledge
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](http://docs.alfresco.com/5.2/concepts/dev-for-developers.html) give useful background information:
An understanding of Alfresco Content Services is assumed. The following pages from the [developer documentation](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services-Community-Edition/23.4/Alfresco-Content-Services-Community-Edition/Develop) give useful background information:
* [ACS Architecture](http://docs.alfresco.com/5.2/concepts/dev-arch-overview.html)
* [Platform Extensions](http://docs.alfresco.com/5.2/concepts/dev-platform-extensions.html)
* [Share Extensions](http://docs.alfresco.com/5.2/concepts/dev-extensions-share.html)
* [ACS Architecture](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Software-Architecture)
* [Platform Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Extension-Points-Overview)
* [Share Extensions](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Share-UI-Extension-Points)
***
@@ -44,12 +44,12 @@ The RM Share module communicates with the repository module via REST APIs. Inter
* A DAO layer responsible for CRUD operations against the database.
#### REST API
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](http://docs.alfresco.com/5.2/references/dev-extension-points-webscripts.html) is older and not recommended for integrations. The [v1 API](http://docs.alfresco.com/5.1/pra/1/topics/pra-welcome-aara.html) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
The REST API endpoints fall into two main types - v0 (Webscripts) and v1. The [v0 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Web-Scripts) is older and not recommended for integrations. The [v1 API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide) is newer but isn't yet feature complete. If you are running RM locally then the GS API Explorer will be available at [this link](http://localhost:8080/gs-api-explorer/).
Internally the GS v1 REST API is built on the [Alfresco v1 REST API framework](https://community.alfresco.com/community/ecm/blog/2016/10/11/v1-rest-api-part-1-introduction). It aims to be consistent with this in terms of behaviour and naming.
#### Java Public API
The Java service layer is fronted by a [Java Public API](http://docs.alfresco.com/5.2/concepts/java-public-api-list.html), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method. The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
The Java service layer is fronted by a [Java Public API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/Java-Foundation-API), which we will ensure backward compatible with previous releases. Before we remove any methods there will first be a release containing that method deprecated to allow third party integrations to migrate to a new method. The Java Public API also includes a set of POJO objects which are needed to communicate with the services. It is easy to identify classes that are part of the Java Public API as they are annotated `@AlfrescoPublicApi`.
Each Java service will have at least four beans defined for it:
@@ -61,7 +61,7 @@ Each Java service will have at least four beans defined for it:
#### DAOs
The DAOs are not part of the Java Public API, but handle CRUD operations against RM stored data. We have some custom queries to improve performance for particularly heavy operations.
We use standard Alfresco [data modelling](http://docs.alfresco.com/5.2/references/dev-extension-points-content-model.html) to store RM metadata. We extend the [Alfresco patching mechanism](http://docs.alfresco.com/5.2/references/dev-extension-points-patch.html) to provide community and enterprise schema upgrades.
We use standard Alfresco [data modelling](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Content-Model-Extension-Point) to store RM metadata. We extend the [Alfresco patching mechanism](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/In-Process-Platform-Extension-Points/Patches) to provide community and enterprise schema upgrades.
***

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -80,6 +80,11 @@ function runAction(p_params)
{
result.fileExist = true;
}
if (error.indexOf("FolderExistsException") != -1)
{
result.fileExist = true;
result.type = "folder";
}
}
results.push(result);

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<dependencies>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<dependencies>

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<modules>

View File

@@ -27,7 +27,7 @@
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](http://docs.alfresco.com/5.1/pra/1/topics/cmis-welcome.html).
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/Reference/CMIS-API).
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<organization>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<developers>

View File

@@ -27,7 +27,7 @@ Back to [TAS Master Documentation](https://git.alfresco.com/tas/alfresco-tas-uti
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **RESTAPI** is the project that handles the automated tests related only to [Alfresco REST API](http://docs.alfresco.com/5.1/pra/1/topics/pra-welcome.html).
**TAS**( **T**est **A**utomation **S**ystem)- **RESTAPI** is the project that handles the automated tests related only to [Alfresco REST API](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide).
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.
@@ -271,7 +271,7 @@ restClient.onResponse().assertThat().body("entry.modifiedBy.firstName", org.hamc
### How to generate models or check coverage
There are some simple generators that could parse [Swagger YAML](http://docs.alfresco.com/community/concepts/alfresco-sdk-tutorials-using-rest-api-explorer.html) files and provide some usefull information to you like:
There are some simple generators that could parse [Swagger YAML](https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Develop/REST-API-Guide/Things-to-Know-Before-You-Start/The-API-Explorer-is-Your-Source-of-Truth) files and provide some usefull information to you like:
a) Show on screen the actual coverage of TAS vs requests that exists in each YAML file - defined in pom.xml)

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<properties>

View File

@@ -74,7 +74,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<div class="index-list">
<h4><%=descriptorService.getServerDescriptor().getEdition()%></h4>
<p></p>
<p><a href="http://docs.alfresco.com/">Online Documentation</a></p>
<p><a href="https://support.hyland.com/p/alfresco">Online Documentation</a></p>
<p></p>
<%
if (shareServicesModule != null && ModuleInstallState.INSTALLED.equals(shareServicesModule.getInstallState()))

14
pom.xml
View File

@@ -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>23.5.0.10</version>
<version>23.5.0.17</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -58,10 +58,10 @@
<dependency.aspectj.version>1.9.22.1</dependency.aspectj.version>
<dependency.spring.version>6.1.14</dependency.spring.version>
<dependency.spring-security.version>6.3.4</dependency.spring-security.version>
<dependency.spring-security.version>6.3.8</dependency.spring-security.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.jackson.version>2.17.2</dependency.jackson.version>
<dependency.cxf.version>4.0.5</dependency.cxf.version>
<dependency.cxf.version>4.1.0</dependency.cxf.version>
<dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
<dependency.webscripts.version>9.4</dependency.webscripts.version>
<dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version>
@@ -85,8 +85,8 @@
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>5.4.1</dependency.poi.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>4.6.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.113.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.camel.version>4.10.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.118.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.activemq.version>5.18.3</dependency.activemq.version>
<dependency.apache-compress.version>1.27.1</dependency.apache-compress.version>
<dependency.awaitility.version>4.2.2</dependency.awaitility.version>
@@ -111,7 +111,7 @@
<dependency.jakarta-ee-json-api.version>2.1.3</dependency.jakarta-ee-json-api.version>
<dependency.jakarta-ee-json-impl.version>1.1.7</dependency.jakarta-ee-json-impl.version>
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
<dependency.json-smart.version>2.5.2</dependency.json-smart.version>
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
<alfresco.aos-module.version>3.3.0-A1</alfresco.aos-module.version>
<alfresco.api-explorer.version>23.4.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
@@ -154,7 +154,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>23.5.0.10</tag>
<tag>23.5.0.17</tag>
</scm>
<distributionManagement>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<dependencies>

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin Console
admin-console.help=Help
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Successfully saved values.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Konzole pro spr\u00e1vce
admin-console.help=N\u00e1pov\u011bda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Hodnoty byly \u00fasp\u011b\u0161n\u011b ulo\u017eeny.
admin-console.host=Hostitel

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Administrationskonsol
admin-console.help=Hj\u00e6lp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=V\u00e6rdierne blev gemt.
admin-console.host=V\u00e6rt

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Administratorkonsole
admin-console.help=Hilfe
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Erfolgreich gespeicherte Werte.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Consola de administraci\u00f3n
admin-console.help=Ayuda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Valores guardados correctamente.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Hallintakonsoli
admin-console.help=Ohje
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Arvot tallennettiin.
admin-console.host=Is\u00e4nt\u00e4

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console d'administration
admin-console.help=Aide
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Les valeurs ont bien \u00e9t\u00e9 enregistr\u00e9es.
admin-console.host=H\u00f4te

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console di amministrazione
admin-console.help=Aiuto
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=I valori sono stati salvati.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u7ba1\u7406\u30b3\u30f3\u30bd\u30fc\u30eb
admin-console.help=\u30d8\u30eb\u30d7
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u5024\u3092\u6b63\u5e38\u306b\u4fdd\u5b58\u3057\u307e\u3057\u305f\u3002
admin-console.host=\u30db\u30b9\u30c8

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin-konsoll
admin-console.help=Hjelp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Verdier som ble lagret.
admin-console.host=Vert

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Beheerconsole
admin-console.help=Help
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Waarden zijn opgeslagen.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Konsola administracyjna
admin-console.help=Pomoc
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Warto\u015bci zosta\u0142y zapisane pomy\u015blnie.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Console de administra\u00e7\u00e3o
admin-console.help=Ajuda
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=Valores salvos com sucesso.
admin-console.host=Host

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u041a\u043e\u043d\u0441\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430
admin-console.help=\u0421\u043f\u0440\u0430\u0432\u043a\u0430
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.
admin-console.host=\u0425\u043e\u0441\u0442

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=Admin-konsol
admin-console.help=Hj\u00e4lp
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=V\u00e4rden sparades.
admin-console.host=V\u00e4rd

View File

@@ -1,7 +1,7 @@
# I18N messages for the Repository Admin Console
admin-console.header=\u7ba1\u7406\u63a7\u5236\u53f0
admin-console.help=\u5e2e\u52a9
admin-console.help-link=http://docs.alfresco.com/{0}/concepts/ch-administering.html
admin-console.help-link=https://support.hyland.com/p/alfresco
admin-console.success=\u5df2\u6210\u529f\u4fdd\u5b58\u7684\u503c\u3002
admin-console.host=\u4e3b\u673a

View File

@@ -12,9 +12,9 @@
<#macro page title readonly=false controller=DEFAULT_CONTROLLER!"/admin" params="" dialog=false>
<#assign FORM_ID="admin-jmx-form" />
<#if server.edition == "Community">
<#assign docsEdition = "community" />
<#assign docsEdition = "/Alfresco-Content-Services-Community-Edition/" + server.getVersionMajor() + "." + server.getVersionMinor() + "/Alfresco-Content-Services-Community-Edition" />
<#elseif server.edition == "Enterprise" >
<#assign docsEdition = server.getVersionMajor() + "." + server.getVersionMinor() />
<#assign docsEdition = "/Alfresco-Content-Services/" + server.getVersionMajor() + "." + server.getVersionMinor() + "/Alfresco-Content-Services" />
</#if>
<#if metadata??>
<#assign HOSTNAME>${msg("admin-console.host")}: ${metadata.hostname}</#assign>
@@ -551,7 +551,7 @@ Admin.addEventListener(window, 'load', function() {
Template for a full page view
-->
<div class="sticky-wrapper">
<div class="header">
<span><a href="${url.serviceContext}${DEFAULT_CONTROLLER!"/admin"}">${msg("admin-console.header")}</a></span><#if metadata??><span class="meta">${HOSTNAME}</span><span class="meta">${HOSTADDR}</span></#if>
<div style="float:right"><a href="${msg("admin-console.help-link", docsEdition)}" target="_blank">${msg("admin-console.help")}</a></div>
@@ -908,4 +908,4 @@ Admin.addEventListener(window, 'load', function() {
<#macro button label description="" onclick="" style="" id="" class="" disabled="false">
<input class="<#if class?has_content>${class?html}<#else>inline</#if>" <#if id?has_content>id="${id?html}"</#if> <#if style?has_content>style="${style?html}"</#if> type="button" value="${label?html}" onclick="${onclick?html}" <#if disabled="true">disabled="true"</#if> />
<#if description?has_content><span class="description">${description?html}</span></#if>
</#macro>
</#macro>

View File

@@ -40,6 +40,7 @@
"items":
[
<#list results as row>
<#if row.item.hasPermission("Read")>
{
"type": "${row.item.typeShort}",
"parentType": "${row.item.parentTypeShort!""}",
@@ -75,9 +76,10 @@
"nodeRef": "${row.item.nodeRef}"<#if row.selectable?exists>,
"selectable" : ${row.selectable?string}</#if>
}<#if row_has_next>,</#if>
</#if>
</#list>
]
}
}
</#escape>
</#macro>
</#macro>

View File

@@ -27,7 +27,7 @@ to integrate with a number of external Authentication providers including
* https://github.com/Alfresco/alfresco-data-model/tree/master/src/main/java/org/alfresco/repo/security/authentication
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/issues/?jql=project%3DREPO
* Documentation Link: http://docs.alfresco.com/5.2/concepts/auth-intro.html
* Documentation Link: https://support.hyland.com/r/Alfresco/Alfresco-Content-Services-Community-Edition/23.4/Alfresco-Content-Services-Community-Edition/Administer/Manage-Security/Authentication-and-sync
* Contribution Model: Alfresco Open Source
***

View File

@@ -16,7 +16,7 @@
* Source Code Link:m https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/
* License: LGPL
* Issue Tracker Link: https://issues.alfresco.com/jira/secure/RapidBoard.jspa?projectKey=REPO&useStoredSettings=true&rapidView=379
* Documentation Link: http://docs.alfresco.com/5.1/concepts/versioning.html
* Documentation Link: https://support.hyland.com/r/Alfresco/Alfresco-Content-Services/23.4/Alfresco-Content-Services/Configure/Repository/About-Versioning
* Contribution Model: Alfresco publishes the source code and will review proposed patch requests
***

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.5.0.10</version>
<version>23.5.0.17</version>
</parent>
<dependencies>

View File

@@ -2,93 +2,96 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2025 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
* 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.action.executer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
/**
* Add features action executor implementation.
*
* @author Roy Wetherall
*/
public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
{
/**
* Action constants
*/
public static final String NAME = "add-features";
public static final String PARAM_ASPECT_NAME = "aspect-name";
public static final String PARAM_CONSTRAINT = "ac-aspects";
/**
* The node service
*/
private NodeService nodeService;
/** Transaction Service, used for retrying operations */
private TransactionService transactionService;
/**
* Set the node service
*
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the transaction service
*
* @param transactionService the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Adhoc properties are allowed for this executor
*/
@Override
protected boolean getAdhocPropertiesAllowed()
{
return true;
package org.alfresco.repo.action.executer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.access.ActionAccessRestriction;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
/**
* Add features action executor implementation.
*
* @author Roy Wetherall
*/
public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
{
/**
* Action constants
*/
public static final String NAME = "add-features";
public static final String PARAM_ASPECT_NAME = "aspect-name";
public static final String PARAM_CONSTRAINT = "ac-aspects";
/**
* The node service
*/
private NodeService nodeService;
/** Transaction Service, used for retrying operations */
private TransactionService transactionService;
/**
* Set the node service
*
* @param nodeService
* the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the transaction service
*
* @param transactionService
* the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Adhoc properties are allowed for this executor
*/
@Override
protected boolean getAdhocPropertiesAllowed()
{
return true;
}
/**
@@ -96,55 +99,61 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
*/
public void executeImpl(final Action ruleAction, final NodeRef actionedUponNodeRef)
{
if (this.nodeService.exists(actionedUponNodeRef))
{
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
QName aspectQName = null;
if(! nodeService.exists(actionedUponNodeRef))
{
// Node has gone away, skip
return null;
}
// Build the aspect details
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
{
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
{
aspectQName = (QName)entry.getValue();
}
else
{
// Must be an adhoc property
QName propertyQName = QName.createQName(entry.getKey());
Serializable propertyValue = entry.getValue();
properties.put(propertyQName, propertyValue);
}
}
// Add the aspect
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
return null;
}
}
);
}
}
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_ASPECT_NAME, DataTypeDefinition.QNAME, true, getParamDisplayLabel(PARAM_ASPECT_NAME), false, "ac-aspects"));
}
}
if (this.nodeService.exists(actionedUponNodeRef))
{
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>() {
public Void execute() throws Throwable
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
QName aspectQName = null;
if (!nodeService.exists(actionedUponNodeRef))
{
// Node has gone away, skip
return null;
}
// Build the aspect details
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
removeActionContextParameter(paramValues);
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
{
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
{
aspectQName = (QName) entry.getValue();
}
else
{
// Must be an adhoc property
QName propertyQName = QName.createQName(entry.getKey());
Serializable propertyValue = entry.getValue();
properties.put(propertyQName, propertyValue);
}
}
// Add the aspect
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
return null;
}
});
}
}
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_ASPECT_NAME, DataTypeDefinition.QNAME, true, getParamDisplayLabel(PARAM_ASPECT_NAME), false, "ac-aspects"));
}
/**
* Remove actionContext from the parameter values to declassify as an adhoc property
*/
private void removeActionContextParameter(Map<String, Serializable> paramValues)
{
paramValues.remove(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME);
}
}

View File

@@ -58,6 +58,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FolderExistsException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -73,6 +74,7 @@ import org.alfresco.util.TempFileProvider;
*
* @author gavinc
*/
@SuppressWarnings("PMD.PreserveStackTrace")
public class ImporterActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "import";
@@ -366,7 +368,11 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase
catch (FileExistsException e)
{
// TODO: add failed file info to status message?
throw new AlfrescoRuntimeException("Failed to process ZIP file.", e);
if (e.getType().equalsIgnoreCase("folder"))
{
throw new FolderExistsException(root, file.getName());
}
throw e;
}
}
}

View File

@@ -542,10 +542,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
@Override
protected void onShutdown(ApplicationEvent applicationEvent)
{
if (eventSender != null)
{
eventSender.destroy();
}
// NOOP
}
protected class EventTransactionListener extends TransactionListenerAdapter

View File

@@ -52,7 +52,7 @@ public interface EventSender
}
/**
* It's called when the application context is closing, allowing {@link org.alfresco.repo.event2.EventGenerator} to perform cleanup operations.
* It's called when the bean instance is destroyed, allowing to perform cleanup operations.
*/
default void destroy()
{

View File

@@ -25,15 +25,16 @@
*/
package org.alfresco.repo.event2;
import java.util.Optional;
import java.util.concurrent.Executor;
import jakarta.annotation.Nonnull;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.core.env.PropertyResolver;
import java.util.Optional;
import java.util.concurrent.Executor;
import org.alfresco.util.PropertyCheck;
public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
{
@@ -51,7 +52,7 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
private boolean legacySkipQueueConfig;
public EventSenderFactoryBean(@Autowired PropertyResolver propertyResolver, Event2MessageProducer event2MessageProducer,
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor)
{
super();
PropertyCheck.mandatory(this, "propertyResolver", propertyResolver);
@@ -155,4 +156,13 @@ public class EventSenderFactoryBean extends AbstractFactoryBean<EventSender>
{
return event2MessageProducer;
}
@Override
protected void destroyInstance(EventSender eventSender)
{
if (eventSender != null)
{
eventSender.destroy();
}
}
}

View File

@@ -37,6 +37,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.transform.config.CoreFunction;
import org.alfresco.util.PropertyCheck;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -46,6 +47,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
@@ -68,6 +70,7 @@ public class LocalTransformClient implements TransformClient, InitializingBean
private ContentService contentService;
private RenditionService2Impl renditionService2;
private boolean directAccessUrlEnabled;
private int threadPoolSize;
private ExecutorService executorService;
private ThreadLocal<LocalTransform> transform = new ThreadLocal<>();
@@ -97,6 +100,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
this.directAccessUrlEnabled = directAccessUrlEnabled;
}
public void setThreadPoolSize(int threadPoolSize)
{
this.threadPoolSize = threadPoolSize;
}
public void setExecutorService(ExecutorService executorService)
{
this.executorService = executorService;
@@ -110,9 +118,11 @@ public class LocalTransformClient implements TransformClient, InitializingBean
PropertyCheck.mandatory(this, "contentService", contentService);
PropertyCheck.mandatory(this, "renditionService2", renditionService2);
PropertyCheck.mandatory(this, "directAccessUrlEnabled", directAccessUrlEnabled);
PropertyCheck.mandatory(this, "threadPoolSize", threadPoolSize);
if (executorService == null)
{
executorService = Executors.newCachedThreadPool();
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("local-transform-%d").build();
executorService = Executors.newFixedThreadPool(threadPoolSize, threadFactory);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,35 +25,27 @@
*/
package org.alfresco.repo.rendition2;
import java.util.List;
import org.alfresco.service.NotAuditable;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import java.util.List;
/**
* The Async Rendition service. Replaces the original rendition services which included synchronous renditions and
* asynchronous methods with Java call backs.<p/>
* The Async Rendition service. Replaces the original rendition services which included synchronous renditions and asynchronous methods with Java call backs.
* <p/>
*
* Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated
* {@link RenditionDefinitionRegistry2}.<p/>
* Renditions are defined as {@link RenditionDefinition2}s and may be registered and looked by the associated {@link RenditionDefinitionRegistry2}.
* <p/>
*
* Unlike the original RenditionService this service, it:
* <ul>
* <li>Performs async renditions without a Java callback, as another node in the cluster may complete the rendition.
* The current node requests a transform, but another node might take the resulting transform and turn it into a
* rendition if the external Transform Service is used.</li>
* <li>Reduces the configurable options to do with with the associations of rendition nodes, their type. They
* are identical to 'hidden' (not normally seen as nodes in their own right in a
* UI) renditions produced by the original service. So, they are always directly under the source node connected by a
* {@code}rn:rendition{@code} association with the name of the rendition.</li>
* <li>The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code}
* property. This property contains a value that allows the service to work out if it holds a rendition of the
* source node's current content.</li>
* <li>Failures are handled by setting the rendition node's content to null.</li>
* <li>When a rendition is requested via the REST API, only the newer service is used.</li>
* <li>Where possible old service renditions migrate automatically over to the new service when content on a
* source node is updated.</li>
* <li>Performs async renditions without a Java callback, as another node in the cluster may complete the rendition. The current node requests a transform, but another node might take the resulting transform and turn it into a rendition if the external Transform Service is used.</li>
* <li>Reduces the configurable options to do with with the associations of rendition nodes, their type. They are identical to 'hidden' (not normally seen as nodes in their own right in a UI) renditions produced by the original service. So, they are always directly under the source node connected by a {@code}rn:rendition{@code} association with the name of the rendition.</li>
* <li>The rendition nodes additionally have a {@code}rn:rendition2{@code} aspect and a {@code}contentUrlHashCode{@code} property. This property contains a value that allows the service to work out if it holds a rendition of the source node's current content.</li>
* <li>Failures are handled by setting the rendition node's content to null.</li>
* <li>When a rendition is requested via the REST API, only the newer service is used.</li>
* <li>Where possible old service renditions migrate automatically over to the new service when content on a source node is updated.</li>
* </ul>
*
* @author adavis
@@ -66,29 +58,30 @@ public interface RenditionService2
RenditionDefinitionRegistry2 getRenditionDefinitionRegistry2();
/**
* This method asynchronously transforms content to a target mimetype with transform options supplied in the
* {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails,
* together with some client supplied data. The response queue and client data are also included in the
* transformDefinition.<p>
* This method asynchronously transforms content to a target mimetype with transform options supplied in the {@code transformDefinition}. A response is set on a message queue once the transform is complete or fails, together with some client supplied data. The response queue and client data are also included in the transformDefinition.
* <p>
*
* This method does not create a rendition node, but uses the same code as renditions to perform the transform. The
* {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a
* {@link RenditionDefinitionRegistry2}, as it is transient in nature.
* This method does not create a rendition node, but uses the same code as renditions to perform the transform. The {@code transformDefinition} extends {@link RenditionDefinition2}, but is not stored in a {@link RenditionDefinitionRegistry2}, as it is transient in nature.
*
* @param sourceNodeRef the node from which the content is retrieved.
* @param transformDefinition which defines the transform, where to sent the response and some client specified data.
* @throws UnsupportedOperationException if the transform is not supported.
* @param sourceNodeRef
* the node from which the content is retrieved.
* @param transformDefinition
* which defines the transform, where to sent the response and some client specified data.
* @throws UnsupportedOperationException
* if the transform is not supported.
*/
@NotAuditable
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition);
/**
* This method asynchronously renders content as specified by the {@code renditionName}. The content to be
* rendered is provided by {@code sourceNodeRef}.
* This method asynchronously renders content as specified by the {@code renditionName}. The content to be rendered is provided by {@code sourceNodeRef}.
*
* @param sourceNodeRef the node from which the content is retrieved.
* @param renditionName the rendition to be performed.
* @throws UnsupportedOperationException if the transform is not supported AND the rendition has not been created before.
* @param sourceNodeRef
* the node from which the content is retrieved.
* @param renditionName
* the rendition to be performed.
* @throws UnsupportedOperationException
* if the transform is not supported AND the rendition has not been created before.
*/
@NotAuditable
public void render(NodeRef sourceNodeRef, String renditionName);
@@ -104,10 +97,11 @@ public interface RenditionService2
/**
* This method gets the rendition of the {@code sourceNodeRef} identified by its name.
*
* @param sourceNodeRef the source node for the renditions
* @param renditionName the renditionName used to identify a rendition.
* @return the {@link ChildAssociationRef} which links the source node to the
* rendition or <code>null</code> if there is no rendition or it is not up to date.
* @param sourceNodeRef
* the source node for the renditions
* @param renditionName
* the renditionName used to identify a rendition.
* @return the {@link ChildAssociationRef} which links the source node to the rendition or <code>null</code> if there is no rendition or it is not up to date.
*/
@NotAuditable
ChildAssociationRef getRenditionByName(NodeRef sourceNodeRef, String renditionName);
@@ -115,7 +109,8 @@ public interface RenditionService2
/**
* This method clears source nodeRef rendition content and content hash code using supplied rendition name.
*
* @param renditionNode the rendition node
* @param renditionNode
* the rendition node
*/
@NotAuditable
void clearRenditionContentDataInTransaction(NodeRef renditionNode);
@@ -124,4 +119,13 @@ public interface RenditionService2
* Indicates if renditions are enabled. Set using the {@code system.thumbnail.generate} value.
*/
boolean isEnabled();
}
/**
* This method forces the content hash code for every {@code sourceNodeRef} renditions.
*
* @param sourceNodeRef
* the source node to update renditions hash code
*/
@NotAuditable
void forceRenditionsContentHashCode(NodeRef sourceNodeRef);
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,24 @@
*/
package org.alfresco.repo.rendition2;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
import static org.alfresco.service.namespace.QName.createQName;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.ContentServicePolicies;
@@ -51,23 +69,6 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
import static org.alfresco.service.namespace.QName.createQName;
/**
* The Async Rendition service. Replaces the original deprecated RenditionService.
@@ -95,12 +96,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
abstract RenditionDefinition2 getRenditionDefinition();
void handleUnsupported(UnsupportedOperationException e)
{
}
{}
void throwIllegalStateExceptionIfAlreadyDone(int sourceContentHashCode)
{
}
{}
}
private TransactionService transactionService;
@@ -217,8 +216,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void transform(NodeRef sourceNodeRef, TransformDefinition transformDefinition)
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -237,8 +235,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void render(NodeRef sourceNodeRef, String renditionName)
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack()
{
requestAsyncTransformOrRendition(sourceNodeRef, new RenderOrTransformCallBack() {
@Override
public String getName()
{
@@ -261,7 +258,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
@Override
public void handleUnsupported(UnsupportedOperationException e)
{
// On the initial request for a rendition throw the exception.
// On the initial request for a rendition throw the exception.
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
if (renditionNode == null)
{
@@ -277,7 +274,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode+ " hashCodes");
logger.debug(getName() + ": Source " + sourceContentHashCode + " rendition " + renditionContentHashCode + " hashCodes");
}
if (renditionContentHashCode == sourceContentHashCode)
{
@@ -299,14 +296,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (!nodeService.exists(sourceNodeRef))
{
throw new IllegalArgumentException(renderOrTransform.getName()+ ": The supplied sourceNodeRef "+sourceNodeRef+" does not exist.");
throw new IllegalArgumentException(renderOrTransform.getName() + ": The supplied sourceNodeRef " + sourceNodeRef + " does not exist.");
}
RenditionDefinition2 renditionDefinition = renderOrTransform.getRenditionDefinition();
if (logger.isDebugEnabled())
{
logger.debug(renderOrTransform.getName()+ ": transform " +sourceNodeRef);
logger.debug(renderOrTransform.getName() + ": transform " + sourceNodeRef);
}
AtomicBoolean supported = new AtomicBoolean(true);
@@ -328,14 +325,13 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
String user = AuthenticationUtil.getRunAsUser();
RetryingTransactionHelper.RetryingTransactionCallback callback = () ->
{
RetryingTransactionHelper.RetryingTransactionCallback callback = () -> {
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
if (!supported.get())
{
if (logger.isDebugEnabled())
{
logger.debug(renderOrTransform.getName() +" is not supported. " +
logger.debug(renderOrTransform.getName() + " is not supported. " +
"The content might be too big or the source mimetype cannot be converted.");
}
failure(sourceNodeRef, renditionDefinition, sourceContentHashCode);
@@ -372,26 +368,24 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
public void failure(NodeRef sourceNodeRef, RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{
// The original transaction may have already have failed
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
return null;
}, false, true));
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
consume(sourceNodeRef, null, renditionDefinition, transformContentHashCode);
return null;
}, false, true));
}
public void consume(NodeRef sourceNodeRef, InputStream transformInputStream, RenditionDefinition2 renditionDefinition,
int transformContentHashCode)
int transformContentHashCode)
{
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
if (logger.isDebugEnabled())
{
logger.debug("Consume: Source " + sourceContentHashCode + " and transform's source " + transformContentHashCode+" hashcodes");
logger.debug("Consume: Source " + sourceContentHashCode + " and transform's source " + transformContentHashCode + " hashcodes");
}
if (renditionDefinition instanceof TransformDefinition)
{
TransformDefinition transformDefinition = (TransformDefinition)renditionDefinition;
TransformDefinition transformDefinition = (TransformDefinition) renditionDefinition;
String targetMimetype = transformDefinition.getTargetMimetype();
if (AsynchronousExtractor.isMetadataExtractMimetype(targetMimetype))
{
@@ -413,7 +407,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeExtractedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (transformInputStream == null)
{
@@ -440,7 +434,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeEmbeddedMetadata(NodeRef nodeRef, int sourceContentHashCode, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (transformInputStream == null)
{
@@ -468,7 +462,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
private void consumeTransformReply(NodeRef sourceNodeRef, InputStream transformInputStream,
TransformDefinition transformDefinition, int transformContentHashCode)
TransformDefinition transformDefinition, int transformContentHashCode)
{
if (logger.isDebugEnabled())
{
@@ -484,12 +478,10 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* Takes a transformation (InputStream) and attaches it as a rendition to the source node.
* Does nothing if there is already a newer rendition.
* If the transformInputStream is null, this is taken to be a transform failure.
* Takes a transformation (InputStream) and attaches it as a rendition to the source node. Does nothing if there is already a newer rendition. If the transformInputStream is null, this is taken to be a transform failure.
*/
private void consumeRendition(NodeRef sourceNodeRef, int sourceContentHashCode, InputStream transformInputStream,
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
RenditionDefinition2 renditionDefinition, int transformContentHashCode)
{
String renditionName = renditionDefinition.getRenditionName();
if (transformContentHashCode != sourceContentHashCode)
@@ -507,93 +499,92 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
(transformInputStream == null ? " to null as the transform failed" : " to the transform result"));
}
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
// Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
boolean createRenditionNode = renditionNode == null;
boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
try
{
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)
{
renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
}
else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
if (logger.isDebugEnabled())
{
logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
}
}
if (logger.isDebugEnabled())
{
logger.debug("Set ThumbnailLastModified for " + renditionName);
}
setThumbnailLastModified(sourceNodeRef, renditionName);
if (transformInputStream != null)
{
// Ensure that the creation of a rendition does not cause updates to the modified, modifier properties on the source node
NodeRef renditionNode = getRenditionNode(sourceNodeRef, renditionName);
boolean createRenditionNode = renditionNode == null;
boolean sourceHasAspectRenditioned = nodeService.hasAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED);
try
{
ruleService.disableRuleType(RuleType.UPDATE);
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
// Set or replace rendition content
ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
String targetMimetype = renditionDefinition.getTargetMimetype();
contentWriter.setMimetype(targetMimetype);
contentWriter.setEncoding(DEFAULT_ENCODING);
ContentWriter renditionWriter = contentWriter;
renditionWriter.putContent(transformInputStream);
// If they do not exist create the rendition association and the rendition node.
if (createRenditionNode)
ContentReader contentReader = renditionWriter.getReader();
long sizeOfRendition = contentReader.getSize();
if (sizeOfRendition > 0L)
{
renditionNode = createRenditionNode(sourceNodeRef, renditionDefinition);
}
else if (!nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_RENDITION2, null);
if (logger.isDebugEnabled())
{
logger.debug("Added rendition2 aspect to rendition " + renditionName + " on " + sourceNodeRef);
}
}
if (logger.isDebugEnabled())
{
logger.debug("Set ThumbnailLastModified for " + renditionName);
}
setThumbnailLastModified(sourceNodeRef, renditionName);
if (transformInputStream != null)
{
try
{
// Set or replace rendition content
ContentWriter contentWriter = contentService.getWriter(renditionNode, DEFAULT_RENDITION_CONTENT_PROP, true);
String targetMimetype = renditionDefinition.getTargetMimetype();
contentWriter.setMimetype(targetMimetype);
contentWriter.setEncoding(DEFAULT_ENCODING);
ContentWriter renditionWriter = contentWriter;
renditionWriter.putContent(transformInputStream);
ContentReader contentReader = renditionWriter.getReader();
long sizeOfRendition = contentReader.getSize();
if (sizeOfRendition > 0L)
{
if (logger.isDebugEnabled()) {
logger.debug("Set rendition hashcode for " + renditionName);
}
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
}
else
{
logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
clearRenditionContentData(renditionNode);
}
}
catch (Exception e)
{
logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
throw e;
logger.debug("Set rendition hashcode for " + renditionName);
}
nodeService.setProperty(renditionNode, RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE, transformContentHashCode);
}
else
{
logger.error("Transform was zero bytes for " + renditionName + " on " + sourceNodeRef);
clearRenditionContentData(renditionNode);
}
if (!sourceHasAspectRenditioned)
{
nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
}
}
catch (Exception e)
{
throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
logger.error("Failed to copy transform InputStream into rendition " + renditionName + " on " + sourceNodeRef);
throw e;
}
finally
{
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
ruleService.enableRuleType(RuleType.UPDATE);
}
return null;
}, false, true));
}
else
{
clearRenditionContentData(renditionNode);
}
if (!sourceHasAspectRenditioned)
{
nodeService.addAspect(sourceNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
}
}
catch (Exception e)
{
throw new RenditionService2Exception(TRANSFORMING_ERROR_MESSAGE + e.getMessage(), e);
}
finally
{
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE);
behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_VERSIONABLE);
ruleService.enableRuleType(RuleType.UPDATE);
}
return null;
}, false, true));
}
}
@@ -634,14 +625,14 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (logger.isTraceEnabled())
{
logger.trace("Setting thumbnail last modified date to " + lastModifiedValue +" on source node: " + sourceNodeRef);
logger.trace("Setting thumbnail last modified date to " + lastModifiedValue + " on source node: " + sourceNodeRef);
}
if (nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
{
List<String> thumbnailMods = (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
String target = null;
for (String currThumbnailMod: thumbnailMods)
for (String currThumbnailMod : thumbnailMods)
{
if (currThumbnailMod.startsWith(prefix))
{
@@ -665,8 +656,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* Returns the hash code of the source node's content url. As transformations may be returned in a different
* sequences to which they were requested, this is used work out if a rendition should be replaced.
* Returns the hash code of the source node's content url. As transformations may be returned in a different sequences to which they were requested, this is used work out if a rendition should be replaced.
*/
private int getSourceContentHashCode(NodeRef sourceNodeRef)
{
@@ -675,7 +665,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
if (contentData != null)
{
// Originally we used the contentData URL, but that is not enough if the mimetype changes.
String contentString = contentData.getContentUrl()+contentData.getMimetype();
String contentString = contentData.getContentUrl() + contentData.getMimetype();
if (contentString != null)
{
hashCode = contentString.hashCode();
@@ -685,13 +675,11 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist.
* Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was
* not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
* Returns the hash code of source node's content url on the rendition node (node may be null) if it does not exist. Used work out if a rendition should be replaced. {@code -2} is returned if the rendition does not exist or was not created by RenditionService2. {@code -1} is returned if there was no source content or the rendition failed.
*/
private int getRenditionContentHashCode(NodeRef renditionNode)
{
if ( renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
if (renditionNode == null || !nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
return RENDITION2_DOES_NOT_EXIST;
}
@@ -699,7 +687,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
Serializable hashCode = nodeService.getProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE);
return hashCode == null
? SOURCE_HAS_NO_CONTENT
: (int)hashCode;
: (int) hashCode;
}
private NodeRef getRenditionNode(NodeRef sourceNodeRef, String renditionName)
@@ -773,11 +761,12 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* This method checks whether the specified source node is of a content class which has been registered for
* rendition prevention.
* This method checks whether the specified source node is of a content class which has been registered for rendition prevention.
*
* @param sourceNode the node to check.
* @throws RenditionService2PreventedException if the source node is configured for rendition prevention.
* @param sourceNode
* the node to check.
* @throws RenditionService2PreventedException
* if the source node is configured for rendition prevention.
*/
// This code is based on the old RenditionServiceImpl.checkSourceNodeForPreventionClass(...)
private void checkSourceNodeForPreventionClass(NodeRef sourceNode)
@@ -823,7 +812,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
for (ChildAssociationRef childAssoc : childAsocs)
{
NodeRef renditionNode = childAssoc.getChildRef();
NodeRef renditionNode = childAssoc.getChildRef();
if (isRenditionAvailable(sourceNodeRef, renditionNode))
{
result.add(childAssoc);
@@ -833,8 +822,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
/**
* Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl
* and out of date renditions or those still being created don't have a matching contentHashCode.
* Indicates if the rendition is available. Failed renditions (there was an error) don't have a contentUrl and out of date renditions or those still being created don't have a matching contentHashCode.
*/
public boolean isRenditionAvailable(NodeRef sourceNodeRef, NodeRef renditionNode)
{
@@ -852,7 +840,7 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
if (logger.isDebugEnabled())
{
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode+" hashcodes");
logger.debug("isRenditionAvailable source " + sourceContentHashCode + " and rendition " + renditionContentHashCode + " hashcodes");
}
if (sourceContentHashCode != renditionContentHashCode)
{
@@ -892,19 +880,17 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
ChildAssociationRef childAssoc = renditions.get(0);
NodeRef renditionNode = childAssoc.getChildRef();
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null: childAssoc;
return !isRenditionAvailable(sourceNodeRef, renditionNode) ? null : childAssoc;
}
}
@Override
public void clearRenditionContentDataInTransaction(NodeRef renditionNode)
{
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
clearRenditionContentData(renditionNode);
return null;
}, false, true));
AuthenticationUtil.runAsSystem((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
clearRenditionContentData(renditionNode);
return null;
}, false, true));
}
@Override
@@ -950,4 +936,35 @@ public class RenditionService2Impl implements RenditionService2, InitializingBea
}
}
@Override
public void forceRenditionsContentHashCode(NodeRef sourceNodeRef)
{
if (sourceNodeRef != null && nodeService.exists(sourceNodeRef))
{
List<ChildAssociationRef> renditions = getRenditionChildAssociations(sourceNodeRef);
if (renditions != null)
{
int sourceContentHashCode = getSourceContentHashCode(sourceNodeRef);
for (ChildAssociationRef rendition : renditions)
{
NodeRef renditionNode = rendition.getChildRef();
if (nodeService.hasAspect(renditionNode, RenditionModel.ASPECT_RENDITION2))
{
int renditionContentHashCode = getRenditionContentHashCode(renditionNode);
String renditionName = rendition.getQName().getLocalName();
if (sourceContentHashCode != renditionContentHashCode)
{
if (logger.isDebugEnabled())
{
logger.debug("Update content hash code for rendition " + renditionName + " of node "
+ sourceNodeRef);
}
nodeService.setProperty(renditionNode, PROP_RENDITION_CONTENT_HASH_CODE,
sourceContentHashCode);
}
}
}
}
}
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -23,41 +23,55 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.service.cmr.model;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Common exception thrown when an operation fails because of a name clash.
*
* @author Derek Hulley
*/
@AlfrescoPublicApi
public class FileExistsException extends AlfrescoRuntimeException
{
private static final String MESSAGE_ID = "file_folder_service.file_exists_message";
private static final long serialVersionUID = -4133713912784624118L;
private NodeRef parentNodeRef;
private String name;
public FileExistsException(NodeRef parentNodeRef, String name)
{
super(MESSAGE_ID, new Object[] { name });
this.parentNodeRef = parentNodeRef;
this.name = name;
}
public NodeRef getParentNodeRef()
{
return parentNodeRef;
}
public String getName()
{
return name;
}
}
package org.alfresco.service.cmr.model;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Common exception thrown when an operation fails because of a name clash.
*
* @author Derek Hulley
*/
@AlfrescoPublicApi
public class FileExistsException extends AlfrescoRuntimeException
{
private static final String MESSAGE_ID = "file_folder_service.file_exists_message";
private static final long serialVersionUID = -4133713912784624118L;
private NodeRef parentNodeRef;
private String name;
private String type;
public FileExistsException(NodeRef parentNodeRef, String name)
{
super(MESSAGE_ID, new Object[]{name});
this.parentNodeRef = parentNodeRef;
this.name = name;
}
public FileExistsException(NodeRef parentNodeRef, String name, String type)
{
super(MESSAGE_ID, new Object[]{name});
this.parentNodeRef = parentNodeRef;
this.name = name;
this.type = type;
}
public NodeRef getParentNodeRef()
{
return parentNodeRef;
}
public String getName()
{
return name;
}
public String getType()
{
return type;
}
}

View File

@@ -0,0 +1,62 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2025 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.service.cmr.model;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Common exception thrown when an operation fails because of a name clash of folder.
*
*/
@AlfrescoPublicApi
public class FolderExistsException extends AlfrescoRuntimeException
{
private static final String MESSAGE_ID = "file_folder_service.file_exists_message";
private static final long serialVersionUID = -4133713912784624118L;
private NodeRef parentNodeRef;
private String name;
public FolderExistsException(NodeRef parentNodeRef, String name)
{
super(MESSAGE_ID, new Object[]{name});
this.parentNodeRef = parentNodeRef;
this.name = name;
}
public NodeRef getParentNodeRef()
{
return parentNodeRef;
}
public String getName()
{
return name;
}
}

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Vlastnost ''{0}'' nebyla nastavena: {1} ({2})
system.err.duplicate_name=Duplicitn\u00ed n\u00e1zvy pod\u0159\u00edzen\u00fdch objekt\u016f nejsou povoleny ({0})
system.err.lucene_not_supported=Subsyst\u00e9m hled\u00e1n\u00ed Lucene nen\u00ed podporov\u00e1n. Viz http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=Subsyst\u00e9m hled\u00e1n\u00ed Lucene nen\u00ed podporov\u00e1n. Viz https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Egenskaben ''{0}'' er ikke blevet indstillet: {1} ({2})
system.err.duplicate_name=Duplikeret navn p\u00e5 underordnet er ikke tilladt: {0}
system.err.lucene_not_supported=Lucene-s\u00f8geundersystemet underst\u00f8ttes ikke. Se http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=Lucene-s\u00f8geundersystemet underst\u00f8ttes ikke. Se https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Ominaisuutta {0} ei ole m\u00e4\u00e4ritetty: {1} ({2})
system.err.duplicate_name=P\u00e4\u00e4llekk\u00e4ist\u00e4 alatasonime\u00e4 ei sallita: {0}
system.err.lucene_not_supported=Lucene-hakualij\u00e4rjestelm\u00e4\u00e4 ei tueta. Saat lis\u00e4tietoja osoitteesta http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=Lucene-hakualij\u00e4rjestelm\u00e4\u00e4 ei tueta. Saat lis\u00e4tietoja osoitteesta https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set : {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed : {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Nie ustawiono w\u0142a\u015bciwo\u015bci ''{0}'': {1} ({2})
system.err.duplicate_name=Zduplikowane nazwy element\u00f3w podrz\u0119dnych s\u0105 niedozwolone: {0}
system.err.lucene_not_supported=Podsystem wyszukiwania Lucene nie jest obs\u0142ugiwany. Zobacz http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=Podsystem wyszukiwania Lucene nie jest obs\u0142ugiwany. Zobacz https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Egenskap ''{0}'' har inte st\u00e4llts in: {1} ({2})
system.err.duplicate_name=Dubbelt underordnat namn inte till\u00e5tet: {0}
system.err.lucene_not_supported=Lucene-s\u00f6kundersystemet st\u00f6ds inte. Se http://docs.alfresco.com/{0}/uppgifter/lucene-solr4-migration.html
system.err.lucene_not_supported=Lucene-s\u00f6kundersystemet st\u00f6ds inte. Se https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -5,7 +5,7 @@
system.err.property_not_set=Property ''{0}'' has not been set: {1} ({2})
system.err.duplicate_name=Duplicate child name not allowed: {0}
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see http://docs.alfresco.com/{0}/tasks/lucene-solr4-migration.html
system.err.lucene_not_supported=The lucene search subsystem is not supported. Please see https://support.hyland.com/p/alfresco
# Bootstrap configuration check messages

View File

@@ -82,6 +82,7 @@
<property name="contentService" ref="contentService" />
<property name="renditionService2" ref="renditionService2" />
<property name="directAccessUrlEnabled" value="${local.transform.directAccessUrl.enabled}"/>
<property name="threadPoolSize" value="${local.transform.threadPoolSize}" />
</bean>
<bean id="synchronousTransformClient" parent="localSynchronousTransformClient" />

View File

@@ -1351,6 +1351,9 @@ restApi.directAccessUrl.defaultExpiryTimeInSec=30
# Controls whether direct access url URLs may be used in transforms.
local.transform.directAccessUrl.enabled=true
# Controls size of thread pool used for transforms.
local.transform.threadPoolSize=8
# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories.
system.new-node-transaction-indexes.ignored=true

View File

@@ -161,5 +161,4 @@
<ref bean="ServiceRegistry"/>
</property>
</bean>
</beans>

View File

@@ -171,5 +171,4 @@
<value>urldecode</value>
</property>
</bean>
</beans>

View File

@@ -1,164 +1,177 @@
/*
* #%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.action.executer;
import java.util.List;
import java.util.Locale;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ParameterDefinition;
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.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.junit.Before;
import org.junit.Test;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.transaction.annotation.Transactional;
/**
* Add features action execution test
*
* @author Roy Wetherall
*/
@Transactional
public class AddFeaturesActionExecuterTest extends BaseSpringTest
{
/**
* The node service
*/
private NodeService nodeService;
/**
* The store reference
*/
private StoreRef testStoreRef;
/**
* The root node reference
*/
private NodeRef rootNodeRef;
/**
* The test node reference
*/
private NodeRef nodeRef;
/**
* The add features action executer
*/
private AddFeaturesActionExecuter executer;
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/**
* Called at the begining of all tests
*/
@Before
public void before() throws Exception
{
this.nodeService = (NodeService)this.applicationContext.getBean("nodeService");
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// Create the store and get the root node
this.testStoreRef = this.nodeService.createStore(
StoreRef.PROTOCOL_WORKSPACE, "Test_"
+ System.currentTimeMillis());
this.rootNodeRef = this.nodeService.getRootNode(this.testStoreRef);
// Create the node used for tests
this.nodeRef = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance
this.executer = (AddFeaturesActionExecuter)this.applicationContext.getBean(AddFeaturesActionExecuter.NAME);
}
/**
* Test execution
*/
@Test
public void testExecution()
{
// Check that the node does not have the classifiable aspect
assertFalse(this.nodeService.hasAspect(this.nodeRef, ContentModel.ASPECT_CLASSIFIABLE));
// Execute the action
ActionImpl action = new ActionImpl(null, ID, AddFeaturesActionExecuter.NAME, null);
action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_CLASSIFIABLE);
this.executer.execute(action, this.nodeRef);
// Check that the node now has the classifiable aspect applied
assertTrue(this.nodeService.hasAspect(this.nodeRef, ContentModel.ASPECT_CLASSIFIABLE));
}
/**
* MNT-15802
*/
@Test
public void testCheckLocalizedParamDefintionWithConstraint()
{
// test for other than default locale
I18NUtil.setLocale(Locale.GERMAN);
ActionDefinition actionDef = executer.getActionDefinition();
List<ParameterDefinition> paramDef = actionDef.getParameterDefinitions();
assertNotNull(paramDef);
String constraintName = paramDef.get(0).getParameterConstraintName();
assertNotNull(constraintName);
assertEquals(AddFeaturesActionExecuter.PARAM_CONSTRAINT, constraintName);
// test for other than default locale
I18NUtil.setLocale(Locale.ITALY);
actionDef = executer.getActionDefinition();
paramDef = actionDef.getParameterDefinitions();
assertNotNull(paramDef);
constraintName = paramDef.get(0).getParameterConstraintName();
assertNotNull(constraintName);
assertEquals(AddFeaturesActionExecuter.PARAM_CONSTRAINT, constraintName);
I18NUtil.setLocale(Locale.getDefault());
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2025 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.action.executer;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.transaction.annotation.Transactional;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.access.ActionAccessRestriction;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ParameterDefinition;
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.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
/**
* Add features action execution test
*
* @author Roy Wetherall
*/
@Transactional
public class AddFeaturesActionExecuterTest extends BaseSpringTest
{
/**
* Id used to identify the test action created
*/
private final static String ID = GUID.generate();
/**
* The node service
*/
private NodeService nodeService;
/**
* The store reference
*/
private StoreRef testStoreRef;
/**
* The root node reference
*/
private NodeRef rootNodeRef;
/**
* The test node reference
*/
private NodeRef nodeRef;
/**
* The add features action executer
*/
private AddFeaturesActionExecuter executer;
/**
* Called at the begining of all tests
*/
@Before
public void before() throws Exception
{
this.nodeService = (NodeService) this.applicationContext.getBean("nodeService");
AuthenticationComponent authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// Create the store and get the root node
this.testStoreRef = this.nodeService.createStore(
StoreRef.PROTOCOL_WORKSPACE, "Test_"
+ System.currentTimeMillis());
this.rootNodeRef = this.nodeService.getRootNode(this.testStoreRef);
// Create the node used for tests
this.nodeRef = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance
this.executer = (AddFeaturesActionExecuter) this.applicationContext.getBean(AddFeaturesActionExecuter.NAME);
}
/**
* Test execution
*/
@Test
public void testExecution()
{
// Check that the node does not have the classifiable aspect
assertFalse(this.nodeService.hasAspect(this.nodeRef, ContentModel.ASPECT_CLASSIFIABLE));
// Execute the action
ActionImpl action = new ActionImpl(null, ID, AddFeaturesActionExecuter.NAME, null);
action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_CLASSIFIABLE);
this.executer.execute(action, this.nodeRef);
// Check that the node now has the classifiable aspect applied
assertTrue(this.nodeService.hasAspect(this.nodeRef, ContentModel.ASPECT_CLASSIFIABLE));
}
/**
* MNT-15802
*/
@Test
public void testCheckLocalizedParamDefintionWithConstraint()
{
// test for other than default locale
I18NUtil.setLocale(Locale.GERMAN);
ActionDefinition actionDef = executer.getActionDefinition();
List<ParameterDefinition> paramDef = actionDef.getParameterDefinitions();
assertNotNull(paramDef);
String constraintName = paramDef.get(0).getParameterConstraintName();
assertNotNull(constraintName);
assertEquals(AddFeaturesActionExecuter.PARAM_CONSTRAINT, constraintName);
// test for other than default locale
I18NUtil.setLocale(Locale.ITALY);
actionDef = executer.getActionDefinition();
paramDef = actionDef.getParameterDefinitions();
assertNotNull(paramDef);
constraintName = paramDef.get(0).getParameterConstraintName();
assertNotNull(constraintName);
assertEquals(AddFeaturesActionExecuter.PARAM_CONSTRAINT, constraintName);
I18NUtil.setLocale(Locale.getDefault());
}
/**
* Test check actionContext param is removed from adhoc properties
*/
@Test
public void testCheckActionContext()
{
// Execute the action
ActionImpl action = new ActionImpl(null, ID, AddFeaturesActionExecuter.NAME, null);
action.setParameterValue(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME, ActionAccessRestriction.V1_ACTION_CONTEXT);
action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_CLASSIFIABLE);
this.executer.execute(action, this.nodeRef);
// Ensure the actionContext parameter has been removed
assertFalse(nodeService.getProperties(this.nodeRef).containsKey(QName.createQName(ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME)));
}
}

View File

@@ -48,6 +48,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.model.FolderExistsException;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -62,7 +63,7 @@ import org.alfresco.util.test.junitrules.ApplicationContextInit;
*
* @author abalmus
*/
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
@SuppressWarnings("PMD.UnitTestsShouldIncludeAssert")
public class ImporterActionExecuterTest
{
// Rule to initialise the default Alfresco spring configuration
@@ -340,6 +341,49 @@ public class ImporterActionExecuterTest
});
}
@Test
public void testDuplicateUnzipping() throws IOException
{
final RetryingTransactionHelper retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// create test data
NodeRef zipFileNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTENT).getChildRef();
NodeRef targetFolderNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER).getChildRef();
putContent(zipFileNodeRef, "import-archive-test/accentCharTestZip.zip");
Action action = createAction(zipFileNodeRef, "ImporterActionExecuterTestActionDefinition", targetFolderNodeRef);
try
{
importerActionExecuter.setUncompressedBytesLimit("100000");
importerActionExecuter.execute(action, zipFileNodeRef);
// unzip again to duplicate node
importerActionExecuter.execute(action, zipFileNodeRef);
}
catch (FolderExistsException e)
{
assertTrue(e.getMessage().contains("File or folder accentCharTestZip already exists"));
}
finally
{
// clean test data
nodeService.deleteNode(targetFolderNodeRef);
nodeService.deleteNode(zipFileNodeRef);
}
return null;
}
});
}
private void putContent(NodeRef zipFileNodeRef, String resource)
{
URL url = AbstractContentTransformerTest.class.getClassLoader().getResource(resource);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,6 +26,7 @@
package org.alfresco.repo.rendition2;
import static java.lang.Thread.sleep;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.alfresco.model.RenditionModel.PROP_RENDITION_CONTENT_HASH_CODE;
import static org.alfresco.repo.content.MimetypeMap.EXTENSION_BINARY;
@@ -35,6 +36,15 @@ import java.io.FileNotFoundException;
import java.io.Serializable;
import java.util.Collections;
import junit.framework.AssertionFailedError;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AsynchronousExtractor;
@@ -45,6 +55,7 @@ import org.alfresco.repo.thumbnail.ThumbnailRegistry;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -52,6 +63,7 @@ import org.alfresco.service.cmr.repository.MimetypeService;
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.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
@@ -61,15 +73,6 @@ import org.alfresco.transform.registry.TransformServiceRegistry;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.alfresco.util.PropertyMap;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import junit.framework.AssertionFailedError;
/**
* Class unites common utility methods for {@link org.alfresco.repo.rendition2} package tests.
@@ -128,6 +131,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
protected static final String ADMIN = "admin";
protected static final String DOC_LIB = "doclib";
protected static final String PDF = "pdf";
private CronExpression origLocalTransCron;
private CronExpression origRenditionCron;
@@ -152,7 +156,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Strict MimetypeCheck
System.setProperty("transformer.strict.mimetype.check", "true");
// Retry on DifferentMimetype
// Retry on DifferentMimetype
System.setProperty("content.transformer.retryOn.different.mimetype", "true");
}
@@ -181,7 +185,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
if (transformServiceRegistry instanceof LocalTransformServiceRegistry)
{
((LocalTransformServiceRegistry)transformServiceRegistry).setEnabled(localTransformServiceEnabled);
((LocalTransformServiceRegistry) transformServiceRegistry).setEnabled(localTransformServiceEnabled);
}
thumbnailRegistry.setTransformServiceRegistry(transformServiceRegistry);
@@ -257,9 +261,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Creates a new source node as the given user in its own transaction.
protected NodeRef createSource(String user, String testFileName)
{
return AuthenticationUtil.runAs(() ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
createSource(testFileName)), user);
return AuthenticationUtil.runAs(() -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> createSource(testFileName)), user);
}
// Creates a new source node as the current user in the current transaction.
@@ -271,12 +273,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Changes the content of a source node as the given user in its own transaction.
protected void updateContent(String user, NodeRef sourceNodeRef, String testFileName)
{
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
updateContent(sourceNodeRef, testFileName);
return null;
}), user);
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
updateContent(sourceNodeRef, testFileName);
return null;
}), user);
}
// Changes the content of a source node as the current user in the current transaction.
@@ -295,12 +295,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Clears the content of a source node as the given user in its own transaction.
protected void clearContent(String user, NodeRef sourceNodeRef)
{
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
clearContent(sourceNodeRef);
return null;
}), user);
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
clearContent(sourceNodeRef);
return null;
}), user);
}
// Clears the content of a source node as the current user in the current transaction.
@@ -312,23 +310,19 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
// Requests a new rendition as the given user in its own transaction.
protected void render(String user, NodeRef sourceNode, String renditionName)
{
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
render(sourceNode, renditionName);
return null;
}), user);
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
render(sourceNode, renditionName);
return null;
}), user);
}
// Requests a new metadata extract as the given user in its own transaction.
protected void extract(String user, NodeRef sourceNode)
{
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
extract(sourceNode);
return null;
}), user);
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
extract(sourceNode);
return null;
}), user);
}
// Requests a new rendition as the current user in the current transaction.
@@ -357,7 +351,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
Throwable cause = e.getCause();
if (cause instanceof AssertionFailedError)
{
throw (AssertionFailedError)cause;
throw (AssertionFailedError) cause;
}
throw e;
}
@@ -375,7 +369,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
Throwable cause = e.getCause();
if (cause instanceof AssertionFailedError)
{
throw (AssertionFailedError)cause;
throw (AssertionFailedError) cause;
}
throw e;
}
@@ -386,16 +380,15 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
long maxMillis = 10000;
ChildAssociationRef assoc = null;
for (int i = (int)(maxMillis / 1000); i >= 0; i--)
for (int i = (int) (maxMillis / 1000); i >= 0; i--)
{
// Must create a new transaction in order to see changes that take place after this method started.
assoc = transactionService.getRetryingTransactionHelper().doInTransaction(() ->
renditionService2.getRenditionByName(sourceNodeRef, renditionName), true, true);
assoc = transactionService.getRetryingTransactionHelper().doInTransaction(() -> renditionService2.getRenditionByName(sourceNodeRef, renditionName), true, true);
if (assoc != null)
{
break;
}
logger.debug("RenditionService2.getRenditionByName(...) sleep "+i);
logger.debug("RenditionService2.getRenditionByName(...) sleep " + i);
sleep(1000);
}
if (shouldExist)
@@ -415,11 +408,10 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
long maxMillis = 5000;
boolean nodeModified = true;
for (int i = (int)(maxMillis / 1000); i >= 0; i--)
for (int i = (int) (maxMillis / 1000); i >= 0; i--)
{
// Must create a new transaction in order to see changes that take place after this method started.
nodeModified = transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
nodeModified = transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
Serializable created = nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CREATED);
Serializable modified = nodeService.getProperty(sourceNodeRef, ContentModel.PROP_MODIFIED);
return !created.equals(modified);
@@ -428,7 +420,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
{
break;
}
logger.debug("waitForExtract sleep "+i);
logger.debug("waitForExtract sleep " + i);
sleep(1000);
}
if (nodePropsShouldChange)
@@ -445,7 +437,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
protected String getTestFileName(String sourceMimetype) throws FileNotFoundException
{
String extension = mimetypeMap.getExtension(sourceMimetype);
String testFileName = extension.equals(EXTENSION_BINARY) ? null : "quick."+extension;
String testFileName = extension.equals(EXTENSION_BINARY) ? null : "quick." + extension;
if (testFileName != null)
{
try
@@ -491,8 +483,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
String createRandomUser()
{
return AuthenticationUtil.runAs(() ->
{
return AuthenticationUtil.runAs(() -> {
String username = generateNewUsernameString();
createUser(username);
return username;
@@ -505,13 +496,12 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
}
void createUser(final String username,
final String firstName,
final String lastName,
final String jobTitle,
final long quota)
final String firstName,
final String lastName,
final String jobTitle,
final long quota)
{
RetryingTransactionHelper.RetryingTransactionCallback<Void> createUserCallback = () ->
{
RetryingTransactionHelper.RetryingTransactionCallback<Void> createUserCallback = () -> {
authenticationService.createAuthentication(username, PASSWORD.toCharArray());
PropertyMap personProperties = new PropertyMap();
@@ -519,7 +509,7 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
personProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + username);
personProperties.put(ContentModel.PROP_FIRSTNAME, firstName);
personProperties.put(ContentModel.PROP_LASTNAME, lastName);
personProperties.put(ContentModel.PROP_EMAIL, username+"@example.com");
personProperties.put(ContentModel.PROP_EMAIL, username + "@example.com");
personProperties.put(ContentModel.PROP_JOBTITLE, jobTitle);
if (quota > 0)
{
@@ -548,14 +538,12 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
}
/**
* Helper method which gets the content hash code from the supplied rendition node without specific validations (the
* equivalent method from {@link RenditionService2Impl} is not exposed)
* Helper method which gets the content hash code from the supplied rendition node without specific validations (the equivalent method from {@link RenditionService2Impl} is not exposed)
*
* @param renditionNodeRef
* the rendition node
*
* @return -1 in case of there is no content, -2 in case rendition doesn't exist, the actual content hash code
* otherwise
* @return -1 in case of there is no content, -2 in case rendition doesn't exist, the actual content hash code otherwise
*/
protected int getRenditionContentHashCode(NodeRef renditionNodeRef)
{
@@ -569,4 +557,28 @@ public abstract class AbstractRenditionIntegrationTest extends BaseSpringTest
return renditionContentHashCode;
}
/**
* Helper method which gets the content hash code from the supplied source node (the equivalent method from {@link RenditionService2Impl} is not public)
*
* @param sourceNodeRef
* the source node
*
* @return -1 in case of there is no content, otherwise, the actual content hash code otherwise
*/
protected int getSourceContentHashCode(NodeRef sourceNodeRef)
{
int hashCode = -1;
ContentData contentData = DefaultTypeConverter.INSTANCE.convert(ContentData.class, nodeService.getProperty(sourceNodeRef, PROP_CONTENT));
if (contentData != null)
{
// Originally we used the contentData URL, but that is not enough if the mimetype changes.
String contentString = contentData.getContentUrl() + contentData.getMimetype();
if (contentString != null)
{
hashCode = contentString.hashCode();
}
}
return hashCode;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* Copyright (C) 2005 - 2025 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,14 +25,18 @@
*/
package org.alfresco.repo.rendition2;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import static org.junit.Assert.assertNotEquals;
import static org.alfresco.model.ContentModel.PROP_CONTENT;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -44,8 +48,6 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Integration tests for {@link RenditionService2}
@@ -62,37 +64,37 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
// PDF transformation
@Test
public void testLocalRenderPdfToJpegMedium()
public void testLocalRenderPdfToJpegMedium()
{
checkRendition("quick.pdf", "medium", true);
}
@Test
public void testLocalRenderPdfToDoclib()
public void testLocalRenderPdfToDoclib()
{
checkRendition("quick.pdf", "doclib", true);
}
@Test
public void testLocalRenderPdfJpegImgpreview()
public void testLocalRenderPdfJpegImgpreview()
{
checkRendition("quick.pdf", "imgpreview", true);
}
@Test
public void testLocalRenderPdfPngAvatar()
public void testLocalRenderPdfPngAvatar()
{
checkRendition("quick.pdf", "avatar", true);
}
@Test
public void testLocalRenderPdfPngAvatar32()
public void testLocalRenderPdfPngAvatar32()
{
checkRendition("quick.pdf", "avatar32", true);
}
@Test
public void testLocalRenderPdfFlashWebpreview()
public void testLocalRenderPdfFlashWebpreview()
{
checkRendition("quick.pdf", "webpreview", false);
}
@@ -100,43 +102,43 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
// DOCX transformation
@Test
public void testLocalRenderDocxJpegMedium()
public void testLocalRenderDocxJpegMedium()
{
checkRendition("quick.docx", "medium", true);
}
@Test
public void testLocalRenderDocxDoclib()
public void testLocalRenderDocxDoclib()
{
checkRendition("quick.docx", "doclib", true);
}
@Test
public void testLocalRenderDocxJpegImgpreview()
public void testLocalRenderDocxJpegImgpreview()
{
checkRendition("quick.docx", "imgpreview", true);
}
@Test
public void testLocalRenderDocxPngAvatar()
public void testLocalRenderDocxPngAvatar()
{
checkRendition("quick.docx", "avatar", true);
}
@Test
public void testLocalRenderDocxPngAvatar32()
public void testLocalRenderDocxPngAvatar32()
{
checkRendition("quick.docx", "avatar32", true);
}
@Test
public void testLocalRenderDocxFlashWebpreview()
public void testLocalRenderDocxFlashWebpreview()
{
checkRendition("quick.docx", "webpreview", false);
}
@Test
public void testLocalRenderDocxPdf()
public void testLocalRenderDocxPdf()
{
checkRendition("quick.docx", "pdf", true);
}
@@ -150,7 +152,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}
@Test
public void changedSourceToNullContent()
public void changedSourceToNullContent()
{
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
render(ADMIN, sourceNodeRef, DOC_LIB);
@@ -158,8 +160,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
assertNull("There should be no rendition as there was no content", assoc);
}
@@ -190,8 +191,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
clearContent(ADMIN, sourceNodeRef);
render(ADMIN, sourceNodeRef, DOC_LIB);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() ->
renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
ChildAssociationRef assoc = AuthenticationUtil.runAs(() -> renditionService2.getRenditionByName(sourceNodeRef, DOC_LIB), ADMIN);
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, false);
assertNull("There should be no rendition as there was no content", assoc);
@@ -201,7 +201,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}
@Test
public void testCreateRenditionByUser()
public void testCreateRenditionByUser()
{
String userName = createRandomUser();
NodeRef sourceNodeRef = createSource(userName, "quick.jpg");
@@ -211,13 +211,12 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}
@Test
public void testReadRenditionByOtherUser()
public void testReadRenditionByOtherUser()
{
String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
String otherUserName = createRandomUser();
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
return null;
});
@@ -231,13 +230,12 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}
@Test
public void testRenderByReader()
public void testRenderByReader()
{
String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
String otherUserName = createRandomUser();
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, otherUserName, PermissionService.READ, true);
return null;
});
@@ -251,14 +249,13 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
}
@Test
public void testAccessWithNoPermissions()
public void testAccessWithNoPermissions()
{
String ownerUserName = createRandomUser();
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
render(ownerUserName, sourceNodeRef, DOC_LIB);
String noPermissionsUser = createRandomUser();
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
permissionService.setPermission(sourceNodeRef, noPermissionsUser, PermissionService.ALL_PERMISSIONS, false);
return null;
});
@@ -280,12 +277,9 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ownerUserName, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ownerUserName));
NodeRef oldRendition = AuthenticationUtil.runAs(() ->
renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
NodeRef oldRendition = AuthenticationUtil.runAs(() -> renditionService.getRenditionByName(sourceNodeRef, doclibRendDefQName).getChildRef(), ownerUserName);
assertFalse("The rendition should be generated by old Rendition Service",
AuthenticationUtil.runAs(() -> nodeService.hasAspect(oldRendition, RenditionModel.ASPECT_RENDITION2), ownerUserName));
@@ -335,12 +329,10 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
renditionService2.setEnabled(false);
// Call 'clearRenditionContentData' method directly to prove rendition content will be cleaned
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () ->
transactionService.getRetryingTransactionHelper().doInTransaction(() ->
{
renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
return null;
}), ADMIN);
AuthenticationUtil.runAs((AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
renditionService2.clearRenditionContentData(sourceNodeRef, DOC_LIB);
return null;
}), ADMIN);
// The rendition should not have content by now
assertNull("Rendition has content", nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT));
@@ -356,8 +348,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
/**
* Tests if a rendition without content (but with contentHashCode) can be generated again.
* <p>
* If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the
* rendition node, allowing new requests to generate the rendition.
* If the rendition consumption receives a null InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
* </p>
*/
@Test
@@ -369,8 +360,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
/**
* Tests if a rendition without content (but with contentHashCode) can be generated again.
* <p>
* If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the
* rendition node, allowing new requests to generate the rendition.
* If the rendition consumption receives a zero length InputStream, the contentHashCode should be cleaned from the rendition node, allowing new requests to generate the rendition.
* </p>
*/
@Test
@@ -455,6 +445,61 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
assertEquals(TOTAL_NODES, countModifier(nodes, user1));
}
@Test
public void testForceRenditionsContentHashCode()
{
// Create a node
NodeRef sourceNodeRef = createSource(ADMIN, "quick.docx");
assertNotNull("Node not generated", sourceNodeRef);
// Get content hash code for the source node
int sourceNodeContentHashCode = getSourceContentHashCode(sourceNodeRef);
// Trigger the pdf rendition
render(ADMIN, sourceNodeRef, PDF);
NodeRef pdfRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, PDF, true);
assertNotNull("pdf rendition was not generated", pdfRenditionNodeRef);
assertNotNull("pdf rendition was not generated", nodeService.getProperty(pdfRenditionNodeRef, PROP_CONTENT));
// Check the pdf rendition content hash code is valid
int pdfRenditionContentHashCode = getRenditionContentHashCode(pdfRenditionNodeRef);
assertEquals("pdf rendition content hash code is different from source node content hash code", sourceNodeContentHashCode, pdfRenditionContentHashCode);
// Trigger the doc lib rendition
render(ADMIN, sourceNodeRef, DOC_LIB);
NodeRef docLibRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
assertNotNull("doc lib rendition was not generated", docLibRenditionNodeRef);
assertNotNull("doc lib rendition was not generated", nodeService.getProperty(docLibRenditionNodeRef, PROP_CONTENT));
// Check the doc lib rendition content hash code is valid
int docLibenditionContentHashCode = getRenditionContentHashCode(docLibRenditionNodeRef);
assertEquals("doc lib rendition content hash code is different from source node content hash code", sourceNodeContentHashCode, docLibenditionContentHashCode);
// Update the source node content
updateContent(ADMIN, sourceNodeRef, "quick.docx");
// Get source node content hash code after update
int sourceNodeContentHashCode2 = getSourceContentHashCode(sourceNodeRef);
// Check content hash code are different after content update
assertNotEquals("Source node content hash code is the same after content update", sourceNodeContentHashCode, sourceNodeContentHashCode2);
assertNotEquals("pdf rendition content hash code is the same after content update", sourceNodeContentHashCode2, pdfRenditionContentHashCode);
assertNotEquals("doc lib rendition content hash code is the same after content update", sourceNodeContentHashCode2, docLibenditionContentHashCode);
// Forces the content hash code for every source node renditions
AuthenticationUtil.runAs(() -> {
renditionService2.forceRenditionsContentHashCode(sourceNodeRef);
return null;
}, ADMIN);
// Check the renditions content hash code are now the same as the latest source node content hash code
int pdfRenditionContentHashCode2 = getRenditionContentHashCode(pdfRenditionNodeRef);
int docLibenditionContentHashCode2 = getRenditionContentHashCode(docLibRenditionNodeRef);
assertEquals("pdf rendition content hash code is different from latest source node content hash code", sourceNodeContentHashCode2, pdfRenditionContentHashCode2);
assertEquals("doc lib rendition content hash code is different from latest source node content hash code", sourceNodeContentHashCode2, docLibenditionContentHashCode2);
}
private int countModifier(List<NodeRef> nodes, String user)
{
int count = 0;
@@ -492,22 +537,16 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
assertNotNull("The old renditions service did not render", waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true));
List<String> lastThumbnailModification = transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
(List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
updateContent(ADMIN, sourceNodeRef, "quick.png");
List<String> newThumbnailModification = null;
for (int i = 0; i < 5; i++)
{
newThumbnailModification = transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
(List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> (List<String>) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA), ADMIN));
if (!newThumbnailModification.equals(lastThumbnailModification))
{
break;
@@ -579,9 +618,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true);
@@ -652,9 +689,7 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
NodeRef sourceNodeRef = createSource(ADMIN, "quick.jpg");
final QName doclibRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
transactionService.getRetryingTransactionHelper()
.doInTransaction(() ->
AuthenticationUtil.runAs(() ->
renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
.doInTransaction(() -> AuthenticationUtil.runAs(() -> renditionService.render(sourceNodeRef, doclibRendDefQName), ADMIN));
waitForRendition(ADMIN, sourceNodeRef, DOC_LIB, true);
renditionService2.setEnabled(true);
@@ -682,4 +717,63 @@ public class RenditionService2IntegrationTest extends AbstractRenditionIntegrati
renditionService2.setEnabled(true);
}
}
@Test
public void testRecreationOfRendition2()
{
renditionService2.setEnabled(true);
try
{
NodeRef sourceNodeRef = createSource(ADMIN, "quick.docx");
assertNotNull("Node not generated", sourceNodeRef);
// Get content hash code for the source node.
int sourceNodeContentHashCode = getSourceContentHashCode(sourceNodeRef);
// Trigger the pdf rendition.
render(ADMIN, sourceNodeRef, PDF);
NodeRef pdfRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, PDF, true);
assertNotNull("pdf rendition was not generated", pdfRenditionNodeRef);
assertNotNull("pdf rendition was not generated",
nodeService.getProperty(pdfRenditionNodeRef, PROP_CONTENT));
// Check the pdf rendition content hash code is valid
int pdfRenditionContentHashCode = getRenditionContentHashCode(pdfRenditionNodeRef);
assertEquals("pdf rendition content hash code is different from source node content hash code",
sourceNodeContentHashCode, pdfRenditionContentHashCode);
// Calling 'clearRenditionContentData' method directly so that rendition content will be cleaned.
AuthenticationUtil.runAs(
(AuthenticationUtil.RunAsWork<Void>) () -> transactionService.getRetryingTransactionHelper()
.doInTransaction(() -> {
renditionService2.clearRenditionContentData(sourceNodeRef, PDF);
return null;
}),
ADMIN);
assertNull("Rendition has content", nodeService.getProperty(pdfRenditionNodeRef, PROP_CONTENT));
pdfRenditionContentHashCode = getRenditionContentHashCode(pdfRenditionNodeRef);
assertFalse("Rendition has content hash code",
isValidRenditionContentHashCode(pdfRenditionContentHashCode));
renditionService2.setEnabled(false);
final QName pdfRendDefQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "pdf");
transactionService.getRetryingTransactionHelper()
.doInTransaction(() -> AuthenticationUtil.runAs(
() -> renditionService.render(sourceNodeRef, pdfRendDefQName), ADMIN));
renditionService2.setEnabled(true);
NodeRef pdfRecreatedRenditionNodeRef = waitForRendition(ADMIN, sourceNodeRef, PDF, true);
assertNotEquals(" Rendition before deletion and after previewing are identical",
pdfRenditionNodeRef.getId(), pdfRecreatedRenditionNodeRef.getId());
}
finally
{
renditionService2.setEnabled(true);
}
}
}

View File

@@ -31,7 +31,7 @@
</onException>
<transacted />
<loadBalance>
<roundRobin/>
<roundRobinLoadBalancer/>
<to uri="bean:mockExceptionThrowingConsumer"/>
<to uri="bean:mockConsumer"/>
</loadBalance>