diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index 83eb72fcf7..8b7ef023bd 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -58,7 +58,7 @@ module.exports = ClsiManager = return outputFiles VALID_COMPILERS: ["pdflatex", "latex", "xelatex", "lualatex"] - _buildRequest: (project_id, settingsOverride={}, callback = (error, request) ->) -> + _buildRequest: (project_id, options={}, callback = (error, request) ->) -> Project.findById project_id, {compiler: 1, rootDoc_id: 1, imageName: 1}, (error, project) -> return callback(error) if error? return callback(new Errors.NotFoundError("project does not exist: #{project_id}")) if !project? @@ -82,7 +82,7 @@ module.exports = ClsiManager = content: doc.lines.join("\n") if project.rootDoc_id? and doc._id.toString() == project.rootDoc_id.toString() rootResourcePath = path - if settingsOverride.rootDoc_id? and doc._id.toString() == settingsOverride.rootDoc_id.toString() + if options.rootDoc_id? and doc._id.toString() == options.rootDoc_id.toString() rootResourcePathOverride = path rootResourcePath = rootResourcePathOverride if rootResourcePathOverride? @@ -101,8 +101,9 @@ module.exports = ClsiManager = compile: options: compiler: project.compiler - timeout: settingsOverride.timeout + timeout: options.timeout imageName: project.imageName + draft: !!options.draft rootResourcePath: rootResourcePath resources: resources } diff --git a/services/web/app/coffee/Features/Compile/CompileController.coffee b/services/web/app/coffee/Features/Compile/CompileController.coffee index 587f00647c..89fbc87e2e 100755 --- a/services/web/app/coffee/Features/Compile/CompileController.coffee +++ b/services/web/app/coffee/Features/Compile/CompileController.coffee @@ -25,6 +25,8 @@ module.exports = CompileController = options.rootDoc_id = req.body.settingsOverride.rootDoc_id if req.body?.compiler options.compiler = req.body.compiler + if req.body?.draft + options.draft = req.body.draft logger.log {options, project_id}, "got compile request" CompileManager.compile project_id, user_id, options, (error, status, outputFiles, output, limits) -> return next(error) if error? diff --git a/services/web/app/coffee/Features/Security/RateLimiterMiddlewear.coffee b/services/web/app/coffee/Features/Security/RateLimiterMiddlewear.coffee index 19b8c15648..dc71da09fc 100644 --- a/services/web/app/coffee/Features/Security/RateLimiterMiddlewear.coffee +++ b/services/web/app/coffee/Features/Security/RateLimiterMiddlewear.coffee @@ -15,7 +15,7 @@ module.exports = RateLimiterMiddlewear = ### rateLimit: (opts) -> return (req, res, next) -> - if req.session.user? + if req.session?.user? user_id = req.session.user._id else user_id = req.ip diff --git a/services/web/app/views/project/editor/pdf.jade b/services/web/app/views/project/editor/pdf.jade index 28d78480fe..aee8c9b152 100644 --- a/services/web/app/views/project/editor/pdf.jade +++ b/services/web/app/views/project/editor/pdf.jade @@ -1,16 +1,34 @@ div.full-size.pdf(ng-controller="PdfController") .toolbar.toolbar-tall - a.btn.btn-info( - href, - ng-disabled="pdf.compiling", - ng-click="recompile()" - ) - i.fa.fa-refresh( - ng-class="{'fa-spin': pdf.compiling }" + .btn-group(dropdown) + a.btn.btn-info( + href, + ng-disabled="pdf.compiling", + ng-click="recompile()" ) - |    - span(ng-show="!pdf.compiling") #{translate("recompile")} - span(ng-show="pdf.compiling") #{translate("compiling")}... + i.fa.fa-refresh( + ng-class="{'fa-spin': pdf.compiling }" + ) + |    + span(ng-show="!pdf.compiling") #{translate("recompile")} + span(ng-show="pdf.compiling") #{translate("compiling")}... + a.btn.btn-info.dropdown-toggle( + href, + ng-disabled="pdf.compiling", + dropdown-toggle + ) + span.caret + ul.dropdown-menu.dropdown-menu-right + li.dropdown-header #{translate("compile_mode")} + li + a(href, ng-click="draft = false") + i.fa.fa-fw(ng-class="{'fa-check': !draft}") + |  #{translate("normal")} + li + a(href, ng-click="draft = true") + i.fa.fa-fw(ng-class="{'fa-check': draft}") + |  #{translate("fast")}  + span.subdued [draft] a.log-btn( href ng-click="toggleLogs()" diff --git a/services/web/config/settings.defaults.coffee b/services/web/config/settings.defaults.coffee index cdfe451c1a..3344db1886 100644 --- a/services/web/config/settings.defaults.coffee +++ b/services/web/config/settings.defaults.coffee @@ -106,6 +106,8 @@ module.exports = url: "http://localhost:3036" sixpack: url: "" + references: + url: "http://localhost:3040" templates: user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2" diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index a87ef79060..1d834e4399 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -12,6 +12,11 @@ define [ $scope.$on "pdf:error:display", () -> $scope.pdf.error = true + + $scope.draft = localStorage("draft:#{$scope.project_id}") or false + $scope.$watch "draft", (new_value, old_value) -> + if new_value? and old_value != new_value + localStorage("draft:#{$scope.project_id}", new_value) sendCompileRequest = (options = {}) -> url = "/project/#{$scope.project_id}/compile" @@ -19,6 +24,7 @@ define [ url += "?auto_compile=true" return $http.post url, { rootDoc_id: options.rootDocOverride_id or null + draft: $scope.draft _csrf: window.csrfToken } @@ -103,8 +109,8 @@ define [ doc = ide.editorManager.getCurrentDocValue() return null if !doc? for line in doc.split("\n") - match = line.match /(.*)\\documentclass/ - if match and !match[1].match /%/ + match = line.match /^[^%]*\\documentclass/ + if match return ide.editorManager.getCurrentDocId() return null diff --git a/services/web/public/coffee/main/universties-site.coffee b/services/web/public/coffee/main/universties-site.coffee index dd9d8858df..5826404f01 100644 --- a/services/web/public/coffee/main/universties-site.coffee +++ b/services/web/public/coffee/main/universties-site.coffee @@ -5,13 +5,21 @@ define [ App.controller 'UniverstiesContactController', ($scope, $modal) -> $scope.form = {} + $scope.sent = false + $scope.sending = false $scope.contactUs = -> + if !$scope.form.email? + console.log "email not set" + return + $scope.sending = true params = name: $scope.form.name || $scope.form.email email: $scope.form.email - labels: $scope.form.type + labels: $scope.form.source message: "Please contact me with more details" subject: $scope.form.subject - about : "#{$scope.form.position || ''} #{$scope.form.university || ''} #{$scope.form.source || ''}" + about : "#{$scope.form.position || ''} #{$scope.form.university || ''}" Groove.createTicket params, (err, json)-> + $scope.sent = true + $scope.$apply() diff --git a/services/web/public/stylesheets/app/editor/toolbar.less b/services/web/public/stylesheets/app/editor/toolbar.less index 30bc95aaaa..0bbb1d705d 100644 --- a/services/web/public/stylesheets/app/editor/toolbar.less +++ b/services/web/public/stylesheets/app/editor/toolbar.less @@ -2,7 +2,7 @@ height: 40px; border-bottom: 1px solid @toolbar-border-color; - a { + > a, .toolbar-right > a { position: relative; .label { position: absolute; @@ -13,7 +13,7 @@ } } - a:not(.btn) { + > a:not(.btn), .toolbar-right > a:not(.btn) { display: inline-block; color: @gray-light; padding: 4px 10px 5px; @@ -90,27 +90,36 @@ &.toolbar-small { height: 32px; - a { + > a, .toolbar-right > a { padding: 4px 2px 2px; margin: 0; + } + > a { margin-left: 6px; } - .toolbar-right { - a { - margin-left: 0; - margin-right: 6px; - } + .toolbar-right > a { + margin-left: 0; + margin-right: 6px; } } &.toolbar-tall { height: 58px; padding-top: 10px; - a.btn { + > a, .toolbar-right > a { + padding: 4px 10px 5px; + } + > a.btn, .toolbar-right > a.btn { margin: 0 (@line-height-computed / 2); } - a { - padding: 4px 10px 5px; + .btn-group { + margin: 0 (@line-height-computed / 2); + > a { + margin: 0; + } + > .btn-group { + margin: 0; + } } } @@ -135,7 +144,7 @@ &.toolbar-header { box-shadow: none; } - a:not(.btn) { + > a:not(.btn) { color: @gray; &:hover { color: @gray-light; diff --git a/services/web/public/stylesheets/components/button-groups.less b/services/web/public/stylesheets/components/button-groups.less index 27eb796b89..1480481905 100755 --- a/services/web/public/stylesheets/components/button-groups.less +++ b/services/web/public/stylesheets/components/button-groups.less @@ -66,6 +66,7 @@ .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { .border-left-radius(0); + border-left: none; } // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) diff --git a/services/web/public/stylesheets/components/dropdowns.less b/services/web/public/stylesheets/components/dropdowns.less index f165165e7a..2b718026a1 100755 --- a/services/web/public/stylesheets/components/dropdowns.less +++ b/services/web/public/stylesheets/components/dropdowns.less @@ -67,6 +67,9 @@ line-height: @line-height-base; color: @dropdown-link-color; white-space: nowrap; // prevent links from randomly breaking onto new lines + .subdued { + color: #7a7a7a + } } } @@ -77,6 +80,9 @@ text-decoration: none; color: @dropdown-link-hover-color; background-color: @dropdown-link-hover-bg; + .subdued { + color: @dropdown-link-hover-color; + } } } diff --git a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee index 00e02af13e..baf2643c05 100644 --- a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee @@ -166,6 +166,7 @@ describe "ClsiManager", -> compiler: @compiler timeout : 100 imageName: @image + draft: false rootResourcePath: "main.tex" resources: [{ path: "main.tex" @@ -220,6 +221,12 @@ describe "ClsiManager", -> it "should return an error", -> expect(@error).to.exist + + describe "with the draft option", -> + it "should add the draft option into the request", (done) -> + @ClsiManager._buildRequest @project_id, {timeout:100, draft: true}, (error, request) => + request.compile.options.draft.should.equal true + done() describe '_postToClsi', -> diff --git a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee index 85cdd55c50..26cf0a2e2d 100644 --- a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee @@ -43,14 +43,15 @@ describe "CompileController", -> @res = new MockResponse() describe "compile", -> + beforeEach -> + @req.params = + Project_id: @project_id + @req.session = {} + @AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id") + @CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"]) describe "when not an auto compile", -> beforeEach -> - @req.params = - Project_id: @project_id - @req.session = {} - @AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id") - @CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"], @output = "mock-output") @CompileController.compile @req, @res, @next it "should look up the user id", -> @@ -77,18 +78,25 @@ describe "CompileController", -> describe "when an auto compile", -> beforeEach -> - @req.params = - Project_id: @project_id @req.query = auto_compile: "true" - @AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id") - @CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"]) @CompileController.compile @req, @res, @next it "should do the compile with the auto compile flag", -> @CompileManager.compile .calledWith(@project_id, @user_id, { isAutoCompile: true }) .should.equal true + + describe "with the draft attribute", -> + beforeEach -> + @req.body = + draft: true + @CompileController.compile @req, @res, @next + + it "should do the compile without the draft compile flag", -> + @CompileManager.compile + .calledWith(@project_id, @user_id, { isAutoCompile: false, draft: true }) + .should.equal true describe "downloadPdf", -> beforeEach -> diff --git a/services/web/test/UnitTests/coffee/Security/RateLimiterMiddlewearTests.coffee b/services/web/test/UnitTests/coffee/Security/RateLimiterMiddlewearTests.coffee index fa9c148b3e..05f40a2553 100644 --- a/services/web/test/UnitTests/coffee/Security/RateLimiterMiddlewearTests.coffee +++ b/services/web/test/UnitTests/coffee/Security/RateLimiterMiddlewearTests.coffee @@ -10,7 +10,6 @@ describe "RateLimiterMiddlewear", -> "logger-sharelatex": @logger = {warn: sinon.stub()} @req = params: {} - session: {} @res = status: sinon.stub() write: sinon.stub() @@ -30,9 +29,30 @@ describe "RateLimiterMiddlewear", -> doc_id: @doc_id = "doc-id" } + describe "when there is no session", -> + beforeEach -> + @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true) + @req.ip = @ip = "1.2.3.4" + @rateLimiter(@req, @res, @next) + + it "should call the rate limiter backend with the ip address", -> + @RateLimiter.addCount + .calledWith({ + endpointName: "test-endpoint" + timeInterval: 42 + throttle: 12 + subjectName: "#{@project_id}:#{@doc_id}:#{@ip}" + }) + .should.equal true + + it "should pass on to next()", -> + + describe "when under the rate limit with logged in user", -> beforeEach -> - @req.session.user = { _id: @user_id = "user-id" } + @req.session = + user : + _id: @user_id = "user-id" @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true) @rateLimiter(@req, @res, @next) @@ -70,7 +90,9 @@ describe "RateLimiterMiddlewear", -> describe "when over the rate limit", -> beforeEach -> - @req.session.user = { _id: @user_id = "user-id" } + @req.session = + user : + _id: @user_id = "user-id" @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, false) @rateLimiter(@req, @res, @next)