mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged HEAD (5.2) to 5.2.N (5.2.1)
126348 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 118663 jvonka: Merge from DEV/SABRE_JANV1 (part 1) File Folder API (PoC - experimental WIP) - relates to RA-613 - initial file folder CRUD including - * list folder children (minimal info by default) with sorting, paging & optional isFolder=true/false - * create folders or (empty) files - * get node info with id, name, nodeType, auditable props, properties (not sys:referencable), aspectNames, ... - * put node info - * put content (upload file) - * get content (download file) - * delete node (cascade for folder) - * support for well-known folder alias, -root-, -my-, -shared- - TODO add tests git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126693 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -25,7 +25,9 @@
|
||||
*/
|
||||
package org.alfresco.rest.api.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -34,27 +36,35 @@ import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.opencmis.CMISConnector;
|
||||
import org.alfresco.opencmis.CMISNodeInfoImpl;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.query.PagingResults;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.model.Document;
|
||||
import org.alfresco.rest.api.model.Folder;
|
||||
import org.alfresco.rest.api.model.Node;
|
||||
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
|
||||
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
|
||||
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
|
||||
import org.alfresco.rest.framework.resource.parameters.Paging;
|
||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.model.FileInfo;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.Path.Element;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
|
||||
import org.apache.chemistry.opencmis.commons.data.Properties;
|
||||
import org.apache.chemistry.opencmis.commons.data.PropertyData;
|
||||
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
|
||||
|
||||
/**
|
||||
* Centralises access to node services and maps between representations.
|
||||
* Centralises access to file/folder/node services and maps between representations.
|
||||
*
|
||||
* @author steveglover
|
||||
* @author janv
|
||||
*
|
||||
* @since publicapi1.0
|
||||
*/
|
||||
public class NodesImpl implements Nodes
|
||||
@@ -65,9 +75,16 @@ public class NodesImpl implements Nodes
|
||||
DOCUMENT, FOLDER;
|
||||
};
|
||||
|
||||
private final static String PATH_ROOT = "-root-";
|
||||
private final static String PATH_MY = "-my-";
|
||||
|
||||
private NodeService nodeService;
|
||||
private DictionaryService dictionaryService;
|
||||
private CMISConnector cmisConnector;
|
||||
private FileFolderService fileFolderService;
|
||||
private Repository repositoryHelper;
|
||||
private NamespaceService namespaceService;
|
||||
private PermissionService permissionService;
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
@@ -83,6 +100,26 @@ public class NodesImpl implements Nodes
|
||||
{
|
||||
this.cmisConnector = cmisConnector;
|
||||
}
|
||||
|
||||
public void setFileFolderService(FileFolderService fileFolderService)
|
||||
{
|
||||
this.fileFolderService = fileFolderService;
|
||||
}
|
||||
|
||||
public void setRepositoryHelper(Repository repositoryHelper)
|
||||
{
|
||||
this.repositoryHelper = repositoryHelper;
|
||||
}
|
||||
|
||||
public void setNamespaceService(NamespaceService namespaceService)
|
||||
{
|
||||
this.namespaceService = namespaceService;
|
||||
}
|
||||
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
@@ -159,23 +196,29 @@ public class NodesImpl implements Nodes
|
||||
{
|
||||
NodeRef nodeRef = validateNode(nodeId);
|
||||
|
||||
return new Node(nodeRef, nodeService.getProperties(nodeRef));
|
||||
return new Node(nodeRef, nodeService.getProperties(nodeRef), namespaceService);
|
||||
}
|
||||
|
||||
public Node getNode(NodeRef nodeRef)
|
||||
{
|
||||
return new Node(nodeRef, nodeService.getProperties(nodeRef));
|
||||
return new Node(nodeRef, nodeService.getProperties(nodeRef), namespaceService);
|
||||
}
|
||||
|
||||
private Type getType(NodeRef nodeRef)
|
||||
{
|
||||
QName type = nodeService.getType(nodeRef);
|
||||
return getType(nodeService.getType(nodeRef));
|
||||
}
|
||||
|
||||
private Type getType(QName type)
|
||||
{
|
||||
boolean isContainer = Boolean.valueOf((dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
|
||||
!dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER)));
|
||||
return isContainer ? Type.FOLDER : Type.DOCUMENT;
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO filter CMIS properties
|
||||
// TODO review & optimise - do we really need to go via CMIS properties !?
|
||||
private Properties getCMISProperties(NodeRef nodeRef)
|
||||
{
|
||||
CMISNodeInfoImpl nodeInfo = cmisConnector.createNodeInfo(nodeRef);
|
||||
@@ -217,6 +260,7 @@ public class NodesImpl implements Nodes
|
||||
|
||||
return wrapProperties;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the public api representation of a document.
|
||||
@@ -226,10 +270,11 @@ public class NodesImpl implements Nodes
|
||||
public Document getDocument(NodeRef nodeRef)
|
||||
{
|
||||
Type type = getType(nodeRef);
|
||||
if(type.equals(Type.DOCUMENT))
|
||||
if (type.equals(Type.DOCUMENT))
|
||||
{
|
||||
Properties properties = getCMISProperties(nodeRef);
|
||||
Document doc = new Document(nodeRef, properties);
|
||||
//Properties properties = getCMISProperties(nodeRef);
|
||||
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
||||
Document doc = new Document(nodeRef, properties, namespaceService);
|
||||
return doc;
|
||||
}
|
||||
else
|
||||
@@ -246,10 +291,11 @@ public class NodesImpl implements Nodes
|
||||
public Folder getFolder(NodeRef nodeRef)
|
||||
{
|
||||
Type type = getType(nodeRef);
|
||||
if(type.equals(Type.FOLDER))
|
||||
if (type.equals(Type.FOLDER))
|
||||
{
|
||||
Properties properties = getCMISProperties(nodeRef);
|
||||
Folder folder = new Folder(nodeRef, properties);
|
||||
//Properties properties = getCMISProperties(nodeRef);
|
||||
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
||||
Folder folder = new Folder(nodeRef, properties, namespaceService);
|
||||
return folder;
|
||||
}
|
||||
else
|
||||
@@ -257,4 +303,223 @@ public class NodesImpl implements Nodes
|
||||
throw new InvalidArgumentException("Node is not a folder");
|
||||
}
|
||||
}
|
||||
|
||||
private NodeRef validateOrLookupNode(String nodeId, String path) {
|
||||
final NodeRef parentNodeRef;
|
||||
if (nodeId.equals(PATH_ROOT))
|
||||
{
|
||||
parentNodeRef = repositoryHelper.getCompanyHome();
|
||||
}
|
||||
else if (nodeId.equals(PATH_MY))
|
||||
{
|
||||
NodeRef person = repositoryHelper.getPerson();
|
||||
if (person == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Unexpected: cannot use "+PATH_MY);
|
||||
}
|
||||
parentNodeRef = repositoryHelper.getUserHome(person);
|
||||
}
|
||||
else
|
||||
{
|
||||
parentNodeRef = validateNode(nodeId);
|
||||
}
|
||||
return parentNodeRef;
|
||||
}
|
||||
|
||||
public Node getFolderOrDocument(String nodeId, Parameters parameters)
|
||||
{
|
||||
String path = parameters.getParameter("path");
|
||||
|
||||
boolean incPrimaryPath = false;
|
||||
String str = parameters.getParameter("incPrimaryPath");
|
||||
if (str != null) {
|
||||
incPrimaryPath = new Boolean(str);
|
||||
}
|
||||
|
||||
NodeRef nodeRef = validateOrLookupNode(nodeId, path);
|
||||
QName typeQName = nodeService.getType(nodeRef);
|
||||
return getFolderOrDocument(nodeRef, typeQName, incPrimaryPath);
|
||||
}
|
||||
|
||||
private Node getFolderOrDocument(NodeRef nodeRef, QName typeQName,boolean incPrimaryPath)
|
||||
{
|
||||
String primaryPath = null;
|
||||
if (incPrimaryPath)
|
||||
{
|
||||
org.alfresco.service.cmr.repository.Path pp = nodeService.getPath(nodeRef);
|
||||
|
||||
// Remove "app:company_home" (2nd element)
|
||||
int ppSize = pp.size();
|
||||
if (ppSize > 1) {
|
||||
if (ppSize == 2) {
|
||||
pp = pp.subPath(0, 0);
|
||||
}
|
||||
else {
|
||||
Element rootElement = pp.get(0);
|
||||
pp = pp.subPath(2, ppSize-1).prepend(rootElement);
|
||||
}
|
||||
}
|
||||
|
||||
primaryPath = pp.toDisplayPath(nodeService, permissionService); // note: slower (hence optional when getting node info)
|
||||
}
|
||||
|
||||
Node node = null;
|
||||
Type type = getType(typeQName);
|
||||
|
||||
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
||||
|
||||
if (type.equals(Type.DOCUMENT))
|
||||
{
|
||||
//Properties properties = getCMISProperties(nodeRef);
|
||||
node = new Document(nodeRef, properties, namespaceService);
|
||||
}
|
||||
else if (type.equals(Type.FOLDER))
|
||||
{
|
||||
// container/folder
|
||||
//Properties properties = getCMISProperties(nodeRef);
|
||||
node = new Folder(nodeRef, properties, namespaceService);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidArgumentException("Node is not a folder or file");
|
||||
}
|
||||
|
||||
node.setType(typeQName.toPrefixString(namespaceService));
|
||||
node.setPrimaryPath(primaryPath); // optional - can be null
|
||||
return node;
|
||||
}
|
||||
|
||||
public CollectionWithPagingInfo<Node> getChildren(String parentFolderNodeId, Parameters parameters)
|
||||
{
|
||||
// TODO consider using: where=(exists(target/file)) / where=(exists(target/file))
|
||||
// instead of: includeFiles=true / includeFolders=true
|
||||
|
||||
boolean includeFolders = true;
|
||||
String str = parameters.getParameter("includeFolders");
|
||||
if (str != null) {
|
||||
includeFolders = new Boolean(str);
|
||||
}
|
||||
|
||||
boolean includeFiles = true;
|
||||
str = parameters.getParameter("includeFiles");
|
||||
if (str != null) {
|
||||
includeFiles = new Boolean(str);
|
||||
}
|
||||
|
||||
String path = parameters.getParameter("path");
|
||||
|
||||
Paging paging = parameters.getPaging();
|
||||
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, path);
|
||||
|
||||
final Set<QName> folders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER));
|
||||
if (! nodeMatches(parentNodeRef, folders, null))
|
||||
{
|
||||
throw new InvalidArgumentException("NodeId of folder is expected");
|
||||
}
|
||||
|
||||
PagingRequest pagingRequest = Util.getPagingRequest(paging);
|
||||
|
||||
final PagingResults<FileInfo> pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, null, null, pagingRequest);
|
||||
|
||||
final List<FileInfo> page = pagingResults.getPage();
|
||||
List<Node> nodes = new AbstractList<Node>()
|
||||
{
|
||||
@Override
|
||||
public Node get(int index)
|
||||
{
|
||||
FileInfo fInfo = page.get(index);
|
||||
return getFolderOrDocument(fInfo.getNodeRef(), fInfo.getType(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return page.size();
|
||||
}
|
||||
};
|
||||
|
||||
return CollectionWithPagingInfo.asPaged(paging, nodes, pagingResults.hasMoreItems(), pagingResults.getTotalResultCount().getFirst());
|
||||
}
|
||||
|
||||
public void deleteNode(String nodeId)
|
||||
{
|
||||
NodeRef nodeRef = validateNode(nodeId);
|
||||
fileFolderService.delete(nodeRef);
|
||||
}
|
||||
|
||||
public Folder createFolder(String parentFolderNodeId, Folder folderInfo, Parameters parameters)
|
||||
{
|
||||
final NodeRef parentNodeRef = validateNode(parentFolderNodeId);
|
||||
|
||||
final Set<QName> folders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER));
|
||||
if (! nodeMatches(parentNodeRef, folders, null))
|
||||
{
|
||||
throw new InvalidArgumentException("NodeId of folder is expected");
|
||||
}
|
||||
|
||||
String folderName = folderInfo.getName();
|
||||
String folderType = folderInfo.getType();
|
||||
if (folderType == null) {
|
||||
folderType = "cm:folder";
|
||||
}
|
||||
|
||||
QName folderTypeQName = QName.resolveToQName(namespaceService, folderType);
|
||||
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(10);
|
||||
|
||||
props.put(ContentModel.PROP_NAME, folderName);
|
||||
|
||||
String title = folderInfo.getTitle();
|
||||
if ((title != null) && (! title.isEmpty())) {
|
||||
props.put(ContentModel.PROP_TITLE, title);
|
||||
}
|
||||
|
||||
String description = folderInfo.getDescription();
|
||||
if ((description != null) && (! description.isEmpty())) {
|
||||
props.put(ContentModel.PROP_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
// TODO other custom properties !!
|
||||
|
||||
QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(folderName));
|
||||
|
||||
NodeRef nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, assocQName, folderTypeQName, props).getChildRef();
|
||||
|
||||
return (Folder) getFolderOrDocument(nodeRef.getId(), parameters);
|
||||
}
|
||||
|
||||
public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters) {
|
||||
|
||||
final NodeRef nodeRef = validateNode(nodeId);
|
||||
|
||||
final Set<QName> fileOrFolder = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER, ContentModel.TYPE_CONTENT));
|
||||
if (! nodeMatches(nodeRef, fileOrFolder, null))
|
||||
{
|
||||
throw new InvalidArgumentException("NodeId of file or folder is expected");
|
||||
}
|
||||
|
||||
Map<QName, Serializable> props = new HashMap<>(10);
|
||||
|
||||
String name = nodeInfo.getName();
|
||||
if ((name != null) && (! name.isEmpty())) {
|
||||
// note: this is equivalent of a rename within target folder
|
||||
props.put(ContentModel.PROP_NAME, name);
|
||||
}
|
||||
|
||||
String title = nodeInfo.getTitle();
|
||||
if ((title != null) && (! title.isEmpty())) {
|
||||
props.put(ContentModel.PROP_TITLE, title);
|
||||
}
|
||||
|
||||
String description = nodeInfo.getDescription();
|
||||
if ((description != null) && (! description.isEmpty())) {
|
||||
props.put(ContentModel.PROP_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
if (props.size() > 0) {
|
||||
nodeService.addProperties(nodeRef, props);
|
||||
}
|
||||
|
||||
return getFolderOrDocument(nodeRef.getId(), parameters);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user