diff --git a/src/main/java/org/olat/core/util/vfs/VFSManager.java b/src/main/java/org/olat/core/util/vfs/VFSManager.java index 41133ae9454baffabfdc2682ce506e5f4e673a27..cb646c4ad7e682fb3be49dc7643487b4520afe93 100644 --- a/src/main/java/org/olat/core/util/vfs/VFSManager.java +++ b/src/main/java/org/olat/core/util/vfs/VFSManager.java @@ -533,6 +533,26 @@ public class VFSManager extends BasicManager { return successful; } + /** + * + * @param container + * @param filename + * @return + */ + public static String rename(VFSContainer container, String filename) { + String newName = filename; + VFSItem newFile = container.resolve(newName); + for(int count=0; newFile != null && count < 999 ; ) { + count++; + newName = appendNumberAtTheEndOfFilename(filename, count); + newFile = container.resolve(newName); + } + if(newFile == null) { + return newName; + } + return null; + } + /** * Check if the file exist or not * @param item diff --git a/src/main/java/org/olat/modules/fo/restapi/ForumWebService.java b/src/main/java/org/olat/modules/fo/restapi/ForumWebService.java index 067ffcf4463bbb67691fcb7177ba98ec312d3d42..e535ce5b4e9eb81e8be1f28a6a3d58df546ddf12 100644 --- a/src/main/java/org/olat/modules/fo/restapi/ForumWebService.java +++ b/src/main/java/org/olat/modules/fo/restapi/ForumWebService.java @@ -25,6 +25,7 @@ import static org.olat.restapi.security.RestSecurityHelper.getIdentity; import static org.olat.restapi.security.RestSecurityHelper.isAdmin; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; @@ -52,6 +53,7 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; +import org.apache.commons.io.IOUtils; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.id.Identity; @@ -63,6 +65,7 @@ import org.olat.core.util.vfs.LocalFileImpl; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; +import org.olat.core.util.vfs.VFSManager; import org.olat.core.util.vfs.restapi.SystemItemFilter; import org.olat.core.util.vfs.restapi.VFSStreamingOutput; import org.olat.modules.fo.Forum; @@ -464,16 +467,23 @@ public class ForumWebService { VFSLeaf attachment = null; if(item == null) { attachment = container.createChildLeaf(filename); - } else if (item instanceof VFSLeaf) { - attachment = (VFSLeaf)item; } else { - return Response.serverError().status(Status.CONFLICT).build(); + filename = VFSManager.rename(container, filename); + if(filename == null) { + return Response.serverError().status(Status.NOT_ACCEPTABLE).build(); + } + attachment = container.createChildLeaf(filename); } OutputStream out = attachment.getOutputStream(false); - FileUtils.copy(file, out); - FileUtils.closeSafely(out); - FileUtils.closeSafely(file); + try { + IOUtils.copy(file, out); + } catch (IOException e) { + return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); + } finally { + FileUtils.closeSafely(out); + FileUtils.closeSafely(file); + } return Response.ok().build(); } diff --git a/src/test/java/org/olat/restapi/ForumTest.java b/src/test/java/org/olat/restapi/ForumTest.java index d7c9d46a532742f16bfc68eac662b0c10ca6fa51..041e5ecc45e2fc9c83c0818c4c0ac3fe061e17ca 100644 --- a/src/test/java/org/olat/restapi/ForumTest.java +++ b/src/test/java/org/olat/restapi/ForumTest.java @@ -26,9 +26,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.util.List; import javax.ws.rs.core.MediaType; @@ -36,7 +39,12 @@ import javax.ws.rs.core.UriBuilder; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.multipart.FilePart; +import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; +import org.apache.commons.httpclient.methods.multipart.Part; +import org.apache.commons.httpclient.methods.multipart.StringPart; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import org.junit.Before; @@ -48,6 +56,7 @@ import org.olat.modules.fo.ForumManager; import org.olat.modules.fo.Message; import org.olat.modules.fo.restapi.MessageVO; import org.olat.modules.fo.restapi.MessageVOes; +import org.olat.restapi.support.vo.FileVO; import org.olat.test.JunitTestHelper; import org.olat.test.OlatJerseyTestCase; @@ -220,6 +229,113 @@ public class ForumTest extends OlatJerseyTestCase { assertTrue(saved); } + @Test + public void testGetAttachment() throws IOException, URISyntaxException { + HttpClient c = loginWithCookie("administrator", "olat"); + + URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); + GetMethod method = createGet(uri, MediaType.APPLICATION_JSON, true); + int code = c.executeMethod(method); + assertEquals(200, code); + InputStream body = method.getResponseBodyAsStream(); + List<FileVO> files = parseFileArray(body); + assertNotNull(files); + } + + @Test + public void testUploadAttachment() throws IOException, URISyntaxException { + HttpClient c = loginWithCookie(id1.getName(), "A6B7C8"); + + URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) + .queryParam("authorKey", id1.getKey()) + .queryParam("title", "New message with attachment ") + .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); + PutMethod method = createPut(uri, MediaType.APPLICATION_JSON, true); + int code = c.executeMethod(method); + assertEquals(200, code); + InputStream body = method.getResponseBodyAsStream(); + MessageVO message = parse(body, MessageVO.class); + assertNotNull(message); + + //attachment + URL portraitUrl = RepositoryEntriesTest.class.getResource("portrait.jpg"); + assertNotNull(portraitUrl); + File portrait = new File(portraitUrl.toURI()); + + //upload portrait + URI attachUri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); + PostMethod attachMethod = createPost(attachUri, MediaType.APPLICATION_JSON, true); + attachMethod.addRequestHeader("Content-Type", MediaType.MULTIPART_FORM_DATA); + Part[] parts = { + new FilePart("file", portrait), + new StringPart("filename","portrait.jpg") + }; + attachMethod.setRequestEntity(new MultipartRequestEntity(parts, attachMethod.getParams())); + int attachCode = c.executeMethod(attachMethod); + assertEquals(200, attachCode); + attachMethod.releaseConnection(); + } + + @Test + public void testUploadAttachmentAndRename() throws IOException, URISyntaxException { + HttpClient c = loginWithCookie(id1.getName(), "A6B7C8"); + + URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) + .queryParam("authorKey", id1.getKey()) + .queryParam("title", "New message with attachment ") + .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); + PutMethod method = createPut(uri, MediaType.APPLICATION_JSON, true); + int code = c.executeMethod(method); + assertEquals(200, code); + InputStream body = method.getResponseBodyAsStream(); + MessageVO message = parse(body, MessageVO.class); + assertNotNull(message); + + //attachment + URL portraitUrl = RepositoryEntriesTest.class.getResource("portrait.jpg"); + assertNotNull(portraitUrl); + File portrait = new File(portraitUrl.toURI()); + + //upload portrait + URI attachUri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); + PostMethod attachMethod = createPost(attachUri, MediaType.APPLICATION_JSON, true); + attachMethod.addRequestHeader("Content-Type", MediaType.MULTIPART_FORM_DATA); + Part[] parts = { + new FilePart("file", portrait), + new StringPart("filename","portrait.jpg") + }; + attachMethod.setRequestEntity(new MultipartRequestEntity(parts, attachMethod.getParams())); + int attachCode = c.executeMethod(attachMethod); + assertEquals(200, attachCode); + attachMethod.releaseConnection(); + + //upload portrait a second time + URI attach2Uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); + PostMethod attach2Method = createPost(attach2Uri, MediaType.APPLICATION_JSON, true); + attach2Method.addRequestHeader("Content-Type", MediaType.MULTIPART_FORM_DATA); + Part[] parts2 = { + new FilePart("file", portrait), + new StringPart("filename","portrait.jpg") + }; + attach2Method.setRequestEntity(new MultipartRequestEntity(parts2, attach2Method.getParams())); + int attach2Code = c.executeMethod(attach2Method); + assertEquals(200, attach2Code); + attach2Method.releaseConnection(); + + // load the attachments + + URI loadUri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); + GetMethod loadMethod = createGet(loadUri, MediaType.APPLICATION_JSON, true); + int loadCode = c.executeMethod(loadMethod); + assertEquals(200, loadCode); + InputStream loadBody = loadMethod.getResponseBodyAsStream(); + List<FileVO> files = parseFileArray(loadBody); + assertNotNull(files); + assertEquals(2, files.size()); + loadMethod.releaseConnection(); + + } + private UriBuilder getForumUriBuilder() { return UriBuilder.fromUri(getContextURI()).path("repo").path("forums").path(forum.getKey().toString()); } diff --git a/src/test/java/org/olat/test/OlatJerseyTestCase.java b/src/test/java/org/olat/test/OlatJerseyTestCase.java index f6054775ab52a3661c40e8f458e45ff3f0185593..95a4c6f148373e1a149db6c5ee67b4bc77b45808 100644 --- a/src/test/java/org/olat/test/OlatJerseyTestCase.java +++ b/src/test/java/org/olat/test/OlatJerseyTestCase.java @@ -55,6 +55,7 @@ import org.olat.restapi.security.RestApiLoginFilter; import org.olat.restapi.security.RestSecurityHelper; import org.olat.restapi.support.OlatRestApplication; import org.olat.restapi.support.vo.ErrorVO; +import org.olat.restapi.support.vo.FileVO; import org.olat.restapi.support.vo.LinkVO; import org.springframework.beans.factory.annotation.Autowired; @@ -381,4 +382,14 @@ public abstract class OlatJerseyTestCase extends OlatTestCase { return null; } } + + protected List<FileVO> parseFileArray(InputStream body) { + try { + ObjectMapper mapper = new ObjectMapper(jsonFactory); + return mapper.readValue(body, new TypeReference<List<FileVO>>(){/* */}); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } } \ No newline at end of file