mirror of
https://github.com/sismics/docs.git
synced 2024-11-16 11:17:57 +01:00
#55: Export document in PDF (REST resource + export options UI)
This commit is contained in:
parent
25a17ae2da
commit
2c791f5123
@ -1,5 +1,8 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -19,8 +22,10 @@ import javax.ws.rs.PUT;
|
|||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
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 javax.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
@ -31,12 +36,14 @@ import org.joda.time.format.DateTimeParser;
|
|||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
import com.sismics.docs.core.dao.jpa.TagDao;
|
import com.sismics.docs.core.dao.jpa.TagDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
@ -50,6 +57,8 @@ import com.sismics.docs.core.model.jpa.Acl;
|
|||||||
import com.sismics.docs.core.model.jpa.Document;
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.model.jpa.Tag;
|
import com.sismics.docs.core.model.jpa.Tag;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
|
import com.sismics.docs.core.util.PdfUtil;
|
||||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
@ -58,6 +67,7 @@ import com.sismics.rest.exception.ForbiddenClientException;
|
|||||||
import com.sismics.rest.exception.ServerException;
|
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.mime.MimeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document REST resources.
|
* Document REST resources.
|
||||||
@ -145,6 +155,64 @@ public class DocumentResource extends BaseResource {
|
|||||||
return Response.ok().entity(document.build()).build();
|
return Response.ok().entity(document.build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a document to PDF.
|
||||||
|
*
|
||||||
|
* @param documentId Document ID
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("{id: [a-z0-9\\-]+}/pdf")
|
||||||
|
public Response getPdf(
|
||||||
|
@PathParam("id") String documentId,
|
||||||
|
@QueryParam("share") String shareId,
|
||||||
|
@QueryParam("metadata") Boolean metadata,
|
||||||
|
@QueryParam("comments") Boolean comments,
|
||||||
|
final @QueryParam("fitimagetopage") Boolean fitImageToPage,
|
||||||
|
@QueryParam("margin") String marginStr) {
|
||||||
|
authenticate();
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
final int margin = ValidationUtil.validateInteger(marginStr, "margin");
|
||||||
|
|
||||||
|
// Get document and check read permission
|
||||||
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
Document document = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
|
||||||
|
if (document == null) {
|
||||||
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get files
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
final List<File> fileList = fileDao.getByDocumentId(null, documentId);
|
||||||
|
for (File file : fileList) {
|
||||||
|
// A file is always encrypted by the creator of it
|
||||||
|
// Store its private key to decrypt it
|
||||||
|
User user = userDao.getById(file.getUserId());
|
||||||
|
file.setPrivateKey(user.getPrivateKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to PDF
|
||||||
|
StreamingOutput stream = new StreamingOutput() {
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
|
||||||
|
try (InputStream inputStream = PdfUtil.convertToPdf(fileList, fitImageToPage, margin)) {
|
||||||
|
ByteStreams.copy(inputStream, outputStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Response.ok(stream)
|
||||||
|
.header("Content-Type", MimeType.APPLICATION_PDF)
|
||||||
|
.header("Content-Disposition", "inline; filename=\"" + document.getTitle() + ".pdf\"")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all documents.
|
* Returns all documents.
|
||||||
*
|
*
|
||||||
|
@ -7,7 +7,8 @@ angular.module('docs').controller('DocumentModalPdf', function ($scope, $window,
|
|||||||
$scope.export = {
|
$scope.export = {
|
||||||
metadata: false,
|
metadata: false,
|
||||||
comments: false,
|
comments: false,
|
||||||
fitimagetopage: false
|
fitimagetopage: true,
|
||||||
|
margin: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export to PDF
|
// Export to PDF
|
||||||
@ -15,7 +16,8 @@ angular.module('docs').controller('DocumentModalPdf', function ($scope, $window,
|
|||||||
$window.open('../api/document/' + $stateParams.id
|
$window.open('../api/document/' + $stateParams.id
|
||||||
+ '/pdf?metadata=' + $scope.export.metadata
|
+ '/pdf?metadata=' + $scope.export.metadata
|
||||||
+ '&comments=' + $scope.export.comments
|
+ '&comments=' + $scope.export.comments
|
||||||
+ '&fitimagetopage=' + $scope.export.fitimagetopage);
|
+ '&fitimagetopage=' + $scope.export.fitimagetopage
|
||||||
|
+ '&margin=' + $scope.export.margin);
|
||||||
|
|
||||||
$modalInstance.close();
|
$modalInstance.close();
|
||||||
};
|
};
|
||||||
|
@ -2,22 +2,39 @@
|
|||||||
<h3>Export to PDF</h3>
|
<h3>Export to PDF</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ng-model="export.metadata" /> Export metadata
|
<input type="checkbox" ng-model="export.metadata" /> Export metadata
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ng-model="export.comments" /> Export comments
|
<input type="checkbox" ng-model="export.comments" /> Export comments
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ng-model="export.fitimagetopage" /> Fit image to page
|
<input type="checkbox" ng-model="export.fitimagetopage" /> Fit image to page
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="inputMargin" class="col-sm-2 control-label">Margin</label>
|
||||||
|
<div class="input-group col-sm-5">
|
||||||
|
<input type="number" class="form-control" id="inputMargin" ng-model="export.margin" min="0" max="100" step="1">
|
||||||
|
<div class="input-group-addon">mm</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -196,6 +196,14 @@ public class TestDocumentResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(1, tags.size());
|
Assert.assertEquals(1, tags.size());
|
||||||
Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id"));
|
Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id"));
|
||||||
|
|
||||||
|
// Export a document in PDF format
|
||||||
|
Response response = target().path("/document/" + document1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
|
||||||
|
.get();
|
||||||
|
InputStream is = (InputStream) response.getEntity();
|
||||||
|
byte[] pdfBytes = ByteStreams.toByteArray(is);
|
||||||
|
Assert.assertTrue(pdfBytes.length > 0);
|
||||||
|
|
||||||
// Create a tag
|
// Create a tag
|
||||||
json = target().path("/tag").request()
|
json = target().path("/tag").request()
|
||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
|
||||||
@ -244,7 +252,7 @@ public class TestDocumentResource extends BaseJerseyTest {
|
|||||||
Assert.assertFalse(thumbnailFile.exists());
|
Assert.assertFalse(thumbnailFile.exists());
|
||||||
|
|
||||||
// Get a document (KO)
|
// Get a document (KO)
|
||||||
Response response = target().path("/document/" + document1Id).request()
|
response = target().path("/document/" + document1Id).request()
|
||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token)
|
||||||
.get();
|
.get();
|
||||||
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
|
||||||
|
Loading…
Reference in New Issue
Block a user