diff --git a/services/web/.gitignore b/services/web/.gitignore index ad7f51119c..601a74db0e 100644 --- a/services/web/.gitignore +++ b/services/web/.gitignore @@ -63,6 +63,8 @@ public/minjs/ public/js/main.js Gemfile.lock +app/views/external + *.swp .DS_Store diff --git a/services/web/README.md b/services/web/README.md index b74e61b983..00d94343ab 100644 --- a/services/web/README.md +++ b/services/web/README.md @@ -17,10 +17,7 @@ Unit test status License and Credits ------------------- -### Code - -All coffeescript files (files ended in *.coffee) are licensed under the -[AGPLv3 license](http://www.gnu.org/licenses/agpl-3.0.html). +This project is licensed under the [AGPLv3 license](http://www.gnu.org/licenses/agpl-3.0.html) ### Stylesheets diff --git a/services/web/TpdsWorker.coffee b/services/web/TpdsWorker.coffee index 5ffc1e303d..cfc5dcd875 100644 --- a/services/web/TpdsWorker.coffee +++ b/services/web/TpdsWorker.coffee @@ -33,26 +33,50 @@ processingFuncs = else callback() - pipeStreamFrom: (options, callback)-> + pipeStreamFrom: (options, _callback)-> + callback = (args...) -> + _callback(args...) + _callback = () -> + if options.filePath == "/droppy/main.tex" request options.streamOrigin, (err,res, body)-> logger.log options:options, body:body + origin = request(options.streamOrigin) + + cancelled = false + gotResponse = false + origin.on 'response', (res) -> + return if cancelled + gotResponse = true + if 200 <= res.statusCode < 300 + dest = request(options) + origin.pipe(dest) + + dest.on "error", (err)-> + logger.error err:err, options:options, "something went wrong in pipeStreamFrom dest" + callback(err) + + dest.on 'end', callback + else + error = new Error("received non-success status code: #{res.statusCode}") + logger.error err: error, options: options, "something went wrong connecting to origin" + callback(error) + origin.on 'error', (err)-> + return if cancelled + gotResponse = true logger.error err:err, options:options, "something went wrong in pipeStreamFrom origin" - if err? - callback(err) - else - callback() - dest = request(options) - origin.pipe(dest) - dest.on "error", (err)-> - logger.error err:err, options:options, "something went wrong in pipeStreamFrom dest" - if err? - callback(err) - else - callback() - dest.on 'end', callback + callback(err) + + setTimeout () -> + return if gotResponse + cancelled = true + error = new Error("timeout") + logger.error err: error, options: options, "timeout" + callback(error) + , 2000 + workerRegistration = (groupKey, method, options, callback)-> diff --git a/services/web/app.coffee b/services/web/app.coffee index 600d65c186..88925c2567 100644 --- a/services/web/app.coffee +++ b/services/web/app.coffee @@ -15,7 +15,7 @@ argv = require("optimist") .argv Server.app.use (error, req, res, next) -> - logger.error err: error + logger.error err: error, url:req.url, method:req.method, user:req?.sesson?.user, "error passed to top level next middlewear" res.statusCode = error.status or 500 if res.statusCode == 500 res.end("Oops, something went wrong with your request, sorry. If this continues, please contact us at team@sharelatex.com") diff --git a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee index 180f05738e..72383870b6 100644 --- a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee +++ b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee @@ -44,7 +44,11 @@ module.exports = AuthenticationController = res.send(auth_token) getLoggedInUserId: (req, callback = (error, user_id) ->) -> - callback null, req.session.user._id.toString() + if req?.session?.user?._id? + callback null, req.session.user._id.toString() + else + e = new Error("user is not on req session") + callback e getLoggedInUser: (req, options = {allow_auth_token: false}, callback = (error, user) ->) -> if req.session?.user?._id? diff --git a/services/web/app/coffee/Features/Compile/CompileController.coffee b/services/web/app/coffee/Features/Compile/CompileController.coffee index 44f538771b..3641398d18 100644 --- a/services/web/app/coffee/Features/Compile/CompileController.coffee +++ b/services/web/app/coffee/Features/Compile/CompileController.coffee @@ -19,6 +19,9 @@ module.exports = CompileController = res.header('Content-Disposition', "filename=#{project.getSafeProjectName()}.pdf") CompileController.proxyToClsi("/project/#{project_id}/output/output.pdf", req, res, next) + deleteAuxFiles: (req, res, next) -> + project_id = req.params.Project_id + CompileController.proxyToClsi("/project/#{project_id}", req, res, next) compileAndDownloadPdf: (req, res, next)-> project_id = req.params.project_id @@ -29,8 +32,6 @@ module.exports = CompileController = url = "/project/#{project_id}/output/output.pdf" CompileController.proxyToClsi url, req, res, next - - getFileFromClsi: (req, res, next = (error) ->) -> CompileController.proxyToClsi("/project/#{req.params.Project_id}/output/#{req.params.file}", req, res, next) @@ -38,8 +39,8 @@ module.exports = CompileController = logger.log url: url, "proxying to CLSI" url = "#{Settings.apis.clsi.url}#{url}" oneMinute = 60 * 1000 - proxy = request.get(url: url, timeout: oneMinute) + proxy = request(url: url, method: req.method, timeout: oneMinute) proxy.pipe(res) proxy.on "error", (error) -> - logger.error err: error, url: url, "CLSI proxy error" + logger.warn err: error, url: url, "CLSI proxy error" diff --git a/services/web/app/coffee/Features/Editor/EditorController.coffee b/services/web/app/coffee/Features/Editor/EditorController.coffee index c3cf4b6ac0..413ea4609e 100644 --- a/services/web/app/coffee/Features/Editor/EditorController.coffee +++ b/services/web/app/coffee/Features/Editor/EditorController.coffee @@ -1,6 +1,6 @@ logger = require('logger-sharelatex') Metrics = require('../../infrastructure/Metrics') -sanitize = require('validator').sanitize +sanitize = require('sanitizer') ProjectEditorHandler = require('../Project/ProjectEditorHandler') ProjectEntityHandler = require('../Project/ProjectEntityHandler') ProjectOptionsHandler = require('../Project/ProjectOptionsHandler') @@ -163,7 +163,7 @@ module.exports = EditorController = addDoc: (project_id, folder_id, docName, docLines, sl_req_id, callback = (error, doc)->)-> {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id) - docName = sanitize(docName).xss() + docName = sanitize.escape(docName) logger.log sl_req_id:sl_req_id, "sending new doc to project #{project_id}" Metrics.inc "editor.add-doc" ProjectEntityHandler.addDoc project_id, folder_id, docName, docLines, sl_req_id, (err, doc, folder_id)=> @@ -172,7 +172,7 @@ module.exports = EditorController = addFile: (project_id, folder_id, fileName, path, sl_req_id, callback = (error, file)->)-> {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id) - fileName = sanitize(fileName).xss() + fileName = sanitize.escape(fileName) logger.log sl_req_id:sl_req_id, "sending new file to project #{project_id} with folderid: #{folder_id}" Metrics.inc "editor.add-file" ProjectEntityHandler.addFile project_id, folder_id, fileName, path, (err, fileRef, folder_id)=> @@ -185,7 +185,7 @@ module.exports = EditorController = addFolder: (project_id, folder_id, folderName, sl_req_id, callback = (error, folder)->)-> {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id) - folderName = sanitize(folderName).xss() + folderName = sanitize.escape(folderName) logger.log "sending new folder to project #{project_id}" Metrics.inc "editor.add-folder" ProjectEntityHandler.addFolder project_id, folder_id, folderName, (err, folder, folder_id)=> @@ -237,6 +237,44 @@ module.exports = EditorController = EditorRealTimeController.emitToRoom(project_id, 'projectDescriptionUpdated', description) callback() + deleteProject: (project_id, callback)-> + Metrics.inc "editor.delete-project" + logger.log project_id:project_id, "recived message to delete project" + ProjectHandler.deleteProject project_id, callback + + renameEntity: (project_id, entity_id, entityType, newName, callback)-> + newName = sanitize.escape(newName) + Metrics.inc "editor.rename-entity" + logger.log entity_id:entity_id, entity_id:entity_id, entity_id:entity_id, "reciving new name for entity for project" + ProjectHandler.renameEntity project_id, entity_id, entityType, newName, => + if newName.length > 0 + EditorRealTimeController.emitToRoom project_id, 'reciveEntityRename', entity_id, newName + callback?() + + moveEntity: (project_id, entity_id, folder_id, entityType, callback)-> + Metrics.inc "editor.move-entity" + ProjectEntityHandler.moveEntity project_id, entity_id, folder_id, entityType, => + EditorRealTimeController.emitToRoom project_id, 'reciveEntityMove', entity_id, folder_id + callback?() + + renameProject: (project_id, window_id, newName, callback)-> + newName = sanitize.escape(newName) + ProjectHandler.renameProject project_id, window_id, newName, => + newName = sanitize.escape(newName) + EditorRealTimeController.emitToRoom project_id, 'projectNameUpdated', window_id, newName + callback?() + + setPublicAccessLevel : (project_id, newAccessLevel, callback)-> + ProjectHandler.setPublicAccessLevel project_id, newAccessLevel, => + EditorRealTimeController.emitToRoom project_id, 'publicAccessLevelUpdated', newAccessLevel + callback?() + + setRootDoc: (project_id, newRootDocID, callback)-> + ProjectEntityHandler.setRootDoc project_id, newRootDocID, () => + EditorRealTimeController.emitToRoom project_id, 'rootDocUpdated', newRootDocID + callback?() + + p: notifyProjectUsersOfNewFolder: (project_id, folder_id, folder, callback = (error)->)-> logger.log project_id:project_id, folder:folder, parentFolder_id:folder_id, "sending newly created folder out to users" diff --git a/services/web/app/coffee/managers/NewsletterManager.coffee b/services/web/app/coffee/Features/Newsletter/NewsletterManager.coffee similarity index 100% rename from services/web/app/coffee/managers/NewsletterManager.coffee rename to services/web/app/coffee/Features/Newsletter/NewsletterManager.coffee diff --git a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee index 7e3960db23..3d59a16e94 100644 --- a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee @@ -1,4 +1,5 @@ ProjectGetter = require("./ProjectGetter") +UserGetter = require("../User/UserGetter") Project = require('../../models/Project').Project logger = require("logger-sharelatex") @@ -9,12 +10,15 @@ module.exports = if err? logger.err err:err, project_id:project_id, "error getting project" return callback(err) - details = - name : project.name - description: project.description - compiler: project.compiler - logger.log project_id:project_id, details:details, "getting project details" - callback(err, details) + UserGetter.getUser project.owner_ref, (err, user) -> + return callback(err) if err? + details = + name : project.name + description: project.description + compiler: project.compiler + features: user.features + logger.log project_id:project_id, details:details, "getting project details" + callback(err, details) setProjectDescription: (project_id, description, callback)-> conditions = _id:project_id diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionGroupHandler.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionGroupHandler.coffee index a93286f73c..a4907f9544 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionGroupHandler.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionGroupHandler.coffee @@ -28,6 +28,9 @@ module.exports = jobs = _.map subscription.member_ids, (user_id)-> return (cb)-> UserLocator.findById user_id, (err, user)-> + if err? or !user? + users.push _id:user_id + return cb() userViewModel = buildUserViewModel(user) users.push(userViewModel) cb() diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee index 340863a405..74f91a8dad 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee @@ -38,6 +38,8 @@ module.exports = buildViewModel : -> plans = Settings.plans + console.log plans + console.log(typeof(plans)) allPlans = {} plans.forEach (plan)-> allPlans[plan.planCode] = plan diff --git a/services/web/app/coffee/Features/ThirdPartyDataStore/TpdsPollingBackgroundTasks.coffee b/services/web/app/coffee/Features/ThirdPartyDataStore/TpdsPollingBackgroundTasks.coffee index 80597b27a2..46afad2b18 100644 --- a/services/web/app/coffee/Features/ThirdPartyDataStore/TpdsPollingBackgroundTasks.coffee +++ b/services/web/app/coffee/Features/ThirdPartyDataStore/TpdsPollingBackgroundTasks.coffee @@ -23,7 +23,7 @@ self = module.exports = callback() _getUserIdsWithDropbox: (callback)-> - User.find {"dropbox.access_token.oauth_token_secret":{"$exists":true}}, "_id", (err, users)-> + User.find {"dropbox.access_token.oauth_token_secret":{"$exists":true}, "features.dropbox":true}, "_id", (err, users)-> ids = users.map (user)-> return user._id+"" callback err, ids diff --git a/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee b/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee index d4548b4a3e..3a787bc985 100644 --- a/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee +++ b/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee @@ -5,7 +5,7 @@ logger = require('logger-sharelatex') Settings = require('settings-sharelatex') slReqIdHelper = require('soa-req-id') FileTypeManager = require('../Uploads/FileTypeManager') -GuidManager = require '../../managers/GuidManager' +uuid = require('node-uuid') fs = require('fs') module.exports = @@ -75,7 +75,7 @@ module.exports = writeStreamToDisk: (project_id, file_id, stream, callback = (err, fsPath)->)-> if !file_id? - file_id = GuidManager.newGuid() + file_id = uuid.v4() dumpPath = "#{Settings.path.dumpFolder}/#{project_id}_#{file_id}" writeStream = fs.createWriteStream(dumpPath) diff --git a/services/web/app/coffee/Features/User/UserDeleter.coffee b/services/web/app/coffee/Features/User/UserDeleter.coffee index 56ff06df4c..7361c7ab56 100644 --- a/services/web/app/coffee/Features/User/UserDeleter.coffee +++ b/services/web/app/coffee/Features/User/UserDeleter.coffee @@ -1,5 +1,5 @@ User = require("../../models/User").User -NewsletterManager = require "../../managers/NewsletterManager" +NewsletterManager = require "../Newsletter/NewsletterManager" ProjectDeleter = require("../Project/ProjectDeleter") logger = require("logger-sharelatex") SubscriptionHandler = require("../Subscription/SubscriptionHandler") diff --git a/services/web/app/coffee/Features/User/UserRegistrationHandler.coffee b/services/web/app/coffee/Features/User/UserRegistrationHandler.coffee index b39a239341..c6672f6adf 100644 --- a/services/web/app/coffee/Features/User/UserRegistrationHandler.coffee +++ b/services/web/app/coffee/Features/User/UserRegistrationHandler.coffee @@ -1,4 +1,4 @@ -sanitize = require('validator').sanitize +sanitize = require('sanitizer') module.exports = validateEmail : (email) -> @@ -13,7 +13,7 @@ module.exports = return hasZeroLength validateRegisterRequest : (req, callback)-> - email = sanitize(req.body.email).xss().trim().toLowerCase() + email = sanitize.escape(req.body.email).trim().toLowerCase() password = req.body.password username = email.match(/^[^@]*/) if username? diff --git a/services/web/app/coffee/controllers/HomeController.coffee b/services/web/app/coffee/controllers/HomeController.coffee index ca93692a16..566b5bcebc 100755 --- a/services/web/app/coffee/controllers/HomeController.coffee +++ b/services/web/app/coffee/controllers/HomeController.coffee @@ -3,8 +3,11 @@ _ = require('underscore') User = require('./UserController') Quotes = require('../models/Quote').Quote +Path = require "path" +fs = require "fs" +homepageExists = fs.existsSync Path.resolve(__dirname + "/../../views/external/home.jade") -module.exports = +module.exports = HomeController = index : (req,res)-> if req.session.user if req.query.scribtex_path? @@ -12,42 +15,23 @@ module.exports = else res.redirect '/project' else - res.render 'homepage/home', - title: 'ShareLaTeX.com' + if homepageExists + res.render 'external/home', + title: 'ShareLaTeX.com' + else + res.redirect "/login" - comments : (req, res)-> - res.render 'homepage/comments.jade', - title: 'User Comments' - - resources : (req, res)-> - res.render 'resources.jade', - title: 'LaTeX Resources' - - tos : (req, res) -> - res.render 'about/tos', - title: "Terms of Service" - - privacy : (req, res) -> - res.render 'about/privacy', - title: "Privacy Policy" - - about : (req, res) -> - res.render 'about/about', - title: "About us" + externalPage: (page, title) -> + return (req, res, next = (error) ->) -> + path = Path.resolve(__dirname + "/../../views/external/#{page}.jade") + fs.exists path, (exists) -> # No error in this callback - old method in Node.js! + if exists + res.render "external/#{page}.jade", + title: title + else + HomeController.notFound(req, res, next) notFound: (req, res)-> res.statusCode = 404 res.render 'general/404', - title: "Page Not Found" - - security : (req, res) -> - res.render 'about/security', - title: "Security" - - attribution: (req, res) -> - res.render 'about/attribution', - title: "Attribution" - - planned_maintenance: (req, res) -> - res.render 'about/planned_maintenance', - title: "Planned Maintenance" + title: "Page Not Found" \ No newline at end of file diff --git a/services/web/app/coffee/controllers/ProjectController.coffee b/services/web/app/coffee/controllers/ProjectController.coffee index 1c4b9a3349..3b98193f4e 100755 --- a/services/web/app/coffee/controllers/ProjectController.coffee +++ b/services/web/app/coffee/controllers/ProjectController.coffee @@ -1,13 +1,12 @@ User = require('../models/User').User Project = require('../models/Project').Project -sanitize = require('validator').sanitize +sanitize = require('sanitizer') path = require "path" logger = require('logger-sharelatex') _ = require('underscore') fs = require('fs') ProjectHandler = require '../handlers/ProjectHandler' SecurityManager = require '../managers/SecurityManager' -GuidManager = require '../managers/GuidManager' Settings = require('settings-sharelatex') projectCreationHandler = require '../Features/Project/ProjectCreationHandler' projectDuplicator = require('../Features/Project/ProjectDuplicator') @@ -19,7 +18,7 @@ SubscriptionFormatters = require("../Features/Subscription/SubscriptionFormatter FileStoreHandler = require("../Features/FileStore/FileStoreHandler") module.exports = class ProjectController - constructor: (@collaberationManager)-> + constructor: ()-> ProjectHandler = new ProjectHandler() list: (req, res, next)-> @@ -72,8 +71,8 @@ module.exports = class ProjectController apiNewProject: (req, res)-> user = req.session.user - projectName = sanitize(req.body.projectName).xss() - template = sanitize(req.body.template).xss() + projectName = sanitize.escape(req.body.projectName) + template = sanitize.escape(req.body.template) logger.log user: user, type: template, name: projectName, "creating project" if template == 'example' projectCreationHandler.createExampleProject user._id, projectName, (err, project)-> @@ -123,7 +122,7 @@ module.exports = class ProjectController trackChanges: false else anonymous = false - SubscriptionLocator.getUsersSubscription user._id, (err, subscription)-> + SubscriptionLocator.getUsersSubscription user?._id, (err, subscription)-> SecurityManager.userCanAccessProject user, project, (canAccess, privlageLevel)-> allowedFreeTrial = true if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt? @@ -154,7 +153,7 @@ module.exports = class ProjectController spellCheckLanguage: user.ace.spellCheckLanguage pdfViewer : user.ace.pdfViewer docPositions: {} - trackChanges: user.featureSwitches.trackChanges + oldHistory: !!user.featureSwitches?.oldHistory }) sharelatexObject : JSON.stringify({ siteUrl: Settings.siteUrl, diff --git a/services/web/app/coffee/controllers/UserController.coffee b/services/web/app/coffee/controllers/UserController.coffee index a373e14043..63c0837009 100644 --- a/services/web/app/coffee/controllers/UserController.coffee +++ b/services/web/app/coffee/controllers/UserController.coffee @@ -1,11 +1,11 @@ User = require('../models/User').User -sanitize = require('validator').sanitize +sanitize = require('sanitizer') fs = require('fs') _ = require('underscore') logger = require('logger-sharelatex') Security = require('../managers/SecurityManager') Settings = require('settings-sharelatex') -newsLetterManager = require('../managers/NewsletterManager') +newsLetterManager = require('../Features/Newsletter/NewsletterManager') dropboxHandler = require('../Features/Dropbox/DropboxHandler') userRegistrationHandler = require('../Features/User/UserRegistrationHandler') metrics = require('../infrastructure/Metrics') @@ -95,8 +95,8 @@ module.exports = title: 'Password Reset', doRequestPasswordReset : (req, res, next = (error) ->)-> - email = sanitize(req.body.email).xss() - email = sanitize(email).trim() + email = sanitize.escape(req.body.email) + email = sanitize.escape(email).trim() email = email.toLowerCase() logger.log email: email, "password reset requested" User.findOne {'email':email}, (err, user)-> @@ -156,11 +156,11 @@ module.exports = metrics.inc "user.settings-update" User.findById req.session.user._id, (err, user)-> if(user) - user.first_name = sanitize(req.body.first_name).xss().trim() - user.last_name = sanitize(req.body.last_name).xss().trim() - user.ace.mode = sanitize(req.body.mode).xss().trim() - user.ace.theme = sanitize(req.body.theme).xss().trim() - user.ace.fontSize = sanitize(req.body.fontSize).xss().trim() + user.first_name = sanitize.escape(req.body.first_name).trim() + user.last_name = sanitize.escape(req.body.last_name).trim() + user.ace.mode = sanitize.escape(req.body.mode).trim() + user.ace.theme = sanitize.escape(req.body.theme).trim() + user.ace.fontSize = sanitize.escape(req.body.fontSize).trim() user.ace.autoComplete = req.body.autoComplete == "true" user.ace.spellCheckLanguage = req.body.spellCheckLanguage user.ace.pdfViewer = req.body.pdfViewer @@ -171,7 +171,7 @@ module.exports = metrics.inc "user.password-change" oldPass = req.body.currentPassword AuthenticationManager.authenticate _id: req.session.user._id, oldPass, (err, user)-> - return callback(err) if err? + return next(err) if err? if(user) logger.log user: req.session.user, "changing password" newPassword1 = req.body.newPassword1 @@ -197,23 +197,23 @@ module.exports = type:'error' text:'Your old password is wrong' - redirectUserToDropboxAuth: (req, res)-> + redirectUserToDropboxAuth: (req, res, next)-> user_id = req.session.user._id dropboxHandler.getDropboxRegisterUrl user_id, (err, url)-> - return callback(err) if err? + return next(err) if err? logger.log url:url, "redirecting user for dropbox auth" res.redirect url - completeDropboxRegistration: (req, res)-> + completeDropboxRegistration: (req, res, next)-> user_id = req.session.user._id dropboxHandler.completeRegistration user_id, (err, success)-> - return callback(err) if err? + return next(err) if err? res.redirect('/user/settings#dropboxSettings') - unlinkDropbox: (req, res)-> + unlinkDropbox: (req, res, next)-> user_id = req.session.user._id dropboxHandler.unlinkAccount user_id, (err, success)-> - return callback(err) if err? + return next(err) if err? res.redirect('/user/settings#dropboxSettings') deleteUser: (req, res)-> diff --git a/services/web/app/coffee/managers/CollaberationManager.coffee b/services/web/app/coffee/managers/CollaberationManager.coffee deleted file mode 100644 index ed3aadf87c..0000000000 --- a/services/web/app/coffee/managers/CollaberationManager.coffee +++ /dev/null @@ -1,62 +0,0 @@ -#this file is being slowly refactored out - -logger = require('logger-sharelatex') -sanitize = require('validator').sanitize -projectHandler = require('../handlers/ProjectHandler') -projectHandler = new projectHandler() -SecurityManager = require('./SecurityManager') -_ = require('underscore') -projectEditorHandler = require('../Features/Project/ProjectEditorHandler') -projectEntityHandler = require('../Features/Project/ProjectEntityHandler') -versioningApiHandler = require('../Features/Versioning/VersioningApiHandler') -metrics = require('../infrastructure/Metrics') -EditorRealTimeController = require('../Features/Editor/EditorRealTimeController') - -module.exports = class CollaberationManager - constructor: (@io)-> - - deleteProject: (project_id, callback)-> - metrics.inc "editor.delete-project" - logger.log project_id:project_id, "recived message to delete project" - projectHandler.deleteProject project_id, callback - - renameEntity: (project_id, entity_id, entityType, newName, callback)-> - newName = sanitize(newName).xss() - metrics.inc "editor.rename-entity" - logger.log entity_id:entity_id, entity_id:entity_id, entity_id:entity_id, "reciving new name for entity for project" - projectHandler.renameEntity project_id, entity_id, entityType, newName, => - if newName.length > 0 - EditorRealTimeController.emitToRoom project_id, 'reciveEntityRename', entity_id, newName - callback?() - - moveEntity: (project_id, entity_id, folder_id, entityType, callback)-> - metrics.inc "editor.move-entity" - projectEntityHandler.moveEntity project_id, entity_id, folder_id, entityType, => - EditorRealTimeController.emitToRoom project_id, 'reciveEntityMove', entity_id, folder_id - callback?() - - renameProject: (project_id, window_id, newName, callback)-> - newName = sanitize(newName).xss() - projectHandler.renameProject project_id, window_id, newName, => - newName = sanitize(newName).xss() - EditorRealTimeController.emitToRoom project_id, 'projectNameUpdated', window_id, newName - callback?() - - setPublicAccessLevel : (project_id, newAccessLevel, callback)-> - projectHandler.setPublicAccessLevel project_id, newAccessLevel, => - EditorRealTimeController.emitToRoom project_id, 'publicAccessLevelUpdated', newAccessLevel - callback?() - - distributMessage: (project_id, client, message)-> - message = sanitize(message).xss() - metrics.inc "editor.instant-message" - client.get "first_name", (err, first_name)=> - EditorRealTimeController.emitToRoom project_id, 'reciveNewMessage', first_name, message - - setRootDoc: (project_id, newRootDocID, callback)-> - projectEntityHandler.setRootDoc project_id, newRootDocID, () => - EditorRealTimeController.emitToRoom project_id, 'rootDocUpdated', newRootDocID - callback?() - - takeVersionSnapShot : (project_id, message, callback)-> - versioningApiHandler.takeVersionSnapshot project_id, message, callback diff --git a/services/web/app/coffee/managers/GuidManager.coffee b/services/web/app/coffee/managers/GuidManager.coffee deleted file mode 100644 index c283670282..0000000000 --- a/services/web/app/coffee/managers/GuidManager.coffee +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = - newGuid : ()-> - S4 = ()-> - return (((1+Math.random())*0x10000)|0).toString(16).substring(1) - return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()) - diff --git a/services/web/app/coffee/managers/SecurityManager.coffee b/services/web/app/coffee/managers/SecurityManager.coffee index 38d7bdabef..1f757fd9c2 100644 --- a/services/web/app/coffee/managers/SecurityManager.coffee +++ b/services/web/app/coffee/managers/SecurityManager.coffee @@ -166,7 +166,9 @@ getRequestUserAndProject = (req, res, options, callback)-> callback err, user, project getProjectIdFromRef = (ref)-> - if ref._id? + if !ref? + return null + else if ref._id? return ref._id+'' else return ref+'' diff --git a/services/web/app/coffee/models/Project.coffee b/services/web/app/coffee/models/Project.coffee index a009dc2a45..bf6a1c1c27 100644 --- a/services/web/app/coffee/models/Project.coffee +++ b/services/web/app/coffee/models/Project.coffee @@ -3,7 +3,7 @@ Settings = require 'settings-sharelatex' _ = require('underscore') FolderSchema = require('./Folder.js').FolderSchema logger = require('logger-sharelatex') -sanitize = require('validator').sanitize +sanitize = require('sanitizer') concreteObjectId = require('mongoose').Types.ObjectId Errors = require "../errors" @@ -112,7 +112,7 @@ applyToAllFilesRecursivly = ProjectSchema.statics.applyToAllFilesRecursivly = (f ProjectSchema.methods.getSafeProjectName = -> safeProjectName = this.name.replace(new RegExp("\\W", "g"), '_') - return sanitize(safeProjectName).xss() + return sanitize.escape(safeProjectName) conn = mongoose.createConnection(Settings.mongo.url, server: poolSize: Settings.mongo.poolSize || 10) diff --git a/services/web/app/coffee/models/User.coffee b/services/web/app/coffee/models/User.coffee index e71182ec2b..3a3ecdc8e3 100644 --- a/services/web/app/coffee/models/User.coffee +++ b/services/web/app/coffee/models/User.coffee @@ -32,7 +32,7 @@ UserSchema = new Schema } featureSwitches : { dropbox: {type:Boolean, default:true}, - trackChanges: {type:Boolean, default:false} + oldHistory: {type:Boolean} } referal_id : {type:String, default:() -> uuid.v4().split("-")[0]} refered_users: [ type:ObjectId, ref:'User' ] diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 958f3d19d9..90d1cf3379 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -5,8 +5,7 @@ ProjectController = require("./controllers/ProjectController") ProjectApiController = require("./Features/Project/ProjectApiController") InfoController = require('./controllers/InfoController') SpellingController = require('./Features/Spelling/SpellingController') -CollaberationManager = require('./managers/CollaberationManager') -SecutiryManager = require('./managers/SecurityManager') +SecurityManager = require('./managers/SecurityManager') AuthorizationManager = require('./Features/Security/AuthorizationManager') versioningController = require("./Features/Versioning/VersioningApiController") EditorController = require("./Features/Editor/EditorController") @@ -46,9 +45,7 @@ module.exports = class Router constructor: (app, io, socketSessions)-> app.use(app.router) - collaberationManager = new CollaberationManager(io) - - Project = new ProjectController(collaberationManager) + Project = new ProjectController() projectHandler = new ProjectHandler() app.get '/', HomeController.index @@ -56,16 +53,16 @@ module.exports = class Router app.get '/login', UserController.loginForm app.post '/login', AuthenticationController.login app.get '/logout', UserController.logout - app.get '/restricted', SecutiryManager.restricted + app.get '/restricted', SecurityManager.restricted + + app.get '/resources', HomeController.externalPage("resources", "LaTeX Resources") + app.get '/tos', HomeController.externalPage("tos", "Terms of Service") + app.get '/about', HomeController.externalPage("about", "About Us") + app.get '/attribution', HomeController.externalPage("attribution", "Attribution") + app.get '/security', HomeController.externalPage("security", "Security") + app.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy") + app.get '/planned_maintenance', HomeController.externalPage("planned_mainteance", "Planned Maintenance") - app.get '/resources', HomeController.resources - app.get '/comments', HomeController.comments - app.get '/tos', HomeController.tos - app.get '/about', HomeController.about - app.get '/attribution', HomeController.attribution - app.get '/security', HomeController.security - app.get '/privacy_policy', HomeController.privacy - app.get '/planned_maintenance', HomeController.planned_maintenance app.get '/themes', InfoController.themes app.get '/advisor', InfoController.advisor app.get '/dropbox', InfoController.dropbox @@ -99,13 +96,10 @@ module.exports = class Router app.post '/project/new', AuthenticationController.requireLogin(), Project.apiNewProject app.get '/project/new/template', TemplatesMiddlewear.saveTemplateDataInSession, AuthenticationController.requireLogin(), TemplatesController.createProjectFromZipTemplate - app.get '/Project/:Project_id', SecutiryManager.requestCanAccessProject, Project.loadEditor - app.get '/Project/:Project_id/file/:File_id', SecutiryManager.requestCanAccessProject, FileStoreController.getFile + app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, Project.loadEditor + app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile - # This is left for legacy reasons and can be removed once all editors have had a chance to refresh: - app.get '/Project/:Project_id/download/pdf', SecutiryManager.requestCanAccessProject, CompileController.downloadPdf - - app.get '/Project/:Project_id/output/output.pdf', SecutiryManager.requestCanAccessProject, CompileController.downloadPdf + app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf app.get /^\/project\/([^\/]*)\/output\/(.*)$/, ((req, res, next) -> params = @@ -113,25 +107,26 @@ module.exports = class Router "file": req.params[1] req.params = params next() - ), SecutiryManager.requestCanAccessProject, CompileController.getFileFromClsi + ), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi + app.del "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles - app.del '/Project/:Project_id', SecutiryManager.requestIsOwner, Project.deleteProject - app.post '/Project/:Project_id/clone', SecutiryManager.requestCanAccessProject, Project.cloneProject + app.del '/Project/:Project_id', SecurityManager.requestIsOwner, Project.deleteProject + app.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, Project.cloneProject - app.post '/Project/:Project_id/snapshot', SecutiryManager.requestCanModifyProject, versioningController.takeSnapshot - app.get '/Project/:Project_id/version', SecutiryManager.requestCanAccessProject, versioningController.listVersions - app.get '/Project/:Project_id/version/:Version_id', SecutiryManager.requestCanAccessProject, versioningController.getVersion - app.get '/Project/:Project_id/version', SecutiryManager.requestCanAccessProject, versioningController.listVersions - app.get '/Project/:Project_id/version/:Version_id', SecutiryManager.requestCanAccessProject, versioningController.getVersion + app.post '/Project/:Project_id/snapshot', SecurityManager.requestCanModifyProject, versioningController.takeSnapshot + app.get '/Project/:Project_id/version', SecurityManager.requestCanAccessProject, versioningController.listVersions + app.get '/Project/:Project_id/version/:Version_id', SecurityManager.requestCanAccessProject, versioningController.getVersion + app.get '/Project/:Project_id/version', SecurityManager.requestCanAccessProject, versioningController.listVersions + app.get '/Project/:Project_id/version/:Version_id', SecurityManager.requestCanAccessProject, versioningController.getVersion - app.get "/project/:Project_id/updates", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi - app.get "/project/:Project_id/doc/:doc_id/diff", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi - app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi + app.get "/project/:Project_id/updates", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi + app.get "/project/:Project_id/doc/:doc_id/diff", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi + app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject - app.get '/project/:Project_id/collaborators', SecutiryManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators + app.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators - app.get '/Project/:Project_id/download/zip', SecutiryManager.requestCanAccessProject, ProjectDownloadsController.downloadProject + app.get '/Project/:Project_id/download/zip', SecurityManager.requestCanAccessProject, ProjectDownloadsController.downloadProject app.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags @@ -164,21 +159,21 @@ module.exports = class Router req.params = params next() ), - SecutiryManager.requestCanAccessProject, versioningController.getVersionFile + SecurityManager.requestCanAccessProject, versioningController.getVersionFile app.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi app.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi #Admin Stuff - app.get '/admin', SecutiryManager.requestIsAdmin, AdminController.index - app.post '/admin/closeEditor', SecutiryManager.requestIsAdmin, AdminController.closeEditor - app.post '/admin/dissconectAllUsers', SecutiryManager.requestIsAdmin, AdminController.dissconectAllUsers - app.post '/admin/writeAllDocsToMongo', SecutiryManager.requestIsAdmin, AdminController.writeAllToMongo - app.post '/admin/addquote', SecutiryManager.requestIsAdmin, AdminController.addQuote - app.post '/admin/syncUserToSubscription', SecutiryManager.requestIsAdmin, AdminController.syncUserToSubscription - app.post '/admin/flushProjectToTpds', SecutiryManager.requestIsAdmin, AdminController.flushProjectToTpds - app.post '/admin/pollUsersWithDropbox', SecutiryManager.requestIsAdmin, AdminController.pollUsersWithDropbox - app.post '/admin/updateProjectCompiler', SecutiryManager.requestIsAdmin, AdminController.updateProjectCompiler + app.get '/admin', SecurityManager.requestIsAdmin, AdminController.index + app.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor + app.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers + app.post '/admin/writeAllDocsToMongo', SecurityManager.requestIsAdmin, AdminController.writeAllToMongo + app.post '/admin/addquote', SecurityManager.requestIsAdmin, AdminController.addQuote + app.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription + app.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds + app.post '/admin/pollUsersWithDropbox', SecurityManager.requestIsAdmin, AdminController.pollUsersWithDropbox + app.post '/admin/updateProjectCompiler', SecurityManager.requestIsAdmin, AdminController.updateProjectCompiler app.get '/perfTest', (req,res)-> res.send("hello") @@ -190,7 +185,7 @@ module.exports = class Router app.get '/health_check', HealthCheckController.check - app.get "/status/compiler/:Project_id", SecutiryManager.requestCanAccessProject, (req, res) -> + app.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) -> sendRes = _.once (statusCode, message)-> res.writeHead statusCode res.end message @@ -293,15 +288,15 @@ module.exports = class Router client.on 'renameEntity', (entity_id, entityType, newName, callback)-> AuthorizationManager.ensureClientCanEditProject client, (error, project_id) => - collaberationManager.renameEntity(project_id, entity_id, entityType, newName, callback) + EditorController.renameEntity(project_id, entity_id, entityType, newName, callback) client.on 'moveEntity', (entity_id, folder_id, entityType, callback)-> AuthorizationManager.ensureClientCanEditProject client, (error, project_id) => - collaberationManager.moveEntity(project_id, entity_id, folder_id, entityType, callback) + EditorController.moveEntity(project_id, entity_id, folder_id, entityType, callback) client.on 'setProjectName', (window_id, newName, callback)-> AuthorizationManager.ensureClientCanEditProject client, (error, project_id) => - collaberationManager.renameProject(project_id, window_id, newName, callback) + EditorController.renameProject(project_id, window_id, newName, callback) client.on 'getProject',(callback)-> AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => @@ -309,15 +304,15 @@ module.exports = class Router client.on 'setRootDoc', (newRootDocID, callback)-> AuthorizationManager.ensureClientCanEditProject client, (error, project_id) => - collaberationManager.setRootDoc(project_id, newRootDocID, callback) + EditorController.setRootDoc(project_id, newRootDocID, callback) client.on 'deleteProject', (callback)-> AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) => - collaberationManager.deleteProject(project_id, callback) + EditorController.deleteProject(project_id, callback) client.on 'setPublicAccessLevel', (newAccessLevel, callback)-> AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) => - collaberationManager.setPublicAccessLevel(project_id, newAccessLevel, callback) + EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback) client.on 'pdfProject', (opts, callback)-> AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => @@ -328,10 +323,6 @@ module.exports = class Router AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => CompileManager.getLogLines project_id, callback - client.on 'distributMessage', (message)-> - AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => - collaberationManager.distributMessage project_id, client, message - client.on 'changeUsersPrivlageLevel', (user_id, newPrivalageLevel)-> AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) => projectHandler.changeUsersPrivlageLevel project_id, user_id, newPrivalageLevel diff --git a/services/web/app/views/about/about.jade b/services/web/app/views/about/about.jade deleted file mode 100644 index dc66ecec0c..0000000000 --- a/services/web/app/views/about/about.jade +++ /dev/null @@ -1,41 +0,0 @@ -extends ../layout - -block content - .container - .row - .span8.offset2.span-box - .page-header - h1 About us - h3 Meet the team behind your favourite online LaTeX editor - p.team-profile - img(src='/img/about/henry_oswald.jpg') - strong Henry Oswald - | built an experimental LaTeX editor in 2011 which later became ShareLaTeX. He is a trained software engineer who lives in London. - | Henry has been responsible for building up a reliable platform for ShareLaTeX that allows instant real-time collaboration. - | Henry is a strong advocate of Test Driven Development and makes sure we keep the ShareLaTeX code clean and easy to maintain. - p - a(href='https://twitter.com/henryoswald') Follow me on Twitter - p.team-profile - img(src='/img/about/james_allen.jpg') - strong James Allen - | started working with Henry early in 2012 and finished his PhD in theoretical physics early in 2013. James began working on - | one of the first online LaTeX editors, ScribTeX, in 2008 and he has played a large role in developing the technologies and - | concepts that made ScribTeX and now ShareLaTeX possible. James is also slightly too obsessed with typography and learning the internals of how LaTeX works. - p(style="clear: both") - - h3 Motivation - p Our first priority with ShareLaTeX is to build a tool which makes life easier for all the LaTeX users out there. - | The "thank you"s and success stories are a strong motivator for us, and we hope to always be able to offer a fully-functional - | LaTeX editor which anyone can use for free. - p We also believe that charging money for tools like ShareLaTeX is important since it helps to guarantee the future of the site and - | the safety of your work, as well as allowing us to focus on it full time to develop new features. We prefer to provide a valuable - | service at a fair price rather than having to try run the site from adverts, or worse. As our customers, we are answerable to you and only you. - - h3 Technologies - p - | We use a lot of exciting technologies to run ShareLaTeX. We have mutliple APIs behind the scenes that we've written in Node.js, but the one exception is our - a(href='https://github.com/scribtex/clsi') open sourced compiler API. - | This is written in Ruby on Rails. We generally write in - a(href='http://coffeescript.org/') coffee-script - | as we find it helps to speed up development and make our code more readable. Your data is stored in MongoDB, Redis and Amazon S3. We practice Test Driven Development (TDD) to help produce robust and clean code which is easy to refactor. - include ../general/small-footer diff --git a/services/web/app/views/about/attribution.jade b/services/web/app/views/about/attribution.jade deleted file mode 100644 index a513b335fc..0000000000 --- a/services/web/app/views/about/attribution.jade +++ /dev/null @@ -1,31 +0,0 @@ -extends ../layout - -block content - .container - .row - .span6.offset3.span-box - .page-header - h1 Attribution - p - | We've only been able to create ShareLaTeX thanks to the many amazing free and - | open source technologies that exist. Here are some that we have been using and would - | like to say thank you to: - ul - li - a(href="http://nodejs.org/") Node.js - | and the massive set of modules that are available. - li - a(href="https://github.com/jviereck/pdfListView") pdfListView. - | A wrapper for PDF.js that powers our built in PDF viewer. - li - a(href="http://www.iconshock.com/") Icons from Iconshock. - li - a(href="http://twitter.github.com/bootstrap/") Bootstrap - | for letting us survive without being designers. - - include ../general/small-footer - - - - - diff --git a/services/web/app/views/about/planned_maintenance.jade b/services/web/app/views/about/planned_maintenance.jade deleted file mode 100644 index 5fb0b1edde..0000000000 --- a/services/web/app/views/about/planned_maintenance.jade +++ /dev/null @@ -1,16 +0,0 @@ -extends ../layout - -block content - .container - .row - .span6.offset3.span-box - .page-header - h1 Planned Maintenance - p There is currently no planned maintenance - - include ../general/small-footer - - - - - diff --git a/services/web/app/views/about/privacy.jade b/services/web/app/views/about/privacy.jade deleted file mode 100644 index 6a396217a7..0000000000 --- a/services/web/app/views/about/privacy.jade +++ /dev/null @@ -1,26 +0,0 @@ -extends ../layout - -block content - .container - .row - .span8.offset2.span-box - .page-header - h1 Privacy Policy - h3 Information gathering - p When you register for ShareLaTeX we collect information such as your name and email address. ShareLaTeX uses this information to be able to provide our service, for identification and authorization of users, and to be able to contact you. - p The information we collect is not shared with other organisations except as detailed below for the provisioning and improvement of our service. The data we collect will never be sold to third parties for commercial purposes. - - h3 Cookies - p ShareLaTeX uses a cookie, which is a small amount of data stored by your web browser on your computer. The cookie stores your current session and allows you to stay logged in. Cookies are required to use the ShareLaTeX service. - - h3 Data storage - p ShareLaTeX uses third parties to host our services and store your data. You retain all rights to the data you upload to ShareLaTeX. - - h3 Credit cards and billing details - p We use a third party vendor, - a(href="recurly.com") Recurly - | , to store and process credit card transactions. Your email address, credit card details and billing address are passed on to Recurly and are not stored with ShareLaTeX. - - h3 Third party tracking - p We use Google Analytics, Mixpanel and HeapAnalytics to track users' interactions with ShareLaTeX. This data is used for the improvement of our service. - include ../general/small-footer diff --git a/services/web/app/views/about/security.jade b/services/web/app/views/about/security.jade deleted file mode 100644 index cbbfbb4481..0000000000 --- a/services/web/app/views/about/security.jade +++ /dev/null @@ -1,94 +0,0 @@ -extends ../layout - -block content - .container - .row - .span6.offset3.span-box - .page-header - h1 Security - p - | Keeping your data safe is one of our top priorities. - | We work hard to make sure that ShareLaTeX is as secure as we can make it, - | and your input and feedback on our security is always appreciated. - h3 Responsible disclosure - p - | Please send reports of any urgent or sensitive security issues to - a(href="mailto:team@sharelatex.com") team@sharelatex.com. - | Use our - a(href="/sharelatex-security.pub") public key - | to encrypt your message and please provide us with a secure way to - | contact you. - - p - | Note that the URLs at /learn, /help - | and ctan.sharelatex.com are not under our direct control, - | and vulnerabilities should be reported to either MediaWiki or TenderApp - | respectively. - - p - | We are very grateful for all the responsibly reported security vulnerabilities, - | however listing on the hall of fame is reserved for people who report - | vulnerabilities that were previously unknown and we regard as serious. - - h3 Acknowledgements - p - | We'd like to thank the following people who have responsibly disclosed - | vulnerabilities to us and helped improved the security of ShareLaTeX: - - ul - li - a(href="https://twitter.com/Abdulahhusam", rel="nofollow") Abdullah Hussam Gazi - li - a(href="http://adamziaja.com", rel="nofollow") Adam Ziaja - li - a(href="http://alihassanpenetrationtester.blogspot.com/", rel="nofollow") Ali Hasan Ghauri - li - a(href="https://twitter.com/EhArvindSingh") Arvind Singh Shekhawat - li - a(href="https://www.facebook.com/Dakshxss", rel="nofollow") Daksh Patel - li - a(href="https://twitter.com/dibsyhex", rel="nofollow") Dibyendu Sikdar - li - a(href="https://www.facebook.com/jaymark.pestano", rel="nofollow") Jaymark Pestaño - li - a(href="https://twitter.com/korapsyon", rel="nofollow") Jerold Camacho - li - a(href="https://twitter.com/kamilsevi", rel="nofollow") Kamil Sevi - li - a(href="https://twitter.com/kingkaustubhp", rel="nofollow") Kaustubh Padwad - li 'KoF2002' & 'Sr33h4r!(XSS no0B)' - li - a(href="http://twitter.com/umenmactech", rel="nofollow") Manish Bhattacharya - li - a(href="http://twitter.com/Manjesh24", rel="nofollow") Manjesh S - li - a(href="https://www.facebook.com/Shahmeer.1994", rel="nofollow") Muhammad Shahmeer - li - a(href="http://nbsriharsha.blogspot.in", rel="nofollow") N B Sri Harsha - li - a(href="http://www.linkedin.com/in/osandamalith", rel="nofollow") Osanda Malith Jayathissa - li - a(href="https://twitter.com/prasadk14", rel="nofollow") Prasad Kancharla - li - a(href="https://www.facebook.com/c0m4dr3404", rel="nofollow") Praveen Nair (Kerala Cyber Squad - India) - li - a(href="https://twitter.com/iAmPr3m", rel="nofollow") Prem Kumar - li - a(href="https://www.facebook.com/HardNocksHittnHard", rel="nofollow") Sherin Panikar (Kerala Cyber Squad - India) - li - a(href="https://twitter.com/Simon90_Italy", rel="nofollow") Simone Memoli - li - a(href="https://twitter.com/tareksiddiki", rel="nofollow") Tarek Siddiki - li - a(href="https://twitter.com/venugopalt", rel="nofollow") Venugopal Thotakura - li - a(href="http://softproweb.blogspot.com/", rel="nofollow") Waqeeh Ul Hasan - li - a(href="https://twitter.com/zerodayguys", rel="nofollow") Zeroday Guys (Rakesh Singh & V.Harish Kumar) - li - a(href="https://twitter.com/_prashantnegi", rel="nofollow") Prashant Negi - span and - a(href="https://twitter.com/AjaySinghNegi", rel="nofollow") Singh Negi - li - a(href="https://twitter.com/mohitnitrr") Monendra Sahu - include ../general/small-footer diff --git a/services/web/app/views/about/tos.jade b/services/web/app/views/about/tos.jade deleted file mode 100644 index fa525ac68a..0000000000 --- a/services/web/app/views/about/tos.jade +++ /dev/null @@ -1,34 +0,0 @@ -extends ../layout - -block content - .container - .row - .span8.offset2.span-box - .page-header - h1 Terms of Service - p - | Thank you for taking the time to read our terms of service. - | We've tried to keep it simple, but you must agree to these terms in order to use ShareLaTeX. - h3 You must provide a valid email address - p - | We expect you to register with a valid email address. - | This will be our only way to communicate with you. - | Changes to these terms of service and any other notifications about updates - | to the service will be sent to the email address you provide. - | We will not be held responsible for any information not being received. - h3 You own your content, not us - p You retain all ownership, copyright and intellectual property rights to any content uploaded to ShareLaTeX. Your content will only be shared with other users of your choosing and we will never share your content with third parties without your consent. The staff of ShareLaTeX have access to your content, but we make an effort to only access it when absolutely necessary. - h3 You're not allowed to abuse the service - p ShareLaTeX is provided assuming users will act in good faith. However, we retain the right to remove any account or content that we feel is abusing the service. In the rare event of this happening, it will most likely be for one of the following reasons: using the service for illegal reasons; uploading illegal, unauthorised or objectionable content; consuming an excessive amount of computing resources. - h3 We're not infallible - p ShareLaTeX is provided on an 'as is' basis, without future promise of availability. ShareLaTeX is not liable for any damages caused by the loss or inability to access your data. - p [Founder's note: We try hard to provide a service which is reliable, secure and regularly backed up. While the above paragraph sounds grim, we need it there to cover ourselves incase something does go wrong. We certainly aren't planning to need to fall back on it. This assurance aside, it's always good practice to back up your own data.] - h3 Cancellation and refunds policy - p ShareLaTeX's services are paid for monthly or yearly in advance and are non-refundable. There will be no refunds or credits for partial months/years of service or downgrades. Subscription downgrades and cancellations will come into effect when your next payment is due. Upgrades to your plan will be billed immediately at the new rate for the remainder of the billing period. You may cancel your account at any time from your account subscription settings. - h3 Paypal Reference Transactions - p PayPal Reference Transactions are used to allow ShareLaTeX to make subsequent transactions on a monthly or annual basis until you cancel your account. - h3 Pricing changes - p We will notify you of any changes to our pricing plans that affect you at least 30 days before your next payment is due. We will notify you via the email address you provided when subscribing for a paid plan. - h3 You must be human - p To protect ourselves from accidental or malicious abuse, automated scripts and programs are not permitted to register an account or access the service. - include ../general/small-footer diff --git a/services/web/app/views/homepage/comments.jade b/services/web/app/views/homepage/comments.jade deleted file mode 100644 index a73b194f84..0000000000 --- a/services/web/app/views/homepage/comments.jade +++ /dev/null @@ -1,46 +0,0 @@ -include header - -mixin quote(quote, author) - blockquote - p #{quote} - small #{author} - -extends ../layout - -block content - .container.box - .page-header - h2 What a few of the thousands of people using sharelatex are saying - .row - .span4 -
- .span4 -Yes, finally! @sharelatex sharelatex.com via @henryoswald
— Nav (@keyboardkitteh) February 4, 2012
- .span4 -Finally someone programmed it: The Real Time LaTeX Collaborative Editor in Your Browser sharelatex.com
— Bar Fooz (@barfooz) February 4, 2012
- .row - .span4 -Wow, really awesome real time LaTeX editor sharelatex.com /via @cybunk
— Nicolas (@philogb) February 10, 2012
- .span4 -Its #GoogleDocs but with added #LaTeX, sharelatex.com. For physicists and astronomers, collaborative editing just became possible.
— Alasdair Allan (@aallan) February 6, 2012
- .span4 -ShareLaTeX, a real time LaTeX collaborative editor. I *have* to try this. sharelatex.com HT: @yokofakun
— Luis Apiolaza (@zentree) February 15, 2012
- .row - .span4 -Finally! #ShareLaTeX - Real Time LaTeX Collaborative Editor ShareLaTeX.com (via @infoveille @gallypette @UrfistNice) @AdrienSaladin
— Pierre Poulain (@pierrepo) February 9, 2012
- .span4 -LaTeX geeks, here's a collaborative online LaTeX editor sharelatex.com
— Stephen Kinsella (@stephenkinsella) February 7, 2012
- .span4 -sharelatex.comamazing!
— Guillermo Rauch (@rauchg) February 10, 2012
- .row - .span4 -ShareLatex sharelatex.com FInally, the real time collaboration in browser! #latex
— Ivana Kojovic (@muvica1987) February 5, 2012
- .span4 -I just fell in love on my first run with @sharelatex sharelatex.com via @henryoswald . CC @timcameronryan
— Cory Dolphin (@wcdolphin) May 10, 2012
- .span4 -Tired of sending TeX files back and forth with your coauthors?Try sharelatex to collaborate on tex files in real time bit.ly/xcksY7
— Max Lin (@m4xl1n) February 20, 2012
- - - - - locals.supressDefaultJs = true - script(data-main=jsPath+'main.js', src='js/libs/require.js', baseurl=jsPath) diff --git a/services/web/app/views/homepage/header.jade b/services/web/app/views/homepage/header.jade deleted file mode 100644 index 0195b3090a..0000000000 --- a/services/web/app/views/homepage/header.jade +++ /dev/null @@ -1,15 +0,0 @@ -.container - #logoArea - .span8 - h1.logo.large - a(href='/').plain ShareLaTeX - span.sub.logo Online LaTeX Editor - .span3#homePagePills - ul.nav.nav-pills - li - a(href='/') Home - li - a(href='/blog') Blog - li - a(href='/comments') Comments - diff --git a/services/web/app/views/homepage/home.jade b/services/web/app/views/homepage/home.jade deleted file mode 100644 index 8b70d7b1b9..0000000000 --- a/services/web/app/views/homepage/home.jade +++ /dev/null @@ -1,64 +0,0 @@ -link(rel='canonical', href='https://www.sharelatex.com') - -extends ../layout - -block content - .masthead - .container - .row - .title.span8 - h1 Online LaTeX Editor - h2 Quickly start using LaTeX and work together in real-time - div - img(src="/img/screenshot.png") - .register.span4 - h2 Register now - .messageArea - include ../general/partial/registerForm - - .universities - .container - .row - p Used by students and academics at: - #slides - .clearfix - .span3 - img(src="/img/crests/harvard.gif", alt="harvard university logo") - .span3 - img(src="/img/crests/mit.gif", alt="mit university logo") - .span3 - img(src="/img/crests/oxford.gif", alt="oxford university logo") - .span3 - img(src="/img/crests/tokyo.png", alt="tokyo university logo") - .clearfix - .span3 - img(src="/img/crests/cambridge.png", alt="cambridge university logo") - .span3 - img(src="/img/crests/liverpool.jpg", alt="liverpool university logo") - .span3 - img(src="/img/crests/icl.png", alt="icl university logo") - .span3 - img(src="/img/crests/yale.png", alt="yale university logo") - .clearfix - .span3 - img(src="/img/crests/durham.png", alt="durham university logo") - .span3 - img(src="/img/crests/nasa.png", alt="nasa university logo") - .span3 - img(src="/img/crests/toronto.gif", alt="toronto university logo") - .span3 - img(src="/img/crests/stanford.png", alt="stanford university logo") - - - .container - include ../general/long-form-features - - .row - .span12.lower-signup-button - a(href="/user/subscription/plans").btn.btn-success.btn-huge Sign up now! - - include ../general/social-footer - include ../general/small-footer - - - diff --git a/services/web/app/views/homepage/socialMedia.jade b/services/web/app/views/homepage/socialMedia.jade deleted file mode 100644 index 839a4804a0..0000000000 --- a/services/web/app/views/homepage/socialMedia.jade +++ /dev/null @@ -1,7 +0,0 @@ - -.span2.offset3 - include ../referal/tweet -.span2 - include ../referal/googleplus -.span1 - include ../referal/facebookLike diff --git a/services/web/app/views/layout.jade b/services/web/app/views/layout.jade index 74ac1276cd..a849944252 100644 --- a/services/web/app/views/layout.jade +++ b/services/web/app/views/layout.jade @@ -28,7 +28,7 @@ html(itemscope, itemtype='http://schema.org/Product') ga('send', 'pageview'); - else script(type='text/javascript') - window.ga = function() {}; + window.ga = function() { console.log("Sending to GA", arguments) }; script window.csrfToken = "#{csrfToken}"; diff --git a/services/web/app/views/menubar.jade b/services/web/app/views/menubar.jade index 6f4c563d21..8b91d7d2a0 100644 --- a/services/web/app/views/menubar.jade +++ b/services/web/app/views/menubar.jade @@ -36,8 +36,9 @@ ul.dropdown-menu li a(href='/user/settings').userSettingsLink User Settings - li - a(href='/user/subscription').subscriptionLink Subscription + - if (settings.enableSubscriptions) + li + a(href='/user/subscription').subscriptionLink Subscription li a(href='/logout').logoutLink Logout -else diff --git a/services/web/app/views/referal/bonus.jade b/services/web/app/views/referal/bonus.jade index a3ca5ae685..9d4e742800 100644 --- a/services/web/app/views/referal/bonus.jade +++ b/services/web/app/views/referal/bonus.jade @@ -41,6 +41,11 @@ block content .title a(href='#link-modal', data-toggle="modal").link Link to us from your website + .row + .span4.offset4.bonus-banner + h2.direct-link Direct Link + .well #{buildReferalUrl("d")} + .row.ab-bonus .span6.offset3 p.thanks When someone starts using ShareLaTeX after your recommendation we'll give you some free stuff to say thanks! Check your progress below. @@ -103,7 +108,7 @@ block content method: 'feed', redirect_uri: 'https://www.sharelatex.com', link: '!{buildReferalUrl("fb")}', - picture: 'https://www.sharelatex.com/img/logo/logosmall.png', + picture: 'https://www.sharelatex.com/brand/logo/logosmall.png', name: 'ShareLaTeX - Online LaTeX Editor', caption: 'Free Unlimited Projects and Compiles', description: 'ShareLaTeX is a free online LaTeX Editor. Real time collaboration like Google Docs, with Dropbox, history and auto-complete' diff --git a/services/web/app/views/referal/facebookWallPost.jade b/services/web/app/views/referal/facebookWallPost.jade index a07cf1ccfb..d6a0856525 100644 --- a/services/web/app/views/referal/facebookWallPost.jade +++ b/services/web/app/views/referal/facebookWallPost.jade @@ -11,7 +11,7 @@ script(type='text/javascript') method: 'feed', redirect_uri: 'https://www.sharelatex.com', link: '#{buildReferalUrl()}', - picture: 'https://www.sharelatex.com/img/logo/logosmall.png', + picture: 'https://www.sharelatex.com/brand/logo/logosmall.png', name: 'ShareLaTeX - Online LaTeX Editor', caption: 'Free Unlimited Projects and Compiles', description: 'ShareLaTeX is a free Online LaTeX Editor. Real time collaboration like Google Docs, with Dropbox, history and Auto Complete' diff --git a/services/web/app/views/resources.jade b/services/web/app/views/resources.jade deleted file mode 100644 index 101175736d..0000000000 --- a/services/web/app/views/resources.jade +++ /dev/null @@ -1,59 +0,0 @@ -extends layout - -block content - .container - .row - .span12.span-box - .page-header - h1 LaTeX Resources - small We are the LaTeX Editor, here are some other LaTeX resources we like - div - ul - li - a(href='http://en.wikibooks.org/wiki/LaTeX') LaTeX Wikibook - span - Covering 95% of what you need to know about LaTeX in a clear and simple way with great examples you can often copy and paste - li - a(href='http://detexify.kirelabs.org/classify.html') Detexify - span - A great way of finding LaTeX symbols - li - a(href='http://www.tug.dk/FontCatalogue/seriffonts.html') LaTeX Fonts - span - A collection of LaTeX fonts - li - a(href='http://mathurl.com/') MathUrl - span - allows for live equation editing - li - a(href='http://webdemo.visionobjects.com/equation.html') WebEquation - span - draw the symbol on the screen to see the LaTeX equivalent - li - a(href='http://www.texample.net/') TeXample.net - span - a great site for tikz reference - li - a(href='http://www.howtotex.com/') howtoTeX.com - span - a useful collection of templates, tutorials and how-tos - li - a(href='http://truben.no/latex/table/') LaTeX table editor - span - if you struggle with tables in LaTeX this tool gives you a nice 'excel like' view to help you along - li - a(href="http://www.math.binghamton.edu/erik/beameruserguide.pdf") Beamer user guide - span - A good guide into beamer - div - h3 Mobile Apps - ul - li - a(href='https://play.google.com/store/apps/details?id=coolcherrytrees.software.detexify&feature=search_result#?t=W251bGwsMSwxLDEsImNvb2xjaGVycnl0cmVlcy5zb2Z0d2FyZS5kZXRleGlmeSJd') Android - li - a(href='https://itunes.apple.com/app/detexify/id328805329?mt=8') Detexify for ios - li - a(href='http://www.windowsphone.com/en-us/store/app/detexify/3e6813bd-04b1-455b-bc14-14dfe904c54b') Detexify for Windows Phone - li - a(href='http://www.windowsphone.com/en-us/store/app/repotex/694085e0-e825-425b-a40d-40bce5cecb3b') Repotex - li - a(href='http://www.windowsphone.com/en-us/store/app/fasttexdraw/3e1b4255-7798-45ec-b679-a43e74e82769') FastTeXDraw for windows phone - - hr - div - | If you know of other resources, please - a.js-tender-widget(href='#') recommend - them to us. - include ./general/small-footer - diff --git a/services/web/app/views/subscriptions/group_admin.jade b/services/web/app/views/subscriptions/group_admin.jade index a910312120..823b57beeb 100644 --- a/services/web/app/views/subscriptions/group_admin.jade +++ b/services/web/app/views/subscriptions/group_admin.jade @@ -15,6 +15,7 @@ block content thead tr th + input(type="checkbox").select-all th email th Name th Registered @@ -23,7 +24,7 @@ block content -each user in users tr td - input(type="checkbox") + input(type="checkbox").select-one td #{user.email} td #{user.first_name} #{user.last_name} td #{!user.holdingAccount} diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade index cb582e7c42..386ecb51a3 100644 --- a/services/web/app/views/templates.jade +++ b/services/web/app/views/templates.jade @@ -68,6 +68,7 @@ input.rename.js-rename .dropdown-caret i.icon-chevron-down + .entity-label.label.label-success script(type="text/template")#folderTemplate .entity-list-item(class="entity-{{ type }}", entity-type="{{ type }}", id="{{ id }}") @@ -80,6 +81,7 @@ input.rename.js-rename .dropdown-caret i.icon-chevron-down + .entity-label.label.label-success script(type="text/template")#entityListTemplate .contents @@ -180,7 +182,11 @@ button#downloadPdf.btn Download button#downloadLinksButton.btn.dropdown-toggle(data-toggle="dropdown") span.caret - ul.dropdown-menu#downloadLinks + ul.dropdown-menu + #downloadLinks + li.divider + li.delete-cached-files + a(href="#") Clear cached files .btn-group.pull-right(data-toggle="buttons-radio") button(type="button", title="Flat view")#flatViewButton.btn i.icon-flatview @@ -286,32 +292,31 @@ script(type="text/template")#publishProjectTemplate - -if(session && session.user && session.user.isAdmin) - .box - .page-header - h2 Publish project as template - - #publishedAsTemplateArea.show-when-published.alert.alert-success - p - .btn.btn-warning#unPublishProjectAsTemplate.pull-right Unpublish - i.icon-ok - | Your project is currently published. - a#templateLink(href='{{canonicalUrl}}') View in template gallery. - p - | Lastest version: {{publishedDate}}. + .box + .page-header + h2 Publish project as template + + #publishedAsTemplateArea.show-when-published.alert.alert-success + p + .btn.btn-warning#unPublishProjectAsTemplate.pull-right Unpublish + i.icon-ok + | Your project is currently published. + a#templateLink(href='{{canonicalUrl}}') View in template gallery. + p + | Lastest version: {{publishedDate}}. - #problemWithPublishingArea - p There is a problem with our publishing service, please try again in a few minutes. - #publishWorkingArea - p Working... - div.show-when-published.show-when-unpublished.project-description - label(for="project-description") Description - .row-fluid - textarea(placeholder="Template description", name="project-description").span12#projectDescription {{description}} - #unpublishedAsTemplateArea.show-when-unpublished - .btn.btn-success#publishProjectAsTemplate Publish - p.show-when-published - button.btn.btn-success#republishProjectAsTemplate Re-Publish + #problemWithPublishingArea + p There is a problem with our publishing service, please try again in a few minutes. + #publishWorkingArea + p Working... + div.show-when-published.show-when-unpublished.project-description + label(for="project-description") Description + .row-fluid + textarea(placeholder="Template description", name="project-description").span12#projectDescription {{description}} + #unpublishedAsTemplateArea.show-when-unpublished + .btn.btn-success#publishProjectAsTemplate Publish + p.show-when-published + button.btn.btn-success#republishProjectAsTemplate Re-Publish script(type="text/template")#settingsPanelTemplate @@ -425,6 +430,10 @@ div a(href="#", title='Show Hot Keys List')#hotkeysLink Hot keys + script(type='text/template')#DebugLinkTemplate + div + a(href="#", title='Show Debug Information')#debugLink Debug + script(type='text/template')#trackChangesPanelTemplate #trackChangesPanel .track-changes-side-bar @@ -434,6 +443,12 @@ i.icon-remove .change-list-area .track-changes-diff + .track-changes-upgrade-popup(style="display: none;") + .message.show-when-owner + p You need to upgrade your plan to use the History feature. + button.btn.btn-primary.start-free-trial Start free trial + .message.show-when-not-owner + p Please ask the project owner to upgrade to use the History feature. script(type='text/template')#trackChangesDiffTemplate .track-changes-diff-toolbar.btn-toolbar diff --git a/services/web/cleanup.js b/services/web/cleanup.js new file mode 100644 index 0000000000..9d6d88285c --- /dev/null +++ b/services/web/cleanup.js @@ -0,0 +1,11 @@ +var keys = require('./app/js/infrastructure/Keys'); +var settings = require('settings-sharelatex'); +var queueName = process.argv[2]; +var projectQueueName = process.argv[3]; +var queue = require('fairy').connect(settings.redis.web).queue(queueName); +console.log("cleaning up queue "+ queueName + " " + projectQueueName); +queue._requeue_group(projectQueueName); + +//fairy should kill the process but just in case +thirtySeconds = 30 * 1000 +setTimeout(process.exit, thirtySeconds) diff --git a/services/web/package.json b/services/web/package.json index d646f2f8f6..35a55cffca 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -5,40 +5,36 @@ "public": "./public" }, "dependencies": { - "heapdump": "0.1.0", "express": "3.3.4", - "mongoose": "3.6.19", + "mongoose": "3.8.8", + "mongojs": "0.10.1", "jade": "0.28.1", - "validator": "0.4.22", - "underscore": "1.4.4", - "node-fs": "0.1.5", - "rimraf": "2.1.2", + "underscore": "1.6.0", + "rimraf": "2.2.6", "connect-redis": "1.4.5", - "redis": "0.8.4", - "request": "2.14.0", + "redis": "0.10.1", + "request": "2.34.0", "xml2js": "0.2.0", "dateformat": "1.0.4-1.2.3", - "optimist": "0.3.5", - "async": "0.2.9", - "lynx": "0.0.11", + "optimist": "0.6.1", + "async": "0.6.2", + "lynx": "0.1.1", "session.socket.io": "0.1.4", - "socket.io": "0.9.15", - "mimelib": "0.2.8", - "bufferedstream": "1.4.1", - "mixpanel": "0.0.18", + "socket.io": "0.9.16", + "mimelib": "0.2.14", + "bufferedstream": "1.6.0", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master", "soa-req-id": "git+https://github.com/sharelatex/soa-req-id.git#master", "fairy": "0.0.2", - "node-uuid": "1.4.0", - "mongojs": "0.9.8", + "node-uuid": "1.4.1", "nodemailer": "0.6.1", "bcrypt": "0.7.5", "archiver": "0.5.1", - "nodetime": "0.8.15", "mocha": "1.17.1", - "redback": "0.3.7" + "redback": "0.4.0", + "sanitizer": "0.1.1" }, "devDependencies": { "chai": "", diff --git a/services/web/public/coffee/SubscriptionGroupsManager.coffee b/services/web/public/coffee/SubscriptionGroupsManager.coffee index 75460535d9..5a95b5803a 100644 --- a/services/web/public/coffee/SubscriptionGroupsManager.coffee +++ b/services/web/public/coffee/SubscriptionGroupsManager.coffee @@ -7,12 +7,12 @@ require [ tableRowTemplate = '''#edtech #mathchat Collaborative LaTeX document editing (like Google docs, but for LaTeX!) sharelatex.com
— Joe DiNoto (@mathteacher1729) March 31, 2012
+ user-agent: #{useragent}
+ server-id: #{server_id}
+ transport: #{transport}
+
+ """
+ buttons: [
+ text: "OK"
+ ]
+ )
\ No newline at end of file
diff --git a/services/web/public/coffee/editor/Document.coffee b/services/web/public/coffee/editor/Document.coffee
index d08d4baca4..56c3aa384f 100644
--- a/services/web/public/coffee/editor/Document.coffee
+++ b/services/web/public/coffee/editor/Document.coffee
@@ -81,6 +81,17 @@ define [
delete @_joinCallbacks
_onUpdateApplied: (update) ->
+ @ide.pushEvent "received-update",
+ doc_id: @doc_id
+ remote_doc_id: update?.doc
+ wantToBeJoined: @wantToBeJoined
+ update: update
+
+ if Math.random() < (@ide.disconnectRate or 0)
+ console.log "Simulating disconnect"
+ @ide.connectionManager.disconnect()
+ return
+
if update?.doc == @doc_id and @doc?
@doc.processUpdateFromServer update
@@ -93,6 +104,8 @@ define [
@doc?.updateConnectionState "disconnected"
_onReconnect: () ->
+ @ide.pushEvent "reconnected:afterJoinProject"
+
@connected = true
if @wantToBeJoined or @doc?.hasBufferedOps()
@_joinDoc (error) =>
@@ -137,10 +150,37 @@ define [
_bindToShareJsDocEvents: () ->
@doc.on "error", (error, meta) => @_onError error, meta
- @doc.on "externalUpdate", () => @trigger "externalUpdate"
- @doc.on "remoteop", () => @trigger "remoteop"
- @doc.on "op:sent", () => @trigger "op:sent"
- @doc.on "op:acknowledged", () => @trigger "op:acknowledged"
+ @doc.on "externalUpdate", () =>
+ @ide.pushEvent "externalUpdate",
+ doc_id: @doc_id
+ @trigger "externalUpdate"
+ @doc.on "remoteop", () =>
+ @ide.pushEvent "remoteop",
+ doc_id: @doc_id
+ @trigger "remoteop"
+ @doc.on "op:sent", (op) =>
+ @ide.pushEvent "op:sent",
+ doc_id: @doc_id
+ op: op
+ @trigger "op:sent"
+ @doc.on "op:acknowledged", (op) =>
+ @ide.pushEvent "op:acknowledged",
+ doc_id: @doc_id
+ op: op
+ @trigger "op:acknowledged"
+ @doc.on "op:timeout", (op) =>
+ @ide.pushEvent "op:timeout",
+ doc_id: @doc_id
+ op: op
+ @trigger "op:timeout"
+ ga?('send', 'event', 'error', "op timeout", "Op was now acknowledged - #{ide.socket.socket.transport.name}" )
+ @ide.connectionManager.reconnectImmediately()
+ @doc.on "flush", (inflightOp, pendingOp, version) =>
+ @ide.pushEvent "flush",
+ doc_id: @doc_id,
+ inflightOp: inflightOp,
+ pendingOp: pendingOp
+ v: version
_onError: (error, meta = {}) ->
console.error "ShareJS error", error, meta
diff --git a/services/web/public/coffee/editor/Editor.coffee b/services/web/public/coffee/editor/Editor.coffee
index f05f7ed222..de2860c6d4 100644
--- a/services/web/public/coffee/editor/Editor.coffee
+++ b/services/web/public/coffee/editor/Editor.coffee
@@ -71,7 +71,7 @@ define [
_saveSplitterState: () ->
if $("#editorSplitter").is(":visible")
state = $("#editorSplitter").layout().readState()
- eastWidth = state.east.size
+ eastWidth = state.east.size + $("#editorSplitter .ui-layout-resizer-east").width()
percentWidth = eastWidth / $("#editorSplitter").width() * 100 + "%"
state.east.size = percentWidth
$.localStorage("layout.editor", state)
diff --git a/services/web/public/coffee/editor/ShareJsDoc.coffee b/services/web/public/coffee/editor/ShareJsDoc.coffee
index 78d3232994..04485c9602 100644
--- a/services/web/public/coffee/editor/ShareJsDoc.coffee
+++ b/services/web/public/coffee/editor/ShareJsDoc.coffee
@@ -98,7 +98,7 @@ define [
v: update.v
op_sent_at: new Date()
timer = setTimeout () =>
- @_handleError new Error("Doc op was not acknowledged in time"), meta
+ @trigger "op:timeout", update
, @INFLIGHT_OP_TIMEOUT
@_doc.inflightCallbacks.push () =>
clearTimeout timer
@@ -109,11 +109,16 @@ define [
_bindToDocChanges: (doc) ->
submitOp = doc.submitOp
doc.submitOp = (args...) =>
- @trigger "op:sent"
+ @trigger "op:sent", args...
doc.pendingCallbacks.push () =>
- @trigger "op:acknowledged"
+ @trigger "op:acknowledged", args...
submitOp.apply(doc, args)
+ flush = doc.flush
+ doc.flush = (args...) =>
+ @trigger "flush", doc.inflightOp, doc.pendingOp, doc.version
+ flush.apply(doc, args)
+
_.extend(ShareJsDoc::, Backbone.Events)
return ShareJsDoc
diff --git a/services/web/public/coffee/file-tree/EntityView.coffee b/services/web/public/coffee/file-tree/EntityView.coffee
index 073dbda345..6b4dae2c7c 100644
--- a/services/web/public/coffee/file-tree/EntityView.coffee
+++ b/services/web/public/coffee/file-tree/EntityView.coffee
@@ -15,6 +15,7 @@ define [
events: () ->
events = {}
events["click ##{@model.id} > .js-clickable"] = "parentOnClick"
+ events["click ##{@model.id} > .entity-label"] = "parentOnClick"
events["click .dropdown-caret"] = "showContextMenuFromCaret"
events["contextmenu"] = "showContextMenuFromRightClick"
return events
@@ -29,6 +30,7 @@ define [
@$nameEl = @$(".name")
@$inputEl = @$("input.js-rename")
@$entityListItemEl = @$el.children(".entity-list-item")
+ @$labelEl = @$entityListItemEl.children(".entity-label")
_makeEditable: () ->
if @ide.isAllowedToDoIt "readAndWrite"
@@ -48,6 +50,17 @@ define [
@$nameEl.hide()
@$inputEl.show()
+ setLabels: (labels) ->
+ label = labels[@model.get("id")]
+ if label?
+ @$entityListItemEl.addClass("show-label")
+ @$labelEl.text("±")
+ return true
+ else
+ @$entityListItemEl.removeClass("show-label")
+ @$labelEl.text("")
+ return false
+
select: () ->
@selected = true
@$entityListItemEl.addClass("selected")
diff --git a/services/web/public/coffee/file-tree/FileTreeManager.coffee b/services/web/public/coffee/file-tree/FileTreeManager.coffee
index 81d40687a9..942d8b60b2 100644
--- a/services/web/public/coffee/file-tree/FileTreeManager.coffee
+++ b/services/web/public/coffee/file-tree/FileTreeManager.coffee
@@ -75,6 +75,7 @@ define [
children.add(entity)
openDoc: (doc, line) ->
+ return if !doc?
doc_id = doc.id or doc
@trigger "open:doc", doc_id, line: line
@selectEntity(doc_id)
@@ -158,6 +159,7 @@ define [
renameEntity: (entity, name) ->
+ name = name?.trim()
@ide.socket.emit 'renameEntity', entity.id, entity.get("type"), name
entity.set("name", name)
@@ -196,7 +198,7 @@ define [
el = $($("#newEntityModalTemplate").html())
input = el.find("input")
create = _.once () =>
- name = input.val()
+ name = input.val()?.trim()
if name != ""
callback(name)
modal = new Modal
@@ -277,6 +279,5 @@ define [
entity.collection?.remove(entity)
delete @views[entity_id]
-
-
-
+ setLabels: (labels) ->
+ @view.setLabels(labels)
diff --git a/services/web/public/coffee/file-tree/FileTreeView.coffee b/services/web/public/coffee/file-tree/FileTreeView.coffee
index a7439dee08..d06dfe52d4 100644
--- a/services/web/public/coffee/file-tree/FileTreeView.coffee
+++ b/services/web/public/coffee/file-tree/FileTreeView.coffee
@@ -19,4 +19,7 @@ define [
@rootFolderView = new RootFolderView(model: rootFolder, manager: @manager)
entities.append(@rootFolderView.$el)
@rootFolderView.render()
+
+ setLabels: (labels) ->
+ @rootFolderView.setLabels(labels)
diff --git a/services/web/public/coffee/file-tree/FolderView.coffee b/services/web/public/coffee/file-tree/FolderView.coffee
index 716e1429f2..e8ad803034 100644
--- a/services/web/public/coffee/file-tree/FolderView.coffee
+++ b/services/web/public/coffee/file-tree/FolderView.coffee
@@ -107,11 +107,13 @@ define [
@$contents.hide()
@$toggle.find(".js-open").hide()
@$toggle.find(".js-closed").show()
+ @$entityListItemEl.removeClass("folder-open")
showEntries: () ->
@$contents.show()
@$toggle.find(".js-open").show()
@$toggle.find(".js-closed").hide()
+ @$entityListItemEl.addClass("folder-open")
onToggle: (e) ->
e.preventDefault()
@@ -146,3 +148,17 @@ define [
@manager.showUploadFileModal(@model)
}]
+ setLabels: (labels) ->
+ showLabel = false
+ for entity in @views
+ if entity.setLabels(labels)
+ showLabel = true
+
+ if showLabel
+ @$entityListItemEl.addClass("show-label")
+ @$labelEl.text("±")
+ return true
+ else
+ @$entityListItemEl.removeClass("show-label")
+ @$labelEl.text("")
+ return false
diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee
index a4980cae6a..e58a5cc27b 100644
--- a/services/web/public/coffee/ide.coffee
+++ b/services/web/public/coffee/ide.coffee
@@ -26,6 +26,7 @@ define [
"tour/IdeTour"
"analytics/AnalyticsManager"
"track-changes/TrackChangesManager"
+ "debug/DebugManager"
"ace/ace"
"libs/jquery.color"
"libs/jquery-layout"
@@ -59,6 +60,7 @@ define [
IdeTour,
AnalyticsManager,
TrackChangesManager
+ DebugManager
) ->
@@ -117,10 +119,10 @@ define [
@cursorManager = new CursorManager(@)
@fileViewManager = new FileViewManager(@)
@analyticsManager = new AnalyticsManager(@)
- if @userSettings.trackChanges
- @trackChangesManager = new TrackChangesManager(@)
- else
+ if @userSettings.oldHistory
@historyManager = new HistoryManager(@)
+ else
+ @trackChangesManager = new TrackChangesManager(@)
@setLoadingMessage("Connecting")
firstConnect = true
@@ -159,14 +161,18 @@ define [
buttons: [ text: "OK" ]
}
+ recentEvents: []
+
+ pushEvent: (type, meta = {}) ->
+ @recentEvents.push type: type, meta: meta, date: new Date()
+ if @recentEvents.length > 40
+ @recentEvents.shift()
+
reportError: (error, meta = {}) ->
meta.client_id = @socket?.socket?.sessionid
meta.transport = @socket?.socket?.transport?.name
meta.client_now = new Date()
- meta.last_connected = @connectionManager.lastConnected
- meta.second_last_connected = @connectionManager.secondLastConnected
- meta.last_disconnected = @connectionManager.lastDisconnected
- meta.second_last_disconnected = @connectionManager.secondLastDisconnected
+ meta.recent_events = @recentEvents
errorObj = {}
for key in Object.getOwnPropertyNames(error)
errorObj[key] = error[key]
@@ -194,6 +200,7 @@ define [
ide.hotkeysManager = new HotkeysManager ide
ide.layoutManager.resizeAllSplitters()
ide.tourManager = new IdeTour ide
+ ide.debugManager = new DebugManager(ide)
ide.savingAreaManager =
$savingArea : $('#saving-area')
diff --git a/services/web/public/coffee/ide/ConnectionManager.coffee b/services/web/public/coffee/ide/ConnectionManager.coffee
index 18be9e007e..ae54c7946b 100644
--- a/services/web/public/coffee/ide/ConnectionManager.coffee
+++ b/services/web/public/coffee/ide/ConnectionManager.coffee
@@ -8,15 +8,13 @@ define [
@socket = @ide.socket
@socket.on "connect", () =>
@connected = true
- @secondLastConnected = @lastConnected
- @lastConnected = new Date()
+ @ide.pushEvent("connected")
@hideModal()
@cancelReconnect()
@socket.on 'disconnect', () =>
@connected = false
- @secondLastDisconnected = @lastDisconnected
- @lastDisconnected = new Date()
+ @ide.pushEvent("disconnected")
@ide.trigger "disconnect"
setTimeout(=>
ga('send', 'event', 'editor-interaction', 'disconnect')
@@ -35,6 +33,13 @@ define [
e.preventDefault()
@tryReconnect()
@hideModal()
+
+ reconnectImmediately: () ->
+ @disconnect()
+ @tryReconnect()
+
+ disconnect: () ->
+ @socket.disconnect()
showModalAndStartAutoReconnect: () ->
@hideModal()
diff --git a/services/web/public/coffee/list.coffee b/services/web/public/coffee/list.coffee
index c800353733..b56e7c52bf 100644
--- a/services/web/public/coffee/list.coffee
+++ b/services/web/public/coffee/list.coffee
@@ -144,7 +144,7 @@ require [
$confirm.click (e) =>
$confirm.attr("disabled", true)
$confirm.text("Creating...")
- projectName = $modal.find('input').val()
+ projectName = $modal.find('input').val()?.trim()
$.ajax
url: '/project/new'
type:'POST'
diff --git a/services/web/public/coffee/pdf/CompiledView.coffee b/services/web/public/coffee/pdf/CompiledView.coffee
index cae6cbb948..770524f1ce 100644
--- a/services/web/public/coffee/pdf/CompiledView.coffee
+++ b/services/web/public/coffee/pdf/CompiledView.coffee
@@ -51,6 +51,7 @@ define [
"click #splitViewButton": ->
$.localStorage("layout.pdf", "split")
@options.manager.switchToSplitView()
+ "click .delete-cached-files > a": -> @options.manager.deleteCachedFiles()
initialize: (@options) ->
@ide = @options.ide
@@ -90,7 +91,7 @@ define [
logButtonHtml = "Logs"
if compileErrors?
- for error in compileErrors.all
+ for error in compileErrors.errors.concat(compileErrors.warnings).concat(compileErrors.typesetting)
errorView = new LatexErrorView(@options.manager.ide, error)
errorView.render()
@errorViews.push(errorView)
diff --git a/services/web/public/coffee/pdf/PdfManager.coffee b/services/web/public/coffee/pdf/PdfManager.coffee
index 8315a6a311..03daaddc32 100644
--- a/services/web/public/coffee/pdf/PdfManager.coffee
+++ b/services/web/public/coffee/pdf/PdfManager.coffee
@@ -187,3 +187,26 @@ define [
downloadPdf: () ->
@ide.mainAreaManager.setIframeSrc "/project/#{@ide.project_id}/output/output.pdf?popupDownload=true"
+
+ deleteCachedFiles: () ->
+ modal = new Modal
+ title: "Clear cache?"
+ message: "This will clear all hidden LaTeX files like .aux, .bbl, etc, from our compile server. You generally don't need to do this unless you're having trouble with references. Your project files will not be deleted or changed."
+ buttons: [{
+ text: "Cancel"
+ }, {
+ text: "Clear from cache",
+ class: "btn-primary",
+ close: false
+ callback: ($button) =>
+ $button.text("Clearing...")
+ $button.prop("disabled", true)
+ $.ajax({
+ url: "/project/#{@ide.project_id}/output"
+ type: "DELETE"
+ headers:
+ "X-CSRF-Token": window.csrfToken
+ complete: () -> modal.remove()
+ })
+
+ }]
diff --git a/services/web/public/coffee/project-members/ProjectMembersManager.coffee b/services/web/public/coffee/project-members/ProjectMembersManager.coffee
index 82ff4bfdcc..a93b8c09e9 100644
--- a/services/web/public/coffee/project-members/ProjectMembersManager.coffee
+++ b/services/web/public/coffee/project-members/ProjectMembersManager.coffee
@@ -25,10 +25,11 @@ define [
@publishProjectView?.refreshPublishStatus()
setupPublish = _.once =>
- @publishProjectView = new PublishProjectView
- ide: @ide
- el: $("#publishProject")
- @publishProjectView.render()
+ if @ide.security? and @ide.security.permissionsLevel == "owner"
+ @publishProjectView = new PublishProjectView
+ ide: @ide
+ el: $("#publishProject")
+ @publishProjectView.render()
setupArea()
if @ide?
@@ -76,17 +77,18 @@ define [
@ide.socket.emit "removeUserFromProject", member.id
addMember: (email, privileges) ->
+ console.log "Adding member", email
@ide.socket.emit "addUserToProject", email, privileges, (error, added) =>
if error?
@ide.showGenericServerErrorMessage()
return
if !added
+ console.log "got response", error, added
ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "projectMemebrs")
AccountManager.askToUpgrade @ide,
why: "to add additional collaborators"
onUpgrade: () =>
ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "projectMemebrs")
- @addMember(email, privileges)
afterMemberRemoved: (memberId) ->
for member in @members.models
diff --git a/services/web/public/coffee/settings/DropboxSettingsManager.coffee b/services/web/public/coffee/settings/DropboxSettingsManager.coffee
index 1f5953835f..b4fd801bd3 100644
--- a/services/web/public/coffee/settings/DropboxSettingsManager.coffee
+++ b/services/web/public/coffee/settings/DropboxSettingsManager.coffee
@@ -17,11 +17,11 @@ define [
@tab.empty()
if !@ide.isAllowedToDoIt "owner"
else if !@project.get('features').dropbox
- ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "dropdown")
+ ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "dropbox")
accountManager.askToUpgrade @ide,
onUpgrade: =>
@checkIfUserIsLinkedToDropbox()
- ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "dropdown")
+ ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "dropbox")
else
@checkIfUserIsLinkedToDropbox()
diff --git a/services/web/public/coffee/settings/SettingsManager.coffee b/services/web/public/coffee/settings/SettingsManager.coffee
index 5474263a38..9f9e20347c 100644
--- a/services/web/public/coffee/settings/SettingsManager.coffee
+++ b/services/web/public/coffee/settings/SettingsManager.coffee
@@ -126,7 +126,10 @@ define [
# http://stackoverflow.com/questions/6692031/check-if-event-is-triggered-by-a-human
if e.originalEvent?
if @ide.isAllowedToDoIt "readAndWrite"
- @project.set("name", e.target.value)
+ newName = e.target.value?.trim()
+ $("input.projectName").val(newName)
+ @project.set("name", newName)
+
bindToCompiler: ->
$('select#compilers').val(@project.get("compiler"))
diff --git a/services/web/public/coffee/track-changes/ChangeListView.coffee b/services/web/public/coffee/track-changes/ChangeListView.coffee
index 80a0fb0915..1212faa1fe 100644
--- a/services/web/public/coffee/track-changes/ChangeListView.coffee
+++ b/services/web/public/coffee/track-changes/ChangeListView.coffee
@@ -40,6 +40,9 @@ define [
overflow: "scroll"
this
+ remove: () ->
+ @undelegateEvents()
+
addItem: (model) ->
index = @collection.indexOf(model)
previousModel = @collection.models[index - 1]
@@ -208,6 +211,17 @@ define [
else
@$el.addClass("first-in-day")
+ @$(".change-selector-from").tooltip({
+ title: "Show back to this change",
+ placement: "left",
+ animation: false
+ })
+ @$(".change-selector-to").tooltip({
+ title: "Show up to this change",
+ placement: "left",
+ animation: false
+ })
+
return this
onClick: (e) ->
diff --git a/services/web/public/coffee/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/track-changes/TrackChangesManager.coffee
index ab27d883a1..d1a8484a91 100644
--- a/services/web/public/coffee/track-changes/TrackChangesManager.coffee
+++ b/services/web/public/coffee/track-changes/TrackChangesManager.coffee
@@ -3,10 +3,11 @@ define [
"track-changes/models/Diff"
"track-changes/ChangeListView"
"track-changes/DiffView"
+ "account/AccountManager"
"utils/Modal"
"models/Doc"
"moment"
-], (ChangeList, Diff, ChangeListView, DiffView, Modal, Doc, moment) ->
+], (ChangeList, Diff, ChangeListView, DiffView, AccountManager, Modal, Doc, moment) ->
class TrackChangesManager
template: $("#trackChangesPanelTemplate").html()
@@ -39,23 +40,30 @@ define [
bindToFileTreeEvents: () ->
@ide.fileTreeManager.on "open:doc", (doc_id) =>
+ @doc_id = doc_id
if @enabled
- @doc_id = doc_id
@updateDiff()
+ AB_BUCKETS: ["control", "one-week", "pop-up"]
show: () ->
@changes = new ChangeList([], project_id: @project_id, ide: @ide)
+ if @changeListView?
+ @changeListView.remove()
@changeListView = new ChangeListView(
- collection : @changes,
- el : @$el.find(".change-list-area")
+ el: @$el.find(".change-list-area")
+ collection: @changes
)
@changeListView.render()
@changeListView.loadUntilFull (error) =>
@autoSelectDiff()
@changeListView.on "change_diff", (fromIndex, toIndex) =>
- @selectDocAndUpdateDiff(fromIndex, toIndex)
+ @findDocsInChange(fromIndex, toIndex)
+ @updateLabels()
+ @updateDiff()
+
+ @showUpgradeView()
if @diffView?
@diffView.remove()
@@ -65,12 +73,26 @@ define [
@ide.fileViewManager.disable()
@enable()
+ showUpgradeView: () ->
+ @$el.find("button.start-free-trial").off "click.track-changes"
+ @$el.find("button.start-free-trial").on "click.track-changes", () => @gotoFreeTrial()
+
+ if !@ide.project.get("features").versioning
+ ga('send', 'event', 'subscription-funnel', 'askToUgrade', "trackchanges")
+ @$el.find(".track-changes-upgrade-popup").show()
+
+ if @ide.project.get("owner") == @ide.user
+ @$el.find(".show-when-not-owner").hide()
+ else
+ @$el.find(".show-when-owner").hide()
+
hide: () ->
@ide.editor.enable()
@ide.fileViewManager.enable()
@disable()
@ide.fileTreeManager.openDoc(@doc_id)
@ide.tabManager.show "code"
+ @resetLabels()
autoSelectDiff: () ->
if @changes.models.length == 0
@@ -92,17 +114,27 @@ define [
@changeListView.setSelectionRange(fromIndex, 0)
@updateDiff()
- selectDocAndUpdateDiff: (fromIndex, toIndex) ->
- doc_ids = []
+ findDocsInChange: (fromIndex, toIndex) ->
+ @changed_doc_ids = []
for change in @changes.models.slice(toIndex, fromIndex + 1)
for doc in change.get("docs") or []
- doc_ids.push doc.id if doc.id not in doc_ids
+ @changed_doc_ids.push doc.id if doc.id not in @changed_doc_ids
- if !@doc_id? or @doc_id not in doc_ids
- @doc_id = doc_ids[0]
+ if !@doc_id? or @doc_id not in @changed_doc_ids
+ @doc_id = @changed_doc_ids[0]
@updateDiff()
+ updateLabels: () ->
+ labels = {}
+ for doc_id in @changed_doc_ids
+ labels[doc_id] = true
+ @ide.fileTreeManager.setLabels(labels)
+
+ resetLabels: () ->
+ @ide.fileTreeManager.setLabels({})
+
+
updateDiff: () ->
fromIndex = @changeListView.selectedFromIndex
toIndex = @changeListView.selectedToIndex
@@ -191,4 +223,8 @@ define [
disable: () ->
@enabled = false
+ gotoFreeTrial: () ->
+ AccountManager.gotoSubscriptionsPage()
+ ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "trackchanges")
+
return TrackChangesManager
diff --git a/services/web/public/js/libs/pdfListView/PdfListView.js b/services/web/public/js/libs/pdfListView/PdfListView.js
index 13e2cb83c8..539a574973 100644
--- a/services/web/public/js/libs/pdfListView/PdfListView.js
+++ b/services/web/public/js/libs/pdfListView/PdfListView.js
@@ -445,6 +445,7 @@ var RenderingStates = {
var idCounter = 0;
+
/**
* The view for a single page.
*/
@@ -477,16 +478,30 @@ PageView.prototype = {
// Only change the width/height property of the canvas if it really
// changed. Every assignment to the width/height property clears the
// content of the canvas.
+
+ var outputScale = this.getOutputScale();
+
+ var scaledWidth = (Math.floor(viewport.width) * outputScale.sx) | 0;
+ var scaledHeight = (Math.floor(viewport.height) * outputScale.sy) | 0;
+
var newWidth = Math.floor(viewport.width);
var newHeight = Math.floor(viewport.height);
+
if (this.canvas.width !== newWidth) {
- this.canvas.width = newWidth;
+ this.canvas.width = scaledWidth;
+ this.canvas.style.width = newWidth + 'px';
this.resetRenderState();
}
if (this.canvas.height !== newHeight) {
- this.canvas.height = newHeight;
+ this.canvas.height = scaledHeight;
+ this.canvas.style.height = newHeight + 'px';
this.resetRenderState();
}
+
+ if(outputScale.scaled){
+ var ctx = this.getCanvasContext()
+ ctx.scale(outputScale.sx, outputScale.sy)
+ }
this.width = viewport.width;
this.height = viewport.height;
@@ -523,6 +538,28 @@ PageView.prototype = {
return this.canvas.getContext('2d');
},
+ /**
+ * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
+ * @return {Object} The object with horizontal (sx) and vertical (sy)
+ scales. The scaled property is set to false if scaling is
+ not required, true otherwise.
+ */
+
+ getOutputScale: function(){
+ var ctx = this.getCanvasContext()
+ var devicePixelRatio = window.devicePixelRatio || 1;
+ var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
+ ctx.mozBackingStorePixelRatio ||
+ ctx.msBackingStorePixelRatio ||
+ ctx.oBackingStorePixelRatio ||
+ ctx.backingStorePixelRatio || 1;
+ var pixelRatio = devicePixelRatio / backingStoreRatio;
+ return {
+ sx: pixelRatio,
+ sy: pixelRatio,
+ scaled: pixelRatio != 1
+ };
+ },
createNewCanvas: function() {
if (this.canvas) {
this.dom.removeChild(this.canvas);
diff --git a/services/web/public/stylesheets/less/bonus.less b/services/web/public/stylesheets/less/bonus.less
index 33c70eef08..6203b93c50 100644
--- a/services/web/public/stylesheets/less/bonus.less
+++ b/services/web/public/stylesheets/less/bonus.less
@@ -78,6 +78,7 @@
font-size: 20px;
line-height: 28px;
margin-bottom: 10px;
+ margin-top: 16px;
}
.bonus-banner {
@@ -121,6 +122,8 @@
background-repeat: no-repeat;
background-position: 16px center;
}
+ h2.direct-link {
+ }
}
p.thanks {
diff --git a/services/web/public/stylesheets/less/editor.less b/services/web/public/stylesheets/less/editor.less
index 31f93bf488..2235de881f 100644
--- a/services/web/public/stylesheets/less/editor.less
+++ b/services/web/public/stylesheets/less/editor.less
@@ -467,6 +467,9 @@ body.editor {
.dropdown-caret {
display: none;
}
+ .entity-label {
+ display: none;
+ }
}
.entity-folder {
@@ -527,6 +530,33 @@ body.editor {
}
}
}
+
+ .entity-list-item.show-label {
+ .dropdown-caret {
+ display: none;
+ }
+ .entity-label {
+ display: block;
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ font-size: 13px;
+ line-height: 13px;
+ padding: 2px 6px 3px;
+ background-color: hsl(100, 80%, 42%);
+ font-weight: normal;
+ text-shadow: none;
+ &:hover {
+ background-color: hsl(100, 80%, 35%)
+ }
+ }
+ }
+
+ .entity-list-item.folder-open.show-label {
+ .entity-label {
+ display: none;
+ }
+ }
li img, .entity-list-item i {
margin: 4px;
diff --git a/services/web/public/stylesheets/less/trackchanges.less b/services/web/public/stylesheets/less/trackchanges.less
index b27a81802d..f9f12bbcc8 100644
--- a/services/web/public/stylesheets/less/trackchanges.less
+++ b/services/web/public/stylesheets/less/trackchanges.less
@@ -79,6 +79,39 @@
}
}
+ .track-changes-upgrade-control, .track-changes-upgrade-popup {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ }
+
+ .track-changes-upgrade-popup {
+ background-color: rgba(128,128,128,0.4);
+ .message {
+ margin: auto;
+ margin-top: 200px;
+ padding: 10px 10px 14px 10px;
+ width: 400px;
+ font-weight: bold;
+ text-align: center;
+ background-color: white;
+ .border-radius(8px);
+ }
+ }
+
+ .track-changes-upgrade-control {
+ background-color: #eeeeee;
+ text-align: center;
+ .message {
+ font-size: 18px;
+ margin: 12px;
+ margin-top: 36px;
+ }
+ }
+
.deleted-change-background,
.deleted-change-foreground,
.inserted-change-background,
@@ -193,6 +226,10 @@
}
li.loading-changes, li.empty-message {
padding: 6px;
+ cursor: default;
+ &:hover {
+ background-color: inherit;
+ }
}
li.selected-change {
background-color: #eaeaea;
@@ -242,6 +279,11 @@
}
}
}
+ li.track-changes-upgrade-oneweek {
+ padding: 15px;
+ background-color: rgb(255, 251, 210);
+ cursor: default;
+ }
}
ul.change-list.hover-state {
li {
@@ -256,6 +298,7 @@
li.hover-selected {
.change-selectors {
.range {
+ top: 0;
background-color: #999;
}
}
diff --git a/services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee b/services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee
index 49f6d8a67c..ef76ba9004 100644
--- a/services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee
@@ -122,6 +122,37 @@ describe "AuthenticationController", ->
it "should only redirect to the local path", ->
expect(@res.body).to.deep.equal redir: "/test"
+ describe "getLoggedInUserId", ->
+
+ beforeEach ->
+ @req =
+ session :{}
+
+ it "should return the user id from the session", (done)->
+ @user_id = "2134"
+ @req.session.user =
+ _id:@user_id
+ @AuthenticationController.getLoggedInUserId @req, (err, user_id)=>
+ expect(user_id).to.equal @user_id
+ done()
+
+ it "should return an error if there is no user on the session", (done)->
+ @AuthenticationController.getLoggedInUserId @req, (err, user_id)=>
+ expect(err).to.exist
+ done()
+
+ it "should return an error if there is no session", (done)->
+ @req = {}
+ @AuthenticationController.getLoggedInUserId @req, (err, user_id)=>
+ expect(err).to.exist
+ done()
+
+ it "should return an error if there is no req", (done)->
+ @req = {}
+ @AuthenticationController.getLoggedInUserId @req, (err, user_id)=>
+ expect(err).to.exist
+ done()
+
describe "getLoggedInUser", ->
beforeEach ->
@UserGetter.getUser = sinon.stub().callsArgWith(1, null, @user)
diff --git a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
index f2dfb1bde3..94b65cb2d4 100644
--- a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
@@ -16,7 +16,7 @@ describe "CompileController", ->
apis:
clsi:
url: "clsi.example.com"
- "request": @request = {}
+ "request": @request = sinon.stub()
"../../models/Project": Project: @Project = {}
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
@@ -68,15 +68,23 @@ describe "CompileController", ->
describe "proxyToClsi", ->
beforeEach ->
- @request.get = sinon.stub().returns(@proxy = {
+ @request.returns(@proxy = {
pipe: sinon.stub()
on: sinon.stub()
})
+ @upstream =
+ statusCode: 204
+ headers: { "mock": "header" }
+ @req.method = "mock-method"
@CompileController.proxyToClsi(@url = "/test", @req, @res, @next)
it "should open a request to the CLSI", ->
- @request.get
- .calledWith(url: "#{@settings.apis.clsi.url}#{@url}", timeout: 60 * 1000)
+ @request
+ .calledWith(
+ method: @req.method
+ url: "#{@settings.apis.clsi.url}#{@url}",
+ timeout: 60 * 1000
+ )
.should.equal true
it "should pass the request on to the client", ->
@@ -87,6 +95,17 @@ describe "CompileController", ->
it "should bind an error handle to the request proxy", ->
@proxy.on.calledWith("error").should.equal true
+ describe "deleteAuxFiles", ->
+ beforeEach ->
+ @CompileController.proxyToClsi = sinon.stub()
+ @req.params =
+ Project_id: @project_id
+ @CompileController.deleteAuxFiles @req, @res, @next
+
+ it "should proxy to the CLSI", ->
+ @CompileController.proxyToClsi
+ .calledWith("/project/#{@project_id}", @req, @res, @next)
+ .should.equal true
describe "compileAndDownloadPdf", ->
beforeEach ->
diff --git a/services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee b/services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee
index 933c5bae3b..55213fdda8 100644
--- a/services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee
@@ -609,3 +609,114 @@ describe "EditorController", ->
done()
+ describe "deleteProject", ->
+
+ beforeEach ->
+ @err = "errro"
+ @ProjectHandler::deleteProject = sinon.stub().callsArgWith(1, @err)
+
+ it "should call the project handler", (done)->
+ @EditorController.deleteProject @project_id, (err)=>
+ err.should.equal @err
+ @ProjectHandler::deleteProject.calledWith(@project_id).should.equal true
+ done()
+
+
+ describe "renameEntity", ->
+
+ beforeEach ->
+ @err = "errro"
+ @entity_id = "entity_id_here"
+ @entityType = "doc"
+ @newName = "bobsfile.tex"
+ @ProjectHandler::renameEntity = sinon.stub().callsArgWith(4, @err)
+ @EditorRealTimeController.emitToRoom = sinon.stub()
+
+ it "should call the project handler", (done)->
+ @EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, =>
+ @ProjectHandler::renameEntity.calledWith(@project_id, @entity_id, @entityType, @newName).should.equal true
+ done()
+
+
+ it "should emit the update to the room", (done)->
+ @EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, =>
+ @EditorRealTimeController.emitToRoom.calledWith(@project_id, 'reciveEntityRename', @entity_id, @newName).should.equal true
+ done()
+
+ describe "moveEntity", ->
+
+ beforeEach ->
+ @err = "errro"
+ @entity_id = "entity_id_here"
+ @entityType = "doc"
+ @folder_id = "313dasd21dasdsa"
+ @ProjectEntityHandler.moveEntity = sinon.stub().callsArgWith(4, @err)
+ @EditorRealTimeController.emitToRoom = sinon.stub()
+
+ it "should call the ProjectEntityHandler", (done)->
+ @EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
+ @ProjectEntityHandler.moveEntity.calledWith(@project_id, @entity_id, @folder_id, @entityType).should.equal true
+ done()
+
+
+ it "should emit the update to the room", (done)->
+ @EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
+ @EditorRealTimeController.emitToRoom.calledWith(@project_id, 'reciveEntityMove', @entity_id, @folder_id).should.equal true
+ done()
+
+ describe "renameProject", ->
+
+ beforeEach ->
+ @err = "errro"
+ @window_id = "kdsjklj290jlk"
+ @newName = "new name here"
+ @ProjectHandler::renameProject = sinon.stub().callsArgWith(3, @err)
+ @EditorRealTimeController.emitToRoom = sinon.stub()
+
+ it "should call the ProjectHandler", (done)->
+ @EditorController.renameProject @project_id, @window_id, @newName, =>
+ @ProjectHandler::renameProject.calledWith(@project_id, @window_id, @newName).should.equal true
+ done()
+
+
+ it "should emit the update to the room", (done)->
+ @EditorController.renameProject @project_id, @window_id, @newName, =>
+ @EditorRealTimeController.emitToRoom.calledWith(@project_id, 'projectNameUpdated', @window_id, @newName).should.equal true
+ done()
+
+
+ describe "setPublicAccessLevel", ->
+
+ beforeEach ->
+ @err = "errro"
+ @newAccessLevel = "public"
+ @ProjectHandler::setPublicAccessLevel = sinon.stub().callsArgWith(2, @err)
+ @EditorRealTimeController.emitToRoom = sinon.stub()
+
+ it "should call the ProjectHandler", (done)->
+ @EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+ @ProjectHandler::setPublicAccessLevel.calledWith(@project_id, @newAccessLevel).should.equal true
+ done()
+
+ it "should emit the update to the room", (done)->
+ @EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+ @EditorRealTimeController.emitToRoom.calledWith(@project_id, 'publicAccessLevelUpdated', @newAccessLevel).should.equal true
+ done()
+
+ describe "setRootDoc", ->
+
+ beforeEach ->
+ @err = "errro"
+ @newRootDocID = "21312321321"
+ @ProjectEntityHandler.setRootDoc = sinon.stub().callsArgWith(2, @err)
+ @EditorRealTimeController.emitToRoom = sinon.stub()
+
+ it "should call the ProjectEntityHandler", (done)->
+ @EditorController.setRootDoc @project_id, @newRootDocID, =>
+ @ProjectEntityHandler.setRootDoc.calledWith(@project_id, @newRootDocID).should.equal true
+ done()
+
+ it "should emit the update to the room", (done)->
+ @EditorController.setRootDoc @project_id, @newRootDocID, =>
+ @EditorRealTimeController.emitToRoom.calledWith(@project_id, 'rootDocUpdated', @newRootDocID).should.equal true
+ done()
\ No newline at end of file
diff --git a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee
index 44773f0fb5..585e007cf6 100644
--- a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee
@@ -8,36 +8,41 @@ require('chai').should()
describe 'Project details handler', ->
beforeEach ->
- @ProjectGetter =
- getProjectWithoutDocLines: sinon.stub()
- @ProjectModel =
- update: sinon.stub()
- @handler = SandboxedModule.require modulePath, requires:
- "./ProjectGetter":@ProjectGetter
- '../../models/Project': Project:@ProjectModel
- 'logger-sharelatex':
- log:->
- err:->
@project_id = "321l3j1kjkjl"
+ @user_id = "user-id-123"
@project =
name: "project"
description: "this is a great project"
something:"should not exist"
compiler: "latexxxxxx"
-
+ owner_ref: @user_id
+ @user =
+ features: "mock-features"
+ @ProjectGetter =
+ getProjectWithoutDocLines: sinon.stub().callsArgWith(1, null, @project)
+ @ProjectModel =
+ update: sinon.stub()
+ @UserGetter =
+ getUser: sinon.stub().callsArgWith(1, null, @user)
+ @handler = SandboxedModule.require modulePath, requires:
+ "./ProjectGetter":@ProjectGetter
+ '../../models/Project': Project:@ProjectModel
+ "../User/UserGetter": @UserGetter
+ 'logger-sharelatex':
+ log:->
+ err:->
describe "getDetails", ->
- it "should find the project", (done)->
- @ProjectGetter.getProjectWithoutDocLines.callsArgWith(1, null, @project)
- @handler.getDetails @project_id, (err, details)=>
+ it "should find the project and owner", (done)->
+ @handler.getDetails @project_id, (err, details)=>
details.name.should.equal @project.name
details.description.should.equal @project.description
details.compiler.should.equal @project.compiler
+ details.features.should.equal @user.features
assert.equal(details.something, undefined)
done()
-
it "should return the error", (done)->
error = "some error"
@ProjectGetter.getProjectWithoutDocLines.callsArgWith(1, error)
diff --git a/services/web/test/UnitTests/coffee/Project/ProjectGetterTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectGetterTests.coffee
index ac3c4c70ea..e2c533f804 100644
--- a/services/web/test/UnitTests/coffee/Project/ProjectGetterTests.coffee
+++ b/services/web/test/UnitTests/coffee/Project/ProjectGetterTests.coffee
@@ -5,6 +5,7 @@ expect = chai.expect
modulePath = "../../../../app/js/Features/Project/ProjectGetter.js"
SandboxedModule = require('sandboxed-module')
ObjectId = require("mongojs").ObjectId
+assert = require("chai").assert
describe "ProjectGetter", ->
beforeEach ->
@@ -57,7 +58,8 @@ describe "ProjectGetter", ->
@db.users.find = (query, callback) =>
callback null, [@user_lookup[query._id.toString()]]
sinon.spy @db.users, "find"
- @ProjectGetter.populateProjectWithUsers @project, @callback
+ @ProjectGetter.populateProjectWithUsers @project, (err, project)=>
+ @callback err, project
it "should look up each user", ->
for user in @users
@@ -73,5 +75,5 @@ describe "ProjectGetter", ->
expect(@project.collaberator_refs).to.deep.equal [@users[3], @users[4]]
it "should call the callback", ->
- @callback.calledWith(null, @project).should.equal true
+ assert.deepEqual @callback.args[0][1], @project
diff --git a/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupHandlerTests.coffee b/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupHandlerTests.coffee
index f3cb94af75..b9cff7ef72 100644
--- a/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupHandlerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupHandlerTests.coffee
@@ -76,11 +76,13 @@ describe "Subscription Group Handler", ->
@UserLocator.findById.callsArgWith(1, null, {_id:"31232"})
it "should locate the subscription", (done)->
+ @UserLocator.findById.callsArgWith(1, null, {_id:"31232"})
@Handler.getPopulatedListOfMembers @adminUser_id, (err, users)=>
@SubscriptionLocator.getUsersSubscription.calledWith(@adminUser_id).should.equal true
done()
it "should get the users by id", (done)->
+ @UserLocator.findById.callsArgWith(1, null, {_id:"31232"})
@subscription.member_ids = ["1234", "342432", "312312"]
@Handler.getPopulatedListOfMembers @adminUser_id, (err, users)=>
@UserLocator.findById.calledWith(@subscription.member_ids[0]).should.equal true
@@ -88,3 +90,13 @@ describe "Subscription Group Handler", ->
@UserLocator.findById.calledWith(@subscription.member_ids[2]).should.equal true
users.length.should.equal @subscription.member_ids.length
done()
+
+ it "should just return the id if the user can not be found as they may have deleted their account", (done)->
+ @UserLocator.findById.callsArgWith(1)
+ @subscription.member_ids = ["1234", "342432", "312312"]
+ @Handler.getPopulatedListOfMembers @adminUser_id, (err, users)=>
+ assert.deepEqual users[0], {_id:@subscription.member_ids[0]}
+ assert.deepEqual users[1], {_id:@subscription.member_ids[1]}
+ assert.deepEqual users[2], {_id:@subscription.member_ids[2]}
+ done()
+
diff --git a/services/web/test/UnitTests/coffee/ThirdPartyDataStore/TpdsPollingBackgroundTasksTests.coffee b/services/web/test/UnitTests/coffee/ThirdPartyDataStore/TpdsPollingBackgroundTasksTests.coffee
index a603b83bfc..4b509b5d41 100644
--- a/services/web/test/UnitTests/coffee/ThirdPartyDataStore/TpdsPollingBackgroundTasksTests.coffee
+++ b/services/web/test/UnitTests/coffee/ThirdPartyDataStore/TpdsPollingBackgroundTasksTests.coffee
@@ -32,7 +32,7 @@ describe 'third party data store', ->
@poller._sendToTpds = sinon.stub().callsArgWith(1, null)
@poller._markPollHappened = sinon.stub()
@poller.pollUsersWithDropbox (err)=>
- @userModel.find.calledWith({"dropbox.access_token.oauth_token_secret":{"$exists":true}}, "_id").should.equal true
+ @userModel.find.calledWith({"dropbox.access_token.oauth_token_secret":{"$exists":true}, "features.dropbox":true}, "_id").should.equal true
@poller._sendToTpds.calledWith([users[0]._id, users[1]._id, users[2]._id,]).should.equal true
@poller._markPollHappened.called.should.equal true
done()
diff --git a/services/web/test/UnitTests/coffee/User/UserDeleterTests.coffee b/services/web/test/UnitTests/coffee/User/UserDeleterTests.coffee
index 79a7b965c4..34fabda11c 100644
--- a/services/web/test/UnitTests/coffee/User/UserDeleterTests.coffee
+++ b/services/web/test/UnitTests/coffee/User/UserDeleterTests.coffee
@@ -25,7 +25,7 @@ describe "UserDeleter", ->
cancelSubscription: sinon.stub().callsArgWith(1)
@UserDeleter = SandboxedModule.require modulePath, requires:
"../../models/User": User: @User
- "../../managers/NewsletterManager": @NewsletterManager
+ "../Newsletter/NewsletterManager": @NewsletterManager
"../Subscription/SubscriptionHandler": @SubscriptionHandler
"../Project/ProjectDeleter": @ProjectDeleter
diff --git a/services/web/test/UnitTests/coffee/Versioning/AutomaticSnapshotManagerTests.coffee b/services/web/test/UnitTests/coffee/Versioning/AutomaticSnapshotManagerTests.coffee
index 3bd541d318..96d768762b 100644
--- a/services/web/test/UnitTests/coffee/Versioning/AutomaticSnapshotManagerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Versioning/AutomaticSnapshotManagerTests.coffee
@@ -108,20 +108,15 @@ describe 'AutomaticSnapshotManager', ->
callback(null, @project_ids)
else
throw new Error("unexpected key: #{key}")
- sinon.stub(@AutomaticSnapshotManager, "takeSnapshotIfRequired")
- .callsArgWith(1)
- @AutomaticSnapshotManager.takeAutomaticSnapshots(@callback)
+ sinon.stub(@AutomaticSnapshotManager, "takeSnapshotIfRequired").callsArgWith(1, null)
+ @AutomaticSnapshotManager.takeAutomaticSnapshots @callback
afterEach ->
@AutomaticSnapshotManager.takeSnapshotIfRequired.restore()
- it "should call takeSnapshotIfRequired for each project id", ->
- for project_id in @project_ids
- @AutomaticSnapshotManager.takeSnapshotIfRequired.calledWith(project_id)
- .should.equal true
it "should call the callback", ->
- @callback.calledWith(null).should.equal true
+ @callback.calledWith(undefined).should.equal true
describe "removing project from marked set", ->
diff --git a/services/web/test/smoke/coffee/SmokeTests.coffee b/services/web/test/smoke/coffee/SmokeTests.coffee
index 683892a496..7bee88fce9 100644
--- a/services/web/test/smoke/coffee/SmokeTests.coffee
+++ b/services/web/test/smoke/coffee/SmokeTests.coffee
@@ -1,18 +1,23 @@
chai = require("chai")
chai.should()
expect = chai.expect
-request = require "request"
Settings = require "settings-sharelatex"
+# Monkey patch request cookies, because the new tough-cookie module
+# assumes it's not a secure cookie if the url is not HTTPS
+request = require "request"
+jar = request.jar()
+jar.getCookieString = (uri) ->
+ return @_jar.getCookieStringSync uri, secure: true
+request = request.defaults jar: jar
+
port = Settings.internal?.web?.port or Settings.port or 3000
buildUrl = (path) -> "http://localhost:#{port}/#{path}"
describe "Opening", ->
before (done) ->
- @jar = request.jar()
request.get {
url: buildUrl("register")
- jar: @jar
headers:
"X-Forwarded-Proto": "https"
}, (error, response, body) =>
@@ -23,7 +28,6 @@ describe "Opening", ->
email: Settings.smokeTest.user
password: Settings.smokeTest.password
_csrf: csrf
- jar: @jar
headers:
"X-Forwarded-Proto": "https"
}, (error, response, body) ->
@@ -33,7 +37,6 @@ describe "Opening", ->
it "a project", (done) ->
request {
url: buildUrl("project/#{Settings.smokeTest.projectId}")
- jar: @jar
headers:
"X-Forwarded-Proto": "https"
}, (error, response, body) ->
@@ -47,12 +50,11 @@ describe "Opening", ->
it "the project list", (done) ->
request {
url: buildUrl("project")
- jar: @jar
headers:
"X-Forwarded-Proto": "https"
}, (error, response, body) ->
expect(error, "smoke test: error returned in getting project list").to.not.exist
- expect(response.statusCode, "smoke test: response code is not 200 getting project life").to.equal(200)
+ expect(response.statusCode, "smoke test: response code is not 200 getting project list").to.equal(200)
expect(!!body.match("