Merge remote-tracking branch 'origin/release/6.2.N' into release/6.2.N

This commit is contained in:
tzclucian
2021-01-11 13:35:30 +02:00
28 changed files with 1949 additions and 273 deletions

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<dependencies> <dependencies>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<properties> <properties>

View File

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

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId> <artifactId>alfresco-community-repo-packaging</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<properties> <properties>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<profiles> <profiles>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId> <artifactId>alfresco-community-repo-packaging</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<properties> <properties>

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"> <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> <modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name> <name>Alfresco Community Repo Parent</name>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<dependencies> <dependencies>

View File

@@ -1831,6 +1831,19 @@ public class NodesImpl implements Nodes
{ {
versionMajor = Boolean.valueOf(str); versionMajor = Boolean.valueOf(str);
} }
String versioningEnabledStringValue = parameters.getParameter("versioningEnabled");
if (null != versioningEnabledStringValue)
{
boolean versioningEnabled = Boolean.parseBoolean(versioningEnabledStringValue);
if (versioningEnabled)
{
versionMajor = (null != versionMajor) ? versionMajor : true;
}
else
{
versionMajor = null;
}
}
String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT); String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT);
// Create the node // Create the node
@@ -2331,6 +2344,11 @@ public class NodesImpl implements Nodes
private void handleNodeRename(Map<QName, Serializable> props, NodeRef nodeRef) private void handleNodeRename(Map<QName, Serializable> props, NodeRef nodeRef)
{ {
Serializable nameProp = props.get(ContentModel.PROP_NAME); Serializable nameProp = props.get(ContentModel.PROP_NAME);
handleNodeRename(nameProp, nodeRef);
}
private void handleNodeRename(Serializable nameProp, NodeRef nodeRef)
{
if ((nameProp != null)) if ((nameProp != null))
{ {
String currentName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String currentName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
@@ -2705,6 +2723,7 @@ public class NodesImpl implements Nodes
String fileName = parameters.getParameter(PARAM_NAME); String fileName = parameters.getParameter(PARAM_NAME);
if (fileName != null) if (fileName != null)
{ {
handleNodeRename(fileName, nodeRef);
// optionally rename, before updating the content // optionally rename, before updating the content
nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, fileName); nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, fileName);
} }
@@ -2891,6 +2910,7 @@ public class NodesImpl implements Nodes
String versionComment = null; String versionComment = null;
String relativePath = null; String relativePath = null;
String renditionNames = null; String renditionNames = null;
boolean versioningEnabled = true;
Map<String, Object> qnameStrProps = new HashMap<>(); Map<String, Object> qnameStrProps = new HashMap<>();
Map<QName, Serializable> properties = null; Map<QName, Serializable> properties = null;
@@ -2947,6 +2967,19 @@ public class NodesImpl implements Nodes
case "renditions": case "renditions":
renditionNames = getStringOrNull(field.getValue()); renditionNames = getStringOrNull(field.getValue());
break; break;
case "versioningenabled":
String versioningEnabledStringValue = getStringOrNull(field.getValue());
if (null != versioningEnabledStringValue)
{
// MNT-22036 versioningenabled parameter was added to disable versioning of newly created nodes.
// The default API mechanism should not be changed/affected.
// Versioning is enabled by default when creating a node using form-data.
// To preserve this, versioningEnabled value must be 'true' for any given value typo/valuesNotSupported (except case-insensitive 'false')
// .equalsIgnoreCase("false") will return true only when the input value is 'false'
// !.equalsIgnoreCase("false") will return false only when the input value is 'false'
versioningEnabled = !versioningEnabledStringValue.equalsIgnoreCase("false");
}
break;
default: default:
{ {
@@ -3025,6 +3058,8 @@ public class NodesImpl implements Nodes
{ {
versionMajor = true; versionMajor = true;
} }
// MNT-22036 add versioningEnabled property for newly created nodes.
versionMajor = versioningEnabled ? versionMajor : null;
// Create a new file. // Create a new file.
NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, versionMajor, versionComment); NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, versionMajor, versionComment);

View File

@@ -5605,6 +5605,279 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertTrue(((ArrayList) (propUpdateResponse.get("custom:locations"))).size() == 1); assertTrue(((ArrayList) (propUpdateResponse.get("custom:locations"))).size() == 1);
} }
@Test
public void versioningEnabledMultipartNodeCreationTest() throws Exception
{
setRequestContext(user1);
String myNodeId = getMyNodeId();
// Test Scenarios:
// 1: majorVersion not set - versioningEnabled not set Expect: MAJOR version
// 2: majorVersion not set - versioningEnabled false Expect: versioning disabled
// 3: majorVersion true - versioningEnabled false Expect: versioning disabled
// 4: majorVersion false - versioningEnabled false Expect: versioning disabled
// 5: majorVersion not set - versioningEnabled true Expect: MAJOR version
// 6: majorVersion true - versioningEnabled true Expect: MAJOR version
// 7: majorVersion false - versioningEnabled true Expect: Minor version
// 8: majorVersion not set - versioningEnabled False Expect: versioning disabled
// 9: majorVersion not set - versioningEnabled invalid Expect: MAJOR version
// Scenario 1:
String fileName = "myfile" + UUID.randomUUID() + ".txt";
File file = getResourceFile("quick-2.pdf");
MultiPartBuilder multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
MultiPartRequest reqBody = multiPartBuilder.build();
HttpResponse response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
Document documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
// Default behaviour, expect to be MAJOR Version 1.0
Map<String, Object> documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
// Scenario 2:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setVersioningEnabled("false");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
// Scenario 3:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setMajorVersion(true);
multiPartBuilder.setVersioningEnabled("false");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
// Scenario 4:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setMajorVersion(false);
multiPartBuilder.setVersioningEnabled("false");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
// Scenario 5:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setVersioningEnabled("true");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
// Scenario 6:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setMajorVersion(true);
multiPartBuilder.setVersioningEnabled("true");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
// Scenario 7:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setMajorVersion(false);
multiPartBuilder.setVersioningEnabled("true");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MINOR", documentProperties.get("cm:versionType"));
assertEquals("0.1", documentProperties.get("cm:versionLabel"));
// Scenario 8:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setVersioningEnabled("False");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
// Scenario 9:
fileName = "myfile" + UUID.randomUUID() + ".txt";
multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file));
multiPartBuilder.setVersioningEnabled("invalid");
reqBody = multiPartBuilder.build();
response = post(getNodeChildrenUrl(myNodeId), reqBody.getBody(), null, reqBody.getContentType(), 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
}
@Test
public void versioningEnabledJSONNodeCreationTest() throws Exception
{
setRequestContext(user1);
String myNodeId = getMyNodeId();
// Test Scenarios:
// 1: majorVersion not set - versioningEnabled not set Expect: versioning disabled
// 2: majorVersion not set - versioningEnabled false Expect: versioning disabled
// 3: majorVersion true - versioningEnabled false Expect: versioning disabled
// 4: majorVersion false - versioningEnabled false Expect: versioning disabled
// 5: majorVersion not set - versioningEnabled true Expect: MAJOR version
// 6: majorVersion true - versioningEnabled true Expect: MAJOR version
// 7: majorVersion false - versioningEnabled true Expect: Minor version
// 8: majorVersion not set - versioningEnabled False Expect: versioning disabled
// 9: majorVersion not set - versioningEnabled invalid Expect: versioning disabled
// 10 majorVersion not set - versioningenabled true Expect: versioning disabled
Document d1 = new Document();
Map<String, String> requestHeaders = new HashMap<>();
//Scenario 1:
d1.setName("testDoc" + UUID.randomUUID());
d1.setNodeType(TYPE_CM_CONTENT);
HttpResponse response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
Document documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
Map<String, Object> documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 2:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","false");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 3:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","false");
requestHeaders.put("majorVersion","true");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 4:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","false");
requestHeaders.put("majorVersion","false");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 5:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","true");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
//Scenario 6:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","true");
requestHeaders.put("majorVersion","true");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MAJOR", documentProperties.get("cm:versionType"));
assertEquals("1.0", documentProperties.get("cm:versionLabel"));
//Scenario 7:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","true");
requestHeaders.put("majorVersion","false");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertEquals("MINOR", documentProperties.get("cm:versionType"));
assertEquals("0.1", documentProperties.get("cm:versionLabel"));
//Scenario 8:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","False");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 9:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningEnabled","invalid");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
//Scenario 10:
d1.setName("testDoc" + UUID.randomUUID());
requestHeaders = new HashMap<>();
requestHeaders.put("versioningenabled","true");
response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1),requestHeaders, null, null, 201);
documentResponse = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
documentProperties = documentResponse.getProperties();
assertNull(documentProperties);
}
@Test public void testAuditableProperties() throws Exception @Test public void testAuditableProperties() throws Exception
{ {
setRequestContext(user1); setRequestContext(user1);
@@ -5708,6 +5981,55 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertTrue(currentPath.equals(expectedPath)); assertTrue(currentPath.equals(expectedPath));
} }
@Test
public void testPrimaryPathVersion() throws Exception
{
setRequestContext(user1);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
String myNodeId = getMyNodeId();
// /Company Home/User Homes/user<timestamp>/folder_A
String folderName = "folder_A";
Folder folder = createFolder(myNodeId, folderName);
NodeRef folderNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, folder.getId());
// /Company Home/User Homes/user<timestamp>/folder_A/testDoc<GUID>
String docName = "testDoc" + GUID.generate();
Document doc = new Document();
doc.setName(docName);
doc.setNodeType(TYPE_CM_CONTENT);
HttpResponse response = post(getNodeChildrenUrl(folderNodeRef.getId()), toJsonAsStringNonNull(doc), 201);
Document docResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
NodeRef docNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, docResp.getId());
// Checks that current path and name match
String expectedPath1 = "/Company Home/User Homes/" + user1 + "/" + folderName + "/" + docName;
Path docPath1 = nodeService.getPath(docNodeRef);
Path.ChildAssocElement docPathLast1 = (Path.ChildAssocElement) docPath1.last();
String docLocalName1 = docPathLast1.getRef().getQName().getLocalName();
String currentPath1 = docPath1.toDisplayPath(nodeService, permissionService) + "/" + docLocalName1;
assertTrue(docName.equals(docLocalName1));
assertTrue(expectedPath1.equals(currentPath1));
// Upload document new content supplying a different name
String docName2 = "testDoc2" + GUID.generate();
Map<String, String> params = new HashMap<>();
params.put("name", docName2);
Document docResp2 = updateTextFileWithRandomContent(docNodeRef.getId(), 1024L, params);
NodeRef docNodeRef2 = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, docResp2.getId());
// Checks new path and name after new version upload
String expectedPath2 = "/Company Home/User Homes/" + user1 + "/" + folderName + "/" + docName2;
Path docPath2 = nodeService.getPath(docNodeRef2);
Path.ChildAssocElement docPathLast2 = (Path.ChildAssocElement) docPath2.last();
String docLocalName2 = docPathLast2.getRef().getQName().getLocalName();
String currentPath2 = docPath2.toDisplayPath(nodeService, permissionService) + "/" + docLocalName2;
assertFalse(docLocalName1.equals(docLocalName2));
assertTrue(docName2.equals(docLocalName2));
assertFalse(expectedPath1.equals(currentPath2));
assertTrue(expectedPath2.equals(currentPath2));
}
private String getDataDictionaryNodeId() throws Exception private String getDataDictionaryNodeId() throws Exception
{ {
Map params = new HashMap<>(); Map params = new HashMap<>();

View File

@@ -43,7 +43,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
/** /**
* <i><b>multipart/form-data</b></i> builder. * <i><b>multipart/form-data</b></i> builder.
@@ -57,6 +56,7 @@ public class MultiPartBuilder
private String updateNodeRef; private String updateNodeRef;
private String description; private String description;
private String contentTypeQNameStr; private String contentTypeQNameStr;
private String versioningEnabled;
private List<String> aspects = Collections.emptyList(); private List<String> aspects = Collections.emptyList();
private Boolean majorVersion; private Boolean majorVersion;
private Boolean overwrite; private Boolean overwrite;
@@ -76,6 +76,7 @@ public class MultiPartBuilder
this.updateNodeRef = that.updateNodeRef; this.updateNodeRef = that.updateNodeRef;
this.description = that.description; this.description = that.description;
this.contentTypeQNameStr = that.contentTypeQNameStr; this.contentTypeQNameStr = that.contentTypeQNameStr;
this.versioningEnabled = that.versioningEnabled;
this.aspects = new ArrayList<>(that.aspects); this.aspects = new ArrayList<>(that.aspects);
this.majorVersion = that.majorVersion; this.majorVersion = that.majorVersion;
this.overwrite = that.overwrite; this.overwrite = that.overwrite;
@@ -125,6 +126,12 @@ public class MultiPartBuilder
return this; return this;
} }
public MultiPartBuilder setVersioningEnabled(String versioningEnabled)
{
this.versioningEnabled = versioningEnabled;
return this;
}
public MultiPartBuilder setAspects(List<String> aspects) public MultiPartBuilder setAspects(List<String> aspects)
{ {
this.aspects = aspects; this.aspects = aspects;
@@ -278,6 +285,7 @@ public class MultiPartBuilder
addPartIfNotNull(parts, "updatenoderef", updateNodeRef); addPartIfNotNull(parts, "updatenoderef", updateNodeRef);
addPartIfNotNull(parts, "description", description); addPartIfNotNull(parts, "description", description);
addPartIfNotNull(parts, "contenttype", contentTypeQNameStr); addPartIfNotNull(parts, "contenttype", contentTypeQNameStr);
addPartIfNotNull(parts, "versioningenabled", versioningEnabled);
addPartIfNotNull(parts, "aspects", getCommaSeparated(aspects)); addPartIfNotNull(parts, "aspects", getCommaSeparated(aspects));
addPartIfNotNull(parts, "majorversion", majorVersion); addPartIfNotNull(parts, "majorversion", majorVersion);
addPartIfNotNull(parts, "overwrite", overwrite); addPartIfNotNull(parts, "overwrite", overwrite);

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId> <artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version> <version>7.308-SNAPSHOT</version>
</parent> </parent>
<dependencies> <dependencies>

View File

@@ -176,14 +176,14 @@ public class CMISResultSetRow implements ResultSetRow
context.setScores(scores); context.setScores(scores);
context.setScore(getScore()); context.setScore(getScore());
for (Column column : query.getColumns()) for (Column column : query.getColumns())
{
if (column.getAlias().equals(columnName))
{ {
//When an SCORE selector is included, score must be adapted to range 0..1 due to CMIS specification //When an SCORE selector is included, score must be adapted to range 0..1 due to CMIS specification
if (column.getFunction()!= null && column.getFunction().getName().equals(Score.NAME)) if (column.getFunction()!= null && column.getFunction().getName().equals(Score.NAME))
{ {
return getNormalisedScore(); return getNormalisedScore();
} }
else if (column.getAlias().equals(columnName))
{
return column.getFunction().getValue(column.getFunctionArguments(), context); return column.getFunction().getValue(column.getFunctionArguments(), context);
} }
// Special case for one selector - ignore any table aliases // Special case for one selector - ignore any table aliases

View File

@@ -150,10 +150,24 @@ public final class DefaultSimpleCache<K extends Serializable, V extends Object>
* @return <code>true</code> if the put resulted in a change in value, <code>false</code> otherwise. * @return <code>true</code> if the put resulted in a change in value, <code>false</code> otherwise.
*/ */
public boolean putAndCheckUpdate(K key, V value) public boolean putAndCheckUpdate(K key, V value)
{
return putAndCheckUpdate(key, value, false);
}
/**
* <code>put</code> method that may be used to check for updates in a thread-safe manner.
*
* @param includeNewCheck if true then we include the new value in the check
* @return <code>true</code> if the put resulted in a change in value,
* or if includeNewCheck is true and the put resulted in a new value,
* <code>false</code> otherwise.
*/
public boolean putAndCheckUpdate(K key, V value, boolean includeNewCheck)
{ {
AbstractMap.SimpleImmutableEntry<K, V> kvp = new AbstractMap.SimpleImmutableEntry<K, V>(key, value); AbstractMap.SimpleImmutableEntry<K, V> kvp = new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
AbstractMap.SimpleImmutableEntry<K, V> priorKVP = cache.asMap().put(key, kvp); AbstractMap.SimpleImmutableEntry<K, V> priorKVP = cache.asMap().put(key, kvp);
return priorKVP != null && (! priorKVP.equals(kvp));
return (includeNewCheck && priorKVP == null) || (priorKVP != null && (!priorKVP.equals(kvp)));
} }
@Override @Override

View File

@@ -35,6 +35,7 @@ public enum DeploymentMethod
INSTALLER, INSTALLER,
DOCKER_COMPOSE, DOCKER_COMPOSE,
HELM_CHART, HELM_CHART,
ANSIBLE,
/** /**
* The distribution zip was used to lay down the ACS artifacts * The distribution zip was used to lay down the ACS artifacts
*/ */

View File

@@ -394,6 +394,18 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
else else
{ {
// When node is copied when the aspect is applied, the sharedACLtoReplace will not match the children's ACLS
// to replace, we need to use the current one.
Long currentAcl = nodeDAO.getNodeAclId(nodeId);
if (nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
// If node has a pending acl, retrieve the sharedAclToReplace from node property. When the job calls
// this, it already does it but on move and copy operations, it uses the new parents old ACL.
sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
}
// Lazily retrieve/create the shared ACL // Lazily retrieve/create the shared ACL
if (mergeFrom == null) if (mergeFrom == null)
{ {
@@ -407,18 +419,15 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId); List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId);
if(children.size() > 0)
{
nodeDAO.setPrimaryChildrenSharedAclId(nodeId, sharedAclToReplace, mergeFrom);
}
if (!propagateOnChildren) if (!propagateOnChildren)
{ {
return; return;
} }
for (NodeIdAndAclId child : children) for (NodeIdAndAclId child : children)
{ {
Long acl = child.getAclId(); //Use the current ACL instead of the stored value, it could've been changed meanwhile
Long acl = nodeDAO.getNodeAclId(child.getId());
if (acl == null) if (acl == null)
{ {
@@ -426,12 +435,8 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
else else
{ {
// if(acl.equals(mergeFrom))
// {
// setFixedAcls(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false);
// }
// Still has old shared ACL or already replaced // Still has old shared ACL or already replaced
if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom)) if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom) || acl.equals(currentAcl))
{ {
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren); propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren);
} }
@@ -457,6 +462,21 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
} }
} }
// By doing an eager update of the direct children we canot see if another thread has changed the ACL
// between the time we get the child nodes and we update them. By updating the direct children last it is
// possible to verify if any child has changed meanwhile.
if(children.size() > 0)
{
nodeDAO.setPrimaryChildrenSharedAclId(nodeId, sharedAclToReplace, mergeFrom);
}
// When this is not executed triggered by the job, but a move or copy operation occures on a pending
// node, we don't want to apply the OLD ACL that was pending
if(nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
removePendingAclAspect(nodeId);
}
} }
} }
@@ -509,14 +529,23 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
// set ASPECT_PENDING_FIX_ACL aspect on node to be later on processed with FixedAclUpdater amd switch flag // set ASPECT_PENDING_FIX_ACL aspect on node to be later on processed with FixedAclUpdater amd switch flag
// FIXED_ACL_ASYNC_REQUIRED_KEY // FIXED_ACL_ASYNC_REQUIRED_KEY
addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom); addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom, mergeFrom);
AlfrescoTransactionSupport.bindResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY, true); AlfrescoTransactionSupport.bindResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY, true);
// stop propagating on children nodes // stop propagating on children nodes
return false; return false;
} }
private void addFixedAclPendingAspect(Long nodeId, Long sharedAclToReplace, Long inheritFrom) private void addFixedAclPendingAspect(Long nodeId, Long sharedAclToReplace, Long inheritFrom, Long mergeFrom)
{ {
//If the node already has the pending ACL aspect, just update the new inheritFrom value
if (nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
Map<QName, Serializable> pendingAclProperties = new HashMap<>();
pendingAclProperties.put(ContentModel.PROP_INHERIT_FROM_ACL, inheritFrom);
nodeDAO.addNodeProperties(nodeId, pendingAclProperties);
return;
}
Set<QName> aspect = new HashSet<>(); Set<QName> aspect = new HashSet<>();
aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL); aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL);
nodeDAO.addNodeAspects(nodeId, aspect); nodeDAO.addNodeAspects(nodeId, aspect);
@@ -530,6 +559,17 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
} }
public void removePendingAclAspect(Long nodeId)
{
Set<QName> aspects = new HashSet<>(1);
aspects.add(ContentModel.ASPECT_PENDING_FIX_ACL);
Set<QName> pendingFixAclProperties = new HashSet<>();
pendingFixAclProperties.add(ContentModel.PROP_SHARED_ACL_TO_REPLACE);
pendingFixAclProperties.add(ContentModel.PROP_INHERIT_FROM_ACL);
nodeDAO.removeNodeAspects(nodeId, aspects);
nodeDAO.removeNodeProperties(nodeId, pendingFixAclProperties);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@@ -106,4 +106,6 @@ public interface AccessControlListDAO
public void updateInheritance(Long childNodeId, Long oldParentAclId, Long newParentAclId); public void updateInheritance(Long childNodeId, Long oldParentAclId, Long newParentAclId);
public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set); public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set);
public void removePendingAclAspect(Long nodeId);
} }

