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>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version>
<version>7.308-SNAPSHOT</version>
</parent>
<dependencies>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>7.301-SNAPSHOT</version>
<version>7.308-SNAPSHOT</version>
</parent>
<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">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>7.301-SNAPSHOT</version>
<version>7.308-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>

View File

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

View File

@@ -1831,6 +1831,19 @@ public class NodesImpl implements Nodes
{
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);
// Create the node
@@ -2331,6 +2344,11 @@ public class NodesImpl implements Nodes
private void handleNodeRename(Map<QName, Serializable> props, NodeRef nodeRef)
{
Serializable nameProp = props.get(ContentModel.PROP_NAME);
handleNodeRename(nameProp, nodeRef);
}
private void handleNodeRename(Serializable nameProp, NodeRef nodeRef)
{
if ((nameProp != null))
{
String currentName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
@@ -2705,6 +2723,7 @@ public class NodesImpl implements Nodes
String fileName = parameters.getParameter(PARAM_NAME);
if (fileName != null)
{
handleNodeRename(fileName, nodeRef);
// optionally rename, before updating the content
nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, fileName);
}
@@ -2891,6 +2910,7 @@ public class NodesImpl implements Nodes
String versionComment = null;
String relativePath = null;
String renditionNames = null;
boolean versioningEnabled = true;
Map<String, Object> qnameStrProps = new HashMap<>();
Map<QName, Serializable> properties = null;
@@ -2947,6 +2967,19 @@ public class NodesImpl implements Nodes
case "renditions":
renditionNames = getStringOrNull(field.getValue());
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:
{
@@ -3019,12 +3052,14 @@ public class NodesImpl implements Nodes
throw new ConstraintViolatedException(fileName + " already exists.");
}
}
// Note: pending REPO-159, we currently auto-enable versioning on new upload (but not when creating empty file)
if (versionMajor == null)
{
versionMajor = true;
}
// MNT-22036 add versioningEnabled property for newly created nodes.
versionMajor = versioningEnabled ? versionMajor : null;
// Create a new file.
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);
}
@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
{
setRequestContext(user1);
@@ -5708,6 +5981,55 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
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
{
Map params = new HashMap<>();

View File

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

View File

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

View File

@@ -177,13 +177,13 @@ public class CMISResultSetRow implements ResultSetRow
context.setScore(getScore());
for (Column column : query.getColumns())
{
// 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))
{
return getNormalisedScore();
}
else if (column.getAlias().equals(columnName))
if (column.getAlias().equals(columnName))
{
//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))
{
return getNormalisedScore();
}
return column.getFunction().getValue(column.getFunctionArguments(), context);
}
// Special case for one selector - ignore any table aliases

View File

@@ -1,28 +1,28 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.cache;
import java.io.Serializable;
@@ -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.
*/
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> priorKVP = cache.asMap().put(key, kvp);
return priorKVP != null && (! priorKVP.equals(kvp));
return (includeNewCheck && priorKVP == null) || (priorKVP != null && (!priorKVP.equals(kvp)));
}
@Override

View File

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

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.domain.permissions;
package org.alfresco.repo.domain.permissions;
import java.io.Serializable;
import java.util.ArrayList;
@@ -392,8 +392,20 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
{
return;
}
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
if (mergeFrom == null)
{
@@ -405,33 +417,26 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
nodeDAO.setNodeAclId(nodeId, mergeFrom);
}
List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId);
if(children.size() > 0)
{
nodeDAO.setPrimaryChildrenSharedAclId(nodeId, sharedAclToReplace, mergeFrom);
}
List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId);
if (!propagateOnChildren)
{
return;
}
}
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)
{
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren);
}
else
{
// if(acl.equals(mergeFrom))
// {
// setFixedAcls(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false);
// }
// 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);
}
@@ -456,7 +461,22 @@ 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,25 +529,45 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
}
// set ASPECT_PENDING_FIX_ACL aspect on node to be later on processed with FixedAclUpdater amd switch flag
// FIXED_ACL_ASYNC_REQUIRED_KEY
addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom);
addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom, mergeFrom);
AlfrescoTransactionSupport.bindResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY, true);
// stop propagating on children nodes
return false;
}
private void addFixedAclPendingAspect(Long nodeId, Long sharedAclToReplace, Long inheritFrom)
{
Set<QName> aspect = new HashSet<>();
aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL);
nodeDAO.addNodeAspects(nodeId, aspect);
Map<QName, Serializable> pendingAclProperties = new HashMap<>();
pendingAclProperties.put(ContentModel.PROP_SHARED_ACL_TO_REPLACE, sharedAclToReplace);
pendingAclProperties.put(ContentModel.PROP_INHERIT_FROM_ACL, inheritFrom);
nodeDAO.addNodeProperties(nodeId, pendingAclProperties);
if (log.isDebugEnabled())
{
log.debug("Set Fixed Acl Pending : " + nodeId + " " + nodeDAO.getNodePair(nodeId).getSecond());
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<>();
aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL);
nodeDAO.addNodeAspects(nodeId, aspect);
Map<QName, Serializable> pendingAclProperties = new HashMap<>();
pendingAclProperties.put(ContentModel.PROP_SHARED_ACL_TO_REPLACE, sharedAclToReplace);
pendingAclProperties.put(ContentModel.PROP_INHERIT_FROM_ACL, inheritFrom);
nodeDAO.addNodeProperties(nodeId, pendingAclProperties);
if (log.isDebugEnabled())
{
log.debug("Set Fixed Acl Pending : " + nodeId + " " + nodeDAO.getNodePair(nodeId).getSecond());
}
}
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);
}
/**

View File

@@ -105,5 +105,7 @@ public interface AccessControlListDAO
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.TransactionListenerAdapter;
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.QName;
import org.alfresco.service.transaction.TransactionService;
@@ -91,8 +92,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
private int maxItemBatchSize = 100;
private int numThreads = 4;
private ClassPolicyDelegate<OnInheritPermissionsDisabled> onInheritPermissionsDisabledDelegate;
private PolicyComponent policyComponent;
private ClassPolicyDelegate<OnInheritPermissionsDisabled> onInheritPermissionsDisabledDelegate;
private PolicyComponent policyComponent;
private PolicyIgnoreUtil policyIgnoreUtil;
public void setNumThreads(int numThreads)
@@ -135,8 +136,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
{
this.lockTimeToLive = lockTimeToLive;
this.lockRefreshTime = lockTimeToLive / 2;
}
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
@@ -149,7 +150,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
public void init()
{
onInheritPermissionsDisabledDelegate = policyComponent.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class);
onInheritPermissionsDisabledDelegate = policyComponent
.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class);
}
private class GetNodesWithAspects
@@ -262,26 +264,34 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
{
log.debug(String.format("Processing node %s", nodeRef));
}
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
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId,
ContentModel.PROP_INHERIT_FROM_ACL);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId,
ContentModel.PROP_SHARED_ACL_TO_REPLACE);
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_INHERIT_FROM_ACL);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
// set inheritance using retrieved prop
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace,
true);
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, true);
// Remove aspect
accessControlListDAO.removePendingAclAspect(nodeId);
nodeDAO.removeNodeAspects(nodeId, aspects);
nodeDAO.removeNodeProperties(nodeId, PENDING_FIX_ACL_ASPECT_PROPS);
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);
}
@@ -395,12 +405,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
AclWorkProvider provider = new AclWorkProvider();
AclWorker worker = new AclWorker();
BatchProcessor<NodeRef> bp = new BatchProcessor<>(
"FixedAclUpdater",
transactionService.getRetryingTransactionHelper(),
provider,
numThreads, maxItemBatchSize,
applicationContext,
BatchProcessor<NodeRef> bp = new BatchProcessor<>("FixedAclUpdater",
transactionService.getRetryingTransactionHelper(), provider, numThreads, maxItemBatchSize, applicationContext,
log, 100);
int count = bp.process(worker, true);
return count;
@@ -413,7 +419,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
finally
{
jobLockRefreshCallback.isActive.set(false);
if(lockToken != null)
if (lockToken != null)
{
jobLockService.releaseLock(lockToken, lockQName);
}

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
# the destination is no longer available?
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
#Invitation Service

View File

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

View File

@@ -1,28 +1,28 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.cache;
import static org.junit.Assert.*;
@@ -131,6 +131,45 @@ public class DefaultSimpleCacheTest extends SimpleCacheTestBase<DefaultSimpleCac
assertEquals(false, cache.putAndCheckUpdate(104, "104"));
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()
// call to a protected method, override that in this test class to return a mock and use the mock to check
@@ -198,6 +237,6 @@ public class DefaultSimpleCacheTest extends SimpleCacheTestBase<DefaultSimpleCac
{
throw new RuntimeException(error);
}
}
}
}