#79: Change background and logo image

This commit is contained in:
jendib 2016-05-08 17:25:21 +02:00
parent f5394534f7
commit 4d79dd7076
No known key found for this signature in database
GPG Key ID: 06EE7F699579166F
13 changed files with 153 additions and 32 deletions

View File

@ -72,7 +72,7 @@ public class AppContext {
eventBus = new EventBus(); eventBus = new EventBus();
eventBus.register(new DeadEventListener()); eventBus.register(new DeadEventListener());
asyncExecutorList = new ArrayList<ExecutorService>(); asyncExecutorList = new ArrayList<>();
asyncEventBus = newAsyncEventBus(); asyncEventBus = newAsyncEventBus();
asyncEventBus.register(new FileCreatedAsyncListener()); asyncEventBus.register(new FileCreatedAsyncListener());

View File

@ -86,6 +86,15 @@ public class DirectoryUtil {
return getDataSubDirectory("log"); return getDataSubDirectory("log");
} }
/**
* Returns the theme directory.
*
* @return Theme directory.
*/
public static Path getThemeDirectory() {
return getDataSubDirectory("theme");
}
/** /**
* Returns a subdirectory of the base data directory * Returns a subdirectory of the base data directory
* *

View File

@ -1,5 +1,6 @@
package com.sismics.util.mime; package com.sismics.util.mime;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -21,16 +22,16 @@ public class MimeTypeUtil {
* *
* @param is Stream to inspect * @param is Stream to inspect
* @return MIME type * @return MIME type
* @throws Exception * @throws IOException
*/ */
public static String guessMimeType(InputStream is) throws Exception { public static String guessMimeType(InputStream is) throws IOException {
byte[] headerBytes = new byte[64]; byte[] headerBytes = new byte[64];
is.mark(headerBytes.length); is.mark(headerBytes.length);
int readCount = is.read(headerBytes); int readCount = is.read(headerBytes);
is.reset(); is.reset();
if (readCount <= 0) { if (readCount <= 0) {
throw new Exception("Cannot read input file"); throw new IOException("Cannot read input file");
} }
return guessMimeType(headerBytes); return guessMimeType(headerBytes);

View File

@ -107,7 +107,7 @@ public class FileResource extends BaseResource {
String mimeType; String mimeType;
try { try {
mimeType = MimeTypeUtil.guessMimeType(fileInputStream); mimeType = MimeTypeUtil.guessMimeType(fileInputStream);
} catch (Exception e) { } catch (IOException e) {
throw new ServerException("ErrorGuessMime", "Error guessing mime type", e); throw new ServerException("ErrorGuessMime", "Error guessing mime type", e);
} }
if (mimeType == null) { if (mimeType == null) {

View File

@ -1,22 +1,31 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import javax.json.*;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.sismics.docs.core.constant.ConfigType; import com.sismics.docs.core.constant.ConfigType;
import com.sismics.docs.core.dao.jpa.ConfigDao; import com.sismics.docs.core.dao.jpa.ConfigDao;
import com.sismics.docs.core.model.jpa.Config; import com.sismics.docs.core.model.jpa.Config;
import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException;
import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.JsonUtil;
import com.sismics.rest.util.ValidationUtil; import com.sismics.rest.util.ValidationUtil;
import com.sismics.util.css.Selector; import com.sismics.util.css.Selector;
import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import javax.json.*;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader; import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Map; import java.util.Map;
/** /**
@ -26,6 +35,10 @@ import java.util.Map;
*/ */
@Path("/theme") @Path("/theme")
public class ThemeResource extends BaseResource { public class ThemeResource extends BaseResource {
// Filenames for images in theme directory
private static final String LOGO_IMAGE = "logo";
private static final String BACKGROUND_IMAGE = "background";
/** /**
* Returns custom CSS stylesheet. * Returns custom CSS stylesheet.
* *
@ -116,10 +129,63 @@ public class ThemeResource extends BaseResource {
throw new ForbiddenClientException(); throw new ForbiddenClientException();
} }
checkBaseFunction(BaseFunction.ADMIN); checkBaseFunction(BaseFunction.ADMIN);
if (logoBodyPart == null && backgrounBodyPart == null) {
throw new ClientException("NoImageProvided", "logo or background is required");
}
// Only a background or a logo is handled
FormDataBodyPart bodyPart = logoBodyPart == null ? backgrounBodyPart : logoBodyPart;
String type = logoBodyPart == null ? BACKGROUND_IMAGE : LOGO_IMAGE;
java.nio.file.Path filePath = DirectoryUtil.getThemeDirectory().resolve(type);
// Copy the image to the theme directory
try (InputStream inputStream = bodyPart.getValueAs(InputStream.class)) {
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
throw new ServerException("CopyError", "Error copying the image to the theme directory", e);
}
return Response.ok().build(); return Response.ok().build();
} }
@GET
@Produces("image/*")
@Path("image/{type: logo|background}")
public Response getImage(@PathParam("type") final String type) {
if (!LOGO_IMAGE.equals(type) && !BACKGROUND_IMAGE.equals(type)) {
throw new ClientException("InvalidType", "Type must be logo or background");
}
final java.nio.file.Path filePath = DirectoryUtil.getThemeDirectory().resolve(type);
// Copy the image to the response output
return Response.ok(new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
InputStream inputStream = null;
try {
if (Files.exists(filePath)) {
inputStream = Files.newInputStream(filePath);
} else {
inputStream = getClass().getResource("/image/" + (type.equals(LOGO_IMAGE) ? "logo.png" : "background.jpg")).openStream();
}
ByteStreams.copy(inputStream, outputStream);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
outputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
})
.header("Content-Type", "image/*")
.build();
}
/** /**
* Returns the theme configuration object. * Returns the theme configuration object.
* *

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -93,7 +93,7 @@ module.exports = function(grunt) {
}, },
replace: { replace: {
dist: { dist: {
src: ['dist/docs.min.js', 'dist/share.min.js', 'dist/**/*.html'], src: ['dist/docs.min.js', 'dist/share.min.js', 'dist/**/*.html', 'dist/style/style.min.css'],
overwrite: true, overwrite: true,
replacements: [{ replacements: [{
from: '../api', from: '../api',

View File

@ -4,7 +4,7 @@
<title ng-bind-template="{{ appName }}">Sismics Docs</title> <title ng-bind-template="{{ appName }}">Sismics Docs</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.png" /> <link rel="shortcut icon" href="../api/theme/image/logo" />
<!-- ref:css style/style.min.css --> <!-- ref:css style/style.min.css -->
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" /> <link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
<link rel="stylesheet" href="style/colorpicker.css" type="text/css" /> <link rel="stylesheet" href="style/colorpicker.css" type="text/css" />
@ -100,7 +100,7 @@
</button> </button>
<div class="hidden-xs navbar-text navbar-logo"> <div class="hidden-xs navbar-text navbar-logo">
<img src="favicon.png" /> <img src="../api/theme/image/logo" />
</div> </div>
<a class="navbar-brand" href="#"> {{ appName }}</a> <a class="navbar-brand" href="#"> {{ appName }}</a>

View File

@ -46,7 +46,7 @@
<div class="navbar navbar-default" role="navigation"> <div class="navbar navbar-default" role="navigation">
<div class="navbar-header"> <div class="navbar-header">
<div class="hidden-xs navbar-text navbar-logo"> <div class="hidden-xs navbar-text navbar-logo">
<img src="favicon.png" /> <img src="../api/theme/image/logo" />
</div> </div>
<a class="navbar-brand" href="#"> {{ appName }}</a> <a class="navbar-brand" href="#"> {{ appName }}</a>

View File

@ -156,10 +156,6 @@ input[readonly].share-link {
// Pagination box // Pagination box
.pagination-box { .pagination-box {
pagination {
vertical-align: top;
}
select { select {
vertical-align: top; vertical-align: top;
width: auto; width: auto;
@ -234,7 +230,7 @@ input[readonly].share-link {
// Login // Login
.login-box-container { .login-box-container {
background: url('../img/background.jpg') no-repeat center; background: url('../../api/theme/image/background') no-repeat center;
} }
.login-box { .login-box {

View File

@ -56,7 +56,7 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertNotNull(document1Id); Assert.assertNotNull(document1Id);
// Add a file // Add a file
String file1Id = null; String file1Id;
try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) { try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg"); StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
@ -68,12 +68,12 @@ public class TestFileResource extends BaseJerseyTest {
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class); MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
file1Id = json.getString("id"); file1Id = json.getString("id");
Assert.assertNotNull(file1Id); Assert.assertNotNull(file1Id);
Assert.assertEquals(163510l, json.getJsonNumber("size").longValue()); Assert.assertEquals(163510L, json.getJsonNumber("size").longValue());
} }
} }
// Add a file // Add a file
String file2Id = null; String file2Id;
try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) { try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg"); StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
@ -136,11 +136,11 @@ public class TestFileResource extends BaseJerseyTest {
JsonArray files = json.getJsonArray("files"); JsonArray files = json.getJsonArray("files");
Assert.assertEquals(2, files.size()); Assert.assertEquals(2, files.size());
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id")); Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
Assert.assertEquals(163510l, files.getJsonObject(0).getJsonNumber("size").longValue()); Assert.assertEquals(163510L, files.getJsonObject(0).getJsonNumber("size").longValue());
Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id")); Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id"));
// Reorder files // Reorder files
json = target().path("/file/reorder").request() target().path("/file/reorder").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token)
.post(Entity.form(new Form() .post(Entity.form(new Form()
.param("id", document1Id) .param("id", document1Id)
@ -210,7 +210,7 @@ public class TestFileResource extends BaseJerseyTest {
String file2Token = clientUtil.login("file2"); String file2Token = clientUtil.login("file2");
// Add a file // Add a file
String file1Id = null; String file1Id;
try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) { try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg"); StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "PIA00452.jpg");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
@ -251,7 +251,7 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertNotNull(document1Id); Assert.assertNotNull(document1Id);
// Attach a file to a document // Attach a file to a document
json = target().path("/file/" + file1Id).request() target().path("/file/" + file1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file2Token) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, file2Token)
.post(Entity.form(new Form() .post(Entity.form(new Form()
.param("id", document1Id)), JsonObject.class); .param("id", document1Id)), JsonObject.class);
@ -266,7 +266,7 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertEquals(1, files.size()); Assert.assertEquals(1, files.size());
// Add a file // Add a file
String file2Id = null; String file2Id;
try (InputStream is0 = Resources.getResource("file/PIA00452.jpg").openStream()) { try (InputStream is0 = Resources.getResource("file/PIA00452.jpg").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is0, "PIA00452.jpg"); StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is0, "PIA00452.jpg");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
@ -300,7 +300,7 @@ public class TestFileResource extends BaseJerseyTest {
String fileQuotaToken = clientUtil.login("file_quota"); String fileQuotaToken = clientUtil.login("file_quota");
// Add a file (292641 bytes large) // Add a file (292641 bytes large)
String file1Id = null; String file1Id;
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png"); StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
@ -319,7 +319,7 @@ public class TestFileResource extends BaseJerseyTest {
JsonObject json = target().path("/user").request() JsonObject json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken)
.get(JsonObject.class); .get(JsonObject.class);
Assert.assertEquals(292641l, json.getJsonNumber("storage_current").longValue()); Assert.assertEquals(292641L, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large) // Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
@ -338,7 +338,7 @@ public class TestFileResource extends BaseJerseyTest {
json = target().path("/user").request() json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken)
.get(JsonObject.class); .get(JsonObject.class);
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue()); Assert.assertEquals(585282L, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large) // Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
@ -357,7 +357,7 @@ public class TestFileResource extends BaseJerseyTest {
json = target().path("/user").request() json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken)
.get(JsonObject.class); .get(JsonObject.class);
Assert.assertEquals(877923l, json.getJsonNumber("storage_current").longValue()); Assert.assertEquals(877923L, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large) // Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
@ -383,6 +383,6 @@ public class TestFileResource extends BaseJerseyTest {
json = target().path("/user").request() json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken) .cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaToken)
.get(JsonObject.class); .get(JsonObject.class);
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue()); Assert.assertEquals(585282L, json.getJsonNumber("storage_current").longValue());
} }
} }

View File

@ -1,12 +1,19 @@
package com.sismics.docs.rest; package com.sismics.docs.rest;
import com.google.common.io.Resources;
import com.sismics.util.filter.TokenBasedSecurityFilter; import com.sismics.util.filter.TokenBasedSecurityFilter;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import javax.json.JsonObject; import javax.json.JsonObject;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form; import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
/** /**
* Test the theme resource. * Test the theme resource.
@ -18,7 +25,7 @@ public class TestThemeResource extends BaseJerseyTest {
* Test the theme resource. * Test the theme resource.
*/ */
@Test @Test
public void testThemeResource() { public void testThemeResource() throws Exception {
// Login admin // Login admin
String adminToken = clientUtil.login("admin", "admin", false); String adminToken = clientUtil.login("admin", "admin", false);
@ -54,5 +61,47 @@ public class TestThemeResource extends BaseJerseyTest {
Assert.assertEquals("My App", json.getString("name")); Assert.assertEquals("My App", json.getString("name"));
Assert.assertEquals("#ff0000", json.getString("color")); Assert.assertEquals("#ff0000", json.getString("color"));
Assert.assertEquals(".body { content: 'Custom CSS'; }", json.getString("css")); Assert.assertEquals(".body { content: 'Custom CSS'; }", json.getString("css"));
// Get the logo
Response response = target().path("/theme/image/logo").request().get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
// Get the background
response = target().path("/theme/image/background").request().get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
// Change the logo
try (InputStream is = Resources.getResource("file/PIA00452.jpg").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("logo", is, "PIA00452.jpg");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
target()
.register(MultiPartFeature.class)
.path("/theme/images").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
}
}
// Change the background
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("background", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
target()
.register(MultiPartFeature.class)
.path("/theme/images").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
}
}
// Get the logo
response = target().path("/theme/image/logo").request().get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
// Get the background
response = target().path("/theme/image/background").request().get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
} }
} }