View File

@@ -53,6 +53,7 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
@@ -149,7 +150,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
public void init() public void init()
{ {
onInheritPermissionsDisabledDelegate = policyComponent.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class); onInheritPermissionsDisabledDelegate = policyComponent
.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class);
} }
private class GetNodesWithAspects private class GetNodesWithAspects
@@ -262,26 +264,34 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
{ {
log.debug(String.format("Processing node %s", nodeRef)); log.debug(String.format("Processing node %s", nodeRef));
} }
final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst(); final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst();
// MNT-22009 - If node was deleted and in archive store, remove the aspect and properties and do not
// process
if (nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE))
{
accessControlListDAO.removePendingAclAspect(nodeId);
return null;
}
// retrieve acl properties from node // retrieve acl properties from node
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_INHERIT_FROM_ACL);
ContentModel.PROP_INHERIT_FROM_ACL); Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId,
ContentModel.PROP_SHARED_ACL_TO_REPLACE);
// set inheritance using retrieved prop // set inheritance using retrieved prop
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, true);
true);
nodeDAO.removeNodeAspects(nodeId, aspects); // Remove aspect
nodeDAO.removeNodeProperties(nodeId, PENDING_FIX_ACL_ASPECT_PROPS); accessControlListDAO.removePendingAclAspect(nodeId);
if (!policyIgnoreUtil.ignorePolicy(nodeRef)) if (!policyIgnoreUtil.ignorePolicy(nodeRef))
{ {
boolean transformedToAsyncOperation = toBoolean((Boolean) AlfrescoTransactionSupport.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY)); boolean transformedToAsyncOperation = toBoolean(
(Boolean) AlfrescoTransactionSupport.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY));
OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate.get(ContentModel.TYPE_BASE); OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate
.get(ContentModel.TYPE_BASE);
onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation); onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation);
} }
@@ -395,12 +405,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
AclWorkProvider provider = new AclWorkProvider(); AclWorkProvider provider = new AclWorkProvider();
AclWorker worker = new AclWorker(); AclWorker worker = new AclWorker();
BatchProcessor<NodeRef> bp = new BatchProcessor<>( BatchProcessor<NodeRef> bp = new BatchProcessor<>("FixedAclUpdater",
"FixedAclUpdater", transactionService.getRetryingTransactionHelper(), provider, numThreads, maxItemBatchSize, applicationContext,
transactionService.getRetryingTransactionHelper(),
provider,
numThreads, maxItemBatchSize,
applicationContext,
log, 100); log, 100);
int count = bp.process(worker, true); int count = bp.process(worker, true);
return count; return count;

