diff --git a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee index 5994521b8e..2160fbc419 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee @@ -65,6 +65,12 @@ module.exports = ProjectEntityHandler = self = files.push({path: path.join(folderPath, file.name), file:file}) callback null, docs, files + getAllDocPathsFromProjectById: (project_id, callback) -> + ProjectGetter.getProjectWithoutDocLines project_id, (err, project) -> + return callback(err) if err? + return callback(Errors.NotFoundError("no project")) if !project? + self.getAllDocPathsFromProject project, callback + getAllDocPathsFromProject: (project, callback) -> logger.log project:project, "getting all docs for project" self._getAllFoldersFromProject project, (err, folders = {}) -> diff --git a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee index 4530fb67d3..973d2b2ed6 100644 --- a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee +++ b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee @@ -31,3 +31,27 @@ module.exports = ProjectRootDocManager = ProjectEntityUpdateHandler.setRootDoc project_id, root_doc_id, callback else callback() + + setRootDocFromName: (project_id, rootDocName, callback = (error) ->) -> + ProjectEntityHandler.getAllDocPathsFromProjectById project_id, (error, docPaths) -> + return callback(error) if error? + # strip off leading and trailing quotes from rootDocName + rootDocName = rootDocName.replace(/^\'|\'$/g,"") + # prepend a slash for the root folder if not present + rootDocName = "/#{rootDocName}" if rootDocName[0] isnt '/' + # find the root doc from the filename + root_doc_id = null + for doc_id, path of docPaths + # docpaths have a leading / so allow matching "folder/filename" and "/folder/filename" + if path == rootDocName + root_doc_id = doc_id + # try a basename match if there was no match + if !root_doc_id + for doc_id, path of docPaths + if Path.basename(path) == Path.basename(rootDocName) + root_doc_id = doc_id + # set the root doc id if we found a match + if root_doc_id? + ProjectEntityUpdateHandler.setRootDoc project_id, root_doc_id, callback + else + callback() \ No newline at end of file diff --git a/services/web/app/coffee/Features/Templates/TemplatesController.coffee b/services/web/app/coffee/Features/Templates/TemplatesController.coffee index c4525f1012..724abd2766 100644 --- a/services/web/app/coffee/Features/Templates/TemplatesController.coffee +++ b/services/web/app/coffee/Features/Templates/TemplatesController.coffee @@ -2,6 +2,7 @@ path = require('path') Project = require('../../../js/models/Project').Project ProjectUploadManager = require('../../../js/Features/Uploads/ProjectUploadManager') ProjectOptionsHandler = require('../../../js/Features/Project/ProjectOptionsHandler') +ProjectRootDocManager = require('../../../js/Features/Project/ProjectRootDocManager') AuthenticationController = require('../../../js/Features/Authentication/AuthenticationController') settings = require('settings-sharelatex') fs = require('fs') @@ -24,6 +25,7 @@ module.exports = TemplatesController = data.templateId = templateId data.name = req.query.templateName data.compiler = req.query.latexEngine + data.mainFile = req.query.mainFile res.render path.resolve(__dirname, "../../../views/project/editor/new_from_template"), data createProjectFromV1Template: (req, res)-> @@ -43,6 +45,7 @@ module.exports = TemplatesController = currentUserId: currentUserId, compiler: req.body.compiler docId: req.body.docId + mainFile: req.body.mainFile templateId: req.body.templateId templateVersionId: req.body.templateVersionId image: 'wl_texlive:2018.1' @@ -65,15 +68,16 @@ module.exports = TemplatesController = return res.sendStatus 500 setCompiler project._id, options.compiler, -> setImage project._id, options.image, -> - fs.unlink dumpPath, -> - delete req.session.templateData - conditions = {_id:project._id} - update = { - fromV1TemplateId:options.templateId, - fromV1TemplateVersionId:options.templateVersionId - } - Project.update conditions, update, {}, (err)-> - res.redirect "/project/#{project._id}" + setMainFile project._id, options.mainFile, -> + fs.unlink dumpPath, -> + delete req.session.templateData + conditions = {_id:project._id} + update = { + fromV1TemplateId:options.templateId, + fromV1TemplateVersionId:options.templateVersionId + } + Project.update conditions, update, {}, (err)-> + res.redirect "/project/#{project._id}" setCompiler = (project_id, compiler, callback)-> if compiler? @@ -86,3 +90,9 @@ setImage = (project_id, imageName, callback)-> ProjectOptionsHandler.setImageName project_id, imageName, callback else callback() + +setMainFile = (project_id, mainFile, callback) -> + if mainFile? + ProjectRootDocManager.setRootDocFromName project_id, mainFile, callback + else + callback() diff --git a/services/web/app/views/project/editor/new_from_template.pug b/services/web/app/views/project/editor/new_from_template.pug index 6dc27a4241..b81d23904c 100644 --- a/services/web/app/views/project/editor/new_from_template.pug +++ b/services/web/app/views/project/editor/new_from_template.pug @@ -24,3 +24,4 @@ block content input(type="hidden" name="templateVersionId" value=templateVersionId) input(type="hidden" name="templateName" value=name) input(type="hidden" name="compiler" value=compiler) + input(type="hidden" name="mainFile" value=mainFile) diff --git a/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee b/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee index 9a8cde3ff5..47c2973e9f 100644 --- a/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee @@ -75,3 +75,93 @@ describe 'ProjectRootDocManager', -> it "should not set the root doc to the doc containing a documentclass", -> @ProjectEntityUpdateHandler.setRootDoc.called.should.equal false + describe "setRootDocFromName", -> + describe "when there is a suitable root doc", -> + beforeEach (done)-> + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) + @ProjectRootDocManager.setRootDocFromName @project_id, '/main.tex', done + + it "should check the docs of the project", -> + @ProjectEntityHandler.getAllDocPathsFromProjectById.calledWith(@project_id) + .should.equal true + + it "should set the root doc to main.tex", -> + @ProjectEntityUpdateHandler.setRootDoc.calledWith(@project_id, "doc-id-2") + .should.equal true + + describe "when there is a suitable root doc but the leading slash is missing", -> + beforeEach (done)-> + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) + @ProjectRootDocManager.setRootDocFromName @project_id, 'main.tex', done + + it "should check the docs of the project", -> + @ProjectEntityHandler.getAllDocPathsFromProjectById.calledWith(@project_id) + .should.equal true + + it "should set the root doc to main.tex", -> + @ProjectEntityUpdateHandler.setRootDoc.calledWith(@project_id, "doc-id-2") + .should.equal true + + describe "when there is a suitable root doc with a basename match", -> + beforeEach (done)-> + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) + @ProjectRootDocManager.setRootDocFromName @project_id, 'chapter1a.tex', done + + it "should check the docs of the project", -> + @ProjectEntityHandler.getAllDocPathsFromProjectById.calledWith(@project_id) + .should.equal true + + it "should set the root doc using the basename", -> + @ProjectEntityUpdateHandler.setRootDoc.calledWith(@project_id, "doc-id-3") + .should.equal true + + describe "when there is a suitable root doc but the filename is in quotes", -> + beforeEach (done)-> + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) + @ProjectRootDocManager.setRootDocFromName @project_id, "'main.tex'", done + + it "should check the docs of the project", -> + @ProjectEntityHandler.getAllDocPathsFromProjectById.calledWith(@project_id) + .should.equal true + + it "should set the root doc to main.tex", -> + @ProjectEntityUpdateHandler.setRootDoc.calledWith(@project_id, "doc-id-2") + .should.equal true + + describe "when there is no suitable root doc", -> + beforeEach (done)-> + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) + @ProjectRootDocManager.setRootDocFromName @project_id, "other.tex", done + + it "should not set the root doc", -> + @ProjectEntityUpdateHandler.setRootDoc.called.should.equal false diff --git a/services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee b/services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee index 8a90245373..776e177244 100644 --- a/services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee +++ b/services/web/test/unit/coffee/Templates/TemplatesControllerTests.coffee @@ -27,6 +27,9 @@ describe 'TemplatesController', -> setImageName:sinon.stub().callsArgWith(2) } @uuid = "1234" + @ProjectRootDocManager = { + setRootDocFromName: sinon.stub().callsArgWith(2) + } @ProjectDetailsHandler = getProjectDescription:sinon.stub() @Project = @@ -34,6 +37,7 @@ describe 'TemplatesController', -> @controller = SandboxedModule.require modulePath, requires: '../../../js/Features/Uploads/ProjectUploadManager':@ProjectUploadManager '../../../js/Features/Project/ProjectOptionsHandler':@ProjectOptionsHandler + '../../../js/Features/Project/ProjectRootDocManager':@ProjectRootDocManager '../../../js/Features/Authentication/AuthenticationController': @AuthenticationController = {getLoggedInUserId: sinon.stub()} './TemplatesPublisher':@TemplatesPublisher "logger-sharelatex":