mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 23:27:57 +01:00
Merge branch 'master' into sismics_prod
This commit is contained in:
commit
43084d9d86
13
.travis.yml
13
.travis.yml
@ -5,7 +5,20 @@ before_install:
|
|||||||
- sudo apt-get -qq update
|
- sudo apt-get -qq update
|
||||||
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
||||||
- sudo apt-get -y -q install haveged && sudo service haveged start
|
- sudo apt-get -y -q install haveged && sudo service haveged start
|
||||||
|
after_success:
|
||||||
|
- mvn -Pprod -DskipTests clean install
|
||||||
|
- docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||||
|
- export REPO=sismics/docs
|
||||||
|
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
|
||||||
|
- docker build -f Dockerfile -t $REPO:$COMMIT .
|
||||||
|
- docker tag $REPO:$COMMIT $REPO:$TAG
|
||||||
|
- docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
|
||||||
|
- docker push $REPO
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||||
- LC_NUMERIC=C
|
- LC_NUMERIC=C
|
||||||
|
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
|
||||||
|
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
|
||||||
|
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=
|
||||||
|
- COMMIT=${TRAVIS_COMMIT::8}
|
||||||
|
11
README.md
11
README.md
@ -41,7 +41,16 @@ Download
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
||||||
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/)
|
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/).
|
||||||
|
The default admin password is "admin". Don't forget to change it before going to production.
|
||||||
|
|
||||||
|
Install with Docker
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
From a Docker host, run this command to download and install Sismics Docs. The server will run on <http://[your-docker-host-ip]:8100>.
|
||||||
|
The default admin password is "admin". Don't forget to change it before going to production.
|
||||||
|
|
||||||
|
docker run --rm --name sismics_docs_latest -d -p 8100:8080 -v sismics_docs_latest:/data sismics/docs:latest
|
||||||
|
|
||||||
How to build Docs from the sources
|
How to build Docs from the sources
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import com.google.common.eventbus.AsyncEventBus;
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import com.lowagie.text.FontFactory;
|
||||||
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.listener.async.DocumentCreatedAsyncListener;
|
import com.sismics.docs.core.listener.async.DocumentCreatedAsyncListener;
|
||||||
@ -20,6 +21,7 @@ import com.sismics.docs.core.listener.async.RebuildIndexAsyncListener;
|
|||||||
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
||||||
import com.sismics.docs.core.model.jpa.Config;
|
import com.sismics.docs.core.model.jpa.Config;
|
||||||
import com.sismics.docs.core.service.IndexingService;
|
import com.sismics.docs.core.service.IndexingService;
|
||||||
|
import com.sismics.docs.core.util.PdfUtil;
|
||||||
import com.sismics.util.EnvironmentUtil;
|
import com.sismics.util.EnvironmentUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,10 +61,13 @@ public class AppContext {
|
|||||||
private AppContext() {
|
private AppContext() {
|
||||||
resetEventBus();
|
resetEventBus();
|
||||||
|
|
||||||
|
// Start indexing service
|
||||||
ConfigDao configDao = new ConfigDao();
|
ConfigDao configDao = new ConfigDao();
|
||||||
Config luceneStorageConfig = configDao.getById(ConfigType.LUCENE_DIRECTORY_STORAGE);
|
Config luceneStorageConfig = configDao.getById(ConfigType.LUCENE_DIRECTORY_STORAGE);
|
||||||
indexingService = new IndexingService(luceneStorageConfig != null ? luceneStorageConfig.getValue() : null);
|
indexingService = new IndexingService(luceneStorageConfig != null ? luceneStorageConfig.getValue() : null);
|
||||||
indexingService.startAsync();
|
indexingService.startAsync();
|
||||||
|
|
||||||
|
PdfUtil.registerFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import java.io.IOException;
|
import com.sismics.tess4j.Tesseract;
|
||||||
import java.io.InputStream;
|
import com.sismics.util.ImageUtil;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.CipherInputStream;
|
|
||||||
import javax.crypto.CipherOutputStream;
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import org.imgscalr.Scalr;
|
import org.imgscalr.Scalr;
|
||||||
import org.imgscalr.Scalr.Method;
|
import org.imgscalr.Scalr.Method;
|
||||||
import org.imgscalr.Scalr.Mode;
|
import org.imgscalr.Scalr.Mode;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import javax.crypto.Cipher;
|
||||||
import com.sismics.tess4j.Tesseract;
|
import javax.crypto.CipherInputStream;
|
||||||
import com.sismics.util.ImageUtil;
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File entity utilities.
|
* File entity utilities.
|
||||||
@ -64,11 +62,12 @@ public class FileUtil {
|
|||||||
private static String ocrFile(InputStream inputStream, String language) {
|
private static String ocrFile(InputStream inputStream, String language) {
|
||||||
Tesseract instance = Tesseract.getInstance();
|
Tesseract instance = Tesseract.getInstance();
|
||||||
String content = null;
|
String content = null;
|
||||||
BufferedImage image = null;
|
BufferedImage image;
|
||||||
try {
|
try {
|
||||||
image = ImageIO.read(inputStream);
|
image = ImageIO.read(inputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error reading the image", e);
|
log.error("Error reading the image", e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upscale and grayscale the image
|
// Upscale and grayscale the image
|
||||||
@ -92,10 +91,9 @@ public class FileUtil {
|
|||||||
* Save a file on the storage filesystem.
|
* Save a file on the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param inputStream Unencrypted input stream
|
||||||
* @param pdf
|
* @param pdfInputStream PDF input stream
|
||||||
* @param file File to save
|
* @param file File to save
|
||||||
* @param privateKey Private key used for encryption
|
* @param privateKey Private key used for encryption
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static void save(InputStream inputStream, InputStream pdfInputStream, File file, String privateKey) throws Exception {
|
public static void save(InputStream inputStream, InputStream pdfInputStream, File file, String privateKey) throws Exception {
|
||||||
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
||||||
@ -114,9 +112,8 @@ public class FileUtil {
|
|||||||
* @param inputStream Unencrypted input stream
|
* @param inputStream Unencrypted input stream
|
||||||
* @param pdfInputStream Unencrypted PDF input stream
|
* @param pdfInputStream Unencrypted PDF input stream
|
||||||
* @param cipher Cipher to use for encryption
|
* @param cipher Cipher to use for encryption
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static void saveVariations(File file, InputStream inputStream, InputStream pdfInputStream, Cipher cipher) throws Exception {
|
private static void saveVariations(File file, InputStream inputStream, InputStream pdfInputStream, Cipher cipher) throws Exception {
|
||||||
BufferedImage image = null;
|
BufferedImage image = null;
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
image = ImageIO.read(inputStream);
|
image = ImageIO.read(inputStream);
|
||||||
@ -151,7 +148,6 @@ public class FileUtil {
|
|||||||
* Remove a file from the storage filesystem.
|
* Remove a file from the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param file File to delete
|
* @param file File to delete
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static void delete(File file) throws IOException {
|
public static void delete(File file) throws IOException {
|
||||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import com.google.common.base.Charsets;
|
||||||
import java.io.ByteArrayInputStream;
|
import com.google.common.base.Strings;
|
||||||
import java.io.ByteArrayOutputStream;
|
import com.google.common.io.ByteStreams;
|
||||||
import java.io.IOException;
|
import com.google.common.io.CharStreams;
|
||||||
import java.io.InputStream;
|
import com.google.common.io.Closer;
|
||||||
import java.nio.file.Files;
|
import com.google.common.io.Resources;
|
||||||
import java.nio.file.Path;
|
import com.lowagie.text.*;
|
||||||
import java.text.SimpleDateFormat;
|
import com.lowagie.text.pdf.PdfWriter;
|
||||||
import java.util.Date;
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
import java.util.List;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||||
import javax.imageio.ImageIO;
|
import com.sismics.util.ImageUtil;
|
||||||
|
import com.sismics.util.mime.MimeType;
|
||||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -32,13 +32,15 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import javax.imageio.ImageIO;
|
||||||
import com.google.common.io.Closer;
|
import java.awt.image.BufferedImage;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
import java.io.*;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import java.net.URL;
|
||||||
import com.sismics.docs.core.util.pdf.PdfPage;
|
import java.nio.file.Files;
|
||||||
import com.sismics.util.ImageUtil;
|
import java.nio.file.Path;
|
||||||
import com.sismics.util.mime.MimeType;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PDF utilities.
|
* PDF utilities.
|
||||||
@ -86,7 +88,6 @@ public class PdfUtil {
|
|||||||
* @param inputStream InputStream
|
* @param inputStream InputStream
|
||||||
* @param reset Reset the stream after usage
|
* @param reset Reset the stream after usage
|
||||||
* @return PDF input stream
|
* @return PDF input stream
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static InputStream convertToPdf(File file, InputStream inputStream, boolean reset) throws Exception {
|
public static InputStream convertToPdf(File file, InputStream inputStream, boolean reset) throws Exception {
|
||||||
if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
||||||
@ -102,17 +103,47 @@ public class PdfUtil {
|
|||||||
return convertOpenDocumentText(inputStream, reset);
|
return convertOpenDocumentText(inputStream, reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.getMimeType().equals(MimeType.TEXT_PLAIN) || file.getMimeType().equals(MimeType.TEXT_CSV)) {
|
||||||
|
return convertTextPlain(inputStream, reset);
|
||||||
|
}
|
||||||
|
|
||||||
// PDF conversion not necessary/possible
|
// PDF conversion not necessary/possible
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a text plain document to PDF.
|
||||||
|
*
|
||||||
|
* @param inputStream Unecnrypted input stream
|
||||||
|
* @param reset Reset the stream after usage
|
||||||
|
* @return PDF input stream
|
||||||
|
*/
|
||||||
|
private static InputStream convertTextPlain(InputStream inputStream, boolean reset) throws Exception {
|
||||||
|
Document output = new Document(PageSize.A4, 40, 40, 40, 40);
|
||||||
|
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
||||||
|
PdfWriter.getInstance(output, pdfOutputStream);
|
||||||
|
|
||||||
|
output.open();
|
||||||
|
String content = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
|
||||||
|
Font font = FontFactory.getFont("LiberationMono-Regular");
|
||||||
|
Paragraph paragraph = new Paragraph(content, font);
|
||||||
|
paragraph.setAlignment(Element.ALIGN_LEFT);
|
||||||
|
output.add(paragraph);
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
if (reset) {
|
||||||
|
inputStream.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ByteArrayInputStream(pdfOutputStream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an open document text file to PDF.
|
* Convert an open document text file to PDF.
|
||||||
*
|
*
|
||||||
* @param inputStream Unencrypted input stream
|
* @param inputStream Unencrypted input stream
|
||||||
* @param reset Reset the stream after usage
|
* @param reset Reset the stream after usage
|
||||||
* @return PDF input stream
|
* @return PDF input stream
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
private static InputStream convertOpenDocumentText(InputStream inputStream, boolean reset) throws Exception {
|
private static InputStream convertOpenDocumentText(InputStream inputStream, boolean reset) throws Exception {
|
||||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
||||||
@ -131,7 +162,6 @@ public class PdfUtil {
|
|||||||
* @param inputStream Unencrypted input stream
|
* @param inputStream Unencrypted input stream
|
||||||
* @param reset Reset the stream after usage
|
* @param reset Reset the stream after usage
|
||||||
* @return PDF input stream
|
* @return PDF input stream
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
private static InputStream convertOfficeDocument(InputStream inputStream, boolean reset) throws Exception {
|
private static InputStream convertOfficeDocument(InputStream inputStream, boolean reset) throws Exception {
|
||||||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
|
||||||
@ -153,7 +183,6 @@ public class PdfUtil {
|
|||||||
* @param metadata Add a page with metadata
|
* @param metadata Add a page with metadata
|
||||||
* @param margin Margins in millimeters
|
* @param margin Margins in millimeters
|
||||||
* @return PDF input stream
|
* @return PDF input stream
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||||
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
||||||
@ -282,7 +311,6 @@ public class PdfUtil {
|
|||||||
*
|
*
|
||||||
* @param inputStream PDF document
|
* @param inputStream PDF document
|
||||||
* @return Render of the first page
|
* @return Render of the first page
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static BufferedImage renderFirstPage(InputStream inputStream) throws IOException {
|
public static BufferedImage renderFirstPage(InputStream inputStream) throws IOException {
|
||||||
try (PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
try (PDDocument pdfDocument = PDDocument.load(inputStream)) {
|
||||||
@ -290,4 +318,20 @@ public class PdfUtil {
|
|||||||
return renderer.renderImage(0);
|
return renderer.renderImage(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register fonts.
|
||||||
|
*/
|
||||||
|
public static void registerFonts() {
|
||||||
|
URL url = Resources.getResource("fonts/LiberationMono-Regular.ttf");
|
||||||
|
try (InputStream is = url.openStream()) {
|
||||||
|
Path file = Files.createTempFile("sismics_docs_font_mono", ".ttf");
|
||||||
|
try (OutputStream os = Files.newOutputStream(file)) {
|
||||||
|
ByteStreams.copy(is, os);
|
||||||
|
}
|
||||||
|
FontFactory.register(file.toAbsolutePath().toString(), "LiberationMono-Regular");
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error loading font", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,10 @@ public class MimeTypeUtil {
|
|||||||
return "odt";
|
return "odt";
|
||||||
case MimeType.OFFICE_DOCUMENT:
|
case MimeType.OFFICE_DOCUMENT:
|
||||||
return "docx";
|
return "docx";
|
||||||
|
case MimeType.TEXT_PLAIN:
|
||||||
|
return "txt";
|
||||||
|
case MimeType.TEXT_CSV:
|
||||||
|
return "csv";
|
||||||
default:
|
default:
|
||||||
return "bin";
|
return "bin";
|
||||||
}
|
}
|
||||||
|
BIN
docs-core/src/main/resources/fonts/LiberationMono-Regular.ttf
Normal file
BIN
docs-core/src/main/resources/fonts/LiberationMono-Regular.ttf
Normal file
Binary file not shown.
@ -13,6 +13,7 @@ import javax.servlet.ServletRequest;
|
|||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.lowagie.text.FontFactory;
|
||||||
import org.apache.log4j.Level;
|
import org.apache.log4j.Level;
|
||||||
import org.apache.log4j.PatternLayout;
|
import org.apache.log4j.PatternLayout;
|
||||||
import org.apache.log4j.RollingFileAppender;
|
import org.apache.log4j.RollingFileAppender;
|
||||||
@ -71,6 +72,9 @@ public class RequestContextFilter implements Filter {
|
|||||||
AppContext.getInstance();
|
AppContext.getInstance();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register fonts
|
||||||
|
FontFactory.registerDirectories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,7 +23,7 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
*/
|
*/
|
||||||
$scope.comment = '';
|
$scope.comment = '';
|
||||||
$scope.addComment = function() {
|
$scope.addComment = function() {
|
||||||
if ($scope.comment.length == 0) {
|
if ($scope.comment.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +40,20 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
* Delete a comment.
|
* Delete a comment.
|
||||||
*/
|
*/
|
||||||
$scope.deleteComment = function(comment) {
|
$scope.deleteComment = function(comment) {
|
||||||
|
var title = 'Delete comment';
|
||||||
|
var msg = 'Do you really want to delete this comment?';
|
||||||
|
var btns = [
|
||||||
|
{result: 'cancel', label: 'Cancel'},
|
||||||
|
{result: 'ok', label: 'OK', cssClass: 'btn-primary'}
|
||||||
|
];
|
||||||
|
|
||||||
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
|
if (result === 'ok') {
|
||||||
Restangular.one('comment', comment.id).remove().then(function() {
|
Restangular.one('comment', comment.id).remove().then(function() {
|
||||||
$scope.comments.splice($scope.comments.indexOf(comment), 1);
|
$scope.comments.splice($scope.comments.indexOf(comment), 1);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,7 +68,7 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
];
|
];
|
||||||
|
|
||||||
$dialog.messageBox(title, msg, btns, function (result) {
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
if (result == 'ok') {
|
if (result === 'ok') {
|
||||||
Restangular.one('document', document.id).remove().then(function() {
|
Restangular.one('document', document.id).remove().then(function() {
|
||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
$state.go('document.default');
|
$state.go('document.default');
|
||||||
@ -74,7 +85,7 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
templateUrl: 'partial/docs/document.share.html',
|
templateUrl: 'partial/docs/document.share.html',
|
||||||
controller: 'DocumentModalShare'
|
controller: 'DocumentModalShare'
|
||||||
}).result.then(function (name) {
|
}).result.then(function (name) {
|
||||||
if (name == null) {
|
if (name === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,18 +111,19 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
var title = 'Shared document';
|
var title = 'Shared document';
|
||||||
var msg = 'You can share this document by giving this link. ' +
|
var msg = 'You can share this document by giving this link. ' +
|
||||||
'Note that everyone having this link can see the document.<br/>' +
|
'Note that everyone having this link can see the document.<br/>' +
|
||||||
'<input class="form-control share-link" type="text" readonly="readonly" value="' + link + '" />';
|
'<input class="form-control share-link" type="text" readonly="readonly" value="' + link + '"' +
|
||||||
|
' onclick="this.select(); document.execCommand(\'copy\');" />';
|
||||||
var btns = [
|
var btns = [
|
||||||
{result: 'unshare', label: 'Unshare', cssClass: 'btn-danger'},
|
{result: 'unshare', label: 'Unshare', cssClass: 'btn-danger'},
|
||||||
{result: 'close', label: 'Close'}
|
{result: 'close', label: 'Close'}
|
||||||
];
|
];
|
||||||
|
|
||||||
$dialog.messageBox(title, msg, btns, function (result) {
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
if (result == 'unshare') {
|
if (result === 'unshare') {
|
||||||
// Unshare this document and update the local shares
|
// Unshare this document and update the local shares
|
||||||
Restangular.one('share', share.id).remove().then(function () {
|
Restangular.one('share', share.id).remove().then(function () {
|
||||||
$scope.document.acls = _.reject($scope.document.acls, function(s) {
|
$scope.document.acls = _.reject($scope.document.acls, function(s) {
|
||||||
return share.id == s.id;
|
return share.id === s.id;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
<a ng-click="openFile(file)">
|
<a ng-click="openFile(file)">
|
||||||
<img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }} | {{ file.size | filesize }}" tooltip-placement="top" />
|
<img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }} | {{ file.size | filesize }}" tooltip-placement="top" />
|
||||||
</a>
|
</a>
|
||||||
<div class="caption pointer" ng-click="file.checked = !file.checked">
|
<div class="caption">
|
||||||
<div class="pull-left">
|
<div class="pull-left">
|
||||||
<input type="checkbox" ng-model="file.checked" />
|
<input type="checkbox" ng-model="file.checked" style="width: 26px; height: 26px" />
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button class="btn btn-danger" ng-click="deleteFile($event, file)"><span class="glyphicon glyphicon-trash"></span></button>
|
<button class="btn btn-danger" ng-click="deleteFile($event, file)"><span class="glyphicon glyphicon-trash"></span></button>
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary" ng-disabled="!documentForm.$valid || fileIsUploading" ng-click="edit()">{{ isEdit() ? 'Edit' : 'Add' }}</button>
|
<button type="submit" class="btn btn-primary" ng-disabled="!documentForm.$valid || fileIsUploading" ng-click="edit()">Save</button>
|
||||||
<button type="submit" class="btn btn-default" ng-click="cancel()" ng-disabled="fileIsUploading">Cancel</button>
|
<button type="submit" class="btn btn-default" ng-click="cancel()" ng-disabled="fileIsUploading">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,9 +40,10 @@
|
|||||||
|
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>
|
||||||
{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}
|
{{ document.title }}
|
||||||
|
<img ng-if="document" ng-src="img/flag/{{ document.language }}.png" title="Document language: {{ document.language }}" />
|
||||||
|
<small>{{ document.create_date | date: 'yyyy-MM-dd' }}
|
||||||
by <a href="#/user/{{ document.creator }}">{{ document.creator }}</a></small>
|
by <a href="#/user/{{ document.creator }}">{{ document.creator }}</a></small>
|
||||||
<img ng-if="document" ng-src="img/flag/{{ document.language }}.png" title="{{ document.language }}" />
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p ng-show="document.writable">
|
<p ng-show="document.writable">
|
||||||
@ -107,7 +108,7 @@
|
|||||||
<p>
|
<p>
|
||||||
{{ comment.content }}<br />
|
{{ comment.content }}<br />
|
||||||
<span class="text-muted">{{ comment.create_date | date: 'yyyy-MM-dd' }}</span>
|
<span class="text-muted">{{ comment.create_date | date: 'yyyy-MM-dd' }}</span>
|
||||||
<span class="text-muted pull-right btn-link"
|
<span class="text-muted pull-right btn-link pointer"
|
||||||
ng-show="document.writable || userInfo.username == comment.creator"
|
ng-show="document.writable || userInfo.username == comment.creator"
|
||||||
ng-click="deleteComment(comment)">Delete</span>
|
ng-click="deleteComment(comment)">Delete</span>
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import com.google.common.io.ByteStreams;
|
||||||
import java.util.Date;
|
import com.google.common.io.Resources;
|
||||||
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
|
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||||
|
import com.sismics.util.mime.MimeType;
|
||||||
|
import com.sismics.util.mime.MimeTypeUtil;
|
||||||
|
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
|
||||||
|
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||||
|
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
@ -10,20 +20,8 @@ import javax.ws.rs.core.Form;
|
|||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import java.io.InputStream;
|
||||||
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
|
import java.util.Date;
|
||||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
|
||||||
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
|
|
||||||
import org.joda.time.format.DateTimeFormat;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.common.io.ByteStreams;
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
|
||||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
|
||||||
import com.sismics.util.mime.MimeType;
|
|
||||||
import com.sismics.util.mime.MimeTypeUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exhaustive test of the document resource.
|
* Exhaustive test of the document resource.
|
||||||
@ -545,4 +543,63 @@ public class TestDocumentResource extends BaseJerseyTest {
|
|||||||
Assert.assertTrue(fileBytes.length > 0); // Images rendered from PDF differ in size from OS to OS due to font issues
|
Assert.assertTrue(fileBytes.length > 0); // Images rendered from PDF differ in size from OS to OS due to font issues
|
||||||
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes, null));
|
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test plain text extraction.
|
||||||
|
*
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPlainTextExtraction() throws Exception {
|
||||||
|
// Login document_plain
|
||||||
|
clientUtil.createUser("document_plain");
|
||||||
|
String documentPlainToken = clientUtil.login("document_plain");
|
||||||
|
|
||||||
|
// Create a document
|
||||||
|
long create1Date = new Date().getTime();
|
||||||
|
JsonObject json = target().path("/document").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, documentPlainToken)
|
||||||
|
.put(Entity.form(new Form()
|
||||||
|
.param("title", "My super title document 1")
|
||||||
|
.param("description", "My super description for document 1")
|
||||||
|
.param("language", "eng")
|
||||||
|
.param("create_date", Long.toString(create1Date))), JsonObject.class);
|
||||||
|
String document1Id = json.getString("id");
|
||||||
|
Assert.assertNotNull(document1Id);
|
||||||
|
|
||||||
|
// Add a plain text file
|
||||||
|
String file1Id;
|
||||||
|
try (InputStream is = Resources.getResource("file/document.txt").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "document.txt");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
json = target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, documentPlainToken)
|
||||||
|
.put(Entity.entity(multiPart.field("id", document1Id).bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
|
file1Id = json.getString("id");
|
||||||
|
Assert.assertNotNull(file1Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search documents by query in full content
|
||||||
|
json = target().path("/document/list")
|
||||||
|
.queryParam("search", "full:love")
|
||||||
|
.request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, documentPlainToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertTrue(json.getJsonArray("documents").size() == 1);
|
||||||
|
|
||||||
|
// Get the file thumbnail data
|
||||||
|
Response response = target().path("/file/" + file1Id + "/data")
|
||||||
|
.queryParam("size", "thumb")
|
||||||
|
.request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, documentPlainToken)
|
||||||
|
.get();
|
||||||
|
InputStream is = (InputStream) response.getEntity();
|
||||||
|
byte[] fileBytes = ByteStreams.toByteArray(is);
|
||||||
|
Assert.assertTrue(fileBytes.length > 0); // Images rendered from PDF differ in size from OS to OS due to font issues
|
||||||
|
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes, null));
|
||||||
|
}
|
||||||
}
|
}
|
4
docs-web/src/test/resources/file/document.txt
Normal file
4
docs-web/src/test/resources/file/document.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is a test document
|
||||||
|
Please love me
|
||||||
|
&é"'(-è_çà)=$^ù*
|
||||||
|
조선글
|
Loading…
Reference in New Issue
Block a user