diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java index db532d8658..0a1ed20129 100644 --- a/source/java/org/alfresco/rest/api/Nodes.java +++ b/source/java/org/alfresco/rest/api/Nodes.java @@ -100,7 +100,7 @@ public interface Nodes void deleteNode(String nodeId); /** - * Create node(s) - folder or (empty) file. + * Create node - folder or (empty) file. * * @param parentFolderNodeId * @param nodeInfo @@ -109,6 +109,28 @@ public interface Nodes */ Node createNode(String parentFolderNodeId, Node nodeInfo, Parameters parameters); + /** + * Move node + * + * @param sourceNodeId + * @param parentFolderNodeId + * @param name + * @param parameters + * @return + */ + Node moveNode(String sourceNodeId, String parentFolderNodeId, String name, Parameters parameters); + + /** + * Copy node + * + * @param sourceNodeId + * @param parentFolderNodeId + * @param name + * @param parameters + * @return + */ + Node copyNode(String sourceNodeId, String parentFolderNodeId, String name, Parameters parameters); + /** * Update node meta-data. * diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 3bda88959b..a7d24d2722 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -1084,8 +1084,18 @@ public class NodesImpl implements Nodes NodeRef parentNodeRef = nodeInfo.getParentId(); if (parentNodeRef != null) { - // move/rename - with exception mapping - move(nodeRef, parentNodeRef, name); + NodeRef currentParentNodeRef = getParentNodeRef(nodeRef); + if (currentParentNodeRef == null) + { + // implies root (Company Home) hence return 403 here + throw new PermissionDeniedException(); + } + + if (! currentParentNodeRef.equals(parentNodeRef)) + { + // move/rename - with exception mapping + moveOrCopy(nodeRef, parentNodeRef, name, false); + } } List aspectNames = nodeInfo.getAspectNames(); @@ -1171,47 +1181,62 @@ public class NodesImpl implements Nodes return getFolderOrDocument(nodeRef.getId(), parameters); } - private void move(NodeRef nodeRef, NodeRef parentNodeRef, String name) + public Node moveNode(String sourceNodeId, String parentFolderNodeId, String name, Parameters parameters) { - NodeRef currentParentNodeRef = getParentNodeRef(nodeRef); - if (currentParentNodeRef == null) - { - // implies root (Company Home) hence return 403 here - throw new PermissionDeniedException(); - } + final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null); + final NodeRef sourceNodeRef = validateOrLookupNode(sourceNodeId, null); - if (! currentParentNodeRef.equals(parentNodeRef)) + FileInfo fi = moveOrCopy(sourceNodeRef, parentNodeRef, name, false); + return getFolderOrDocument(fi.getNodeRef().getId(), parameters); + } + + public Node copyNode(String sourceNodeId, String parentFolderNodeId, String name, Parameters parameters) + { + final NodeRef parentNodeRef = validateOrLookupNode(parentFolderNodeId, null); + final NodeRef sourceNodeRef = validateOrLookupNode(sourceNodeId, null); + + FileInfo fi = moveOrCopy(sourceNodeRef, parentNodeRef, name, true); + return getFolderOrDocument(fi.getNodeRef().getId(), parameters); + } + + protected FileInfo moveOrCopy(NodeRef nodeRef, NodeRef parentNodeRef, String name, boolean isCopy) + { + try { - try + if (isCopy) + { + return fileFolderService.copy(nodeRef, parentNodeRef, name); + } + else { // updating "parentId" means moving primary parent ! // note: in the future (as and when we support secondary parent/child assocs) we may also // wish to select which parent to "move from" (in case where the node resides in multiple locations) - fileFolderService.move(nodeRef, parentNodeRef, name); - } - catch (InvalidNodeRefException inre) - { - throw new EntityNotFoundException(inre.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); - } - catch (FileNotFoundException fnfe) - { - // convert checked exception - throw new EntityNotFoundException(fnfe.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); - } - catch (FileExistsException fee) - { - // duplicate - name clash - throw new ConstraintViolatedException(fee.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); - } - catch (FileFolderServiceImpl.InvalidTypeException ite) - { - throw new InvalidArgumentException(ite.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); - } - catch (CyclicChildRelationshipException ccre) - { - throw new InvalidArgumentException(ccre.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); + return fileFolderService.move(nodeRef, parentNodeRef, name); } } + catch (InvalidNodeRefException inre) + { + throw new EntityNotFoundException(inre.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); + } + catch (FileNotFoundException fnfe) + { + // convert checked exception + throw new EntityNotFoundException(fnfe.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); + } + catch (FileExistsException fee) + { + // duplicate - name clash + throw new ConstraintViolatedException(fee.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); + } + catch (FileFolderServiceImpl.InvalidTypeException ite) + { + throw new InvalidArgumentException(ite.getMessage()+" ["+nodeRef+","+parentNodeRef+"]"); + } + catch (CyclicChildRelationshipException ccre) + { + throw new InvalidArgumentException(ccre.getMessage() + " [" + nodeRef + "," + parentNodeRef + "]"); + } } @Override diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index ac8d447c64..d54b471e32 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -27,7 +27,6 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.rest.framework.resource.UniqueId; -import org.alfresco.rest.framework.resource.content.*; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.NoSuchPersonException; @@ -61,7 +60,6 @@ public class Node implements Comparable protected String prefixTypeQName; protected List aspectNames; - protected Map properties; public Node(NodeRef nodeRef, NodeRef parentNodeRef, Map nodeProps, Map mapUserInfo, ServiceRegistry sr) @@ -136,6 +134,8 @@ public class Node implements Comparable return userInfo; } + // note: nodeRef maps to json "id" (when serializing/deserializng) + @UniqueId public NodeRef getNodeRef() { @@ -278,6 +278,7 @@ public class Node implements Comparable } // here to allow POST /nodes/{id}/children when creating empty file with specified content.mimeType + protected ContentInfo contentInfo; public void setContent(ContentInfo contentInfo) @@ -290,6 +291,21 @@ public class Node implements Comparable return this.contentInfo; } + + // TODO experimental (API subject to change) - special property (request-only) to allow move/copy via POST /nodes/{id}/children + + protected String action; + + public String getAction() + { + return action; + } + + public void setAction(String action) + { + this.action = action; + } + // TODO for backwards compat' - set explicitly when needed (ie. favourites) (note: we could choose to have separate old Node/NodeImpl etc) protected String title; @@ -376,4 +392,5 @@ public class Node implements Comparable { this.modifiedBy = modifiedBy; } + } \ No newline at end of file diff --git a/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java index 6182d5e4ab..166bb7a1c6 100644 --- a/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java +++ b/source/java/org/alfresco/rest/api/nodes/NodeChildrenRelation.java @@ -25,6 +25,7 @@ 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.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; @@ -100,10 +101,45 @@ public class NodeChildrenRelation implements RelationshipResourceAction.Read result = new ArrayList<>(nodeInfos.size()); + // TODO experimental (API subject to change) - eg. this may move to a separate endpoint ! + for (Node nodeInfo : nodeInfos) + { + String action = nodeInfo.getAction(); + if (action != null) + { + String sourceNodeId = nodeInfo.getNodeRef().getId(); + String optionalName = nodeInfo.getName(); + + if (action.equalsIgnoreCase("move")) + { + result.add(nodes.moveNode(sourceNodeId, parentFolderNodeId, optionalName, parameters)); + } + else if (action.equalsIgnoreCase("copy")) + { + result.add(nodes.copyNode(sourceNodeId, parentFolderNodeId, optionalName, parameters)); + } + else + { + throw new InvalidArgumentException("Unknown action: "+action); + } + } + else + { + if (nodeInfo.getNodeRef() != null) + { + throw new InvalidArgumentException("Unexpected id without action, eg. move/copy: "+nodeInfo.getNodeRef()); + } + result.add(nodes.createNode(parentFolderNodeId, nodeInfo, parameters)); + } + + } + + /* for (Node nodeInfo : nodeInfos) { result.add(nodes.createNode(parentFolderNodeId, nodeInfo, parameters)); } + */ return result; }