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 '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: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()
|
||||
|
||||
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.on "uncaughtException", (error) ->
|
||||
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')
|
||||
Url = require("url")
|
||||
Settings = require "settings-sharelatex"
|
||||
basicAuth = require('basic-auth-connect')
|
||||
|
||||
|
||||
module.exports = AuthenticationController =
|
||||
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"
|
||||
return res.redirect "/login"
|
||||
|
||||
httpAuth: require('express').basicAuth (user, pass)->
|
||||
httpAuth: basicAuth (user, pass)->
|
||||
isValid = Settings.httpAuthUsers[user] == pass
|
||||
if !isValid
|
||||
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
|
||||
# protect against session fixation attacks
|
||||
oldSession = req.session
|
||||
req.session.destroy()
|
||||
req.sessionStore.generate(req)
|
||||
for key, value of oldSession
|
||||
req.session[key] = value
|
||||
|
|
|
@ -22,9 +22,10 @@ module.exports = BlogController =
|
|||
|
||||
logger.log url:url, "proxying request to blog api"
|
||||
request.get blogUrl, (err, r, data)->
|
||||
return next(err) if err?
|
||||
if r?.statusCode == 404
|
||||
return ErrorController.notFound(req, res, next)
|
||||
if err?
|
||||
return res.send 500
|
||||
data = data.trim()
|
||||
try
|
||||
data = JSON.parse(data)
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports =
|
|||
ChatHandler.sendMessage project_id, user_id, messageContent, (err, builtMessge)->
|
||||
if err?
|
||||
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)->
|
||||
res.send()
|
||||
|
||||
|
@ -23,7 +23,7 @@ module.exports =
|
|||
ChatHandler.getMessages project_id, query, (err, messages)->
|
||||
if err?
|
||||
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"
|
||||
res.set 'Content-Type', 'application/json'
|
||||
res.send messages
|
||||
|
|
|
@ -4,7 +4,6 @@ EditorController = require "../Editor/EditorController"
|
|||
|
||||
module.exports = CollaboratorsController =
|
||||
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) ->
|
||||
return next(error) if error?
|
||||
ProjectGetter.populateProjectWithUsers project, (error, project) ->
|
||||
|
@ -19,7 +18,7 @@ module.exports = CollaboratorsController =
|
|||
return next(new Error("User should be logged in"))
|
||||
CollaboratorsHandler.removeUserFromProject req.params.project_id, user_id, (error) ->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.sendStatus 204
|
||||
|
||||
addUserToProject: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
|
@ -33,7 +32,7 @@ module.exports = CollaboratorsController =
|
|||
user_id = req.params.user_id
|
||||
EditorController.removeUserFromProject project_id, user_id, (error)->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.sendStatus 204
|
||||
|
||||
_formatCollaborators: (project, callback = (error, collaborators) ->) ->
|
||||
collaborators = []
|
||||
|
|
|
@ -3,9 +3,9 @@ SecurityManager = require('../../managers/SecurityManager')
|
|||
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
||||
app.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
||||
apiRouter.get '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
||||
|
||||
app.post '/project/:Project_id/users', SecurityManager.requestIsOwner, CollaboratorsController.addUserToProject
|
||||
app.delete '/project/:Project_id/users/:user_id', SecurityManager.requestIsOwner, CollaboratorsController.removeUserFromProject
|
||||
webRouter.post '/project/:Project_id/users', SecurityManager.requestIsOwner, CollaboratorsController.addUserToProject
|
||||
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
|
||||
CompileManager.deleteAuxFiles project_id, (error) ->
|
||||
return next(error) if error?
|
||||
res.send(200)
|
||||
res.sendStatus(200)
|
||||
|
||||
compileAndDownloadPdf: (req, res, next)->
|
||||
project_id = req.params.project_id
|
||||
CompileManager.compile project_id, null, {}, (err)->
|
||||
if err?
|
||||
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"
|
||||
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}")
|
||||
logger.error err: error, project_id: project_id, doc_id: doc_id, "error updating doc in docstore"
|
||||
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 {
|
||||
lines: lines
|
||||
}
|
||||
req.session.destroy()
|
||||
|
||||
setDocument: (req, res, next = (error) ->) ->
|
||||
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"
|
||||
return next(error)
|
||||
logger.log doc_id:doc_id, project_id:project_id, "finished receiving set document request from api (docupdater)"
|
||||
res.send 200
|
||||
req.session.destroy()
|
||||
res.sendStatus 200
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = EditorHttpController =
|
|||
name = req.body.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"
|
||||
ProjectEntityHandler.restoreDoc project_id, doc_id, name, (err, doc, folder_id) =>
|
||||
|
@ -68,7 +68,7 @@ module.exports = EditorHttpController =
|
|||
name = req.body.name
|
||||
parent_folder_id = req.body.parent_folder_id
|
||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||
return res.send 400
|
||||
return res.sendStatus 400
|
||||
EditorController.addDoc project_id, parent_folder_id, name, [], "editor", (error, doc) ->
|
||||
return next(error) if error?
|
||||
res.json doc
|
||||
|
@ -78,7 +78,7 @@ module.exports = EditorHttpController =
|
|||
name = req.body.name
|
||||
parent_folder_id = req.body.parent_folder_id
|
||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||
return res.send 400
|
||||
return res.sendStatus 400
|
||||
EditorController.addFolder project_id, parent_folder_id, name, "editor", (error, doc) ->
|
||||
return next(error) if error?
|
||||
res.json doc
|
||||
|
@ -89,10 +89,10 @@ module.exports = EditorHttpController =
|
|||
entity_type = req.params.entity_type
|
||||
name = req.body.name
|
||||
if !EditorHttpController._nameIsAcceptableLength(name)
|
||||
return res.send 400
|
||||
return res.sendStatus 400
|
||||
EditorController.renameEntity project_id, entity_id, entity_type, name, (error) ->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.sendStatus 204
|
||||
|
||||
moveEntity: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
|
@ -101,7 +101,7 @@ module.exports = EditorHttpController =
|
|||
folder_id = req.body.folder_id
|
||||
EditorController.moveEntity project_id, entity_id, folder_id, entity_type, (error) ->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.sendStatus 204
|
||||
|
||||
deleteDoc: (req, res, next)->
|
||||
req.params.entity_type = "doc"
|
||||
|
@ -121,6 +121,6 @@ module.exports = EditorHttpController =
|
|||
entity_type = req.params.entity_type
|
||||
EditorController.deleteEntity project_id, entity_id, entity_type, "editor", (error) ->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.sendStatus 204
|
||||
|
||||
|
||||
|
|
|
@ -3,21 +3,20 @@ SecurityManager = require('../../managers/SecurityManager')
|
|||
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
app.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
||||
app.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
||||
webRouter.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder
|
||||
|
||||
app.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/rename', SecurityManager.requestCanModifyProject, EditorHttpController.renameEntity
|
||||
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
|
||||
app.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/file/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteFile
|
||||
webRouter.delete '/project/:Project_id/doc/:entity_id', SecurityManager.requestCanModifyProject, EditorHttpController.deleteDoc
|
||||
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.
|
||||
# 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.
|
||||
app.post '/project/:Project_id/join', AuthenticationController.httpAuth, EditorHttpController.joinProject
|
||||
app.ignoreCsrf('post', '/project/:Project_id/join')
|
||||
apiRouter.post '/project/:Project_id/join', AuthenticationController.httpAuth, EditorHttpController.joinProject
|
||||
|
|
|
@ -1,21 +1,39 @@
|
|||
logger = require('logger-sharelatex')
|
||||
FileStoreHandler = require("./FileStoreHandler")
|
||||
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 =
|
||||
|
||||
getFile : (req, res)->
|
||||
project_id = req.params.Project_id
|
||||
file_id = req.params.File_id
|
||||
queryString = req.query
|
||||
user_agent = req.get('User-Agent')
|
||||
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)->
|
||||
if err?
|
||||
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)->
|
||||
if err?
|
||||
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}")
|
||||
stream.pipe res
|
||||
stream.pipe res
|
||||
|
|
|
@ -32,9 +32,9 @@ module.exports = HealthCheckController =
|
|||
|
||||
checkRedis: (req, res, next)->
|
||||
if redisCheck.isAlive()
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
else
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
|
||||
Reporter = (res) ->
|
||||
(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
|
||||
|
|
@ -6,12 +6,12 @@ module.exports =
|
|||
|
||||
renderRequestResetForm: (req, res)->
|
||||
logger.log "rendering request reset form"
|
||||
res.render "user/passwordReset",
|
||||
res.render "user/passwordReset",
|
||||
title:"reset_password"
|
||||
|
||||
requestReset: (req, res)->
|
||||
email = req.body.email.trim().toLowerCase()
|
||||
opts =
|
||||
opts =
|
||||
endpointName: "password_reset_rate_limit"
|
||||
timeInterval: 60
|
||||
subjectName: req.ip
|
||||
|
@ -23,22 +23,28 @@ module.exports =
|
|||
if err?
|
||||
res.send 500, {message:err?.message}
|
||||
else if exists
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
else
|
||||
res.send 404, {message: req.i18n.translate("cant_find_email")}
|
||||
|
||||
renderSetPasswordForm: (req, res)->
|
||||
res.render "user/setPassword",
|
||||
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",
|
||||
title:"set_password"
|
||||
passwordResetToken:req.query.passwordResetToken
|
||||
passwordResetToken: req.session.resetToken
|
||||
|
||||
setNewUserPassword: (req, res)->
|
||||
{passwordResetToken, password} = req.body
|
||||
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) ->
|
||||
return next(err) if err?
|
||||
if found
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
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')
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
apply: (webRouter, apiRouter) ->
|
||||
|
||||
app.get '/user/password/reset', PasswordResetController.renderRequestResetForm
|
||||
app.post '/user/password/reset', PasswordResetController.requestReset
|
||||
webRouter.get '/user/password/reset', PasswordResetController.renderRequestResetForm
|
||||
webRouter.post '/user/password/reset', PasswordResetController.requestReset
|
||||
AuthenticationController.addEndpointToLoginWhitelist '/user/password/reset'
|
||||
|
||||
app.get '/user/password/set', PasswordResetController.renderSetPasswordForm
|
||||
app.post '/user/password/set', PasswordResetController.setNewUserPassword
|
||||
webRouter.get '/user/password/set', PasswordResetController.renderSetPasswordForm
|
||||
webRouter.post '/user/password/set', PasswordResetController.setNewUserPassword
|
||||
AuthenticationController.addEndpointToLoginWhitelist '/user/password/set'
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ module.exports =
|
|||
ProjectDetailsHandler.getDetails project_id, (err, projDetails)->
|
||||
if err?
|
||||
logger.log err:err, project_id:project_id, "something went wrong getting project details"
|
||||
return res.send 500
|
||||
req.session.destroy()
|
||||
return res.sendStatus 500
|
||||
res.json(projDetails)
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ _ = require("underscore")
|
|||
Settings = require("settings-sharelatex")
|
||||
SecurityManager = require("../../managers/SecurityManager")
|
||||
fs = require "fs"
|
||||
InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
|
||||
ProjectUpdateHandler = require("./ProjectUpdateHandler")
|
||||
|
||||
module.exports = ProjectController =
|
||||
|
||||
|
@ -44,7 +46,7 @@ module.exports = ProjectController =
|
|||
|
||||
async.series jobs, (error) ->
|
||||
return next(error) if error?
|
||||
res.send(204)
|
||||
res.sendStatus(204)
|
||||
|
||||
deleteProject: (req, res) ->
|
||||
project_id = req.params.Project_id
|
||||
|
@ -58,18 +60,18 @@ module.exports = ProjectController =
|
|||
|
||||
doDelete project_id, (err)->
|
||||
if err?
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
restoreProject: (req, res) ->
|
||||
project_id = req.params.Project_id
|
||||
logger.log project_id:project_id, "received request to restore project"
|
||||
projectDeleter.restoreProject project_id, (err)->
|
||||
if err?
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
cloneProject: (req, res, next)->
|
||||
metrics.inc "cloned-project"
|
||||
|
@ -99,7 +101,7 @@ module.exports = ProjectController =
|
|||
], (err, project)->
|
||||
if err?
|
||||
logger.error err: err, project: project, user: user, name: projectName, templateType: template, "error creating project"
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
logger.log project: project, user: user, name: projectName, templateType: template, "created project"
|
||||
res.send {project_id:project._id}
|
||||
|
@ -109,13 +111,13 @@ module.exports = ProjectController =
|
|||
project_id = req.params.Project_id
|
||||
newName = req.body.newProjectName
|
||||
if newName.length > 150
|
||||
return res.send 400
|
||||
return res.sendStatus 400
|
||||
editorController.renameProject project_id, newName, (err)->
|
||||
if err?
|
||||
logger.err err:err, project_id:project_id, newName:newName, "problem renaming project"
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
projectListPage: (req, res, next)->
|
||||
timer = new metrics.Timer("project-list")
|
||||
|
@ -173,6 +175,7 @@ module.exports = ProjectController =
|
|||
user_id = 'openUser'
|
||||
|
||||
project_id = req.params.Project_id
|
||||
logger.log project_id:project_id, "loading editor"
|
||||
|
||||
async.parallel {
|
||||
project: (cb)->
|
||||
|
@ -181,11 +184,19 @@ module.exports = ProjectController =
|
|||
if user_id == 'openUser'
|
||||
cb null, defaultSettingsForAnonymousUser(user_id)
|
||||
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)->
|
||||
if user_id == 'openUser'
|
||||
return 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)->
|
||||
if err?
|
||||
logger.err err:err, "error getting details for project page"
|
||||
|
@ -194,13 +205,16 @@ module.exports = ProjectController =
|
|||
user = results.user
|
||||
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)->
|
||||
if !canAccess
|
||||
return res.send 401
|
||||
return res.sendStatus 401
|
||||
|
||||
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
||||
allowedFreeTrial = !!subscription.freeTrial.allowed || true
|
||||
|
||||
logger.log project_id:project_id, "rendering editor page"
|
||||
res.render 'project/editor',
|
||||
title: project.name
|
||||
priority_title: true
|
||||
|
|
|
@ -19,7 +19,6 @@ module.exports =
|
|||
project = new Project
|
||||
owner_ref : new ObjectId(owner_id)
|
||||
name : projectName
|
||||
useClsi2 : true
|
||||
project.rootFolder[0] = rootFolder
|
||||
User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
|
||||
project.spellCheckLanguage = user.ace.spellCheckLanguage
|
||||
|
|
|
@ -54,7 +54,10 @@ module.exports =
|
|||
|
||||
findRootDoc : (opts, callback)->
|
||||
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
|
||||
if project?
|
||||
getRootDoc project
|
||||
|
|
|
@ -15,8 +15,12 @@ module.exports = ProjectRootDocManager =
|
|||
return (cb)->
|
||||
rootDocId = null
|
||||
for line in doc.lines || []
|
||||
match = line.match /(.*)\\documentclass/ # no lookbehind in js regexp :(
|
||||
isRootDoc = Path.extname(path).match(/\.R?tex$/) and match and !match[1].match /%/
|
||||
# We've had problems with this regex locking up CPU.
|
||||
# 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
|
||||
rootDocId = doc?._id
|
||||
cb(rootDocId)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
Project = require('../../models/Project').Project
|
||||
logger = require('logger-sharelatex')
|
||||
Project = require("../../models/Project").Project
|
||||
|
||||
module.exports =
|
||||
markAsUpdated : (project_id, callback)->
|
||||
|
@ -8,3 +9,24 @@ module.exports =
|
|||
Project.update conditions, update, {}, (err)->
|
||||
if 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 =
|
||||
apply: (app) ->
|
||||
app.all /\/socket\.io\/.*/, (req, res, next) ->
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.all /\/socket\.io\/.*/, (req, res, next) ->
|
||||
proxy.web req, res, next
|
||||
|
||||
setTimeout () ->
|
||||
|
|
|
@ -50,12 +50,12 @@ module.exports = AdminController =
|
|||
dissconectAllUsers: (req, res)=>
|
||||
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."
|
||||
res.send(200)
|
||||
res.sendStatus(200)
|
||||
|
||||
closeEditor : (req, res)->
|
||||
logger.warn "closing editor"
|
||||
Settings.editorIsOpen = req.body.isOpen
|
||||
res.send(200)
|
||||
res.sendStatus(200)
|
||||
|
||||
writeAllToMongo : (req, res)->
|
||||
logger.log "writing all docs to mongo"
|
||||
|
@ -74,19 +74,19 @@ module.exports = AdminController =
|
|||
|
||||
flushProjectToTpds: (req, res)->
|
||||
projectEntityHandler.flushProjectToThirdPartyDataStore req.body.project_id, (err)->
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
pollDropboxForUser: (req, res)->
|
||||
user_id = req.body.user_id
|
||||
TpdsUpdateSender.pollDropboxForUser user_id, () ->
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
createMessage: (req, res, next) ->
|
||||
SystemMessageManager.createMessage req.body.content, (error) ->
|
||||
return next(error) if error?
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
clearMessages: (req, res, next) ->
|
||||
SystemMessageManager.clearMessages (error) ->
|
||||
return next(error) if error?
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
|
|
@ -11,4 +11,4 @@ module.exports = SpellingController =
|
|||
getReq.pipe(res)
|
||||
getReq.on "error", (error) ->
|
||||
logger.error err: error, "Spelling API error"
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
|
|
|
@ -3,18 +3,18 @@ UniversityController = require("./UniversityController")
|
|||
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
app.get '/', HomeController.index
|
||||
app.get '/home', HomeController.home
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.get '/', HomeController.index
|
||||
webRouter.get '/home', HomeController.home
|
||||
|
||||
app.get '/tos', HomeController.externalPage("tos", "Terms of Service")
|
||||
app.get '/about', HomeController.externalPage("about", "About Us")
|
||||
app.get '/security', HomeController.externalPage("security", "Security")
|
||||
app.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
|
||||
app.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance")
|
||||
app.get '/style', HomeController.externalPage("style_guide", "Style Guide")
|
||||
webRouter.get '/tos', HomeController.externalPage("tos", "Terms of Service")
|
||||
webRouter.get '/about', HomeController.externalPage("about", "About Us")
|
||||
webRouter.get '/security', HomeController.externalPage("security", "Security")
|
||||
webRouter.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
|
||||
webRouter.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance")
|
||||
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
|
||||
app.get '/university/*', UniversityController.getPage
|
||||
webRouter.get '/university', UniversityController.getIndexPage
|
||||
webRouter.get '/university/*', UniversityController.getPage
|
|
@ -18,6 +18,8 @@ module.exports = UniversityController =
|
|||
request.get universityUrl, (err, r, data)->
|
||||
if r?.statusCode == 404
|
||||
return ErrorController.notFound(req, res, next)
|
||||
if err?
|
||||
return res.send 500
|
||||
data = data.trim()
|
||||
try
|
||||
data = JSON.parse(data)
|
||||
|
|
|
@ -147,8 +147,8 @@ module.exports = SubscriptionController =
|
|||
SubscriptionHandler.createSubscription user, subscriptionDetails, recurly_token_id, (err)->
|
||||
if err?
|
||||
logger.err err:err, user_id:user._id, "something went wrong creating subscription"
|
||||
return res.send 500
|
||||
res.send 201
|
||||
return res.sendStatus 500
|
||||
res.sendStatus 201
|
||||
|
||||
successful_subscription: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) =>
|
||||
|
@ -191,9 +191,9 @@ module.exports = SubscriptionController =
|
|||
if req.body? and req.body["expired_subscription_notification"]?
|
||||
recurlySubscription = req.body["expired_subscription_notification"].subscription
|
||||
SubscriptionHandler.recurlyCallback recurlySubscription, ->
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
renderUpgradeToAnnualPlanPage: (req, res)->
|
||||
SecurityManager.getCurrentUser req, (error, user) ->
|
||||
|
@ -221,9 +221,9 @@ module.exports = SubscriptionController =
|
|||
SubscriptionHandler.updateSubscription user, annualPlanName, coupon_code, (err)->
|
||||
if err?
|
||||
logger.err err:err, user_id:user._id, "error updating subscription"
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
|
||||
recurlyNotificationParser: (req, res, next) ->
|
||||
|
|
|
@ -62,9 +62,9 @@ module.exports =
|
|||
return ErrorsController.notFound(req, res)
|
||||
SubscriptionGroupHandler.sendVerificationEmail subscription_id, licence.name, req.session.user.email, (err)->
|
||||
if err?
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.send 200
|
||||
res.sendStatus 200
|
||||
|
||||
completeJoin: (req, res)->
|
||||
subscription_id = req.params.subscription_id
|
||||
|
@ -74,7 +74,7 @@ module.exports =
|
|||
if err? and err == "token_not_found"
|
||||
res.redirect "/user/subscription/#{subscription_id}/group/invited?expired=true"
|
||||
else if err?
|
||||
res.send 500
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.redirect "/user/subscription/#{subscription_id}/group/successful-join"
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ module.exports =
|
|||
else if user_or_id?
|
||||
user_id = user_or_id
|
||||
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) ->
|
||||
logger.log user_id: user_id, "getting users group subscriptions"
|
||||
|
|
|
@ -4,44 +4,43 @@ SubscriptionGroupController = require './SubscriptionGroupController'
|
|||
Settings = require "settings-sharelatex"
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
apply: (webRouter, apiRouter) ->
|
||||
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
|
||||
app.get '/user/subscription/billing-details/edit', AuthenticationController.requireLogin(), SubscriptionController.editBillingDetailsPage
|
||||
webRouter.get '/user/subscription/new', AuthenticationController.requireLogin(), SubscriptionController.paymentPage
|
||||
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
|
||||
app.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
||||
app.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
||||
app.del '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
||||
webRouter.get '/subscription/group', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSubscriptionGroupAdminPage
|
||||
webRouter.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
||||
webRouter.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
||||
webRouter.delete '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
||||
|
||||
app.get '/user/subscription/:subscription_id/group/invited', AuthenticationController.requireLogin(), SubscriptionGroupController.renderGroupInvitePage
|
||||
app.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
|
||||
app.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage
|
||||
webRouter.get '/user/subscription/:subscription_id/group/invited', AuthenticationController.requireLogin(), SubscriptionGroupController.renderGroupInvitePage
|
||||
webRouter.post '/user/subscription/:subscription_id/group/begin-join', AuthenticationController.requireLogin(), SubscriptionGroupController.beginJoinGroup
|
||||
webRouter.get '/user/subscription/:subscription_id/group/complete-join', AuthenticationController.requireLogin(), SubscriptionGroupController.completeJoin
|
||||
webRouter.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage
|
||||
|
||||
#recurly callback
|
||||
app.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
||||
app.ignoreCsrf("post", '/user/subscription/callback')
|
||||
apiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
||||
|
||||
#user changes their account state
|
||||
app.post '/user/subscription/create', AuthenticationController.requireLogin(), SubscriptionController.createSubscription
|
||||
app.post '/user/subscription/update', AuthenticationController.requireLogin(), SubscriptionController.updateSubscription
|
||||
app.post '/user/subscription/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
|
||||
app.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
|
||||
webRouter.post '/user/subscription/create', AuthenticationController.requireLogin(), SubscriptionController.createSubscription
|
||||
webRouter.post '/user/subscription/update', AuthenticationController.requireLogin(), SubscriptionController.updateSubscription
|
||||
webRouter.post '/user/subscription/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
|
||||
webRouter.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
|
||||
|
||||
|
||||
app.get "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage
|
||||
app.post "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.processUpgradeToAnnualPlan
|
||||
webRouter.get "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage
|
||||
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"
|
||||
if err?
|
||||
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
||||
res.send(500)
|
||||
res.sendStatus(500)
|
||||
else
|
||||
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds update has been processed"
|
||||
res.send 200
|
||||
req.session.destroy()
|
||||
res.sendStatus 200
|
||||
|
||||
|
||||
deleteUpdate: (req, res)->
|
||||
|
@ -32,11 +31,10 @@ module.exports =
|
|||
tpdsUpdateHandler.deleteUpdate user_id, projectName, filePath, source, (err)->
|
||||
if err?
|
||||
logger.err err:err, user_id:user_id, filePath:filePath, "error reciving update from tpds"
|
||||
res.send(500)
|
||||
res.sendStatus(500)
|
||||
else
|
||||
logger.log user_id:user_id, filePath:filePath, projectName:projectName, "telling tpds delete has been processed"
|
||||
res.send 200
|
||||
req.session.destroy()
|
||||
res.sendStatus 200
|
||||
|
||||
# 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
|
||||
|
@ -49,8 +47,7 @@ module.exports =
|
|||
logger.log project_id: project_id, path: path, source: source, "received project contents update"
|
||||
UpdateMerger.mergeUpdate project_id, path, req, source, (error) ->
|
||||
return next(error) if error?
|
||||
res.send(200)
|
||||
req.session.destroy()
|
||||
res.sendStatus(200)
|
||||
|
||||
deleteProjectContents: (req, res, next = (error) ->) ->
|
||||
{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"
|
||||
UpdateMerger.deleteUpdate project_id, path, source, (error) ->
|
||||
return next(error) if error?
|
||||
res.send(200)
|
||||
req.session.destroy()
|
||||
res.sendStatus(200)
|
||||
|
||||
parseParams: parseParams = (req)->
|
||||
path = req.params[0]
|
||||
|
|
|
@ -4,6 +4,7 @@ path = require('path')
|
|||
Project = require('../../models/Project').Project
|
||||
keys = require('../../infrastructure/Keys')
|
||||
metrics = require("../../infrastructure/Metrics")
|
||||
request = require("request")
|
||||
|
||||
buildPath = (user_id, 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)
|
||||
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)->)->
|
||||
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)}"
|
||||
title: "addFile"
|
||||
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"
|
||||
callback()
|
||||
callback(err)
|
||||
|
||||
addFile : (options, callback = (err)->)->
|
||||
metrics.inc("tpds.add-file")
|
||||
|
@ -64,7 +89,7 @@ module.exports =
|
|||
user_id : user_id
|
||||
endPath: endPath
|
||||
startPath: startPath
|
||||
queue.enqueue options.project_id, "standardHttpRequest", moveOptions, callback
|
||||
TpdsUpdateSender._enqueue options.project_id, "standardHttpRequest", moveOptions, callback
|
||||
|
||||
deleteEntity : (options, callback = (err)->)->
|
||||
metrics.inc("tpds.delete-entity")
|
||||
|
@ -78,7 +103,7 @@ module.exports =
|
|||
uri : "#{settings.apis.thirdPartyDataStore.url}#{buildPath(user_id, options.project_name, options.path)}"
|
||||
title:"deleteEntity"
|
||||
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) ->) ->
|
||||
metrics.inc("tpds.poll-dropbox")
|
||||
|
@ -88,7 +113,7 @@ module.exports =
|
|||
uri:"#{settings.apis.thirdPartyDataStore.url}/user/poll"
|
||||
json:
|
||||
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)->)->
|
||||
Project.findById project_id, "_id owner_ref readOnly_refs collaberator_refs", (err, project)->
|
||||
|
|
|
@ -9,8 +9,8 @@ module.exports = ProjectUploadController =
|
|||
uploadProject: (req, res, next) ->
|
||||
timer = new metrics.Timer("project-upload")
|
||||
user_id = req.session.user._id
|
||||
{name, path} = req.files.qqfile
|
||||
name = Path.basename(name, ".zip")
|
||||
{originalname, path} = req.files.qqfile
|
||||
name = Path.basename(originalname, ".zip")
|
||||
ProjectUploadManager.createProjectFromZipArchive user_id, name, path, (error, project) ->
|
||||
fs.unlink path, ->
|
||||
timer.done()
|
||||
|
@ -27,7 +27,8 @@ module.exports = ProjectUploadController =
|
|||
|
||||
uploadFile: (req, res, next) ->
|
||||
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
|
||||
folder_id = req.query.folder_id
|
||||
if !name? or name.length == 0 or name.length > 150
|
||||
|
|
|
@ -3,11 +3,11 @@ AuthenticationController = require('../Authentication/AuthenticationController')
|
|||
ProjectUploadController = require "./ProjectUploadController"
|
||||
|
||||
module.exports =
|
||||
apply: (app) ->
|
||||
app.post '/project/new/upload',
|
||||
apply: (webRouter, apiRouter) ->
|
||||
webRouter.post '/project/new/upload',
|
||||
AuthenticationController.requireLogin(),
|
||||
ProjectUploadController.uploadProject
|
||||
app.post '/Project/:Project_id/upload',
|
||||
webRouter.post '/Project/:Project_id/upload',
|
||||
SecurityManager.requestCanModifyProject,
|
||||
ProjectUploadController.uploadFile
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ module.exports =
|
|||
user_id = req.session.user._id
|
||||
UserDeleter.deleteUser user_id, (err)->
|
||||
if !err?
|
||||
req.session.destroy()
|
||||
res.send(200)
|
||||
req.session?.destroy()
|
||||
res.sendStatus(200)
|
||||
|
||||
unsubscribe: (req, res)->
|
||||
UserLocator.findById req.session.user._id, (err, user)->
|
||||
|
@ -34,7 +34,7 @@ module.exports =
|
|||
User.findById user_id, (err, user)->
|
||||
if err? or !user?
|
||||
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?
|
||||
user.first_name = req.body.first_name.trim()
|
||||
|
@ -59,9 +59,9 @@ module.exports =
|
|||
user.save (err)->
|
||||
newEmail = req.body.email?.trim().toLowerCase()
|
||||
if !newEmail? or newEmail == user.email
|
||||
return res.send 200
|
||||
return res.sendStatus 200
|
||||
else if newEmail.indexOf("@") == -1
|
||||
return res.send(400)
|
||||
return res.sendStatus(400)
|
||||
else
|
||||
UserUpdater.changeEmailAddress user_id, newEmail, (err)->
|
||||
if err?
|
||||
|
@ -71,7 +71,7 @@ module.exports =
|
|||
else
|
||||
message = req.i18n.translate("problem_changing_email_address")
|
||||
return res.send 500, {message:message}
|
||||
res.send(200)
|
||||
res.sendStatus(200)
|
||||
|
||||
logout : (req, res)->
|
||||
metrics.inc "user.logout"
|
||||
|
@ -84,7 +84,7 @@ module.exports =
|
|||
register : (req, res, next = (error) ->)->
|
||||
email = req.body.email
|
||||
if !email? or email == ""
|
||||
res.send 422 # Unprocessable Entity
|
||||
res.sendStatus 422 # Unprocessable Entity
|
||||
return
|
||||
logger.log {email}, "registering new user"
|
||||
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
|
||||
# we use the auth token, actually destroying session from the chat api request
|
||||
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"
|
||||
return next(new Error("User is not logged in")) if !req.user?
|
||||
UserGetter.getUser req.user._id, {
|
||||
|
@ -26,7 +26,6 @@ module.exports = UserController =
|
|||
return next(error) if error?
|
||||
return res.send(404) if !user?
|
||||
UserController.sendFormattedPersonalInfo(user, res, next)
|
||||
req.session.destroy()
|
||||
|
||||
sendFormattedPersonalInfo: (user, res, next = (error) ->) ->
|
||||
UserController._formatPersonalInfo user, (error, info) ->
|
||||
|
|
|
@ -39,38 +39,38 @@ for path in [
|
|||
logger.log filePath:filePath, "file does not exist for fingerprints"
|
||||
|
||||
|
||||
module.exports = (app)->
|
||||
app.use (req, res, next)->
|
||||
module.exports = (app, webRouter, apiRouter)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.session = req.session
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.jsPath = jsPath
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.settings = Settings
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.translate = (key, vars = {}) ->
|
||||
vars.appName = Settings.appName
|
||||
req.i18n.translate(key, vars)
|
||||
res.locals.currentUrl = req.originalUrl
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.getSiteHost = ->
|
||||
Settings.siteUrl.substring(Settings.siteUrl.indexOf("//")+2)
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.formatProjectPublicAccessLevel = (privilegeLevel)->
|
||||
formatedPrivileges = private:"Private", readOnly:"Public: Read Only", readAndWrite:"Public: Read and Write"
|
||||
return formatedPrivileges[privilegeLevel] || "Private"
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.buildReferalUrl = (referal_medium) ->
|
||||
url = Settings.siteUrl
|
||||
if req.session? and req.session.user? and req.session.user.referal_id?
|
||||
|
@ -94,16 +94,16 @@ module.exports = (app)->
|
|||
return ""
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
res.locals.csrfToken = req.session._csrf
|
||||
webRouter.use (req, res, next) ->
|
||||
res.locals.csrfToken = req?.csrfToken()
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
webRouter.use (req, res, next) ->
|
||||
res.locals.getReqQueryParam = (field)->
|
||||
return req.query?[field]
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.fingerprint = (path) ->
|
||||
if fingerprints[path]?
|
||||
return fingerprints[path]
|
||||
|
@ -112,16 +112,16 @@ module.exports = (app)->
|
|||
return ""
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.externalAuthenticationSystemUsed = ->
|
||||
Settings.ldap?
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
if req.session.user?
|
||||
res.locals.user =
|
||||
email: req.session.user.email
|
||||
|
@ -139,34 +139,34 @@ module.exports = (app)->
|
|||
res.locals.sentryPublicDSN = Settings.sentry?.publicDSN
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
webRouter.use (req, res, next) ->
|
||||
if req.query? and req.query.scribtex_path?
|
||||
res.locals.lookingForScribtex = true
|
||||
res.locals.scribtexPath = req.query.scribtex_path
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
webRouter.use (req, res, next) ->
|
||||
res.locals.nav = Settings.nav
|
||||
res.locals.templates = Settings.templateLinks
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
webRouter.use (req, res, next) ->
|
||||
SystemMessageManager.getMessages (error, messages = []) ->
|
||||
res.locals.systemMessages = messages
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.query = req.query
|
||||
next()
|
||||
|
||||
app.use (req, res, next)->
|
||||
webRouter.use (req, res, next)->
|
||||
subdomain = _.find Settings.i18n.subdomainLang, (subdomain)->
|
||||
subdomain.lngCode == req.showUserOtherLng and !subdomain.hide
|
||||
res.locals.recomendSubdomain = subdomain
|
||||
res.locals.currentLngCode = req.lng
|
||||
next()
|
||||
|
||||
app.use (req, res, next) ->
|
||||
webRouter.use (req, res, next) ->
|
||||
if Settings.reloadModuleViewsOnEachRequest
|
||||
Modules.loadViewIncludes()
|
||||
res.locals.moduleIncludes = Modules.moduleIncludes
|
||||
|
|
|
@ -13,9 +13,9 @@ module.exports = Modules =
|
|||
loadedModule.name = moduleName
|
||||
@modules.push loadedModule
|
||||
|
||||
applyRouter: (app) ->
|
||||
applyRouter: (webRouter, apiRouter) ->
|
||||
for module in @modules
|
||||
module.router?.apply(app)
|
||||
module.router?.apply(webRouter, apiRouter)
|
||||
|
||||
viewIncludes: {}
|
||||
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')
|
||||
Router = require('../router')
|
||||
metrics.inc("startup")
|
||||
|
||||
redis = require("redis-sharelatex")
|
||||
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)
|
||||
|
||||
cookieParser = express.cookieParser(Settings.security.sessionSecret)
|
||||
Mongoose = require("./Mongoose")
|
||||
|
||||
oneDayInMilliseconds = 86400000
|
||||
ReferalConnect = require('../Features/Referal/ReferalConnect')
|
||||
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/mongoose/node_modules/mongodb"), logger)
|
||||
|
||||
metrics.event_loop?.monitor(logger)
|
||||
|
||||
Settings.editorIsOpen ||= true
|
||||
|
||||
if Settings.cacheStaticAssets
|
||||
|
@ -34,56 +44,59 @@ else
|
|||
|
||||
app = express()
|
||||
|
||||
csrf = express.csrf()
|
||||
ignoreCsrfRoutes = []
|
||||
app.ignoreCsrf = (method, route) ->
|
||||
ignoreCsrfRoutes.push new express.Route(method, route)
|
||||
webRouter = express.Router()
|
||||
apiRouter = express.Router()
|
||||
|
||||
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 (req, res, next) ->
|
||||
req.session.touch()
|
||||
next()
|
||||
|
||||
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 bodyParser.urlencoded({ extended: true, limit: "2mb"})
|
||||
app.use bodyParser.json({limit: "2mb"})
|
||||
app.use multer(dest: Settings.path.uploadFolder)
|
||||
app.use methodOverride()
|
||||
|
||||
app.use metrics.http.monitor(logger)
|
||||
app.use RedirectManager
|
||||
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)->
|
||||
metrics.inc "http-request"
|
||||
crawlerLogger.log(req)
|
||||
|
@ -96,12 +109,11 @@ app.use (req, res, next) ->
|
|||
else
|
||||
next()
|
||||
|
||||
app.get "/status", (req, res)->
|
||||
apiRouter.get "/status", (req, res)->
|
||||
res.send("web sharelatex is alive")
|
||||
req.session.destroy()
|
||||
|
||||
profiler = require "v8-profiler"
|
||||
app.get "/profile", (req, res) ->
|
||||
apiRouter.get "/profile", (req, res) ->
|
||||
time = parseInt(req.query.time || "1000")
|
||||
profiler.startProfiling("test")
|
||||
setTimeout () ->
|
||||
|
@ -112,7 +124,12 @@ app.get "/profile", (req, res) ->
|
|||
logger.info ("creating HTTP server").yellow
|
||||
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 =
|
||||
app: app
|
||||
|
|
|
@ -17,6 +17,8 @@ DeletedDocSchema = new Schema
|
|||
ProjectSchema = new Schema
|
||||
name : {type:String, default:'new project'}
|
||||
lastUpdated : {type:Date, default: () -> new Date()}
|
||||
lastOpened : {type:Date}
|
||||
active : { type: Boolean, default: true }
|
||||
owner_ref : {type:ObjectId, ref:'User'}
|
||||
collaberator_refs : [ type:ObjectId, ref:'User' ]
|
||||
readOnly_refs : [ type:ObjectId, ref:'User' ]
|
||||
|
@ -26,7 +28,6 @@ ProjectSchema = new Schema
|
|||
compiler : {type:String, default:'pdflatex'}
|
||||
spellCheckLanguage : {type:String, default:'en'}
|
||||
deletedByExternalDataSource : {type: Boolean, default: false}
|
||||
useClsi2 : {type:Boolean, default: true}
|
||||
description : {type:String, default:''}
|
||||
archived : { type: Boolean }
|
||||
deletedDocs : [DeletedDocSchema]
|
||||
|
@ -42,6 +43,7 @@ ProjectSchema.statics.getProject = (project_or_id, fields, callback)->
|
|||
this.findById project_or_id, fields, callback
|
||||
|
||||
ProjectSchema.statics.findPopulatedById = (project_id, callback)->
|
||||
logger.log project_id:project_id, "findPopulatedById"
|
||||
this.find(_id: project_id )
|
||||
.populate('collaberator_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"
|
||||
callback "not found"
|
||||
else
|
||||
logger.log project_id:project_id, "finished findPopulatedById"
|
||||
callback(null, projects[0])
|
||||
|
||||
ProjectSchema.statics.findAllUsersProjects = (user_id, requiredFields, callback)->
|
||||
|
|
|
@ -35,71 +35,72 @@ WikiController = require("./Features/Wiki/WikiController")
|
|||
Modules = require "./infrastructure/Modules"
|
||||
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
||||
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
||||
InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
|
||||
|
||||
logger = require("logger-sharelatex")
|
||||
_ = require("underscore")
|
||||
|
||||
module.exports = class Router
|
||||
constructor: (app)->
|
||||
constructor: (webRouter, apiRouter)->
|
||||
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'
|
||||
|
||||
app.post '/login', AuthenticationController.login
|
||||
app.get '/logout', UserController.logout
|
||||
app.get '/restricted', SecurityManager.restricted
|
||||
webRouter.post '/login', AuthenticationController.login
|
||||
webRouter.get '/logout', UserController.logout
|
||||
webRouter.get '/restricted', SecurityManager.restricted
|
||||
|
||||
# Left as a placeholder for implementing a public register page
|
||||
app.get '/register', UserPagesController.registerPage
|
||||
webRouter.get '/register', UserPagesController.registerPage
|
||||
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)
|
||||
|
||||
app.get '/blog', BlogController.getIndexPage
|
||||
app.get '/blog/*', BlogController.getPage
|
||||
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)
|
||||
|
||||
|
||||
if Settings.enableSubscriptions
|
||||
app.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
|
||||
webRouter.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
|
||||
|
||||
webRouter.get '/blog', BlogController.getIndexPage
|
||||
webRouter.get '/blog/*', BlogController.getPage
|
||||
|
||||
webRouter.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
|
||||
webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
|
||||
webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
|
||||
|
||||
app.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
|
||||
app.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
|
||||
app.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
|
||||
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||
webRouter.delete '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
||||
|
||||
app.del '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||
app.del '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
||||
webRouter.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
||||
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 '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
|
||||
app.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
|
||||
app.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
||||
webRouter.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
||||
webRouter.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
|
||||
|
||||
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
|
||||
app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
|
||||
|
||||
app.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({
|
||||
webRouter.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({
|
||||
endpointName: "open-project"
|
||||
params: ["Project_id"]
|
||||
maxRequests: 10
|
||||
timeInterval: 60
|
||||
}), 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
|
||||
|
||||
app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||
webRouter.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||
webRouter.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||
webRouter.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||
((req, res, next) ->
|
||||
params =
|
||||
"Project_id": req.params[0]
|
||||
|
@ -107,78 +108,86 @@ module.exports = class Router
|
|||
req.params = params
|
||||
next()
|
||||
), SecurityManager.requestCanAccessProject, CompileController.getFileFromClsi
|
||||
app.del "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
|
||||
app.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
app.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
webRouter.delete "/project/:Project_id/output", SecurityManager.requestCanAccessProject, CompileController.deleteAuxFiles
|
||||
webRouter.get "/project/:Project_id/sync/code", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
webRouter.get "/project/:Project_id/sync/pdf", SecurityManager.requestCanAccessProject, CompileController.proxySync
|
||||
|
||||
app.del '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
|
||||
app.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
|
||||
app.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
|
||||
webRouter.delete '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
|
||||
webRouter.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
|
||||
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
|
||||
app.get "/project/:Project_id/doc/:doc_id/diff", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.get "/project/:Project_id/updates", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
webRouter.get "/project/:Project_id/doc/:doc_id/diff", 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
|
||||
app.get '/project/download/zip', SecurityManager.requestCanAccessMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||
webRouter.get '/Project/:Project_id/download/zip', SecurityManager.requestCanAccessProject, ProjectDownloadsController.downloadProject
|
||||
webRouter.get '/project/download/zip', SecurityManager.requestCanAccessMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||
|
||||
app.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
|
||||
app.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
|
||||
webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
|
||||
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
|
||||
app.get '/internal/project/:project_id/compile/pdf', AuthenticationController.httpAuth, CompileController.compileAndDownloadPdf
|
||||
# New 'stable' /internal API end points
|
||||
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
|
||||
app.post '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.setDocument
|
||||
app.ignoreCsrf('post', '/project/:Project_id/doc/:doc_id')
|
||||
webRouter.get /^\/internal\/project\/([^\/]*)\/output\/(.*)$/,
|
||||
((req, res, next) ->
|
||||
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
|
||||
app.del '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
|
||||
app.ignoreCsrf('post', '/user/:user_id/update/*')
|
||||
app.ignoreCsrf('delete', '/user/:user_id/update/*')
|
||||
apiRouter.get '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.getDocument
|
||||
apiRouter.post '/project/:Project_id/doc/:doc_id', AuthenticationController.httpAuth, DocumentController.setDocument
|
||||
|
||||
apiRouter.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
|
||||
apiRouter.delete '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
|
||||
|
||||
app.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
|
||||
app.del '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
|
||||
app.ignoreCsrf('post', '/project/:project_id/contents/*')
|
||||
app.ignoreCsrf('delete', '/project/:project_id/contents/*')
|
||||
apiRouter.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
|
||||
apiRouter.delete '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
|
||||
|
||||
app.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
app.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
webRouter.post "/spelling/check", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
webRouter.post "/spelling/learn", AuthenticationController.requireLogin(), SpellingController.proxyRequestToSpellingApi
|
||||
|
||||
app.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
|
||||
app.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
|
||||
webRouter.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
|
||||
webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
|
||||
|
||||
app.get /learn(\/.*)?/, WikiController.getPage
|
||||
webRouter.get /learn(\/.*)?/, WikiController.getPage
|
||||
|
||||
#Admin Stuff
|
||||
app.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
||||
app.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
||||
app.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
||||
app.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
||||
app.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
||||
app.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
||||
app.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds
|
||||
app.post '/admin/pollDropboxForUser', SecurityManager.requestIsAdmin, AdminController.pollDropboxForUser
|
||||
app.post '/admin/messages', SecurityManager.requestIsAdmin, AdminController.createMessage
|
||||
app.post '/admin/messages/clear', SecurityManager.requestIsAdmin, AdminController.clearMessages
|
||||
webRouter.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
||||
webRouter.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
||||
webRouter.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
||||
webRouter.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
||||
webRouter.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
||||
webRouter.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
||||
webRouter.post '/admin/flushProjectToTpds', SecurityManager.requestIsAdmin, AdminController.flushProjectToTpds
|
||||
webRouter.post '/admin/pollDropboxForUser', SecurityManager.requestIsAdmin, AdminController.pollDropboxForUser
|
||||
webRouter.post '/admin/messages', SecurityManager.requestIsAdmin, AdminController.createMessage
|
||||
webRouter.post '/admin/messages/clear', SecurityManager.requestIsAdmin, AdminController.clearMessages
|
||||
|
||||
app.get '/perfTest', (req,res)->
|
||||
apiRouter.get '/perfTest', (req,res)->
|
||||
res.send("hello")
|
||||
req.session.destroy()
|
||||
|
||||
app.get '/status', (req,res)->
|
||||
apiRouter.get '/status', (req,res)->
|
||||
res.send("websharelatex is up")
|
||||
req.session.destroy()
|
||||
|
||||
|
||||
app.get '/health_check', HealthCheckController.check
|
||||
app.get '/health_check/redis', HealthCheckController.checkRedis
|
||||
webRouter.get '/health_check', HealthCheckController.check
|
||||
webRouter.get '/health_check/redis', HealthCheckController.checkRedis
|
||||
|
||||
app.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) ->
|
||||
apiRouter.get "/status/compiler/:Project_id", SecurityManager.requestCanAccessProject, (req, res) ->
|
||||
sendRes = _.once (statusCode, message)->
|
||||
res.writeHead statusCode
|
||||
res.end message
|
||||
|
@ -187,27 +196,26 @@ module.exports = class Router
|
|||
setTimeout (() ->
|
||||
sendRes 500, "Compiler timed out"
|
||||
), 10000
|
||||
req.session.destroy()
|
||||
|
||||
app.get "/ip", (req, res, next) ->
|
||||
apiRouter.get "/ip", (req, res, next) ->
|
||||
res.send({
|
||||
ip: req.ip
|
||||
ips: req.ips
|
||||
headers: req.headers
|
||||
})
|
||||
|
||||
app.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
||||
app.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
||||
app.get '/oops-mongo', (req, res, next) ->
|
||||
apiRouter.get '/oops-express', (req, res, next) -> next(new Error("Test error"))
|
||||
apiRouter.get '/oops-internal', (req, res, next) -> throw new Error("Test error")
|
||||
apiRouter.get '/oops-mongo', (req, res, next) ->
|
||||
require("./models/Project").Project.findOne {}, () ->
|
||||
throw new Error("Test error")
|
||||
|
||||
app.get '/opps-small', (req, res, next)->
|
||||
apiRouter.get '/opps-small', (req, res, next)->
|
||||
logger.err "test error occured"
|
||||
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"
|
||||
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-controller="OnlineUsersController"
|
||||
)
|
||||
span.online-user(
|
||||
ng-repeat="user in onlineUsersArray",
|
||||
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }",
|
||||
popover="{{ user.name }}"
|
||||
popover-placement="bottom"
|
||||
popover-append-to-body="true"
|
||||
popover-trigger="mouseenter"
|
||||
ng-click="gotoUser(user)"
|
||||
) {{ user.name.slice(0,1) }}
|
||||
span(ng-if="onlineUsersArray.length < 4")
|
||||
span.online-user(
|
||||
ng-repeat="user in onlineUsersArray",
|
||||
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }",
|
||||
popover="{{ user.name }}"
|
||||
popover-placement="bottom"
|
||||
popover-append-to-body="true"
|
||||
popover-trigger="mouseenter"
|
||||
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(
|
||||
href,
|
||||
|
|
|
@ -48,6 +48,11 @@ aside#left-menu.full-size(
|
|||
h4() #{translate("sync")}
|
||||
!= moduleIncludes("editorLeftMenu:sync", locals)
|
||||
|
||||
span(ng-show="!anonymous")
|
||||
h4 #{translate("services")}
|
||||
!= moduleIncludes("editorLeftMenu:editing_services", locals)
|
||||
|
||||
|
||||
h4(ng-show="!anonymous") #{translate("settings")}
|
||||
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
|
||||
.containter-fluid
|
||||
|
|
|
@ -48,8 +48,8 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
|||
.small #{translate("share_with_your_collabs")}
|
||||
.form-group
|
||||
input.form-control(
|
||||
type="email"
|
||||
placeholder="Enter email address..."
|
||||
type="text"
|
||||
placeholder="joe@example.com, sue@example.com, ..."
|
||||
ng-model="inputs.email"
|
||||
focus-on="open"
|
||||
)
|
||||
|
@ -64,7 +64,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
|||
|
|
||||
button.btn.btn-info(
|
||||
type="submit"
|
||||
ng-click="addMember()"
|
||||
ng-click="addMembers()"
|
||||
) #{translate("share")}
|
||||
div.text-center(ng-hide="canAddCollaborators")
|
||||
p #{translate("need_to_upgrade_for_more_collabs")}.
|
||||
|
|
|
@ -71,19 +71,33 @@
|
|||
strong #{translate("create_your_first_project")}
|
||||
|
||||
- if (showUserDetailsArea)
|
||||
.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}}"})}
|
||||
- if (Math.random() < 0.5)
|
||||
.row-spaced
|
||||
hr
|
||||
.card.card-thin
|
||||
p.text-center.small
|
||||
| <strong>Python</strong> or <strong>R</strong> user?
|
||||
|
||||
button#completeUserProfileInformation.btn.btn-info(
|
||||
ng-hide="formVisable",
|
||||
ng-click="openUserProfileModal()"
|
||||
) #{translate("complete")}
|
||||
p.text-center.small
|
||||
a(href="https://www.getdatajoy.com/", target="_blank").btn.btn-info.btn-small Try DataJoy
|
||||
|
||||
p.text-center.small(style="font-size: 0.8em")
|
||||
a(href="https://www.getdatajoy.com/", target="_blank") DataJoy
|
||||
| is a new online Python and R editor from ShareLaTeX.
|
||||
- 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)
|
||||
.row-spaced(ng-if="projects.length > 0", ng-cloak).text-centered
|
||||
|
|
|
@ -12,22 +12,30 @@
|
|||
"dependencies": {
|
||||
"archiver": "0.9.0",
|
||||
"async": "0.6.2",
|
||||
"base64-stream": "^0.1.2",
|
||||
"basic-auth-connect": "^1.0.0",
|
||||
"bcrypt": "0.8.3",
|
||||
"body-parser": "^1.13.1",
|
||||
"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",
|
||||
"express": "3.3.4",
|
||||
"fairy": "0.0.2",
|
||||
"express": "4.13.0",
|
||||
"express-session": "1.11.3",
|
||||
"http-proxy": "^1.8.1",
|
||||
"jade": "~1.3.1",
|
||||
"ldapjs": "^0.7.1",
|
||||
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
|
||||
"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",
|
||||
"mocha": "1.17.1",
|
||||
"mongojs": "0.18.2",
|
||||
"mongoose": "3.8.28",
|
||||
"mongoose": "4.1.0",
|
||||
"multer": "^0.1.8",
|
||||
"node-uuid": "1.4.1",
|
||||
"nodemailer": "0.6.1",
|
||||
"optimist": "0.6.1",
|
||||
|
|
|
@ -56,7 +56,11 @@ define [
|
|||
user.doc = @ide.fileTreeManager.findEntityById(user.doc_id)
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -22,19 +22,34 @@ define [
|
|||
allowedNoOfMembers = $scope.project.features.collaborators
|
||||
$scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS
|
||||
|
||||
$scope.addMember = () ->
|
||||
$scope.addMembers = () ->
|
||||
return if !$scope.inputs.email? or $scope.inputs.email == ""
|
||||
|
||||
emails = $scope.inputs.email.split(/,\s*/)
|
||||
$scope.inputs.email = ""
|
||||
$scope.state.error = null
|
||||
$scope.state.inflight = true
|
||||
projectMembers
|
||||
.addMember($scope.inputs.email, $scope.inputs.privileges)
|
||||
.success (data) ->
|
||||
|
||||
do addNextMember = () ->
|
||||
if emails.length == 0 or !$scope.canAddCollaborators
|
||||
$scope.state.inflight = false
|
||||
$scope.inputs.email = ""
|
||||
$scope.project.members.push data?.user
|
||||
.error () ->
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = "Sorry, something went wrong :("
|
||||
$scope.$apply()
|
||||
return
|
||||
|
||||
email = emails.shift()
|
||||
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) ->
|
||||
|
|
|
@ -3,7 +3,6 @@ define [
|
|||
"main/user-details"
|
||||
"main/account-settings"
|
||||
"main/account-upgrade"
|
||||
"main/templates"
|
||||
"main/plans"
|
||||
"main/group-members"
|
||||
"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/left-menu.less";
|
||||
@import "./editor/pdf.less";
|
||||
@import "./editor/enago.less";
|
||||
@import "./editor/share.less";
|
||||
@import "./editor/chat.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-user {
|
||||
background-color: rgb(0, 170, 255);
|
||||
background-color: @online-user-color;
|
||||
width: 24px;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
|
@ -11,4 +13,27 @@
|
|||
border-radius: 3px;
|
||||
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 {
|
||||
padding-bottom: 20px;
|
||||
.btn {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.searchResult {
|
||||
|
|
|
@ -423,6 +423,7 @@ describe "AuthenticationController", ->
|
|||
beforeEach ->
|
||||
@req.session =
|
||||
save: sinon.stub().callsArg(0)
|
||||
destroy : sinon.stub()
|
||||
@req.sessionStore =
|
||||
generate: sinon.stub()
|
||||
@AuthenticationController.establishUserSession @req, @user, @callback
|
||||
|
@ -435,6 +436,9 @@ describe "AuthenticationController", ->
|
|||
@req.session.user.referal_id.should.equal @user.referal_id
|
||||
@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", ->
|
||||
@req.sessionStore.generate.calledWith(@req).should.equal true
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ describe "CollaboratorsController", ->
|
|||
@req.params =
|
||||
Project_id: @project_id = "project-id-123"
|
||||
user_id: @user_id = "user-id-123"
|
||||
@res.send = sinon.stub()
|
||||
@res.sendStatus = sinon.stub()
|
||||
@EditorController.removeUserFromProject = sinon.stub().callsArg(2)
|
||||
@CollaboratorsController.removeUserFromProject @req, @res
|
||||
|
||||
|
@ -101,7 +101,7 @@ describe "CollaboratorsController", ->
|
|||
.should.equal true
|
||||
|
||||
it "should send the back a success response", ->
|
||||
@res.send.calledWith(204).should.equal true
|
||||
@res.sendStatus.calledWith(204).should.equal true
|
||||
|
||||
|
||||
describe "_formatCollaborators", ->
|
||||
|
|
|
@ -98,7 +98,6 @@ describe "CompileController", ->
|
|||
|
||||
describe "when downloading for embedding", ->
|
||||
beforeEach ->
|
||||
@project.useClsi2 = true
|
||||
@CompileController.proxyToClsi = sinon.stub()
|
||||
@CompileController.downloadPdf(@req, @res, @next)
|
||||
|
||||
|
@ -321,7 +320,7 @@ describe "CompileController", ->
|
|||
@CompileManager.deleteAuxFiles = sinon.stub().callsArg(1)
|
||||
@req.params =
|
||||
Project_id: @project_id
|
||||
@res.send = sinon.stub()
|
||||
@res.sendStatus = sinon.stub()
|
||||
@CompileController.deleteAuxFiles @req, @res, @next
|
||||
|
||||
it "should proxy to the CLSI", ->
|
||||
|
@ -330,7 +329,7 @@ describe "CompileController", ->
|
|||
.should.equal true
|
||||
|
||||
it "should return a 200", ->
|
||||
@res.send
|
||||
@res.sendStatus
|
||||
.calledWith(200)
|
||||
.should.equal true
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ describe "DocstoreManager", ->
|
|||
apis:
|
||||
docstore:
|
||||
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
|
||||
|
||||
|
@ -179,3 +179,42 @@ describe "DocstoreManager", ->
|
|||
project_id: @project_id
|
||||
}, "error getting all docs from docstore")
|
||||
.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
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,10 @@ Errors = require "../../../../app/js/errors"
|
|||
|
||||
describe "DocumentController", ->
|
||||
beforeEach ->
|
||||
@DocumentController = SandboxedModule.require modulePath, requires:
|
||||
@DocumentController = SandboxedModule.require modulePath, requires:
|
||||
"logger-sharelatex":
|
||||
log:->
|
||||
err:->
|
||||
"../Project/ProjectEntityHandler": @ProjectEntityHandler = {}
|
||||
@res = new MockResponse()
|
||||
@req = new MockRequest()
|
||||
|
|
|
@ -24,6 +24,7 @@ describe "EditorHttpController", ->
|
|||
@req = {}
|
||||
@res =
|
||||
send: sinon.stub()
|
||||
sendStatus: sinon.stub()
|
||||
json: sinon.stub()
|
||||
@callback = sinon.stub()
|
||||
|
||||
|
@ -190,7 +191,7 @@ describe "EditorHttpController", ->
|
|||
@EditorHttpController.addDoc @req, @res
|
||||
|
||||
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", ->
|
||||
beforeEach ->
|
||||
|
@ -223,7 +224,7 @@ describe "EditorHttpController", ->
|
|||
@EditorHttpController.addFolder @req, @res
|
||||
|
||||
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", ->
|
||||
|
@ -243,7 +244,7 @@ describe "EditorHttpController", ->
|
|||
.should.equal true
|
||||
|
||||
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", ->
|
||||
beforeEach ->
|
||||
|
@ -257,7 +258,7 @@ describe "EditorHttpController", ->
|
|||
@EditorHttpController.renameEntity @req, @res
|
||||
|
||||
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", ->
|
||||
|
||||
|
@ -272,7 +273,7 @@ describe "EditorHttpController", ->
|
|||
@EditorHttpController.renameEntity @req, @res
|
||||
|
||||
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", ->
|
||||
|
@ -292,7 +293,7 @@ describe "EditorHttpController", ->
|
|||
.should.equal true
|
||||
|
||||
it "should send back a success response", ->
|
||||
@res.send.calledWith(204).should.equal true
|
||||
@res.sendStatus.calledWith(204).should.equal true
|
||||
|
||||
describe "deleteEntity", ->
|
||||
beforeEach ->
|
||||
|
@ -309,4 +310,4 @@ describe "EditorHttpController", ->
|
|||
.should.equal true
|
||||
|
||||
it "should send back a success response", ->
|
||||
@res.send.calledWith(204).should.equal true
|
||||
@res.sendStatus.calledWith(204).should.equal true
|
||||
|
|
|
@ -21,14 +21,15 @@ describe "FileStoreController", ->
|
|||
@stream = {}
|
||||
@project_id = "2k3j1lk3j21lk3j"
|
||||
@file_id = "12321kklj1lk3jk12"
|
||||
@req =
|
||||
@req =
|
||||
params:
|
||||
Project_id: @project_id
|
||||
File_id: @file_id
|
||||
query: "query string here"
|
||||
@res =
|
||||
get: (key) -> undefined
|
||||
@res =
|
||||
setHeader: sinon.stub()
|
||||
@file =
|
||||
@file =
|
||||
name: "myfile.png"
|
||||
|
||||
describe "getFile", ->
|
||||
|
@ -49,7 +50,7 @@ describe "FileStoreController", ->
|
|||
done()
|
||||
@controller.getFile @req, @res
|
||||
|
||||
it "should get the file from the db", (done)->
|
||||
it "should get the file from the db", (done)->
|
||||
@stream.pipe = (des)=>
|
||||
opts =
|
||||
project_id: @project_id
|
||||
|
@ -64,5 +65,66 @@ describe "FileStoreController", ->
|
|||
@res.setHeader.calledWith("Content-Disposition", "attachment; filename=#{@file.name}").should.equal true
|
||||
done()
|
||||
@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()
|
|
@ -14,7 +14,7 @@ describe "PasswordResetController", ->
|
|||
@PasswordResetHandler =
|
||||
generateAndEmailResetToken:sinon.stub()
|
||||
setNewUserPassword:sinon.stub()
|
||||
@RateLimiter =
|
||||
@RateLimiter =
|
||||
addCount: sinon.stub()
|
||||
@PasswordResetController = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex":@settings
|
||||
|
@ -32,6 +32,8 @@ describe "PasswordResetController", ->
|
|||
password:@password
|
||||
i18n:
|
||||
translate:->
|
||||
session: {}
|
||||
query: {}
|
||||
|
||||
@res = {}
|
||||
|
||||
|
@ -51,7 +53,7 @@ describe "PasswordResetController", ->
|
|||
it "should tell the handler to process that email", (done)->
|
||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 200
|
||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.trim()).should.equal true
|
||||
done()
|
||||
|
@ -78,7 +80,7 @@ describe "PasswordResetController", ->
|
|||
@req.body.email = @email
|
||||
@RateLimiter.addCount.callsArgWith(1, null, true)
|
||||
@PasswordResetHandler.generateAndEmailResetToken.callsArgWith(1, null, true)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 200
|
||||
@PasswordResetHandler.generateAndEmailResetToken.calledWith(@email.toLowerCase()).should.equal true
|
||||
done()
|
||||
|
@ -86,9 +88,12 @@ describe "PasswordResetController", ->
|
|||
|
||||
describe "setNewUserPassword", ->
|
||||
|
||||
beforeEach ->
|
||||
@req.session.resetToken = @token
|
||||
|
||||
it "should tell the user handler to reset the password", (done)->
|
||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2, null, true)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 200
|
||||
@PasswordResetHandler.setNewUserPassword.calledWith(@token, @password).should.equal true
|
||||
done()
|
||||
|
@ -104,7 +109,7 @@ describe "PasswordResetController", ->
|
|||
it "should return 400 (Bad Request) if there is no password", (done)->
|
||||
@req.body.password = ""
|
||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 400
|
||||
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
||||
done()
|
||||
|
@ -113,11 +118,51 @@ describe "PasswordResetController", ->
|
|||
it "should return 400 (Bad Request) if there is no passwordResetToken", (done)->
|
||||
@req.body.passwordResetToken = ""
|
||||
@PasswordResetHandler.setNewUserPassword.callsArgWith(2)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 400
|
||||
@PasswordResetHandler.setNewUserPassword.called.should.equal false
|
||||
done()
|
||||
@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)->
|
||||
@ProjectDetailsHandler.getDetails.callsArgWith(1, "error")
|
||||
@res.send = (resCode)=>
|
||||
@res.sendStatus = (resCode)=>
|
||||
resCode.should.equal 500
|
||||
done()
|
||||
@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()
|
||||
@EditorController =
|
||||
renameProject:sinon.stub()
|
||||
@InactiveProjectManager =
|
||||
reactivateProjectIfRequired:sinon.stub()
|
||||
@ProjectUpdateHandler =
|
||||
markAsOpened: sinon.stub()
|
||||
@ProjectController = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex":@settings
|
||||
"logger-sharelatex":
|
||||
|
@ -57,6 +61,8 @@ describe "ProjectController", ->
|
|||
'../../models/Project': Project:@ProjectModel
|
||||
"../../models/User":User:@UserModel
|
||||
"../../managers/SecurityManager":@SecurityManager
|
||||
"../InactiveData/InactiveProjectManager":@InactiveProjectManager
|
||||
"./ProjectUpdateHandler":@ProjectUpdateHandler
|
||||
|
||||
@user =
|
||||
_id:"!£123213kjljkl"
|
||||
|
@ -78,7 +84,7 @@ describe "ProjectController", ->
|
|||
@EditorController.renameProject = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
name: @name = "New name"
|
||||
@res.send = (code) =>
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.renameProject
|
||||
.calledWith(@project_id, @name)
|
||||
.should.equal true
|
||||
|
@ -90,7 +96,7 @@ describe "ProjectController", ->
|
|||
@EditorController.setCompiler = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
compiler: @compiler = "pdflatex"
|
||||
@res.send = (code) =>
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setCompiler
|
||||
.calledWith(@project_id, @compiler)
|
||||
.should.equal true
|
||||
|
@ -102,7 +108,7 @@ describe "ProjectController", ->
|
|||
@EditorController.setSpellCheckLanguage = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
spellCheckLanguage: @languageCode = "fr"
|
||||
@res.send = (code) =>
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setSpellCheckLanguage
|
||||
.calledWith(@project_id, @languageCode)
|
||||
.should.equal true
|
||||
|
@ -114,7 +120,7 @@ describe "ProjectController", ->
|
|||
@EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
publicAccessLevel: @publicAccessLevel = "readonly"
|
||||
@res.send = (code) =>
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setPublicAccessLevel
|
||||
.calledWith(@project_id, @publicAccessLevel)
|
||||
.should.equal true
|
||||
|
@ -126,7 +132,7 @@ describe "ProjectController", ->
|
|||
@EditorController.setRootDoc = sinon.stub().callsArg(2)
|
||||
@req.body =
|
||||
rootDocId: @rootDocId = "root-doc-id"
|
||||
@res.send = (code) =>
|
||||
@res.sendStatus = (code) =>
|
||||
@EditorController.setRootDoc
|
||||
.calledWith(@project_id, @rootDocId)
|
||||
.should.equal true
|
||||
|
@ -136,7 +142,7 @@ describe "ProjectController", ->
|
|||
|
||||
describe "deleteProject", ->
|
||||
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
|
||||
code.should.equal 200
|
||||
done()
|
||||
|
@ -144,7 +150,7 @@ describe "ProjectController", ->
|
|||
|
||||
it "should tell the project deleter to delete when forever=true", (done)->
|
||||
@req.query = forever: "true"
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@ProjectDeleter.deleteProject.calledWith(@project_id).should.equal true
|
||||
code.should.equal 200
|
||||
done()
|
||||
|
@ -152,7 +158,7 @@ describe "ProjectController", ->
|
|||
|
||||
describe "restoreProject", ->
|
||||
it "should tell the project deleter", (done)->
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@ProjectDeleter.restoreProject.calledWith(@project_id).should.equal true
|
||||
code.should.equal 200
|
||||
done()
|
||||
|
@ -244,7 +250,7 @@ describe "ProjectController", ->
|
|||
|
||||
it "should call the editor controller", (done)->
|
||||
@EditorController.renameProject.callsArgWith(2)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 200
|
||||
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
||||
done()
|
||||
|
@ -252,7 +258,7 @@ describe "ProjectController", ->
|
|||
|
||||
it "should send a 500 if there is a problem", (done)->
|
||||
@EditorController.renameProject.callsArgWith(2, "problem")
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 500
|
||||
@EditorController.renameProject.calledWith(@project_id, @newProjectName).should.equal true
|
||||
done()
|
||||
|
@ -260,7 +266,7 @@ describe "ProjectController", ->
|
|||
|
||||
it "should return an error if the name is over 150 chars", (done)->
|
||||
@req.body.newProjectName = "EDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOTEDMUBEEBKBXUUUZERMNSXFFWIBHGSDAWGMRIQWJBXGWSBVWSIKLFPRBYSJEKMFHTRZBHVKJSRGKTBHMJRXPHORFHAKRNPZGGYIOT"
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 400
|
||||
done()
|
||||
@ProjectController.renameProject @req, @res
|
||||
|
@ -282,6 +288,9 @@ describe "ProjectController", ->
|
|||
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
||||
@SecurityManager.userCanAccessProject.callsArgWith 2, true, "owner"
|
||||
@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||
@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
|
||||
@ProjectUpdateHandler.markAsOpened.callsArgWith(1)
|
||||
|
||||
|
||||
it "should render the project/editor page", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
|
@ -317,7 +326,20 @@ describe "ProjectController", ->
|
|||
|
||||
it "should not render the page if the project can not be accessed", (done)->
|
||||
@SecurityManager.userCanAccessProject = sinon.stub().callsArgWith 2, false
|
||||
@res.send = (resCode, opts)=>
|
||||
@res.sendStatus = (resCode, opts)=>
|
||||
resCode.should.equal 401
|
||||
done()
|
||||
@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
|
||||
|
||||
|
|
|
@ -159,6 +159,13 @@ describe 'project model', ->
|
|||
assert !err?
|
||||
doc._id.should.equal rootDoc._id
|
||||
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', ->
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ chai = require('chai').should()
|
|||
modulePath = "../../../../app/js/Features/Project/ProjectUpdateHandler.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe 'updating a project', ->
|
||||
describe 'ProjectUpdateHandler', ->
|
||||
|
||||
|
||||
beforeEach ->
|
||||
|
@ -22,3 +22,36 @@ describe 'updating a project', ->
|
|||
now = Date.now()+""
|
||||
date.substring(0,5).should.equal now.substring(0,5)
|
||||
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", ->
|
||||
beforeEach (done)->
|
||||
@res =
|
||||
send:->
|
||||
sendStatus:->
|
||||
done()
|
||||
sinon.spy @res, "send"
|
||||
sinon.spy @res, "sendStatus"
|
||||
@subscriptionDetails =
|
||||
card:"1234"
|
||||
cvv:"123"
|
||||
|
@ -300,7 +300,7 @@ describe "SubscriptionController sanboxed", ->
|
|||
done()
|
||||
|
||||
it "should redurect to the subscription page", (done)->
|
||||
@res.send.calledWith(201).should.equal true
|
||||
@res.sendStatus.calledWith(201).should.equal true
|
||||
done()
|
||||
|
||||
|
||||
|
@ -363,9 +363,9 @@ describe "SubscriptionController sanboxed", ->
|
|||
expired_subscription_notification:
|
||||
subscription:
|
||||
uuid: @activeRecurlySubscription.uuid
|
||||
@res = send:->
|
||||
@res = sendStatus:->
|
||||
done()
|
||||
sinon.spy @res, "send"
|
||||
sinon.spy @res, "sendStatus"
|
||||
@SubscriptionController.recurlyCallback @req, @res
|
||||
|
||||
it "should tell the SubscriptionHandler to process the recurly callback", (done)->
|
||||
|
@ -374,7 +374,7 @@ describe "SubscriptionController sanboxed", ->
|
|||
|
||||
|
||||
it "should send a 200", (done)->
|
||||
@res.send.calledWith(200)
|
||||
@res.sendStatus.calledWith(200)
|
||||
done()
|
||||
|
||||
describe "with a non-actionable request", ->
|
||||
|
@ -385,16 +385,16 @@ describe "SubscriptionController sanboxed", ->
|
|||
new_subscription_notification:
|
||||
subscription:
|
||||
uuid: @activeRecurlySubscription.uuid
|
||||
@res = send:->
|
||||
@res = sendStatus:->
|
||||
done()
|
||||
sinon.spy @res, "send"
|
||||
sinon.spy @res, "sendStatus"
|
||||
@SubscriptionController.recurlyCallback @req, @res
|
||||
|
||||
it "should not call the subscriptionshandler", ->
|
||||
@SubscriptionHandler.recurlyCallback.called.should.equal false
|
||||
|
||||
it "should respond with a 200 status", ->
|
||||
@res.send.calledWith(200)
|
||||
@res.sendStatus.calledWith(200)
|
||||
|
||||
|
||||
describe "renderUpgradeToAnnualPlanPage", ->
|
||||
|
@ -442,7 +442,7 @@ describe "SubscriptionController sanboxed", ->
|
|||
@req.body =
|
||||
planName:"student"
|
||||
|
||||
@res.send = ()=>
|
||||
@res.sendStatus = ()=>
|
||||
@SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true
|
||||
done()
|
||||
|
||||
|
@ -453,7 +453,7 @@ describe "SubscriptionController sanboxed", ->
|
|||
@req.body =
|
||||
planName:"collaborator"
|
||||
|
||||
@res.send = (url)=>
|
||||
@res.sendStatus = (url)=>
|
||||
@SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true
|
||||
done()
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ describe "SubscriptionGroupController", ->
|
|||
|
||||
it "should ask the SubscriptionGroupHandler to send the verification email", (done)->
|
||||
res =
|
||||
send : (statusCode)=>
|
||||
sendStatus : (statusCode)=>
|
||||
statusCode.should.equal 200
|
||||
@GroupHandler.sendVerificationEmail.calledWith(@subscription_id, @licenceName, @user_email).should.equal true
|
||||
done()
|
||||
|
|
|
@ -28,7 +28,7 @@ describe 'TpdsController', ->
|
|||
headers:
|
||||
"x-sl-update-source": @source = "dropbox"
|
||||
@TpdsUpdateHandler.newUpdate = sinon.stub().callsArg(5)
|
||||
res = send: =>
|
||||
res = sendStatus: =>
|
||||
@TpdsUpdateHandler.newUpdate.calledWith(@user_id, "projectName","/here.txt", req, @source).should.equal true
|
||||
done()
|
||||
@TpdsController.mergeUpdate req, res
|
||||
|
@ -43,7 +43,7 @@ describe 'TpdsController', ->
|
|||
headers:
|
||||
"x-sl-update-source": @source = "dropbox"
|
||||
@TpdsUpdateHandler.deleteUpdate = sinon.stub().callsArg(4)
|
||||
res = send: =>
|
||||
res = sendStatus: =>
|
||||
@TpdsUpdateHandler.deleteUpdate.calledWith(@user_id, "projectName", "/here.txt", @source).should.equal true
|
||||
done()
|
||||
@TpdsController.deleteUpdate req, res
|
||||
|
@ -86,7 +86,7 @@ describe 'TpdsController', ->
|
|||
headers:
|
||||
"x-sl-update-source": @source = "github"
|
||||
@res =
|
||||
send: sinon.stub()
|
||||
sendStatus: sinon.stub()
|
||||
|
||||
@TpdsController.updateProjectContents @req, @res
|
||||
|
||||
|
@ -96,10 +96,8 @@ describe 'TpdsController', ->
|
|||
.should.equal true
|
||||
|
||||
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', ->
|
||||
beforeEach ->
|
||||
|
@ -113,7 +111,7 @@ describe 'TpdsController', ->
|
|||
headers:
|
||||
"x-sl-update-source": @source = "github"
|
||||
@res =
|
||||
send: sinon.stub()
|
||||
sendStatus: sinon.stub()
|
||||
|
||||
@TpdsController.deleteProjectContents @req, @res
|
||||
|
||||
|
@ -123,8 +121,5 @@ describe 'TpdsController', ->
|
|||
.should.equal true
|
||||
|
||||
it "should return a success", ->
|
||||
@res.send.calledWith(200).should.equal true
|
||||
|
||||
it "should clear the session", ->
|
||||
@req.session.destroy.called.should.equal true
|
||||
@res.sendStatus.calledWith(200).should.equal true
|
||||
|
||||
|
|
|
@ -19,32 +19,55 @@ filestoreUrl = "filestore.sharelatex.com"
|
|||
|
||||
describe 'TpdsUpdateSender', ->
|
||||
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 = findById:sinon.stub().callsArgWith(2, null, project)
|
||||
@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:
|
||||
'fairy':{connect:=>{queue:=>@requestQueuer}}
|
||||
"settings-sharelatex":
|
||||
siteUrl:siteUrl
|
||||
httpAuthSiteUrl:httpAuthSiteUrl,
|
||||
apis:
|
||||
thirdPartyDataStore: {url: thirdPartyDataStoreApiUrl}
|
||||
filestore:
|
||||
url: filestoreUrl
|
||||
docstore:
|
||||
pubUrl: @docstoreUrl
|
||||
redis:fairy:{}
|
||||
"settings-sharelatex": @settings
|
||||
"logger-sharelatex":{log:->}
|
||||
'../../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', ->
|
||||
|
||||
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'
|
||||
path = '/some/path/here.jpg'
|
||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
||||
@updateSender._enqueue = (uid, method, job, callback)->
|
||||
uid.should.equal project_id
|
||||
job.method.should.equal "post"
|
||||
job.streamOrigin.should.equal "#{filestoreUrl}/project/#{project_id}/file/#{file_id}"
|
||||
|
@ -59,7 +82,7 @@ describe 'TpdsUpdateSender', ->
|
|||
path = "/some/path/here.tex"
|
||||
lines = ["line1", "line2", "line3"]
|
||||
|
||||
@requestQueuer.enqueue = (uid, method, job, callback)=>
|
||||
@updateSender._enqueue = (uid, method, job, callback)=>
|
||||
uid.should.equal project_id
|
||||
job.method.should.equal "post"
|
||||
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
||||
|
@ -71,7 +94,7 @@ describe 'TpdsUpdateSender', ->
|
|||
|
||||
it 'deleting entity', (done)->
|
||||
path = "/path/here/t.tex"
|
||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
||||
@updateSender._enqueue = (uid, method, job, callback)->
|
||||
uid.should.equal project_id
|
||||
job.method.should.equal "DELETE"
|
||||
expectedUrl = "#{thirdPartyDataStoreApiUrl}/user/#{user_id}/entity/#{encodeURIComponent(project_name)}#{encodeURIComponent(path)}"
|
||||
|
@ -83,7 +106,7 @@ describe 'TpdsUpdateSender', ->
|
|||
it 'moving entity', (done)->
|
||||
startPath = "staring/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
|
||||
job.method.should.equal "put"
|
||||
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)->
|
||||
oldProjectName = "/oldProjectName/"
|
||||
newProjectName = "/newProjectName/"
|
||||
@requestQueuer.enqueue = (uid, method, job, callback)->
|
||||
@updateSender._enqueue = (uid, method, job, callback)->
|
||||
uid.should.equal project_id
|
||||
job.method.should.equal "put"
|
||||
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}
|
||||
|
||||
it "pollDropboxForUser", (done) ->
|
||||
@requestQueuer.enqueue = sinon.stub().callsArg(3)
|
||||
@updateSender._enqueue = sinon.stub().callsArg(3)
|
||||
@updateSender.pollDropboxForUser user_id, (error) =>
|
||||
@requestQueuer.enqueue
|
||||
@updateSender._enqueue
|
||||
.calledWith(
|
||||
"poll-dropbox:#{user_id}",
|
||||
"standardHttpRequest",
|
||||
|
|
|
@ -29,7 +29,7 @@ describe "ProjectUploadController", ->
|
|||
@req.files =
|
||||
qqfile:
|
||||
path: @path
|
||||
name: @name
|
||||
originalname: @name
|
||||
@req.session =
|
||||
user:
|
||||
_id: @user_id
|
||||
|
@ -102,7 +102,7 @@ describe "ProjectUploadController", ->
|
|||
@req.files =
|
||||
qqfile:
|
||||
path: @path
|
||||
name: @name
|
||||
originalname: @name
|
||||
@req.params =
|
||||
Project_id: @project_id
|
||||
@req.query =
|
||||
|
@ -173,7 +173,7 @@ describe "ProjectUploadController", ->
|
|||
describe "with a bad request", ->
|
||||
|
||||
beforeEach ->
|
||||
@req.files.qqfile.name = ""
|
||||
@req.files.qqfile.originalname = ""
|
||||
@ProjectUploadController.uploadFile @req, @res
|
||||
|
||||
it "should return a a non success response", ->
|
||||
|
|
|
@ -78,7 +78,7 @@ describe "UserController", ->
|
|||
|
||||
it "should delete the user", (done)->
|
||||
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@UserDeleter.deleteUser.calledWith(@user_id)
|
||||
code.should.equal 200
|
||||
done()
|
||||
|
@ -98,7 +98,7 @@ describe "UserController", ->
|
|||
|
||||
it "should call save", (done)->
|
||||
@req.body = {}
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@user.save.called.should.equal true
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
@ -106,7 +106,7 @@ describe "UserController", ->
|
|||
it "should set the first name", (done)->
|
||||
@req.body =
|
||||
first_name: "bobby "
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@user.first_name.should.equal "bobby"
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
@ -114,7 +114,7 @@ describe "UserController", ->
|
|||
it "should set the role", (done)->
|
||||
@req.body =
|
||||
role: "student"
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@user.role.should.equal "student"
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
@ -122,7 +122,7 @@ describe "UserController", ->
|
|||
it "should set the institution", (done)->
|
||||
@req.body =
|
||||
institution: "MIT"
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@user.institution.should.equal "MIT"
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
@ -130,21 +130,21 @@ describe "UserController", ->
|
|||
it "should set some props on ace", (done)->
|
||||
@req.body =
|
||||
theme: "something"
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
@user.ace.theme.should.equal "something"
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
||||
it "should send an error if the email is 0 len", (done)->
|
||||
@req.body.email = ""
|
||||
@res.send = (code)->
|
||||
@res.sendStatus = (code)->
|
||||
code.should.equal 400
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
||||
it "should send an error if the email does not contain an @", (done)->
|
||||
@req.body.email = "bob at something dot com"
|
||||
@res.send = (code)->
|
||||
@res.sendStatus = (code)->
|
||||
code.should.equal 400
|
||||
done()
|
||||
@UserController.updateUserSettings @req, @res
|
||||
|
@ -152,7 +152,7 @@ describe "UserController", ->
|
|||
it "should call the user updater with the new email and user _id", (done)->
|
||||
@req.body.email = @newEmail.toUpperCase()
|
||||
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
||||
@res.send = (code)=>
|
||||
@res.sendStatus = (code)=>
|
||||
code.should.equal 200
|
||||
@UserUpdater.changeEmailAddress.calledWith(@user_id, @newEmail).should.equal true
|
||||
done()
|
||||
|
|
|
@ -22,6 +22,19 @@ class MockResponse
|
|||
@redirectedTo = url
|
||||
@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) ->
|
||||
if arguments.length < 2
|
||||
if typeof status != "number"
|
||||
|
|
Loading…
Reference in a new issue