diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml
index 789323b30c..f4985b72d9 100644
--- a/config/alfresco/public-rest-context.xml
+++ b/config/alfresco/public-rest-context.xml
@@ -430,13 +430,8 @@
-
-
-
-
-
-
-
+
+
diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java
index 42853ca643..075282251f 100644
--- a/source/java/org/alfresco/rest/api/Nodes.java
+++ b/source/java/org/alfresco/rest/api/Nodes.java
@@ -18,11 +18,14 @@
*/
package org.alfresco.rest.api;
+import java.io.InputStream;
import java.util.Set;
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.resource.content.BasicContentInfo;
+import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -89,13 +92,14 @@ public interface Nodes
void deleteNode(String nodeId);
/**
+ * Create node(s) - folder or (empty) file
*
* @param parentFolderNodeId
- * @param folderInfo
+ * @param nodeInfo
* @param parameters
* @return
*/
- Folder createFolder(String parentFolderNodeId, Folder folderInfo, Parameters parameters);
+ Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters);
/**
*
@@ -105,4 +109,10 @@ public interface Nodes
* @return
*/
Node updateNode(String nodeId, Node entity, Parameters parameters);
+
+ // TODO update REST fwk - to optionally support "attachment" (Content-Disposition) header
+ BinaryResource getContent(String fileNodeId, Parameters parameters);
+
+ // TODO update REST fwk - to optionally support return of json
+ void updateContent(String fileNodeId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters);
}
diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
index 0b1d74ea8d..491a86d924 100644
--- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java
+++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
@@ -16,60 +16,59 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
*/
-/*
- * Copyright (C) 2005-2012 Alfresco Software Limited.
- *
- * This file is part of Alfresco
- *
- * 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 .
- */
package org.alfresco.rest.api.impl;
+import org.alfresco.model.ContentModel;
+import org.alfresco.query.PagingRequest;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.model.Repository;
+import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery;
+import org.alfresco.rest.antlr.WhereClauseParser;
+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.api.model.PathInfo;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.rest.framework.resource.content.BasicContentInfo;
+import org.alfresco.rest.framework.resource.content.BinaryResource;
+import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
+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.rest.framework.resource.parameters.SortColumn;
+import org.alfresco.rest.framework.resource.parameters.where.Query;
+import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
+import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
+import org.alfresco.service.ServiceRegistry;
+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.model.FileNotFoundException;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
+
+import java.io.InputStream;
import java.io.Serializable;
import java.util.AbstractList;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.alfresco.model.ContentModel;
-import org.alfresco.opencmis.CMISConnector;
-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;
-
/**
* Centralises access to file/folder/node services and maps between representations.
*
@@ -88,51 +87,86 @@ public class NodesImpl implements Nodes
private final static String PATH_ROOT = "-root-";
private final static String PATH_MY = "-my-";
+ private final static String PATH_SHARED = "-shared-";
private NodeService nodeService;
private DictionaryService dictionaryService;
- private CMISConnector cmisConnector;
private FileFolderService fileFolderService;
- private Repository repositoryHelper;
private NamespaceService namespaceService;
private PermissionService permissionService;
+ private Repository repositoryHelper;
+ private ServiceRegistry sr;
- public void setDictionaryService(DictionaryService dictionaryService)
- {
- this.dictionaryService = dictionaryService;
- }
+ public void setServiceRegistry(ServiceRegistry sr) {
+ this.sr = sr;
- public void setNodeService(NodeService nodeService)
- {
- this.nodeService = nodeService;
- }
-
- public void setCmisConnector(CMISConnector cmisConnector)
- {
- 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;
- }
+ this.namespaceService = sr.getNamespaceService();
+ this.fileFolderService = sr.getFileFolderService();
+ this.nodeService = sr.getNodeService();
+ this.permissionService = sr.getPermissionService();
+ this.dictionaryService = sr.getDictionaryService();
+ }
- /*
+ public void setRepositoryHelper(Repository repositoryHelper)
+ {
+ this.repositoryHelper = repositoryHelper;
+ }
+
+ private static final List EXCLUDED_ASPECTS = Arrays.asList(
+ ContentModel.ASPECT_REFERENCEABLE,
+ ContentModel.ASPECT_LOCALIZED);
+
+ private static final List EXCLUDED_PROPS = Arrays.asList(
+ // top-level basic info
+ ContentModel.PROP_NAME,
+ ContentModel.PROP_MODIFIER,
+ ContentModel.PROP_MODIFIED,
+ ContentModel.PROP_CREATOR,
+ ContentModel.PROP_CREATED,
+ ContentModel.PROP_CONTENT,
+ // sys:localized
+ ContentModel.PROP_LOCALE,
+ // sys:referenceable
+ ContentModel.PROP_NODE_UUID,
+ ContentModel.PROP_STORE_IDENTIFIER,
+ ContentModel.PROP_STORE_PROTOCOL,
+ ContentModel.PROP_NODE_DBID,
+ // other - TBC
+ ContentModel.PROP_INITIAL_VERSION,
+ ContentModel.PROP_AUTO_VERSION_PROPS,
+ ContentModel.PROP_AUTO_VERSION);
+
+ private final static String PARAM_ISFOLDER = "isFolder";
+ private final static String PARAM_NAME = "name";
+ private final static String PARAM_CREATEDAT = "createdAt";
+ private final static String PARAM_MODIFIEDAT = "modifiedAt";
+ private final static String PARAM_CREATEBYUSER = "createdByUser";
+ private final static String PARAM_MODIFIEDBYUSER = "modifiedByUser";
+ private final static String PARAM_MIMETYPE = "mimeType";
+ private final static String PARAM_SIZEINBYTES = "sizeInBytes";
+ private final static String PARAM_NODETYPE = "nodeType";
+
+ private final static Map MAP_PARAM_QNAME;
+ static {
+ Map aMap = new HashMap<>(9);
+
+ aMap.put(PARAM_ISFOLDER, GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER);
+ aMap.put(PARAM_NAME, ContentModel.PROP_NAME);
+ aMap.put(PARAM_CREATEDAT, ContentModel.PROP_CREATED);
+ aMap.put(PARAM_MODIFIEDAT, ContentModel.PROP_MODIFIED);
+ aMap.put(PARAM_CREATEBYUSER, ContentModel.PROP_CREATOR);
+ aMap.put(PARAM_MODIFIEDBYUSER, ContentModel.PROP_MODIFIER);
+ aMap.put(PARAM_MIMETYPE, GetChildrenCannedQuery.SORT_QNAME_CONTENT_MIMETYPE);
+ aMap.put(PARAM_SIZEINBYTES, GetChildrenCannedQuery.SORT_QNAME_CONTENT_SIZE);
+ aMap.put(PARAM_NODETYPE, GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE);
+
+ MAP_PARAM_QNAME = Collections.unmodifiableMap(aMap);
+ }
+
+ private final static Set LIST_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES =
+ new HashSet<>(Arrays.asList(new String[] {PARAM_ISFOLDER}));
+
+ /*
*
* Note: assumes workspace://SpacesStore
*/
@@ -163,7 +197,7 @@ public class NodesImpl implements Nodes
public NodeRef validateNode(NodeRef nodeRef)
{
- if(!nodeService.exists(nodeRef))
+ if (! nodeService.exists(nodeRef))
{
throw new EntityNotFoundException(nodeRef.getId());
}
@@ -172,27 +206,30 @@ public class NodesImpl implements Nodes
}
public boolean nodeMatches(NodeRef nodeRef, Set expectedTypes, Set excludedTypes)
- {
- if(!nodeService.exists(nodeRef))
- {
- throw new EntityNotFoundException(nodeRef.getId());
- }
+ {
+ if (! nodeService.exists(nodeRef))
+ {
+ throw new EntityNotFoundException(nodeRef.getId());
+ }
- QName type = nodeService.getType(nodeRef);
+ return typeMatches(nodeService.getType(nodeRef), expectedTypes, excludedTypes);
+ }
- Set allExpectedTypes = new HashSet();
- if(expectedTypes != null)
+ protected boolean typeMatches(QName type, Set expectedTypes, Set excludedTypes)
+ {
+ Set allExpectedTypes = new HashSet<>();
+ if (expectedTypes != null)
{
- for(QName expectedType : expectedTypes)
+ for (QName expectedType : expectedTypes)
{
allExpectedTypes.addAll(dictionaryService.getSubTypes(expectedType, true));
}
}
- Set allExcludedTypes = new HashSet();
- if(excludedTypes != null)
+ Set allExcludedTypes = new HashSet<>();
+ if (excludedTypes != null)
{
- for(QName excludedType : excludedTypes)
+ for (QName excludedType : excludedTypes)
{
allExcludedTypes.addAll(dictionaryService.getSubTypes(excludedType, true));
}
@@ -202,17 +239,23 @@ public class NodesImpl implements Nodes
boolean excluded = allExcludedTypes.contains(type);
return(inExpected && !excluded);
}
-
+
+ /**
+ * @deprecated review usage (backward compat')
+ */
public Node getNode(String nodeId)
{
NodeRef nodeRef = validateNode(nodeId);
- return new Node(nodeRef, nodeService.getProperties(nodeRef), namespaceService);
+ return new Node(nodeRef, null, nodeService.getProperties(nodeRef), sr);
}
-
+
+ /**
+ * @deprecated review usage (backward compat')
+ */
public Node getNode(NodeRef nodeRef)
{
- return new Node(nodeRef, nodeService.getProperties(nodeRef), namespaceService);
+ return new Node(nodeRef, null, nodeService.getProperties(nodeRef), sr);
}
private Type getType(NodeRef nodeRef)
@@ -227,65 +270,19 @@ public class NodesImpl implements Nodes
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);
- final Properties properties = cmisConnector.getNodeProperties(nodeInfo, null);
-
- // fake the title property, which CMIS doesn't give us
- String title = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE);
- final PropertyStringImpl titleProp = new PropertyStringImpl(ContentModel.PROP_TITLE.toString(), title);
- Properties wrapProperties = new Properties()
- {
- @Override
- public List getExtensions()
- {
- return properties.getExtensions();
- }
-
- @Override
- public void setExtensions(List extensions)
- {
- properties.setExtensions(extensions);
- }
-
- @Override
- public Map> getProperties()
- {
- Map> updatedProperties = new HashMap>(properties.getProperties());
- updatedProperties.put(titleProp.getId(), titleProp);
- return updatedProperties;
- }
-
- @Override
- public List> getPropertyList()
- {
- List> propertyList = new ArrayList>(properties.getPropertyList());
- propertyList.add(titleProp);
- return propertyList;
- }
- };
-
- return wrapProperties;
- }
- */
-
/**
* Returns the public api representation of a document.
- *
- * Note: properties are modelled after the OpenCMIS node properties
+ *
+ * @deprecated review usage (backward compat')
*/
public Document getDocument(NodeRef nodeRef)
{
Type type = getType(nodeRef);
if (type.equals(Type.DOCUMENT))
{
- //Properties properties = getCMISProperties(nodeRef);
Map properties = nodeService.getProperties(nodeRef);
- Document doc = new Document(nodeRef, properties, namespaceService);
+
+ Document doc = new Document(nodeRef, getParentNodeRef(nodeRef), properties, sr);
return doc;
}
else
@@ -296,17 +293,17 @@ public class NodesImpl implements Nodes
/**
* Returns the public api representation of a folder.
- *
- * Note: properties are modelled after the OpenCMIS node properties
+ *
+ * @deprecated review usage (backward compat')
*/
public Folder getFolder(NodeRef nodeRef)
{
Type type = getType(nodeRef);
if (type.equals(Type.FOLDER))
{
- //Properties properties = getCMISProperties(nodeRef);
Map properties = nodeService.getProperties(nodeRef);
- Folder folder = new Folder(nodeRef, properties, namespaceService);
+
+ Folder folder = new Folder(nodeRef, getParentNodeRef(nodeRef), properties, sr);
return folder;
}
else
@@ -314,13 +311,28 @@ public class NodesImpl implements Nodes
throw new InvalidArgumentException("Node is not a folder");
}
}
+
+ private NodeRef getParentNodeRef(final NodeRef nodeRef) {
+
+ if (repositoryHelper.getCompanyHome().equals(nodeRef))
+ {
+ return null; // note: does not make sense to return parent above C/H
+ }
+
+ return nodeService.getPrimaryParent(nodeRef).getParentRef();
+ }
private NodeRef validateOrLookupNode(String nodeId, String path) {
- final NodeRef parentNodeRef;
+ NodeRef parentNodeRef;
+
if (nodeId.equals(PATH_ROOT))
{
parentNodeRef = repositoryHelper.getCompanyHome();
}
+ else if (nodeId.equals(PATH_SHARED))
+ {
+ parentNodeRef = repositoryHelper.getSharedHome();
+ }
else if (nodeId.equals(PATH_MY))
{
NodeRef person = repositoryHelper.getPerson();
@@ -334,103 +346,272 @@ public class NodesImpl implements Nodes
{
parentNodeRef = validateNode(nodeId);
}
+
+ if (path != null) {
+ // resolve path relative to current nodeId
+ parentNodeRef = resolveNodeByPath(parentNodeRef, path, true);
+ }
+
return parentNodeRef;
}
+
+ protected NodeRef resolveNodeByPath(final NodeRef parentNodeRef, String path, boolean checkForCompanyHome)
+ {
+ final List pathElements = new ArrayList<>(0);
+
+ if ((path != null) && (! path.isEmpty())) {
+
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+
+ if (! path.isEmpty()) {
+ pathElements.addAll(Arrays.asList(path.split("/")));
+
+ if (checkForCompanyHome)
+ {
+ /*
+ if (nodeService.getRootNode(parentNodeRef.getStoreRef()).equals(parentNodeRef)) {
+ // special case
+ NodeRef chNodeRef = repositoryHelper.getCompanyHome();
+ String chName = (String)nodeService.getProperty(chNodeRef, ContentModel.PROP_NAME);
+ if (chName.equals(pathElements.get(0))) {
+ pathElements = pathElements.subList(1, pathElements.size());
+ parentNodeRef = chNodeRef;
+ }
+ }
+ */
+ }
+ }
+ }
+
+ FileInfo fileInfo = null;
+ try {
+ if (pathElements.size() != 0) {
+ fileInfo = fileFolderService.resolveNamePath(parentNodeRef, pathElements);
+ }
+ else
+ {
+ fileInfo = fileFolderService.getFileInfo(parentNodeRef);
+ if (fileInfo == null)
+ {
+ throw new FileNotFoundException(parentNodeRef);
+ }
+ }
+ }
+ catch (FileNotFoundException fnfe) {
+ // convert checked exception
+ throw new InvalidNodeRefException(fnfe.getMessage()+" ["+path+"]", parentNodeRef);
+ }
+
+ return fileInfo.getNodeRef();
+ }
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);
+ return getFolderOrDocument(nodeRef, getParentNodeRef(nodeRef), typeQName, false);
}
- private Node getFolderOrDocument(NodeRef nodeRef, QName typeQName,boolean incPrimaryPath)
+ private Node getFolderOrDocument(final NodeRef nodeRef, NodeRef parentNodeRef, QName typeQName, boolean minimalnfo)
{
- String primaryPath = null;
- if (incPrimaryPath)
+ PathInfo pathInfo = null;
+ if (! minimalnfo)
{
- 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)
+ pathInfo = lookupPathInfo(nodeRef);
}
-
- Node node = null;
+
Type type = getType(typeQName);
+ Node node;
Map properties = nodeService.getProperties(nodeRef);
if (type.equals(Type.DOCUMENT))
{
- //Properties properties = getCMISProperties(nodeRef);
- node = new Document(nodeRef, properties, namespaceService);
+ node = new Document(nodeRef, parentNodeRef, properties, sr);
}
else if (type.equals(Type.FOLDER))
{
// container/folder
- //Properties properties = getCMISProperties(nodeRef);
- node = new Folder(nodeRef, properties, namespaceService);
+ node = new Folder(nodeRef, parentNodeRef, properties, sr);
}
else
{
throw new InvalidArgumentException("Node is not a folder or file");
}
-
- node.setType(typeQName.toPrefixString(namespaceService));
- node.setPrimaryPath(primaryPath); // optional - can be null
+
+ if (! minimalnfo) {
+ node.setProperties(mapProperties(properties));
+ node.setAspectNames(mapAspects(nodeService.getAspects(nodeRef)));
+ }
+
+ node.setNodeType(typeQName.toPrefixString(namespaceService));
+ node.setPath(pathInfo);
+
return node;
}
-
+
+ protected PathInfo lookupPathInfo(NodeRef nodeRefIn)
+ {
+ List elements = new ArrayList<>(5);
+
+ NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();
+ boolean isComplete = true;
+
+ NodeRef pNodeRef = nodeRefIn;
+ while (pNodeRef != null)
+ {
+ if (pNodeRef.equals(companyHomeNodeRef))
+ {
+ pNodeRef = null;
+ }
+ else {
+ pNodeRef = nodeService.getPrimaryParent(pNodeRef).getParentRef();
+
+ if (pNodeRef == null)
+ {
+ // belts-and-braces - is it even possible to get here ?
+ isComplete = false;
+ }
+ else
+ {
+ if (permissionService.hasPermission(pNodeRef, PermissionService.READ) == AccessStatus.ALLOWED)
+ {
+ String name = (String) nodeService.getProperty(pNodeRef, ContentModel.PROP_NAME);
+ elements.add(0, new PathInfo().new ElementInfo(pNodeRef.getId(), name));
+ }
+ else
+ {
+ isComplete = false;
+ pNodeRef = null;
+ }
+ }
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (PathInfo.ElementInfo e : elements)
+ {
+ sb.append("/").append(e.getName());
+ }
+
+ return new PathInfo(sb.toString(), isComplete, elements);
+ }
+
+ protected Map mapProperties(Map nodeProps)
+ {
+ Map props = new HashMap<>(nodeProps.size());
+
+ for (Map.Entry entry : nodeProps.entrySet()) {
+ QName propQName = entry.getKey();
+ if (!EXCLUDED_PROPS.contains(propQName))
+ {
+ props.put(entry.getKey().toPrefixString(namespaceService), entry.getValue());
+ }
+ }
+
+ if (props.size() == 0)
+ {
+ props = null; // no props to return
+ }
+
+ return props;
+ }
+
+ protected List mapAspects(Set nodeAspects)
+ {
+ List aspectNames = new ArrayList<>(nodeAspects.size());
+
+ for (QName aspectName : nodeAspects)
+ {
+ if (! EXCLUDED_ASPECTS.contains(aspectName))
+ {
+ aspectNames.add(aspectName.toPrefixString(namespaceService));
+ }
+ }
+
+ if (aspectNames.size() == 0)
+ {
+ aspectNames = null; // no aspects to return
+ }
+
+ return aspectNames;
+ }
+
public CollectionWithPagingInfo 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);
+ // TODO
+ // map - where (filter) properties - including isFolder
+ // map - orderBy (sort) properties - including isFolder
+
+ // TODO refactor & fix !
+ final boolean minimalnfo = (parameters.getSelectedProperties().size() == 0);
+
+ boolean includeFolders = true;
+ boolean includeFiles = true;
+
+ Query q = parameters.getQuery();
+
+ if (q != null)
+ {
+ // TODO confirm list of filter props - what about custom props (+ across types/aspects) ?
+ MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(LIST_FOLDER_CHILDREN_EQUALS_QUERY_PROPERTIES, null);
+ QueryHelper.walk(q, propertyWalker);
+
+ Boolean b = propertyWalker.getProperty(PARAM_ISFOLDER, WhereClauseParser.EQUALS, Boolean.class);
+ if (b != null)
+ {
+ includeFiles = !b;
+ includeFolders = b;
+ }
}
-
- boolean includeFiles = true;
- str = parameters.getParameter("includeFiles");
- if (str != null) {
- includeFiles = new Boolean(str);
+
+ List sortCols = parameters.getSorting();
+ List> sortProps = null;
+ if ((sortCols != null) && (sortCols.size() > 0))
+ {
+ sortProps = new ArrayList<>(sortCols.size());
+ for (SortColumn sortCol : sortCols)
+ {
+ QName propQname = MAP_PARAM_QNAME.get(sortCol.column);
+ if (propQname == null)
+ {
+ propQname = QName.resolveToQName(namespaceService, sortCol.column);
+ }
+
+ if (propQname != null)
+ {
+ sortProps.add(new Pair<>(propQname, sortCol.asc));
+ }
+ }
}
-
- String path = parameters.getParameter("path");
-
- Paging paging = parameters.getPaging();
+ else
+ {
+ // default sort order
+ sortProps = new ArrayList<>(Arrays.asList(
+ new Pair<>(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, Boolean.FALSE),
+ new Pair<>(ContentModel.PROP_NAME, true)));
+ }
+
+ Paging paging = parameters.getPaging();
+
+ // TODO do we want to support path with list folder children ?
+ String path = null;
+ //String path = parameters.getParameter("path");
+
final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, path);
-
- final Set folders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER));
- if (! nodeMatches(parentNodeRef, folders, null))
+
+ if (! nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null))
{
throw new InvalidArgumentException("NodeId of folder is expected");
}
PagingRequest pagingRequest = Util.getPagingRequest(paging);
- final PagingResults pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, null, null, pagingRequest);
+ final PagingResults pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, null, sortProps, pagingRequest);
final List page = pagingResults.getPage();
List nodes = new AbstractList()
@@ -439,7 +620,9 @@ public class NodesImpl implements Nodes
public Node get(int index)
{
FileInfo fInfo = page.get(index);
- return getFolderOrDocument(fInfo.getNodeRef(), fInfo.getType(), false);
+
+ // basic info by default (unless "select"ed otherwise)
+ return getFolderOrDocument(fInfo.getNodeRef(), parentNodeRef, fInfo.getType(), minimalnfo);
}
@Override
@@ -458,48 +641,68 @@ public class NodesImpl implements Nodes
fileFolderService.delete(nodeRef);
}
- public Folder createFolder(String parentFolderNodeId, Folder folderInfo, Parameters parameters)
+ // TODO should we able to specify content properties (eg. mimeType ... or use extension for now, or encoding)
+ public Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters)
{
- final NodeRef parentNodeRef = validateNode(parentFolderNodeId);
+ // check that requested parent node exists and it's type is a (sub-)type of folder
+ final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null);
- final Set folders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER));
- if (! nodeMatches(parentNodeRef, folders, null))
+ if (! nodeMatches(parentNodeRef, Collections.singleton(ContentModel.TYPE_FOLDER), null))
{
- throw new InvalidArgumentException("NodeId of folder is expected");
+ throw new InvalidArgumentException("NodeId of folder is expected: "+parentNodeRef);
}
- String folderName = folderInfo.getName();
- String folderType = folderInfo.getType();
- if (folderType == null) {
- folderType = "cm:folder";
- }
-
- QName folderTypeQName = QName.resolveToQName(namespaceService, folderType);
-
- Map props = new HashMap(10);
-
- props.put(ContentModel.PROP_NAME, folderName);
-
- String title = folderInfo.getTitle();
- if ((title != null) && (! title.isEmpty())) {
- props.put(ContentModel.PROP_TITLE, title);
+ String nodeName = nodeInfo.getName();
+ if ((nodeName == null) || nodeName.isEmpty())
+ {
+ throw new InvalidArgumentException("Node name is expected: "+parentNodeRef);
}
- String description = folderInfo.getDescription();
- if ((description != null) && (! description.isEmpty())) {
- props.put(ContentModel.PROP_DESCRIPTION, description);
+ String nodeType = nodeInfo.getNodeType();
+ if ((nodeType == null) || nodeType.isEmpty())
+ {
+ throw new InvalidArgumentException("Node type is expected: "+parentNodeRef+","+nodeName);
}
- // TODO other custom properties !!
+ // check that requested type is a (sub-) type of folder or content
+ QName nodeTypeQName = QName.resolveToQName(namespaceService, nodeType);
- QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(folderName));
+ Set contentAndFolders = new HashSet<>(Arrays.asList(ContentModel.TYPE_FOLDER, ContentModel.TYPE_CONTENT));
+ if (! typeMatches(nodeTypeQName, contentAndFolders, null)) {
+ throw new InvalidArgumentException("Type of folder or content is expected: "+ nodeType);
+ }
- NodeRef nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, assocQName, folderTypeQName, props).getChildRef();
+ boolean isContent = typeMatches(nodeTypeQName, Collections.singleton(ContentModel.TYPE_CONTENT), null);
- return (Folder) getFolderOrDocument(nodeRef.getId(), parameters);
+ Map props = new HashMap<>(10);
+
+ if (nodeInfo.getProperties() != null)
+ {
+ for (Map.Entry entry : (Set>)nodeInfo.getProperties().entrySet())
+ {
+ QName propQName = QName.createQName((String)entry.getKey(), namespaceService);
+ props.put(propQName, (Serializable)entry.getValue());
+ }
+ }
+
+ props.put(ContentModel.PROP_NAME, nodeName);
+
+ QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(nodeName));
+ NodeRef nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, assocQName, nodeTypeQName, props).getChildRef();
+
+ if (isContent) {
+ // add empty file
+ ContentWriter writer = sr.getContentService().getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
+ String mimeType = sr.getMimetypeService().guessMimetype(nodeName);
+ writer.setMimetype(mimeType);
+ writer.putContent("");
+ }
+
+ return getFolderOrDocument(nodeRef.getId(), parameters);
}
- public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters) {
+ public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters)
+ {
final NodeRef nodeRef = validateNode(nodeId);
@@ -511,26 +714,72 @@ public class NodesImpl implements Nodes
Map props = new HashMap<>(10);
+ if (nodeInfo.getProperties() != null)
+ {
+ for (Map.Entry entry : (Set>)nodeInfo.getProperties().entrySet())
+ {
+ QName propQName = QName.createQName((String)entry.getKey(), namespaceService);
+ props.put(propQName, (Serializable)entry.getValue());
+ }
+ }
+
String name = nodeInfo.getName();
- if ((name != null) && (! name.isEmpty())) {
+ 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) {
+ if (props.size() > 0)
+ {
nodeService.addProperties(nodeRef, props);
}
return getFolderOrDocument(nodeRef.getId(), parameters);
}
+
+ @Override
+ public BinaryResource getContent(String fileNodeId, Parameters parameters)
+ {
+ final NodeRef nodeRef = validateNode(fileNodeId);
+
+ if (! nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_CONTENT), null))
+ {
+ throw new InvalidArgumentException("NodeId of content is expected: "+nodeRef);
+ }
+
+ // TODO attachment header - update (or extend ?) REST fwk
+ return new NodeBinaryResource(nodeRef, ContentModel.PROP_CONTENT);
+ }
+
+ @Override
+ public void updateContent(String fileNodeId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters)
+ {
+ final NodeRef nodeRef = validateNode(fileNodeId);
+
+ if (! nodeMatches(nodeRef, Collections.singleton(ContentModel.TYPE_CONTENT), null))
+ {
+ throw new InvalidArgumentException("NodeId of content is expected: "+nodeRef);
+ }
+
+ ContentWriter writer = sr.getContentService().getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
+
+ String mimeType = contentInfo.getMimeType();
+ if (mimeType == null)
+ {
+ String fileName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
+ writer.guessMimetype(fileName);
+ }
+ else
+ {
+ writer.setMimetype(mimeType);
+ }
+
+ writer.guessEncoding();
+
+ writer.putContent(stream);
+
+ // TODO - hmm - we may wish to return json info !!
+ return;
+ }
}
diff --git a/source/java/org/alfresco/rest/api/model/ContentInfo.java b/source/java/org/alfresco/rest/api/model/ContentInfo.java
new file mode 100644
index 0000000000..a7f2823452
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/model/ContentInfo.java
@@ -0,0 +1,50 @@
+package org.alfresco.rest.api.model;
+
+/**
+ * Representation of content info
+ *
+ * @author janv
+ *
+ */
+public class ContentInfo
+{
+ private String mimeType;
+ private String mimeTypeName;
+ private long sizeInBytes;
+ private String encoding;
+
+ public ContentInfo()
+ {
+ }
+
+ public ContentInfo( String mimeType, String mimeTypeName, long sizeInBytes, String encoding)
+ {
+ this.mimeType = mimeType;
+ this.mimeTypeName = mimeTypeName;
+ this.sizeInBytes = sizeInBytes;
+ this.encoding = encoding;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public String getMimeTypeName() {
+ return mimeTypeName;
+ }
+
+ public long getSizeInBytes() {
+ return sizeInBytes;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "ContentInfo [mimeType=" + mimeType + ", mimeTypeName=" + mimeTypeName
+ + ", encoding=" + encoding + ", sizeInBytes=" + sizeInBytes + "]";
+ }
+}
diff --git a/source/java/org/alfresco/rest/api/model/Document.java b/source/java/org/alfresco/rest/api/model/Document.java
index 0d5210c1d6..cb679c26d8 100644
--- a/source/java/org/alfresco/rest/api/model/Document.java
+++ b/source/java/org/alfresco/rest/api/model/Document.java
@@ -1,15 +1,32 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
package org.alfresco.rest.api.model;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Map;
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
-import org.apache.chemistry.opencmis.commons.PropertyIds;
-import org.apache.chemistry.opencmis.commons.data.Properties;
-import org.apache.chemistry.opencmis.commons.data.PropertyData;
/**
* Representation of a document node.
@@ -20,31 +37,32 @@ import org.apache.chemistry.opencmis.commons.data.PropertyData;
*/
public class Document extends Node
{
+ // TODO backward compat' - favourites etc
private String mimeType;
private BigInteger sizeInBytes;
- private String versionLabel;
+ private String versionLabel;
- public Document()
- {
- super();
- }
+ private ContentInfo contentInfo;
- /*
- public Document(NodeRef nodeRef, Properties properties)
- {
- super(nodeRef, properties);
+ public Document() {
+ super();
+ }
- Map> props = properties.getProperties();
- this.mimeType = (String)getValue(props, PropertyIds.CONTENT_STREAM_MIME_TYPE);
- this.sizeInBytes = (BigInteger)getValue(props, PropertyIds.CONTENT_STREAM_LENGTH);
- this.versionLabel = (String)getValue(props, PropertyIds.VERSION_LABEL);
- }
- */
+ public Document(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, ServiceRegistry sr)
+ {
+ super(nodeRef, parentNodeRef, nodeProps, sr);
- public Document(NodeRef nodeRef, Map nodeProps, NamespaceService namespaceService)
- {
- super(nodeRef, nodeProps, namespaceService);
- }
+ Serializable val = nodeProps.get(ContentModel.PROP_CONTENT);
+
+ if ((val != null) && (val instanceof ContentData)) {
+ ContentData cd = (ContentData)val;
+ String mimeType = cd.getMimetype();
+ String mimeTypeName = sr.getMimetypeService().getDisplaysByMimetype().get(mimeType);
+ this.contentInfo = new ContentInfo(mimeType, mimeTypeName, cd.getSize(), cd.getEncoding());
+ }
+
+ //this.versionLabel = (String)nodeProps.get(ContentModel.PROP_VERSION_LABEL);
+ }
public String getMimeType()
{
@@ -66,13 +84,16 @@ public class Document extends Node
return false;
}
+ public ContentInfo getContent()
+ {
+ return contentInfo;
+ }
+
@Override
public String toString()
{
- return "Document [mimeType=" + mimeType + ", sizeInBytes="
- + sizeInBytes + ", versionLabel=" + versionLabel + ", nodeRef="
- + nodeRef + ", name=" + name + ", title=" + title
- + ", description=" + description + ", createdAt=" + createdAt
+ return "Document [contentInfo=" + contentInfo.toString() + ", nodeRef="
+ + nodeRef + ", name=" + name + ", createdAt=" + createdAt
+ ", modifiedAt=" + modifiedAt + ", createdBy=" + createdBy
+ ", modifiedBy=" + modifiedBy + "]";
}
diff --git a/source/java/org/alfresco/rest/api/model/Folder.java b/source/java/org/alfresco/rest/api/model/Folder.java
index 20f1395893..2881191af7 100644
--- a/source/java/org/alfresco/rest/api/model/Folder.java
+++ b/source/java/org/alfresco/rest/api/model/Folder.java
@@ -1,12 +1,29 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
package org.alfresco.rest.api.model;
import java.io.Serializable;
import java.util.Map;
+import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
-import org.apache.chemistry.opencmis.commons.data.Properties;
/**
* Representation of a folder node.
@@ -22,17 +39,10 @@ public class Folder extends Node
super();
}
- /*
- public Folder(NodeRef nodeRef, Properties properties)
- {
- super(nodeRef, properties);
- }
- */
-
- public Folder(NodeRef nodeRef, Map nodeProps, NamespaceService namespaceService)
- {
- super(nodeRef, nodeProps, namespaceService);
- }
+ public Folder(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, ServiceRegistry sr)
+ {
+ super(nodeRef, parentNodeRef, nodeProps, sr);
+ }
public Boolean getIsFolder()
{
diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java
index 6a53801899..09cb41e194 100644
--- a/source/java/org/alfresco/rest/api/model/Node.java
+++ b/source/java/org/alfresco/rest/api/model/Node.java
@@ -19,167 +19,139 @@
package org.alfresco.rest.api.model;
import java.io.Serializable;
-import java.util.Arrays;
import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import com.fasterxml.jackson.annotation.JsonAnyGetter;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.framework.resource.UniqueId;
+import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
-import org.apache.chemistry.opencmis.commons.PropertyIds;
-import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
/**
* Concrete class carrying general information for alf_node data
- *
+ *
* @author steveglover
* @author Gethin James
* @author janv
*/
public class Node implements Comparable
{
- protected NodeRef nodeRef;
- protected String name;
- protected String title;
- protected NodeRef guid; // TODO review - do we need for favorites (backwards compat') ?
- protected String description;
- protected Date createdAt;
- protected Date modifiedAt;
+ protected NodeRef nodeRef;
+ protected String name;
+
+ // TODO needed for favourties - backwards compat' - we could also choose to split of NodeInfo / Node impl's etc
+ protected String title;
+ protected NodeRef guid;
+ protected String description;
protected String createdBy;
protected String modifiedBy;
-
- protected String primaryPath;
+
+ protected Date createdAt;
+ protected Date modifiedAt;
+ protected UserInfo createdByUser;
+ protected UserInfo modifiedByUser;
+
+ protected NodeRef parentNodeRef;
+ protected PathInfo pathInfo;
protected String prefixTypeQName;
+ protected List aspectNames;
+
protected Map props;
- private static final List EXCLUDED_PROPS = Arrays.asList(
- ContentModel.PROP_NAME,
- ContentModel.PROP_TITLE,
- ContentModel.PROP_DESCRIPTION,
- ContentModel.PROP_MODIFIER,
- ContentModel.PROP_MODIFIED,
- ContentModel.PROP_CREATOR,
- ContentModel.PROP_CREATED,
- ContentModel.PROP_CONTENT,
- ContentModel.PROP_LOCALE,
- ContentModel.PROP_NODE_UUID,
- ContentModel.PROP_STORE_IDENTIFIER,
- ContentModel.PROP_STORE_PROTOCOL,
- ContentModel.PROP_NODE_DBID,
- ContentModel.PROP_INITIAL_VERSION,
- ContentModel.PROP_AUTO_VERSION_PROPS,
- ContentModel.PROP_AUTO_VERSION);
-
// TODO fixme !
- public Node(NodeRef nodeRef, Map nodeProps, NamespaceService namespaceService)
+ // also need to optionally pass in user map - eg. when listing children (to avoid multiple lookups for same user)
+ public Node(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, ServiceRegistry sr)
{
- if(nodeRef == null)
- {
- throw new IllegalArgumentException();
- }
+ if(nodeRef == null)
+ {
+ throw new IllegalArgumentException();
+ }
- this.nodeRef = nodeRef;
- mapProperties(nodeProps, namespaceService);
+ this.nodeRef = nodeRef;
+ this.parentNodeRef = parentNodeRef;
+
+ mapBasicInfo(nodeProps, sr);
}
-
- protected Object getValue(Map> props, String name)
- {
- PropertyData> prop = props.get(name);
- Object value = (prop != null ? prop.getFirstValue() : null);
- return value;
- }
- /*
- public Node(NodeRef nodeRef, Properties properties)
- {
- this.nodeRef = nodeRef;
-
- Map> props = properties.getProperties();
-
- this.guid = nodeRef;
-
- this.name = (String)getValue(props, PropertyIds.NAME);
- this.title = (String)getValue(props, ContentModel.PROP_TITLE.toString());
- this.description = (String)getValue(props, PropertyIds.DESCRIPTION);
-
- GregorianCalendar cal = (GregorianCalendar)getValue(props, PropertyIds.CREATION_DATE);
- this.createdAt = cal.getTime();
- cal = (GregorianCalendar)getValue(props, PropertyIds.LAST_MODIFICATION_DATE);
- this.modifiedAt = cal.getTime();
- this.createdBy = (String)getValue(props, PropertyIds.CREATED_BY);
- this.modifiedBy = (String)getValue(props, PropertyIds.LAST_MODIFIED_BY);
- }
- */
+ protected Object getValue(Map> props, String name)
+ {
+ PropertyData> prop = props.get(name);
+ Object value = (prop != null ? prop.getFirstValue() : null);
+ return value;
+ }
public Node()
{
}
- protected void mapProperties(Map nodeProps, NamespaceService namespaceService)
+ protected void mapBasicInfo(Map nodeProps, ServiceRegistry sr)
{
+ PersonService personService = sr.getPersonService();
+
// TODO review backwards compat' for favorites & others (eg. set guid explicitly where still needed)
//this.guid = nodeRef;
+ //this.title = (String)nodeProps.get(ContentModel.PROP_TITLE);
+ //this.description = (String)nodeProps.get(ContentModel.PROP_DESCRIPTION);
+ //this.createdBy = (String)nodeProps.get(ContentModel.PROP_CREATOR);
+ //this.modifiedBy = (String)nodeProps.get(ContentModel.PROP_MODIFIER);
- this.name = (String)nodeProps.get(ContentModel.PROP_NAME);
- this.title = (String)nodeProps.get(ContentModel.PROP_TITLE);
- this.description = (String)nodeProps.get(ContentModel.PROP_DESCRIPTION);
+ this.name = (String)nodeProps.get(ContentModel.PROP_NAME);
- this.createdAt = (Date)nodeProps.get(ContentModel.PROP_CREATED);
- this.createdBy = (String)nodeProps.get(ContentModel.PROP_CREATOR);
- this.modifiedAt = (Date)nodeProps.get(ContentModel.PROP_MODIFIED);
- this.modifiedBy = (String)nodeProps.get(ContentModel.PROP_MODIFIER);
+ this.createdAt = (Date)nodeProps.get(ContentModel.PROP_CREATED);
+ this.createdByUser = lookupUserInfo((String)nodeProps.get(ContentModel.PROP_CREATOR), personService);
- this.props = new HashMap<>(nodeProps.size());
+ this.modifiedAt = (Date)nodeProps.get(ContentModel.PROP_MODIFIED);
+ this.modifiedByUser = lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), personService);
+ }
- for (Map.Entry entry : nodeProps.entrySet()) {
- QName propQName = entry.getKey();
- if (! EXCLUDED_PROPS.contains(propQName)) {
- props.put(entry.getKey().toPrefixString(namespaceService), entry.getValue());
- }
+ // TODO refactor & optimise to avoid multiple person lookups
+ private UserInfo lookupUserInfo(final String userName, final PersonService personService) {
+
+ String sysUserName = AuthenticationUtil.getSystemUserName();
+ if (userName.equals(sysUserName) || (AuthenticationUtil.isMtEnabled() && userName.startsWith(sysUserName+"@")))
+ {
+ return new UserInfo(userName, userName, "");
+ }
+ else
+ {
+ PersonService.PersonInfo pInfo = personService.getPerson(personService.getPerson(userName));
+ return new UserInfo(userName, pInfo.getFirstName(), pInfo.getLastName());
}
}
-
+
public void setGuid(NodeRef guid)
- {
- this.guid = guid;
- }
+ {
+ this.guid = guid;
+ }
- public NodeRef getGuid()
- {
- return guid;
- }
+ public NodeRef getGuid() {
+ return guid;
+ }
- public String getTitle()
- {
- return title;
- }
+ public String getTitle()
+ {
+ return title;
+ }
- @UniqueId
+ @UniqueId
public NodeRef getNodeRef()
{
- return nodeRef;
- }
+ return nodeRef;
+ }
- public void setNodeRef(NodeRef nodeRef)
- {
-// if(nodeRef == null)
-// {
-// throw new IllegalArgumentException();
-// }
- this.nodeRef = nodeRef;
- }
-
- public Date getCreatedAt()
+ public void setNodeRef(NodeRef nodeRef)
+ {
+ this.nodeRef = nodeRef;
+ }
+
+ public Date getCreatedAt()
{
return this.createdAt;
}
@@ -189,20 +161,28 @@ public class Node implements Comparable
this.createdAt = createdAt;
}
- public Date getModifiedAt()
- {
- return modifiedAt;
- }
+ public Date getModifiedAt()
+ {
+ return modifiedAt;
+ }
- public String getModifiedBy()
- {
- return modifiedBy;
- }
+ public String getModifiedBy()
+ {
+ return modifiedBy;
+ }
+
+ public UserInfo getModifiedByUser() {
+ return modifiedByUser;
+ }
+
+ public UserInfo getCreatedByUser() {
+ return createdByUser;
+ }
public String getDescription()
- {
- return description;
- }
+ {
+ return description;
+ }
public String getName()
{
@@ -223,59 +203,76 @@ public class Node implements Comparable
{
this.createdBy = createdBy;
}
-
- public String getPrimaryPath()
- {
- return primaryPath;
- }
-
- public void setPrimaryPath(String primaryPath)
- {
- this.primaryPath = primaryPath;
- }
-
- public String getType()
- {
- return prefixTypeQName;
- }
-
- public void setType(String prefixType)
- {
- this.prefixTypeQName = prefixType;
+
+ public PathInfo getPath()
+ {
+ return pathInfo;
}
- public Map getProperties() {
+ public void setPath(PathInfo pathInfo)
+ {
+ this.pathInfo = pathInfo;
+ }
+
+ public String getNodeType()
+ {
+ return prefixTypeQName;
+ }
+
+ public void setNodeType(String prefixType)
+ {
+ this.prefixTypeQName = prefixType;
+ }
+
+ public Map getProperties() {
return this.props;
}
-
- public boolean equals(Object other)
- {
- if(this == other)
- {
- return true;
- }
- if(!(other instanceof Node))
- {
- return false;
- }
-
- Node node = (Node)other;
- return EqualsHelper.nullSafeEquals(getNodeRef(), node.getNodeRef());
- }
+ public void setProperties(Map props) {
+ this.props = props;
+ }
- @Override
- public int compareTo(Node node)
- {
- return getNodeRef().toString().compareTo(node.getNodeRef().toString());
- }
+ public List getAspectNames() {
+ return aspectNames;
+ }
- @Override
- public String toString()
- {
- return "Node [nodeRef=" + nodeRef + ", type=" + prefixTypeQName + ", name=" + name + ", title="
- + title + ", description=" + description + ", createdAt="
- + createdAt + ", modifiedAt=" + modifiedAt + ", createdBy=" + createdBy + ", modifiedBy="
- + modifiedBy + ", primaryPath =" + primaryPath +"]";
- }
-}
+ public void setAspectNames(List aspectNames) {
+ this.aspectNames = aspectNames;
+ }
+
+ public NodeRef getParentId()
+ {
+ return parentNodeRef;
+ }
+
+ public boolean equals(Object other)
+ {
+ if(this == other)
+ {
+ return true;
+ }
+
+ if(!(other instanceof Node))
+ {
+ return false;
+ }
+
+ Node node = (Node)other;
+ return EqualsHelper.nullSafeEquals(getNodeRef(), node.getNodeRef());
+ }
+
+ @Override
+ public int compareTo(Node node)
+ {
+ return getNodeRef().toString().compareTo(node.getNodeRef().toString());
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Node [nodeRef=" + nodeRef + ", type=" + prefixTypeQName + ", name=" + name + ", title="
+ + title + ", description=" + description + ", createdAt="
+ + createdAt + ", modifiedAt=" + modifiedAt + ", createdByUser=" + createdByUser + ", modifiedBy="
+ + modifiedByUser + ", pathInfo =" + pathInfo +"]";
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/rest/api/model/PathInfo.java b/source/java/org/alfresco/rest/api/model/PathInfo.java
new file mode 100644
index 0000000000..8c0f96e06f
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/model/PathInfo.java
@@ -0,0 +1,64 @@
+package org.alfresco.rest.api.model;
+
+import java.util.List;
+
+/**
+ * Representation of a path info
+ *
+ * @author janv
+ *
+ */
+public class PathInfo
+{
+ private String name;
+ private Boolean isComplete;
+ private List elements;
+
+ public PathInfo()
+ {
+ }
+
+ public PathInfo(String name, Boolean isComplete, List elements)
+ {
+ this.name = name;
+ this.isComplete = isComplete;
+ this.elements = elements;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Boolean getIsComplete() {
+ return isComplete;
+ }
+
+ public List getElements() {
+ return elements;
+ }
+
+ public class ElementInfo {
+
+ private String id;
+
+ private String name;
+
+ public ElementInfo()
+ {
+ }
+
+ public ElementInfo(String id, String name)
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/rest/api/model/UserInfo.java b/source/java/org/alfresco/rest/api/model/UserInfo.java
new file mode 100644
index 0000000000..fe78dc204b
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/model/UserInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+package org.alfresco.rest.api.model;
+
+/**
+ * Representation of a user info
+ *
+ * @author janv
+ *
+ */
+public class UserInfo
+{
+ private String userName;
+ private String displayName;
+
+ public UserInfo()
+ {
+ }
+
+ public UserInfo(String userName, String firstName, String lastName)
+ {
+ this.userName = userName;
+ this.displayName = ((firstName != null ? firstName + " " : "") + (lastName != null ? lastName : "")).replaceAll("^\\s+|\\s+$", "");
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return "User [userName=" + userName + ", displayName=" + displayName + "]";
+ }
+}
diff --git a/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java
index e982948641..723f7104e2 100644
--- a/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java
+++ b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java
@@ -38,7 +38,7 @@ import java.util.List;
* @author janv
*/
@RelationshipResource(name = "children", entityResource = NodesEntityResource.class, title = "Folder children")
-public class NodeChildrenRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create, InitializingBean
+public class NodeChildrenRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create, InitializingBean
{
private Nodes nodes;
@@ -84,22 +84,22 @@ public class NodeChildrenRelation implements RelationshipResourceAction.Read create(String parentFolderNodeId, List folderInfos, Parameters parameters)
+ @WebApiDescription(title="Create one (or more) nodes as children of folder identified by parentFolderNodeId")
+ public List create(String parentFolderNodeId, List nodeInfos, Parameters parameters)
{
- List result = new ArrayList<>(folderInfos.size());
+ List result = new ArrayList<>(nodeInfos.size());
- for (Folder folderInfo : folderInfos)
+ for (Node nodeInfo : nodeInfos)
{
- result.add(nodes.createFolder(parentFolderNodeId, folderInfo, parameters));
+ result.add(nodes.createNode(parentFolderNodeId, nodeInfo, parameters));
}
return result;
diff --git a/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java b/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java
index d9c03517c2..ae9c923e47 100644
--- a/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java
+++ b/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java
@@ -20,23 +20,32 @@ package org.alfresco.rest.api.nodes;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.Node;
+import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
+import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.resource.EntityResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
+import org.alfresco.rest.framework.resource.content.BasicContentInfo;
+import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.util.ParameterCheck;
import org.springframework.beans.factory.InitializingBean;
+import java.io.InputStream;
+
/**
- * An implementation of an Entity Resource for a Node
+ * An implementation of an Entity Resource for a Node (file or folder)
*
* @author sglover
* @author Gethin James
* @author janv
*/
@EntityResource(name="nodes", title = "Nodes")
-public class NodesEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Delete, EntityResourceAction.Update, InitializingBean
+public class NodesEntityResource implements
+ EntityResourceAction.ReadById, EntityResourceAction.Delete, EntityResourceAction.Update,
+ BinaryResourceAction.Read, BinaryResourceAction.Update, InitializingBean
{
private Nodes nodes;
@@ -54,13 +63,10 @@ public class NodesEntityResource implements EntityResourceAction.ReadById,
/**
* Returns information regarding the node 'nodeId' - folder or document
*
- * TODO other metadata/properties & permissions etc ...
- *
* @param nodeId String id of node (folder or document) - will also accept well-known aliases, eg. "-root-" or "-my-"
*
* Optional parameters:
* - path
- * - incPrimaryPath
*/
@WebApiDescription(title = "Get Node Information", description = "Get information for the node with id 'nodeId'")
@WebApiParam(name = "nodeId", title = "The node id")
@@ -69,15 +75,30 @@ public class NodesEntityResource implements EntityResourceAction.ReadById,
return nodes.getFolderOrDocument(nodeId, parameters);
}
+ @Override
+ @WebApiDescription(title = "Download content", description = "Download content")
+ @BinaryProperties({"content"})
+ public BinaryResource readProperty(String fileNodeId, Parameters parameters) throws EntityNotFoundException
+ {
+ return nodes.getContent(fileNodeId, parameters);
+ }
+
+ @Override
+ @WebApiDescription(title = "Upload content", description = "Upload content")
+ @BinaryProperties({"content"})
+ public void update(String fileNodeId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters)
+ {
+ nodes.updateContent(fileNodeId, contentInfo, stream, parameters);
+ }
+
/**
* Update info on the node 'nodeId' - folder or document
*
- * Initially, name, title &/or description. Note: changing name is a "rename" (and must be unique within the current parent folder).
- *
- * TODO other metadata/properties & permissions etc ...
+ * Can update name (which is a "rename" and hence must be unique within the current parent folder)
+ * or update other properties.
*
* @param nodeId String nodeId of node (folder or document)
- * @param nodeInfo node entity with info to update (eg. name, title, description ...)
+ * @param nodeInfo node entity with info to update (eg. name, properties ...)
* @param parameters
* @return
*/
diff --git a/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java b/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java
index e418a4cd1d..905f217341 100644
--- a/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java
+++ b/source/java/org/alfresco/rest/framework/webscripts/ResourceWebScriptPut.java
@@ -6,6 +6,7 @@ import java.util.Locale;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.repo.web.scripts.BufferedRequest;
import org.alfresco.rest.framework.core.ResourceInspector;
import org.alfresco.rest.framework.core.ResourceLocator;
import org.alfresco.rest.framework.core.ResourceMetadata;
@@ -23,6 +24,7 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.webscripts.WebScriptRequest;
+import org.springframework.extensions.webscripts.WrappingWebScriptRequest;
import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
@@ -127,6 +129,14 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
logger.warn("Failed to get the input stream.", error);
}
}
+ else if (req instanceof WrappingWebScriptRequest)
+ {
+ // TODO review REST fwk change
+ // eg. BufferredRequest
+ WrappingWebScriptRequest wrappedRequest = (WrappingWebScriptRequest) req;
+ return wrappedRequest.getContent().getInputStream();
+ }
+
return null;
}