diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index 12de17f01d..789323b30c 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -433,6 +433,10 @@ + + + + @@ -659,6 +663,10 @@ + + + + diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java index e402f53b58..42853ca643 100644 --- a/source/java/org/alfresco/rest/api/Nodes.java +++ b/source/java/org/alfresco/rest/api/Nodes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -23,10 +23,16 @@ 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.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; +/** + * @author steveglover + * @author janv + */ public interface Nodes { NodeRef validateNode(StoreRef storeRef, String nodeId); @@ -54,4 +60,49 @@ public interface Nodes * @return Folder */ Folder getFolder(NodeRef nodeRef); + + /** + * Get the folder or document representation (as appropriate) for the given node. + * @param nodeId String nodeId or well-known alias, eg. "-root-" or "-my-" + * @param parameters the {@link Parameters} object to get the parameters passed into the request + * including: + * - incPrimaryParent + * @return + */ + Node getFolderOrDocument(String nodeId, Parameters parameters); + + /** + * Get list of children of a parent folder. + * @param parentFolderNodeId String id of parent folder node or well-known alias, eg. "-root-" or "-my-" + * @param parameters the {@link Parameters} object to get the parameters passed into the request + * including: + * - filter, sort & paging params (where, orderBy, skipCount, maxItems) + * - incFiles, incFolders (both true by default) + * @return a paged list of {@code org.alfresco.rest.api.model.Node} objects + */ + CollectionWithPagingInfo getChildren(String parentFolderNodeId, Parameters parameters); + + /** + * Delete the given node. Note: will cascade delete for a folder. + * @param nodeId String id of node (folder or document) + */ + void deleteNode(String nodeId); + + /** + * + * @param parentFolderNodeId + * @param folderInfo + * @param parameters + * @return + */ + Folder createFolder(String parentFolderNodeId, Folder folderInfo, Parameters parameters); + + /** + * + * @param nodeId + * @param entity + * @param parameters + * @return + */ + Node updateNode(String nodeId, Node entity, 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 203b153181..0b1d74ea8d 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -36,7 +36,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; @@ -45,27 +47,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 @@ -76,9 +86,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) { @@ -94,6 +111,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; + } /* * @@ -170,23 +207,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); @@ -228,6 +271,7 @@ public class NodesImpl implements Nodes return wrapProperties; } + */ /** * Returns the public api representation of a document. @@ -237,10 +281,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 properties = nodeService.getProperties(nodeRef); + Document doc = new Document(nodeRef, properties, namespaceService); return doc; } else @@ -257,10 +302,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 properties = nodeService.getProperties(nodeRef); + Folder folder = new Folder(nodeRef, properties, namespaceService); return folder; } else @@ -268,4 +314,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 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 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 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 pagingResults = fileFolderService.list(parentNodeRef, includeFiles, includeFolders, null, null, pagingRequest); + + final List page = pagingResults.getPage(); + List nodes = new AbstractList() + { + @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 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 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 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 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 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); + } } diff --git a/source/java/org/alfresco/rest/api/model/Document.java b/source/java/org/alfresco/rest/api/model/Document.java index fef05c6982..0d5210c1d6 100644 --- a/source/java/org/alfresco/rest/api/model/Document.java +++ b/source/java/org/alfresco/rest/api/model/Document.java @@ -5,6 +5,7 @@ import java.math.BigInteger; import java.util.Map; 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; @@ -14,6 +15,7 @@ import org.apache.chemistry.opencmis.commons.data.PropertyData; * Representation of a document node. * * @author steveglover + * @author janv * */ public class Document extends Node @@ -26,7 +28,8 @@ public class Document extends Node { super(); } - + + /* public Document(NodeRef nodeRef, Properties properties) { super(nodeRef, properties); @@ -36,10 +39,11 @@ public class Document extends Node this.sizeInBytes = (BigInteger)getValue(props, PropertyIds.CONTENT_STREAM_LENGTH); this.versionLabel = (String)getValue(props, PropertyIds.VERSION_LABEL); } + */ - public Document(NodeRef nodeRef, Map nodeProps) + public Document(NodeRef nodeRef, Map nodeProps, NamespaceService namespaceService) { - super(nodeRef, nodeProps); + super(nodeRef, nodeProps, namespaceService); } public String getMimeType() @@ -57,6 +61,11 @@ public class Document extends Node return versionLabel; } + public Boolean getIsFolder() + { + return false; + } + @Override public String toString() { diff --git a/source/java/org/alfresco/rest/api/model/Folder.java b/source/java/org/alfresco/rest/api/model/Folder.java index 69ad18a4a2..20f1395893 100644 --- a/source/java/org/alfresco/rest/api/model/Folder.java +++ b/source/java/org/alfresco/rest/api/model/Folder.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.Map; 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; @@ -11,6 +12,7 @@ import org.apache.chemistry.opencmis.commons.data.Properties; * Representation of a folder node. * * @author steveglover + * @author janv * */ public class Folder extends Node @@ -20,14 +22,21 @@ public class Folder extends Node super(); } + /* public Folder(NodeRef nodeRef, Properties properties) { super(nodeRef, properties); } + */ - public Folder(NodeRef nodeRef, Map nodeProps) + public Folder(NodeRef nodeRef, Map nodeProps, NamespaceService namespaceService) { - super(nodeRef, nodeProps); + super(nodeRef, nodeProps, namespaceService); + } + + public Boolean getIsFolder() + { + return true; } @Override diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index 0404dc0706..6a53801899 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -19,13 +19,19 @@ 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.rest.framework.resource.UniqueId; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.apache.chemistry.opencmis.commons.PropertyIds; @@ -37,20 +43,45 @@ import org.apache.chemistry.opencmis.commons.data.PropertyData; * * @author steveglover * @author Gethin James + * @author janv */ public class Node implements Comparable { protected NodeRef nodeRef; protected String name; protected String title; - protected NodeRef guid; + protected NodeRef guid; // TODO review - do we need for favorites (backwards compat') ? protected String description; protected Date createdAt; protected Date modifiedAt; protected String createdBy; protected String modifiedBy; - public Node(NodeRef nodeRef, Map nodeProps) + protected String primaryPath; + protected String prefixTypeQName; + + 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) { if(nodeRef == null) { @@ -58,7 +89,7 @@ public class Node implements Comparable } this.nodeRef = nodeRef; - mapProperties(nodeProps); + mapProperties(nodeProps, namespaceService); } protected Object getValue(Map> props, String name) @@ -68,14 +99,19 @@ public class Node implements Comparable 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.guid = nodeRef; + 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); @@ -83,21 +119,34 @@ public class Node implements Comparable this.createdBy = (String)getValue(props, PropertyIds.CREATED_BY); this.modifiedBy = (String)getValue(props, PropertyIds.LAST_MODIFIED_BY); } + */ public Node() { } - protected void mapProperties(Map nodeProps) + protected void mapProperties(Map nodeProps, NamespaceService namespaceService) { + // TODO review backwards compat' for favorites & others (eg. set guid explicitly where still needed) + //this.guid = nodeRef; + this.name = (String)nodeProps.get(ContentModel.PROP_NAME); - this.guid = nodeRef; this.title = (String)nodeProps.get(ContentModel.PROP_TITLE); + this.description = (String)nodeProps.get(ContentModel.PROP_DESCRIPTION); + 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.description = (String)nodeProps.get(ContentModel.PROP_DESCRIPTION); + + this.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()); + } + } } public void setGuid(NodeRef guid) @@ -175,6 +224,30 @@ 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 Map getProperties() { + return this.props; + } + public boolean equals(Object other) { if(this == other) @@ -200,9 +273,9 @@ public class Node implements Comparable @Override public String toString() { - return "Node [nodeRef=" + nodeRef + ", name=" + name + ", title=" + return "Node [nodeRef=" + nodeRef + ", type=" + prefixTypeQName + ", name=" + name + ", title=" + title + ", description=" + description + ", createdAt=" + createdAt + ", modifiedAt=" + modifiedAt + ", createdBy=" + createdBy + ", modifiedBy=" - + modifiedBy + "]"; + + modifiedBy + ", primaryPath =" + primaryPath +"]"; } } diff --git a/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java new file mode 100644 index 0000000000..e982948641 --- /dev/null +++ b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java @@ -0,0 +1,107 @@ +/* + * 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.nodes; + +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Folder; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.resource.RelationshipResource; +import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.util.ParameterCheck; +import org.springframework.beans.factory.InitializingBean; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO ... work-in-progress + * + * @author janv + */ +@RelationshipResource(name = "children", entityResource = NodesEntityResource.class, title = "Folder children") +public class NodeChildrenRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create, InitializingBean +{ + private Nodes nodes; + + public void setNodes(Nodes nodes) + { + this.nodes = nodes; + } + + @Override + public void afterPropertiesSet() + { + ParameterCheck.mandatory("nodes", this.nodes); + } + + /** + * List folder children - returns a filtered/sorted/paged list of nodes that are immediate children of the parent folder + * + * TODO filtering, sorting, ... + * TODO metadata/properties & permissions etc ... + * + * @param parentFolderNodeId String id of parent folder - will also accept aliases "-root-" (Company Home) or "-my-" (current user's home folder) + * + * Optional query parameters: + * + * - incFiles + * - incFolders + * + * - properties + * - where + * - orderBy + * + * - skipCount + * - maxItems + * + * If parentFolderNodeId does not exist, EntityNotFoundException (status 404). + * If parentFolderNodeId does not represent a folder, InvalidArgumentException (status 400). + */ + @Override + @WebApiDescription(title = "Return a paged list of nodes for the document/folder identified by parentFolderNodeId") + public CollectionWithPagingInfo readAll(String parentFolderNodeId, Parameters parameters) + { + return nodes.getChildren(parentFolderNodeId, parameters); + } + + /** + * Create one or more sub-folders below parent folder. Note: can also use well-known alias, eg. -root- or -my- + * + * TODO also consider option to create path - eg. by passing name path in name (see Sparta API as an example) ... or should this be a separate API ? + * + * If parentFolderNodeId does not exist, EntityNotFoundException (status 404). + * If parentFolderNodeId does not represent a folder, InvalidArgumentException (status 400). + */ + @Override + @WebApiDescription(title="Create one (or more) folder(s) as a child of folder identified by parentFolderNodeId") + public List create(String parentFolderNodeId, List folderInfos, Parameters parameters) + { + List result = new ArrayList<>(folderInfos.size()); + + for (Folder folderInfo : folderInfos) + { + result.add(nodes.createFolder(parentFolderNodeId, folderInfo, 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 fefb730ff6..d9c03517c2 100644 --- a/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java +++ b/source/java/org/alfresco/rest/api/nodes/NodesEntityResource.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -19,7 +19,12 @@ package org.alfresco.rest.api.nodes; import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.resource.EntityResource; +import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; +import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.util.ParameterCheck; import org.springframework.beans.factory.InitializingBean; @@ -28,9 +33,10 @@ import org.springframework.beans.factory.InitializingBean; * * @author sglover * @author Gethin James + * @author janv */ @EntityResource(name="nodes", title = "Nodes") -public class NodesEntityResource implements InitializingBean +public class NodesEntityResource implements EntityResourceAction.ReadById, EntityResourceAction.Delete, EntityResourceAction.Update, InitializingBean { private Nodes nodes; @@ -44,4 +50,53 @@ public class NodesEntityResource implements InitializingBean { ParameterCheck.mandatory("nodes", this.nodes); } + + /** + * 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") + public Node readById(String nodeId, Parameters parameters) + { + return nodes.getFolderOrDocument(nodeId, 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 ... + * + * @param nodeId String nodeId of node (folder or document) + * @param nodeInfo node entity with info to update (eg. name, title, description ...) + * @param parameters + * @return + */ + @Override + @WebApiDescription(title="Updates a node (file or folder) with id 'nodeId'") + public Node update(String nodeId, Node nodeInfo, Parameters parameters) + { + return nodes.updateNode(nodeId, nodeInfo, parameters); + } + + /** + * Delete the given node. Note: will cascade delete for a folder. + * + * @param nodeId String id of node (folder or document) + */ + @Override + @WebApiDescription(title = "Delete Node", description="Delete the file or folder with id 'nodeId'. Folder will cascade delete") + public void delete(String nodeId, Parameters parameters) + { + nodes.deleteNode(nodeId); + } } diff --git a/source/java/org/alfresco/rest/framework/core/ResourceDictionaryBuilder.java b/source/java/org/alfresco/rest/framework/core/ResourceDictionaryBuilder.java index 337c569827..47d88fc2e4 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceDictionaryBuilder.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceDictionaryBuilder.java @@ -138,7 +138,11 @@ public class ResourceDictionaryBuilder ResourceDictionary rd = new ResourceDictionary(); processResources(rd,apiMap,null); processTopLevelApis(rd); - logger.debug(rd.prettyPrint()); + + if (logger.isDebugEnabled()) { + logger.debug(rd.prettyPrint()); + } + return rd; }