View File

@@ -764,7 +764,7 @@ deployment.service.targetLockRefreshTime=60000
# How long to wait in mS from the last communication before deciding that deployment has failed, possibly # How long to wait in mS from the last communication before deciding that deployment has failed, possibly
# the destination is no longer available? # the destination is no longer available?
deployment.service.targetLockTimeout=3600000 deployment.service.targetLockTimeout=3600000
# Deployment method used to deploy this Alfresco instance (DEFAULT, INSTALLER, DOCKER_COMPOSE, HELM_CHART, ZIP, QUICK_START) # Deployment method used to deploy this Alfresco instance (DEFAULT, INSTALLER, DOCKER_COMPOSE, HELM_CHART, ANSIBLE, ZIP, QUICK_START)
deployment.method=DEFAULT deployment.method=DEFAULT
#Invitation Service #Invitation Service

View File

@@ -136,6 +136,7 @@
<property name="tenantAware" value="false" /> <property name="tenantAware" value="false" />
<property name="cacheStats" ref="cacheStatistics"/> <property name="cacheStats" ref="cacheStatistics"/>
<property name="cacheStatsEnabled" value="${cache.immutableEntitySharedCache.tx.statsEnabled}"/> <property name="cacheStatsEnabled" value="${cache.immutableEntitySharedCache.tx.statsEnabled}"/>
<property name="allowEqualsChecks" value="true" />
</bean> </bean>

