diff --git a/app/coffee/CompileController.coffee b/app/coffee/CompileController.coffee index dd73040..7ee9eef 100644 --- a/app/coffee/CompileController.coffee +++ b/app/coffee/CompileController.coffee @@ -11,6 +11,7 @@ module.exports = CompileController = RequestParser.parse req.body, (error, request) -> return next(error) if error? request.project_id = req.params.project_id + logger.log {request}, "got request" ProjectPersistenceManager.markProjectAsJustAccessed request.project_id, (error) -> return next(error) if error? CompileManager.doCompile request, (error, outputFiles = []) -> diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 20e9bc9..82ac793 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -8,6 +8,7 @@ logger = require "logger-sharelatex" Metrics = require "./Metrics" child_process = require "child_process" CommandRunner = require(Settings.clsi?.commandRunner or "./CommandRunner") +DraftModeManager = require "./DraftModeManager" fs = require("fs") module.exports = CompileManager = @@ -20,24 +21,32 @@ module.exports = CompileManager = return callback(error) if error? logger.log project_id: request.project_id, time_taken: Date.now() - timer.start, "written files to disk" timer.done() - - timer = new Metrics.Timer("run-compile") - Metrics.inc("compiles") - LatexRunner.runLatex request.project_id, { - directory: compileDir - mainFile: request.rootResourcePath - compiler: request.compiler - timeout: request.timeout - image: request.imageName - }, (error) -> + + injectDraftModeIfRequired = (callback) -> + if request.draft + DraftModeManager.injectDraftMode Path.join(compileDir, request.rootResourcePath), callback + else + callback() + + injectDraftModeIfRequired (error) -> return callback(error) if error? - logger.log project_id: request.project_id, time_taken: Date.now() - timer.start, "done compile" - timer.done() - - OutputFileFinder.findOutputFiles request.resources, compileDir, (error, outputFiles) -> + timer = new Metrics.Timer("run-compile") + Metrics.inc("compiles") + LatexRunner.runLatex request.project_id, { + directory: compileDir + mainFile: request.rootResourcePath + compiler: request.compiler + timeout: request.timeout + image: request.imageName + }, (error) -> return callback(error) if error? - OutputCacheManager.saveOutputFiles outputFiles, compileDir, (error, newOutputFiles) -> - callback null, newOutputFiles + logger.log project_id: request.project_id, time_taken: Date.now() - timer.start, "done compile" + timer.done() + + OutputFileFinder.findOutputFiles request.resources, compileDir, (error, outputFiles) -> + return callback(error) if error? + OutputCacheManager.saveOutputFiles outputFiles, compileDir, (error, newOutputFiles) -> + callback null, newOutputFiles clearProject: (project_id, _callback = (error) ->) -> callback = (error) -> diff --git a/app/coffee/DraftModeManager.coffee b/app/coffee/DraftModeManager.coffee new file mode 100644 index 0000000..a0d859d --- /dev/null +++ b/app/coffee/DraftModeManager.coffee @@ -0,0 +1,21 @@ +fs = require "fs" +logger = require "logger-sharelatex" + +module.exports = DraftModeManager = + injectDraftMode: (filename, callback = (error) ->) -> + fs.readFile filename, "utf8", (error, content) -> + return callback(error) if error? + modified_content = DraftModeManager._injectDraftOption content + logger.log { + content: content.slice(0,1024), # \documentclass is normally v near the top + modified_content: modified_content.slice(0,1024), + filename + }, "injected draft class" + fs.writeFile filename, modified_content, callback + + _injectDraftOption: (content) -> + content + # With existing options (must be first, otherwise both are applied) + .replace(/\\documentclass\[/, "\\documentclass[draft,") + # Without existing options + .replace(/\\documentclass\{/, "\\documentclass[draft]{") \ No newline at end of file diff --git a/app/coffee/RequestParser.coffee b/app/coffee/RequestParser.coffee index 1b4e06d..bd081fd 100644 --- a/app/coffee/RequestParser.coffee +++ b/app/coffee/RequestParser.coffee @@ -24,6 +24,10 @@ module.exports = RequestParser = response.imageName = @_parseAttribute "imageName", compile.options.imageName, type: "string" + response.draft = @_parseAttribute "draft", + compile.options.draft, + default: false, + type: "boolean" if response.timeout > RequestParser.MAX_TIMEOUT response.timeout = RequestParser.MAX_TIMEOUT diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 54576d8..018e01d 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -17,6 +17,7 @@ describe "CompileManager", -> "logger-sharelatex": @logger = { log: sinon.stub() } "child_process": @child_process = {} "./CommandRunner": @CommandRunner = {} + "./DraftModeManager": @DraftModeManager = {} "fs": @fs = {} @callback = sinon.stub() @@ -51,31 +52,48 @@ describe "CompileManager", -> @LatexRunner.runLatex = sinon.stub().callsArg(2) @OutputFileFinder.findOutputFiles = sinon.stub().callsArgWith(2, null, @output_files) @OutputCacheManager.saveOutputFiles = sinon.stub().callsArgWith(2, null, @build_files) - @CompileManager.doCompile @request, @callback + @DraftModeManager.injectDraftMode = sinon.stub().callsArg(1) + + describe "normally", -> + beforeEach -> + @CompileManager.doCompile @request, @callback - it "should write the resources to disk", -> - @ResourceWriter.syncResourcesToDisk - .calledWith(@project_id, @resources, @compileDir) - .should.equal true + it "should write the resources to disk", -> + @ResourceWriter.syncResourcesToDisk + .calledWith(@project_id, @resources, @compileDir) + .should.equal true - it "should run LaTeX", -> - @LatexRunner.runLatex - .calledWith(@project_id, { - directory: @compileDir - mainFile: @rootResourcePath - compiler: @compiler - timeout: @timeout - image: @image - }) - .should.equal true + it "should run LaTeX", -> + @LatexRunner.runLatex + .calledWith(@project_id, { + directory: @compileDir + mainFile: @rootResourcePath + compiler: @compiler + timeout: @timeout + image: @image + }) + .should.equal true - it "should find the output files", -> - @OutputFileFinder.findOutputFiles - .calledWith(@resources, @compileDir) - .should.equal true + it "should find the output files", -> + @OutputFileFinder.findOutputFiles + .calledWith(@resources, @compileDir) + .should.equal true - it "should return the output files", -> - @callback.calledWith(null, @build_files).should.equal true + it "should return the output files", -> + @callback.calledWith(null, @build_files).should.equal true + + it "should not inject draft mode by default", -> + @DraftModeManager.injectDraftMode.called.should.equal false + + describe "with draft mode", -> + beforeEach -> + @request.draft = true + @CompileManager.doCompile @request, @callback + + it "should inject the draft mode header", -> + @DraftModeManager.injectDraftMode + .calledWith(@compileDir + "/" + @rootResourcePath) + .should.equal true describe "clearProject", -> describe "succesfully", -> diff --git a/test/unit/coffee/DraftModeManagerTests.coffee b/test/unit/coffee/DraftModeManagerTests.coffee new file mode 100644 index 0000000..549be29 --- /dev/null +++ b/test/unit/coffee/DraftModeManagerTests.coffee @@ -0,0 +1,61 @@ +SandboxedModule = require('sandboxed-module') +sinon = require('sinon') +require('chai').should() +modulePath = require('path').join __dirname, '../../../app/js/DraftModeManager' + +describe 'DraftModeManager', -> + beforeEach -> + @DraftModeManager = SandboxedModule.require modulePath, requires: + "fs": @fs = {} + "logger-sharelatex": @logger = {log: () ->} + + describe "_injectDraftOption", -> + it "should add draft option into documentclass with existing options", -> + @DraftModeManager + ._injectDraftOption(''' + \\documentclass[a4paper,foo=bar]{article} + ''') + .should.equal(''' + \\documentclass[draft,a4paper,foo=bar]{article} + ''') + + it "should add draft option into documentclass with no options", -> + @DraftModeManager + ._injectDraftOption(''' + \\documentclass{article} + ''') + .should.equal(''' + \\documentclass[draft]{article} + ''') + + describe "injectDraftMode", -> + beforeEach -> + @filename = "/mock/filename.tex" + @callback = sinon.stub() + content = ''' + \\documentclass{article} + \\begin{document} + Hello world + \\end{document} + ''' + @fs.readFile = sinon.stub().callsArgWith(2, null, content) + @fs.writeFile = sinon.stub().callsArg(2) + @DraftModeManager.injectDraftMode @filename, @callback + + it "should read the file", -> + @fs.readFile + .calledWith(@filename, "utf8") + .should.equal true + + it "should write the modified file", -> + @fs.writeFile + .calledWith(@filename, """ + \\documentclass[draft]{article} + \\begin{document} + Hello world + \\end{document} + """) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true