Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2)

123064 jvonka: Nodes (FileFolder) API - WIP create or update (versionable) content with optional majorVersion &/or comment
   - via query params for PUT
   - via form-data fields for POST (when overwrite field is true)
   - return updated "cm:versionLabel"
   - TODO tests + api def (hence WIP)
   RA-690, RA-637, RA-640


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126525 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-05-10 11:20:27 +00:00
parent 38788678c1
commit 8e728ea79c
3 changed files with 106 additions and 21 deletions

View File

@@ -208,4 +208,7 @@ public interface Nodes
String PARAM_MIMETYPE = "mimeType";
String PARAM_SIZEINBYTES = "sizeInBytes";
String PARAM_NODETYPE = "nodeType";
String PARAM_VERSION_MAJOR = "majorVersion"; // true if major, false if minor
String PARAM_VERSION_COMMENT = "comment";
}

View File

@@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -38,12 +39,15 @@ import org.alfresco.model.ContentModel;
import org.alfresco.model.QuickShareModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.action.executer.ContentMetadataExtracter;
import org.alfresco.repo.content.ContentLimitViolationException;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.QuickShareLinks;
@@ -64,6 +68,7 @@ import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.RequestEntityTooLargeException;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
@@ -104,6 +109,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.usage.ContentQuotaException;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
@@ -120,7 +126,9 @@ import org.springframework.http.MediaType;
*
* Note:
* This class was originally used for returning some basic node info when listing Favourites.
* It has now been re-purposed and extended to implement the new File Folder (RESTful) API.
*
* It has now been re-purposed and extended to implement the new Nodes (RESTful) API for
* managing files & folders, as well as custom node types.
*
* @author steveglover
* @author janv
@@ -151,6 +159,8 @@ public class NodesImpl implements Nodes
private OwnableService ownableService;
private AuthorityService authorityService;
private BehaviourFilter behaviourFilter;
// note: circular - Nodes/QuickShareLinks currently use each other (albeit for different methods)
private QuickShareLinks quickShareLinks;
@@ -198,6 +208,11 @@ public class NodesImpl implements Nodes
this.sr = sr;
}
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
public void setRepositoryHelper(Repository repositoryHelper)
{
this.repositoryHelper = repositoryHelper;
@@ -1557,11 +1572,69 @@ public class NodesImpl implements Nodes
throw new InvalidArgumentException("NodeId of content is expected: " + nodeRef.getId());
}
Boolean versionMajor = null;
String str = parameters.getParameter(PARAM_VERSION_MAJOR);
if (str != null)
{
versionMajor = new Boolean(str);
}
String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT);
return updateExistingFile(nodeRef, contentInfo, stream, parameters, versionMajor, versionComment);
}
private Node updateExistingFile(NodeRef nodeRef, BasicContentInfo contentInfo, InputStream stream, Parameters parameters, Boolean versionMajor, String versionComment)
{
boolean isVersioned = versionService.isVersioned(nodeRef);
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
try
{
writeContent(nodeRef, contentInfo, stream);
if ((isVersioned) || (versionMajor != null) || (versionComment != null) )
{
VersionType versionType = VersionType.MINOR;
if ((versionMajor != null) && (versionMajor == true))
{
versionType = VersionType.MAJOR;
}
createVersion(nodeRef, isVersioned, versionType, versionComment);
}
extractMetadata(nodeRef);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
}
return getFolderOrDocumentFullInfo(nodeRef, null, null, parameters);
}
private void writeContent(NodeRef nodeRef, BasicContentInfo contentInfo, InputStream stream)
{
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
setWriterContentType(writer, new ContentInfoWrapper(contentInfo), nodeRef, true);
writer.putContent(stream);
}
return getFolderOrDocumentFullInfo(nodeRef, null, null, parameters);
protected void createVersion(NodeRef nodeRef, boolean isVersioned, VersionType versionType, String reason)
{
if (! isVersioned)
{
// Ensure the file is versionable (autoVersion = true, autoVersionProps = false)
ensureVersioningEnabled(nodeRef, true, false);
}
Map<String, Serializable> versionProperties = new HashMap<>(2);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType);
if (reason != null)
{
versionProperties.put(VersionModel.PROP_DESCRIPTION, reason);
}
versionService.createVersion(nodeRef, versionProperties);
}
private void setWriterContentType(ContentWriter writer, ContentInfoWrapper contentInfo, NodeRef nodeRef, boolean guessEncodingIfNull)
@@ -1591,6 +1664,7 @@ public class NodesImpl implements Nodes
}
}
@Override
public Node upload(String parentFolderNodeId, FormData formData, Parameters parameters)
{
@@ -1611,6 +1685,8 @@ public class NodesImpl implements Nodes
boolean autoRename = false;
QName nodeTypeQName = null;
boolean overwrite = false; // If a fileName clashes for a versionable file
Boolean majorVersion = null;
String versionComment = null;
Map<String, Object> qnameStrProps = new HashMap<>();
Map<QName, Serializable> properties = null;
@@ -1642,9 +1718,17 @@ public class NodesImpl implements Nodes
}
break;
// case "overwrite":
// overwrite = Boolean.valueOf(field.getValue());
// break;
case "overwrite":
overwrite = Boolean.valueOf(field.getValue());
break;
case "majorversion":
majorVersion = Boolean.valueOf(field.getValue());
break;
case "comment":
versionComment = getStringOrNull(field.getValue());
break;
default:
{
@@ -1684,28 +1768,23 @@ public class NodesImpl implements Nodes
NodeRef existingFile = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, fileName);
if (existingFile != null)
{
// TODO throw 400 error if both autoRename and overwrite are true ?
// File already exists, decide what to do
if (autoRename)
{
// attempt to find a unique name
fileName = findUniqueName(parentNodeRef, fileName);
}
// TODO uncomment when we decide on uploading a new version vs overwriting
// else if (overwrite && nodeService.hasAspect(existingFile, ContentModel.ASPECT_VERSIONABLE))
// {
// // Upload component was configured to overwrite files if name clashes
// write(existingFile, content);
//
// // Extract the metadata (The overwrite policy controls
// // which if any parts of the document's properties are updated from this)
// extractMetadata(existingFile);
//
// // Do not clean formData temp files to
// // allow for retries. Temp files will be deleted later
// // when GC call DiskFileItem#finalize() method or by temp file cleaner.
// return createUploadResponse(parentNodeRef, existingFile);
// }
else if (overwrite && nodeService.hasAspect(existingFile, ContentModel.ASPECT_VERSIONABLE))
{
// overwrite existing (versionable) file
BasicContentInfo contentInfo = new ContentInfoImpl(content.getMimetype(), content.getEncoding(), -1, null);
return updateExistingFile(existingFile, contentInfo, content.getInputStream(), parameters, majorVersion, versionComment);
}
else
{
// name clash (and no autoRename or overwrite)
throw new ConstraintViolatedException(fileName + " already exists.");
}
}
@@ -1814,10 +1893,12 @@ public class NodesImpl implements Nodes
/**
* Extracts the given node metadata asynchronously.
*
* The overwrite policy controls which if any parts of the document's properties are updated from this.
*/
private void extractMetadata(NodeRef nodeRef)
{
final String actionName = "extract-metadata";
final String actionName = ContentMetadataExtracter.EXECUTOR_NAME;
ActionDefinition actionDef = actionService.getActionDefinition(actionName);
if (actionDef != null)
{