diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/context/AppContext.java b/docs-core/src/main/java/com/sismics/docs/core/model/context/AppContext.java index 2d2ab739..526371e7 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/context/AppContext.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/context/AppContext.java @@ -72,7 +72,7 @@ public class AppContext { eventBus = new EventBus(); eventBus.register(new DeadEventListener()); - asyncExecutorList = new ArrayList(); + asyncExecutorList = new ArrayList<>(); asyncEventBus = newAsyncEventBus(); asyncEventBus.register(new FileCreatedAsyncListener()); diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java index 7f947157..6f0973ba 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java +++ b/docs-core/src/main/java/com/sismics/docs/core/util/DirectoryUtil.java @@ -86,6 +86,15 @@ public class DirectoryUtil { 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 * diff --git a/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java b/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java index fcb3d2ec..2eb5f079 100644 --- a/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java +++ b/docs-core/src/main/java/com/sismics/util/mime/MimeTypeUtil.java @@ -1,5 +1,6 @@ package com.sismics.util.mime; +import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -21,16 +22,16 @@ public class MimeTypeUtil { * * @param is Stream to inspect * @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]; is.mark(headerBytes.length); int readCount = is.read(headerBytes); is.reset(); if (readCount <= 0) { - throw new Exception("Cannot read input file"); + throw new IOException("Cannot read input file"); } return guessMimeType(headerBytes); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java index b692ed4e..35520179 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java @@ -107,7 +107,7 @@ public class FileResource extends BaseResource { String mimeType; try { mimeType = MimeTypeUtil.guessMimeType(fileInputStream); - } catch (Exception e) { + } catch (IOException e) { throw new ServerException("ErrorGuessMime", "Error guessing mime type", e); } if (mimeType == null) { diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/ThemeResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/ThemeResource.java index 6e3f96a1..672b8e0c 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/ThemeResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/ThemeResource.java @@ -1,22 +1,31 @@ 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.io.ByteStreams; import com.sismics.docs.core.constant.ConfigType; import com.sismics.docs.core.dao.jpa.ConfigDao; 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.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; +import com.sismics.rest.exception.ServerException; import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.ValidationUtil; import com.sismics.util.css.Selector; import org.glassfish.jersey.media.multipart.FormDataBodyPart; 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.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.Map; /** @@ -26,6 +35,10 @@ import java.util.Map; */ @Path("/theme") 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. * @@ -116,10 +129,63 @@ public class ThemeResource extends BaseResource { throw new ForbiddenClientException(); } 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(); } + @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. * diff --git a/docs-web/src/main/webapp/src/img/background.jpg b/docs-web/src/main/resources/image/background.jpg similarity index 100% rename from docs-web/src/main/webapp/src/img/background.jpg rename to docs-web/src/main/resources/image/background.jpg diff --git a/docs-web/src/main/webapp/src/favicon.png b/docs-web/src/main/resources/image/logo.png similarity index 100% rename from docs-web/src/main/webapp/src/favicon.png rename to docs-web/src/main/resources/image/logo.png diff --git a/docs-web/src/main/webapp/Gruntfile.js b/docs-web/src/main/webapp/Gruntfile.js index cb4ad357..5dba6d2d 100644 --- a/docs-web/src/main/webapp/Gruntfile.js +++ b/docs-web/src/main/webapp/Gruntfile.js @@ -93,7 +93,7 @@ module.exports = function(grunt) { }, replace: { 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, replacements: [{ from: '../api', diff --git a/docs-web/src/main/webapp/src/index.html b/docs-web/src/main/webapp/src/index.html index d4cce917..c7760315 100644 --- a/docs-web/src/main/webapp/src/index.html +++ b/docs-web/src/main/webapp/src/index.html @@ -4,7 +4,7 @@ Sismics Docs - + @@ -100,7 +100,7 @@ {{ appName }} diff --git a/docs-web/src/main/webapp/src/share.html b/docs-web/src/main/webapp/src/share.html index a9a7ddf8..ab67af4a 100644 --- a/docs-web/src/main/webapp/src/share.html +++ b/docs-web/src/main/webapp/src/share.html @@ -46,7 +46,7 @@