diff --git a/source/java/org/alfresco/rest/api/DeletedNodes.java b/source/java/org/alfresco/rest/api/DeletedNodes.java index 567d320760..1c74f798a3 100644 --- a/source/java/org/alfresco/rest/api/DeletedNodes.java +++ b/source/java/org/alfresco/rest/api/DeletedNodes.java @@ -18,9 +18,11 @@ */ package org.alfresco.rest.api; +import org.alfresco.repo.node.archive.RestoreNodeReport; 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; /** * Handles trashcan / deleted nodes @@ -31,4 +33,5 @@ public interface DeletedNodes { CollectionWithPagingInfo listDeleted(Parameters parameters); Node getDeletedNode(String originalId, Parameters parameters); + Node restoreArchivedNode(String archivedId); } diff --git a/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java b/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java index 81a61349a5..2475d4a237 100644 --- a/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/DeletedNodesImpl.java @@ -22,11 +22,18 @@ import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.node.archive.ArchivedNodesCannedQueryBuilder; +import org.alfresco.repo.node.archive.RestoreNodeReport; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.rest.api.model.Node; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.rest.api.DeletedNodes; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.framework.core.exceptions.ApiException; +import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; +import org.alfresco.rest.framework.core.exceptions.NotFoundException; +import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.service.ServiceRegistry; @@ -128,4 +135,29 @@ public class DeletedNodesImpl implements DeletedNodes if (foundNode != null) mapArchiveInfo(foundNode,null); return foundNode; } + + @Override + public Node restoreArchivedNode(String archivedId) + { + //First check the node is valid and has been archived. + NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, archivedId); + RestoreNodeReport restored = nodeArchiveService.restoreArchivedNode(validatedNodeRef); + switch (restored.getStatus()) + { + case SUCCESS: + return nodes.getFolderOrDocumentFullInfo(restored.getRestoredNodeRef(), null, null, null, null); + case FAILURE_PERMISSION: + throw new PermissionDeniedException(); + case FAILURE_INTEGRITY: + throw new IntegrityException("Restore failed due to an integrity error", null); + case FAILURE_DUPLICATE_CHILD_NODE_NAME: + throw new ConstraintViolatedException("Name already exists in target"); + case FAILURE_INVALID_ARCHIVE_NODE: + throw new EntityNotFoundException(archivedId); + case FAILURE_INVALID_PARENT: + throw new NotFoundException("Invalid parent id "+restored.getTargetParentNodeRef()); + default: + throw new ApiException("Unable to restore node "+archivedId); + } + } } diff --git a/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java b/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java index 03c1519eb0..531ac3df50 100644 --- a/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java +++ b/source/java/org/alfresco/rest/api/trashcan/TrashcanEntityResource.java @@ -27,11 +27,15 @@ import org.alfresco.rest.api.DeletedNodes; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.impl.Util; import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.api.model.NodeTarget; +import org.alfresco.rest.framework.Operation; +import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.rest.framework.webscripts.WithResponse; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -66,4 +70,11 @@ public class TrashcanEntityResource implements { return deletedNodes.getDeletedNode(id, parameters); } + + @Operation("restore") + @WebApiDescription(title = "Restore deleted Node", description="Restores an archived node") + public Node restoreDeletedNode(String nodeId, Void ignored, Parameters parameters, WithResponse withResponse) + { + return deletedNodes.restoreArchivedNode(nodeId); + } } diff --git a/source/test-java/org/alfresco/rest/DeletedNodesTest.java b/source/test-java/org/alfresco/rest/DeletedNodesTest.java index db5202713d..3219ea818c 100644 --- a/source/test-java/org/alfresco/rest/DeletedNodesTest.java +++ b/source/test-java/org/alfresco/rest/DeletedNodesTest.java @@ -18,6 +18,7 @@ */ package org.alfresco.rest; +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -43,6 +44,7 @@ import org.alfresco.service.cmr.site.SiteVisibility; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.extensions.webscripts.Status; import java.util.Date; import java.util.List; @@ -123,6 +125,46 @@ public class DeletedNodesTest extends AbstractSingleNetworkSiteTest checkDeletedNodes(now, createdFolder, createdFolderNonSite, document, nodes); } + @Test + public void testCreateAndRestore() throws Exception + { + publicApiClient.setRequestContext(new RequestContext(u1.getId())); + Date now = new Date(); + String folder1 = "folder" + now.getTime() + "_1"; + Folder createdFolder = createFolder(u1.getId(), docLibNodeRef.getId(), folder1, null); + assertNotNull(createdFolder); + + //Create a folder outside a site + Folder createdFolderNonSite = createFolder(u1.getId(), Nodes.PATH_MY, folder1, null); + assertNotNull(createdFolderNonSite); + + Document document = createDocument(createdFolder, "restoreme.txt"); + delete(URL_NODES, u1.getId(), document.getId(), 204); + //Create another document with the same name + Document documentSameName = createDocument(createdFolder, "restoreme.txt"); + + //Can't restore a node of the same name + HttpResponse response = post("deleted-nodes/"+document.getId()+"/restore", u1.getId(), null, null, Status.STATUS_CONFLICT); + + delete(URL_NODES, u1.getId(), documentSameName.getId(), 204); + + //Now we can restore it. + response = post("deleted-nodes/"+document.getId()+"/restore", u1.getId(), null, null, 201); + + delete(URL_NODES, u1.getId(), createdFolder.getId(), 204); + + //We deleted the parent folder so lets see if we can restore a child doc, hopefully not. + response = post("deleted-nodes/"+documentSameName.getId()+"/restore", u1.getId(), null, null, Status.STATUS_NOT_FOUND); + + //Can't delete "nonsense" noderef + response = post("deleted-nodes/nonsense/restore", u1.getId(), null, null, Status.STATUS_NOT_FOUND); + + RepoService.TestPerson u2 = networkOne.createUser(); + //User 2 can't restore it but user 1 can. + response = post("deleted-nodes/"+createdFolder.getId()+"/restore", u2.getId(), null, null, Status.STATUS_FORBIDDEN); + response = post("deleted-nodes/"+createdFolder.getId()+"/restore", u1.getId(), null, null, 201); + } + protected void checkDeletedNodes(Date now, Folder createdFolder, Folder createdFolderNonSite, Document document, List nodes) { Node aNode = (Node) nodes.get(0);