mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' of https://github.com/sharelatex/web-sharelatex
This commit is contained in:
commit
9924882b59
31 changed files with 394 additions and 97 deletions
2
services/web/.gitignore
vendored
2
services/web/.gitignore
vendored
|
@ -67,3 +67,5 @@ Gemfile.lock
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
app/views/external
|
app/views/external
|
||||||
|
|
||||||
|
modules
|
||||||
|
|
|
@ -10,7 +10,7 @@ module.exports = (grunt) ->
|
||||||
grunt.loadNpmTasks 'grunt-execute'
|
grunt.loadNpmTasks 'grunt-execute'
|
||||||
grunt.loadNpmTasks 'grunt-bunyan'
|
grunt.loadNpmTasks 'grunt-bunyan'
|
||||||
|
|
||||||
grunt.initConfig
|
config =
|
||||||
execute:
|
execute:
|
||||||
app:
|
app:
|
||||||
src: "app.js"
|
src: "app.js"
|
||||||
|
@ -27,10 +27,6 @@ module.exports = (grunt) ->
|
||||||
app:
|
app:
|
||||||
src: 'app.coffee'
|
src: 'app.coffee'
|
||||||
dest: 'app.js'
|
dest: 'app.js'
|
||||||
|
|
||||||
BackgroundJobsWorker:
|
|
||||||
src: 'BackgroundJobsWorker.coffee'
|
|
||||||
dest: 'BackgroundJobsWorker.js'
|
|
||||||
|
|
||||||
sharejs:
|
sharejs:
|
||||||
options:
|
options:
|
||||||
|
@ -163,6 +159,75 @@ module.exports = (grunt) ->
|
||||||
"help"
|
"help"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
moduleCompileServerTasks = []
|
||||||
|
moduleCompileUnitTestTasks = []
|
||||||
|
moduleUnitTestTasks = []
|
||||||
|
moduleCompileClientTasks = []
|
||||||
|
moduleIdeClientSideIncludes = []
|
||||||
|
moduleMainClientSideIncludes = []
|
||||||
|
if fs.existsSync "./modules"
|
||||||
|
for module in fs.readdirSync "./modules"
|
||||||
|
if fs.existsSync "./modules/#{module}/index.coffee"
|
||||||
|
config.coffee["module_#{module}_server"] = {
|
||||||
|
expand: true,
|
||||||
|
flatten: false,
|
||||||
|
cwd: "modules/#{module}/app/coffee",
|
||||||
|
src: ['**/*.coffee'],
|
||||||
|
dest: "modules/#{module}/app/js",
|
||||||
|
ext: '.js'
|
||||||
|
}
|
||||||
|
config.coffee["module_#{module}_index"] = {
|
||||||
|
src: "modules/#{module}/index.coffee",
|
||||||
|
dest: "modules/#{module}/index.js"
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleCompileServerTasks.push "coffee:module_#{module}_server"
|
||||||
|
moduleCompileServerTasks.push "coffee:module_#{module}_index"
|
||||||
|
|
||||||
|
config.coffee["module_#{module}_unit_tests"] = {
|
||||||
|
expand: true,
|
||||||
|
flatten: false,
|
||||||
|
cwd: "modules/#{module}/test/unit/coffee",
|
||||||
|
src: ['**/*.coffee'],
|
||||||
|
dest: "modules/#{module}/test/unit/js",
|
||||||
|
ext: '.js'
|
||||||
|
}
|
||||||
|
config.mochaTest["module_#{module}_unit"] = {
|
||||||
|
src: ["modules/#{module}/test/unit/js/*.js"]
|
||||||
|
options:
|
||||||
|
reporter: grunt.option('reporter') or 'spec'
|
||||||
|
grep: grunt.option("grep")
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleCompileUnitTestTasks.push "coffee:module_#{module}_unit_tests"
|
||||||
|
moduleUnitTestTasks.push "mochaTest:module_#{module}_unit"
|
||||||
|
|
||||||
|
if fs.existsSync "./modules/#{module}/public/coffee/ide/index.coffee"
|
||||||
|
config.coffee["module_#{module}_client_ide"] = {
|
||||||
|
expand: true,
|
||||||
|
flatten: false,
|
||||||
|
cwd: "modules/#{module}/public/coffee/ide",
|
||||||
|
src: ['**/*.coffee'],
|
||||||
|
dest: "public/js/ide/#{module}",
|
||||||
|
ext: '.js'
|
||||||
|
}
|
||||||
|
moduleCompileClientTasks.push "coffee:module_#{module}_client_ide"
|
||||||
|
moduleIdeClientSideIncludes.push "ide/#{module}/index"
|
||||||
|
|
||||||
|
if fs.existsSync "./modules/#{module}/public/coffee/main/index.coffee"
|
||||||
|
config.coffee["module_#{module}_client_main"] = {
|
||||||
|
expand: true,
|
||||||
|
flatten: false,
|
||||||
|
cwd: "modules/#{module}/public/coffee/main",
|
||||||
|
src: ['**/*.coffee'],
|
||||||
|
dest: "public/js/main/#{module}",
|
||||||
|
ext: '.js'
|
||||||
|
}
|
||||||
|
moduleCompileClientTasks.push "coffee:module_#{module}_client_main"
|
||||||
|
moduleMainClientSideIncludes.push "main/#{module}/index"
|
||||||
|
|
||||||
|
grunt.initConfig config
|
||||||
|
|
||||||
grunt.registerTask 'wrap_sharejs', 'Wrap the compiled ShareJS code for AMD module loading', () ->
|
grunt.registerTask 'wrap_sharejs', 'Wrap the compiled ShareJS code for AMD module loading', () ->
|
||||||
content = fs.readFileSync "public/js/libs/sharejs.js"
|
content = fs.readFileSync "public/js/libs/sharejs.js"
|
||||||
fs.writeFileSync "public/js/libs/sharejs.js", """
|
fs.writeFileSync "public/js/libs/sharejs.js", """
|
||||||
|
@ -174,8 +239,20 @@ module.exports = (grunt) ->
|
||||||
|
|
||||||
grunt.registerTask 'help', 'Display this help list', 'availabletasks'
|
grunt.registerTask 'help', 'Display this help list', 'availabletasks'
|
||||||
|
|
||||||
grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir']
|
grunt.registerTask 'compile:modules:server', 'Compile all the modules', moduleCompileServerTasks
|
||||||
grunt.registerTask 'compile:client', 'Compile the client side coffee script', ['coffee:client', 'coffee:sharejs', 'wrap_sharejs']
|
grunt.registerTask 'compile:modules:unit_tests', 'Compile all the modules unit tests', moduleCompileUnitTestTasks
|
||||||
|
grunt.registerTask 'compile:modules:client', 'Compile all the module client side code', moduleCompileClientTasks
|
||||||
|
grunt.registerTask 'compile:modules:inject_clientside_includes', () ->
|
||||||
|
content = fs.readFileSync("public/js/ide.js").toString()
|
||||||
|
content = content.replace(/, "__IDE_CLIENTSIDE_INCLUDES__"/g, moduleIdeClientSideIncludes.map((i) -> ", \"#{i}\"").join(""))
|
||||||
|
fs.writeFileSync "public/js/ide.js", content
|
||||||
|
|
||||||
|
content = fs.readFileSync("public/js/main.js").toString()
|
||||||
|
content = content.replace(/, "__MAIN_CLIENTSIDE_INCLUDES__"/g, moduleMainClientSideIncludes.map((i) -> ", \"#{i}\"").join(""))
|
||||||
|
fs.writeFileSync "public/js/main.js", content
|
||||||
|
|
||||||
|
grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir', 'compile:modules:server']
|
||||||
|
grunt.registerTask 'compile:client', 'Compile the client side coffee script', ['coffee:client', 'coffee:sharejs', 'wrap_sharejs', "compile:modules:client", 'compile:modules:inject_clientside_includes']
|
||||||
grunt.registerTask 'compile:css', 'Compile the less files to css', ['less']
|
grunt.registerTask 'compile:css', 'Compile the less files to css', ['less']
|
||||||
grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs']
|
grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs']
|
||||||
grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests']
|
grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests']
|
||||||
|
@ -187,6 +264,8 @@ module.exports = (grunt) ->
|
||||||
|
|
||||||
grunt.registerTask 'test:unit', 'Run the unit tests (use --grep=<regex> or --feature=<feature> for individual tests)', ['compile:server', 'compile:unit_tests', 'mochaTest:unit']
|
grunt.registerTask 'test:unit', 'Run the unit tests (use --grep=<regex> or --feature=<feature> for individual tests)', ['compile:server', 'compile:unit_tests', 'mochaTest:unit']
|
||||||
grunt.registerTask 'test:smoke', 'Run the smoke tests', ['compile:smoke_tests', 'mochaTest:smoke']
|
grunt.registerTask 'test:smoke', 'Run the smoke tests', ['compile:smoke_tests', 'mochaTest:smoke']
|
||||||
|
|
||||||
|
grunt.registerTask 'test:modules:unit', 'Run the unit tests for the modules', ['compile:modules:server', 'compile:modules:unit_tests'].concat(moduleUnitTestTasks)
|
||||||
|
|
||||||
grunt.registerTask 'run', "Compile and run the web-sharelatex server", ['compile', 'bunyan', 'execute']
|
grunt.registerTask 'run', "Compile and run the web-sharelatex server", ['compile', 'bunyan', 'execute']
|
||||||
grunt.registerTask 'default', 'run'
|
grunt.registerTask 'default', 'run'
|
||||||
|
|
|
@ -50,6 +50,10 @@ module.exports = AuthenticationController =
|
||||||
callback null, null
|
callback null, null
|
||||||
|
|
||||||
getLoggedInUser: (req, options = {allow_auth_token: false}, callback = (error, user) ->) ->
|
getLoggedInUser: (req, options = {allow_auth_token: false}, callback = (error, user) ->) ->
|
||||||
|
if typeof(options) == "function"
|
||||||
|
callback = options
|
||||||
|
options = {allow_auth_token: false}
|
||||||
|
|
||||||
if req.session?.user?._id?
|
if req.session?.user?._id?
|
||||||
query = req.session.user._id
|
query = req.session.user._id
|
||||||
else if req.query?.auth_token? and options.allow_auth_token
|
else if req.query?.auth_token? and options.allow_auth_token
|
||||||
|
|
|
@ -58,6 +58,10 @@ module.exports = EditorController =
|
||||||
|
|
||||||
# can be done affter the connection has happened
|
# can be done affter the connection has happened
|
||||||
ConnectedUsersManager.updateUserPosition project_id, client.id, user, null, ->
|
ConnectedUsersManager.updateUserPosition project_id, client.id, user, null, ->
|
||||||
|
|
||||||
|
# Only show the 'renamed or deleted' message once
|
||||||
|
ProjectDeleter.unmarkAsDeletedByExternalSource project
|
||||||
|
|
||||||
|
|
||||||
leaveProject: (client, user) ->
|
leaveProject: (client, user) ->
|
||||||
self = @
|
self = @
|
||||||
|
|
|
@ -34,9 +34,10 @@ module.exports =
|
||||||
setNewUserPassword: (req, res)->
|
setNewUserPassword: (req, res)->
|
||||||
{passwordResetToken, password} = req.body
|
{passwordResetToken, password} = req.body
|
||||||
if !password? or password.length == 0 or !passwordResetToken? or passwordResetToken.length == 0
|
if !password? or password.length == 0 or !passwordResetToken? or passwordResetToken.length == 0
|
||||||
return res.send 500
|
return res.send 400
|
||||||
PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err)->
|
PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err, found) ->
|
||||||
if err?
|
return next(err) if err?
|
||||||
res.send 500
|
if found
|
||||||
|
res.send 200
|
||||||
else
|
else
|
||||||
res.send 200
|
res.send 404, {message: req.i18n.translate("password_reset_token_expired")}
|
|
@ -23,10 +23,11 @@ module.exports =
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
callback null, true
|
callback null, true
|
||||||
|
|
||||||
setNewUserPassword: (token, password, callback)->
|
setNewUserPassword: (token, password, callback = (error, found) ->)->
|
||||||
PasswordResetTokenHandler.getUserIdFromTokenAndExpire token, (err, user_id)->
|
PasswordResetTokenHandler.getUserIdFromTokenAndExpire token, (err, user_id)->
|
||||||
if err then return callback(err)
|
if err then return callback(err)
|
||||||
if !user_id?
|
if !user_id?
|
||||||
logger.err user_id:user_id, "token for password reset did not find user_id"
|
return callback null, false
|
||||||
return callback("no user found")
|
AuthenticationManager.setUserPassword user_id, password, (err) ->
|
||||||
AuthenticationManager.setUserPassword user_id, password, callback
|
if err then return callback(err)
|
||||||
|
callback null, true
|
|
@ -128,6 +128,8 @@ module.exports = ProjectController =
|
||||||
Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
|
Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
|
||||||
hasSubscription: (cb)->
|
hasSubscription: (cb)->
|
||||||
LimitationsManager.userHasSubscriptionOrIsGroupMember req.session.user, cb
|
LimitationsManager.userHasSubscriptionOrIsGroupMember req.session.user, cb
|
||||||
|
user: (cb) ->
|
||||||
|
User.findById user_id, "featureSwitches", cb
|
||||||
}, (err, results)->
|
}, (err, results)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, "error getting data for project list page"
|
logger.err err:err, "error getting data for project list page"
|
||||||
|
@ -135,6 +137,7 @@ module.exports = ProjectController =
|
||||||
logger.log results:results, user_id:user_id, "rendering project list"
|
logger.log results:results, user_id:user_id, "rendering project list"
|
||||||
tags = results.tags[0]
|
tags = results.tags[0]
|
||||||
projects = ProjectController._buildProjectList results.projects[0], results.projects[1], results.projects[2]
|
projects = ProjectController._buildProjectList results.projects[0], results.projects[1], results.projects[2]
|
||||||
|
user = results.user
|
||||||
ProjectController._injectProjectOwners projects, (error, projects) ->
|
ProjectController._injectProjectOwners projects, (error, projects) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
|
|
||||||
|
@ -143,6 +146,7 @@ module.exports = ProjectController =
|
||||||
priority_title: true
|
priority_title: true
|
||||||
projects: projects
|
projects: projects
|
||||||
tags: tags
|
tags: tags
|
||||||
|
user: user
|
||||||
hasSubscription: results.hasSubscription
|
hasSubscription: results.hasSubscription
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +216,7 @@ module.exports = ProjectController =
|
||||||
referal_id : user.referal_id
|
referal_id : user.referal_id
|
||||||
subscription :
|
subscription :
|
||||||
freeTrial: {allowed: allowedFreeTrial}
|
freeTrial: {allowed: allowedFreeTrial}
|
||||||
|
featureSwitches: user.featureSwitches
|
||||||
}
|
}
|
||||||
userSettings: {
|
userSettings: {
|
||||||
mode : user.ace.mode
|
mode : user.ace.mode
|
||||||
|
@ -283,8 +288,7 @@ defaultSettingsForAnonymousUser = (user_id)->
|
||||||
freeTrial:
|
freeTrial:
|
||||||
allowed: true
|
allowed: true
|
||||||
featureSwitches:
|
featureSwitches:
|
||||||
dropbox: false
|
github: false
|
||||||
trackChanges: false
|
|
||||||
|
|
||||||
THEME_LIST = []
|
THEME_LIST = []
|
||||||
do generateThemeList = () ->
|
do generateThemeList = () ->
|
||||||
|
|
|
@ -7,7 +7,7 @@ FileStoreHandler = require("../FileStore/FileStoreHandler")
|
||||||
|
|
||||||
module.exports = ProjectDeleter =
|
module.exports = ProjectDeleter =
|
||||||
|
|
||||||
markAsDeletedByExternalSource : (project_id, callback)->
|
markAsDeletedByExternalSource : (project_id, callback = (error) ->)->
|
||||||
logger.log project_id:project_id, "marking project as deleted by external data source"
|
logger.log project_id:project_id, "marking project as deleted by external data source"
|
||||||
conditions = {_id:project_id}
|
conditions = {_id:project_id}
|
||||||
update = {deletedByExternalDataSource:true}
|
update = {deletedByExternalDataSource:true}
|
||||||
|
@ -15,6 +15,15 @@ module.exports = ProjectDeleter =
|
||||||
Project.update conditions, update, {}, (err)->
|
Project.update conditions, update, {}, (err)->
|
||||||
require('../Editor/EditorController').notifyUsersProjectHasBeenDeletedOrRenamed project_id, ->
|
require('../Editor/EditorController').notifyUsersProjectHasBeenDeletedOrRenamed project_id, ->
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
|
unmarkAsDeletedByExternalSource: (project, callback = (error) ->) ->
|
||||||
|
logger.log project_id: "removing flag marking project as deleted by external data source"
|
||||||
|
if project.deletedByExternalDataSource
|
||||||
|
conditions = {_id:project._id.toString()}
|
||||||
|
update = {deletedByExternalDataSource: false}
|
||||||
|
Project.update conditions, update, {}, callback
|
||||||
|
else
|
||||||
|
callback()
|
||||||
|
|
||||||
deleteUsersProjects: (owner_id, callback)->
|
deleteUsersProjects: (owner_id, callback)->
|
||||||
logger.log owner_id:owner_id, "deleting users projects"
|
logger.log owner_id:owner_id, "deleting users projects"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
tpdsUpdateHandler = require('./TpdsUpdateHandler')
|
tpdsUpdateHandler = require('./TpdsUpdateHandler')
|
||||||
|
UpdateMerger = require "./UpdateMerger"
|
||||||
logger = require('logger-sharelatex')
|
logger = require('logger-sharelatex')
|
||||||
Path = require('path')
|
Path = require('path')
|
||||||
metrics = require("../../infrastructure/Metrics")
|
metrics = require("../../infrastructure/Metrics")
|
||||||
|
@ -31,6 +32,24 @@ module.exports =
|
||||||
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds delete has been processed"
|
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds delete has been processed"
|
||||||
res.send 200
|
res.send 200
|
||||||
req.session.destroy()
|
req.session.destroy()
|
||||||
|
|
||||||
|
updateProjectContents: (req, res, next = (error) ->) ->
|
||||||
|
{project_id} = req.params
|
||||||
|
path = "/" + req.params[0] # UpdateMerger expects leading slash
|
||||||
|
logger.log project_id: project_id, path: path, "received project contents update"
|
||||||
|
UpdateMerger.mergeUpdate project_id, path, req, (error) ->
|
||||||
|
return next(error) if error?
|
||||||
|
res.send(200)
|
||||||
|
req.session.destroy()
|
||||||
|
|
||||||
|
deleteProjectContents: (req, res, next = (error) ->) ->
|
||||||
|
{project_id} = req.params
|
||||||
|
path = "/" + req.params[0] # UpdateMerger expects leading slash
|
||||||
|
logger.log project_id: project_id, path: path, "received project contents delete request"
|
||||||
|
UpdateMerger.deleteUpdate project_id, path, (error) ->
|
||||||
|
return next(error) if error?
|
||||||
|
res.send(200)
|
||||||
|
req.session.destroy()
|
||||||
|
|
||||||
parseParams: parseParams = (req)->
|
parseParams: parseParams = (req)->
|
||||||
path = req.params[0]
|
path = req.params[0]
|
||||||
|
|
|
@ -33,8 +33,7 @@ module.exports =
|
||||||
dropboxHandler.getUserRegistrationStatus user._id, (err, status)->
|
dropboxHandler.getUserRegistrationStatus user._id, (err, status)->
|
||||||
userIsRegisteredWithDropbox = !err? and status.registered
|
userIsRegisteredWithDropbox = !err? and status.registered
|
||||||
res.render 'user/settings',
|
res.render 'user/settings',
|
||||||
title:'account_settings',
|
title:'account_settings'
|
||||||
userHasDropboxFeature: user.features.dropbox
|
|
||||||
userIsRegisteredWithDropbox: userIsRegisteredWithDropbox
|
userIsRegisteredWithDropbox: userIsRegisteredWithDropbox
|
||||||
user: user,
|
user: user,
|
||||||
languages: Settings.languages,
|
languages: Settings.languages,
|
||||||
|
|
|
@ -6,6 +6,7 @@ SubscriptionFormatters = require('../Features/Subscription/SubscriptionFormatter
|
||||||
querystring = require('querystring')
|
querystring = require('querystring')
|
||||||
SystemMessageManager = require("../Features/SystemMessages/SystemMessageManager")
|
SystemMessageManager = require("../Features/SystemMessages/SystemMessageManager")
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
|
Modules = require "./Modules"
|
||||||
|
|
||||||
fingerprints = {}
|
fingerprints = {}
|
||||||
Path = require 'path'
|
Path = require 'path'
|
||||||
|
@ -146,3 +147,9 @@ module.exports = (app)->
|
||||||
res.locals.currentLngCode = req.lng
|
res.locals.currentLngCode = req.lng
|
||||||
next()
|
next()
|
||||||
|
|
||||||
|
app.use (req, res, next) ->
|
||||||
|
if Settings.reloadModuleViewsOnEachRequest
|
||||||
|
Modules.loadViewIncludes()
|
||||||
|
res.locals.moduleIncludes = Modules.moduleIncludes
|
||||||
|
next()
|
||||||
|
|
||||||
|
|
36
services/web/app/coffee/infrastructure/Modules.coffee
Normal file
36
services/web/app/coffee/infrastructure/Modules.coffee
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
fs = require "fs"
|
||||||
|
Path = require "path"
|
||||||
|
jade = require "jade"
|
||||||
|
|
||||||
|
MODULE_BASE_PATH = Path.resolve(__dirname + "/../../../modules")
|
||||||
|
|
||||||
|
module.exports = Modules =
|
||||||
|
modules: []
|
||||||
|
loadModules: () ->
|
||||||
|
for moduleName in fs.readdirSync(MODULE_BASE_PATH)
|
||||||
|
if fs.existsSync(Path.join(MODULE_BASE_PATH, moduleName, "index.js"))
|
||||||
|
loadedModule = require(Path.join(MODULE_BASE_PATH, moduleName, "index"))
|
||||||
|
loadedModule.name = moduleName
|
||||||
|
@modules.push loadedModule
|
||||||
|
|
||||||
|
applyRouter: (app) ->
|
||||||
|
for module in @modules
|
||||||
|
module.router?.apply(app)
|
||||||
|
|
||||||
|
viewIncludes: {}
|
||||||
|
loadViewIncludes: (app) ->
|
||||||
|
@viewIncludes = {}
|
||||||
|
for module in @modules
|
||||||
|
for view, partial of module.viewIncludes or {}
|
||||||
|
@viewIncludes[view] ||= []
|
||||||
|
@viewIncludes[view].push fs.readFileSync(Path.join(MODULE_BASE_PATH, module.name, "app/views", partial + ".jade"))
|
||||||
|
|
||||||
|
moduleIncludes: (view, locals) ->
|
||||||
|
partials = Modules.viewIncludes[view] or []
|
||||||
|
html = ""
|
||||||
|
for partial in partials
|
||||||
|
compiler = jade.compile(partial, doctype: "html")
|
||||||
|
html += compiler(locals)
|
||||||
|
return html
|
||||||
|
|
||||||
|
Modules.loadModules()
|
|
@ -24,6 +24,7 @@ ReferalConnect = require('../Features/Referal/ReferalConnect')
|
||||||
RedirectManager = require("./RedirectManager")
|
RedirectManager = require("./RedirectManager")
|
||||||
OldAssetProxy = require("./OldAssetProxy")
|
OldAssetProxy = require("./OldAssetProxy")
|
||||||
translations = require("translations-sharelatex").setup(Settings.i18n)
|
translations = require("translations-sharelatex").setup(Settings.i18n)
|
||||||
|
Modules = require "./Modules"
|
||||||
|
|
||||||
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongojs/node_modules/mongodb"), logger)
|
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongojs/node_modules/mongodb"), logger)
|
||||||
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongoose/node_modules/mongodb"), logger)
|
metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongoose/node_modules/mongodb"), logger)
|
||||||
|
@ -46,13 +47,13 @@ app.ignoreCsrf = (method, route) ->
|
||||||
ignoreCsrfRoutes.push new express.Route(method, route)
|
ignoreCsrfRoutes.push new express.Route(method, route)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.configure () ->
|
app.configure () ->
|
||||||
if Settings.behindProxy
|
if Settings.behindProxy
|
||||||
app.enable('trust proxy')
|
app.enable('trust proxy')
|
||||||
app.use express.static(__dirname + '/../../../public', {maxAge: staticCacheAge })
|
app.use express.static(__dirname + '/../../../public', {maxAge: staticCacheAge })
|
||||||
app.set 'views', __dirname + '/../../views'
|
app.set 'views', __dirname + '/../../views'
|
||||||
app.set 'view engine', 'jade'
|
app.set 'view engine', 'jade'
|
||||||
|
Modules.loadViewIncludes app
|
||||||
app.use express.bodyParser(uploadDir: Settings.path.uploadFolder)
|
app.use express.bodyParser(uploadDir: Settings.path.uploadFolder)
|
||||||
app.use express.bodyParser(uploadDir: __dirname + "/../../../data/uploads")
|
app.use express.bodyParser(uploadDir: __dirname + "/../../../data/uploads")
|
||||||
app.use translations.expressMiddlewear
|
app.use translations.expressMiddlewear
|
||||||
|
|
|
@ -33,8 +33,7 @@ UserSchema = new Schema
|
||||||
dropbox: { type:Boolean, default: Settings.defaultFeatures.dropbox }
|
dropbox: { type:Boolean, default: Settings.defaultFeatures.dropbox }
|
||||||
}
|
}
|
||||||
featureSwitches : {
|
featureSwitches : {
|
||||||
dropbox: {type:Boolean, default:true},
|
github: {type: Boolean}
|
||||||
oldHistory: {type:Boolean}
|
|
||||||
}
|
}
|
||||||
referal_id : {type:String, default:() -> uuid.v4().split("-")[0]}
|
referal_id : {type:String, default:() -> uuid.v4().split("-")[0]}
|
||||||
refered_users: [ type:ObjectId, ref:'User' ]
|
refered_users: [ type:ObjectId, ref:'User' ]
|
||||||
|
|
|
@ -39,6 +39,7 @@ WikiController = require("./Features/Wiki/WikiController")
|
||||||
ConnectedUsersController = require("./Features/ConnectedUsers/ConnectedUsersController")
|
ConnectedUsersController = require("./Features/ConnectedUsers/ConnectedUsersController")
|
||||||
DropboxRouter = require "./Features/Dropbox/DropboxRouter"
|
DropboxRouter = require "./Features/Dropbox/DropboxRouter"
|
||||||
dropboxHandler = require "./Features/Dropbox/DropboxHandler"
|
dropboxHandler = require "./Features/Dropbox/DropboxHandler"
|
||||||
|
Modules = require "./infrastructure/Modules"
|
||||||
|
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
|
@ -67,6 +68,8 @@ module.exports = class Router
|
||||||
StaticPagesRouter.apply(app)
|
StaticPagesRouter.apply(app)
|
||||||
TemplatesRouter.apply(app)
|
TemplatesRouter.apply(app)
|
||||||
DropboxRouter.apply(app)
|
DropboxRouter.apply(app)
|
||||||
|
|
||||||
|
Modules.applyRouter(app)
|
||||||
|
|
||||||
app.get '/blog', BlogController.getIndexPage
|
app.get '/blog', BlogController.getIndexPage
|
||||||
app.get '/blog/*', BlogController.getPage
|
app.get '/blog/*', BlogController.getPage
|
||||||
|
@ -154,6 +157,11 @@ module.exports = class Router
|
||||||
app.del '/user/:user_id/update/*', httpAuth, TpdsController.deleteUpdate
|
app.del '/user/:user_id/update/*', httpAuth, TpdsController.deleteUpdate
|
||||||
app.ignoreCsrf('post', '/user/:user_id/update/*')
|
app.ignoreCsrf('post', '/user/:user_id/update/*')
|
||||||
app.ignoreCsrf('delete', '/user/:user_id/update/*')
|
app.ignoreCsrf('delete', '/user/:user_id/update/*')
|
||||||
|
|
||||||
|
app.post '/project/:project_id/contents/*', httpAuth, TpdsController.updateProjectContents
|
||||||
|
app.del '/project/:project_id/contents/*', httpAuth, TpdsController.deleteProjectContents
|
||||||
|
app.ignoreCsrf('post', '/project/:project_id/contents/*')
|
||||||
|
app.ignoreCsrf('delete', '/project/:project_id/contents/*')
|
||||||
|
|
||||||
app.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
app.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||||
app.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
app.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||||
|
|
|
@ -46,14 +46,16 @@ aside#left-menu.full-size(
|
||||||
i.fa.fa-external-link.fa-fw
|
i.fa.fa-external-link.fa-fw
|
||||||
| #{translate("publish_as_template")}
|
| #{translate("publish_as_template")}
|
||||||
|
|
||||||
|
div(ng-show="permissions.admin")
|
||||||
span(ng-controller="DropboxController", ng-show="permissions.admin")
|
|
||||||
h4() #{translate("sync")}
|
h4() #{translate("sync")}
|
||||||
ul.list-unstyled.nav()
|
span(ng-controller="DropboxController")
|
||||||
li
|
ul.list-unstyled.nav()
|
||||||
a(ng-click="openDropboxModal()")
|
li
|
||||||
i.fa.fa-dropbox.fa-fw
|
a(ng-click="openDropboxModal()")
|
||||||
| Dropbox
|
i.fa.fa-dropbox.fa-fw
|
||||||
|
| Dropbox
|
||||||
|
|
||||||
|
!{moduleIncludes("editorLeftMenu", locals)}
|
||||||
|
|
||||||
h4(ng-show="!anonymous") #{translate("settings")}
|
h4(ng-show="!anonymous") #{translate("settings")}
|
||||||
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
|
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
href,
|
href,
|
||||||
ng-click="openUploadProjectModal()"
|
ng-click="openUploadProjectModal()"
|
||||||
) #{translate("upload_project")}
|
) #{translate("upload_project")}
|
||||||
|
!{moduleIncludes("newProjectMenu", locals)}
|
||||||
if (templates)
|
if (templates)
|
||||||
li.divider
|
li.divider
|
||||||
li.dropdown-header #{translate("templates")}
|
li.dropdown-header #{translate("templates")}
|
||||||
|
|
|
@ -96,22 +96,30 @@ block content
|
||||||
ng-disabled="changePasswordForm.$invalid"
|
ng-disabled="changePasswordForm.$invalid"
|
||||||
) #{translate("change")}
|
) #{translate("change")}
|
||||||
|
|
||||||
hr.soften
|
hr
|
||||||
|
|
||||||
h3 #{translate("dropbox_integration")}
|
h3 #{translate("dropbox_integration")}
|
||||||
span.small
|
span.small
|
||||||
a(href='/help/kb/dropbox-2') (#{translate("learn_more")})
|
a(href='/help/kb/dropbox-2') (#{translate("learn_more")})
|
||||||
- if(!userHasDropboxFeature)
|
- if(!user.features.dropbox)
|
||||||
.alert.alert-info #{translate("dropbox_is_premium")}
|
p.small #{translate("dropbox_sync_description")}
|
||||||
a.btn.btn-info(href='/user/subscription/plans') #{translate("upgrade")}
|
.alert.alert-info
|
||||||
- else if(userIsRegisteredWithDropbox)
|
p #{translate("dropbox_is_premium")}
|
||||||
.alert.alert-success #{translate("account_is_linked")}
|
p
|
||||||
row
|
a.btn.btn-info(href='/user/subscription/plans') #{translate("upgrade")}
|
||||||
a(href='/dropbox/unlink').btn #{translate("unlink_dropbox")}
|
- else if(userIsRegisteredWithDropbox)
|
||||||
- else
|
.alert.alert-success
|
||||||
a.btn.btn-info(href='/dropbox/beginAuth') #{translate("link_to_dropbox")}
|
| #{translate("account_is_linked")}.
|
||||||
|
|
|
||||||
|
a(href='/dropbox/unlink') #{translate("unlink_dropbox")}
|
||||||
|
- else
|
||||||
|
p.small #{translate("dropbox_sync_description")}
|
||||||
|
p
|
||||||
|
a.btn.btn-info(href='/dropbox/beginAuth') #{translate("link_to_dropbox")}
|
||||||
|
|
||||||
|
| !{moduleIncludes("userSettings", locals)}
|
||||||
|
|
||||||
hr.soften
|
hr
|
||||||
|
|
||||||
p.small
|
p.small
|
||||||
| #{translate("newsletter_info_and_unsubscribe")}
|
| #{translate("newsletter_info_and_unsubscribe")}
|
||||||
|
|
|
@ -90,6 +90,8 @@ module.exports =
|
||||||
url: "http://localhost:3013"
|
url: "http://localhost:3013"
|
||||||
templates:
|
templates:
|
||||||
url: "http://localhost:3007"
|
url: "http://localhost:3007"
|
||||||
|
githubSync:
|
||||||
|
url: "http://localhost:3022"
|
||||||
recurly:
|
recurly:
|
||||||
privateKey: ""
|
privateKey: ""
|
||||||
apiKey: ""
|
apiKey: ""
|
||||||
|
@ -304,4 +306,5 @@ module.exports =
|
||||||
"/templates/index": "/templates/"
|
"/templates/index": "/templates/"
|
||||||
|
|
||||||
proxyUrls: {}
|
proxyUrls: {}
|
||||||
|
|
||||||
|
reloadModuleViewsOnEachRequest: true
|
||||||
|
|
|
@ -17,6 +17,7 @@ define [
|
||||||
"ide/hotkeys/index"
|
"ide/hotkeys/index"
|
||||||
"ide/directives/layout"
|
"ide/directives/layout"
|
||||||
"ide/services/ide"
|
"ide/services/ide"
|
||||||
|
"__IDE_CLIENTSIDE_INCLUDES__"
|
||||||
"analytics/AbTestingManager"
|
"analytics/AbTestingManager"
|
||||||
"directives/focus"
|
"directives/focus"
|
||||||
"directives/fineUpload"
|
"directives/fineUpload"
|
||||||
|
|
|
@ -78,6 +78,34 @@ define [
|
||||||
readOnly: true
|
readOnly: true
|
||||||
editor.commands.removeCommand "replace"
|
editor.commands.removeCommand "replace"
|
||||||
|
|
||||||
|
# Bold text on CMD+B
|
||||||
|
editor.commands.addCommand
|
||||||
|
name: "bold",
|
||||||
|
bindKey: win: "Ctrl-B", mac: "Command-B"
|
||||||
|
exec: (editor) ->
|
||||||
|
selection = editor.getSelection()
|
||||||
|
if selection.isEmpty()
|
||||||
|
editor.insert("\\textbf{}")
|
||||||
|
editor.navigateLeft(1)
|
||||||
|
else
|
||||||
|
text = editor.getCopyText()
|
||||||
|
editor.insert("\\textbf{" + text + "}")
|
||||||
|
readOnly: false
|
||||||
|
|
||||||
|
# Italicise text on CMD+I
|
||||||
|
editor.commands.addCommand
|
||||||
|
name: "italics",
|
||||||
|
bindKey: win: "Ctrl-I", mac: "Command-I"
|
||||||
|
exec: (editor) ->
|
||||||
|
selection = editor.getSelection()
|
||||||
|
if selection.isEmpty()
|
||||||
|
editor.insert("\\textit{}")
|
||||||
|
editor.navigateLeft(1)
|
||||||
|
else
|
||||||
|
text = editor.getCopyText()
|
||||||
|
editor.insert("\\textit{" + text + "}")
|
||||||
|
readOnly: false
|
||||||
|
|
||||||
scope.$watch "onCtrlEnter", (callback) ->
|
scope.$watch "onCtrlEnter", (callback) ->
|
||||||
if callback?
|
if callback?
|
||||||
editor.commands.addCommand
|
editor.commands.addCommand
|
||||||
|
|
|
@ -22,5 +22,6 @@ define [
|
||||||
"directives/selectAll"
|
"directives/selectAll"
|
||||||
"directives/maxHeight"
|
"directives/maxHeight"
|
||||||
"filters/formatDate"
|
"filters/formatDate"
|
||||||
|
"__MAIN_CLIENTSIDE_INCLUDES__"
|
||||||
], () ->
|
], () ->
|
||||||
angular.bootstrap(document.body, ["SharelatexApp"])
|
angular.bootstrap(document.body, ["SharelatexApp"])
|
||||||
|
|
|
@ -196,11 +196,12 @@ define [
|
||||||
|
|
||||||
$scope.createTag = (name) ->
|
$scope.createTag = (name) ->
|
||||||
event_tracking.send 'project-list-page-interaction', 'project action', 'createTag'
|
event_tracking.send 'project-list-page-interaction', 'project action', 'createTag'
|
||||||
$scope.tags.push {
|
$scope.tags.push tag = {
|
||||||
name: name
|
name: name
|
||||||
project_ids: []
|
project_ids: []
|
||||||
showWhenEmpty: true
|
showWhenEmpty: true
|
||||||
}
|
}
|
||||||
|
return tag
|
||||||
|
|
||||||
$scope.openNewTagModal = (e) ->
|
$scope.openNewTagModal = (e) ->
|
||||||
modalInstance = $modal.open(
|
modalInstance = $modal.open(
|
||||||
|
@ -210,11 +211,8 @@ define [
|
||||||
|
|
||||||
modalInstance.result.then(
|
modalInstance.result.then(
|
||||||
(newTagName) ->
|
(newTagName) ->
|
||||||
$scope.createTag(newTagName)
|
tag = $scope.createTag(newTagName)
|
||||||
console.log $scope.tag, $scope.addSelectedProjectsToTag, newTagName
|
$scope.addSelectedProjectsToTag(tag)
|
||||||
$scope.addSelectedProjectsToTag($scope.tag)
|
|
||||||
|
|
||||||
# add selected projects to this new tag
|
|
||||||
)
|
)
|
||||||
|
|
||||||
$scope.createProject = (name, template = "none") ->
|
$scope.createProject = (name, template = "none") ->
|
||||||
|
|
|
@ -159,15 +159,16 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.highlights-before-label {
|
.highlights-before-label, .highlights-after-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: @line-height-computed / 2;
|
|
||||||
right: @line-height-computed;
|
right: @line-height-computed;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.highlights-before-label {
|
||||||
|
top: @line-height-computed / 2;
|
||||||
}
|
}
|
||||||
.highlights-after-label {
|
.highlights-after-label {
|
||||||
position: absolute;
|
|
||||||
bottom: @line-height-computed / 2;
|
bottom: @line-height-computed / 2;
|
||||||
right: @line-height-computed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,14 @@
|
||||||
}
|
}
|
||||||
.recurly .plan {
|
.recurly .plan {
|
||||||
color: #333;
|
color: #333;
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
zoom: 1;
|
|
||||||
font-family: @font-family-serif;
|
font-family: @font-family-serif;
|
||||||
|
padding: 0 20px 40px;
|
||||||
|
position: relative;
|
||||||
|
margin-top: -20px;
|
||||||
}
|
}
|
||||||
.recurly .plan .name {
|
.recurly .plan .name {
|
||||||
float: left;
|
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
min-width: 200px;
|
margin-right: 130px;
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
}
|
||||||
.recurly .plan .quantity.field {
|
.recurly .plan .quantity.field {
|
||||||
clear: none;
|
clear: none;
|
||||||
|
@ -56,9 +53,10 @@
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.recurly .plan .recurring_cost {
|
.recurly .plan .recurring_cost {
|
||||||
float: right;
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-right: 20px;
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
||||||
.recurly .plan .recurring_cost .cost {
|
.recurly .plan .recurring_cost .cost {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
|
@ -68,14 +66,7 @@
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
.recurly .free_trial {
|
.recurly .free_trial {
|
||||||
clear: left;
|
|
||||||
float: left;
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
height: 22px;
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 35px;
|
|
||||||
left: 20px;
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.recurly .setup_fee {
|
.recurly .setup_fee {
|
||||||
|
|
|
@ -89,6 +89,7 @@ describe "EditorController", ->
|
||||||
@AuthorizationManager.setPrivilegeLevelOnClient = sinon.stub()
|
@AuthorizationManager.setPrivilegeLevelOnClient = sinon.stub()
|
||||||
@EditorRealTimeController.emitToRoom = sinon.stub()
|
@EditorRealTimeController.emitToRoom = sinon.stub()
|
||||||
@ConnectedUsersManager.updateUserPosition.callsArgWith(4)
|
@ConnectedUsersManager.updateUserPosition.callsArgWith(4)
|
||||||
|
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||||
|
|
||||||
describe "when authorized", ->
|
describe "when authorized", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -122,8 +123,12 @@ describe "EditorController", ->
|
||||||
|
|
||||||
it "should mark the user as connected with the ConnectedUsersManager", ->
|
it "should mark the user as connected with the ConnectedUsersManager", ->
|
||||||
@ConnectedUsersManager.updateUserPosition.calledWith(@project_id, @client.id, @user, null).should.equal true
|
@ConnectedUsersManager.updateUserPosition.calledWith(@project_id, @client.id, @user, null).should.equal true
|
||||||
|
|
||||||
|
it "should remove the flag to send a user a message about the project being deleted", ->
|
||||||
|
@ProjectDeleter.unmarkAsDeletedByExternalSource
|
||||||
|
.calledWith(@project)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
describe "when not authorized", ->
|
describe "when not authorized", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@AuthorizationManager.getPrivilegeLevelForProject =
|
@AuthorizationManager.getPrivilegeLevelForProject =
|
||||||
|
|
|
@ -87,34 +87,34 @@ describe "PasswordResetController", ->
|
||||||
describe "setNewUserPassword", ->
|
describe "setNewUserPassword", ->
|
||||||
|
|
||||||
it "should tell the user handler to reset the password", (done)->
|
it "should tell the user handler to reset the password", (done)->
|
||||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true)
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@PasswordResetHandler.setNewUserPassword.calledWith(@token, @password).should.equal true
|
@PasswordResetHandler.setNewUserPassword.calledWith(@token, @password).should.equal true
|
||||||
done()
|
done()
|
||||||
@PasswordResetController.setNewUserPassword @req, @res
|
@PasswordResetController.setNewUserPassword @req, @res
|
||||||
|
|
||||||
it "should send a 500 if there is an error", (done)->
|
it "should send 404 if the token didn't work", (done)->
|
||||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, "error")
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, false)
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 500
|
code.should.equal 404
|
||||||
done()
|
done()
|
||||||
@PasswordResetController.setNewUserPassword @req, @res
|
@PasswordResetController.setNewUserPassword @req, @res
|
||||||
|
|
||||||
it "should error if there is no password", (done)->
|
it "should return 400 (Bad Request) if there is no password", (done)->
|
||||||
@req.body.password = ""
|
@req.body.password = ""
|
||||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 500
|
code.should.equal 400
|
||||||
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
||||||
done()
|
done()
|
||||||
@PasswordResetController.setNewUserPassword @req, @res
|
@PasswordResetController.setNewUserPassword @req, @res
|
||||||
|
|
||||||
it "should error if there is no password", (done)->
|
it "should return 400 (Bad Request) if there is no passwordResetToken", (done)->
|
||||||
@req.body.passwordResetToken = ""
|
@req.body.passwordResetToken = ""
|
||||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
||||||
@res.send = (code)=>
|
@res.send = (code)=>
|
||||||
code.should.equal 500
|
code.should.equal 400
|
||||||
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
||||||
done()
|
done()
|
||||||
@PasswordResetController.setNewUserPassword @req, @res
|
@PasswordResetController.setNewUserPassword @req, @res
|
||||||
|
|
|
@ -63,17 +63,18 @@ describe "PasswordResetHandler", ->
|
||||||
|
|
||||||
describe "setNewUserPassword", ->
|
describe "setNewUserPassword", ->
|
||||||
|
|
||||||
it "should return err if no user id can be found", (done)->
|
it "should return false if no user id can be found", (done)->
|
||||||
@PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1)
|
@PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1)
|
||||||
@PasswordResetHandler.setNewUserPassword @token, @password, (err)=>
|
@PasswordResetHandler.setNewUserPassword @token, @password, (err, found) =>
|
||||||
err.should.exists
|
found.should.equal false
|
||||||
@AuthenticationManager.setUserPassword.called.should.equal false
|
@AuthenticationManager.setUserPassword.called.should.equal false
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should set the user password", (done)->
|
it "should set the user password", (done)->
|
||||||
@PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1, null, @user_id)
|
@PasswordResetTokenHandler.getUserIdFromTokenAndExpire.callsArgWith(1, null, @user_id)
|
||||||
@AuthenticationManager.setUserPassword.callsArgWith(2)
|
@AuthenticationManager.setUserPassword.callsArgWith(2)
|
||||||
@PasswordResetHandler.setNewUserPassword @token, @password, (err)=>
|
@PasswordResetHandler.setNewUserPassword @token, @password, (err, found) =>
|
||||||
|
found.should.equal true
|
||||||
@AuthenticationManager.setUserPassword.calledWith(@user_id, @password).should.equal true
|
@AuthenticationManager.setUserPassword.calledWith(@user_id, @password).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
|
@ -198,8 +198,8 @@ describe "ProjectController", ->
|
||||||
first_name: 'James'
|
first_name: 'James'
|
||||||
'user-2':
|
'user-2':
|
||||||
first_name: 'Henry'
|
first_name: 'Henry'
|
||||||
|
@users[@user._id] = @user # Owner
|
||||||
@UserModel.findById = (id, fields, callback) =>
|
@UserModel.findById = (id, fields, callback) =>
|
||||||
fields.should.equal 'first_name last_name'
|
|
||||||
callback null, @users[id]
|
callback null, @users[id]
|
||||||
|
|
||||||
@LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, false)
|
@LimitationsManager.userHasSubscriptionOrIsGroupMember.callsArgWith(1, null, false)
|
||||||
|
@ -224,6 +224,12 @@ describe "ProjectController", ->
|
||||||
done()
|
done()
|
||||||
@ProjectController.projectListPage @req, @res
|
@ProjectController.projectListPage @req, @res
|
||||||
|
|
||||||
|
it "should send the user", (done)->
|
||||||
|
@res.render = (pageName, opts)=>
|
||||||
|
opts.user.should.deep.equal @user
|
||||||
|
done()
|
||||||
|
@ProjectController.projectListPage @req, @res
|
||||||
|
|
||||||
it "should inject the users", (done) ->
|
it "should inject the users", (done) ->
|
||||||
@res.render = (pageName, opts)=>
|
@res.render = (pageName, opts)=>
|
||||||
opts.projects[0].owner.should.equal (@users[@projects[0].owner_ref])
|
opts.projects[0].owner.should.equal (@users[@projects[0].owner_ref])
|
||||||
|
@ -275,6 +281,7 @@ describe "ProjectController", ->
|
||||||
@UserModel.findById.callsArgWith(1, null, @user)
|
@UserModel.findById.callsArgWith(1, null, @user)
|
||||||
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
||||||
@SecurityManager.userCanAccessProject.callsArgWith 2, true, "owner"
|
@SecurityManager.userCanAccessProject.callsArgWith 2, true, "owner"
|
||||||
|
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||||
|
|
||||||
it "should render the project/editor page", (done)->
|
it "should render the project/editor page", (done)->
|
||||||
@res.render = (pageName, opts)=>
|
@res.render = (pageName, opts)=>
|
||||||
|
|
|
@ -30,6 +30,7 @@ describe 'Project deleter', ->
|
||||||
'../../models/Project':{Project:@Project}
|
'../../models/Project':{Project:@Project}
|
||||||
'../DocumentUpdater/DocumentUpdaterHandler': @documentUpdaterHandler
|
'../DocumentUpdater/DocumentUpdaterHandler': @documentUpdaterHandler
|
||||||
"../Tags/TagsHandler":@TagsHandler
|
"../Tags/TagsHandler":@TagsHandler
|
||||||
|
"../FileStore/FileStoreHandler": @FileStoreHandler = {}
|
||||||
'logger-sharelatex':
|
'logger-sharelatex':
|
||||||
log:->
|
log:->
|
||||||
|
|
||||||
|
@ -46,6 +47,32 @@ describe 'Project deleter', ->
|
||||||
@deleter.markAsDeletedByExternalSource project_id, =>
|
@deleter.markAsDeletedByExternalSource project_id, =>
|
||||||
@editorController.notifyUsersProjectHasBeenDeletedOrRenamed.calledWith(project_id).should.equal true
|
@editorController.notifyUsersProjectHasBeenDeletedOrRenamed.calledWith(project_id).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe "unmarkAsDeletedByExternalSource", ->
|
||||||
|
beforeEach ->
|
||||||
|
@Project.update = sinon.stub().callsArg(3)
|
||||||
|
@callback = sinon.stub()
|
||||||
|
@project = {
|
||||||
|
_id: @project_id
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "when the project does not have the flag set", ->
|
||||||
|
beforeEach ->
|
||||||
|
@project.deletedByExternalDataSource = false
|
||||||
|
@deleter.unmarkAsDeletedByExternalSource @project, @callback
|
||||||
|
|
||||||
|
it "should not update the project", ->
|
||||||
|
@Project.update.called.should.equal false
|
||||||
|
|
||||||
|
describe "when the project does have the flag set", ->
|
||||||
|
beforeEach ->
|
||||||
|
@project.deletedByExternalDataSource = true
|
||||||
|
@deleter.unmarkAsDeletedByExternalSource @project, @callback
|
||||||
|
|
||||||
|
it "should remove the flag from the project", ->
|
||||||
|
@Project.update
|
||||||
|
.calledWith({_id: @project_id}, {deletedByExternalDataSource:false})
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
describe "deleteUsersProjects", ->
|
describe "deleteUsersProjects", ->
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ modulePath = require('path').join __dirname, '../../../../app/js/Features/ThirdP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe 'third party data store', ->
|
describe 'TpdsController', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@updateHandler = {}
|
@TpdsUpdateHandler = {}
|
||||||
@controller = SandboxedModule.require modulePath, requires:
|
@TpdsController = SandboxedModule.require modulePath, requires:
|
||||||
'./TpdsUpdateHandler':@updateHandler
|
'./TpdsUpdateHandler':@TpdsUpdateHandler
|
||||||
|
'./UpdateMerger': @UpdateMerger = {}
|
||||||
'logger-sharelatex':
|
'logger-sharelatex':
|
||||||
log:->
|
log:->
|
||||||
err:->
|
err:->
|
||||||
|
@ -24,11 +25,11 @@ describe 'third party data store', ->
|
||||||
params:{0:path, "user_id":@user_id}
|
params:{0:path, "user_id":@user_id}
|
||||||
session:
|
session:
|
||||||
destroy:->
|
destroy:->
|
||||||
@updateHandler.newUpdate = sinon.stub().callsArg(5)
|
@TpdsUpdateHandler.newUpdate = sinon.stub().callsArg(5)
|
||||||
res = send: =>
|
res = send: =>
|
||||||
@updateHandler.newUpdate.calledWith(@user_id, "projectName","/here.txt", req).should.equal true
|
@TpdsUpdateHandler.newUpdate.calledWith(@user_id, "projectName","/here.txt", req).should.equal true
|
||||||
done()
|
done()
|
||||||
@controller.mergeUpdate req, res
|
@TpdsController.mergeUpdate req, res
|
||||||
|
|
||||||
describe 'getting a delete update', ->
|
describe 'getting a delete update', ->
|
||||||
it 'should process the delete with the update reciver', (done)->
|
it 'should process the delete with the update reciver', (done)->
|
||||||
|
@ -37,18 +38,18 @@ describe 'third party data store', ->
|
||||||
params:{0:path, "user_id":@user_id}
|
params:{0:path, "user_id":@user_id}
|
||||||
session:
|
session:
|
||||||
destroy:->
|
destroy:->
|
||||||
@updateHandler.deleteUpdate = sinon.stub().callsArg(4)
|
@TpdsUpdateHandler.deleteUpdate = sinon.stub().callsArg(4)
|
||||||
res = send: =>
|
res = send: =>
|
||||||
@updateHandler.deleteUpdate.calledWith(@user_id, "projectName", "/here.txt").should.equal true
|
@TpdsUpdateHandler.deleteUpdate.calledWith(@user_id, "projectName", "/here.txt").should.equal true
|
||||||
done()
|
done()
|
||||||
@controller.deleteUpdate req, res
|
@TpdsController.deleteUpdate req, res
|
||||||
|
|
||||||
describe 'parseParams', ->
|
describe 'parseParams', ->
|
||||||
|
|
||||||
it 'should take the project name off the start and replace with slash', ->
|
it 'should take the project name off the start and replace with slash', ->
|
||||||
path = "noSlashHere"
|
path = "noSlashHere"
|
||||||
req = params:{0:path, user_id:@user_id}
|
req = params:{0:path, user_id:@user_id}
|
||||||
result = @controller.parseParams(req)
|
result = @TpdsController.parseParams(req)
|
||||||
result.user_id.should.equal @user_id
|
result.user_id.should.equal @user_id
|
||||||
result.filePath.should.equal "/"
|
result.filePath.should.equal "/"
|
||||||
result.projectName.should.equal path
|
result.projectName.should.equal path
|
||||||
|
@ -57,7 +58,7 @@ describe 'third party data store', ->
|
||||||
it 'should take the project name off the start and return it with no slashes in', ->
|
it 'should take the project name off the start and return it with no slashes in', ->
|
||||||
path = "/project/file.tex"
|
path = "/project/file.tex"
|
||||||
req = params:{0:path, user_id:@user_id}
|
req = params:{0:path, user_id:@user_id}
|
||||||
result = @controller.parseParams(req)
|
result = @TpdsController.parseParams(req)
|
||||||
result.user_id.should.equal @user_id
|
result.user_id.should.equal @user_id
|
||||||
result.filePath.should.equal "/file.tex"
|
result.filePath.should.equal "/file.tex"
|
||||||
result.projectName.should.equal "project"
|
result.projectName.should.equal "project"
|
||||||
|
@ -65,8 +66,57 @@ describe 'third party data store', ->
|
||||||
it 'should take the project name of and return a slash for the file path', ->
|
it 'should take the project name of and return a slash for the file path', ->
|
||||||
path = "/project_name"
|
path = "/project_name"
|
||||||
req = params:{0:path, user_id:@user_id}
|
req = params:{0:path, user_id:@user_id}
|
||||||
result = @controller.parseParams(req)
|
result = @TpdsController.parseParams(req)
|
||||||
result.projectName.should.equal "project_name"
|
result.projectName.should.equal "project_name"
|
||||||
result.filePath.should.equal "/"
|
result.filePath.should.equal "/"
|
||||||
|
|
||||||
|
describe 'updateProjectContents', ->
|
||||||
|
beforeEach ->
|
||||||
|
@UpdateMerger.mergeUpdate = sinon.stub().callsArg(3)
|
||||||
|
@req =
|
||||||
|
params:
|
||||||
|
0: @path = "chapters/main.tex"
|
||||||
|
project_id: @project_id = "project-id-123"
|
||||||
|
session:
|
||||||
|
destroy: sinon.stub()
|
||||||
|
@res =
|
||||||
|
send: sinon.stub()
|
||||||
|
|
||||||
|
@TpdsController.updateProjectContents @req, @res
|
||||||
|
|
||||||
|
it "should merge the update", ->
|
||||||
|
@UpdateMerger.mergeUpdate
|
||||||
|
.calledWith(@project_id, "/" + @path, @req)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should return a success", ->
|
||||||
|
@res.send.calledWith(200).should.equal true
|
||||||
|
|
||||||
|
it "should clear the session", ->
|
||||||
|
@req.session.destroy.called.should.equal true
|
||||||
|
|
||||||
|
describe 'deleteProjectContents', ->
|
||||||
|
beforeEach ->
|
||||||
|
@UpdateMerger.deleteUpdate = sinon.stub().callsArg(2)
|
||||||
|
@req =
|
||||||
|
params:
|
||||||
|
0: @path = "chapters/main.tex"
|
||||||
|
project_id: @project_id = "project-id-123"
|
||||||
|
session:
|
||||||
|
destroy: sinon.stub()
|
||||||
|
@res =
|
||||||
|
send: sinon.stub()
|
||||||
|
|
||||||
|
@TpdsController.deleteProjectContents @req, @res
|
||||||
|
|
||||||
|
it "should delete the file", ->
|
||||||
|
@UpdateMerger.deleteUpdate
|
||||||
|
.calledWith(@project_id, "/" + @path)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should return a success", ->
|
||||||
|
@res.send.calledWith(200).should.equal true
|
||||||
|
|
||||||
|
it "should clear the session", ->
|
||||||
|
@req.session.destroy.called.should.equal true
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue