diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.desc.xml new file mode 100644 index 0000000000..72e6cba4fd --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.desc.xml @@ -0,0 +1,9 @@ + + Cancel a Download + Cancel a Dwonload + /api/internal/downloads/{store_type}/{store_id}/{node_id} + argument + user + + internal + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.json.ftl new file mode 100644 index 0000000000..e1d73e7ac2 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/download.delete.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> + { + "status": "CANCELLED", + } + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.desc.xml new file mode 100644 index 0000000000..02e0aac671 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.desc.xml @@ -0,0 +1,9 @@ + + Get Download Status + Get Download Status + /api/internal/downloads/{store_type}/{store_id}/{node_id}/status + argument + user + + internal + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.json.ftl new file mode 100644 index 0000000000..a442ba7910 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloadStatus.get.json.ftl @@ -0,0 +1,9 @@ +<#escape x as jsonUtils.encodeJSONString(x)> + { + "status": "${downloadStatus.status?string}", + "done": "${downloadStatus.done?string}", + "total": "${downloadStatus.total?string}", + "filesAdded": "${downloadStatus.filesAdded?string}", + "totalFiles": "${downloadStatus.totalFiles?string}" + } + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.desc.xml new file mode 100644 index 0000000000..fdb4569764 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.desc.xml @@ -0,0 +1,7 @@ + + Create Download + Create Download + /api/internal/downloads + argument + user + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.json.ftl new file mode 100644 index 0000000000..5a3a8bbf09 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/download/downloads.post.json.ftl @@ -0,0 +1,6 @@ +<#escape x as jsonUtils.encodeJSONString(x)> + { + "status": "success", + "nodeRef": "${downloadNodeRef.nodeRef?string}" + } + diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 780f81dca2..0f82ac8729 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1890,5 +1890,30 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/download/AbstractDownloadWebscript.java b/source/java/org/alfresco/repo/web/scripts/download/AbstractDownloadWebscript.java new file mode 100644 index 0000000000..a09683f227 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/download/AbstractDownloadWebscript.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2011 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.repo.web.scripts.download; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.download.DownloadService; +import org.springframework.extensions.webscripts.DeclarativeWebScript; + +/** + * Base class for download related webscripts. + * + * @author Alex Miller + */ +abstract class AbstractDownloadWebscript extends DeclarativeWebScript +{ + // Shared dependencies + protected DownloadService downloadService; + + public void setDownloadService(DownloadService downloadSerivce) + { + this.downloadService = downloadSerivce; + } + + /** + * Helper method to embed error informaion in a map. + */ + protected Map buildError(String message) + { + HashMap result = new HashMap(); + result.put("error", message); + + HashMap model = new HashMap(); + model.put("error", message); + model.put("result", result); + + return model; + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/download/DownloadDelete.java b/source/java/org/alfresco/repo/web/scripts/download/DownloadDelete.java new file mode 100644 index 0000000000..4170f710c1 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/download/DownloadDelete.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2011 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.repo.web.scripts.download; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Webscript for canceling a download. + * + * @author Alex Miller + */ +public class DownloadDelete extends AbstractDownloadWebscript +{ + + protected NodeService nodeService; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + String error = "No parameters supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + if (! ( templateVars.containsKey("store_type") + && templateVars.containsKey("store_id") + && templateVars.containsKey("node_id")) ) + { + String error = "Missing template variables (store_type, store_id or node_id)."; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + StoreRef store = new StoreRef(templateVars.get("store_type"), templateVars.get("store_id")); + NodeRef nodeRef = new NodeRef(store, templateVars.get("node_id")); + if (! nodeService.exists(nodeRef)) + { + String error = "Could not find node: " + nodeRef; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + + downloadService.cancelDownload(nodeRef); + + status.setCode(HttpServletResponse.SC_OK); + + return new HashMap(); + + } + + public void setNodeService(NodeService nodeSerivce) + { + this.nodeService = nodeSerivce; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/download/DownloadPost.java b/source/java/org/alfresco/repo/web/scripts/download/DownloadPost.java new file mode 100644 index 0000000000..d53c0da1f0 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/download/DownloadPost.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2011 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.repo.web.scripts.download; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.NodeRef; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Web script for creating a new download. + * + * @author Alex Miller + */ +public class DownloadPost extends AbstractDownloadWebscript +{ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + String error = "No parameters supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + + // Parse the JSON, if supplied + JSONArray json = null; + String contentType = req.getContentType(); + if (contentType != null && contentType.indexOf(';') != -1) + { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + + List nodes = new LinkedList(); + if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) + { + JSONParser parser = new JSONParser(); + try + { + json = (JSONArray)parser.parse(req.getContent().getContent()); + for (int i = 0 ; i < json.size() ; i++) + { + JSONObject obj = (JSONObject)json.get(i); + String nodeRefString = (String)obj.get("nodeRef"); + if (nodeRefString != null) + { + nodes.add(new NodeRef(nodeRefString)); + } + } + } + catch (IOException io) + { + throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Unexpected IOException", io); + } + catch (org.json.simple.parser.ParseException je) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Unexpected ParseException", je); + } + } + + if (nodes.size() <= 0) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No nodeRefs provided"); + } + + NodeRef downloadNode = downloadService.createDownload(nodes.toArray(new NodeRef[nodes.size()]), true); + + Map model = new HashMap(); + model.put("downloadNodeRef", downloadNode); + + return model; + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/download/DownloadRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/download/DownloadRestApiTest.java new file mode 100644 index 0000000000..c0ad6f2f87 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/download/DownloadRestApiTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2005-2011 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.repo.web.scripts.download; + +import java.io.IOException; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyMap; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Test; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * Tests for the Download webscripts. + * + * @author Alex Miller + */ +public class DownloadRestApiTest extends BaseWebScriptTest +{ + // Test COnstants + private static final long MAX_TIME = 5000; + private static final long PAUSE_TIME = 1000; + + private static final String TEST_USERNAME = "downloadTestUser"; + + // Urls + public static final String URL_DOWNLOADS = "/api/internal/downloads"; + public static final String URL_DOWNLOAD_STATUS = "/api/internal/downloads/{0}/{1}/{2}/status"; + + + // Various supporting services + private AuthenticationComponent authenticationComponent; + private MutableAuthenticationService authenticationService; + private ContentService contentService; + private NodeService nodeService; + private PersonService personService; + + // Test Content + private NodeRef rootFolder; + private NodeRef rootFile; + private NodeRef level1File; + private NodeRef level1Folder; + private NodeRef level2File; + + + public void setUp() + { + // Resolve required services + authenticationService = getServer().getApplicationContext().getBean("AuthenticationService", MutableAuthenticationService.class); + authenticationComponent = getServer().getApplicationContext().getBean("authenticationComponent", AuthenticationComponent.class); + contentService = getServer().getApplicationContext().getBean("ContentService", ContentService.class); + nodeService = getServer().getApplicationContext().getBean("NodeService", NodeService.class); + personService = getServer().getApplicationContext().getBean("PersonService", PersonService.class); + + // Authenticate as user + this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + // if user with given user name doesn't already exist then create user + if (this.authenticationService.authenticationExists(TEST_USERNAME) == false) + { + // create user + this.authenticationService.createAuthentication(TEST_USERNAME, "password".toCharArray()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, TEST_USERNAME); + personProps.put(ContentModel.PROP_FIRSTNAME, "FirstName123"); + personProps.put(ContentModel.PROP_LASTNAME, "LastName123"); + personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com"); + personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123"); + personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123"); + + // create person node for user + this.personService.createPerson(personProps); + } + + Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + + // Create some static test content + rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName()); + rootFile = createNodeWithTextContent(companyHome, "rootFile", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Root file content"); + + level1File = createNodeWithTextContent(rootFolder, "level1File", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 1 file content"); + level1Folder = createNode(rootFolder, "level1Folder", ContentModel.TYPE_FOLDER, AuthenticationUtil.getAdminUserName()); + + level2File = createNodeWithTextContent(level1Folder, "level2File", ContentModel.TYPE_CONTENT, AuthenticationUtil.getAdminUserName(), "Level 2 file content"); + } + + public void tearDown() + { + nodeService.deleteNode(level2File); + nodeService.deleteNode(level1Folder); + nodeService.deleteNode(level1File); + nodeService.deleteNode(rootFolder); + nodeService.deleteNode(rootFile); + + personService.deletePerson(TEST_USERNAME); + if (this.authenticationService.authenticationExists(TEST_USERNAME)) + { + this.authenticationService.deleteAuthentication(TEST_USERNAME); + } + + } + + + public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String ownerUserName, String content) + { + NodeRef nodeRef = createNode(parentNode, nodeCmName, nodeType, ownerUserName); + + // If there is any content, add it. + if (content != null) + { + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(content); + } + return nodeRef; + + } + + + private NodeRef createNode(NodeRef parentNode, String nodeCmName, QName nodeType, String ownerUserName) + { + QName childName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, nodeCmName); + + Map props = new HashMap(); + props.put(ContentModel.PROP_NAME, nodeCmName); + ChildAssociationRef childAssoc = nodeService.createNode(parentNode, + ContentModel.ASSOC_CONTAINS, + childName, + nodeType, + props); + return childAssoc.getChildRef(); + } + + @Test + public void testCreateAndGetDownload() throws UnsupportedEncodingException, IOException, JSONException + { + // CReate the download + String postData = "[{ \"nodeRef\": \"" + + rootFile + + "\"}, { \"nodeRef\": \"" + + rootFolder + + "\"}]"; + Response response = sendRequest(new PostRequest(URL_DOWNLOADS, postData, "application/json"), 200); + + // Parse the response + JSONObject result = new JSONObject(response.getContentAsString()); + + NodeRef downloadNodeRef = new NodeRef(result.getString("nodeRef")); + + // Get the status + String statusUrl = MessageFormat.format(URL_DOWNLOAD_STATUS, downloadNodeRef.getStoreRef().getProtocol(), downloadNodeRef.getStoreRef().getIdentifier(), downloadNodeRef.getId()); + Response statusResponse = sendRequest(new GetRequest(statusUrl), 200); + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/download/DownloadStatusGet.java b/source/java/org/alfresco/repo/web/scripts/download/DownloadStatusGet.java new file mode 100644 index 0000000000..5327ce33dd --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/download/DownloadStatusGet.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2011 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.repo.web.scripts.download; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.download.DownloadStatus; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * Webscript for retrieving the status of a download. + * + * @author Alex Miller + */ +public class DownloadStatusGet extends AbstractDownloadWebscript +{ + + protected NodeService nodeService; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + if (templateVars == null) + { + String error = "No parameters supplied"; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + if (! ( templateVars.containsKey("store_type") + && templateVars.containsKey("store_id") + && templateVars.containsKey("node_id")) ) + { + String error = "Missing template variables (store_type, store_id or node_id)."; + throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); + } + + StoreRef store = new StoreRef(templateVars.get("store_type"), templateVars.get("store_id")); + NodeRef nodeRef = new NodeRef(store, templateVars.get("node_id")); + if (! nodeService.exists(nodeRef)) + { + String error = "Could not find node: " + nodeRef; + throw new WebScriptException(Status.STATUS_NOT_FOUND, error); + } + + DownloadStatus downloadStatus = downloadService.getDownloadStatus(nodeRef); + + Map result = new HashMap(); + result.put("downloadStatus", downloadStatus); + return result; + + } + + public void setNodeService(NodeService nodeSerivce) + { + this.nodeService = nodeSerivce; + } +}