diff --git a/src/main/java/ru/ulstu/file/model/FileData.java b/src/main/java/ru/ulstu/file/model/FileData.java index e5b3277..08445a3 100644 --- a/src/main/java/ru/ulstu/file/model/FileData.java +++ b/src/main/java/ru/ulstu/file/model/FileData.java @@ -19,6 +19,9 @@ public class FileData extends BaseEntity { private byte[] data; + @Column(name = "is_latex_attach") + private boolean isLatexAttach; + public String getName() { return name; } @@ -50,4 +53,12 @@ public class FileData extends BaseEntity { public void setData(byte[] data) { this.data = data; } + + public boolean isLatexAttach() { + return isLatexAttach; + } + + public void setLatexAttach(boolean latexAttach) { + isLatexAttach = latexAttach; + } } diff --git a/src/main/java/ru/ulstu/file/model/FileDataDto.java b/src/main/java/ru/ulstu/file/model/FileDataDto.java index 919cd80..65ef275 100644 --- a/src/main/java/ru/ulstu/file/model/FileDataDto.java +++ b/src/main/java/ru/ulstu/file/model/FileDataDto.java @@ -9,6 +9,7 @@ public class FileDataDto { private String fileName; private String tmpFileName; private boolean deleted; + private boolean isLatexAttach; public FileDataDto() { } @@ -16,17 +17,20 @@ public class FileDataDto { @JsonCreator public FileDataDto(@JsonProperty("id") Integer id, @JsonProperty("name") String name, + @JsonProperty("isLatexAttach") boolean isLatexAttach, @JsonProperty("fileName") String fileName, @JsonProperty("tmpFileName") String tmpFileName) { this.id = id; this.name = name; this.fileName = fileName; this.tmpFileName = tmpFileName; + this.isLatexAttach = isLatexAttach; } public FileDataDto(FileData fileData) { this.id = fileData.getId(); this.name = fileData.getName(); + this.isLatexAttach = fileData.isLatexAttach(); } public FileDataDto(String fileName, String tmpFileName) { @@ -73,4 +77,19 @@ public class FileDataDto { this.deleted = deleted; } + public boolean isLatexAttach() { + return isLatexAttach; + } + + public boolean getIsLatexAttach() { + return isLatexAttach; + } + + public void setLatexAttach(boolean latexAttach) { + isLatexAttach = latexAttach; + } + + public void setIsLatexAttach(boolean latexAttach) { + isLatexAttach = latexAttach; + } } diff --git a/src/main/java/ru/ulstu/file/service/FileService.java b/src/main/java/ru/ulstu/file/service/FileService.java index ad9f946..3a0a87c 100644 --- a/src/main/java/ru/ulstu/file/service/FileService.java +++ b/src/main/java/ru/ulstu/file/service/FileService.java @@ -6,7 +6,10 @@ import org.springframework.web.multipart.MultipartFile; import ru.ulstu.file.model.FileData; import ru.ulstu.file.model.FileDataDto; import ru.ulstu.file.repostory.FileRepository; +import ru.ulstu.paper.model.PaperDto; +import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -15,6 +18,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; @@ -107,6 +111,7 @@ public class FileService { private FileData copyFromDto(FileData fileData, FileDataDto fileDataDto) { fileData.setName(fileDataDto.getName()); + fileData.setLatexAttach(fileDataDto.isLatexAttach()); return fileData; } @@ -118,4 +123,27 @@ public class FileService { public FileDataDto createFromMultipartFile(MultipartFile multipartFile) throws IOException { return new FileDataDto(multipartFile.getOriginalFilename(), uploadToTmpDir(multipartFile)); } + + public void createLatexAttachs(PaperDto paper) throws IOException { + for (FileDataDto fileDataDto : paper.getFiles() + .stream() + .filter(f -> f.isLatexAttach() && !f.isDeleted()) + .collect(Collectors.toList())) { + if (fileDataDto.getId() == null) { + File oldFile = getTmpFilePath(fileDataDto.getTmpFileName()).toFile(); + File renamed = getTmpFilePath(fileDataDto.getName()).toFile(); + oldFile.renameTo(renamed); + } else { + Files.write(getTmpFilePath(fileDataDto.getName()), fileRepository.findOne(fileDataDto.getId()).getData()); + } + } + } + + public File createLatexFile(PaperDto paper) throws IOException { + BufferedWriter writer = Files.newBufferedWriter(getTmpFilePath(paper.getTitle() + ".tex")); + writer.write(paper.getLatexText()); + writer.close(); + + return getTmpFilePath(paper.getTitle() + ".tex").toFile(); + } } diff --git a/src/main/java/ru/ulstu/paper/controller/PaperController.java b/src/main/java/ru/ulstu/paper/controller/PaperController.java index 27fec4e..ad30822 100644 --- a/src/main/java/ru/ulstu/paper/controller/PaperController.java +++ b/src/main/java/ru/ulstu/paper/controller/PaperController.java @@ -1,5 +1,8 @@ package ru.ulstu.paper.controller; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.Errors; @@ -13,17 +16,20 @@ import ru.ulstu.deadline.model.Deadline; import ru.ulstu.paper.model.Paper; import ru.ulstu.paper.model.PaperDto; import ru.ulstu.paper.model.PaperFilterDto; +import ru.ulstu.paper.service.LatexService; import ru.ulstu.paper.service.PaperService; import ru.ulstu.user.model.User; import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.io.IOException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.stream.Collectors; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.springframework.util.StringUtils.isEmpty; @@ -32,9 +38,11 @@ import static org.springframework.util.StringUtils.isEmpty; @ApiIgnore public class PaperController { private final PaperService paperService; + private final LatexService latexService; - public PaperController(PaperService paperService) { + public PaperController(PaperService paperService, LatexService latexService) { this.paperService = paperService; + this.latexService = latexService; } @GetMapping("/papers") @@ -111,6 +119,14 @@ public class PaperController { return years; } + @PostMapping("/generatePdf") + public ResponseEntity getPdfFile(PaperDto paper) throws IOException, InterruptedException { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Disposition", "attachment; filename='" + + URLEncoder.encode(paper.getTitle() + ".pdf", UTF_8.toString()) + "'"); + return new ResponseEntity<>(latexService.generatePdfFromLatexFile(paper), headers, HttpStatus.OK); + } + private void filterEmptyDeadlines(PaperDto paperDto) { paperDto.setDeadlines(paperDto.getDeadlines().stream() .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) diff --git a/src/main/java/ru/ulstu/paper/model/Paper.java b/src/main/java/ru/ulstu/paper/model/Paper.java index 1d7e468..c7f7fcf 100644 --- a/src/main/java/ru/ulstu/paper/model/Paper.java +++ b/src/main/java/ru/ulstu/paper/model/Paper.java @@ -91,6 +91,9 @@ public class Paper extends BaseEntity implements UserContainer { @ManyToMany(fetch = FetchType.EAGER) private Set authors = new HashSet<>(); + @Column(name = "latex_text") + private String latexText; + public PaperStatus getStatus() { return status; } @@ -179,6 +182,14 @@ public class Paper extends BaseEntity implements UserContainer { this.url = url; } + public String getLatexText() { + return latexText; + } + + public void setLatexText(String latexText) { + this.latexText = latexText; + } + @Override public Set getUsers() { return getAuthors(); diff --git a/src/main/java/ru/ulstu/paper/model/PaperDto.java b/src/main/java/ru/ulstu/paper/model/PaperDto.java index 182b12c..7ddc0e0 100644 --- a/src/main/java/ru/ulstu/paper/model/PaperDto.java +++ b/src/main/java/ru/ulstu/paper/model/PaperDto.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.NotEmpty; -import ru.ulstu.file.model.FileDataDto; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.file.model.FileDataDto; import ru.ulstu.user.model.UserDto; import javax.validation.constraints.Size; @@ -36,6 +36,7 @@ public class PaperDto { private Set authorIds; private Set authors; private Integer filterAuthorId; + private String latexText; public PaperDto() { deadlines.add(new Deadline()); @@ -49,6 +50,7 @@ public class PaperDto { @JsonProperty("updateDate") Date updateDate, @JsonProperty("deadlines") List deadlines, @JsonProperty("comment") String comment, + @JsonProperty("latex_text") String latexText, @JsonProperty("url") String url, @JsonProperty("locked") Boolean locked, @JsonProperty("files") List files, @@ -62,6 +64,7 @@ public class PaperDto { this.deadlines = deadlines; this.comment = comment; this.url = url; + this.latexText = latexText; this.locked = locked; this.files = files; this.authors = authors; @@ -76,6 +79,7 @@ public class PaperDto { this.deadlines = paper.getDeadlines(); this.comment = paper.getComment(); this.url = paper.getUrl(); + this.latexText = paper.getLatexText(); this.locked = paper.getLocked(); this.files = convert(paper.getFiles(), FileDataDto::new); this.authorIds = convert(paper.getAuthors(), user -> user.getId()); @@ -178,6 +182,14 @@ public class PaperDto { this.url = url; } + public String getLatexText() { + return latexText; + } + + public void setLatexText(String latexText) { + this.latexText = latexText; + } + public String getAuthorsString() { return StringUtils.abbreviate(authors .stream() diff --git a/src/main/java/ru/ulstu/paper/service/LatexService.java b/src/main/java/ru/ulstu/paper/service/LatexService.java new file mode 100644 index 0000000..6bf5226 --- /dev/null +++ b/src/main/java/ru/ulstu/paper/service/LatexService.java @@ -0,0 +1,72 @@ +package ru.ulstu.paper.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.file.service.FileService; +import ru.ulstu.paper.model.PaperDto; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; + +@Service +public class LatexService { + private String errorMessage; + private File pdfFile; + private FileService fileService; + private final String pdfLatexError = "Errors occurred while executing pdfLaTeX."; + private final String bibtexError = "Errors occurred while executing bibtex."; + + public LatexService(FileService fileService) { + this.fileService = fileService; + } + + public byte[] generatePdfFromLatexFile(PaperDto paper) throws IOException, InterruptedException { + fileService.createLatexAttachs(paper); + File tex = fileService.createLatexFile(paper); + + if (!generate(paper.getTitle(), tex.getParentFile())) { + throw new IOException(errorMessage); + } + + return Files.readAllBytes(pdfFile.toPath()); + } + + private int startProcess(String[] args, File dir, String message) throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder(args); + processBuilder.redirectErrorStream(true); + processBuilder.directory(dir); + + Process process = processBuilder.start(); + InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream()); + + try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + while ((bufferedReader.readLine()) != null) ; + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + errorMessage = message + " Exit value of the process: " + exitCode; + } + return exitCode; + } + + private boolean generate(String filename, File dir) throws IOException, InterruptedException { + startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError); + startProcess(new String[]{"bibtex", filename}, dir, bibtexError); + if (startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError) != 0) + return false; + return checkPdf(filename, dir); + } + + private boolean checkPdf(String filename, File dir) { + pdfFile = new File(dir.getAbsolutePath() + File.separator + filename + ".pdf"); + + if (pdfFile.isFile()) return true; + else { + errorMessage = "The pdf file could not be created."; + return false; + } + } +} diff --git a/src/main/java/ru/ulstu/paper/service/PaperService.java b/src/main/java/ru/ulstu/paper/service/PaperService.java index 6c27c6e..a10d941 100644 --- a/src/main/java/ru/ulstu/paper/service/PaperService.java +++ b/src/main/java/ru/ulstu/paper/service/PaperService.java @@ -95,6 +95,7 @@ public class PaperService { private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException { paper.setComment(paperDto.getComment()); paper.setUrl(paperDto.getUrl()); + paper.setLatexText(paperDto.getLatexText()); paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate()); paper.setLocked(paperDto.getLocked()); paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus()); diff --git a/src/main/resources/db/changelog-20190323_000001-schema.xml b/src/main/resources/db/changelog-20190323_000001-schema.xml new file mode 100644 index 0000000..1ab3efb --- /dev/null +++ b/src/main/resources/db/changelog-20190323_000001-schema.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index 22d3cbd..be48ef2 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -25,5 +25,6 @@ + \ No newline at end of file diff --git a/src/main/resources/public/css/paper.css b/src/main/resources/public/css/paper.css index 6e61d0c..824209f 100644 --- a/src/main/resources/public/css/paper.css +++ b/src/main/resources/public/css/paper.css @@ -1,5 +1,9 @@ -#files-list .row > div:nth-child(6) { +#files-list .row > div:nth-child(7) { display: flex; justify-content: center; flex-direction: column; +} + +.nav-tabs { + margin-bottom: 20px; } \ No newline at end of file diff --git a/src/main/resources/public/js/core.js b/src/main/resources/public/js/core.js index bf6fb77..58e68f4 100644 --- a/src/main/resources/public/js/core.js +++ b/src/main/resources/public/js/core.js @@ -3,6 +3,7 @@ var urlFileUpload = "/api/1.0/files/uploadTmpFile"; var urlFileDownload = "/api/1.0/files/download/"; +var urlPdfGenerating = "/papers/generatePdf"; var urlFileDownloadTmp = "/api/1.0/files/download-tmp/"; /* exported MessageTypesEnum */ diff --git a/src/main/resources/templates/papers/fragments/paperFilesListFragment.html b/src/main/resources/templates/papers/fragments/paperFilesListFragment.html new file mode 100644 index 0000000..fcc875c --- /dev/null +++ b/src/main/resources/templates/papers/fragments/paperFilesListFragment.html @@ -0,0 +1,45 @@ + + + + + + + +
+ + + + +
+ + + + + +
+ + + +
+
+ + +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/papers/paper.html b/src/main/resources/templates/papers/paper.html index ab1cd67..fe355f6 100644 --- a/src/main/resources/templates/papers/paper.html +++ b/src/main/resources/templates/papers/paper.html @@ -20,35 +20,47 @@
-
- -
- - -

Incorrect title

-

-
+ +