mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into angular_1.3.15
This commit is contained in:
commit
aa06eb55b7
83 changed files with 1150 additions and 552 deletions
|
@ -280,7 +280,7 @@ module.exports = (grunt) ->
|
||||||
|
|
||||||
grunt.registerTask 'install', "Compile everything when installing as an npm module", ['compile']
|
grunt.registerTask 'install', "Compile everything when installing as an npm module", ['compile']
|
||||||
|
|
||||||
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:modules:server', 'compile:unit_tests', 'compile:modules:unit_tests', 'mochaTest:unit'].concat(moduleUnitTestTasks)
|
||||||
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 'test:modules:unit', 'Run the unit tests for the modules', ['compile:modules:server', 'compile:modules:unit_tests'].concat(moduleUnitTestTasks)
|
||||||
|
|
|
@ -23,9 +23,6 @@ Server.app.use (error, req, res, next) ->
|
||||||
res.end()
|
res.end()
|
||||||
|
|
||||||
if Settings.catchErrors
|
if Settings.catchErrors
|
||||||
# fairy cleans then exits on an uncaughtError, but we don't want
|
|
||||||
# to exit so it doesn't need to do this.
|
|
||||||
require "fairy"
|
|
||||||
process.removeAllListeners "uncaughtException"
|
process.removeAllListeners "uncaughtException"
|
||||||
process.on "uncaughtException", (error) ->
|
process.on "uncaughtException", (error) ->
|
||||||
logger.error err: error, "uncaughtException"
|
logger.error err: error, "uncaughtException"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
AnalyticsController = require('./AnalyticsController')
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
apply: (webRouter, apiRouter) ->
|
||||||
|
webRouter.post '/event/:event', AnalyticsController.recordEvent
|
|
@ -7,6 +7,8 @@ logger = require("logger-sharelatex")
|
||||||
querystring = require('querystring')
|
querystring = require('querystring')
|
||||||
Url = require("url")
|
Url = require("url")
|
||||||
Settings = require "settings-sharelatex"
|
Settings = require "settings-sharelatex"
|
||||||
|
basicAuth = require('basic-auth-connect')
|
||||||
|
|
||||||
|
|
||||||
module.exports = AuthenticationController =
|
module.exports = AuthenticationController =
|
||||||
login: (req, res, next = (error) ->) ->
|
login: (req, res, next = (error) ->) ->
|
||||||
|
@ -101,7 +103,7 @@ module.exports = AuthenticationController =
|
||||||
logger.log url:req.url, "user trying to access endpoint not in global whitelist"
|
logger.log url:req.url, "user trying to access endpoint not in global whitelist"
|
||||||
return res.redirect "/login"
|
return res.redirect "/login"
|
||||||
|
|
||||||
httpAuth: require('express').basicAuth (user, pass)->
|
httpAuth: basicAuth (user, pass)->
|
||||||
isValid = Settings.httpAuthUsers[user] == pass
|
isValid = Settings.httpAuthUsers[user] == pass
|
||||||
if !isValid
|
if !isValid
|
||||||
logger.err user:user, pass:pass, "invalid login details"
|
logger.err user:user, pass:pass, "invalid login details"
|
||||||
|
@ -152,6 +154,7 @@ module.exports = AuthenticationController =
|
||||||
# Regenerate the session to get a new sessionID (cookie value) to
|
# Regenerate the session to get a new sessionID (cookie value) to
|
||||||
# protect against session fixation attacks
|
# protect against session fixation attacks
|
||||||
oldSession = req.session
|
oldSession = req.session
|
||||||
|
req.session.destroy()
|
||||||
req.sessionStore.generate(req)
|
req.sessionStore.generate(req)
|
||||||
for key, value of oldSession
|
for key, value of oldSession
|
||||||
req.session[key] = value
|
req.session[key] = value
|
||||||
|
|
|
@ -22,9 +22,10 @@ module.exports = BlogController =
|
||||||
|
|
||||||
logger.log url:url, "proxying request to blog api"
|
logger.log url:url, "proxying request to blog api"
|
||||||
request.get blogUrl, (err, r, data)->
|
request.get blogUrl, (err, r, data)->
|
||||||
return next(err) if err?
|
|
||||||
if r?.statusCode == 404
|
if r?.statusCode == 404
|
||||||
return ErrorController.notFound(req, res, next)
|
return ErrorController.notFound(req, res, next)
|
||||||
|
if err?
|
||||||
|
return res.send 500
|
||||||
data = data.trim()
|
data = data.trim()
|
||||||
try
|
try
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
|
|
|
@ -12,7 +12,7 @@ module.exports =
|
||||||
ChatHandler.sendMessage project_id, user_id, messageContent, (err, builtMessge)->
|
ChatHandler.sendMessage project_id, user_id, messageContent, (err, builtMessge)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, project_id:project_id, user_id:user_id, messageContent:messageContent, "problem sending message to chat api"
|
logger.err err:err, project_id:project_id, user_id:user_id, messageContent:messageContent, "problem sending message to chat api"
|
||||||
return res.send(500)
|
return res.sendStatus(500)
|
||||||
EditorRealTimeController.emitToRoom project_id, "new-chat-message", builtMessge, (err)->
|
EditorRealTimeController.emitToRoom project_id, "new-chat-message", builtMessge, (err)->
|
||||||
res.send()
|
res.send()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ module.exports =
|
||||||
ChatHandler.getMessages project_id, query, (err, messages)->
|
ChatHandler.getMessages project_id, query, (err, messages)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, query:query, "problem getting messages from chat api"
|
logger.err err:err, query:query, "problem getting messages from chat api"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
logger.log length:messages?.length, "sending messages to client"
|
logger.log length:messages?.length, "sending messages to client"
|
||||||
res.set 'Content-Type', 'application/json'
|
res.set 'Content-Type', 'application/json'
|
||||||
res.send messages
|
res.send messages
|
||||||
|
|
|
@ -4,7 +4,6 @@ EditorController = require "../Editor/EditorController"
|
||||||
|
|
||||||
module.exports = CollaboratorsController =
|
module.exports = CollaboratorsController =
|
||||||
getCollaborators: (req, res, next = (error) ->) ->
|
getCollaborators: (req, res, next = (error) ->) ->
|
||||||
req.session.destroy()
|
|
||||||
ProjectGetter.getProject req.params.Project_id, { owner_ref: true, collaberator_refs: true, readOnly_refs: true}, (error, project) ->
|
ProjectGetter.getProject req.params.Project_id, { owner_ref: true, collaberator_refs: true, readOnly_refs: true}, (error, project) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
ProjectGetter.populateProjectWithUsers project, (error, project) ->
|
ProjectGetter.populateProjectWithUsers project, (error, project) ->
|
||||||
|
@ -19,7 +18,7 @@ module.exports = CollaboratorsController =
|
||||||
return next(new Error("User should be logged in"))
|
return next(new Error("User should be logged in"))
|
||||||
CollaboratorsHandler.removeUserFromProject req.params.project_id, user_id, (error) ->
|
CollaboratorsHandler.removeUserFromProject req.params.project_id, user_id, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 204
|
res.sendStatus 204
|
||||||
|
|
||||||
addUserToProject: (req, res, next) ->
|
addUserToProject: (req, res, next) ->
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
@ -33,7 +32,7 @@ module.exports = CollaboratorsController =
|
||||||
user_id = req.params.user_id
|
user_id = req.params.user_id
|
||||||
EditorController.removeUserFromProject project_id, user_id, (error)->
|
EditorController.removeUserFromProject project_id, user_id, (error)->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 204
|
res.sendStatus 204
|
||||||
|
|
||||||
_formatCollaborators: (project, callback = (error, collaborators) ->) ->
|
_formatCollaborators: (project, callback = (error, collaborators) ->) ->
|
||||||
collaborators = []
|
collaborators = []
|
||||||
|
|
|
@ -3,9 +3,9 @@ SecurityManager = require('../../managers/SecurityManager')
|
||||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
webRouter.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
||||||
app.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
apiRouter.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
||||||
|
|
||||||
app.post '/project/:Project_id/users', SecurityManager.requestIsOwner, CollaboratorsController.addUserToProject
|
webRouter.post '/project/:Project_id/users', SecurityManager.requestIsOwner, CollaboratorsController.addUserToProject
|
||||||
app.delete '/project/:Project_id/users/:user_id', SecurityManager.requestIsOwner, CollaboratorsController.removeUserFromProject
|
webRouter.delete '/project/:Project_id/users/:user_id', SecurityManager.requestIsOwner, CollaboratorsController.removeUserFromProject
|
||||||
|
|
|
@ -51,14 +51,14 @@ module.exports = CompileController =
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
CompileManager.deleteAuxFiles project_id, (error) ->
|
CompileManager.deleteAuxFiles project_id, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
compileAndDownloadPdf: (req, res, next)->
|
compileAndDownloadPdf: (req, res, next)->
|
||||||
project_id = req.params.project_id
|
project_id = req.params.project_id
|
||||||
CompileManager.compile project_id, null, {}, (err)->
|
CompileManager.compile project_id, null, {}, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
|
logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
url = "/project/#{project_id}/output/output.pdf"
|
url = "/project/#{project_id}/output/output.pdf"
|
||||||
CompileController.proxyToClsi project_id, url, req, res, next
|
CompileController.proxyToClsi project_id, url, req, res, next
|
||||||
|
|
||||||
|
|
|
@ -67,3 +67,31 @@ module.exports = DocstoreManager =
|
||||||
error = new Error("docstore api responded with non-success code: #{res.statusCode}")
|
error = new Error("docstore api responded with non-success code: #{res.statusCode}")
|
||||||
logger.error err: error, project_id: project_id, doc_id: doc_id, "error updating doc in docstore"
|
logger.error err: error, project_id: project_id, doc_id: doc_id, "error updating doc in docstore"
|
||||||
callback(error)
|
callback(error)
|
||||||
|
|
||||||
|
archiveProject: (project_id, callback)->
|
||||||
|
url = "#{settings.apis.docstore.url}/project/#{project_id}/archive"
|
||||||
|
logger.log project_id:project_id, "archiving project in docstore"
|
||||||
|
request.post url, (err, res, docs) ->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_id:project_id, "error archving project in docstore"
|
||||||
|
return callback(err)
|
||||||
|
if 200 <= res.statusCode < 300
|
||||||
|
callback()
|
||||||
|
else
|
||||||
|
error = new Error("docstore api responded with non-success code: #{res.statusCode}")
|
||||||
|
logger.err err: error, project_id: project_id, "error archiving project in docstore"
|
||||||
|
return callback(error)
|
||||||
|
|
||||||
|
unarchiveProject: (project_id, callback)->
|
||||||
|
url = "#{settings.apis.docstore.url}/project/#{project_id}/unarchive"
|
||||||
|
logger.log project_id:project_id, "unarchiving project in docstore"
|
||||||
|
request.post url, (err, res, docs) ->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_id:project_id, "error unarchiving project in docstore"
|
||||||
|
return callback(err)
|
||||||
|
if 200 <= res.statusCode < 300
|
||||||
|
callback()
|
||||||
|
else
|
||||||
|
error = new Error("docstore api responded with non-success code: #{res.statusCode}")
|
||||||
|
logger.err err: error, project_id: project_id, "error unarchiving project in docstore"
|
||||||
|
return callback(error)
|
|
@ -15,7 +15,6 @@ module.exports =
|
||||||
res.send JSON.stringify {
|
res.send JSON.stringify {
|
||||||
lines: lines
|
lines: lines
|
||||||
}
|
}
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
setDocument: (req, res, next = (error) ->) ->
|
setDocument: (req, res, next = (error) ->) ->
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
@ -27,8 +26,7 @@ module.exports =
|
||||||
logger.err err:error, doc_id:doc_id, project_id:project_id, "error finding element for getDocument"
|
logger.err err:error, doc_id:doc_id, project_id:project_id, "error finding element for getDocument"
|
||||||
return next(error)
|
return next(error)
|
||||||
logger.log doc_id:doc_id, project_id:project_id, "finished receiving set document request from api (docupdater)"
|
logger.log doc_id:doc_id, project_id:project_id, "finished receiving set document request from api (docupdater)"
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ module.exports = EditorHttpController =
|
||||||
name = req.body.name
|
name = req.body.name
|
||||||
|
|
||||||
if !name?
|
if !name?
|
||||||
return res.send 400 # Malformed request
|
return res.sendStatus 400 # Malformed request
|
||||||
|
|
||||||
logger.log project_id: project_id, doc_id: doc_id, "restoring doc"
|
logger.log project_id: project_id, doc_id: doc_id, "restoring doc"
|
||||||
ProjectEntityHandler.restoreDoc project_id, doc_id, name, (err, doc, folder_id) =>
|
ProjectEntityHandler.restoreDoc project_id, doc_id, name, (err, doc, folder_id) =>
|
||||||
|
@ -68,7 +68,7 @@ module.exports = EditorHttpController =
|
||||||
name = req.body.name
|
name = req.body.name
|
||||||
parent_folder_id = req.body.parent_folder_id
|
parent_folder_id = req.body.parent_folder_id
|
||||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||||
return res.send 400
|
return res.sendStatus 400
|
||||||
EditorController.addDoc project_id, parent_folder_id, name, [], "editor", (error, doc) ->
|
EditorController.addDoc project_id, parent_folder_id, name, [], "editor", (error, doc) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.json doc
|
res.json doc
|
||||||
|
@ -78,7 +78,7 @@ module.exports = EditorHttpController =
|
||||||
name = req.body.name
|
name = req.body.name
|
||||||
parent_folder_id = req.body.parent_folder_id
|
parent_folder_id = req.body.parent_folder_id
|
||||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||||
return res.send 400
|
return res.sendStatus 400
|
||||||
EditorController.addFolder project_id, parent_folder_id, name, "editor", (error, doc) ->
|
EditorController.addFolder project_id, parent_folder_id, name, "editor", (error, doc) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.json doc
|
res.json doc
|
||||||
|
@ -89,10 +89,10 @@ module.exports = EditorHttpController =
|
||||||
entity_type = req.params.entity_type
|
entity_type = req.params.entity_type
|
||||||
name = req.body.name
|
name = req.body.name
|
||||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||||
return res.send 400
|
return res.sendStatus 400
|
||||||
EditorController.renameEntity project_id, entity_id, entity_type, name, (error) ->
|
EditorController.renameEntity project_id, entity_id, entity_type, name, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 204
|
res.sendStatus 204
|
||||||
|
|
||||||
moveEntity: (req, res, next) ->
|
moveEntity: (req, res, next) ->
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
@ -101,7 +101,7 @@ module.exports = EditorHttpController =
|
||||||
folder_id = req.body.folder_id
|
folder_id = req.body.folder_id
|
||||||
EditorController.moveEntity project_id, entity_id, folder_id, entity_type, (error) ->
|
EditorController.moveEntity project_id, entity_id, folder_id, entity_type, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 204
|
res.sendStatus 204
|
||||||
|
|
||||||
deleteDoc: (req, res, next)->
|
deleteDoc: (req, res, next)->
|
||||||
req.params.entity_type = "doc"
|
req.params.entity_type = "doc"
|
||||||
|
@ -121,6 +121,6 @@ module.exports = EditorHttpController =
|
||||||
entity_type = req.params.entity_type
|
entity_type = req.params.entity_type
|
||||||
EditorController.deleteEntity project_id, entity_id, entity_type, "editor", (error) ->
|
EditorController.deleteEntity project_id, entity_id, entity_type, "editor", (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 204
|
res.sendStatus 204
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,20 @@ SecurityManager = require('../../managers/SecurityManager')
|
||||||
AuthenticationController = require "../Authentication/AuthenticationController"
|
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
app.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
webRouter.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
||||||
app.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
|
webRouter.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
|
||||||
|
|
||||||
app.post '/project/:Project_id/:entity_type/:entity_id/rename', SecurityManager.requestCanModifyProject, EditorHttpController.renameEntity
|
webRouter.post '/project/:Project_id/:entity_type/:entity_id/rename', SecurityManager.requestCanModifyProject, EditorHttpController.renameEntity
|
||||||
app.post '/project/:Project_id/:entity_type/:entity_id/move', SecurityManager.requestCanModifyProject, EditorHttpController.moveEntity
|
webRouter.post '/project/:Project_id/:entity_type/:entity_id/move', SecurityManager.requestCanModifyProject, EditorHttpController.moveEntity
|
||||||
|
|
||||||
app.delete '/project/:Project_id/file/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFile
|
webRouter.delete '/project/:Project_id/file/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFile
|
||||||
app.delete '/project/:Project_id/doc/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteDoc
|
webRouter.delete '/project/:Project_id/doc/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteDoc
|
||||||
app.delete '/project/:Project_id/folder/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFolder
|
webRouter.delete '/project/:Project_id/folder/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFolder
|
||||||
|
|
||||||
app.post '/project/:Project_id/doc/:doc_id/restore', SecurityManager.requestCanModifyProject, EditorHttpController.restoreDoc
|
webRouter.post '/project/:Project_id/doc/:doc_id/restore', SecurityManager.requestCanModifyProject, EditorHttpController.restoreDoc
|
||||||
|
|
||||||
# Called by the real-time API to load up the current project state.
|
# Called by the real-time API to load up the current project state.
|
||||||
# This is a post request because it's more than just a getting of data. We take actions
|
# This is a post request because it's more than just a getting of data. We take actions
|
||||||
# whenever a user joins a project, like updating the deleted status.
|
# whenever a user joins a project, like updating the deleted status.
|
||||||
app.post '/project/:Project_id/join', AuthenticationController.httpAuth, EditorHttpController.joinProject
|
apiRouter.post '/project/:Project_id/join', AuthenticationController.httpAuth, EditorHttpController.joinProject
|
||||||
app.ignoreCsrf('post', '/project/:Project_id/join')
|
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
logger = require('logger-sharelatex')
|
logger = require('logger-sharelatex')
|
||||||
FileStoreHandler = require("./FileStoreHandler")
|
FileStoreHandler = require("./FileStoreHandler")
|
||||||
ProjectLocator = require("../Project/ProjectLocator")
|
ProjectLocator = require("../Project/ProjectLocator")
|
||||||
|
_ = require('underscore')
|
||||||
|
|
||||||
|
is_mobile_safari = (user_agent) ->
|
||||||
|
user_agent and (user_agent.indexOf('iPhone') >= 0 or
|
||||||
|
user_agent.indexOf('iPad') >= 0)
|
||||||
|
|
||||||
|
is_html = (file) ->
|
||||||
|
ends_with = (ext) ->
|
||||||
|
file.name? and
|
||||||
|
file.name.length > ext.length and
|
||||||
|
(file.name.lastIndexOf(ext) == file.name.length - ext.length)
|
||||||
|
|
||||||
|
ends_with('.html') or ends_with('.htm') or ends_with('.xhtml')
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
|
|
||||||
|
@ -8,14 +21,19 @@ module.exports =
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
file_id = req.params.File_id
|
file_id = req.params.File_id
|
||||||
queryString = req.query
|
queryString = req.query
|
||||||
|
user_agent = req.get('User-Agent')
|
||||||
logger.log project_id: project_id, file_id: file_id, queryString:queryString, "file download"
|
logger.log project_id: project_id, file_id: file_id, queryString:queryString, "file download"
|
||||||
ProjectLocator.findElement {project_id: project_id, element_id: file_id, type: "file"}, (err, file)->
|
ProjectLocator.findElement {project_id: project_id, element_id: file_id, type: "file"}, (err, file)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, project_id: project_id, file_id: file_id, queryString:queryString, "error finding element for downloading file"
|
logger.err err:err, project_id: project_id, file_id: file_id, queryString:queryString, "error finding element for downloading file"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
FileStoreHandler.getFileStream project_id, file_id, queryString, (err, stream)->
|
FileStoreHandler.getFileStream project_id, file_id, queryString, (err, stream)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, project_id: project_id, file_id: file_id, queryString:queryString, "error getting file stream for downloading file"
|
logger.err err:err, project_id: project_id, file_id: file_id, queryString:queryString, "error getting file stream for downloading file"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
|
# mobile safari will try to render html files, prevent this
|
||||||
|
if (is_mobile_safari(user_agent) and is_html(file))
|
||||||
|
logger.log filename: file.name, user_agent: user_agent, "sending html file to mobile-safari as plain text"
|
||||||
|
res.setHeader('Content-Type', 'text/plain')
|
||||||
res.setHeader("Content-Disposition", "attachment; filename=#{file.name}")
|
res.setHeader("Content-Disposition", "attachment; filename=#{file.name}")
|
||||||
stream.pipe res
|
stream.pipe res
|
|
@ -32,9 +32,9 @@ module.exports = HealthCheckController =
|
||||||
|
|
||||||
checkRedis: (req, res, next)->
|
checkRedis: (req, res, next)->
|
||||||
if redisCheck.isAlive()
|
if redisCheck.isAlive()
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
else
|
else
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
|
|
||||||
Reporter = (res) ->
|
Reporter = (res) ->
|
||||||
(runner) ->
|
(runner) ->
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
InactiveProjectManager = require("./InactiveProjectManager")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
|
||||||
|
deactivateOldProjects: (req, res)->
|
||||||
|
logger.log "recived request to deactivate old projects"
|
||||||
|
numberOfProjectsToArchive = req.body.numberOfProjectsToArchive
|
||||||
|
ageOfProjects = req.body.ageOfProjects
|
||||||
|
InactiveProjectManager.deactivateOldProjects numberOfProjectsToArchive, ageOfProjects, (err, projectsDeactivated)->
|
||||||
|
if err?
|
||||||
|
res.sendStatus(500)
|
||||||
|
else
|
||||||
|
res.send(projectsDeactivated)
|
||||||
|
|
||||||
|
|
||||||
|
deactivateProject: (req, res)->
|
||||||
|
project_id = req.params.project_id
|
||||||
|
logger.log project_id:project_id, "recived request to deactivating project"
|
||||||
|
InactiveProjectManager.deactivateProject project_id, (err)->
|
||||||
|
if err?
|
||||||
|
res.sendStatus 500
|
||||||
|
else
|
||||||
|
res.sendStatus 200
|
|
@ -0,0 +1,56 @@
|
||||||
|
async = require("async")
|
||||||
|
_ = require("underscore")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
DocstoreManager = require("../Docstore/DocstoreManager")
|
||||||
|
ProjectGetter = require("../Project/ProjectGetter")
|
||||||
|
ProjectUpdateHandler = require("../Project/ProjectUpdateHandler")
|
||||||
|
Project = require("../../models/Project").Project
|
||||||
|
|
||||||
|
MILISECONDS_IN_DAY = 86400000
|
||||||
|
module.exports = InactiveProjectManager =
|
||||||
|
|
||||||
|
reactivateProjectIfRequired: (project_id, callback)->
|
||||||
|
ProjectGetter.getProject project_id, {active:true}, (err, project)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_id:project_id, "error getting project"
|
||||||
|
return callback(err)
|
||||||
|
logger.log project_id:project_id, active:project.active, "seeing if need to reactivate project"
|
||||||
|
|
||||||
|
if project.active
|
||||||
|
return callback()
|
||||||
|
|
||||||
|
DocstoreManager.unarchiveProject project_id, (err)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_id:project_id, "error reactivating project in docstore"
|
||||||
|
return callback(err)
|
||||||
|
ProjectUpdateHandler.markAsActive project_id, callback
|
||||||
|
|
||||||
|
deactivateOldProjects: (limit = 10, daysOld = 360, callback)->
|
||||||
|
oldProjectDate = new Date() - (MILISECONDS_IN_DAY * daysOld)
|
||||||
|
logger.log oldProjectDate:oldProjectDate, limit:limit, daysOld:daysOld, "starting process of deactivating old projects"
|
||||||
|
Project.find()
|
||||||
|
.where("lastOpened").lt(oldProjectDate)
|
||||||
|
.where("active").equals(true)
|
||||||
|
.select("_id")
|
||||||
|
.limit(limit)
|
||||||
|
.exec (err, projects)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, "could not get projects for deactivating"
|
||||||
|
jobs = _.map projects, (project)->
|
||||||
|
return (cb)->
|
||||||
|
InactiveProjectManager.deactivateProject project._id, cb
|
||||||
|
logger.log numberOfProjects:projects?.length, "deactivating projects"
|
||||||
|
async.series jobs, (err)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, "error deactivating projects"
|
||||||
|
callback err, projects
|
||||||
|
|
||||||
|
|
||||||
|
deactivateProject: (project_id, callback)->
|
||||||
|
logger.log project_id:project_id, "deactivating inactive project"
|
||||||
|
DocstoreManager.archiveProject project_id, (err)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_id:project_id, "error deactivating project in docstore"
|
||||||
|
return callback(err)
|
||||||
|
ProjectUpdateHandler.markAsInactive project_id, callback
|
||||||
|
|
|
@ -23,22 +23,28 @@ module.exports =
|
||||||
if err?
|
if err?
|
||||||
res.send 500, {message:err?.message}
|
res.send 500, {message:err?.message}
|
||||||
else if exists
|
else if exists
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
else
|
else
|
||||||
res.send 404, {message: req.i18n.translate("cant_find_email")}
|
res.send 404, {message: req.i18n.translate("cant_find_email")}
|
||||||
|
|
||||||
renderSetPasswordForm: (req, res)->
|
renderSetPasswordForm: (req, res)->
|
||||||
|
if req.query.passwordResetToken?
|
||||||
|
req.session.resetToken = req.query.passwordResetToken
|
||||||
|
return res.redirect('/user/password/set')
|
||||||
|
if !req.session.resetToken?
|
||||||
|
return res.redirect('/user/password/reset')
|
||||||
res.render "user/setPassword",
|
res.render "user/setPassword",
|
||||||
title:"set_password"
|
title:"set_password"
|
||||||
passwordResetToken:req.query.passwordResetToken
|
passwordResetToken: req.session.resetToken
|
||||||
|
|
||||||
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 400
|
return res.sendStatus 400
|
||||||
|
delete req.session.resetToken
|
||||||
PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err, found) ->
|
PasswordResetHandler.setNewUserPassword passwordResetToken?.trim(), password?.trim(), (err, found) ->
|
||||||
return next(err) if err?
|
return next(err) if err?
|
||||||
if found
|
if found
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
else
|
else
|
||||||
res.send 404, {message: req.i18n.translate("password_reset_token_expired")}
|
res.send 404, {message: req.i18n.translate("password_reset_token_expired")}
|
|
@ -2,13 +2,13 @@ PasswordResetController = require("./PasswordResetController")
|
||||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
|
|
||||||
app.get '/user/password/reset', PasswordResetController.renderRequestResetForm
|
webRouter.get '/user/password/reset', PasswordResetController.renderRequestResetForm
|
||||||
app.post '/user/password/reset', PasswordResetController.requestReset
|
webRouter.post '/user/password/reset', PasswordResetController.requestReset
|
||||||
AuthenticationController.addEndpointToLoginWhitelist '/user/password/reset'
|
AuthenticationController.addEndpointToLoginWhitelist '/user/password/reset'
|
||||||
|
|
||||||
app.get '/user/password/set', PasswordResetController.renderSetPasswordForm
|
webRouter.get '/user/password/set', PasswordResetController.renderSetPasswordForm
|
||||||
app.post '/user/password/set', PasswordResetController.setNewUserPassword
|
webRouter.post '/user/password/set', PasswordResetController.setNewUserPassword
|
||||||
AuthenticationController.addEndpointToLoginWhitelist '/user/password/set'
|
AuthenticationController.addEndpointToLoginWhitelist '/user/password/set'
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ module.exports =
|
||||||
ProjectDetailsHandler.getDetails project_id, (err, projDetails)->
|
ProjectDetailsHandler.getDetails project_id, (err, projDetails)->
|
||||||
if err?
|
if err?
|
||||||
logger.log err:err, project_id:project_id, "something went wrong getting project details"
|
logger.log err:err, project_id:project_id, "something went wrong getting project details"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
req.session.destroy()
|
|
||||||
res.json(projDetails)
|
res.json(projDetails)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ _ = require("underscore")
|
||||||
Settings = require("settings-sharelatex")
|
Settings = require("settings-sharelatex")
|
||||||
SecurityManager = require("../../managers/SecurityManager")
|
SecurityManager = require("../../managers/SecurityManager")
|
||||||
fs = require "fs"
|
fs = require "fs"
|
||||||
|
InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
|
||||||
|
ProjectUpdateHandler = require("./ProjectUpdateHandler")
|
||||||
|
|
||||||
module.exports = ProjectController =
|
module.exports = ProjectController =
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ module.exports = ProjectController =
|
||||||
|
|
||||||
async.series jobs, (error) ->
|
async.series jobs, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send(204)
|
res.sendStatus(204)
|
||||||
|
|
||||||
deleteProject: (req, res) ->
|
deleteProject: (req, res) ->
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
@ -58,18 +60,18 @@ module.exports = ProjectController =
|
||||||
|
|
||||||
doDelete project_id, (err)->
|
doDelete project_id, (err)->
|
||||||
if err?
|
if err?
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
restoreProject: (req, res) ->
|
restoreProject: (req, res) ->
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
logger.log project_id:project_id, "received request to restore project"
|
logger.log project_id:project_id, "received request to restore project"
|
||||||
projectDeleter.restoreProject project_id, (err)->
|
projectDeleter.restoreProject project_id, (err)->
|
||||||
if err?
|
if err?
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
cloneProject: (req, res, next)->
|
cloneProject: (req, res, next)->
|
||||||
metrics.inc "cloned-project"
|
metrics.inc "cloned-project"
|
||||||
|
@ -99,7 +101,7 @@ module.exports = ProjectController =
|
||||||
], (err, project)->
|
], (err, project)->
|
||||||
if err?
|
if err?
|
||||||
logger.error err: err, project: project, user: user, name: projectName, templateType: template, "error creating project"
|
logger.error err: err, project: project, user: user, name: projectName, templateType: template, "error creating project"
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
logger.log project: project, user: user, name: projectName, templateType: template, "created project"
|
logger.log project: project, user: user, name: projectName, templateType: template, "created project"
|
||||||
res.send {project_id:project._id}
|
res.send {project_id:project._id}
|
||||||
|
@ -109,13 +111,13 @@ module.exports = ProjectController =
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
newName = req.body.newProjectName
|
newName = req.body.newProjectName
|
||||||
if newName.length > 150
|
if newName.length > 150
|
||||||
return res.send 400
|
return res.sendStatus 400
|
||||||
editorController.renameProject project_id, newName, (err)->
|
editorController.renameProject project_id, newName, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, project_id:project_id, newName:newName, "problem renaming project"
|
logger.err err:err, project_id:project_id, newName:newName, "problem renaming project"
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
projectListPage: (req, res, next)->
|
projectListPage: (req, res, next)->
|
||||||
timer = new metrics.Timer("project-list")
|
timer = new metrics.Timer("project-list")
|
||||||
|
@ -173,6 +175,7 @@ module.exports = ProjectController =
|
||||||
user_id = 'openUser'
|
user_id = 'openUser'
|
||||||
|
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
logger.log project_id:project_id, "loading editor"
|
||||||
|
|
||||||
async.parallel {
|
async.parallel {
|
||||||
project: (cb)->
|
project: (cb)->
|
||||||
|
@ -181,11 +184,19 @@ module.exports = ProjectController =
|
||||||
if user_id == 'openUser'
|
if user_id == 'openUser'
|
||||||
cb null, defaultSettingsForAnonymousUser(user_id)
|
cb null, defaultSettingsForAnonymousUser(user_id)
|
||||||
else
|
else
|
||||||
User.findById user_id, cb
|
User.findById user_id, (err, user)->
|
||||||
|
logger.log project_id:project_id, user_id:user_id, "got user"
|
||||||
|
cb err, user
|
||||||
subscription: (cb)->
|
subscription: (cb)->
|
||||||
if user_id == 'openUser'
|
if user_id == 'openUser'
|
||||||
return cb()
|
return cb()
|
||||||
SubscriptionLocator.getUsersSubscription user_id, cb
|
SubscriptionLocator.getUsersSubscription user_id, cb
|
||||||
|
activate: (cb)->
|
||||||
|
InactiveProjectManager.reactivateProjectIfRequired project_id, cb
|
||||||
|
markAsOpened: (cb)->
|
||||||
|
#don't need to wait for this to complete
|
||||||
|
ProjectUpdateHandler.markAsOpened project_id, ->
|
||||||
|
cb()
|
||||||
}, (err, results)->
|
}, (err, results)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, "error getting details for project page"
|
logger.err err:err, "error getting details for project page"
|
||||||
|
@ -194,13 +205,16 @@ module.exports = ProjectController =
|
||||||
user = results.user
|
user = results.user
|
||||||
subscription = results.subscription
|
subscription = results.subscription
|
||||||
|
|
||||||
|
daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000
|
||||||
|
logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
|
||||||
|
|
||||||
SecurityManager.userCanAccessProject user, project, (canAccess, privilegeLevel)->
|
SecurityManager.userCanAccessProject user, project, (canAccess, privilegeLevel)->
|
||||||
if !canAccess
|
if !canAccess
|
||||||
return res.send 401
|
return res.sendStatus 401
|
||||||
|
|
||||||
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
||||||
allowedFreeTrial = !!subscription.freeTrial.allowed || true
|
allowedFreeTrial = !!subscription.freeTrial.allowed || true
|
||||||
|
logger.log project_id:project_id, "rendering editor page"
|
||||||
res.render 'project/editor',
|
res.render 'project/editor',
|
||||||
title: project.name
|
title: project.name
|
||||||
priority_title: true
|
priority_title: true
|
||||||
|
|
|
@ -19,7 +19,6 @@ module.exports =
|
||||||
project = new Project
|
project = new Project
|
||||||
owner_ref : new ObjectId(owner_id)
|
owner_ref : new ObjectId(owner_id)
|
||||||
name : projectName
|
name : projectName
|
||||||
useClsi2 : true
|
|
||||||
project.rootFolder[0] = rootFolder
|
project.rootFolder[0] = rootFolder
|
||||||
User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
|
User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
|
||||||
project.spellCheckLanguage = user.ace.spellCheckLanguage
|
project.spellCheckLanguage = user.ace.spellCheckLanguage
|
||||||
|
|
|
@ -54,7 +54,10 @@ module.exports =
|
||||||
|
|
||||||
findRootDoc : (opts, callback)->
|
findRootDoc : (opts, callback)->
|
||||||
getRootDoc = (project)=>
|
getRootDoc = (project)=>
|
||||||
@findElement {project:project, element_id:project.rootDoc_id, type:"docs"}, callback
|
if project.rootDoc_id?
|
||||||
|
@findElement {project:project, element_id:project.rootDoc_id, type:"docs"}, callback
|
||||||
|
else
|
||||||
|
callback null, null
|
||||||
{project, project_id} = opts
|
{project, project_id} = opts
|
||||||
if project?
|
if project?
|
||||||
getRootDoc project
|
getRootDoc project
|
||||||
|
|
|
@ -15,8 +15,12 @@ module.exports = ProjectRootDocManager =
|
||||||
return (cb)->
|
return (cb)->
|
||||||
rootDocId = null
|
rootDocId = null
|
||||||
for line in doc.lines || []
|
for line in doc.lines || []
|
||||||
match = line.match /(.*)\\documentclass/ # no lookbehind in js regexp :(
|
# We've had problems with this regex locking up CPU.
|
||||||
isRootDoc = Path.extname(path).match(/\.R?tex$/) and match and !match[1].match /%/
|
# Previously /.*\\documentclass/ would totally lock up on lines of 500kb (data text files :()
|
||||||
|
# This regex will only look from the start of the line, including whitespace so will return quickly
|
||||||
|
# regardless of line length.
|
||||||
|
match = line.match /^\s*\\documentclass/
|
||||||
|
isRootDoc = Path.extname(path).match(/\.R?tex$/) and match
|
||||||
if isRootDoc
|
if isRootDoc
|
||||||
rootDocId = doc?._id
|
rootDocId = doc?._id
|
||||||
cb(rootDocId)
|
cb(rootDocId)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
Project = require('../../models/Project').Project
|
Project = require('../../models/Project').Project
|
||||||
logger = require('logger-sharelatex')
|
logger = require('logger-sharelatex')
|
||||||
|
Project = require("../../models/Project").Project
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
markAsUpdated : (project_id, callback)->
|
markAsUpdated : (project_id, callback)->
|
||||||
|
@ -8,3 +9,24 @@ module.exports =
|
||||||
Project.update conditions, update, {}, (err)->
|
Project.update conditions, update, {}, (err)->
|
||||||
if callback?
|
if callback?
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
|
markAsOpened : (project_id, callback)->
|
||||||
|
conditions = {_id:project_id}
|
||||||
|
update = {lastOpened:Date.now()}
|
||||||
|
Project.update conditions, update, {}, (err)->
|
||||||
|
if callback?
|
||||||
|
callback()
|
||||||
|
|
||||||
|
markAsInactive: (project_id, callback)->
|
||||||
|
conditions = {_id:project_id}
|
||||||
|
update = {active:false}
|
||||||
|
Project.update conditions, update, {}, (err)->
|
||||||
|
if callback?
|
||||||
|
callback()
|
||||||
|
|
||||||
|
markAsActive: (project_id, callback)->
|
||||||
|
conditions = {_id:project_id}
|
||||||
|
update = {active:true}
|
||||||
|
Project.update conditions, update, {}, (err)->
|
||||||
|
if callback?
|
||||||
|
callback()
|
|
@ -10,8 +10,8 @@ wsProxy = httpProxy.createProxyServer({
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
app.all /\/socket\.io\/.*/, (req, res, next) ->
|
webRouter.all /\/socket\.io\/.*/, (req, res, next) ->
|
||||||
proxy.web req, res, next
|
proxy.web req, res, next
|
||||||
|
|
||||||
setTimeout () ->
|
setTimeout () ->
|
||||||
|
|
|
@ -50,12 +50,12 @@ module.exports = AdminController =
|
||||||
dissconectAllUsers: (req, res)=>
|
dissconectAllUsers: (req, res)=>
|
||||||
logger.warn "disconecting everyone"
|
logger.warn "disconecting everyone"
|
||||||
EditorRealTimeController.emitToAll 'forceDisconnect', "Sorry, we are performing a quick update to the editor and need to close it down. Please refresh the page to continue."
|
EditorRealTimeController.emitToAll 'forceDisconnect', "Sorry, we are performing a quick update to the editor and need to close it down. Please refresh the page to continue."
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
closeEditor : (req, res)->
|
closeEditor : (req, res)->
|
||||||
logger.warn "closing editor"
|
logger.warn "closing editor"
|
||||||
Settings.editorIsOpen = req.body.isOpen
|
Settings.editorIsOpen = req.body.isOpen
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
writeAllToMongo : (req, res)->
|
writeAllToMongo : (req, res)->
|
||||||
logger.log "writing all docs to mongo"
|
logger.log "writing all docs to mongo"
|
||||||
|
@ -74,19 +74,19 @@ module.exports = AdminController =
|
||||||
|
|
||||||
flushProjectToTpds: (req, res)->
|
flushProjectToTpds: (req, res)->
|
||||||
projectEntityHandler.flushProjectToThirdPartyDataStore req.body.project_id, (err)->
|
projectEntityHandler.flushProjectToThirdPartyDataStore req.body.project_id, (err)->
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
pollDropboxForUser: (req, res)->
|
pollDropboxForUser: (req, res)->
|
||||||
user_id = req.body.user_id
|
user_id = req.body.user_id
|
||||||
TpdsUpdateSender.pollDropboxForUser user_id, () ->
|
TpdsUpdateSender.pollDropboxForUser user_id, () ->
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
createMessage: (req, res, next) ->
|
createMessage: (req, res, next) ->
|
||||||
SystemMessageManager.createMessage req.body.content, (error) ->
|
SystemMessageManager.createMessage req.body.content, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
clearMessages: (req, res, next) ->
|
clearMessages: (req, res, next) ->
|
||||||
SystemMessageManager.clearMessages (error) ->
|
SystemMessageManager.clearMessages (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
|
@ -11,4 +11,4 @@ module.exports = SpellingController =
|
||||||
getReq.pipe(res)
|
getReq.pipe(res)
|
||||||
getReq.on "error", (error) ->
|
getReq.on "error", (error) ->
|
||||||
logger.error err: error, "Spelling API error"
|
logger.error err: error, "Spelling API error"
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
|
|
|
@ -3,18 +3,18 @@ UniversityController = require("./UniversityController")
|
||||||
|
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
app.get '/', HomeController.index
|
webRouter.get '/', HomeController.index
|
||||||
app.get '/home', HomeController.home
|
webRouter.get '/home', HomeController.home
|
||||||
|
|
||||||
app.get '/tos', HomeController.externalPage("tos", "Terms of Service")
|
webRouter.get '/tos', HomeController.externalPage("tos", "Terms of Service")
|
||||||
app.get '/about', HomeController.externalPage("about", "About Us")
|
webRouter.get '/about', HomeController.externalPage("about", "About Us")
|
||||||
app.get '/security', HomeController.externalPage("security", "Security")
|
webRouter.get '/security', HomeController.externalPage("security", "Security")
|
||||||
app.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
|
webRouter.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
|
||||||
app.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance")
|
webRouter.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance")
|
||||||
app.get '/style', HomeController.externalPage("style_guide", "Style Guide")
|
webRouter.get '/style', HomeController.externalPage("style_guide", "Style Guide")
|
||||||
|
|
||||||
app.get '/dropbox', HomeController.externalPage("dropbox", "Dropbox and ShareLaTeX")
|
webRouter.get '/dropbox', HomeController.externalPage("dropbox", "Dropbox and ShareLaTeX")
|
||||||
|
|
||||||
app.get '/university', UniversityController.getIndexPage
|
webRouter.get '/university', UniversityController.getIndexPage
|
||||||
app.get '/university/*', UniversityController.getPage
|
webRouter.get '/university/*', UniversityController.getPage
|
|
@ -18,6 +18,8 @@ module.exports = UniversityController =
|
||||||
request.get universityUrl, (err, r, data)->
|
request.get universityUrl, (err, r, data)->
|
||||||
if r?.statusCode == 404
|
if r?.statusCode == 404
|
||||||
return ErrorController.notFound(req, res, next)
|
return ErrorController.notFound(req, res, next)
|
||||||
|
if err?
|
||||||
|
return res.send 500
|
||||||
data = data.trim()
|
data = data.trim()
|
||||||
try
|
try
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
|
|
|
@ -147,8 +147,8 @@ module.exports = SubscriptionController =
|
||||||
SubscriptionHandler.createSubscription user, subscriptionDetails, recurly_token_id, (err)->
|
SubscriptionHandler.createSubscription user, subscriptionDetails, recurly_token_id, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, user_id:user._id, "something went wrong creating subscription"
|
logger.err err:err, user_id:user._id, "something went wrong creating subscription"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
res.send 201
|
res.sendStatus 201
|
||||||
|
|
||||||
successful_subscription: (req, res)->
|
successful_subscription: (req, res)->
|
||||||
SecurityManager.getCurrentUser req, (error, user) =>
|
SecurityManager.getCurrentUser req, (error, user) =>
|
||||||
|
@ -191,9 +191,9 @@ module.exports = SubscriptionController =
|
||||||
if req.body? and req.body["expired_subscription_notification"]?
|
if req.body? and req.body["expired_subscription_notification"]?
|
||||||
recurlySubscription = req.body["expired_subscription_notification"].subscription
|
recurlySubscription = req.body["expired_subscription_notification"].subscription
|
||||||
SubscriptionHandler.recurlyCallback recurlySubscription, ->
|
SubscriptionHandler.recurlyCallback recurlySubscription, ->
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
renderUpgradeToAnnualPlanPage: (req, res)->
|
renderUpgradeToAnnualPlanPage: (req, res)->
|
||||||
SecurityManager.getCurrentUser req, (error, user) ->
|
SecurityManager.getCurrentUser req, (error, user) ->
|
||||||
|
@ -221,9 +221,9 @@ module.exports = SubscriptionController =
|
||||||
SubscriptionHandler.updateSubscription user, annualPlanName, coupon_code, (err)->
|
SubscriptionHandler.updateSubscription user, annualPlanName, coupon_code, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, user_id:user._id, "error updating subscription"
|
logger.err err:err, user_id:user._id, "error updating subscription"
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
|
|
||||||
recurlyNotificationParser: (req, res, next) ->
|
recurlyNotificationParser: (req, res, next) ->
|
||||||
|
|
|
@ -62,9 +62,9 @@ module.exports =
|
||||||
return ErrorsController.notFound(req, res)
|
return ErrorsController.notFound(req, res)
|
||||||
SubscriptionGroupHandler.sendVerificationEmail subscription_id, licence.name, req.session.user.email, (err)->
|
SubscriptionGroupHandler.sendVerificationEmail subscription_id, licence.name, req.session.user.email, (err)->
|
||||||
if err?
|
if err?
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
|
|
||||||
completeJoin: (req, res)->
|
completeJoin: (req, res)->
|
||||||
subscription_id = req.params.subscription_id
|
subscription_id = req.params.subscription_id
|
||||||
|
@ -74,7 +74,7 @@ module.exports =
|
||||||
if err? and err == "token_not_found"
|
if err? and err == "token_not_found"
|
||||||
res.redirect "/user/subscription/#{subscription_id}/group/invited?expired=true"
|
res.redirect "/user/subscription/#{subscription_id}/group/invited?expired=true"
|
||||||
else if err?
|
else if err?
|
||||||
res.send 500
|
res.sendStatus 500
|
||||||
else
|
else
|
||||||
res.redirect "/user/subscription/#{subscription_id}/group/successful-join"
|
res.redirect "/user/subscription/#{subscription_id}/group/successful-join"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,9 @@ module.exports =
|
||||||
else if user_or_id?
|
else if user_or_id?
|
||||||
user_id = user_or_id
|
user_id = user_or_id
|
||||||
logger.log user_id:user_id, "getting users subscription"
|
logger.log user_id:user_id, "getting users subscription"
|
||||||
Subscription.findOne admin_id:user_id, callback
|
Subscription.findOne admin_id:user_id, (err, subscription)->
|
||||||
|
logger.log user_id:user_id, "got users subscription"
|
||||||
|
callback(err, subscription)
|
||||||
|
|
||||||
getMemberSubscriptions: (user_id, callback) ->
|
getMemberSubscriptions: (user_id, callback) ->
|
||||||
logger.log user_id: user_id, "getting users group subscriptions"
|
logger.log user_id: user_id, "getting users group subscriptions"
|
||||||
|
|
|
@ -4,44 +4,43 @@ SubscriptionGroupController = require './SubscriptionGroupController'
|
||||||
Settings = require "settings-sharelatex"
|
Settings = require "settings-sharelatex"
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
return unless Settings.enableSubscriptions
|
return unless Settings.enableSubscriptions
|
||||||
|
|
||||||
app.get '/user/subscription/plans', SubscriptionController.plansPage
|
webRouter.get '/user/subscription/plans', SubscriptionController.plansPage
|
||||||
|
|
||||||
app.get '/user/subscription', AuthenticationController.requireLogin(), SubscriptionController.userSubscriptionPage
|
webRouter.get '/user/subscription', AuthenticationController.requireLogin(), SubscriptionController.userSubscriptionPage
|
||||||
|
|
||||||
app.get '/user/subscription/custom_account', AuthenticationController.requireLogin(), SubscriptionController.userCustomSubscriptionPage
|
webRouter.get '/user/subscription/custom_account', AuthenticationController.requireLogin(), SubscriptionController.userCustomSubscriptionPage
|
||||||
|
|
||||||
|
|
||||||
app.get '/user/subscription/new', AuthenticationController.requireLogin(), SubscriptionController.paymentPage
|
webRouter.get '/user/subscription/new', AuthenticationController.requireLogin(), SubscriptionController.paymentPage
|
||||||
app.get '/user/subscription/billing-details/edit', AuthenticationController.requireLogin(), SubscriptionController.editBillingDetailsPage
|
webRouter.get '/user/subscription/billing-details/edit', AuthenticationController.requireLogin(), SubscriptionController.editBillingDetailsPage
|
||||||
|
|
||||||
app.get '/user/subscription/thank-you', AuthenticationController.requireLogin(), SubscriptionController.successful_subscription
|
webRouter.get '/user/subscription/thank-you', AuthenticationController.requireLogin(), SubscriptionController.successful_subscription
|
||||||
|
|
||||||
|
|
||||||
app.get '/subscription/group', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSubscriptionGroupAdminPage
|
webRouter.get '/subscription/group', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSubscriptionGroupAdminPage
|
||||||
app.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
webRouter.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
||||||
app.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
webRouter.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
||||||
app.del '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
webRouter.delete '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
||||||
|
|
||||||
app.get '/user/subscription/:subscription_id/group/invited', AuthenticationController.requireLogin(), SubscriptionGroupController.renderGroupInvitePage
|
webRouter.get '/user/subscription/:subscription_id/group/invited', AuthenticationController.requireLogin(), SubscriptionGroupController.renderGroupInvitePage
|
||||||
app.post '/user/subscription/:subscription_id/group/begin-join', AuthenticationController.requireLogin(), SubscriptionGroupController.beginJoinGroup
|
webRouter.post '/user/subscription/:subscription_id/group/begin-join', AuthenticationController.requireLogin(), SubscriptionGroupController.beginJoinGroup
|
||||||
app.get '/user/subscription/:subscription_id/group/complete-join', AuthenticationController.requireLogin(), SubscriptionGroupController.completeJoin
|
webRouter.get '/user/subscription/:subscription_id/group/complete-join', AuthenticationController.requireLogin(), SubscriptionGroupController.completeJoin
|
||||||
app.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage
|
webRouter.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage
|
||||||
|
|
||||||
#recurly callback
|
#recurly callback
|
||||||
app.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
apiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
||||||
app.ignoreCsrf("post", '/user/subscription/callback')
|
|
||||||
|
|
||||||
#user changes their account state
|
#user changes their account state
|
||||||
app.post '/user/subscription/create', AuthenticationController.requireLogin(), SubscriptionController.createSubscription
|
webRouter.post '/user/subscription/create', AuthenticationController.requireLogin(), SubscriptionController.createSubscription
|
||||||
app.post '/user/subscription/update', AuthenticationController.requireLogin(), SubscriptionController.updateSubscription
|
webRouter.post '/user/subscription/update', AuthenticationController.requireLogin(), SubscriptionController.updateSubscription
|
||||||
app.post '/user/subscription/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
|
webRouter.post '/user/subscription/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
|
||||||
app.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
|
webRouter.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
|
||||||
|
|
||||||
|
|
||||||
app.get "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage
|
webRouter.get "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage
|
||||||
app.post "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.processUpgradeToAnnualPlan
|
webRouter.post "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.processUpgradeToAnnualPlan
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,10 @@ module.exports =
|
||||||
logger.log user_id:user_id, filePath:filePath, fullPath:req.params[0], "sending response that tpdsUpdate has been completed"
|
logger.log user_id:user_id, filePath:filePath, fullPath:req.params[0], "sending response that tpdsUpdate has been completed"
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
||||||
res.send(500)
|
res.sendStatus(500)
|
||||||
else
|
else
|
||||||
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds update has been processed"
|
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds update has been processed"
|
||||||
res.send 200
|
res.sendStatus 200
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
deleteUpdate: (req, res)->
|
deleteUpdate: (req, res)->
|
||||||
|
@ -32,11 +31,10 @@ module.exports =
|
||||||
tpdsUpdateHandler.deleteUpdate user_id, projectName, filePath, source, (err)->
|
tpdsUpdateHandler.deleteUpdate user_id, projectName, filePath, source, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
||||||
res.send(500)
|
res.sendStatus(500)
|
||||||
else
|
else
|
||||||
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.sendStatus 200
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
# updateProjectContents and deleteProjectContents are used by GitHub. The project_id is known so we
|
# updateProjectContents and deleteProjectContents are used by GitHub. The project_id is known so we
|
||||||
# can skip right ahead to creating/updating/deleting the file. These methods will not ignore noisy
|
# can skip right ahead to creating/updating/deleting the file. These methods will not ignore noisy
|
||||||
|
@ -49,8 +47,7 @@ module.exports =
|
||||||
logger.log project_id: project_id, path: path, source: source, "received project contents update"
|
logger.log project_id: project_id, path: path, source: source, "received project contents update"
|
||||||
UpdateMerger.mergeUpdate project_id, path, req, source, (error) ->
|
UpdateMerger.mergeUpdate project_id, path, req, source, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
deleteProjectContents: (req, res, next = (error) ->) ->
|
deleteProjectContents: (req, res, next = (error) ->) ->
|
||||||
{project_id} = req.params
|
{project_id} = req.params
|
||||||
|
@ -59,8 +56,7 @@ module.exports =
|
||||||
logger.log project_id: project_id, path: path, source: source, "received project contents delete request"
|
logger.log project_id: project_id, path: path, source: source, "received project contents delete request"
|
||||||
UpdateMerger.deleteUpdate project_id, path, source, (error) ->
|
UpdateMerger.deleteUpdate project_id, path, source, (error) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
parseParams: parseParams = (req)->
|
parseParams: parseParams = (req)->
|
||||||
path = req.params[0]
|
path = req.params[0]
|
||||||
|
|
|
@ -4,6 +4,7 @@ path = require('path')
|
||||||
Project = require('../../models/Project').Project
|
Project = require('../../models/Project').Project
|
||||||
keys = require('../../infrastructure/Keys')
|
keys = require('../../infrastructure/Keys')
|
||||||
metrics = require("../../infrastructure/Metrics")
|
metrics = require("../../infrastructure/Metrics")
|
||||||
|
request = require("request")
|
||||||
|
|
||||||
buildPath = (user_id, project_name, filePath)->
|
buildPath = (user_id, project_name, filePath)->
|
||||||
projectPath = path.join(project_name, "/", filePath)
|
projectPath = path.join(project_name, "/", filePath)
|
||||||
|
@ -11,9 +12,33 @@ buildPath = (user_id, project_name, filePath)->
|
||||||
fullPath = path.join("/user/", "#{user_id}", "/entity/",projectPath)
|
fullPath = path.join("/user/", "#{user_id}", "/entity/",projectPath)
|
||||||
return fullPath
|
return fullPath
|
||||||
|
|
||||||
queue = require('fairy').connect(settings.redis.fairy).queue(keys.queue.web_to_tpds_http_requests)
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
|
|
||||||
|
tpdsworkerEnabled = -> settings.apis.tpdsworker?.url?
|
||||||
|
if !tpdsworkerEnabled()
|
||||||
|
logger.log "tpdsworker is not enabled, request will not be sent to it"
|
||||||
|
|
||||||
|
module.exports = TpdsUpdateSender =
|
||||||
|
|
||||||
|
_enqueue: (group, method, job, callback)->
|
||||||
|
if !tpdsworkerEnabled()
|
||||||
|
return callback()
|
||||||
|
opts =
|
||||||
|
uri:"#{settings.apis.tpdsworker.url}/enqueue/web_to_tpds_http_requests"
|
||||||
|
json :
|
||||||
|
group:group
|
||||||
|
method:method
|
||||||
|
job:job
|
||||||
|
method:"post"
|
||||||
|
timeout: (5 * 1000)
|
||||||
|
request opts, (err)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, "error queuing something in the tpdsworker"
|
||||||
|
callback(err)
|
||||||
|
else
|
||||||
|
logger.log group:group, "successfully queued up job for tpdsworker"
|
||||||
|
callback()
|
||||||
|
|
||||||
_addEntity: (options, callback = (err)->)->
|
_addEntity: (options, callback = (err)->)->
|
||||||
getProjectsUsersIds options.project_id, (err, user_id, allUserIds)->
|
getProjectsUsersIds options.project_id, (err, user_id, allUserIds)->
|
||||||
|
@ -27,9 +52,9 @@ module.exports =
|
||||||
uri : "#{settings.apis.thirdPartyDataStore.url}#{buildPath(user_id, options.project_name, options.path)}"
|
uri : "#{settings.apis.thirdPartyDataStore.url}#{buildPath(user_id, options.project_name, options.path)}"
|
||||||
title: "addFile"
|
title: "addFile"
|
||||||
streamOrigin : options.streamOrigin
|
streamOrigin : options.streamOrigin
|
||||||
queue.enqueue options.project_id, "pipeStreamFrom", postOptions, ->
|
TpdsUpdateSender._enqueue options.project_id, "pipeStreamFrom", postOptions, (err)->
|
||||||
logger.log project_id: options.project_id, user_id:user_id, path: options.path, uri:options.uri, rev:options.rev, "sending file to third party data store queued up for processing"
|
logger.log project_id: options.project_id, user_id:user_id, path: options.path, uri:options.uri, rev:options.rev, "sending file to third party data store queued up for processing"
|
||||||
callback()
|
callback(err)
|
||||||
|
|
||||||
addFile : (options, callback = (err)->)->
|
addFile : (options, callback = (err)->)->
|
||||||
metrics.inc("tpds.add-file")
|
metrics.inc("tpds.add-file")
|
||||||
|
@ -64,7 +89,7 @@ module.exports =
|
||||||
user_id : user_id
|
user_id : user_id
|
||||||
endPath: endPath
|
endPath: endPath
|
||||||
startPath: startPath
|
startPath: startPath
|
||||||
queue.enqueue options.project_id, "standardHttpRequest", moveOptions, callback
|
TpdsUpdateSender._enqueue options.project_id, "standardHttpRequest", moveOptions, callback
|
||||||
|
|
||||||
deleteEntity : (options, callback = (err)->)->
|
deleteEntity : (options, callback = (err)->)->
|
||||||
metrics.inc("tpds.delete-entity")
|
metrics.inc("tpds.delete-entity")
|
||||||
|
@ -78,7 +103,7 @@ module.exports =
|
||||||
uri : "#{settings.apis.thirdPartyDataStore.url}#{buildPath(user_id, options.project_name, options.path)}"
|
uri : "#{settings.apis.thirdPartyDataStore.url}#{buildPath(user_id, options.project_name, options.path)}"
|
||||||
title:"deleteEntity"
|
title:"deleteEntity"
|
||||||
sl_all_user_ids:JSON.stringify(allUserIds)
|
sl_all_user_ids:JSON.stringify(allUserIds)
|
||||||
queue.enqueue options.project_id, "standardHttpRequest", deleteOptions, callback
|
TpdsUpdateSender._enqueue options.project_id, "standardHttpRequest", deleteOptions, callback
|
||||||
|
|
||||||
pollDropboxForUser: (user_id, callback = (err) ->) ->
|
pollDropboxForUser: (user_id, callback = (err) ->) ->
|
||||||
metrics.inc("tpds.poll-dropbox")
|
metrics.inc("tpds.poll-dropbox")
|
||||||
|
@ -88,7 +113,7 @@ module.exports =
|
||||||
uri:"#{settings.apis.thirdPartyDataStore.url}/user/poll"
|
uri:"#{settings.apis.thirdPartyDataStore.url}/user/poll"
|
||||||
json:
|
json:
|
||||||
user_ids: [user_id]
|
user_ids: [user_id]
|
||||||
queue.enqueue "poll-dropbox:#{user_id}", "standardHttpRequest", options, callback
|
TpdsUpdateSender._enqueue "poll-dropbox:#{user_id}", "standardHttpRequest", options, callback
|
||||||
|
|
||||||
getProjectsUsersIds = (project_id, callback = (err, owner_id, allUserIds)->)->
|
getProjectsUsersIds = (project_id, callback = (err, owner_id, allUserIds)->)->
|
||||||
Project.findById project_id, "_id owner_ref readOnly_refs collaberator_refs", (err, project)->
|
Project.findById project_id, "_id owner_ref readOnly_refs collaberator_refs", (err, project)->
|
||||||
|
|
|
@ -9,8 +9,8 @@ module.exports = ProjectUploadController =
|
||||||
uploadProject: (req, res, next) ->
|
uploadProject: (req, res, next) ->
|
||||||
timer = new metrics.Timer("project-upload")
|
timer = new metrics.Timer("project-upload")
|
||||||
user_id = req.session.user._id
|
user_id = req.session.user._id
|
||||||
{name, path} = req.files.qqfile
|
{originalname, path} = req.files.qqfile
|
||||||
name = Path.basename(name, ".zip")
|
name = Path.basename(originalname, ".zip")
|
||||||
ProjectUploadManager.createProjectFromZipArchive user_id, name, path, (error, project) ->
|
ProjectUploadManager.createProjectFromZipArchive user_id, name, path, (error, project) ->
|
||||||
fs.unlink path, ->
|
fs.unlink path, ->
|
||||||
timer.done()
|
timer.done()
|
||||||
|
@ -27,7 +27,8 @@ module.exports = ProjectUploadController =
|
||||||
|
|
||||||
uploadFile: (req, res, next) ->
|
uploadFile: (req, res, next) ->
|
||||||
timer = new metrics.Timer("file-upload")
|
timer = new metrics.Timer("file-upload")
|
||||||
{name, path} = req.files.qqfile
|
name = req.files.qqfile.originalname
|
||||||
|
path = req.files.qqfile.path
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
folder_id = req.query.folder_id
|
folder_id = req.query.folder_id
|
||||||
if !name? or name.length == 0 or name.length > 150
|
if !name? or name.length == 0 or name.length > 150
|
||||||
|
|
|
@ -3,11 +3,11 @@ AuthenticationController = require('../Authentication/AuthenticationController')
|
||||||
ProjectUploadController = require "./ProjectUploadController"
|
ProjectUploadController = require "./ProjectUploadController"
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
apply: (app) ->
|
apply: (webRouter, apiRouter) ->
|
||||||
app.post '/project/new/upload',
|
webRouter.post '/project/new/upload',
|
||||||
AuthenticationController.requireLogin(),
|
AuthenticationController.requireLogin(),
|
||||||
ProjectUploadController.uploadProject
|
ProjectUploadController.uploadProject
|
||||||
app.post '/Project/:Project_id/upload',
|
webRouter.post '/Project/:Project_id/upload',
|
||||||
SecurityManager.requestCanModifyProject,
|
SecurityManager.requestCanModifyProject,
|
||||||
ProjectUploadController.uploadFile
|
ProjectUploadController.uploadFile
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ module.exports =
|
||||||
user_id = req.session.user._id
|
user_id = req.session.user._id
|
||||||
UserDeleter.deleteUser user_id, (err)->
|
UserDeleter.deleteUser user_id, (err)->
|
||||||
if !err?
|
if !err?
|
||||||
req.session.destroy()
|
req.session?.destroy()
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
unsubscribe: (req, res)->
|
unsubscribe: (req, res)->
|
||||||
UserLocator.findById req.session.user._id, (err, user)->
|
UserLocator.findById req.session.user._id, (err, user)->
|
||||||
|
@ -34,7 +34,7 @@ module.exports =
|
||||||
User.findById user_id, (err, user)->
|
User.findById user_id, (err, user)->
|
||||||
if err? or !user?
|
if err? or !user?
|
||||||
logger.err err:err, user_id:user_id, "problem updaing user settings"
|
logger.err err:err, user_id:user_id, "problem updaing user settings"
|
||||||
return res.send 500
|
return res.sendStatus 500
|
||||||
|
|
||||||
if req.body.first_name?
|
if req.body.first_name?
|
||||||
user.first_name = req.body.first_name.trim()
|
user.first_name = req.body.first_name.trim()
|
||||||
|
@ -59,9 +59,9 @@ module.exports =
|
||||||
user.save (err)->
|
user.save (err)->
|
||||||
newEmail = req.body.email?.trim().toLowerCase()
|
newEmail = req.body.email?.trim().toLowerCase()
|
||||||
if !newEmail? or newEmail == user.email
|
if !newEmail? or newEmail == user.email
|
||||||
return res.send 200
|
return res.sendStatus 200
|
||||||
else if newEmail.indexOf("@") == -1
|
else if newEmail.indexOf("@") == -1
|
||||||
return res.send(400)
|
return res.sendStatus(400)
|
||||||
else
|
else
|
||||||
UserUpdater.changeEmailAddress user_id, newEmail, (err)->
|
UserUpdater.changeEmailAddress user_id, newEmail, (err)->
|
||||||
if err?
|
if err?
|
||||||
|
@ -71,7 +71,7 @@ module.exports =
|
||||||
else
|
else
|
||||||
message = req.i18n.translate("problem_changing_email_address")
|
message = req.i18n.translate("problem_changing_email_address")
|
||||||
return res.send 500, {message:message}
|
return res.send 500, {message:message}
|
||||||
res.send(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
logout : (req, res)->
|
logout : (req, res)->
|
||||||
metrics.inc "user.logout"
|
metrics.inc "user.logout"
|
||||||
|
@ -84,7 +84,7 @@ module.exports =
|
||||||
register : (req, res, next = (error) ->)->
|
register : (req, res, next = (error) ->)->
|
||||||
email = req.body.email
|
email = req.body.email
|
||||||
if !email? or email == ""
|
if !email? or email == ""
|
||||||
res.send 422 # Unprocessable Entity
|
res.sendStatus 422 # Unprocessable Entity
|
||||||
return
|
return
|
||||||
logger.log {email}, "registering new user"
|
logger.log {email}, "registering new user"
|
||||||
UserRegistrationHandler.registerNewUser {
|
UserRegistrationHandler.registerNewUser {
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = UserController =
|
||||||
# this is funcky as hell, we don't use the current session to get the user
|
# this is funcky as hell, we don't use the current session to get the user
|
||||||
# we use the auth token, actually destroying session from the chat api request
|
# we use the auth token, actually destroying session from the chat api request
|
||||||
if req.query?.auth_token?
|
if req.query?.auth_token?
|
||||||
req.session.destroy()
|
req.session?.destroy()
|
||||||
logger.log user: req.user, "reciving request for getting logged in users personal info"
|
logger.log user: req.user, "reciving request for getting logged in users personal info"
|
||||||
return next(new Error("User is not logged in")) if !req.user?
|
return next(new Error("User is not logged in")) if !req.user?
|
||||||
UserGetter.getUser req.user._id, {
|
UserGetter.getUser req.user._id, {
|
||||||
|
@ -26,7 +26,6 @@ module.exports = UserController =
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
return res.send(404) if !user?
|
return res.send(404) if !user?
|
||||||
UserController.sendFormattedPersonalInfo(user, res, next)
|
UserController.sendFormattedPersonalInfo(user, res, next)
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
sendFormattedPersonalInfo: (user, res, next = (error) ->) ->
|
sendFormattedPersonalInfo: (user, res, next = (error) ->) ->
|
||||||
UserController._formatPersonalInfo user, (error, info) ->
|
UserController._formatPersonalInfo user, (error, info) ->
|
||||||
|
|
|
@ -39,38 +39,38 @@ for path in [
|
||||||
logger.log filePath:filePath, "file does not exist for fingerprints"
|
logger.log filePath:filePath, "file does not exist for fingerprints"
|
||||||
|
|
||||||
|
|
||||||
module.exports = (app)->
|
module.exports = (app, webRouter, apiRouter)->
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.session = req.session
|
res.locals.session = req.session
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.jsPath = jsPath
|
res.locals.jsPath = jsPath
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.settings = Settings
|
res.locals.settings = Settings
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.translate = (key, vars = {}) ->
|
res.locals.translate = (key, vars = {}) ->
|
||||||
vars.appName = Settings.appName
|
vars.appName = Settings.appName
|
||||||
req.i18n.translate(key, vars)
|
req.i18n.translate(key, vars)
|
||||||
res.locals.currentUrl = req.originalUrl
|
res.locals.currentUrl = req.originalUrl
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.getSiteHost = ->
|
res.locals.getSiteHost = ->
|
||||||
Settings.siteUrl.substring(Settings.siteUrl.indexOf("//")+2)
|
Settings.siteUrl.substring(Settings.siteUrl.indexOf("//")+2)
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.formatProjectPublicAccessLevel = (privilegeLevel)->
|
res.locals.formatProjectPublicAccessLevel = (privilegeLevel)->
|
||||||
formatedPrivileges = private:"Private", readOnly:"Public: Read Only", readAndWrite:"Public: Read and Write"
|
formatedPrivileges = private:"Private", readOnly:"Public: Read Only", readAndWrite:"Public: Read and Write"
|
||||||
return formatedPrivileges[privilegeLevel] || "Private"
|
return formatedPrivileges[privilegeLevel] || "Private"
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.buildReferalUrl = (referal_medium) ->
|
res.locals.buildReferalUrl = (referal_medium) ->
|
||||||
url = Settings.siteUrl
|
url = Settings.siteUrl
|
||||||
if req.session? and req.session.user? and req.session.user.referal_id?
|
if req.session? and req.session.user? and req.session.user.referal_id?
|
||||||
|
@ -94,16 +94,16 @@ module.exports = (app)->
|
||||||
return ""
|
return ""
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
res.locals.csrfToken = req.session._csrf
|
res.locals.csrfToken = req?.csrfToken()
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
res.locals.getReqQueryParam = (field)->
|
res.locals.getReqQueryParam = (field)->
|
||||||
return req.query?[field]
|
return req.query?[field]
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.fingerprint = (path) ->
|
res.locals.fingerprint = (path) ->
|
||||||
if fingerprints[path]?
|
if fingerprints[path]?
|
||||||
return fingerprints[path]
|
return fingerprints[path]
|
||||||
|
@ -112,16 +112,16 @@ module.exports = (app)->
|
||||||
return ""
|
return ""
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.externalAuthenticationSystemUsed = ->
|
res.locals.externalAuthenticationSystemUsed = ->
|
||||||
Settings.ldap?
|
Settings.ldap?
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
if req.session.user?
|
if req.session.user?
|
||||||
res.locals.user =
|
res.locals.user =
|
||||||
email: req.session.user.email
|
email: req.session.user.email
|
||||||
|
@ -139,34 +139,34 @@ module.exports = (app)->
|
||||||
res.locals.sentryPublicDSN = Settings.sentry?.publicDSN
|
res.locals.sentryPublicDSN = Settings.sentry?.publicDSN
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
if req.query? and req.query.scribtex_path?
|
if req.query? and req.query.scribtex_path?
|
||||||
res.locals.lookingForScribtex = true
|
res.locals.lookingForScribtex = true
|
||||||
res.locals.scribtexPath = req.query.scribtex_path
|
res.locals.scribtexPath = req.query.scribtex_path
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
res.locals.nav = Settings.nav
|
res.locals.nav = Settings.nav
|
||||||
res.locals.templates = Settings.templateLinks
|
res.locals.templates = Settings.templateLinks
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
SystemMessageManager.getMessages (error, messages = []) ->
|
SystemMessageManager.getMessages (error, messages = []) ->
|
||||||
res.locals.systemMessages = messages
|
res.locals.systemMessages = messages
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.query = req.query
|
res.locals.query = req.query
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
subdomain = _.find Settings.i18n.subdomainLang, (subdomain)->
|
subdomain = _.find Settings.i18n.subdomainLang, (subdomain)->
|
||||||
subdomain.lngCode == req.showUserOtherLng and !subdomain.hide
|
subdomain.lngCode == req.showUserOtherLng and !subdomain.hide
|
||||||
res.locals.recomendSubdomain = subdomain
|
res.locals.recomendSubdomain = subdomain
|
||||||
res.locals.currentLngCode = req.lng
|
res.locals.currentLngCode = req.lng
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
if Settings.reloadModuleViewsOnEachRequest
|
if Settings.reloadModuleViewsOnEachRequest
|
||||||
Modules.loadViewIncludes()
|
Modules.loadViewIncludes()
|
||||||
res.locals.moduleIncludes = Modules.moduleIncludes
|
res.locals.moduleIncludes = Modules.moduleIncludes
|
||||||
|
|
|
@ -13,9 +13,9 @@ module.exports = Modules =
|
||||||
loadedModule.name = moduleName
|
loadedModule.name = moduleName
|
||||||
@modules.push loadedModule
|
@modules.push loadedModule
|
||||||
|
|
||||||
applyRouter: (app) ->
|
applyRouter: (webRouter, apiRouter) ->
|
||||||
for module in @modules
|
for module in @modules
|
||||||
module.router?.apply(app)
|
module.router?.apply(webRouter, apiRouter)
|
||||||
|
|
||||||
viewIncludes: {}
|
viewIncludes: {}
|
||||||
loadViewIncludes: (app) ->
|
loadViewIncludes: (app) ->
|
||||||
|
|
16
services/web/app/coffee/infrastructure/Mongoose.coffee
Normal file
16
services/web/app/coffee/infrastructure/Mongoose.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
mongoose = require('mongoose')
|
||||||
|
Settings = require 'settings-sharelatex'
|
||||||
|
logger = require('logger-sharelatex')
|
||||||
|
|
||||||
|
mongoose.connect(Settings.mongo.url, server: poolSize: 10)
|
||||||
|
|
||||||
|
mongoose.connection.on 'connected', () ->
|
||||||
|
logger.log {url:Settings.mongo.url}, 'mongoose default connection open'
|
||||||
|
|
||||||
|
mongoose.connection.on 'error', (err) ->
|
||||||
|
logger.err err:err, 'mongoose error on default connection';
|
||||||
|
|
||||||
|
mongoose.connection.on 'disconnected', () ->
|
||||||
|
logger.log 'mongoose default connection disconnected'
|
||||||
|
|
||||||
|
module.exports = mongoose
|
|
@ -7,14 +7,22 @@ crawlerLogger = require('./CrawlerLogger')
|
||||||
expressLocals = require('./ExpressLocals')
|
expressLocals = require('./ExpressLocals')
|
||||||
Router = require('../router')
|
Router = require('../router')
|
||||||
metrics.inc("startup")
|
metrics.inc("startup")
|
||||||
|
|
||||||
redis = require("redis-sharelatex")
|
redis = require("redis-sharelatex")
|
||||||
rclient = redis.createClient(Settings.redis.web)
|
rclient = redis.createClient(Settings.redis.web)
|
||||||
|
|
||||||
RedisStore = require('connect-redis')(express)
|
session = require("express-session")
|
||||||
|
RedisStore = require('connect-redis')(session)
|
||||||
|
bodyParser = require('body-parser')
|
||||||
|
multer = require('multer')
|
||||||
|
methodOverride = require('method-override')
|
||||||
|
csrf = require('csurf')
|
||||||
|
csrfProtection = csrf()
|
||||||
|
cookieParser = require('cookie-parser')
|
||||||
|
|
||||||
sessionStore = new RedisStore(client:rclient)
|
sessionStore = new RedisStore(client:rclient)
|
||||||
|
|
||||||
cookieParser = express.cookieParser(Settings.security.sessionSecret)
|
Mongoose = require("./Mongoose")
|
||||||
|
|
||||||
oneDayInMilliseconds = 86400000
|
oneDayInMilliseconds = 86400000
|
||||||
ReferalConnect = require('../Features/Referal/ReferalConnect')
|
ReferalConnect = require('../Features/Referal/ReferalConnect')
|
||||||
RedirectManager = require("./RedirectManager")
|
RedirectManager = require("./RedirectManager")
|
||||||
|
@ -25,6 +33,8 @@ 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)
|
||||||
|
|
||||||
|
metrics.event_loop?.monitor(logger)
|
||||||
|
|
||||||
Settings.editorIsOpen ||= true
|
Settings.editorIsOpen ||= true
|
||||||
|
|
||||||
if Settings.cacheStaticAssets
|
if Settings.cacheStaticAssets
|
||||||
|
@ -34,56 +44,59 @@ else
|
||||||
|
|
||||||
app = express()
|
app = express()
|
||||||
|
|
||||||
csrf = express.csrf()
|
webRouter = express.Router()
|
||||||
ignoreCsrfRoutes = []
|
apiRouter = express.Router()
|
||||||
app.ignoreCsrf = (method, route) ->
|
|
||||||
ignoreCsrfRoutes.push new express.Route(method, route)
|
if Settings.behindProxy
|
||||||
|
app.enable('trust proxy')
|
||||||
|
|
||||||
|
webRouter.use express.static(__dirname + '/../../../public', {maxAge: staticCacheAge })
|
||||||
|
app.set 'views', __dirname + '/../../views'
|
||||||
|
app.set 'view engine', 'jade'
|
||||||
|
Modules.loadViewIncludes app
|
||||||
|
|
||||||
|
|
||||||
app.configure () ->
|
|
||||||
if Settings.behindProxy
|
|
||||||
app.enable('trust proxy')
|
|
||||||
app.use express.static(__dirname + '/../../../public', {maxAge: staticCacheAge })
|
|
||||||
app.set 'views', __dirname + '/../../views'
|
|
||||||
app.set 'view engine', 'jade'
|
|
||||||
Modules.loadViewIncludes app
|
|
||||||
app.use express.bodyParser(uploadDir: Settings.path.uploadFolder)
|
|
||||||
app.use translations.expressMiddlewear
|
|
||||||
app.use translations.setLangBasedOnDomainMiddlewear
|
|
||||||
app.use cookieParser
|
|
||||||
app.use express.session
|
|
||||||
proxy: Settings.behindProxy
|
|
||||||
cookie:
|
|
||||||
domain: Settings.cookieDomain
|
|
||||||
maxAge: Settings.cookieSessionLength
|
|
||||||
secure: Settings.secureCookie
|
|
||||||
store: sessionStore
|
|
||||||
key: Settings.cookieName
|
|
||||||
|
|
||||||
# Measure expiry from last request, not last login
|
app.use bodyParser.urlencoded({ extended: true, limit: "2mb"})
|
||||||
app.use (req, res, next) ->
|
app.use bodyParser.json({limit: "2mb"})
|
||||||
req.session.touch()
|
app.use multer(dest: Settings.path.uploadFolder)
|
||||||
next()
|
app.use methodOverride()
|
||||||
|
|
||||||
app.use (req, res, next) ->
|
|
||||||
for route in ignoreCsrfRoutes
|
|
||||||
if route.method == req.method?.toLowerCase() and route.match(req.path)
|
|
||||||
return next()
|
|
||||||
csrf(req, res, next)
|
|
||||||
|
|
||||||
app.use ReferalConnect.use
|
|
||||||
app.use express.methodOverride()
|
|
||||||
|
|
||||||
expressLocals(app)
|
|
||||||
|
|
||||||
app.configure 'production', ->
|
|
||||||
logger.info "Production Enviroment"
|
|
||||||
app.enable('view cache')
|
|
||||||
|
|
||||||
app.use metrics.http.monitor(logger)
|
app.use metrics.http.monitor(logger)
|
||||||
app.use RedirectManager
|
app.use RedirectManager
|
||||||
app.use OldAssetProxy
|
app.use OldAssetProxy
|
||||||
|
|
||||||
|
|
||||||
|
webRouter.use cookieParser(Settings.security.sessionSecret)
|
||||||
|
webRouter.use session
|
||||||
|
resave: false
|
||||||
|
saveUninitialized:false
|
||||||
|
secret:Settings.security.sessionSecret
|
||||||
|
proxy: Settings.behindProxy
|
||||||
|
cookie:
|
||||||
|
domain: Settings.cookieDomain
|
||||||
|
maxAge: Settings.cookieSessionLength
|
||||||
|
secure: Settings.secureCookie
|
||||||
|
store: sessionStore
|
||||||
|
key: Settings.cookieName
|
||||||
|
webRouter.use csrfProtection
|
||||||
|
webRouter.use translations.expressMiddlewear
|
||||||
|
webRouter.use translations.setLangBasedOnDomainMiddlewear
|
||||||
|
|
||||||
|
# Measure expiry from last request, not last login
|
||||||
|
webRouter.use (req, res, next) ->
|
||||||
|
req.session.touch()
|
||||||
|
next()
|
||||||
|
|
||||||
|
webRouter.use ReferalConnect.use
|
||||||
|
expressLocals(app, webRouter, apiRouter)
|
||||||
|
|
||||||
|
if app.get('env') == 'production'
|
||||||
|
logger.info "Production Enviroment"
|
||||||
|
app.enable('view cache')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.use (req, res, next)->
|
app.use (req, res, next)->
|
||||||
metrics.inc "http-request"
|
metrics.inc "http-request"
|
||||||
crawlerLogger.log(req)
|
crawlerLogger.log(req)
|
||||||
|
@ -96,12 +109,11 @@ app.use (req, res, next) ->
|
||||||
else
|
else
|
||||||
next()
|
next()
|
||||||
|
|
||||||
app.get "/status", (req, res)->
|
apiRouter.get "/status", (req, res)->
|
||||||
res.send("web sharelatex is alive")
|
res.send("web sharelatex is alive")
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
profiler = require "v8-profiler"
|
profiler = require "v8-profiler"
|
||||||
app.get "/profile", (req, res) ->
|
apiRouter.get "/profile", (req, res) ->
|
||||||
time = parseInt(req.query.time || "1000")
|
time = parseInt(req.query.time || "1000")
|
||||||
profiler.startProfiling("test")
|
profiler.startProfiling("test")
|
||||||
setTimeout () ->
|
setTimeout () ->
|
||||||
|
@ -112,7 +124,12 @@ app.get "/profile", (req, res) ->
|
||||||
logger.info ("creating HTTP server").yellow
|
logger.info ("creating HTTP server").yellow
|
||||||
server = require('http').createServer(app)
|
server = require('http').createServer(app)
|
||||||
|
|
||||||
router = new Router(app)
|
# process api routes first, if nothing matched fall though and use
|
||||||
|
# web middlewear + routes
|
||||||
|
app.use(apiRouter)
|
||||||
|
app.use(webRouter)
|
||||||
|
|
||||||
|
router = new Router(webRouter, apiRouter)
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
app: app
|
app: app
|
||||||
|
|
|
@ -17,6 +17,8 @@ DeletedDocSchema = new Schema
|
||||||
ProjectSchema = new Schema
|
ProjectSchema = new Schema
|
||||||
name : {type:String, default:'new project'}
|
name : {type:String, default:'new project'}
|
||||||
lastUpdated : {type:Date, default: () -> new Date()}
|
lastUpdated : {type:Date, default: () -> new Date()}
|
||||||
|
lastOpened : {type:Date}
|
||||||
|
active : { type: Boolean, default: true }
|
||||||
owner_ref : {type:ObjectId, ref:'User'}
|
owner_ref : {type:ObjectId, ref:'User'}
|
||||||
collaberator_refs : [ type:ObjectId, ref:'User' ]
|
collaberator_refs : [ type:ObjectId, ref:'User' ]
|
||||||
readOnly_refs : [ type:ObjectId, ref:'User' ]
|
readOnly_refs : [ type:ObjectId, ref:'User' ]
|
||||||
|
@ -26,7 +28,6 @@ ProjectSchema = new Schema
|
||||||
compiler : {type:String, default:'pdflatex'}
|
compiler : {type:String, default:'pdflatex'}
|
||||||
spellCheckLanguage : {type:String, default:'en'}
|
spellCheckLanguage : {type:String, default:'en'}
|
||||||
deletedByExternalDataSource : {type: Boolean, default: false}
|
deletedByExternalDataSource : {type: Boolean, default: false}
|
||||||
useClsi2 : {type:Boolean, default: true}
|
|
||||||
description : {type:String, default:''}
|
description : {type:String, default:''}
|
||||||
archived : { type: Boolean }
|
archived : { type: Boolean }
|
||||||
deletedDocs : [DeletedDocSchema]
|
deletedDocs : [DeletedDocSchema]
|
||||||
|
@ -42,6 +43,7 @@ ProjectSchema.statics.getProject = (project_or_id, fields, callback)->
|
||||||
this.findById project_or_id, fields, callback
|
this.findById project_or_id, fields, callback
|
||||||
|
|
||||||
ProjectSchema.statics.findPopulatedById = (project_id, callback)->
|
ProjectSchema.statics.findPopulatedById = (project_id, callback)->
|
||||||
|
logger.log project_id:project_id, "findPopulatedById"
|
||||||
this.find(_id: project_id )
|
this.find(_id: project_id )
|
||||||
.populate('collaberator_refs')
|
.populate('collaberator_refs')
|
||||||
.populate('readOnly_refs')
|
.populate('readOnly_refs')
|
||||||
|
@ -54,6 +56,7 @@ ProjectSchema.statics.findPopulatedById = (project_id, callback)->
|
||||||
logger.err project_id:project_id, "something went wrong looking for project findPopulatedById, no project could be found"
|
logger.err project_id:project_id, "something went wrong looking for project findPopulatedById, no project could be found"
|
||||||
callback "not found"
|
callback "not found"
|
||||||
else
|
else
|
||||||
|
logger.log project_id:project_id, "finished findPopulatedById"
|
||||||
callback(null, projects[0])
|
callback(null, projects[0])
|
||||||
|
|
||||||
ProjectSchema.statics.findAllUsersProjects = (user_id, requiredFields, callback)->
|
ProjectSchema.statics.findAllUsersProjects = (user_id, requiredFields, callback)->
|
||||||
|
|
|
@ -35,71 +35,72 @@ WikiController = require("./Features/Wiki/WikiController")
|
||||||
Modules = require "./infrastructure/Modules"
|
Modules = require "./infrastructure/Modules"
|
||||||
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
||||||
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
||||||
|
InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
|
||||||
|
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
|
|
||||||
module.exports = class Router
|
module.exports = class Router
|
||||||
constructor: (app)->
|
constructor: (webRouter, apiRouter)->
|
||||||
if !Settings.allowPublicAccess
|
if !Settings.allowPublicAccess
|
||||||
app.all '*', AuthenticationController.requireGlobalLogin
|
webRouter.all '*', AuthenticationController.requireGlobalLogin
|
||||||
|
|
||||||
app.use(app.router)
|
|
||||||
|
|
||||||
app.get '/login', UserPagesController.loginPage
|
webRouter.get '/login', UserPagesController.loginPage
|
||||||
AuthenticationController.addEndpointToLoginWhitelist '/login'
|
AuthenticationController.addEndpointToLoginWhitelist '/login'
|
||||||
|
|
||||||
app.post '/login', AuthenticationController.login
|
webRouter.post '/login', AuthenticationController.login
|
||||||
app.get '/logout', UserController.logout
|
webRouter.get '/logout', UserController.logout
|
||||||
app.get '/restricted', SecurityManager.restricted
|
webRouter.get '/restricted', SecurityManager.restricted
|
||||||
|
|
||||||
# Left as a placeholder for implementing a public register page
|
# Left as a placeholder for implementing a public register page
|
||||||
app.get '/register', UserPagesController.registerPage
|
webRouter.get '/register', UserPagesController.registerPage
|
||||||
AuthenticationController.addEndpointToLoginWhitelist '/register'
|
AuthenticationController.addEndpointToLoginWhitelist '/register'
|
||||||
|
|
||||||
EditorRouter.apply(app)
|
|
||||||
CollaboratorsRouter.apply(app)
|
|
||||||
SubscriptionRouter.apply(app)
|
|
||||||
UploadsRouter.apply(app)
|
|
||||||
PasswordResetRouter.apply(app)
|
|
||||||
StaticPagesRouter.apply(app)
|
|
||||||
RealTimeProxyRouter.apply(app)
|
|
||||||
|
|
||||||
Modules.applyRouter(app)
|
EditorRouter.apply(webRouter, apiRouter)
|
||||||
|
CollaboratorsRouter.apply(webRouter, apiRouter)
|
||||||
|
SubscriptionRouter.apply(webRouter, apiRouter)
|
||||||
|
UploadsRouter.apply(webRouter, apiRouter)
|
||||||
|
PasswordResetRouter.apply(webRouter, apiRouter)
|
||||||
|
StaticPagesRouter.apply(webRouter, apiRouter)
|
||||||
|
RealTimeProxyRouter.apply(webRouter, apiRouter)
|
||||||
|
|
||||||
|
Modules.applyRouter(webRouter, apiRouter)
|
||||||
|
|
||||||
app.get '/blog', BlogController.getIndexPage
|
|
||||||
app.get '/blog/*', BlogController.getPage
|
|
||||||
|
|
||||||
if Settings.enableSubscriptions
|
if Settings.enableSubscriptions
|
||||||
app.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
|
webRouter.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
|
||||||
|
|
||||||
app.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
|
webRouter.get '/blog', BlogController.getIndexPage
|
||||||
app.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
|
webRouter.get '/blog/*', BlogController.getPage
|
||||||
app.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
|
|
||||||
|
|
||||||
app.del '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
webRouter.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
|
||||||
app.del '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
|
||||||
|
webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
|
||||||
|
|
||||||
app.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||||
app.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
|
webRouter.delete '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
||||||
app.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
|
||||||
|
|
||||||
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
webRouter.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
||||||
app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
|
webRouter.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
|
||||||
|
apiRouter.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
||||||
|
|
||||||
app.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({
|
webRouter.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
||||||
|
webRouter.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
|
||||||
|
|
||||||
|
webRouter.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({
|
||||||
endpointName: "open-project"
|
endpointName: "open-project"
|
||||||
params: ["Project_id"]
|
params: ["Project_id"]
|
||||||
maxRequests: 10
|
maxRequests: 10
|
||||||
timeInterval: 60
|
timeInterval: 60
|
||||||
}), SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
}), SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
||||||
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
webRouter.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
||||||
|
webRouter.post '/project/:Project_id/settings', SecurityManager.requestCanModifyProject, ProjectController.updateProjectSettings
|
||||||
|
|
||||||
app.post '/project/:Project_id/settings', SecurityManager.requestCanModifyProject, ProjectController.updateProjectSettings
|
webRouter.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||||
|
webRouter.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||||
app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
webRouter.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||||
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
|
||||||
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
|
||||||
((req, res, next) ->
|
((req, res, next) ->
|
||||||
params =
|
params =
|
||||||
"Project_id": req.params[0]
|
"Project_id": req.params[0]
|
||||||
|
@ -107,78 +108,86 @@ module.exports = class Router
|
||||||
req.params = params
|
req.params = params
|
||||||
next()
|
next()
|
||||||
), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi
|
), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi
|
||||||
app.del "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
|
webRouter.delete "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
|
||||||
app.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
webRouter.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||||
app.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
webRouter.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||||
|
|
||||||
app.del '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
|
webRouter.delete '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
|
||||||
app.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
|
webRouter.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
|
||||||
app.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
|
webRouter.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
|
||||||
|
|
||||||
app.post '/project/:Project_id/rename', SecurityManager.requestIsOwner, ProjectController.renameProject
|
webRouter.post '/project/:Project_id/rename', SecurityManager.requestIsOwner, ProjectController.renameProject
|
||||||
|
|
||||||
app.get "/project/:Project_id/updates", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
webRouter.get "/project/:Project_id/updates", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||||
app.get "/project/:Project_id/doc/:doc_id/diff", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
webRouter.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
|
webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||||
|
|
||||||
app.get '/Project/:Project_id/download/zip', SecurityManager.requestCanAccessProject, ProjectDownloadsController.downloadProject
|
webRouter.get '/Project/:Project_id/download/zip', SecurityManager.requestCanAccessProject, ProjectDownloadsController.downloadProject
|
||||||
app.get '/project/download/zip', SecurityManager.requestCanAccessMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
webRouter.get '/project/download/zip', SecurityManager.requestCanAccessMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||||
|
|
||||||
app.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
|
webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
|
||||||
app.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
|
webRouter.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
|
||||||
|
|
||||||
app.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
|
# Deprecated in favour of /internal/project/:project_id but still used by versioning
|
||||||
|
apiRouter.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
|
||||||
|
|
||||||
app.get '/internal/project/:Project_id/zip', AuthenticationController.httpAuth, ProjectDownloadsController.downloadProject
|
# New 'stable' /internal API end points
|
||||||
app.get '/internal/project/:project_id/compile/pdf', AuthenticationController.httpAuth, CompileController.compileAndDownloadPdf
|
apiRouter.get '/internal/project/:project_id', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
|
||||||
|
apiRouter.get '/internal/project/:Project_id/zip', AuthenticationController.httpAuth, ProjectDownloadsController.downloadProject
|
||||||
|
apiRouter.get '/internal/project/:project_id/compile/pdf', AuthenticationController.httpAuth, CompileController.compileAndDownloadPdf
|
||||||
|
|
||||||
|
apiRouter.post '/internal/deactivateOldProjects', AuthenticationController.httpAuth, InactiveProjectController.deactivateOldProjects
|
||||||
|
apiRouter.post '/internal/project/:project_id/deactivate', AuthenticationController.httpAuth, InactiveProjectController.deactivateProject
|
||||||
|
|
||||||
app.get '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.getDocument
|
webRouter.get /^\/internal\/project\/([^\/]*)\/output\/(.*)$/,
|
||||||
app.post '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.setDocument
|
((req, res, next) ->
|
||||||
app.ignoreCsrf('post', '/project/:Project_id/doc/:doc_id')
|
params =
|
||||||
|
"Project_id": req.params[0]
|
||||||
|
"file": req.params[1]
|
||||||
|
req.params = params
|
||||||
|
next()
|
||||||
|
), AuthenticationController.httpAuth, CompileController.getFileFromClsi
|
||||||
|
|
||||||
app.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
|
apiRouter.get '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.getDocument
|
||||||
app.del '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
|
apiRouter.post '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.setDocument
|
||||||
app.ignoreCsrf('post', '/user/:user_id/update/*')
|
|
||||||
app.ignoreCsrf('delete', '/user/:user_id/update/*')
|
|
||||||
|
|
||||||
app.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
|
apiRouter.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
|
||||||
app.del '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
|
apiRouter.delete '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
|
||||||
app.ignoreCsrf('post', '/project/:project_id/contents/*')
|
|
||||||
app.ignoreCsrf('delete', '/project/:project_id/contents/*')
|
|
||||||
|
|
||||||
app.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
apiRouter.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
|
||||||
app.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
apiRouter.delete '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
|
||||||
|
|
||||||
app.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
|
webRouter.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||||
app.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
|
webRouter.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||||
|
|
||||||
app.get /learn(\/.*)?/, WikiController.getPage
|
webRouter.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
|
||||||
|
webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
|
||||||
|
|
||||||
|
webRouter.get /learn(\/.*)?/, WikiController.getPage
|
||||||
|
|
||||||
#Admin Stuff
|
#Admin Stuff
|
||||||
app.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
webRouter.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
||||||
app.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
webRouter.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
||||||
app.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
webRouter.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
||||||
app.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
webRouter.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
||||||
app.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
webRouter.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
||||||
app.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
webRouter.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
||||||
app.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds
|
webRouter.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds
|
||||||
app.post '/admin/pollDropboxForUser', SecurityManager.requestIsAdmin, AdminController.pollDropboxForUser
|
webRouter.post '/admin/pollDropboxForUser', SecurityManager.requestIsAdmin, AdminController.pollDropboxForUser
|
||||||
app.post '/admin/messages', SecurityManager.requestIsAdmin, AdminController.createMessage
|
webRouter.post '/admin/messages', SecurityManager.requestIsAdmin, AdminController.createMessage
|
||||||
app.post '/admin/messages/clear', SecurityManager.requestIsAdmin, AdminController.clearMessages
|
webRouter.post '/admin/messages/clear', SecurityManager.requestIsAdmin, AdminController.clearMessages
|
||||||
|
|
||||||
app.get '/perfTest', (req,res)->
|
apiRouter.get '/perfTest', (req,res)->
|
||||||
res.send("hello")
|
res.send("hello")
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
app.get '/status', (req,res)->
|
apiRouter.get '/status', (req,res)->
|
||||||
res.send("websharelatex is up")
|
res.send("websharelatex is up")
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
app.get '/health_check', HealthCheckController.check
|
|
||||||
app.get '/health_check/redis', HealthCheckController.checkRedis
|
|
||||||
|
|
||||||
app.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) ->
|
webRouter.get '/health_check', HealthCheckController.check
|
||||||
|
webRouter.get '/health_check/redis', HealthCheckController.checkRedis
|
||||||
|
|
||||||
|
apiRouter.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) ->
|
||||||
sendRes = _.once (statusCode, message)->
|
sendRes = _.once (statusCode, message)->
|
||||||
res.writeHead statusCode
|
res.writeHead statusCode
|
||||||
res.end message
|
res.end message
|
||||||
|
@ -187,27 +196,26 @@ module.exports = class Router
|
||||||
setTimeout (() ->
|
setTimeout (() ->
|
||||||
sendRes 500, "Compiler timed out"
|
sendRes 500, "Compiler timed out"
|
||||||
), 10000
|
), 10000
|
||||||
req.session.destroy()
|
|
||||||
|
|
||||||
app.get "/ip", (req, res, next) ->
|
apiRouter.get "/ip", (req, res, next) ->
|
||||||
res.send({
|
res.send({
|
||||||
ip: req.ip
|
ip: req.ip
|
||||||
ips: req.ips
|
ips: req.ips
|
||||||
headers: req.headers
|
headers: req.headers
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
apiRouter.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
||||||
app.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
apiRouter.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
||||||
app.get '/oops-mongo', (req, res, next) ->
|
apiRouter.get '/oops-mongo', (req, res, next) ->
|
||||||
require("./models/Project").Project.findOne {}, () ->
|
require("./models/Project").Project.findOne {}, () ->
|
||||||
throw new Error("Test error")
|
throw new Error("Test error")
|
||||||
|
|
||||||
app.get '/opps-small', (req, res, next)->
|
apiRouter.get '/opps-small', (req, res, next)->
|
||||||
logger.err "test error occured"
|
logger.err "test error occured"
|
||||||
res.send()
|
res.send()
|
||||||
|
|
||||||
app.post '/error/client', (req, res, next) ->
|
webRouter.post '/error/client', (req, res, next) ->
|
||||||
logger.error err: req.body.error, meta: req.body.meta, "client side error"
|
logger.error err: req.body.error, meta: req.body.meta, "client side error"
|
||||||
res.send(204)
|
res.sendStatus(204)
|
||||||
|
|
||||||
app.get '*', ErrorController.notFound
|
webRouter.get '*', ErrorController.notFound
|
||||||
|
|
|
@ -57,15 +57,34 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading")
|
||||||
ng-show="onlineUsersArray.length > 0"
|
ng-show="onlineUsersArray.length > 0"
|
||||||
ng-controller="OnlineUsersController"
|
ng-controller="OnlineUsersController"
|
||||||
)
|
)
|
||||||
span.online-user(
|
span(ng-if="onlineUsersArray.length < 4")
|
||||||
ng-repeat="user in onlineUsersArray",
|
span.online-user(
|
||||||
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }",
|
ng-repeat="user in onlineUsersArray",
|
||||||
popover="{{ user.name }}"
|
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }",
|
||||||
popover-placement="bottom"
|
popover="{{ user.name }}"
|
||||||
popover-append-to-body="true"
|
popover-placement="bottom"
|
||||||
popover-trigger="mouseenter"
|
popover-append-to-body="true"
|
||||||
ng-click="gotoUser(user)"
|
popover-trigger="mouseenter"
|
||||||
) {{ user.name.slice(0,1) }}
|
ng-click="gotoUser(user)"
|
||||||
|
) {{ user.name.slice(0,1) }}
|
||||||
|
|
||||||
|
span.dropdown(dropdown, ng-if="onlineUsersArray.length >= 4")
|
||||||
|
span.online-user.online-user-multi(
|
||||||
|
dropdown-toggle,
|
||||||
|
tooltip="#{translate('connected_users')}",
|
||||||
|
tooltip-placement="left"
|
||||||
|
)
|
||||||
|
strong {{ onlineUsersArray.length }}
|
||||||
|
i.fa.fa-fw.fa-user
|
||||||
|
ul.dropdown-menu.pull-right
|
||||||
|
li.dropdown-header #{translate('connected_users')}
|
||||||
|
li(ng-repeat="user in onlineUsersArray")
|
||||||
|
a(href, ng-click="gotoUser(user)")
|
||||||
|
span.online-user(
|
||||||
|
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }"
|
||||||
|
) {{ user.name.slice(0,1) }}
|
||||||
|
| {{ user.name }}
|
||||||
|
|
||||||
|
|
||||||
a.btn.btn-full-height(
|
a.btn.btn-full-height(
|
||||||
href,
|
href,
|
||||||
|
|
|
@ -48,6 +48,11 @@ aside#left-menu.full-size(
|
||||||
h4() #{translate("sync")}
|
h4() #{translate("sync")}
|
||||||
!= moduleIncludes("editorLeftMenu:sync", locals)
|
!= moduleIncludes("editorLeftMenu:sync", locals)
|
||||||
|
|
||||||
|
span(ng-show="!anonymous")
|
||||||
|
h4 #{translate("services")}
|
||||||
|
!= moduleIncludes("editorLeftMenu:editing_services", 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")
|
||||||
.containter-fluid
|
.containter-fluid
|
||||||
|
|
|
@ -48,8 +48,8 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
||||||
.small #{translate("share_with_your_collabs")}
|
.small #{translate("share_with_your_collabs")}
|
||||||
.form-group
|
.form-group
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type="email"
|
type="text"
|
||||||
placeholder="Enter email address..."
|
placeholder="joe@example.com, sue@example.com, ..."
|
||||||
ng-model="inputs.email"
|
ng-model="inputs.email"
|
||||||
focus-on="open"
|
focus-on="open"
|
||||||
)
|
)
|
||||||
|
@ -64,7 +64,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
||||||
|
|
|
|
||||||
button.btn.btn-info(
|
button.btn.btn-info(
|
||||||
type="submit"
|
type="submit"
|
||||||
ng-click="addMember()"
|
ng-click="addMembers()"
|
||||||
) #{translate("share")}
|
) #{translate("share")}
|
||||||
div.text-center(ng-hide="canAddCollaborators")
|
div.text-center(ng-hide="canAddCollaborators")
|
||||||
p #{translate("need_to_upgrade_for_more_collabs")}.
|
p #{translate("need_to_upgrade_for_more_collabs")}.
|
||||||
|
|
|
@ -71,19 +71,33 @@
|
||||||
strong #{translate("create_your_first_project")}
|
strong #{translate("create_your_first_project")}
|
||||||
|
|
||||||
- if (showUserDetailsArea)
|
- if (showUserDetailsArea)
|
||||||
.row-spaced#userProfileInformation(ng-if="projects.length > 0", ng-cloak)
|
- if (Math.random() < 0.5)
|
||||||
div(ng-controller="UserProfileController")
|
.row-spaced
|
||||||
hr(ng-show="percentComplete < 100")
|
hr
|
||||||
.text-centered.user-profile(ng-show="percentComplete < 100")
|
.card.card-thin
|
||||||
.progress
|
p.text-center.small
|
||||||
.progress-bar.progress-bar-info(ng-style="{'width' : (percentComplete+'%')}")
|
| <strong>Python</strong> or <strong>R</strong> user?
|
||||||
|
|
||||||
p.small #{translate("profile_complete_percentage", {percentval:"{{percentComplete}}"})}
|
p.text-center.small
|
||||||
|
a(href="https://www.getdatajoy.com/", target="_blank").btn.btn-info.btn-small Try DataJoy
|
||||||
|
|
||||||
button#completeUserProfileInformation.btn.btn-info(
|
p.text-center.small(style="font-size: 0.8em")
|
||||||
ng-hide="formVisable",
|
a(href="https://www.getdatajoy.com/", target="_blank") DataJoy
|
||||||
ng-click="openUserProfileModal()"
|
| is a new online Python and R editor from ShareLaTeX.
|
||||||
) #{translate("complete")}
|
- else
|
||||||
|
.row-spaced#userProfileInformation(ng-if="projects.length > 0", ng-cloak)
|
||||||
|
div(ng-controller="UserProfileController")
|
||||||
|
hr(ng-show="percentComplete < 100")
|
||||||
|
.text-centered.user-profile(ng-show="percentComplete < 100")
|
||||||
|
.progress
|
||||||
|
.progress-bar.progress-bar-info(ng-style="{'width' : (percentComplete+'%')}")
|
||||||
|
|
||||||
|
p.small #{translate("profile_complete_percentage", {percentval:"{{percentComplete}}"})}
|
||||||
|
|
||||||
|
button#completeUserProfileInformation.btn.btn-info(
|
||||||
|
ng-hide="formVisable",
|
||||||
|
ng-click="openUserProfileModal()"
|
||||||
|
) #{translate("complete")}
|
||||||
|
|
||||||
-if (settings.enableSubscriptions && !hasSubscription)
|
-if (settings.enableSubscriptions && !hasSubscription)
|
||||||
.row-spaced(ng-if="projects.length > 0", ng-cloak).text-centered
|
.row-spaced(ng-if="projects.length > 0", ng-cloak).text-centered
|
||||||
|
|
|
@ -12,22 +12,30 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "0.9.0",
|
"archiver": "0.9.0",
|
||||||
"async": "0.6.2",
|
"async": "0.6.2",
|
||||||
|
"base64-stream": "^0.1.2",
|
||||||
|
"basic-auth-connect": "^1.0.0",
|
||||||
"bcrypt": "0.8.3",
|
"bcrypt": "0.8.3",
|
||||||
|
"body-parser": "^1.13.1",
|
||||||
"bufferedstream": "1.6.0",
|
"bufferedstream": "1.6.0",
|
||||||
"connect-redis": "1.4.5",
|
"connect-redis": "2.3.0",
|
||||||
|
"cookie-parser": "1.3.5",
|
||||||
|
"csurf": "^1.8.3",
|
||||||
"dateformat": "1.0.4-1.2.3",
|
"dateformat": "1.0.4-1.2.3",
|
||||||
"express": "3.3.4",
|
"express": "4.13.0",
|
||||||
"fairy": "0.0.2",
|
"express-session": "1.11.3",
|
||||||
"http-proxy": "^1.8.1",
|
"http-proxy": "^1.8.1",
|
||||||
"jade": "~1.3.1",
|
"jade": "~1.3.1",
|
||||||
"ldapjs": "^0.7.1",
|
"ldapjs": "^0.7.1",
|
||||||
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
|
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
|
||||||
"lynx": "0.1.1",
|
"lynx": "0.1.1",
|
||||||
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0",
|
"marked": "^0.3.3",
|
||||||
|
"method-override": "^2.3.3",
|
||||||
|
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.2.0",
|
||||||
"mimelib": "0.2.14",
|
"mimelib": "0.2.14",
|
||||||
"mocha": "1.17.1",
|
"mocha": "1.17.1",
|
||||||
"mongojs": "0.18.2",
|
"mongojs": "0.18.2",
|
||||||
"mongoose": "3.8.28",
|
"mongoose": "4.1.0",
|
||||||
|
"multer": "^0.1.8",
|
||||||
"node-uuid": "1.4.1",
|
"node-uuid": "1.4.1",
|
||||||
"nodemailer": "0.6.1",
|
"nodemailer": "0.6.1",
|
||||||
"optimist": "0.6.1",
|
"optimist": "0.6.1",
|
||||||
|
|
|
@ -56,7 +56,11 @@ define [
|
||||||
user.doc = @ide.fileTreeManager.findEntityById(user.doc_id)
|
user.doc = @ide.fileTreeManager.findEntityById(user.doc_id)
|
||||||
|
|
||||||
if user.name?.trim().length == 0
|
if user.name?.trim().length == 0
|
||||||
user.name = user.email
|
user.name = user.email.trim()
|
||||||
|
|
||||||
|
user.initial = user.name?[0]
|
||||||
|
if !user.initial or user.initial == " "
|
||||||
|
user.initial = "?"
|
||||||
|
|
||||||
@$scope.onlineUsersArray.push user
|
@$scope.onlineUsersArray.push user
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,34 @@ define [
|
||||||
allowedNoOfMembers = $scope.project.features.collaborators
|
allowedNoOfMembers = $scope.project.features.collaborators
|
||||||
$scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS
|
$scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS
|
||||||
|
|
||||||
$scope.addMember = () ->
|
$scope.addMembers = () ->
|
||||||
return if !$scope.inputs.email? or $scope.inputs.email == ""
|
return if !$scope.inputs.email? or $scope.inputs.email == ""
|
||||||
|
|
||||||
|
emails = $scope.inputs.email.split(/,\s*/)
|
||||||
|
$scope.inputs.email = ""
|
||||||
$scope.state.error = null
|
$scope.state.error = null
|
||||||
$scope.state.inflight = true
|
$scope.state.inflight = true
|
||||||
projectMembers
|
|
||||||
.addMember($scope.inputs.email, $scope.inputs.privileges)
|
do addNextMember = () ->
|
||||||
.success (data) ->
|
if emails.length == 0 or !$scope.canAddCollaborators
|
||||||
$scope.state.inflight = false
|
$scope.state.inflight = false
|
||||||
$scope.inputs.email = ""
|
$scope.$apply()
|
||||||
$scope.project.members.push data?.user
|
return
|
||||||
.error () ->
|
|
||||||
$scope.state.inflight = false
|
email = emails.shift()
|
||||||
$scope.state.error = "Sorry, something went wrong :("
|
projectMembers
|
||||||
|
.addMember(email, $scope.inputs.privileges)
|
||||||
|
.success (data) ->
|
||||||
|
if data?.user # data.user is false if collaborator limit is hit.
|
||||||
|
$scope.project.members.push data.user
|
||||||
|
setTimeout () ->
|
||||||
|
# Give $scope a chance to update $scope.canAddCollaborators
|
||||||
|
# with new collaborator information.
|
||||||
|
addNextMember()
|
||||||
|
, 0
|
||||||
|
.error () ->
|
||||||
|
$scope.state.inflight = false
|
||||||
|
$scope.state.error = "Sorry, something went wrong :("
|
||||||
|
|
||||||
|
|
||||||
$scope.removeMember = (member) ->
|
$scope.removeMember = (member) ->
|
||||||
|
|
|
@ -3,7 +3,6 @@ define [
|
||||||
"main/user-details"
|
"main/user-details"
|
||||||
"main/account-settings"
|
"main/account-settings"
|
||||||
"main/account-upgrade"
|
"main/account-upgrade"
|
||||||
"main/templates"
|
|
||||||
"main/plans"
|
"main/plans"
|
||||||
"main/group-members"
|
"main/group-members"
|
||||||
"main/scribtex-popup"
|
"main/scribtex-popup"
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
define [
|
|
||||||
"base"
|
|
||||||
], (App) ->
|
|
||||||
|
|
||||||
App.controller "openInSlController", ($scope) ->
|
|
||||||
|
|
||||||
$scope.openInSlText = "Open in ShareLaTeX"
|
|
||||||
$scope.isDisabled = false
|
|
||||||
|
|
||||||
$scope.open = ->
|
|
||||||
$scope.openInSlText = "Creating..."
|
|
||||||
$scope.isDisabled = true
|
|
||||||
ga('send', 'event', 'template-site', 'open-in-sl', $('.page-header h1').text())
|
|
||||||
|
|
||||||
$scope.downloadZip = ->
|
|
||||||
ga('send', 'event', 'template-site', 'download-zip', $('.page-header h1').text())
|
|
||||||
|
|
||||||
|
|
||||||
App.factory "algolia", ->
|
|
||||||
if window?.sharelatex?.algolia?.app_id?
|
|
||||||
client = new AlgoliaSearch(window.sharelatex.algolia?.app_id, window.sharelatex.algolia?.api_key)
|
|
||||||
index = client.initIndex(window.sharelatex.algolia?.indexes?.templates)
|
|
||||||
return index
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
App.controller "SearchController", ($scope, algolia, _) ->
|
|
||||||
$scope.hits = []
|
|
||||||
|
|
||||||
$scope.clearSearchText = ->
|
|
||||||
$scope.searchQueryText = ""
|
|
||||||
updateHits []
|
|
||||||
|
|
||||||
$scope.safeApply = (fn)->
|
|
||||||
phase = $scope.$root.$$phase
|
|
||||||
if(phase == '$apply' || phase == '$digest')
|
|
||||||
$scope.$eval(fn)
|
|
||||||
else
|
|
||||||
$scope.$apply(fn)
|
|
||||||
|
|
||||||
buildHitViewModel = (hit)->
|
|
||||||
result =
|
|
||||||
name : hit._highlightResult.name.value
|
|
||||||
description: hit._highlightResult.description.value
|
|
||||||
url :"/templates/#{hit._id}"
|
|
||||||
image_url: "#{window.sharelatex?.templates?.cdnDomain}/#{hit._id}/v/#{hit.version}/pdf-converted-cache/style-thumbnail"
|
|
||||||
|
|
||||||
updateHits = (hits)->
|
|
||||||
$scope.safeApply ->
|
|
||||||
$scope.hits = hits
|
|
||||||
|
|
||||||
$scope.search = ->
|
|
||||||
query = $scope.searchQueryText
|
|
||||||
if !query? or query.length == 0
|
|
||||||
updateHits []
|
|
||||||
return
|
|
||||||
|
|
||||||
query = "#{window.sharelatex?.templates?.user_id} #{query}"
|
|
||||||
algolia.search query, (err, response)->
|
|
||||||
if response.hits.length == 0
|
|
||||||
updateHits []
|
|
||||||
else
|
|
||||||
hits = _.map response.hits, buildHitViewModel
|
|
||||||
updateHits hits
|
|
||||||
|
|
||||||
|
|
||||||
App.controller "MissingTemplateController", ($scope, $modal)->
|
|
||||||
$scope.showMissingTemplateModal = ->
|
|
||||||
$modal.open {
|
|
||||||
templateUrl: "missingTemplateModal"
|
|
||||||
controller:"MissingTemplateModalController"
|
|
||||||
}
|
|
||||||
|
|
||||||
App.controller "MissingTemplateModalController", ($scope, $modalInstance) ->
|
|
||||||
$scope.cancel = () ->
|
|
||||||
$modalInstance.dismiss()
|
|
||||||
|
|
BIN
services/web/public/img/about/geri.jpg
Normal file
BIN
services/web/public/img/about/geri.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
BIN
services/web/public/img/about/kiri_channon.jpg
Normal file
BIN
services/web/public/img/about/kiri_channon.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
services/web/public/img/about/shane_kilkelly.jpg
Normal file
BIN
services/web/public/img/about/shane_kilkelly.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
services/web/public/img/enago.png
Normal file
BIN
services/web/public/img/enago.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
|
@ -3,6 +3,7 @@
|
||||||
@import "./editor/toolbar.less";
|
@import "./editor/toolbar.less";
|
||||||
@import "./editor/left-menu.less";
|
@import "./editor/left-menu.less";
|
||||||
@import "./editor/pdf.less";
|
@import "./editor/pdf.less";
|
||||||
|
@import "./editor/enago.less";
|
||||||
@import "./editor/share.less";
|
@import "./editor/share.less";
|
||||||
@import "./editor/chat.less";
|
@import "./editor/chat.less";
|
||||||
@import "./editor/binary-file.less";
|
@import "./editor/binary-file.less";
|
||||||
|
|
13
services/web/public/stylesheets/app/editor/enago.less
Normal file
13
services/web/public/stylesheets/app/editor/enago.less
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.services {
|
||||||
|
h1, h2, h3, p {
|
||||||
|
text-shadow: 0 -1px 1px white;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
color: @red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hr.small {
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
|
@online-user-color: rgb(0, 170, 255);
|
||||||
|
|
||||||
.online-users {
|
.online-users {
|
||||||
.online-user {
|
.online-user {
|
||||||
background-color: rgb(0, 170, 255);
|
background-color: @online-user-color;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
@ -11,4 +13,27 @@
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.online-user-multi {
|
||||||
|
width: auto;
|
||||||
|
min-width: 24px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
a {
|
||||||
|
// Override toolbar link styles
|
||||||
|
display: block;
|
||||||
|
padding: 4px 10px 5px;
|
||||||
|
margin: 1px 2px;
|
||||||
|
color: @text-color;
|
||||||
|
&:hover, &:active {
|
||||||
|
color: @text-color;
|
||||||
|
background-color: @gray-lightest;
|
||||||
|
text-shadow: none;
|
||||||
|
.box-shadow(none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -49,6 +49,9 @@
|
||||||
|
|
||||||
.template-details-section {
|
.template-details-section {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
|
.btn {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchResult {
|
.searchResult {
|
||||||
|
|
|
@ -423,6 +423,7 @@ describe "AuthenticationController", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@req.session =
|
@req.session =
|
||||||
save: sinon.stub().callsArg(0)
|
save: sinon.stub().callsArg(0)
|
||||||
|
destroy : sinon.stub()
|
||||||
@req.sessionStore =
|
@req.sessionStore =
|
||||||
generate: sinon.stub()
|
generate: sinon.stub()
|
||||||
@AuthenticationController.establishUserSession @req, @user, @callback
|
@AuthenticationController.establishUserSession @req, @user, @callback
|
||||||
|
@ -435,6 +436,9 @@ describe "AuthenticationController", ->
|
||||||
@req.session.user.referal_id.should.equal @user.referal_id
|
@req.session.user.referal_id.should.equal @user.referal_id
|
||||||
@req.session.user.isAdmin.should.equal @user.isAdmin
|
@req.session.user.isAdmin.should.equal @user.isAdmin
|
||||||
|
|
||||||
|
it "should destroy the session", ->
|
||||||
|
@req.session.destroy.called.should.equal true
|
||||||
|
|
||||||
it "should regenerate the session to protect against session fixation", ->
|
it "should regenerate the session to protect against session fixation", ->
|
||||||
@req.sessionStore.generate.calledWith(@req).should.equal true
|
@req.sessionStore.generate.calledWith(@req).should.equal true
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ describe "CollaboratorsController", ->
|
||||||
@req.params =
|
@req.params =
|
||||||
Project_id: @project_id = "project-id-123"
|
Project_id: @project_id = "project-id-123"
|
||||||
user_id: @user_id = "user-id-123"
|
user_id: @user_id = "user-id-123"
|
||||||
@res.send = sinon.stub()
|
@res.sendStatus = sinon.stub()
|
||||||
@EditorController.removeUserFromProject = sinon.stub().callsArg(2)
|
@EditorController.removeUserFromProject = sinon.stub().callsArg(2)
|
||||||
@CollaboratorsController.removeUserFromProject @req, @res
|
@CollaboratorsController.removeUserFromProject @req, @res
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ describe "CollaboratorsController", ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should send the back a success response", ->
|
it "should send the back a success response", ->
|
||||||
@res.send.calledWith(204).should.equal true
|
@res.sendStatus.calledWith(204).should.equal true
|
||||||
|
|
||||||
|
|
||||||
describe "_formatCollaborators", ->
|
describe "_formatCollaborators", ->
|
||||||
|
|
|
@ -98,7 +98,6 @@ describe "CompileController", ->
|
||||||
|
|
||||||
describe "when downloading for embedding", ->
|
describe "when downloading for embedding", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@project.useClsi2 = true
|
|
||||||
@CompileController.proxyToClsi = sinon.stub()
|
@CompileController.proxyToClsi = sinon.stub()
|
||||||
@CompileController.downloadPdf(@req, @res, @next)
|
@CompileController.downloadPdf(@req, @res, @next)
|
||||||
|
|
||||||
|
@ -321,7 +320,7 @@ describe "CompileController", ->
|
||||||
@CompileManager.deleteAuxFiles = sinon.stub().callsArg(1)
|
@CompileManager.deleteAuxFiles = sinon.stub().callsArg(1)
|
||||||
@req.params =
|
@req.params =
|
||||||
Project_id: @project_id
|
Project_id: @project_id
|
||||||
@res.send = sinon.stub()
|
@res.sendStatus = sinon.stub()
|
||||||
@CompileController.deleteAuxFiles @req, @res, @next
|
@CompileController.deleteAuxFiles @req, @res, @next
|
||||||
|
|
||||||
it "should proxy to the CLSI", ->
|
it "should proxy to the CLSI", ->
|
||||||
|
@ -330,7 +329,7 @@ describe "CompileController", ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should return a 200", ->
|
it "should return a 200", ->
|
||||||
@res.send
|
@res.sendStatus
|
||||||
.calledWith(200)
|
.calledWith(200)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe "DocstoreManager", ->
|
||||||
apis:
|
apis:
|
||||||
docstore:
|
docstore:
|
||||||
url: "docstore.sharelatex.com"
|
url: "docstore.sharelatex.com"
|
||||||
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
|
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->}
|
||||||
|
|
||||||
@requestDefaults.calledWith(jar: false).should.equal true
|
@requestDefaults.calledWith(jar: false).should.equal true
|
||||||
|
|
||||||
|
@ -179,3 +179,42 @@ describe "DocstoreManager", ->
|
||||||
project_id: @project_id
|
project_id: @project_id
|
||||||
}, "error getting all docs from docstore")
|
}, "error getting all docs from docstore")
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
|
|
||||||
|
describe "archiveProject", ->
|
||||||
|
describe "with a successful response code", ->
|
||||||
|
beforeEach ->
|
||||||
|
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 204)
|
||||||
|
@DocstoreManager.archiveProject @project_id, @callback
|
||||||
|
|
||||||
|
it "should call the callback", ->
|
||||||
|
@callback.called.should.equal true
|
||||||
|
|
||||||
|
describe "with a failed response code", ->
|
||||||
|
beforeEach ->
|
||||||
|
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500)
|
||||||
|
@DocstoreManager.archiveProject @project_id, @callback
|
||||||
|
|
||||||
|
it "should call the callback with an error", ->
|
||||||
|
@callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe "unarchiveProject", ->
|
||||||
|
describe "with a successful response code", ->
|
||||||
|
beforeEach ->
|
||||||
|
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 204)
|
||||||
|
@DocstoreManager.unarchiveProject @project_id, @callback
|
||||||
|
|
||||||
|
it "should call the callback", ->
|
||||||
|
@callback.called.should.equal true
|
||||||
|
|
||||||
|
describe "with a failed response code", ->
|
||||||
|
beforeEach ->
|
||||||
|
@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500)
|
||||||
|
@DocstoreManager.unarchiveProject @project_id, @callback
|
||||||
|
|
||||||
|
it "should call the callback with an error", ->
|
||||||
|
@callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,9 @@ Errors = require "../../../../app/js/errors"
|
||||||
describe "DocumentController", ->
|
describe "DocumentController", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentController = SandboxedModule.require modulePath, requires:
|
@DocumentController = SandboxedModule.require modulePath, requires:
|
||||||
|
"logger-sharelatex":
|
||||||
|
log:->
|
||||||
|
err:->
|
||||||
"../Project/ProjectEntityHandler": @ProjectEntityHandler = {}
|
"../Project/ProjectEntityHandler": @ProjectEntityHandler = {}
|
||||||
@res = new MockResponse()
|
@res = new MockResponse()
|
||||||
@req = new MockRequest()
|
@req = new MockRequest()
|
||||||
|
|
|
@ -24,6 +24,7 @@ describe "EditorHttpController", ->
|
||||||
@req = {}
|
@req = {}
|
||||||
@res =
|
@res =
|
||||||
send: sinon.stub()
|
send: sinon.stub()
|
||||||
|
sendStatus: sinon.stub()
|
||||||
json: sinon.stub()
|
json: sinon.stub()
|
||||||
@callback = sinon.stub()
|
@callback = sinon.stub()
|
||||||
|
|
||||||
|
@ -190,7 +191,7 @@ describe "EditorHttpController", ->
|
||||||
@EditorHttpController.addDoc @req, @res
|
@EditorHttpController.addDoc @req, @res
|
||||||
|
|
||||||
it "should send back a bad request status code", ->
|
it "should send back a bad request status code", ->
|
||||||
@res.send.calledWith(400).should.equal true
|
@res.sendStatus.calledWith(400).should.equal true
|
||||||
|
|
||||||
describe "addFolder", ->
|
describe "addFolder", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -223,7 +224,7 @@ describe "EditorHttpController", ->
|
||||||
@EditorHttpController.addFolder @req, @res
|
@EditorHttpController.addFolder @req, @res
|
||||||
|
|
||||||
it "should send back a bad request status code", ->
|
it "should send back a bad request status code", ->
|
||||||
@res.send.calledWith(400).should.equal true
|
@res.sendStatus.calledWith(400).should.equal true
|
||||||
|
|
||||||
|
|
||||||
describe "renameEntity", ->
|
describe "renameEntity", ->
|
||||||
|
@ -243,7 +244,7 @@ describe "EditorHttpController", ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should send back a success response", ->
|
it "should send back a success response", ->
|
||||||
@res.send.calledWith(204).should.equal true
|
@res.sendStatus.calledWith(204).should.equal true
|
||||||
|
|
||||||
describe "renameEntity with long name", ->
|
describe "renameEntity with long name", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -257,7 +258,7 @@ describe "EditorHttpController", ->
|
||||||
@EditorHttpController.renameEntity @req, @res
|
@EditorHttpController.renameEntity @req, @res
|
||||||
|
|
||||||
it "should send back a bad request status code", ->
|
it "should send back a bad request status code", ->
|
||||||
@res.send.calledWith(400).should.equal true
|
@res.sendStatus.calledWith(400).should.equal true
|
||||||
|
|
||||||
describe "rename entity with 0 length name", ->
|
describe "rename entity with 0 length name", ->
|
||||||
|
|
||||||
|
@ -272,7 +273,7 @@ describe "EditorHttpController", ->
|
||||||
@EditorHttpController.renameEntity @req, @res
|
@EditorHttpController.renameEntity @req, @res
|
||||||
|
|
||||||
it "should send back a bad request status code", ->
|
it "should send back a bad request status code", ->
|
||||||
@res.send.calledWith(400).should.equal true
|
@res.sendStatus.calledWith(400).should.equal true
|
||||||
|
|
||||||
|
|
||||||
describe "moveEntity", ->
|
describe "moveEntity", ->
|
||||||
|
@ -292,7 +293,7 @@ describe "EditorHttpController", ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should send back a success response", ->
|
it "should send back a success response", ->
|
||||||
@res.send.calledWith(204).should.equal true
|
@res.sendStatus.calledWith(204).should.equal true
|
||||||
|
|
||||||
describe "deleteEntity", ->
|
describe "deleteEntity", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -309,4 +310,4 @@ describe "EditorHttpController", ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should send back a success response", ->
|
it "should send back a success response", ->
|
||||||
@res.send.calledWith(204).should.equal true
|
@res.sendStatus.calledWith(204).should.equal true
|
||||||
|
|
|
@ -26,6 +26,7 @@ describe "FileStoreController", ->
|
||||||
Project_id: @project_id
|
Project_id: @project_id
|
||||||
File_id: @file_id
|
File_id: @file_id
|
||||||
query: "query string here"
|
query: "query string here"
|
||||||
|
get: (key) -> undefined
|
||||||
@res =
|
@res =
|
||||||
setHeader: sinon.stub()
|
setHeader: sinon.stub()
|
||||||
@file =
|
@file =
|
||||||
|
@ -65,4 +66,65 @@ describe "FileStoreController", ->
|
||||||
done()
|
done()
|
||||||
@controller.getFile @req, @res
|
@controller.getFile @req, @res
|
||||||
|
|
||||||
|
# Test behaviour around handling html files
|
||||||
|
['.html', '.htm', '.xhtml'].forEach (extension) ->
|
||||||
|
describe "with a '#{extension}' file extension", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user_agent = 'A generic browser'
|
||||||
|
@file.name = "bad#{extension}"
|
||||||
|
@req.get = (key) =>
|
||||||
|
if key == 'User-Agent'
|
||||||
|
@user_agent
|
||||||
|
|
||||||
|
describe "from a non-ios browser", ->
|
||||||
|
|
||||||
|
it "should not set Content-Type", (done) ->
|
||||||
|
@stream.pipe = (des) =>
|
||||||
|
@res.setHeader.calledWith("Content-Type", "text/plain").should.equal false
|
||||||
|
done()
|
||||||
|
@controller.getFile @req, @res
|
||||||
|
|
||||||
|
describe "from an iPhone", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user_agent = "An iPhone browser"
|
||||||
|
|
||||||
|
it "should set Content-Type to 'text/plain'", (done) ->
|
||||||
|
@stream.pipe = (des) =>
|
||||||
|
@res.setHeader.calledWith("Content-Type", "text/plain").should.equal true
|
||||||
|
done()
|
||||||
|
@controller.getFile @req, @res
|
||||||
|
|
||||||
|
describe "from an iPad", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user_agent = "An iPad browser"
|
||||||
|
|
||||||
|
it "should set Content-Type to 'text/plain'", (done) ->
|
||||||
|
@stream.pipe = (des) =>
|
||||||
|
@res.setHeader.calledWith("Content-Type", "text/plain").should.equal true
|
||||||
|
done()
|
||||||
|
@controller.getFile @req, @res
|
||||||
|
|
||||||
|
# None of these should trigger the iOS/html logic
|
||||||
|
['x.html-is-rad', 'html.pdf', '.html-is-good-for-hidden-files', 'somefile'].forEach (filename) ->
|
||||||
|
describe "with filename as '#{filename}'", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user_agent = 'A generic browser'
|
||||||
|
@file.name = filename
|
||||||
|
@req.get = (key) =>
|
||||||
|
if key == 'User-Agent'
|
||||||
|
@user_agent
|
||||||
|
|
||||||
|
['iPhone', 'iPad', 'Firefox', 'Chrome'].forEach (browser) ->
|
||||||
|
describe "downloaded from #{browser}", ->
|
||||||
|
beforeEach ->
|
||||||
|
@user_agent = "Some #{browser} thing"
|
||||||
|
|
||||||
|
it 'Should not set the Content-type', (done) ->
|
||||||
|
@stream.pipe = (des) =>
|
||||||
|
@res.setHeader.calledWith("Content-Type", "text/plain").should.equal false
|
||||||
|
done()
|
||||||
|
@controller.getFile @req, @res
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
should = require('chai').should()
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
assert = require('assert')
|
||||||
|
path = require('path')
|
||||||
|
sinon = require('sinon')
|
||||||
|
modulePath = path.join __dirname, "../../../../app/js/Features/InactiveData/InactiveProjectManager"
|
||||||
|
expect = require("chai").expect
|
||||||
|
|
||||||
|
describe "InactiveProjectManager", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
|
||||||
|
@settings = {}
|
||||||
|
@DocstoreManager =
|
||||||
|
unarchiveProject:sinon.stub()
|
||||||
|
archiveProject:sinon.stub()
|
||||||
|
@ProjectUpdateHandler =
|
||||||
|
markAsActive:sinon.stub()
|
||||||
|
markAsInactive:sinon.stub()
|
||||||
|
@ProjectGetter =
|
||||||
|
getProject:sinon.stub()
|
||||||
|
@InactiveProjectManager = SandboxedModule.require modulePath, requires:
|
||||||
|
"settings-sharelatex":@settings
|
||||||
|
"logger-sharelatex":
|
||||||
|
log:->
|
||||||
|
err:->
|
||||||
|
"../Docstore/DocstoreManager":@DocstoreManager
|
||||||
|
"../Project/ProjectUpdateHandler":@ProjectUpdateHandler
|
||||||
|
"../Project/ProjectGetter":@ProjectGetter
|
||||||
|
|
||||||
|
@project_id = "1234"
|
||||||
|
|
||||||
|
describe "reactivateProjectIfRequired", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@project = {active:false}
|
||||||
|
@ProjectGetter.getProject.callsArgWith(2, null, @project)
|
||||||
|
@ProjectUpdateHandler.markAsActive.callsArgWith(1)
|
||||||
|
|
||||||
|
it "should call unarchiveProject", (done)->
|
||||||
|
@DocstoreManager.unarchiveProject.callsArgWith(1)
|
||||||
|
@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
|
||||||
|
@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
|
||||||
|
@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should not mark project as active if error with unarchinging", (done)->
|
||||||
|
@DocstoreManager.unarchiveProject.callsArgWith(1, "error")
|
||||||
|
@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
|
||||||
|
err.should.equal "error"
|
||||||
|
@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
|
||||||
|
@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal false
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
it "should not call unarchiveProject if it is active", (done)->
|
||||||
|
@project.active = true
|
||||||
|
@DocstoreManager.unarchiveProject.callsArgWith(1)
|
||||||
|
@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
|
||||||
|
@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal false
|
||||||
|
@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal false
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
describe "deactivateProject", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
|
||||||
|
it "should call unarchiveProject and markAsInactive", (done)->
|
||||||
|
@DocstoreManager.archiveProject.callsArgWith(1)
|
||||||
|
@ProjectUpdateHandler.markAsInactive.callsArgWith(1)
|
||||||
|
|
||||||
|
@InactiveProjectManager.deactivateProject @project_id, (err)=>
|
||||||
|
@DocstoreManager.archiveProject.calledWith(@project_id).should.equal true
|
||||||
|
@ProjectUpdateHandler.markAsInactive.calledWith(@project_id).should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should not call markAsInactive if there was a problem unarchiving", (done)->
|
||||||
|
@DocstoreManager.archiveProject.callsArgWith(1, "errorrr")
|
||||||
|
@ProjectUpdateHandler.markAsInactive.callsArgWith(1)
|
||||||
|
|
||||||
|
@InactiveProjectManager.deactivateProject @project_id, (err)=>
|
||||||
|
err.should.equal "errorrr"
|
||||||
|
@DocstoreManager.archiveProject.calledWith(@project_id).should.equal true
|
||||||
|
@ProjectUpdateHandler.markAsInactive.calledWith(@project_id).should.equal false
|
||||||
|
done()
|
|
@ -32,6 +32,8 @@ describe "PasswordResetController", ->
|
||||||
password:@password
|
password:@password
|
||||||
i18n:
|
i18n:
|
||||||
translate:->
|
translate:->
|
||||||
|
session: {}
|
||||||
|
query: {}
|
||||||
|
|
||||||
@res = {}
|
@res = {}
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ describe "PasswordResetController", ->
|
||||||
it "should tell the handler to process that email", (done)->
|
it "should tell the handler to process that email", (done)->
|
||||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
|
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
|
||||||
done()
|
done()
|
||||||
|
@ -78,7 +80,7 @@ describe "PasswordResetController", ->
|
||||||
@req.body.email = @email
|
@req.body.email = @email
|
||||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
|
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
|
||||||
done()
|
done()
|
||||||
|
@ -86,9 +88,12 @@ describe "PasswordResetController", ->
|
||||||
|
|
||||||
describe "setNewUserPassword", ->
|
describe "setNewUserPassword", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@req.session.resetToken = @token
|
||||||
|
|
||||||
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, null, true)
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true)
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (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()
|
||||||
|
@ -104,7 +109,7 @@ describe "PasswordResetController", ->
|
||||||
it "should return 400 (Bad Request) 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.sendStatus = (code)=>
|
||||||
code.should.equal 400
|
code.should.equal 400
|
||||||
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
||||||
done()
|
done()
|
||||||
|
@ -113,11 +118,51 @@ describe "PasswordResetController", ->
|
||||||
it "should return 400 (Bad Request) if there is no passwordResetToken", (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.sendStatus = (code)=>
|
||||||
code.should.equal 400
|
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 clear the session.resetToken", (done) ->
|
||||||
|
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true)
|
||||||
|
@res.sendStatus = (code)=>
|
||||||
|
code.should.equal 200
|
||||||
|
@req.session.should.not.have.property 'resetToken'
|
||||||
|
done()
|
||||||
|
@PasswordResetController.setNewUserPassword @req, @res
|
||||||
|
|
||||||
|
describe "renderSetPasswordForm", ->
|
||||||
|
|
||||||
|
describe "with token in query-string", ->
|
||||||
|
beforeEach ->
|
||||||
|
@req.query.passwordResetToken = @token
|
||||||
|
|
||||||
|
it "should set session.resetToken and redirect", (done) ->
|
||||||
|
@req.session.should.not.have.property 'resetToken'
|
||||||
|
@res.redirect = (path) =>
|
||||||
|
path.should.equal '/user/password/set'
|
||||||
|
@req.session.resetToken.should.equal @token
|
||||||
|
done()
|
||||||
|
@PasswordResetController.renderSetPasswordForm(@req, @res)
|
||||||
|
|
||||||
|
describe "without a token in query-string", ->
|
||||||
|
|
||||||
|
describe "with token in session", ->
|
||||||
|
beforeEach ->
|
||||||
|
@req.session.resetToken = @token
|
||||||
|
|
||||||
|
it "should render the page, passing the reset token", (done) ->
|
||||||
|
@res.render = (template_path, options) =>
|
||||||
|
options.passwordResetToken.should.equal @req.session.resetToken
|
||||||
|
done()
|
||||||
|
@PasswordResetController.renderSetPasswordForm(@req, @res)
|
||||||
|
|
||||||
|
describe "without a token in session", ->
|
||||||
|
|
||||||
|
it "should redirect to the reset request page", (done) ->
|
||||||
|
@res.redirect = (path) =>
|
||||||
|
path.should.equal "/user/password/reset"
|
||||||
|
@req.session.should.not.have.property 'resetToken'
|
||||||
|
done()
|
||||||
|
@PasswordResetController.renderSetPasswordForm(@req, @res)
|
||||||
|
|
|
@ -36,14 +36,7 @@ describe 'Project api controller', ->
|
||||||
|
|
||||||
it "should send a 500 if there is an error", (done)->
|
it "should send a 500 if there is an error", (done)->
|
||||||
@ProjectDetailsHandler.getDetails.callsArgWith(1, "error")
|
@ProjectDetailsHandler.getDetails.callsArgWith(1, "error")
|
||||||
@res.send = (resCode)=>
|
@res.sendStatus = (resCode)=>
|
||||||
resCode.should.equal 500
|
resCode.should.equal 500
|
||||||
done()
|
done()
|
||||||
@controller.getProjectDetails @req, @res
|
@controller.getProjectDetails @req, @res
|
||||||
|
|
||||||
it "should destroy the session", (done)->
|
|
||||||
@ProjectDetailsHandler.getDetails.callsArgWith(1, null, @projDetails)
|
|
||||||
@res.json = (data)=>
|
|
||||||
@req.session.destroy.called.should.equal true
|
|
||||||
done()
|
|
||||||
@controller.getProjectDetails @req, @res
|
|
|
@ -42,6 +42,10 @@ describe "ProjectController", ->
|
||||||
userCanAccessProject:sinon.stub()
|
userCanAccessProject:sinon.stub()
|
||||||
@EditorController =
|
@EditorController =
|
||||||
renameProject:sinon.stub()
|
renameProject:sinon.stub()
|
||||||
|
@InactiveProjectManager =
|
||||||
|
reactivateProjectIfRequired:sinon.stub()
|
||||||
|
@ProjectUpdateHandler =
|
||||||
|
markAsOpened: sinon.stub()
|
||||||
@ProjectController = SandboxedModule.require modulePath, requires:
|
@ProjectController = SandboxedModule.require modulePath, requires:
|
||||||
"settings-sharelatex":@settings
|
"settings-sharelatex":@settings
|
||||||
"logger-sharelatex":
|
"logger-sharelatex":
|
||||||
|
@ -57,6 +61,8 @@ describe "ProjectController", ->
|
||||||
'../../models/Project': Project:@ProjectModel
|
'../../models/Project': Project:@ProjectModel
|
||||||
"../../models/User":User:@UserModel
|
"../../models/User":User:@UserModel
|
||||||
"../../managers/SecurityManager":@SecurityManager
|
"../../managers/SecurityManager":@SecurityManager
|
||||||
|
"../InactiveData/InactiveProjectManager":@InactiveProjectManager
|
||||||
|
"./ProjectUpdateHandler":@ProjectUpdateHandler
|
||||||
|
|
||||||
@user =
|
@user =
|
||||||
_id:"!£123213kjljkl"
|
_id:"!£123213kjljkl"
|
||||||
|
@ -78,7 +84,7 @@ describe "ProjectController", ->
|
||||||
@EditorController.renameProject = sinon.stub().callsArg(2)
|
@EditorController.renameProject = sinon.stub().callsArg(2)
|
||||||
@req.body =
|
@req.body =
|
||||||
name: @name = "New name"
|
name: @name = "New name"
|
||||||
@res.send = (code) =>
|
@res.sendStatus = (code) =>
|
||||||
@EditorController.renameProject
|
@EditorController.renameProject
|
||||||
.calledWith(@project_id, @name)
|
.calledWith(@project_id, @name)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
@ -90,7 +96,7 @@ describe "ProjectController", ->
|
||||||
@EditorController.setCompiler = sinon.stub().callsArg(2)
|
@EditorController.setCompiler = sinon.stub().callsArg(2)
|
||||||
@req.body =
|
@req.body =
|
||||||
compiler: @compiler = "pdflatex"
|
compiler: @compiler = "pdflatex"
|
||||||
@res.send = (code) =>
|
@res.sendStatus = (code) =>
|
||||||
@EditorController.setCompiler
|
@EditorController.setCompiler
|
||||||
.calledWith(@project_id, @compiler)
|
.calledWith(@project_id, @compiler)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
@ -102,7 +108,7 @@ describe "ProjectController", ->
|
||||||
@EditorController.setSpellCheckLanguage = sinon.stub().callsArg(2)
|
@EditorController.setSpellCheckLanguage = sinon.stub().callsArg(2)
|
||||||
@req.body =
|
@req.body =
|
||||||
spellCheckLanguage: @languageCode = "fr"
|
spellCheckLanguage: @languageCode = "fr"
|
||||||
@res.send = (code) =>
|
@res.sendStatus = (code) =>
|
||||||
@EditorController.setSpellCheckLanguage
|
@EditorController.setSpellCheckLanguage
|
||||||
.calledWith(@project_id, @languageCode)
|
.calledWith(@project_id, @languageCode)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
@ -114,7 +120,7 @@ describe "ProjectController", ->
|
||||||
@EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
@EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
||||||
@req.body =
|
@req.body =
|
||||||
publicAccessLevel: @publicAccessLevel = "readonly"
|
publicAccessLevel: @publicAccessLevel = "readonly"
|
||||||
@res.send = (code) =>
|
@res.sendStatus = (code) =>
|
||||||
@EditorController.setPublicAccessLevel
|
@EditorController.setPublicAccessLevel
|
||||||
.calledWith(@project_id, @publicAccessLevel)
|
.calledWith(@project_id, @publicAccessLevel)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
@ -126,7 +132,7 @@ describe "ProjectController", ->
|
||||||
@EditorController.setRootDoc = sinon.stub().callsArg(2)
|
@EditorController.setRootDoc = sinon.stub().callsArg(2)
|
||||||
@req.body =
|
@req.body =
|
||||||
rootDocId: @rootDocId = "root-doc-id"
|
rootDocId: @rootDocId = "root-doc-id"
|
||||||
@res.send = (code) =>
|
@res.sendStatus = (code) =>
|
||||||
@EditorController.setRootDoc
|
@EditorController.setRootDoc
|
||||||
.calledWith(@project_id, @rootDocId)
|
.calledWith(@project_id, @rootDocId)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
@ -136,7 +142,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
describe "deleteProject", ->
|
describe "deleteProject", ->
|
||||||
it "should tell the project deleter to archive when forever=false", (done)->
|
it "should tell the project deleter to archive when forever=false", (done)->
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@ProjectDeleter.archiveProject.calledWith(@project_id).should.equal true
|
@ProjectDeleter.archiveProject.calledWith(@project_id).should.equal true
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
done()
|
done()
|
||||||
|
@ -144,7 +150,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
it "should tell the project deleter to delete when forever=true", (done)->
|
it "should tell the project deleter to delete when forever=true", (done)->
|
||||||
@req.query = forever: "true"
|
@req.query = forever: "true"
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@ProjectDeleter.deleteProject.calledWith(@project_id).should.equal true
|
@ProjectDeleter.deleteProject.calledWith(@project_id).should.equal true
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
done()
|
done()
|
||||||
|
@ -152,7 +158,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
describe "restoreProject", ->
|
describe "restoreProject", ->
|
||||||
it "should tell the project deleter", (done)->
|
it "should tell the project deleter", (done)->
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@ProjectDeleter.restoreProject.calledWith(@project_id).should.equal true
|
@ProjectDeleter.restoreProject.calledWith(@project_id).should.equal true
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
done()
|
done()
|
||||||
|
@ -244,7 +250,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
it "should call the editor controller", (done)->
|
it "should call the editor controller", (done)->
|
||||||
@EditorController.renameProject.callsArgWith(2)
|
@EditorController.renameProject.callsArgWith(2)
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
||||||
done()
|
done()
|
||||||
|
@ -252,7 +258,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
it "should send a 500 if there is a problem", (done)->
|
it "should send a 500 if there is a problem", (done)->
|
||||||
@EditorController.renameProject.callsArgWith(2, "problem")
|
@EditorController.renameProject.callsArgWith(2, "problem")
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 500
|
code.should.equal 500
|
||||||
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
||||||
done()
|
done()
|
||||||
|
@ -260,7 +266,7 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
it "should return an error if the name is over 150 chars", (done)->
|
it "should return an error if the name is over 150 chars", (done)->
|
||||||
@req.body.newProjectName = "EDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOTEDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOT"
|
@req.body.newProjectName = "EDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOTEDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOT"
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 400
|
code.should.equal 400
|
||||||
done()
|
done()
|
||||||
@ProjectController.renameProject @req, @res
|
@ProjectController.renameProject @req, @res
|
||||||
|
@ -282,6 +288,9 @@ describe "ProjectController", ->
|
||||||
@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()
|
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||||
|
@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
|
||||||
|
@ProjectUpdateHandler.markAsOpened.callsArgWith(1)
|
||||||
|
|
||||||
|
|
||||||
it "should render the project/editor page", (done)->
|
it "should render the project/editor page", (done)->
|
||||||
@res.render = (pageName, opts)=>
|
@res.render = (pageName, opts)=>
|
||||||
|
@ -317,7 +326,20 @@ describe "ProjectController", ->
|
||||||
|
|
||||||
it "should not render the page if the project can not be accessed", (done)->
|
it "should not render the page if the project can not be accessed", (done)->
|
||||||
@SecurityManager.userCanAccessProject = sinon.stub().callsArgWith 2, false
|
@SecurityManager.userCanAccessProject = sinon.stub().callsArgWith 2, false
|
||||||
@res.send = (resCode, opts)=>
|
@res.sendStatus = (resCode, opts)=>
|
||||||
resCode.should.equal 401
|
resCode.should.equal 401
|
||||||
done()
|
done()
|
||||||
@ProjectController.loadEditor @req, @res
|
@ProjectController.loadEditor @req, @res
|
||||||
|
|
||||||
|
it "should reactivateProjectIfRequired", (done)->
|
||||||
|
@res.render = (pageName, opts)=>
|
||||||
|
@InactiveProjectManager.reactivateProjectIfRequired.calledWith(@project_id).should.equal true
|
||||||
|
done()
|
||||||
|
@ProjectController.loadEditor @req, @res
|
||||||
|
|
||||||
|
it "should mark project as opened", (done)->
|
||||||
|
@res.render = (pageName, opts)=>
|
||||||
|
@ProjectUpdateHandler.markAsOpened.calledWith(@project_id).should.equal true
|
||||||
|
done()
|
||||||
|
@ProjectController.loadEditor @req, @res
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,13 @@ describe 'project model', ->
|
||||||
doc._id.should.equal rootDoc._id
|
doc._id.should.equal rootDoc._id
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
it 'should return null when the project has no rootDoc', (done) ->
|
||||||
|
project.rootDoc_id = null
|
||||||
|
@locator.findRootDoc project, (err, doc)->
|
||||||
|
assert !err?
|
||||||
|
expect(doc).to.equal null
|
||||||
|
done()
|
||||||
|
|
||||||
describe 'findElementByPath', ->
|
describe 'findElementByPath', ->
|
||||||
|
|
||||||
it 'should take a doc path and return the element for a root level document', (done)->
|
it 'should take a doc path and return the element for a root level document', (done)->
|
||||||
|
|
|
@ -3,7 +3,7 @@ chai = require('chai').should()
|
||||||
modulePath = "../../../../app/js/Features/Project/ProjectUpdateHandler.js"
|
modulePath = "../../../../app/js/Features/Project/ProjectUpdateHandler.js"
|
||||||
SandboxedModule = require('sandboxed-module')
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
|
||||||
describe 'updating a project', ->
|
describe 'ProjectUpdateHandler', ->
|
||||||
|
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -22,3 +22,36 @@ describe 'updating a project', ->
|
||||||
now = Date.now()+""
|
now = Date.now()+""
|
||||||
date.substring(0,5).should.equal now.substring(0,5)
|
date.substring(0,5).should.equal now.substring(0,5)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe "markAsOpened", ->
|
||||||
|
|
||||||
|
it 'should send an update to mongo', (done)->
|
||||||
|
project_id = "project_id"
|
||||||
|
@handler.markAsOpened project_id, (err)=>
|
||||||
|
args = @ProjectModel.update.args[0]
|
||||||
|
args[0]._id.should.equal project_id
|
||||||
|
date = args[1].lastOpened+""
|
||||||
|
now = Date.now()+""
|
||||||
|
date.substring(0,5).should.equal now.substring(0,5)
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "markAsInactive", ->
|
||||||
|
|
||||||
|
it 'should send an update to mongo', (done)->
|
||||||
|
project_id = "project_id"
|
||||||
|
@handler.markAsInactive project_id, (err)=>
|
||||||
|
args = @ProjectModel.update.args[0]
|
||||||
|
args[0]._id.should.equal project_id
|
||||||
|
args[1].active.should.equal false
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "markAsActive", ->
|
||||||
|
it 'should send an update to mongo', (done)->
|
||||||
|
project_id = "project_id"
|
||||||
|
@handler.markAsActive project_id, (err)=>
|
||||||
|
args = @ProjectModel.update.args[0]
|
||||||
|
args[0]._id.should.equal project_id
|
||||||
|
args[1].active.should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -285,9 +285,9 @@ describe "SubscriptionController sanboxed", ->
|
||||||
describe "createSubscription", ->
|
describe "createSubscription", ->
|
||||||
beforeEach (done)->
|
beforeEach (done)->
|
||||||
@res =
|
@res =
|
||||||
send:->
|
sendStatus:->
|
||||||
done()
|
done()
|
||||||
sinon.spy @res, "send"
|
sinon.spy @res, "sendStatus"
|
||||||
@subscriptionDetails =
|
@subscriptionDetails =
|
||||||
card:"1234"
|
card:"1234"
|
||||||
cvv:"123"
|
cvv:"123"
|
||||||
|
@ -300,7 +300,7 @@ describe "SubscriptionController sanboxed", ->
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should redurect to the subscription page", (done)->
|
it "should redurect to the subscription page", (done)->
|
||||||
@res.send.calledWith(201).should.equal true
|
@res.sendStatus.calledWith(201).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,9 +363,9 @@ describe "SubscriptionController sanboxed", ->
|
||||||
expired_subscription_notification:
|
expired_subscription_notification:
|
||||||
subscription:
|
subscription:
|
||||||
uuid: @activeRecurlySubscription.uuid
|
uuid: @activeRecurlySubscription.uuid
|
||||||
@res = send:->
|
@res = sendStatus:->
|
||||||
done()
|
done()
|
||||||
sinon.spy @res, "send"
|
sinon.spy @res, "sendStatus"
|
||||||
@SubscriptionController.recurlyCallback @req, @res
|
@SubscriptionController.recurlyCallback @req, @res
|
||||||
|
|
||||||
it "should tell the SubscriptionHandler to process the recurly callback", (done)->
|
it "should tell the SubscriptionHandler to process the recurly callback", (done)->
|
||||||
|
@ -374,7 +374,7 @@ describe "SubscriptionController sanboxed", ->
|
||||||
|
|
||||||
|
|
||||||
it "should send a 200", (done)->
|
it "should send a 200", (done)->
|
||||||
@res.send.calledWith(200)
|
@res.sendStatus.calledWith(200)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "with a non-actionable request", ->
|
describe "with a non-actionable request", ->
|
||||||
|
@ -385,16 +385,16 @@ describe "SubscriptionController sanboxed", ->
|
||||||
new_subscription_notification:
|
new_subscription_notification:
|
||||||
subscription:
|
subscription:
|
||||||
uuid: @activeRecurlySubscription.uuid
|
uuid: @activeRecurlySubscription.uuid
|
||||||
@res = send:->
|
@res = sendStatus:->
|
||||||
done()
|
done()
|
||||||
sinon.spy @res, "send"
|
sinon.spy @res, "sendStatus"
|
||||||
@SubscriptionController.recurlyCallback @req, @res
|
@SubscriptionController.recurlyCallback @req, @res
|
||||||
|
|
||||||
it "should not call the subscriptionshandler", ->
|
it "should not call the subscriptionshandler", ->
|
||||||
@SubscriptionHandler.recurlyCallback.called.should.equal false
|
@SubscriptionHandler.recurlyCallback.called.should.equal false
|
||||||
|
|
||||||
it "should respond with a 200 status", ->
|
it "should respond with a 200 status", ->
|
||||||
@res.send.calledWith(200)
|
@res.sendStatus.calledWith(200)
|
||||||
|
|
||||||
|
|
||||||
describe "renderUpgradeToAnnualPlanPage", ->
|
describe "renderUpgradeToAnnualPlanPage", ->
|
||||||
|
@ -442,7 +442,7 @@ describe "SubscriptionController sanboxed", ->
|
||||||
@req.body =
|
@req.body =
|
||||||
planName:"student"
|
planName:"student"
|
||||||
|
|
||||||
@res.send = ()=>
|
@res.sendStatus = ()=>
|
||||||
@SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true
|
@SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ describe "SubscriptionController sanboxed", ->
|
||||||
@req.body =
|
@req.body =
|
||||||
planName:"collaborator"
|
planName:"collaborator"
|
||||||
|
|
||||||
@res.send = (url)=>
|
@res.sendStatus = (url)=>
|
||||||
@SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true
|
@SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ describe "SubscriptionGroupController", ->
|
||||||
|
|
||||||
it "should ask the SubscriptionGroupHandler to send the verification email", (done)->
|
it "should ask the SubscriptionGroupHandler to send the verification email", (done)->
|
||||||
res =
|
res =
|
||||||
send : (statusCode)=>
|
sendStatus : (statusCode)=>
|
||||||
statusCode.should.equal 200
|
statusCode.should.equal 200
|
||||||
@GroupHandler.sendVerificationEmail.calledWith(@subscription_id, @licenceName, @user_email).should.equal true
|
@GroupHandler.sendVerificationEmail.calledWith(@subscription_id, @licenceName, @user_email).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
|
@ -28,7 +28,7 @@ describe 'TpdsController', ->
|
||||||
headers:
|
headers:
|
||||||
"x-sl-update-source": @source = "dropbox"
|
"x-sl-update-source": @source = "dropbox"
|
||||||
@TpdsUpdateHandler.newUpdate = sinon.stub().callsArg(5)
|
@TpdsUpdateHandler.newUpdate = sinon.stub().callsArg(5)
|
||||||
res = send: =>
|
res = sendStatus: =>
|
||||||
@TpdsUpdateHandler.newUpdate.calledWith(@user_id, "projectName","/here.txt", req, @source).should.equal true
|
@TpdsUpdateHandler.newUpdate.calledWith(@user_id, "projectName","/here.txt", req, @source).should.equal true
|
||||||
done()
|
done()
|
||||||
@TpdsController.mergeUpdate req, res
|
@TpdsController.mergeUpdate req, res
|
||||||
|
@ -43,7 +43,7 @@ describe 'TpdsController', ->
|
||||||
headers:
|
headers:
|
||||||
"x-sl-update-source": @source = "dropbox"
|
"x-sl-update-source": @source = "dropbox"
|
||||||
@TpdsUpdateHandler.deleteUpdate = sinon.stub().callsArg(4)
|
@TpdsUpdateHandler.deleteUpdate = sinon.stub().callsArg(4)
|
||||||
res = send: =>
|
res = sendStatus: =>
|
||||||
@TpdsUpdateHandler.deleteUpdate.calledWith(@user_id, "projectName", "/here.txt", @source).should.equal true
|
@TpdsUpdateHandler.deleteUpdate.calledWith(@user_id, "projectName", "/here.txt", @source).should.equal true
|
||||||
done()
|
done()
|
||||||
@TpdsController.deleteUpdate req, res
|
@TpdsController.deleteUpdate req, res
|
||||||
|
@ -86,7 +86,7 @@ describe 'TpdsController', ->
|
||||||
headers:
|
headers:
|
||||||
"x-sl-update-source": @source = "github"
|
"x-sl-update-source": @source = "github"
|
||||||
@res =
|
@res =
|
||||||
send: sinon.stub()
|
sendStatus: sinon.stub()
|
||||||
|
|
||||||
@TpdsController.updateProjectContents @req, @res
|
@TpdsController.updateProjectContents @req, @res
|
||||||
|
|
||||||
|
@ -96,10 +96,8 @@ describe 'TpdsController', ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should return a success", ->
|
it "should return a success", ->
|
||||||
@res.send.calledWith(200).should.equal true
|
@res.sendStatus.calledWith(200).should.equal true
|
||||||
|
|
||||||
it "should clear the session", ->
|
|
||||||
@req.session.destroy.called.should.equal true
|
|
||||||
|
|
||||||
describe 'deleteProjectContents', ->
|
describe 'deleteProjectContents', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -113,7 +111,7 @@ describe 'TpdsController', ->
|
||||||
headers:
|
headers:
|
||||||
"x-sl-update-source": @source = "github"
|
"x-sl-update-source": @source = "github"
|
||||||
@res =
|
@res =
|
||||||
send: sinon.stub()
|
sendStatus: sinon.stub()
|
||||||
|
|
||||||
@TpdsController.deleteProjectContents @req, @res
|
@TpdsController.deleteProjectContents @req, @res
|
||||||
|
|
||||||
|
@ -123,8 +121,5 @@ describe 'TpdsController', ->
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should return a success", ->
|
it "should return a success", ->
|
||||||
@res.send.calledWith(200).should.equal true
|
@res.sendStatus.calledWith(200).should.equal true
|
||||||
|
|
||||||
it "should clear the session", ->
|
|
||||||
@req.session.destroy.called.should.equal true
|
|
||||||
|
|
||||||
|
|
|
@ -19,32 +19,55 @@ filestoreUrl = "filestore.sharelatex.com"
|
||||||
|
|
||||||
describe 'TpdsUpdateSender', ->
|
describe 'TpdsUpdateSender', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@requestQueuer = regist:(queue, meth, opts, callback)->
|
@requestQueuer = (queue, meth, opts, callback)->
|
||||||
project = {owner_ref:user_id,readOnly_refs:[read_only_ref_1], collaberator_refs:[collaberator_ref_1]}
|
project = {owner_ref:user_id,readOnly_refs:[read_only_ref_1], collaberator_refs:[collaberator_ref_1]}
|
||||||
@Project = findById:sinon.stub().callsArgWith(2, null, project)
|
@Project = findById:sinon.stub().callsArgWith(2, null, project)
|
||||||
@docstoreUrl = "docstore.sharelatex.env"
|
@docstoreUrl = "docstore.sharelatex.env"
|
||||||
|
@request = sinon.stub().returns(pipe:->)
|
||||||
|
@settings =
|
||||||
|
siteUrl:siteUrl
|
||||||
|
httpAuthSiteUrl:httpAuthSiteUrl,
|
||||||
|
apis:
|
||||||
|
thirdPartyDataStore: {url: thirdPartyDataStoreApiUrl}
|
||||||
|
filestore:
|
||||||
|
url: filestoreUrl
|
||||||
|
docstore:
|
||||||
|
pubUrl: @docstoreUrl
|
||||||
@updateSender = SandboxedModule.require modulePath, requires:
|
@updateSender = SandboxedModule.require modulePath, requires:
|
||||||
'fairy':{connect:=>{queue:=>@requestQueuer}}
|
"settings-sharelatex": @settings
|
||||||
"settings-sharelatex":
|
|
||||||
siteUrl:siteUrl
|
|
||||||
httpAuthSiteUrl:httpAuthSiteUrl,
|
|
||||||
apis:
|
|
||||||
thirdPartyDataStore: {url: thirdPartyDataStoreApiUrl}
|
|
||||||
filestore:
|
|
||||||
url: filestoreUrl
|
|
||||||
docstore:
|
|
||||||
pubUrl: @docstoreUrl
|
|
||||||
redis:fairy:{}
|
|
||||||
"logger-sharelatex":{log:->}
|
"logger-sharelatex":{log:->}
|
||||||
'../../models/Project': Project:@Project
|
'../../models/Project': Project:@Project
|
||||||
'request':->{pipe:->}
|
'request':@request
|
||||||
|
|
||||||
|
describe "_enqueue", ->
|
||||||
|
|
||||||
|
it "should not call request if there is no tpdsworker url", (done)->
|
||||||
|
@updateSender._enqueue null, null, null, (err)=>
|
||||||
|
@request.called.should.equal false
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should post the message to the tpdsworker", (done)->
|
||||||
|
@settings.apis.tpdsworker = url:"www.tpdsworker.env"
|
||||||
|
group = "myproject"
|
||||||
|
method = "somemethod"
|
||||||
|
job = "do something"
|
||||||
|
@request.callsArgWith(1)
|
||||||
|
@updateSender._enqueue group, method, job, (err)=>
|
||||||
|
args = @request.args[0][0]
|
||||||
|
args.json.group.should.equal group
|
||||||
|
args.json.job.should.equal job
|
||||||
|
args.json.method.should.equal method
|
||||||
|
args.uri.should.equal "www.tpdsworker.env/enqueue/web_to_tpds_http_requests"
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe 'sending updates', ->
|
describe 'sending updates', ->
|
||||||
|
|
||||||
it 'ques a post the file with user and file id', (done)->
|
it 'queues a post the file with user and file id', (done)->
|
||||||
file_id = '4545345'
|
file_id = '4545345'
|
||||||
path = '/some/path/here.jpg'
|
path = '/some/path/here.jpg'
|
||||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
@updateSender._enqueue = (uid, method, job, callback)->
|
||||||
uid.should.equal project_id
|
uid.should.equal project_id
|
||||||
job.method.should.equal "post"
|
job.method.should.equal "post"
|
||||||
job.streamOrigin.should.equal "#{filestoreUrl}/project/#{project_id}/file/#{file_id}"
|
job.streamOrigin.should.equal "#{filestoreUrl}/project/#{project_id}/file/#{file_id}"
|
||||||
|
@ -59,7 +82,7 @@ describe 'TpdsUpdateSender', ->
|
||||||
path = "/some/path/here.tex"
|
path = "/some/path/here.tex"
|
||||||
lines = ["line1", "line2", "line3"]
|
lines = ["line1", "line2", "line3"]
|
||||||
|
|
||||||
@requestQueuer.enqueue = (uid, method, job, callback)=>
|
@updateSender._enqueue = (uid, method, job, callback)=>
|
||||||
uid.should.equal project_id
|
uid.should.equal project_id
|
||||||
job.method.should.equal "post"
|
job.method.should.equal "post"
|
||||||
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
||||||
|
@ -71,7 +94,7 @@ describe 'TpdsUpdateSender', ->
|
||||||
|
|
||||||
it 'deleting entity', (done)->
|
it 'deleting entity', (done)->
|
||||||
path = "/path/here/t.tex"
|
path = "/path/here/t.tex"
|
||||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
@updateSender._enqueue = (uid, method, job, callback)->
|
||||||
uid.should.equal project_id
|
uid.should.equal project_id
|
||||||
job.method.should.equal "DELETE"
|
job.method.should.equal "DELETE"
|
||||||
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
||||||
|
@ -83,7 +106,7 @@ describe 'TpdsUpdateSender', ->
|
||||||
it 'moving entity', (done)->
|
it 'moving entity', (done)->
|
||||||
startPath = "staring/here/file.tex"
|
startPath = "staring/here/file.tex"
|
||||||
endPath = "ending/here/file.tex"
|
endPath = "ending/here/file.tex"
|
||||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
@updateSender._enqueue = (uid, method, job, callback)->
|
||||||
uid.should.equal project_id
|
uid.should.equal project_id
|
||||||
job.method.should.equal "put"
|
job.method.should.equal "put"
|
||||||
job.uri.should.equal "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity"
|
job.uri.should.equal "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity"
|
||||||
|
@ -96,7 +119,7 @@ describe 'TpdsUpdateSender', ->
|
||||||
it 'should be able to rename a project using the move entity func', (done)->
|
it 'should be able to rename a project using the move entity func', (done)->
|
||||||
oldProjectName = "/oldProjectName/"
|
oldProjectName = "/oldProjectName/"
|
||||||
newProjectName = "/newProjectName/"
|
newProjectName = "/newProjectName/"
|
||||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
@updateSender._enqueue = (uid, method, job, callback)->
|
||||||
uid.should.equal project_id
|
uid.should.equal project_id
|
||||||
job.method.should.equal "put"
|
job.method.should.equal "put"
|
||||||
job.uri.should.equal "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity"
|
job.uri.should.equal "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity"
|
||||||
|
@ -107,9 +130,9 @@ describe 'TpdsUpdateSender', ->
|
||||||
@updateSender.moveEntity {project_id:project_id, project_name:oldProjectName, newProjectName:newProjectName}
|
@updateSender.moveEntity {project_id:project_id, project_name:oldProjectName, newProjectName:newProjectName}
|
||||||
|
|
||||||
it "pollDropboxForUser", (done) ->
|
it "pollDropboxForUser", (done) ->
|
||||||
@requestQueuer.enqueue = sinon.stub().callsArg(3)
|
@updateSender._enqueue = sinon.stub().callsArg(3)
|
||||||
@updateSender.pollDropboxForUser user_id, (error) =>
|
@updateSender.pollDropboxForUser user_id, (error) =>
|
||||||
@requestQueuer.enqueue
|
@updateSender._enqueue
|
||||||
.calledWith(
|
.calledWith(
|
||||||
"poll-dropbox:#{user_id}",
|
"poll-dropbox:#{user_id}",
|
||||||
"standardHttpRequest",
|
"standardHttpRequest",
|
||||||
|
|
|
@ -29,7 +29,7 @@ describe "ProjectUploadController", ->
|
||||||
@req.files =
|
@req.files =
|
||||||
qqfile:
|
qqfile:
|
||||||
path: @path
|
path: @path
|
||||||
name: @name
|
originalname: @name
|
||||||
@req.session =
|
@req.session =
|
||||||
user:
|
user:
|
||||||
_id: @user_id
|
_id: @user_id
|
||||||
|
@ -102,7 +102,7 @@ describe "ProjectUploadController", ->
|
||||||
@req.files =
|
@req.files =
|
||||||
qqfile:
|
qqfile:
|
||||||
path: @path
|
path: @path
|
||||||
name: @name
|
originalname: @name
|
||||||
@req.params =
|
@req.params =
|
||||||
Project_id: @project_id
|
Project_id: @project_id
|
||||||
@req.query =
|
@req.query =
|
||||||
|
@ -173,7 +173,7 @@ describe "ProjectUploadController", ->
|
||||||
describe "with a bad request", ->
|
describe "with a bad request", ->
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@req.files.qqfile.name = ""
|
@req.files.qqfile.originalname = ""
|
||||||
@ProjectUploadController.uploadFile @req, @res
|
@ProjectUploadController.uploadFile @req, @res
|
||||||
|
|
||||||
it "should return a a non success response", ->
|
it "should return a a non success response", ->
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe "UserController", ->
|
||||||
|
|
||||||
it "should delete the user", (done)->
|
it "should delete the user", (done)->
|
||||||
|
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@UserDeleter.deleteUser.calledWith(@user_id)
|
@UserDeleter.deleteUser.calledWith(@user_id)
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
done()
|
done()
|
||||||
|
@ -98,7 +98,7 @@ describe "UserController", ->
|
||||||
|
|
||||||
it "should call save", (done)->
|
it "should call save", (done)->
|
||||||
@req.body = {}
|
@req.body = {}
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@user.save.called.should.equal true
|
@user.save.called.should.equal true
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
@ -106,7 +106,7 @@ describe "UserController", ->
|
||||||
it "should set the first name", (done)->
|
it "should set the first name", (done)->
|
||||||
@req.body =
|
@req.body =
|
||||||
first_name: "bobby "
|
first_name: "bobby "
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@user.first_name.should.equal "bobby"
|
@user.first_name.should.equal "bobby"
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
@ -114,7 +114,7 @@ describe "UserController", ->
|
||||||
it "should set the role", (done)->
|
it "should set the role", (done)->
|
||||||
@req.body =
|
@req.body =
|
||||||
role: "student"
|
role: "student"
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@user.role.should.equal "student"
|
@user.role.should.equal "student"
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
@ -122,7 +122,7 @@ describe "UserController", ->
|
||||||
it "should set the institution", (done)->
|
it "should set the institution", (done)->
|
||||||
@req.body =
|
@req.body =
|
||||||
institution: "MIT"
|
institution: "MIT"
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@user.institution.should.equal "MIT"
|
@user.institution.should.equal "MIT"
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
@ -130,21 +130,21 @@ describe "UserController", ->
|
||||||
it "should set some props on ace", (done)->
|
it "should set some props on ace", (done)->
|
||||||
@req.body =
|
@req.body =
|
||||||
theme: "something"
|
theme: "something"
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
@user.ace.theme.should.equal "something"
|
@user.ace.theme.should.equal "something"
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
|
||||||
it "should send an error if the email is 0 len", (done)->
|
it "should send an error if the email is 0 len", (done)->
|
||||||
@req.body.email = ""
|
@req.body.email = ""
|
||||||
@res.send = (code)->
|
@res.sendStatus = (code)->
|
||||||
code.should.equal 400
|
code.should.equal 400
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
|
||||||
it "should send an error if the email does not contain an @", (done)->
|
it "should send an error if the email does not contain an @", (done)->
|
||||||
@req.body.email = "bob at something dot com"
|
@req.body.email = "bob at something dot com"
|
||||||
@res.send = (code)->
|
@res.sendStatus = (code)->
|
||||||
code.should.equal 400
|
code.should.equal 400
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
@ -152,7 +152,7 @@ describe "UserController", ->
|
||||||
it "should call the user updater with the new email and user _id", (done)->
|
it "should call the user updater with the new email and user _id", (done)->
|
||||||
@req.body.email = @newEmail.toUpperCase()
|
@req.body.email = @newEmail.toUpperCase()
|
||||||
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
||||||
@res.send = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@UserUpdater.changeEmailAddress.calledWith(@user_id, @newEmail).should.equal true
|
@UserUpdater.changeEmailAddress.calledWith(@user_id, @newEmail).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
|
@ -22,6 +22,19 @@ class MockResponse
|
||||||
@redirectedTo = url
|
@redirectedTo = url
|
||||||
@callback() if @callback?
|
@callback() if @callback?
|
||||||
|
|
||||||
|
sendStatus: (status) ->
|
||||||
|
if arguments.length < 2
|
||||||
|
if typeof status != "number"
|
||||||
|
body = status
|
||||||
|
status = 200
|
||||||
|
@statusCode = status
|
||||||
|
@returned = true
|
||||||
|
if 200 <= status < 300
|
||||||
|
@success = true
|
||||||
|
else
|
||||||
|
@success = false
|
||||||
|
@callback() if @callback?
|
||||||
|
|
||||||
send: (status, body) ->
|
send: (status, body) ->
|
||||||
if arguments.length < 2
|
if arguments.length < 2
|
||||||
if typeof status != "number"
|
if typeof status != "number"
|
||||||
|
|
Loading…
Reference in a new issue