View File

@@ -132,6 +132,45 @@ public class DefaultSimpleCacheTest extends SimpleCacheTestBase<DefaultSimpleCac
assertEquals(true, cache.putAndCheckUpdate(104, null)); assertEquals(true, cache.putAndCheckUpdate(104, null));
} }
@Test
public void putAndCheckUpdateIncludeNewCheck()
{
// Put an initial value
cache.put(101, "101");
// Update it
assertEquals(true, cache.putAndCheckUpdate(101, "99101", true));
// Check the value really was updated
assertEquals("99101", cache.get(101));
// Precondition: no value for key 102
assertFalse(cache.contains(102));
// Put a value - and test the return
assertEquals(true, cache.putAndCheckUpdate(102, "102", true));
assertEquals("102", cache.get(102));
cache.put(103, null);
assertEquals(true, cache.putAndCheckUpdate(103, "103", true));
// Repeat the put, this should not be an update
assertEquals(false, cache.putAndCheckUpdate(103, "103", true));
assertFalse(cache.contains(104));
assertEquals(true, cache.putAndCheckUpdate(104, null, true));
// Repeat putting null - still not an update, as we had that value a moment ago.
assertEquals(false, cache.putAndCheckUpdate(104, null, true));
// Now an update
assertEquals(true, cache.putAndCheckUpdate(104, "104", true));
// Another update
assertEquals(true, cache.putAndCheckUpdate(104, "99104", true));
// Another update, back to null
assertEquals(true, cache.putAndCheckUpdate(104, null, true));
// Not an update - still null
assertEquals(false, cache.putAndCheckUpdate(104, null, true));
cache.remove(104);
assertEquals(true, cache.putAndCheckUpdate(104, "104", true));
assertEquals(true, cache.putAndCheckUpdate(104, null, true));
}
// TODO: Timer-based tests are not ideal. An alternative approach is to factor out the CacheBuilder.newBuilder() // TODO: Timer-based tests are not ideal. An alternative approach is to factor out the CacheBuilder.newBuilder()
// call to a protected method, override that in this test class to return a mock and use the mock to check // call to a protected method, override that in this test class to return a mock and use the mock to check
// that the Cache is being configured correctly, e.g. assert that expireAfterWrite(int, TimeUnit) is called. // that the Cache is being configured correctly, e.g. assert that expireAfterWrite(int, TimeUnit) is called.