/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.web.basic;
import org.structr.web.StructrUiTest;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.entity.AbstractNode;
import org.structr.core.graph.NodeAttribute;
import org.structr.core.graph.Tx;
import org.structr.core.property.PropertyMap;
import org.structr.dynamic.File;
import org.structr.web.common.FileHelper;
import org.structr.web.common.ImageHelper;
import org.structr.web.common.ImageHelper.Thumbnail;
import org.structr.web.entity.FileBase;
import org.structr.web.entity.Folder;
import org.structr.web.entity.Image;
import org.structr.web.entity.TestImage;
public class UiTest extends StructrUiTest {
private static String base64Image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWAAAABUCAYAAAC8/e1DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2RjYyNjlFMUNFMTNFMjExQTQ2N0ZGMDI2MEZEQ0Q3NSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2MDcwOEExQzEzRDMxMUUyQTMyQzlEQjBGNTBBQUUwMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2MDcwOEExQjEzRDMxMUUyQTMyQzlEQjBGNTBBQUUwMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkYzODhBQzYwRDIxM0UyMTFBNDY3RkYwMjYwRkRDRDc1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZGNjI2OUUxQ0UxM0UyMTFBNDY3RkYwMjYwRkRDRDc1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+xNxK9AAAAt9JREFUeNrs3TFOKlEUgGGuIWhGSwmVLoMlWFi8ddiZuA41sTMuw9LlYGWBEqOE4DC8c4mNhSUzhvt9yQ2813GCf46DOmk8Hu9dXl7+q6rqPqU06gGwNev1+mU+n1/c3d099uPfB+Eh/nMYx3QAtmuUmxuPTznAh+IL0Kphbm8O8GC1WhkHQLsGOcCpaRqjAGhXygHu2YAB2ifAAF0GuK5rkwCwAQPYgAEQYIAdDbBLEAA2YAABBqCFALsEASDAAGUF2CUIABswQFkB9tfQAGzAAGUF2DVggI4CvFgsTALABgxQUIDdkBOgfXtGACDAAAIMgAADCDAAAgwgwAAIMIAAAyDAAH/a5leRU0omAWADBrABA2ADBtjNAK/Dm1EAtCOaO8sPOcDL19fXm6Zp3o0FYLuitbNo7nVubxqPx1U8OY1zEucojgvC3egbAYV81516Py9/5jtC5DsD19/Pd3r5jfMR5znOJH/R5xvCTeJM4wwEuNM3JpQg/RKmpoDXnl/nMs5nbm//+Pi4d3V1dVZV1X1KaeS9AbDFAq/XL/P5/OL29vYxb8AH+/v7D03TDI0GYOtGubnx+JQDfBhFHroxJ0Br8sJ7mAM8iO3XOADaNcgBTgIM0Lq0+dGn1WplFAAt2wTYBgzQUYC/vr5MAqCLALsEASDAAGUF2CUIgI4C7EM4gI4CXNe1SQAIMEBBAfYhHIAAA5QVYJcgAGzAAGUF2N8CBugowC5BAAgwQFkBXiwWJgFgAwYoKMA+hANo354RAAgwgAADIMAAAgyAAAMIMAACDCDAAAgwwJ+2+VXklJJJANiAAWzAANiAAXYzwOvwZhQA7YjmzvJDDvByOp3eNE3zbiwA2xWtnUVzr3N70/n5eRVPTuOcxDmK44JwN/pGQCHfdafez8uf+Y4Qqzj19/OdXn7jfMR5jjPJX/T5hnCTONM4AwHu9I0JJUi/hKkp4LXn17mM85nb+1+AAQDuVAgNv/BqVwAAAABJRU5ErkJggg==";
private static final Logger logger = LoggerFactory.getLogger(UiTest.class.getName());
@Test
public void test01CreateThumbnail() {
try (final Tx tx = app.tx()) {
TestImage img = (TestImage) ImageHelper.createFileBase64(securityContext, base64Image, TestImage.class);
img.setProperties(img.getSecurityContext(), new PropertyMap(AbstractNode.name, "test-image.png"));
assertNotNull(img);
assertTrue(img instanceof TestImage);
Image tn = img.getProperty(TestImage.thumbnail);
assertNotNull(tn);
assertEquals(new Integer(200), tn.getWidth());
assertEquals(new Integer(48), tn.getHeight()); // cropToFit = false
assertEquals("image/" + Thumbnail.Format.jpeg, tn.getContentType());
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01AutoRenameThumbnail() {
final String initialImageName = "initial_image_name.png";
final String renamedImageName = "image_name_after_rename.png";
Image testImage = null;
try (final Tx tx = app.tx()) {
testImage = (Image) ImageHelper.createFileBase64(securityContext, base64Image, Image.class);
testImage.setProperties(testImage.getSecurityContext(), new PropertyMap(Image.name, initialImageName));
assertNotNull(testImage);
assertTrue(testImage instanceof Image);
final Image tnSmall = testImage.getProperty(Image.tnSmall);
final Image tnMid = testImage.getProperty(Image.tnMid);
assertEquals("Initial small thumbnail name not as expected", ImageHelper.getThumbnailName(initialImageName, tnSmall.getWidth(), tnSmall.getHeight()), tnSmall.getProperty(Image.name));
assertEquals("Initial mid thumbnail name not as expected", ImageHelper.getThumbnailName(initialImageName, tnMid.getWidth(), tnMid.getHeight()), tnMid.getProperty(Image.name));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
testImage.setProperties(testImage.getSecurityContext(), new PropertyMap(Image.name, renamedImageName));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
final Image tnSmall = testImage.getProperty(Image.tnSmall);
final Image tnMid = testImage.getProperty(Image.tnMid);
assertEquals("Small Thumbnail name not auto-renamed as expected", ImageHelper.getThumbnailName(renamedImageName, tnSmall.getWidth(), tnSmall.getHeight()), tnSmall.getProperty(Image.name));
assertEquals("Mid Thumbnail name not auto-renamed as expected", ImageHelper.getThumbnailName(renamedImageName, tnMid.getWidth(), tnMid.getHeight()), tnMid.getProperty(Image.name));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01AutoRenameThumbnailForImageSubclass() {
TestImage subclassTestImage = null;
final String initialImageName = "initial_image_name.png";
final String renamedImageName = "image_name_after_rename.png";
try (final Tx tx = app.tx()) {
subclassTestImage = (TestImage) ImageHelper.createFileBase64(securityContext, base64Image, TestImage.class);
subclassTestImage.setProperties(subclassTestImage.getSecurityContext(), new PropertyMap(TestImage.name, initialImageName));
assertNotNull(subclassTestImage);
assertTrue(subclassTestImage instanceof TestImage);
final Image tnSmall = subclassTestImage.getProperty(Image.tnSmall);
final Image tnMid = subclassTestImage.getProperty(Image.tnMid);
final Image tnCustom = subclassTestImage.getProperty(TestImage.thumbnail);
assertEquals("Initial small thumbnail name not as expected", ImageHelper.getThumbnailName(initialImageName, tnSmall.getWidth(), tnSmall.getHeight()), tnSmall.getProperty(Image.name));
assertEquals("Initial mid thumbnail name not as expected", ImageHelper.getThumbnailName(initialImageName, tnMid.getWidth(), tnMid.getHeight()), tnMid.getProperty(Image.name));
assertEquals("Initial custom thumbnail name not as expected", ImageHelper.getThumbnailName(initialImageName, tnCustom.getWidth(), tnCustom.getHeight()), tnCustom.getProperty(Image.name));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
subclassTestImage.setProperties(subclassTestImage.getSecurityContext(), new PropertyMap(Image.name, renamedImageName));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
final Image tnSmall = subclassTestImage.getProperty(Image.tnSmall);
final Image tnMid = subclassTestImage.getProperty(Image.tnMid);
final Image tnCustom = subclassTestImage.getProperty(TestImage.thumbnail);
assertEquals("Small Thumbnail name not auto-renamed as expected for image subclass", ImageHelper.getThumbnailName(renamedImageName, tnSmall.getWidth(), tnSmall.getHeight()), tnSmall.getProperty(Image.name));
assertEquals("Mid Thumbnail name not auto-renamed as expected for image subclass", ImageHelper.getThumbnailName(renamedImageName, tnMid.getWidth(), tnMid.getHeight()), tnMid.getProperty(Image.name));
assertEquals("Custom Thumbnail name not auto-renamed as expected for image subclass", ImageHelper.getThumbnailName(renamedImageName, tnCustom.getWidth(), tnCustom.getHeight()), tnCustom.getProperty(Image.name));
tx.success();
} catch (Exception ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void testFolderPath() {
try (final Tx tx = app.tx()) {
Folder test4 = FileHelper.createFolderPath(SecurityContext.getSuperUserInstance(), "/a/a");
Folder test3 = FileHelper.createFolderPath(SecurityContext.getSuperUserInstance(), "/c/b/a");
Folder test2 = FileHelper.createFolderPath(SecurityContext.getSuperUserInstance(), "/b/a");
Folder test1 = FileHelper.createFolderPath(SecurityContext.getSuperUserInstance(), "/a/b/c");
tx.success();
} catch (FrameworkException ex) {
logger.error("", ex);
}
try (final Tx tx = app.tx()) {
Folder a = (Folder) FileHelper.getFileByAbsolutePath(SecurityContext.getSuperUserInstance(), "/a");
assertNotNull(a);
assertEquals(FileHelper.getFolderPath(a), "/a");
Folder b = (Folder) FileHelper.getFileByAbsolutePath(SecurityContext.getSuperUserInstance(), "/a/b");
assertNotNull(b);
assertEquals(FileHelper.getFolderPath(b), "/a/b");
Folder c = (Folder) FileHelper.getFileByAbsolutePath(SecurityContext.getSuperUserInstance(), "/a/b/c");
assertNotNull(c);
assertEquals(FileHelper.getFolderPath(c), "/a/b/c");
} catch (FrameworkException ex) {
logger.error("", ex);
}
}
@Test
public void testAllowedCharacters() {
try (final Tx tx = app.tx()) {
app.create(Folder.class, "/a/b");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "a/b");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "/");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "c/");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "abc\0");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "\0abc");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
try (final Tx tx = app.tx()) {
app.create(Folder.class, "a\0bc");
tx.success();
fail("Folder with non-allowed characters were created.");
} catch (FrameworkException ex) {}
}
@Test
public void testCreateFolder() {
Folder folder1 = null;
try (final Tx tx = app.tx()) {
folder1 = FileHelper.createFolderPath(SecurityContext.getSuperUserInstance(), "/folder1");
tx.success();
} catch (FrameworkException ex) {
logger.error("", ex);
}
try (final Tx tx = app.tx()) {
FileBase file1 = (FileBase) app.create(File.class, "file1");
assertNotNull(file1);
assertEquals(FileHelper.getFolderPath(file1), "/file1");
file1.setProperties(file1.getSecurityContext(), new PropertyMap(File.parent, folder1));
assertEquals(FileHelper.getFolderPath(file1), "/folder1/file1");
tx.success();
} catch (FrameworkException ex) {
logger.error("", ex);
}
try (final Tx tx = app.tx()) {
Image image1 = (Image) app.create(Image.class, "image1");
assertNotNull(image1);
assertEquals(FileHelper.getFolderPath(image1), "/image1");
image1.setProperties(image1.getSecurityContext(), new PropertyMap(File.parent, folder1));
assertEquals(FileHelper.getFolderPath(image1), "/folder1/image1");
tx.success();
} catch (FrameworkException ex) {
logger.error("", ex);
}
try (final Tx tx = app.tx()) {
assertEquals(2, folder1.getProperty(Folder.files).size());
assertEquals(1, folder1.getProperty(Folder.images).size());
} catch (FrameworkException ex) {
logger.error("", ex);
}
}
@Test
public void testCreateBase64File() {
final String base64Data = "data:text/plain;base64,RGllcyBpc3QgZWluIFRlc3Q=";
final String plaintext = "Dies ist ein Test";
FileBase file = null;
try (final Tx tx = app.tx()) {
file = app.create(File.class,
new NodeAttribute<>(AbstractNode.name, "test.txt"),
new NodeAttribute<>(FileBase.base64Data, base64Data)
);
tx.success();
} catch (FrameworkException ex) {
logger.error("", ex);
}
try (final Tx tx = app.tx()) {
assertEquals("Invalid base64 encoded file content creation result", plaintext, IOUtils.toString(file.getInputStream()));
tx.success();
} catch (FrameworkException | IOException ex) {
logger.error("", ex);
}
}
@Test
public void testExtensionBasedMimeTypeDetection() {
final Map<String, Map<String, byte[]>> testMap = new LinkedHashMap<>();
testMap.put("text/html", toMap(new Pair("test.html", "<!DOCTYPE html><html><head><title>Test</title></head><body><h1>Test</h1></body></html>".getBytes()), new Pair("test.htm", "<!DOCTYPE html>".getBytes())));
testMap.put("text/plain", toMap(new Pair("test.txt", "Hello world!".getBytes())));
testMap.put("text/css", toMap(new Pair("test.css", "body { background-color: #ffffff; }".getBytes())));
testMap.put("application/javascript", toMap(new Pair("test.js", "function() { alert('Test'); }".getBytes())));
testMap.put("application/zip", toMap(new Pair("test.zip", "".getBytes())));
testMap.put("image/jpeg", toMap(new Pair("test.jpg", "".getBytes()), new Pair("test.jpeg", "".getBytes())));
testMap.put("image/png", toMap(new Pair("test.png", "".getBytes())));
try (final Tx tx = app.tx()) {
for (final Entry<String, Map<String, byte[]>> entry : testMap.entrySet()) {
final String mimeType = entry.getKey();
for (final Entry<String, byte[]> fileEntry : entry.getValue().entrySet()) {
final String fileName = fileEntry.getKey();
final byte[] content = fileEntry.getValue();
try {
final FileBase file = FileHelper.createFile(securityContext, content, null, File.class, fileName);
assertEquals("MIME type detection failed", mimeType, file.getContentType());
} catch (IOException ioex) {
logger.warn("", ioex);
fail("Unexpected exception");
}
}
}
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception");
}
}
@Test
public void testContentBasedMimeTypeDetection() {
final Map<String, Map<String, byte[]>> testMap = new LinkedHashMap<>();
try {
// text-based formats will of course resolved into "text/plain"
testMap.put("text/plain", toMap(new Pair("test01", "<!DOCTYPE html><html><head><title>Test</title></head><body><h1>Test</h1></body></html>".getBytes())));
testMap.put("text/plain", toMap(new Pair("test02", "Hello world!".getBytes())));
testMap.put("text/plain", toMap(new Pair("test03", "body { background-color: #ffffff; }".getBytes())));
// disabled because jmimemagic detects matlab..
// testMap.put("text/plain", toMap(new Pair("test04", "function test() { alert('Test'); return 'Hello world!'; }".getBytes())));
testMap.put("application/zip", toMap(new Pair("test05", IOUtils.toByteArray(UiTest.class.getResourceAsStream("/test/test.zip")))));
testMap.put("image/jpeg", toMap(new Pair("test06", IOUtils.toByteArray(UiTest.class.getResourceAsStream("/test/test.jpg")))));
testMap.put("image/png", toMap(new Pair("test07", IOUtils.toByteArray(UiTest.class.getResourceAsStream("/test/test.png")))));
testMap.put("image/gif", toMap(new Pair("test08", IOUtils.toByteArray(UiTest.class.getResourceAsStream("/test/test.gif")))));
// disabled because jmimemagic v0.1.2 does not properly detect image/tiff cross-OS
// testMap.put("image/tiff", toMap(new Pair("test09", IOUtils.toByteArray(FileHelperTest.class.getResourceAsStream("/test/test.tiff")))));
// disabled because jmimemagic v0.1.2 does not properly detect image/bmp cross-OS
// testMap.put("image/bmp", toMap(new Pair("test10", IOUtils.toByteArray(FileHelperTest.class.getResourceAsStream("/test/test.bmp")))));
// disabled because jmimemagic v0.1.2 does not properly detect image/vnd.microsoft.icon cross-OS
// testMap.put("image/vnd.microsoft.icon", toMap(new Pair("test11", IOUtils.toByteArray(FileHelperTest.class.getResourceAsStream("/test/test.ico")))));
} catch (IOException ioex) {
fail("Unexpected exception.");
}
try (final Tx tx = app.tx()) {
for (final Entry<String, Map<String, byte[]>> entry : testMap.entrySet()) {
final String mimeType = entry.getKey();
for (final Entry<String, byte[]> fileEntry : entry.getValue().entrySet()) {
final String fileName = fileEntry.getKey();
final byte[] content = fileEntry.getValue();
try {
final FileBase file = FileHelper.createFile(securityContext, content, null, File.class, fileName);
assertEquals("MIME type detection failed for " + fileName, mimeType, file.getContentType());
} catch (IOException ioex) {
logger.warn("", ioex);
fail("Unexpected exception");
}
}
}
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception");
}
}
}