mirror of
https://github.com/overleaf/overleaf.git
synced 2025-03-15 08:23:08 +00:00
Merge branch 'master' into ja-review-panel
This commit is contained in:
commit
ba5118d02a
212 changed files with 56549 additions and 710 deletions
|
@ -1,43 +1,48 @@
|
||||||
Settings = require "settings-sharelatex"
|
settings = require "settings-sharelatex"
|
||||||
logger = require "logger-sharelatex"
|
logger = require "logger-sharelatex"
|
||||||
_ = require "underscore"
|
_ = require "underscore"
|
||||||
|
request = require "request"
|
||||||
|
|
||||||
if !Settings.analytics?.postgres?
|
|
||||||
module.exports =
|
|
||||||
recordEvent: (user_id, event, segmentation, callback = () ->) ->
|
|
||||||
logger.log {user_id, event, segmentation}, "no event tracking configured, logging event"
|
|
||||||
callback()
|
|
||||||
else
|
|
||||||
Sequelize = require "sequelize"
|
|
||||||
options = _.extend {logging:false}, Settings.analytics.postgres
|
|
||||||
|
|
||||||
sequelize = new Sequelize(
|
makeRequest = (opts, callback)->
|
||||||
Settings.analytics.postgres.database,
|
if settings.apis?.analytics?.url?
|
||||||
Settings.analytics.postgres.username,
|
urlPath = opts.url
|
||||||
Settings.analytics.postgres.password,
|
opts.url = "#{settings.apis.analytics.url}#{urlPath}"
|
||||||
options
|
request opts, callback
|
||||||
)
|
else
|
||||||
|
callback()
|
||||||
Event = sequelize.define("Event", {
|
|
||||||
user_id: Sequelize.STRING,
|
|
||||||
event: Sequelize.STRING,
|
|
||||||
segmentation: Sequelize.JSONB
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
|
|
||||||
if user_id? and typeof(user_id) != "string"
|
module.exports =
|
||||||
user_id = user_id.toString()
|
|
||||||
if user_id == Settings.smokeTest?.userId
|
|
||||||
# Don't record smoke tests analytics
|
recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
|
||||||
return callback()
|
if user_id == settings.smokeTest?.userId
|
||||||
Event
|
return callback()
|
||||||
.create({ user_id, event, segmentation })
|
opts =
|
||||||
.then(
|
body:
|
||||||
(result) -> callback(),
|
event:event
|
||||||
(error) ->
|
segmentation:segmentation
|
||||||
logger.err {err: error, user_id, event, segmentation}, "error recording analytics event"
|
json:true
|
||||||
callback(error)
|
method:"POST"
|
||||||
)
|
timeout:1000
|
||||||
|
url: "/user/#{user_id}/event"
|
||||||
sync: () -> sequelize.sync()
|
makeRequest opts, callback
|
||||||
|
|
||||||
|
|
||||||
|
getLastOccurance: (user_id, event, callback = (error) ->) ->
|
||||||
|
opts =
|
||||||
|
body:
|
||||||
|
event:event
|
||||||
|
json:true
|
||||||
|
method:"POST"
|
||||||
|
timeout:1000
|
||||||
|
url: "/user/#{user_id}/event/last_occurnace"
|
||||||
|
makeRequest opts, (err, response, body)->
|
||||||
|
if err?
|
||||||
|
console.log response, opts
|
||||||
|
logger.err {user_id, err}, "error getting last occurance of event"
|
||||||
|
return callback err
|
||||||
|
else
|
||||||
|
return callback null, body
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
AnnouncementsHandler = require("./AnnouncementsHandler")
|
||||||
|
AuthenticationController = require("../Authentication/AuthenticationController")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
|
||||||
|
getUndreadAnnouncements: (req, res, next)->
|
||||||
|
if !settings?.apis?.analytics?.url? or !settings.apis.blog.url?
|
||||||
|
return res.json []
|
||||||
|
|
||||||
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
logger.log {user_id}, "getting unread announcements"
|
||||||
|
AnnouncementsHandler.getUnreadAnnouncements user_id, (err, announcements)->
|
||||||
|
if err?
|
||||||
|
logger.err {err, user_id}, "unable to get unread announcements"
|
||||||
|
next(err)
|
||||||
|
else
|
||||||
|
res.json announcements
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
AnalyticsManager = require("../Analytics/AnalyticsManager")
|
||||||
|
BlogHandler = require("../Blog/BlogHandler")
|
||||||
|
async = require("async")
|
||||||
|
_ = require("lodash")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
settings = require("settings-sharelatex")
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
|
||||||
|
getUnreadAnnouncements : (user_id, callback = (err, announcements)->)->
|
||||||
|
async.parallel {
|
||||||
|
lastEvent: (cb)->
|
||||||
|
AnalyticsManager.getLastOccurance user_id, "announcement-alert-dismissed", cb
|
||||||
|
announcements: (cb)->
|
||||||
|
BlogHandler.getLatestAnnouncements cb
|
||||||
|
}, (err, results)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, user_id:user_id, "error getting unread announcements"
|
||||||
|
return callback(err)
|
||||||
|
|
||||||
|
announcements = _.sortBy(results.announcements, "date").reverse()
|
||||||
|
|
||||||
|
lastSeenBlogId = results?.lastEvent?.segmentation?.blogPostId
|
||||||
|
|
||||||
|
announcementIndex = _.findIndex announcements, (announcement)->
|
||||||
|
announcement.id == lastSeenBlogId
|
||||||
|
|
||||||
|
if announcementIndex != -1
|
||||||
|
announcements = announcements.slice(0, announcementIndex)
|
||||||
|
|
||||||
|
logger.log announcementsLength:announcements?.length, user_id:user_id, "returning announcements"
|
||||||
|
|
||||||
|
callback null, announcements
|
|
@ -32,21 +32,27 @@ module.exports = AuthenticationController =
|
||||||
|
|
||||||
afterLoginSessionSetup: (req, user, callback=(err)->) ->
|
afterLoginSessionSetup: (req, user, callback=(err)->) ->
|
||||||
req.login user, (err) ->
|
req.login user, (err) ->
|
||||||
|
if err?
|
||||||
|
logger.err {user_id: user._id, err}, "error from req.login"
|
||||||
|
return callback(err)
|
||||||
# Regenerate the session to get a new sessionID (cookie value) to
|
# Regenerate the session to get a new sessionID (cookie value) to
|
||||||
# protect against session fixation attacks
|
# protect against session fixation attacks
|
||||||
oldSession = req.session
|
oldSession = req.session
|
||||||
req.session.destroy()
|
req.session.destroy (err) ->
|
||||||
req.sessionStore.generate(req)
|
|
||||||
for key, value of oldSession
|
|
||||||
req.session[key] = value
|
|
||||||
# copy to the old `session.user` location, for backward-comptability
|
|
||||||
req.session.user = req.session.passport.user
|
|
||||||
req.session.save (err) ->
|
|
||||||
if err?
|
if err?
|
||||||
logger.err {user_id: user._id}, "error saving regenerated session after login"
|
logger.err {user_id: user._id, err}, "error when trying to destroy old session"
|
||||||
return callback(err)
|
return callback(err)
|
||||||
UserSessionsManager.trackSession(user, req.sessionID, () ->)
|
req.sessionStore.generate(req)
|
||||||
callback(null)
|
for key, value of oldSession
|
||||||
|
req.session[key] = value
|
||||||
|
# copy to the old `session.user` location, for backward-comptability
|
||||||
|
req.session.user = req.session.passport.user
|
||||||
|
req.session.save (err) ->
|
||||||
|
if err?
|
||||||
|
logger.err {user_id: user._id}, "error saving regenerated session after login"
|
||||||
|
return callback(err)
|
||||||
|
UserSessionsManager.trackSession(user, req.sessionID, () ->)
|
||||||
|
callback(null)
|
||||||
|
|
||||||
passportLogin: (req, res, next) ->
|
passportLogin: (req, res, next) ->
|
||||||
# This function is middleware which wraps the passport.authenticate middleware,
|
# This function is middleware which wraps the passport.authenticate middleware,
|
||||||
|
|
24
services/web/app/coffee/Features/Blog/BlogHandler.coffee
Normal file
24
services/web/app/coffee/Features/Blog/BlogHandler.coffee
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
request = require "request"
|
||||||
|
settings = require "settings-sharelatex"
|
||||||
|
_ = require("underscore")
|
||||||
|
logger = require "logger-sharelatex"
|
||||||
|
|
||||||
|
module.exports = BlogHandler =
|
||||||
|
|
||||||
|
getLatestAnnouncements: (callback)->
|
||||||
|
blogUrl = "#{settings.apis.blog.url}/blog/latestannouncements.json"
|
||||||
|
opts =
|
||||||
|
url:blogUrl
|
||||||
|
json:true
|
||||||
|
timeout:500
|
||||||
|
request.get opts, (err, res, announcements)->
|
||||||
|
if err?
|
||||||
|
return callback err
|
||||||
|
if res.statusCode != 200
|
||||||
|
return callback("blog announcement returned non 200")
|
||||||
|
logger.log announcementsLength: announcements?.length, "announcements returned"
|
||||||
|
announcements = _.map announcements, (announcement)->
|
||||||
|
announcement.url = "/blog#{announcement.url}"
|
||||||
|
announcement.date = new Date(announcement.date)
|
||||||
|
return announcement
|
||||||
|
callback(err, announcements)
|
|
@ -25,7 +25,7 @@ else if Settings?.email?.parameters?.sendgridApiKey?
|
||||||
logger.log "using sendgrid for email"
|
logger.log "using sendgrid for email"
|
||||||
nm_client = nodemailer.createTransport(sgTransport({auth:{api_key:Settings?.email?.parameters?.sendgridApiKey}}))
|
nm_client = nodemailer.createTransport(sgTransport({auth:{api_key:Settings?.email?.parameters?.sendgridApiKey}}))
|
||||||
else if Settings?.email?.parameters?
|
else if Settings?.email?.parameters?
|
||||||
smtp = _.pick(Settings?.email?.parameters, "host", "port", "secure", "auth")
|
smtp = _.pick(Settings?.email?.parameters, "host", "port", "secure", "auth", "ignoreTLS")
|
||||||
|
|
||||||
|
|
||||||
logger.log "using smtp for email"
|
logger.log "using smtp for email"
|
||||||
|
|
|
@ -64,7 +64,7 @@ Reporter = (res) ->
|
||||||
res.contentType("application/json")
|
res.contentType("application/json")
|
||||||
if failures.length > 0
|
if failures.length > 0
|
||||||
logger.err failures:failures, "health check failed"
|
logger.err failures:failures, "health check failed"
|
||||||
res.send 500, JSON.stringify(results, null, 2)
|
res.status(500).send(JSON.stringify(results, null, 2))
|
||||||
else
|
else
|
||||||
res.send 200, JSON.stringify(results, null, 2)
|
res.status(200).send(JSON.stringify(results, null, 2))
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,8 @@ module.exports = ProjectController =
|
||||||
return next(err)
|
return next(err)
|
||||||
logger.log results:results, user_id:user_id, "rendering project list"
|
logger.log results:results, user_id:user_id, "rendering project list"
|
||||||
tags = results.tags[0]
|
tags = results.tags[0]
|
||||||
|
|
||||||
|
|
||||||
notifications = require("underscore").map results.notifications, (notification)->
|
notifications = require("underscore").map results.notifications, (notification)->
|
||||||
notification.html = req.i18n.translate(notification.templateKey, notification.messageOpts)
|
notification.html = req.i18n.translate(notification.templateKey, notification.messageOpts)
|
||||||
return notification
|
return notification
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
Settings = require("settings-sharelatex")
|
|
||||||
redis = require("redis-sharelatex")
|
|
||||||
rclient = redis.createClient(Settings.redis.web)
|
|
||||||
crypto = require("crypto")
|
|
||||||
async = require("async")
|
|
||||||
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
|
|
||||||
_getEmailKey : (email)->
|
|
||||||
hash = crypto.createHash("md5").update(email).digest("hex")
|
|
||||||
return "e_sess:#{hash}"
|
|
||||||
|
|
||||||
tracksession:(sessionId, email, callback = ->)->
|
|
||||||
session_lookup_key = @_getEmailKey(email)
|
|
||||||
rclient.set session_lookup_key, sessionId, callback
|
|
||||||
|
|
||||||
invalidateSession:(email, callback = ->)->
|
|
||||||
session_lookup_key = @_getEmailKey(email)
|
|
||||||
rclient.get session_lookup_key, (err, sessionId)->
|
|
||||||
async.series [
|
|
||||||
(cb)-> rclient.del sessionId, cb
|
|
||||||
(cb)-> rclient.del session_lookup_key, cb
|
|
||||||
], callback
|
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ module.exports = UserController =
|
||||||
|
|
||||||
updateUserSettings : (req, res)->
|
updateUserSettings : (req, res)->
|
||||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
usingExternalAuth = settings.ldap? or settings.saml?
|
||||||
logger.log user_id: user_id, "updating account settings"
|
logger.log user_id: user_id, "updating account settings"
|
||||||
User.findById user_id, (err, user)->
|
User.findById user_id, (err, user)->
|
||||||
if err? or !user?
|
if err? or !user?
|
||||||
|
@ -74,12 +75,15 @@ module.exports = UserController =
|
||||||
user.ace.syntaxValidation = req.body.syntaxValidation
|
user.ace.syntaxValidation = req.body.syntaxValidation
|
||||||
user.save (err)->
|
user.save (err)->
|
||||||
newEmail = req.body.email?.trim().toLowerCase()
|
newEmail = req.body.email?.trim().toLowerCase()
|
||||||
if !newEmail? or newEmail == user.email
|
if !newEmail? or newEmail == user.email or usingExternalAuth
|
||||||
|
# end here, don't update email
|
||||||
AuthenticationController.setInSessionUser(req, {first_name: user.first_name, last_name: user.last_name})
|
AuthenticationController.setInSessionUser(req, {first_name: user.first_name, last_name: user.last_name})
|
||||||
return res.sendStatus 200
|
return res.sendStatus 200
|
||||||
else if newEmail.indexOf("@") == -1
|
else if newEmail.indexOf("@") == -1
|
||||||
|
# email invalid
|
||||||
return res.sendStatus(400)
|
return res.sendStatus(400)
|
||||||
else
|
else
|
||||||
|
# update the user email
|
||||||
UserUpdater.changeEmailAddress user_id, newEmail, (err)->
|
UserUpdater.changeEmailAddress user_id, newEmail, (err)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, user_id:user_id, newEmail:newEmail, "problem updaing users email address"
|
logger.err err:err, user_id:user_id, newEmail:newEmail, "problem updaing users email address"
|
||||||
|
|
|
@ -3,14 +3,11 @@ redis = require('redis-sharelatex')
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
Async = require('async')
|
Async = require('async')
|
||||||
_ = require('underscore')
|
_ = require('underscore')
|
||||||
|
UserSessionsRedis = require('./UserSessionsRedis')
|
||||||
|
|
||||||
rclient = redis.createClient(Settings.redis.web)
|
rclient = UserSessionsRedis.client()
|
||||||
|
|
||||||
module.exports = UserSessionsManager =
|
module.exports = UserSessionsManager =
|
||||||
|
|
||||||
_sessionSetKey: (user) ->
|
|
||||||
return "UserSessions:#{user._id}"
|
|
||||||
|
|
||||||
# mimic the key used by the express sessions
|
# mimic the key used by the express sessions
|
||||||
_sessionKey: (sessionId) ->
|
_sessionKey: (sessionId) ->
|
||||||
return "sess:#{sessionId}"
|
return "sess:#{sessionId}"
|
||||||
|
@ -23,7 +20,7 @@ module.exports = UserSessionsManager =
|
||||||
logger.log {user_id: user._id}, "no sessionId to track, returning"
|
logger.log {user_id: user._id}, "no sessionId to track, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
logger.log {user_id: user._id, sessionId}, "onLogin handler"
|
logger.log {user_id: user._id, sessionId}, "onLogin handler"
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
value = UserSessionsManager._sessionKey sessionId
|
value = UserSessionsManager._sessionKey sessionId
|
||||||
rclient.multi()
|
rclient.multi()
|
||||||
.sadd(sessionSetKey, value)
|
.sadd(sessionSetKey, value)
|
||||||
|
@ -43,7 +40,7 @@ module.exports = UserSessionsManager =
|
||||||
logger.log {user_id: user._id}, "no sessionId to untrack, returning"
|
logger.log {user_id: user._id}, "no sessionId to untrack, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
logger.log {user_id: user._id, sessionId}, "onLogout handler"
|
logger.log {user_id: user._id, sessionId}, "onLogout handler"
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
value = UserSessionsManager._sessionKey sessionId
|
value = UserSessionsManager._sessionKey sessionId
|
||||||
rclient.multi()
|
rclient.multi()
|
||||||
.srem(sessionSetKey, value)
|
.srem(sessionSetKey, value)
|
||||||
|
@ -57,7 +54,7 @@ module.exports = UserSessionsManager =
|
||||||
|
|
||||||
getAllUserSessions: (user, exclude, callback=(err, sessionKeys)->) ->
|
getAllUserSessions: (user, exclude, callback=(err, sessionKeys)->) ->
|
||||||
exclude = _.map(exclude, UserSessionsManager._sessionKey)
|
exclude = _.map(exclude, UserSessionsManager._sessionKey)
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err user_id: user._id, "error getting all session keys for user from redis"
|
logger.err user_id: user._id, "error getting all session keys for user from redis"
|
||||||
|
@ -66,7 +63,8 @@ module.exports = UserSessionsManager =
|
||||||
if sessionKeys.length == 0
|
if sessionKeys.length == 0
|
||||||
logger.log {user_id: user._id}, "no other sessions found, returning"
|
logger.log {user_id: user._id}, "no other sessions found, returning"
|
||||||
return callback(null, [])
|
return callback(null, [])
|
||||||
rclient.mget sessionKeys, (err, sessions) ->
|
|
||||||
|
Async.mapSeries sessionKeys, ((k, cb) -> rclient.get(k, cb)), (err, sessions) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {user_id: user._id}, "error getting all sessions for user from redis"
|
logger.err {user_id: user._id}, "error getting all sessions for user from redis"
|
||||||
return callback(err)
|
return callback(err)
|
||||||
|
@ -92,7 +90,7 @@ module.exports = UserSessionsManager =
|
||||||
logger.log {}, "no user to revoke sessions for, returning"
|
logger.log {}, "no user to revoke sessions for, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
logger.log {user_id: user._id}, "revoking all existing sessions for user"
|
logger.log {user_id: user._id}, "revoking all existing sessions for user"
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
||||||
|
@ -102,12 +100,18 @@ module.exports = UserSessionsManager =
|
||||||
logger.log {user_id: user._id}, "no sessions in UserSessions set to delete, returning"
|
logger.log {user_id: user._id}, "no sessions in UserSessions set to delete, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
logger.log {user_id: user._id, count: keysToDelete.length}, "deleting sessions for user"
|
logger.log {user_id: user._id, count: keysToDelete.length}, "deleting sessions for user"
|
||||||
rclient.multi()
|
|
||||||
.del(keysToDelete)
|
deletions = keysToDelete.map (k) ->
|
||||||
.srem(sessionSetKey, keysToDelete)
|
(cb) ->
|
||||||
.exec (err, result) ->
|
rclient.del k, cb
|
||||||
|
|
||||||
|
Async.series deletions, (err, _result) ->
|
||||||
|
if err?
|
||||||
|
logger.err {err, user_id: user._id, sessionSetKey}, "errror revoking all sessions for user"
|
||||||
|
return callback(err)
|
||||||
|
rclient.srem sessionSetKey, keysToDelete, (err) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {err, user_id: user._id, sessionSetKey}, "error revoking all sessions for user"
|
logger.err {err, user_id: user._id, sessionSetKey}, "error removing session set for user"
|
||||||
return callback(err)
|
return callback(err)
|
||||||
callback(null)
|
callback(null)
|
||||||
|
|
||||||
|
@ -115,7 +119,7 @@ module.exports = UserSessionsManager =
|
||||||
if !user
|
if !user
|
||||||
logger.log {}, "no user to touch sessions for, returning"
|
logger.log {}, "no user to touch sessions for, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
rclient.expire sessionSetKey, "#{Settings.cookieSessionLength}", (err, response) ->
|
rclient.expire sessionSetKey, "#{Settings.cookieSessionLength}", (err, response) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {err, user_id: user._id}, "error while updating ttl on UserSessions set"
|
logger.err {err, user_id: user._id}, "error while updating ttl on UserSessions set"
|
||||||
|
@ -127,7 +131,7 @@ module.exports = UserSessionsManager =
|
||||||
logger.log {}, "no user, returning"
|
logger.log {}, "no user, returning"
|
||||||
return callback(null)
|
return callback(null)
|
||||||
logger.log {user_id: user._id}, "checking sessions for user"
|
logger.log {user_id: user._id}, "checking sessions for user"
|
||||||
sessionSetKey = UserSessionsManager._sessionSetKey(user)
|
sessionSetKey = UserSessionsRedis.sessionSetKey(user)
|
||||||
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
rclient.smembers sessionSetKey, (err, sessionKeys) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
logger.err {err, user_id: user._id, sessionSetKey}, "error getting contents of UserSessions set"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
Settings = require 'settings-sharelatex'
|
||||||
|
redis = require 'redis-sharelatex'
|
||||||
|
ioredis = require 'ioredis'
|
||||||
|
logger = require 'logger-sharelatex'
|
||||||
|
|
||||||
|
redisSessionsSettings = Settings.redis.websessions or Settings.redis.web
|
||||||
|
|
||||||
|
module.exports = Redis =
|
||||||
|
client: () ->
|
||||||
|
if redisSessionsSettings?.cluster?
|
||||||
|
logger.log {}, "using redis cluster for web sessions"
|
||||||
|
rclient = new ioredis.Cluster(redisSessionsSettings.cluster)
|
||||||
|
else
|
||||||
|
rclient = redis.createClient(redisSessionsSettings)
|
||||||
|
return rclient
|
||||||
|
|
||||||
|
sessionSetKey: (user) ->
|
||||||
|
if redisSessionsSettings?.cluster?
|
||||||
|
return "UserSessions:{#{user._id}}"
|
||||||
|
else
|
||||||
|
return "UserSessions:#{user._id}"
|
|
@ -210,7 +210,7 @@ module.exports = (app, webRouter, apiRouter)->
|
||||||
|
|
||||||
webRouter.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.externalAuthenticationSystemUsed = ->
|
res.locals.externalAuthenticationSystemUsed = ->
|
||||||
Settings.ldap?
|
Settings.ldap? or Settings.saml?
|
||||||
next()
|
next()
|
||||||
|
|
||||||
webRouter.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
|
|
|
@ -18,6 +18,10 @@ module.exports = Modules =
|
||||||
applyRouter: (webRouter, apiRouter) ->
|
applyRouter: (webRouter, apiRouter) ->
|
||||||
for module in @modules
|
for module in @modules
|
||||||
module.router?.apply(webRouter, apiRouter)
|
module.router?.apply(webRouter, apiRouter)
|
||||||
|
|
||||||
|
applyNonCsrfRouter: (webRouter, apiRouter) ->
|
||||||
|
for module in @modules
|
||||||
|
module.nonCsrfRouter?.apply(webRouter, apiRouter)
|
||||||
|
|
||||||
viewIncludes: {}
|
viewIncludes: {}
|
||||||
loadViewIncludes: (app) ->
|
loadViewIncludes: (app) ->
|
||||||
|
@ -58,4 +62,4 @@ module.exports = Modules =
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
return callback null, results
|
return callback null, results
|
||||||
|
|
||||||
Modules.loadModules()
|
Modules.loadModules()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
version = {
|
version = {
|
||||||
"pdfjs": "1.6.210p1"
|
"pdfjs": "1.6.210p2"
|
||||||
"moment": "2.9.0"
|
"moment": "2.9.0"
|
||||||
"ace": "1.2.5"
|
"ace": "1.2.5"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ expressLocals = require('./ExpressLocals')
|
||||||
Router = require('../router')
|
Router = require('../router')
|
||||||
metrics.inc("startup")
|
metrics.inc("startup")
|
||||||
redis = require("redis-sharelatex")
|
redis = require("redis-sharelatex")
|
||||||
rclient = redis.createClient(Settings.redis.web)
|
UserSessionsRedis = require('../Features/User/UserSessionsRedis')
|
||||||
|
|
||||||
|
sessionsRedisClient = UserSessionsRedis.client()
|
||||||
|
|
||||||
session = require("express-session")
|
session = require("express-session")
|
||||||
RedisStore = require('connect-redis')(session)
|
RedisStore = require('connect-redis')(session)
|
||||||
|
@ -19,7 +21,8 @@ csrf = require('csurf')
|
||||||
csrfProtection = csrf()
|
csrfProtection = csrf()
|
||||||
cookieParser = require('cookie-parser')
|
cookieParser = require('cookie-parser')
|
||||||
|
|
||||||
sessionStore = new RedisStore(client:rclient)
|
# Init the session store
|
||||||
|
sessionStore = new RedisStore(client:sessionsRedisClient)
|
||||||
|
|
||||||
passport = require('passport')
|
passport = require('passport')
|
||||||
LocalStrategy = require('passport-local').Strategy
|
LocalStrategy = require('passport-local').Strategy
|
||||||
|
@ -87,9 +90,6 @@ webRouter.use session
|
||||||
secure: Settings.secureCookie
|
secure: Settings.secureCookie
|
||||||
store: sessionStore
|
store: sessionStore
|
||||||
key: Settings.cookieName
|
key: Settings.cookieName
|
||||||
webRouter.use csrfProtection
|
|
||||||
webRouter.use translations.expressMiddlewear
|
|
||||||
webRouter.use translations.setLangBasedOnDomainMiddlewear
|
|
||||||
|
|
||||||
# passport
|
# passport
|
||||||
webRouter.use passport.initialize()
|
webRouter.use passport.initialize()
|
||||||
|
@ -110,6 +110,12 @@ Modules.hooks.fire 'passportSetup', passport, (err) ->
|
||||||
if err?
|
if err?
|
||||||
logger.err {err}, "error setting up passport in modules"
|
logger.err {err}, "error setting up passport in modules"
|
||||||
|
|
||||||
|
Modules.applyNonCsrfRouter(webRouter, apiRouter)
|
||||||
|
|
||||||
|
webRouter.use csrfProtection
|
||||||
|
webRouter.use translations.expressMiddlewear
|
||||||
|
webRouter.use translations.setLangBasedOnDomainMiddlewear
|
||||||
|
|
||||||
# Measure expiry from last request, not last login
|
# Measure expiry from last request, not last login
|
||||||
webRouter.use (req, res, next) ->
|
webRouter.use (req, res, next) ->
|
||||||
req.session.touch()
|
req.session.touch()
|
||||||
|
|
|
@ -39,6 +39,7 @@ ReferencesController = require('./Features/References/ReferencesController')
|
||||||
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
|
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
|
||||||
BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
|
BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
|
||||||
AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
|
AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
|
||||||
|
AnnouncementsController = require("./Features/Announcements/AnnouncementsController")
|
||||||
|
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
|
@ -187,6 +188,9 @@ module.exports = class Router
|
||||||
webRouter.get '/notifications', AuthenticationController.requireLogin(), NotificationsController.getAllUnreadNotifications
|
webRouter.get '/notifications', AuthenticationController.requireLogin(), NotificationsController.getAllUnreadNotifications
|
||||||
webRouter.delete '/notifications/:notification_id', AuthenticationController.requireLogin(), NotificationsController.markNotificationAsRead
|
webRouter.delete '/notifications/:notification_id', AuthenticationController.requireLogin(), NotificationsController.markNotificationAsRead
|
||||||
|
|
||||||
|
webRouter.get '/announcements', AuthenticationController.requireLogin(), AnnouncementsController.getUndreadAnnouncements
|
||||||
|
|
||||||
|
|
||||||
# Deprecated in favour of /internal/project/:project_id but still used by versioning
|
# Deprecated in favour of /internal/project/:project_id but still used by versioning
|
||||||
apiRouter.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
|
apiRouter.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
head
|
head
|
||||||
title Something went wrong
|
title Something went wrong
|
||||||
link(rel="icon", href="/favicon.ico")
|
link(rel="icon", href="/favicon.ico")
|
||||||
link(rel='stylesheet', href=buildCssPath('/style.css'))
|
if buildCssPath
|
||||||
|
link(rel='stylesheet', href=buildCssPath('/style.css'))
|
||||||
link(href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css",rel="stylesheet")
|
link(href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css",rel="stylesheet")
|
||||||
body
|
body
|
||||||
.content
|
.content
|
||||||
|
@ -12,7 +13,9 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
.col-md-8.col-md-offset-2.text-center
|
.col-md-8.col-md-offset-2.text-center
|
||||||
.page-header
|
.page-header
|
||||||
h2 Oh dear, something went wrong.
|
h2 Oh dear, something went wrong.
|
||||||
p: img(src=buildImgPath("lion-sad-128.png"), alt="Sad Lion")
|
if buildImgPath
|
||||||
|
p
|
||||||
|
img(src=buildImgPath("lion-sad-128.png"), alt="Sad Lion")
|
||||||
p
|
p
|
||||||
| Something went wrong with your request, sorry. Our staff are probably looking into this, but if it continues, please contact us at #{settings.adminEmail}
|
| Something went wrong with your request, sorry. Our staff are probably looking into this, but if it continues, please contact us at #{settings.adminEmail}
|
||||||
p
|
p
|
||||||
|
|
|
@ -1,388 +1,125 @@
|
||||||
div(ng-if="!shouldABTestHeaderLabels")
|
header.toolbar.toolbar-header.toolbar-with-labels(
|
||||||
header.toolbar.toolbar-header(
|
ng-cloak,
|
||||||
ng-cloak,
|
ng-hide="state.loading"
|
||||||
ng-hide="state.loading"
|
)
|
||||||
)
|
.toolbar-left
|
||||||
a.btn.btn-full-height(
|
a.btn.btn-full-height(
|
||||||
href,
|
href,
|
||||||
ng-click="ui.leftMenuShown = true"
|
ng-click="ui.leftMenuShown = true;",
|
||||||
tooltip='#{translate("menu")}',
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-bars
|
i.fa.fa-fw.fa-bars
|
||||||
|
p.toolbar-label #{translate("menu")}
|
||||||
a(
|
a(
|
||||||
href="/project"
|
href="/project"
|
||||||
tooltip="#{translate('back_to_projects')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true"
|
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-level-up
|
i.fa.fa-fw.fa-level-up
|
||||||
|
|
||||||
span(ng-controller="PdfViewToggleController")
|
|
||||||
a(
|
|
||||||
href,
|
|
||||||
ng-show="ui.pdfLayout == 'flat' && fileTreeClosed",
|
|
||||||
tooltip="PDF",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
ng-click="togglePdfView()",
|
|
||||||
ng-class="{ 'active': ui.view == 'pdf' }"
|
|
||||||
)
|
|
||||||
i.fa.fa-file-pdf-o
|
|
||||||
|
|
||||||
.toolbar-center.project-name(ng-controller="ProjectNameController")
|
span(ng-controller="PdfViewToggleController")
|
||||||
span.name(
|
a(
|
||||||
ng-dblclick="!permissions.admin || startRenaming()",
|
href,
|
||||||
ng-show="!state.renaming"
|
ng-show="ui.pdfLayout == 'flat' && fileTreeClosed",
|
||||||
) {{ project.name }}
|
tooltip="PDF",
|
||||||
|
tooltip-placement="bottom",
|
||||||
input.form-control(
|
tooltip-append-to-body="true",
|
||||||
type="text"
|
ng-click="togglePdfView()",
|
||||||
ng-model="inputs.name",
|
ng-class="{ 'active': ui.view == 'pdf' }"
|
||||||
ng-show="state.renaming",
|
|
||||||
on-enter="finishRenaming()",
|
|
||||||
ng-blur="finishRenaming()",
|
|
||||||
select-name-when="state.renaming"
|
|
||||||
)
|
|
||||||
|
|
||||||
a.rename(
|
|
||||||
ng-if="permissions.admin",
|
|
||||||
href='#',
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip="#{translate('rename')}",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
ng-click="startRenaming()",
|
|
||||||
ng-show="!state.renaming"
|
|
||||||
)
|
|
||||||
i.fa.fa-pencil
|
|
||||||
|
|
||||||
.toolbar-right
|
|
||||||
span.online-users(
|
|
||||||
ng-show="onlineUsersArray.length > 0"
|
|
||||||
ng-controller="OnlineUsersController"
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
ng-if="trackChangesFeatureFlag",
|
|
||||||
ng-class="{ active: ui.reviewPanelOpen }"
|
|
||||||
tooltip="#{translate('review')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
ng-click="toggleReviewPanel()"
|
|
||||||
)
|
|
||||||
| Review
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
ng-if="permissions.admin",
|
|
||||||
tooltip="#{translate('share')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
ng-click="openShareProjectModal()",
|
|
||||||
ng-controller="ShareController",
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-group
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
ng-click="toggleHistory()",
|
|
||||||
ng-class="{ active: (ui.view == 'history') }"
|
|
||||||
tooltip="#{translate('recent_changes')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-history
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
tooltip="#{translate('chat')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
ng-class="{ active: ui.chatOpen }",
|
|
||||||
ng-click="toggleChat()",
|
|
||||||
ng-controller="ChatButtonController",
|
|
||||||
ng-show="!anonymous",
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-comment(
|
|
||||||
ng-class="{ 'bounce': unreadMessages > 0 }"
|
|
||||||
)
|
|
||||||
span.label.label-info(
|
|
||||||
ng-show="unreadMessages > 0"
|
|
||||||
) {{ unreadMessages }}
|
|
||||||
|
|
||||||
div(ng-if="shouldABTestHeaderLabels")
|
|
||||||
div(sixpack-switch="editor-header")
|
|
||||||
header.toolbar.toolbar-header(
|
|
||||||
ng-cloak,
|
|
||||||
ng-hide="state.loading"
|
|
||||||
sixpack-default
|
|
||||||
)
|
)
|
||||||
a.btn.btn-full-height(
|
i.fa.fa-file-pdf-o
|
||||||
href,
|
|
||||||
ng-click="ui.leftMenuShown = true; trackABTestConversion('menu');"
|
|
||||||
tooltip='#{translate("menu")}',
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-bars
|
|
||||||
a(
|
|
||||||
href="/project"
|
|
||||||
tooltip="#{translate('back_to_projects')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-level-up
|
|
||||||
|
|
||||||
span(ng-controller="PdfViewToggleController")
|
|
||||||
a(
|
|
||||||
href,
|
|
||||||
ng-show="ui.pdfLayout == 'flat' && fileTreeClosed",
|
|
||||||
tooltip="PDF",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
ng-click="togglePdfView()",
|
|
||||||
ng-class="{ 'active': ui.view == 'pdf' }"
|
|
||||||
)
|
|
||||||
i.fa.fa-file-pdf-o
|
|
||||||
|
|
||||||
.toolbar-center.project-name(ng-controller="ProjectNameController")
|
.toolbar-center.project-name(ng-controller="ProjectNameController")
|
||||||
span.name(
|
span.name(
|
||||||
ng-dblclick="!permissions.admin || startRenaming()",
|
ng-dblclick="!permissions.admin || startRenaming()",
|
||||||
ng-show="!state.renaming"
|
ng-show="!state.renaming"
|
||||||
) {{ project.name }}
|
) {{ project.name }}
|
||||||
|
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type="text"
|
type="text"
|
||||||
ng-model="inputs.name",
|
ng-model="inputs.name",
|
||||||
ng-show="state.renaming",
|
ng-show="state.renaming",
|
||||||
on-enter="finishRenaming()",
|
on-enter="finishRenaming()",
|
||||||
ng-blur="finishRenaming()",
|
ng-blur="finishRenaming()",
|
||||||
select-name-when="state.renaming"
|
select-name-when="state.renaming"
|
||||||
)
|
|
||||||
|
|
||||||
a.rename(
|
|
||||||
ng-if="permissions.admin",
|
|
||||||
href='#',
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip="#{translate('rename')}",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
ng-click="startRenaming()",
|
|
||||||
ng-show="!state.renaming"
|
|
||||||
)
|
|
||||||
i.fa.fa-pencil
|
|
||||||
|
|
||||||
.toolbar-right
|
|
||||||
span.online-users(
|
|
||||||
ng-show="onlineUsersArray.length > 0"
|
|
||||||
ng-controller="OnlineUsersController"
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
ng-if="permissions.admin",
|
|
||||||
tooltip="#{translate('share')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
ng-click="openShareProjectModal(); trackABTestConversion('share');",
|
|
||||||
ng-controller="ShareController",
|
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-group
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
ng-click="toggleHistory(); trackABTestConversion('history');",
|
|
||||||
ng-class="{ active: (ui.view == 'history') }"
|
|
||||||
tooltip="#{translate('recent_changes')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-history
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
tooltip="#{translate('chat')}",
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
ng-class="{ active: ui.chatOpen }",
|
|
||||||
ng-click="toggleChat(); trackABTestConversion('chat');",
|
|
||||||
ng-controller="ChatButtonController",
|
|
||||||
ng-show="!anonymous",
|
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-comment(
|
|
||||||
ng-class="{ 'bounce': unreadMessages > 0 }"
|
|
||||||
)
|
|
||||||
span.label.label-info(
|
|
||||||
ng-show="unreadMessages > 0"
|
|
||||||
) {{ unreadMessages }}
|
|
||||||
|
|
||||||
header.toolbar.toolbar-header.toolbar-with-labels(
|
|
||||||
ng-cloak,
|
|
||||||
ng-hide="state.loading"
|
|
||||||
sixpack-when="labels"
|
|
||||||
)
|
)
|
||||||
.toolbar-left
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
ng-click="ui.leftMenuShown = true; trackABTestConversion('menu');",
|
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-bars
|
|
||||||
p.toolbar-label #{translate("menu")}
|
|
||||||
a(
|
|
||||||
href="/project"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-level-up
|
|
||||||
|
|
||||||
span(ng-controller="PdfViewToggleController")
|
a.rename(
|
||||||
a(
|
ng-if="permissions.admin",
|
||||||
href,
|
href='#',
|
||||||
ng-show="ui.pdfLayout == 'flat' && fileTreeClosed",
|
tooltip-placement="bottom",
|
||||||
tooltip="PDF",
|
tooltip="#{translate('rename')}",
|
||||||
tooltip-placement="bottom",
|
tooltip-append-to-body="true",
|
||||||
tooltip-append-to-body="true",
|
ng-click="startRenaming()",
|
||||||
ng-click="togglePdfView()",
|
ng-show="!state.renaming"
|
||||||
ng-class="{ 'active': ui.view == 'pdf' }"
|
)
|
||||||
)
|
i.fa.fa-pencil
|
||||||
i.fa.fa-file-pdf-o
|
|
||||||
|
|
||||||
.toolbar-center.project-name(ng-controller="ProjectNameController")
|
|
||||||
span.name(
|
|
||||||
ng-dblclick="!permissions.admin || startRenaming()",
|
|
||||||
ng-show="!state.renaming"
|
|
||||||
) {{ project.name }}
|
|
||||||
|
|
||||||
input.form-control(
|
|
||||||
type="text"
|
|
||||||
ng-model="inputs.name",
|
|
||||||
ng-show="state.renaming",
|
|
||||||
on-enter="finishRenaming()",
|
|
||||||
ng-blur="finishRenaming()",
|
|
||||||
select-name-when="state.renaming"
|
|
||||||
)
|
|
||||||
|
|
||||||
a.rename(
|
|
||||||
ng-if="permissions.admin",
|
|
||||||
href='#',
|
|
||||||
tooltip-placement="bottom",
|
|
||||||
tooltip="#{translate('rename')}",
|
|
||||||
tooltip-append-to-body="true",
|
|
||||||
ng-click="startRenaming()",
|
|
||||||
ng-show="!state.renaming"
|
|
||||||
)
|
|
||||||
i.fa.fa-pencil
|
|
||||||
|
|
||||||
.toolbar-right
|
|
||||||
span.online-users(
|
|
||||||
ng-show="onlineUsersArray.length > 0"
|
|
||||||
ng-controller="OnlineUsersController"
|
|
||||||
)
|
|
||||||
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 }}
|
|
||||||
|
|
||||||
|
.toolbar-right
|
||||||
|
span.online-users(
|
||||||
|
ng-show="onlineUsersArray.length > 0"
|
||||||
|
ng-controller="OnlineUsersController"
|
||||||
|
)
|
||||||
|
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) }}
|
||||||
|
|
||||||
a.btn.btn-full-height(
|
span.dropdown(dropdown, ng-if="onlineUsersArray.length >= 4")
|
||||||
href,
|
span.online-user.online-user-multi(
|
||||||
ng-if="permissions.admin",
|
dropdown-toggle,
|
||||||
ng-click="openShareProjectModal(); trackABTestConversion('share');",
|
tooltip="#{translate('connected_users')}",
|
||||||
ng-controller="ShareController",
|
tooltip-placement="left"
|
||||||
sixpack-convert="editor-header"
|
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-group
|
strong {{ onlineUsersArray.length }}
|
||||||
p.toolbar-label #{translate("share")}
|
i.fa.fa-fw.fa-user
|
||||||
a.btn.btn-full-height(
|
ul.dropdown-menu.pull-right
|
||||||
href,
|
li.dropdown-header #{translate('connected_users')}
|
||||||
ng-click="toggleHistory(); trackABTestConversion('history');",
|
li(ng-repeat="user in onlineUsersArray")
|
||||||
ng-class="{ active: (ui.view == 'history') }",
|
a(href, ng-click="gotoUser(user)")
|
||||||
sixpack-convert="editor-header"
|
span.online-user(
|
||||||
)
|
ng-style="{ 'background-color': 'hsl({{ getHueForUserId(user.user_id) }}, 70%, 50%)' }"
|
||||||
i.fa.fa-fw.fa-history
|
) {{ user.name.slice(0,1) }}
|
||||||
p.toolbar-label #{translate("history")}
|
| {{ user.name }}
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
a.btn.btn-full-height(
|
||||||
ng-class="{ active: ui.chatOpen }",
|
href,
|
||||||
ng-click="toggleChat(); trackABTestConversion('chat');",
|
ng-if="trackChangesFeatureFlag",
|
||||||
ng-controller="ChatButtonController",
|
ng-class="{ active: ui.reviewPanelOpen }"
|
||||||
ng-show="!anonymous",
|
tooltip="#{translate('review')}",
|
||||||
sixpack-convert="editor-header"
|
tooltip-placement="bottom",
|
||||||
)
|
ng-click="toggleReviewPanel()"
|
||||||
i.fa.fa-fw.fa-comment(
|
)
|
||||||
ng-class="{ 'bounce': unreadMessages > 0 }"
|
| Review
|
||||||
)
|
a.btn.btn-full-height(
|
||||||
span.label.label-info(
|
href,
|
||||||
ng-show="unreadMessages > 0"
|
ng-if="permissions.admin",
|
||||||
) {{ unreadMessages }}
|
ng-click="openShareProjectModal();",
|
||||||
p.toolbar-label #{translate("chat")}
|
ng-controller="ShareController",
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-group
|
||||||
|
p.toolbar-label #{translate("share")}
|
||||||
|
a.btn.btn-full-height(
|
||||||
|
href,
|
||||||
|
ng-click="toggleHistory();",
|
||||||
|
ng-class="{ active: (ui.view == 'history') }",
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-history
|
||||||
|
p.toolbar-label #{translate("history")}
|
||||||
|
a.btn.btn-full-height(
|
||||||
|
href,
|
||||||
|
ng-class="{ active: ui.chatOpen }",
|
||||||
|
ng-click="toggleChat();",
|
||||||
|
ng-controller="ChatButtonController",
|
||||||
|
ng-show="!anonymous",
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-comment(
|
||||||
|
ng-class="{ 'bounce': unreadMessages > 0 }"
|
||||||
|
)
|
||||||
|
span.label.label-info(
|
||||||
|
ng-show="unreadMessages > 0"
|
||||||
|
) {{ unreadMessages }}
|
||||||
|
p.toolbar-label #{translate("chat")}
|
|
@ -21,7 +21,11 @@ block content
|
||||||
|
|
||||||
.content.content-alt(ng-controller="ProjectPageController")
|
.content.content-alt(ng-controller="ProjectPageController")
|
||||||
.container
|
.container
|
||||||
|
|
||||||
|
//- div(ng-controller="AnnouncementsController", ng-cloak)
|
||||||
|
//- .alert.alert-success(ng-show="dataRecived")
|
||||||
|
//- a(href, ng-click="openLink()") {{title}} and {{totalAnnouncements}} others
|
||||||
|
|
||||||
.row(ng-cloak)
|
.row(ng-cloak)
|
||||||
span(ng-if="projects.length > 0")
|
span(ng-if="projects.length > 0")
|
||||||
aside.col-md-2.col-xs-3
|
aside.col-md-2.col-xs-3
|
||||||
|
|
|
@ -33,6 +33,12 @@ block content
|
||||||
)
|
)
|
||||||
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
|
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
|
||||||
| #{translate("must_be_email_address")}
|
| #{translate("must_be_email_address")}
|
||||||
|
else
|
||||||
|
// show the email, non-editable
|
||||||
|
.form-group
|
||||||
|
label.control-label #{translate("email")}
|
||||||
|
div.form-control(readonly="true") #{user.email}
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label(for='firstName').control-label #{translate("first_name")}
|
label(for='firstName').control-label #{translate("first_name")}
|
||||||
input.form-control(
|
input.form-control(
|
||||||
|
|
|
@ -38,6 +38,16 @@ module.exports = settings =
|
||||||
port: "6379"
|
port: "6379"
|
||||||
password: ""
|
password: ""
|
||||||
|
|
||||||
|
# websessions:
|
||||||
|
# cluster: [
|
||||||
|
# {host: 'localhost', port: 7000}
|
||||||
|
# {host: 'localhost', port: 7001}
|
||||||
|
# {host: 'localhost', port: 7002}
|
||||||
|
# {host: 'localhost', port: 7003}
|
||||||
|
# {host: 'localhost', port: 7004}
|
||||||
|
# {host: 'localhost', port: 7005}
|
||||||
|
# ]
|
||||||
|
|
||||||
api:
|
api:
|
||||||
host: "localhost"
|
host: "localhost"
|
||||||
port: "6379"
|
port: "6379"
|
||||||
|
@ -107,8 +117,8 @@ module.exports = settings =
|
||||||
# references:
|
# references:
|
||||||
# url: "http://localhost:3040"
|
# url: "http://localhost:3040"
|
||||||
notifications:
|
notifications:
|
||||||
url: "http://localhost:3042"
|
url: "http://localhost:3042"
|
||||||
|
|
||||||
templates:
|
templates:
|
||||||
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
|
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
|
||||||
showSocialButtons: false
|
showSocialButtons: false
|
||||||
|
@ -131,13 +141,13 @@ module.exports = settings =
|
||||||
|
|
||||||
# this is only used if cookies are used for clsi backend
|
# this is only used if cookies are used for clsi backend
|
||||||
#clsiCookieKey: "clsiserver"
|
#clsiCookieKey: "clsiserver"
|
||||||
|
|
||||||
# Same, but with http auth credentials.
|
# Same, but with http auth credentials.
|
||||||
httpAuthSiteUrl: 'http://#{httpAuthUser}:#{httpAuthPass}@localhost:3000'
|
httpAuthSiteUrl: 'http://#{httpAuthUser}:#{httpAuthPass}@localhost:3000'
|
||||||
|
|
||||||
|
|
||||||
maxEntitiesPerProject: 2000
|
maxEntitiesPerProject: 2000
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
# --------
|
# --------
|
||||||
security:
|
security:
|
||||||
|
@ -166,12 +176,12 @@ module.exports = settings =
|
||||||
price: 0
|
price: 0
|
||||||
features: defaultFeatures
|
features: defaultFeatures
|
||||||
}]
|
}]
|
||||||
|
|
||||||
enableSubscriptions:false
|
enableSubscriptions:false
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
# ------
|
# ------
|
||||||
#
|
#
|
||||||
i18n:
|
i18n:
|
||||||
subdomainLang:
|
subdomainLang:
|
||||||
www: {lngCode:"en", url: siteUrl}
|
www: {lngCode:"en", url: siteUrl}
|
||||||
|
@ -180,7 +190,7 @@ module.exports = settings =
|
||||||
# Spelling languages
|
# Spelling languages
|
||||||
# ------------------
|
# ------------------
|
||||||
#
|
#
|
||||||
# You must have the corresponding aspell package installed to
|
# You must have the corresponding aspell package installed to
|
||||||
# be able to use a language.
|
# be able to use a language.
|
||||||
languages: [
|
languages: [
|
||||||
{name: "English", code: "en"},
|
{name: "English", code: "en"},
|
||||||
|
@ -228,7 +238,7 @@ module.exports = settings =
|
||||||
# analytics:
|
# analytics:
|
||||||
# ga:
|
# ga:
|
||||||
# token: ""
|
# token: ""
|
||||||
#
|
#
|
||||||
# ShareLaTeX's help desk is provided by tenderapp.com
|
# ShareLaTeX's help desk is provided by tenderapp.com
|
||||||
# tenderUrl: ""
|
# tenderUrl: ""
|
||||||
#
|
#
|
||||||
|
@ -262,10 +272,10 @@ module.exports = settings =
|
||||||
# then set this to true to allow it to correctly detect the forwarded IP
|
# then set this to true to allow it to correctly detect the forwarded IP
|
||||||
# address and http/https protocol information.
|
# address and http/https protocol information.
|
||||||
behindProxy: false
|
behindProxy: false
|
||||||
|
|
||||||
# Cookie max age (in milliseconds). Set to false for a browser session.
|
# Cookie max age (in milliseconds). Set to false for a browser session.
|
||||||
cookieSessionLength: 5 * 24 * 60 * 60 * 1000 # 5 days
|
cookieSessionLength: 5 * 24 * 60 * 60 * 1000 # 5 days
|
||||||
|
|
||||||
# Should we allow access to any page without logging in? This includes
|
# Should we allow access to any page without logging in? This includes
|
||||||
# public projects, /learn, /templates, about pages, etc.
|
# public projects, /learn, /templates, about pages, etc.
|
||||||
allowPublicAccess: if process.env["SHARELATEX_ALLOW_PUBLIC_ACCESS"] == 'true' then true else false
|
allowPublicAccess: if process.env["SHARELATEX_ALLOW_PUBLIC_ACCESS"] == 'true' then true else false
|
||||||
|
@ -273,10 +283,10 @@ module.exports = settings =
|
||||||
# Use a single compile directory for all users in a project
|
# Use a single compile directory for all users in a project
|
||||||
# (otherwise each user has their own directory)
|
# (otherwise each user has their own directory)
|
||||||
# disablePerUserCompiles: true
|
# disablePerUserCompiles: true
|
||||||
|
|
||||||
# Maximum size of text documents in the real-time editing system.
|
# Maximum size of text documents in the real-time editing system.
|
||||||
max_doc_length: 2 * 1024 * 1024 # 2mb
|
max_doc_length: 2 * 1024 * 1024 # 2mb
|
||||||
|
|
||||||
# Internal configs
|
# Internal configs
|
||||||
# ----------------
|
# ----------------
|
||||||
path:
|
path:
|
||||||
|
@ -285,11 +295,11 @@ module.exports = settings =
|
||||||
# them to disk here).
|
# them to disk here).
|
||||||
dumpFolder: Path.resolve __dirname + "/../data/dumpFolder"
|
dumpFolder: Path.resolve __dirname + "/../data/dumpFolder"
|
||||||
uploadFolder: Path.resolve __dirname + "/../data/uploads"
|
uploadFolder: Path.resolve __dirname + "/../data/uploads"
|
||||||
|
|
||||||
# Automatic Snapshots
|
# Automatic Snapshots
|
||||||
# -------------------
|
# -------------------
|
||||||
automaticSnapshots:
|
automaticSnapshots:
|
||||||
# How long should we wait after the user last edited to
|
# How long should we wait after the user last edited to
|
||||||
# take a snapshot?
|
# take a snapshot?
|
||||||
waitTimeAfterLastEdit: 5 * minutes
|
waitTimeAfterLastEdit: 5 * minutes
|
||||||
# Even if edits are still taking place, this is maximum
|
# Even if edits are still taking place, this is maximum
|
||||||
|
@ -305,13 +315,13 @@ module.exports = settings =
|
||||||
# user: ""
|
# user: ""
|
||||||
# password: ""
|
# password: ""
|
||||||
# projectId: ""
|
# projectId: ""
|
||||||
|
|
||||||
appName: "ShareLaTeX (Community Edition)"
|
appName: "ShareLaTeX (Community Edition)"
|
||||||
adminEmail: "placeholder@example.com"
|
adminEmail: "placeholder@example.com"
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
title: "ShareLaTeX Community Edition"
|
title: "ShareLaTeX Community Edition"
|
||||||
|
|
||||||
left_footer: [{
|
left_footer: [{
|
||||||
text: "Powered by <a href='https://www.sharelatex.com'>ShareLaTeX</a> © 2016"
|
text: "Powered by <a href='https://www.sharelatex.com'>ShareLaTeX</a> © 2016"
|
||||||
}]
|
}]
|
||||||
|
@ -377,11 +387,11 @@ module.exports = settings =
|
||||||
"/templates/index": "/templates/"
|
"/templates/index": "/templates/"
|
||||||
|
|
||||||
proxyUrls: {}
|
proxyUrls: {}
|
||||||
|
|
||||||
reloadModuleViewsOnEachRequest: true
|
reloadModuleViewsOnEachRequest: true
|
||||||
|
|
||||||
domainLicences: [
|
domainLicences: [
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
sixpack:
|
sixpack:
|
||||||
|
@ -390,12 +400,12 @@ module.exports = settings =
|
||||||
# ----------
|
# ----------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# LDAP
|
# LDAP
|
||||||
# ----------
|
# ----------
|
||||||
# Settings below use a working LDAP test server kindly provided by forumsys.com
|
# Settings below use a working LDAP test server kindly provided by forumsys.com
|
||||||
# When testing with forumsys.com use username = einstein and password = password
|
# When testing with forumsys.com use username = einstein and password = password
|
||||||
|
|
||||||
# ldap :
|
# ldap :
|
||||||
# host: 'ldap://ldap.forumsys.com'
|
# host: 'ldap://ldap.forumsys.com'
|
||||||
# dn: 'uid=:userKey,dc=example,dc=com'
|
# dn: 'uid=:userKey,dc=example,dc=com'
|
||||||
|
@ -406,13 +416,13 @@ module.exports = settings =
|
||||||
# placeholder: 'email@example.com'
|
# placeholder: 'email@example.com'
|
||||||
# emailAtt: 'mail'
|
# emailAtt: 'mail'
|
||||||
# anonymous: false
|
# anonymous: false
|
||||||
# adminDN: 'cn=read-only-admin,dc=example,dc=com'
|
# adminDN: 'cn=read-only-admin,dc=example,dc=com'
|
||||||
# adminPW: 'password'
|
# adminPW: 'password'
|
||||||
# starttls: true
|
# starttls: true
|
||||||
# tlsOptions:
|
# tlsOptions:
|
||||||
# rejectUnauthorized: false
|
# rejectUnauthorized: false
|
||||||
# ca: ['/etc/ldap/ca_certs.pem']
|
# ca: ['/etc/ldap/ca_certs.pem']
|
||||||
|
|
||||||
#templateLinks: [{
|
#templateLinks: [{
|
||||||
# name : "CV projects",
|
# name : "CV projects",
|
||||||
# url : "/templates/cv"
|
# url : "/templates/cv"
|
||||||
|
@ -420,5 +430,3 @@ module.exports = settings =
|
||||||
# name : "all projects",
|
# name : "all projects",
|
||||||
# url: "/templates/all"
|
# url: "/templates/all"
|
||||||
#}]
|
#}]
|
||||||
|
|
||||||
|
|
||||||
|
|
274
services/web/npm-shrinkwrap.json
generated
274
services/web/npm-shrinkwrap.json
generated
|
@ -274,12 +274,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"from": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
|
"from": "bytes@2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz"
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz"
|
||||||
},
|
},
|
||||||
"content-type": {
|
"content-type": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
|
"from": "content-type@~1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
@ -328,7 +328,7 @@
|
||||||
},
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"from": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"from": "on-finished@~2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
|
@ -345,7 +345,7 @@
|
||||||
},
|
},
|
||||||
"raw-body": {
|
"raw-body": {
|
||||||
"version": "2.1.7",
|
"version": "2.1.7",
|
||||||
"from": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
|
"from": "raw-body@~2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
|
@ -357,7 +357,7 @@
|
||||||
},
|
},
|
||||||
"type-is": {
|
"type-is": {
|
||||||
"version": "1.6.13",
|
"version": "1.6.13",
|
||||||
"from": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
"from": "type-is@~1.6.10",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"media-typer": {
|
"media-typer": {
|
||||||
|
@ -387,26 +387,42 @@
|
||||||
"resolved": "https://registry.npmjs.org/bufferedstream/-/bufferedstream-1.6.0.tgz"
|
"resolved": "https://registry.npmjs.org/bufferedstream/-/bufferedstream-1.6.0.tgz"
|
||||||
},
|
},
|
||||||
"connect-redis": {
|
"connect-redis": {
|
||||||
"version": "2.3.0",
|
"version": "3.1.0",
|
||||||
"from": "https://registry.npmjs.org/connect-redis/-/connect-redis-2.3.0.tgz",
|
"from": "connect-redis@3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-3.1.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "1.0.4",
|
"version": "2.3.0",
|
||||||
"from": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
|
"from": "https://registry.npmjs.org/debug/-/debug-2.3.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "0.6.2",
|
"version": "0.7.2",
|
||||||
"from": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
|
"from": "ms@0.7.2"
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"version": "0.12.1",
|
"version": "2.6.3",
|
||||||
"from": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz",
|
"from": "https://registry.npmjs.org/redis/-/redis-2.6.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
|
"resolved": "https://registry.npmjs.org/redis/-/redis-2.6.3.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"double-ended-queue": {
|
||||||
|
"version": "2.1.0-0",
|
||||||
|
"from": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz"
|
||||||
|
},
|
||||||
|
"redis-commands": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"from": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.0.tgz"
|
||||||
|
},
|
||||||
|
"redis-parser": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"from": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.1.1.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.1.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -470,7 +486,7 @@
|
||||||
},
|
},
|
||||||
"json-stringify-safe": {
|
"json-stringify-safe": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
"from": "json-stringify-safe@~5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,7 +520,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": {
|
"cookie": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"from": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
"from": "cookie@0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz"
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz"
|
||||||
},
|
},
|
||||||
"cookie-signature": {
|
"cookie-signature": {
|
||||||
|
@ -616,7 +632,7 @@
|
||||||
},
|
},
|
||||||
"content-type": {
|
"content-type": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
|
"from": "content-type@~1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz"
|
||||||
},
|
},
|
||||||
"cookie": {
|
"cookie": {
|
||||||
|
@ -631,7 +647,7 @@
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"from": "debug@~2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
"ms": {
|
||||||
|
@ -685,7 +701,7 @@
|
||||||
},
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"from": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"from": "on-finished@~2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
|
@ -829,7 +845,7 @@
|
||||||
},
|
},
|
||||||
"type-is": {
|
"type-is": {
|
||||||
"version": "1.6.13",
|
"version": "1.6.13",
|
||||||
"from": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
"from": "type-is@~1.6.10",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"media-typer": {
|
"media-typer": {
|
||||||
|
@ -864,68 +880,66 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"express-session": {
|
"express-session": {
|
||||||
"version": "1.11.3",
|
"version": "1.14.2",
|
||||||
"from": "https://registry.npmjs.org/express-session/-/express-session-1.11.3.tgz",
|
"from": "express-session@1.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.14.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": {
|
"cookie": {
|
||||||
"version": "0.1.3",
|
"version": "0.3.1",
|
||||||
"from": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz",
|
"from": "cookie@0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz"
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz"
|
||||||
},
|
},
|
||||||
"cookie-signature": {
|
"cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"from": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"from": "cookie-signature@1.0.6"
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
|
|
||||||
},
|
},
|
||||||
"crc": {
|
"crc": {
|
||||||
"version": "3.3.0",
|
"version": "3.4.1",
|
||||||
"from": "https://registry.npmjs.org/crc/-/crc-3.3.0.tgz",
|
"from": "crc@3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/crc/-/crc-3.3.0.tgz"
|
"resolved": "https://registry.npmjs.org/crc/-/crc-3.4.1.tgz"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"from": "debug@2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
|
"from": "ms@0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
|
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"from": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz",
|
"from": "depd@1.1.0"
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz"
|
|
||||||
},
|
},
|
||||||
"on-headers": {
|
"on-headers": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"from": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
|
"from": "on-headers@1.0.1"
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz"
|
|
||||||
},
|
},
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"from": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
|
"from": "parseurl@1.3.1"
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz"
|
|
||||||
},
|
},
|
||||||
"uid-safe": {
|
"uid-safe": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.3",
|
||||||
"from": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz",
|
"from": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.3.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base64-url": {
|
"base64-url": {
|
||||||
"version": "1.2.1",
|
"version": "1.3.3",
|
||||||
"from": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz",
|
"from": "base64-url@1.3.3"
|
||||||
"resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz"
|
},
|
||||||
|
"random-bytes": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "random-bytes@1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"utils-merge": {
|
"utils-merge": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"from": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
|
"from": "utils-merge@1.0.0"
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1166,6 +1180,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ioredis": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"from": "https://registry.npmjs.org/ioredis/-/ioredis-2.4.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.4.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"bluebird": {
|
||||||
|
"version": "3.4.6",
|
||||||
|
"from": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.6.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.6.tgz"
|
||||||
|
},
|
||||||
|
"cluster-key-slot": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"from": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz"
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"from": "https://registry.npmjs.org/debug/-/debug-2.3.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"from": "ms@0.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"double-ended-queue": {
|
||||||
|
"version": "2.1.0-0",
|
||||||
|
"from": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz"
|
||||||
|
},
|
||||||
|
"flexbuffer": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"from": "flexbuffer@0.0.6"
|
||||||
|
},
|
||||||
|
"redis-commands": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"from": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.0.tgz"
|
||||||
|
},
|
||||||
|
"redis-parser": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"from": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"jade": {
|
"jade": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"from": "https://registry.npmjs.org/jade/-/jade-1.3.1.tgz",
|
"from": "https://registry.npmjs.org/jade/-/jade-1.3.1.tgz",
|
||||||
|
@ -1602,7 +1663,7 @@
|
||||||
},
|
},
|
||||||
"logger-sharelatex": {
|
"logger-sharelatex": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"from": "git+https://github.com/sharelatex/logger-sharelatex.git#bf413ec621a000cf0e08c939de38d5e24541a08c",
|
"from": "logger-sharelatex@git+https://github.com/sharelatex/logger-sharelatex.git#bf413ec621a000cf0e08c939de38d5e24541a08c",
|
||||||
"resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#bf413ec621a000cf0e08c939de38d5e24541a08c",
|
"resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#bf413ec621a000cf0e08c939de38d5e24541a08c",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bunyan": {
|
"bunyan": {
|
||||||
|
@ -1783,7 +1844,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"from": "debug@2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
"ms": {
|
||||||
|
@ -1812,7 +1873,7 @@
|
||||||
},
|
},
|
||||||
"metrics-sharelatex": {
|
"metrics-sharelatex": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"from": "git+https://github.com/sharelatex/metrics-sharelatex.git#080c4aeb696edcd5d6d86f202f2c528f0661d7a6",
|
"from": "metrics-sharelatex@git+https://github.com/sharelatex/metrics-sharelatex.git#080c4aeb696edcd5d6d86f202f2c528f0661d7a6",
|
||||||
"resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#080c4aeb696edcd5d6d86f202f2c528f0661d7a6",
|
"resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#080c4aeb696edcd5d6d86f202f2c528f0661d7a6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"coffee-script": {
|
"coffee-script": {
|
||||||
|
@ -2535,32 +2596,32 @@
|
||||||
},
|
},
|
||||||
"passport-ldapauth": {
|
"passport-ldapauth": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"from": "passport-ldapauth@*",
|
"from": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-0.6.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-0.6.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"passport-strategy": {
|
"passport-strategy": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"from": "passport-strategy@>=1.0.0 <2.0.0",
|
"from": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz"
|
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz"
|
||||||
},
|
},
|
||||||
"ldapauth-fork": {
|
"ldapauth-fork": {
|
||||||
"version": "2.5.3",
|
"version": "2.5.3",
|
||||||
"from": "ldapauth-fork@>=2.5.0 <2.6.0",
|
"from": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-2.5.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-2.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-2.5.3.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": {
|
"bcryptjs": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"from": "bcryptjs@2.3.0",
|
"from": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz"
|
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz"
|
||||||
},
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"from": "lru-cache@3.2.0",
|
"from": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pseudomap": {
|
"pseudomap": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "pseudomap@>=1.0.1 <2.0.0",
|
"from": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2581,6 +2642,89 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"passport-saml": {
|
||||||
|
"version": "0.15.0",
|
||||||
|
"from": "passport-saml@",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-saml/-/passport-saml-0.15.0.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-strategy": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "passport-strategy@*"
|
||||||
|
},
|
||||||
|
"q": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"from": "q@1.1.x"
|
||||||
|
},
|
||||||
|
"xml2js": {
|
||||||
|
"version": "0.4.17",
|
||||||
|
"from": "xml2js@0.4.x",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"from": "sax@>=0.6.0"
|
||||||
|
},
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"from": "xmlbuilder@^4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml-crypto": {
|
||||||
|
"version": "0.8.4",
|
||||||
|
"from": "xml-crypto@0.8.x",
|
||||||
|
"dependencies": {
|
||||||
|
"xmldom": {
|
||||||
|
"version": "0.1.19",
|
||||||
|
"from": "xmldom@=0.1.19"
|
||||||
|
},
|
||||||
|
"xpath.js": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"from": "xpath.js@>=0.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xmldom": {
|
||||||
|
"version": "0.1.22",
|
||||||
|
"from": "xmldom@0.1.x"
|
||||||
|
},
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"from": "xmlbuilder@2.5.x",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"from": "lodash@~3.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml-encryption": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"from": "xml-encryption@~0.7",
|
||||||
|
"dependencies": {
|
||||||
|
"ejs": {
|
||||||
|
"version": "0.8.8",
|
||||||
|
"from": "ejs@~0.8.3"
|
||||||
|
},
|
||||||
|
"async": {
|
||||||
|
"version": "0.2.10",
|
||||||
|
"from": "async@~0.2.7"
|
||||||
|
},
|
||||||
|
"xpath": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"from": "xpath@0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz"
|
||||||
|
},
|
||||||
|
"node-forge": {
|
||||||
|
"version": "0.2.24",
|
||||||
|
"from": "node-forge@0.2.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.2.24.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"pg": {
|
"pg": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"from": "https://registry.npmjs.org/pg/-/pg-6.0.3.tgz",
|
"from": "https://registry.npmjs.org/pg/-/pg-6.0.3.tgz",
|
||||||
|
@ -3430,7 +3574,7 @@
|
||||||
},
|
},
|
||||||
"json-stringify-safe": {
|
"json-stringify-safe": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
"from": "json-stringify-safe@~5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
|
@ -3462,7 +3606,7 @@
|
||||||
},
|
},
|
||||||
"stringstream": {
|
"stringstream": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.5",
|
||||||
"from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
|
"from": "stringstream@~0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
|
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
|
||||||
},
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
|
@ -3472,7 +3616,7 @@
|
||||||
},
|
},
|
||||||
"tunnel-agent": {
|
"tunnel-agent": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
|
"from": "tunnel-agent@~0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz"
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3558,7 +3702,7 @@
|
||||||
},
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"from": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
|
"from": "depd@~1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz"
|
||||||
},
|
},
|
||||||
"dottie": {
|
"dottie": {
|
||||||
|
@ -3656,7 +3800,7 @@
|
||||||
},
|
},
|
||||||
"settings-sharelatex": {
|
"settings-sharelatex": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"from": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559",
|
"from": "settings-sharelatex@git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559",
|
||||||
"resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559",
|
"resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"coffee-script": {
|
"coffee-script": {
|
||||||
|
|
|
@ -17,17 +17,18 @@
|
||||||
"bcrypt": "0.8.3",
|
"bcrypt": "0.8.3",
|
||||||
"body-parser": "^1.13.1",
|
"body-parser": "^1.13.1",
|
||||||
"bufferedstream": "1.6.0",
|
"bufferedstream": "1.6.0",
|
||||||
"connect-redis": "2.3.0",
|
"connect-redis": "^3.1.0",
|
||||||
"contentful": "^3.3.14",
|
"contentful": "^3.3.14",
|
||||||
"cookie": "^0.2.3",
|
"cookie": "^0.2.3",
|
||||||
"cookie-parser": "1.3.5",
|
"cookie-parser": "1.3.5",
|
||||||
"csurf": "^1.8.3",
|
"csurf": "^1.8.3",
|
||||||
"dateformat": "1.0.4-1.2.3",
|
"dateformat": "1.0.4-1.2.3",
|
||||||
"express": "4.13.0",
|
"express": "4.13.0",
|
||||||
"express-session": "1.11.3",
|
"express-session": "^1.14.2",
|
||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
"heapdump": "^0.3.7",
|
"heapdump": "^0.3.7",
|
||||||
"http-proxy": "^1.8.1",
|
"http-proxy": "^1.8.1",
|
||||||
|
"ioredis": "^2.4.0",
|
||||||
"jade": "~1.3.1",
|
"jade": "~1.3.1",
|
||||||
"ldapjs": "^1.0.0",
|
"ldapjs": "^1.0.0",
|
||||||
"lodash": "^4.13.1",
|
"lodash": "^4.13.1",
|
||||||
|
@ -49,8 +50,6 @@
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-ldapauth": "^0.6.0",
|
"passport-ldapauth": "^0.6.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pg": "^6.0.3",
|
|
||||||
"pg-hstore": "^2.3.2",
|
|
||||||
"redback": "0.4.0",
|
"redback": "0.4.0",
|
||||||
"redis": "0.10.1",
|
"redis": "0.10.1",
|
||||||
"redis-sharelatex": "0.0.9",
|
"redis-sharelatex": "0.0.9",
|
||||||
|
@ -64,7 +63,8 @@
|
||||||
"temp": "^0.8.3",
|
"temp": "^0.8.3",
|
||||||
"underscore": "1.6.0",
|
"underscore": "1.6.0",
|
||||||
"v8-profiler": "^5.2.3",
|
"v8-profiler": "^5.2.3",
|
||||||
"xml2js": "0.2.0"
|
"xml2js": "0.2.0",
|
||||||
|
"passport-saml": "^0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bunyan": "0.22.1",
|
"bunyan": "0.22.1",
|
||||||
|
|
|
@ -91,23 +91,6 @@ define [
|
||||||
if value?
|
if value?
|
||||||
localStorage "ui.reviewPanelOpen.#{window.project_id}", value
|
localStorage "ui.reviewPanelOpen.#{window.project_id}", value
|
||||||
|
|
||||||
# Only run the header AB test for newly registered users.
|
|
||||||
_abTestStartDate = new Date(Date.UTC(2016, 8, 28))
|
|
||||||
_userSignUpDate = new Date(window.user.signUpDate)
|
|
||||||
|
|
||||||
$scope.shouldABTestHeaderLabels = _userSignUpDate > _abTestStartDate
|
|
||||||
$scope.headerLabelsABVariant = ""
|
|
||||||
|
|
||||||
if ($scope.shouldABTestHeaderLabels)
|
|
||||||
sixpack.participate "editor-header", [ "default", "labels"], (chosenVariation) ->
|
|
||||||
$scope.headerLabelsABVariant = chosenVariation
|
|
||||||
|
|
||||||
$scope.trackABTestConversion = (headerItem) ->
|
|
||||||
event_tracking.sendMB "header-ab-conversion", {
|
|
||||||
headerItem: headerItem,
|
|
||||||
variant: $scope.headerLabelsABVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
# Tracking code.
|
# Tracking code.
|
||||||
$scope.$watch "ui.view", (newView, oldView) ->
|
$scope.$watch "ui.view", (newView, oldView) ->
|
||||||
if newView? and newView != "editor" and newView != "pdf"
|
if newView? and newView != "editor" and newView != "pdf"
|
||||||
|
|
|
@ -310,7 +310,7 @@ define [
|
||||||
|
|
||||||
_onError: (error, meta = {}) ->
|
_onError: (error, meta = {}) ->
|
||||||
meta.doc_id = @doc_id
|
meta.doc_id = @doc_id
|
||||||
console.error "ShareJS error", error, meta
|
sl_console.log "ShareJS error", error, meta
|
||||||
ga?('send', 'event', 'error', "shareJsError", "#{error.message} - #{@ide.socket.socket.transport.name}" )
|
ga?('send', 'event', 'error', "shareJsError", "#{error.message} - #{@ide.socket.socket.transport.name}" )
|
||||||
@doc?.clearInflightAndPendingOps()
|
@doc?.clearInflightAndPendingOps()
|
||||||
@trigger "error", error, meta
|
@trigger "error", error, meta
|
||||||
|
|
|
@ -207,16 +207,19 @@ define [
|
||||||
return doc.split("\n")
|
return doc.split("\n")
|
||||||
|
|
||||||
_aceDeltaSetsToSimpleDeltaSets: (aceDeltaSets, docLines) ->
|
_aceDeltaSetsToSimpleDeltaSets: (aceDeltaSets, docLines) ->
|
||||||
|
simpleDeltaSets = []
|
||||||
for deltaSet in aceDeltaSets
|
for deltaSet in aceDeltaSets
|
||||||
simpleDeltas = []
|
if deltaSet.group == "doc" # ignore fold changes
|
||||||
for delta in deltaSet.deltas
|
simpleDeltas = []
|
||||||
simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines)
|
for delta in deltaSet.deltas
|
||||||
docLines = @_applyAceDeltasToDocLines([delta], docLines)
|
simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines)
|
||||||
{
|
docLines = @_applyAceDeltasToDocLines([delta], docLines)
|
||||||
deltas: simpleDeltas
|
simpleDeltaSets.push {
|
||||||
group: deltaSet.group
|
deltas: simpleDeltas
|
||||||
}
|
group: deltaSet.group
|
||||||
|
}
|
||||||
|
return simpleDeltaSets
|
||||||
|
|
||||||
_simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) ->
|
_simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) ->
|
||||||
for deltaSet in simpleDeltaSets
|
for deltaSet in simpleDeltaSets
|
||||||
aceDeltas = []
|
aceDeltas = []
|
||||||
|
|
|
@ -14,6 +14,7 @@ define [
|
||||||
"main/subscription-dashboard"
|
"main/subscription-dashboard"
|
||||||
"main/new-subscription"
|
"main/new-subscription"
|
||||||
"main/annual-upgrade"
|
"main/annual-upgrade"
|
||||||
|
"main/announcements"
|
||||||
"main/register-users"
|
"main/register-users"
|
||||||
"main/subscription/group-subscription-invite-controller"
|
"main/subscription/group-subscription-invite-controller"
|
||||||
"main/contact-us"
|
"main/contact-us"
|
||||||
|
|
20
services/web/public/coffee/main/announcements.coffee
Normal file
20
services/web/public/coffee/main/announcements.coffee
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
App.controller "AnnouncementsController", ($scope, $http, event_tracking, $window) ->
|
||||||
|
|
||||||
|
$scope.dataRecived = false
|
||||||
|
announcement = null
|
||||||
|
$http.get("/announcements").success (announcements) ->
|
||||||
|
if announcements?[0]?
|
||||||
|
announcement = announcements[0]
|
||||||
|
$scope.title = announcement.title
|
||||||
|
$scope.totalAnnouncements = announcements.length
|
||||||
|
$scope.dataRecived = true
|
||||||
|
|
||||||
|
dismissannouncement = ->
|
||||||
|
event_tracking.sendMB "announcement-alert-dismissed", {blogPostId:announcement.id}
|
||||||
|
|
||||||
|
$scope.openLink = ->
|
||||||
|
dismissannouncement()
|
||||||
|
$window.location.href = announcement.url
|
|
@ -53,16 +53,6 @@ define [
|
||||||
@sendMB key, segmentation
|
@sendMB key, segmentation
|
||||||
}
|
}
|
||||||
|
|
||||||
# App.directive "countlyTrack", () ->
|
|
||||||
# return {
|
|
||||||
# restrict: "A"
|
|
||||||
# scope: false,
|
|
||||||
# link: (scope, el, attrs) ->
|
|
||||||
# eventKey = attrs.countlyTrack
|
|
||||||
# if (eventKey?)
|
|
||||||
# el.on "click", () ->
|
|
||||||
# console.log eventKey
|
|
||||||
# }
|
|
||||||
|
|
||||||
#header
|
#header
|
||||||
$('.navbar a').on "click", (e)->
|
$('.navbar a').on "click", (e)->
|
||||||
|
|
|
@ -99,7 +99,7 @@ define [
|
||||||
visible = true
|
visible = true
|
||||||
# Only show if it matches any search text
|
# Only show if it matches any search text
|
||||||
if $scope.searchText.value? and $scope.searchText.value != ""
|
if $scope.searchText.value? and $scope.searchText.value != ""
|
||||||
if !project.name.toLowerCase().match($scope.searchText.value.toLowerCase())
|
if project.name.toLowerCase().indexOf($scope.searchText.value.toLowerCase()) == -1
|
||||||
visible = false
|
visible = false
|
||||||
# Only show if it matches the selected tag
|
# Only show if it matches the selected tag
|
||||||
if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids
|
if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids
|
||||||
|
|
|
@ -1813,7 +1813,7 @@ var CheckEnvironments = function (Environments, ErrorReporter) {
|
||||||
var verbatimRanges = [];
|
var verbatimRanges = [];
|
||||||
for (var i = 0, len = Environments.length; i < len; i++) {
|
for (var i = 0, len = Environments.length; i < len; i++) {
|
||||||
var name = Environments[i].name ;
|
var name = Environments[i].name ;
|
||||||
if (name && name.match(/^(verbatim|boxedverbatim|lstlisting)$/)) {
|
if (name && name.match(/^(verbatim|boxedverbatim|lstlisting|minted)$/)) {
|
||||||
Environments[i].verbatim = true;
|
Environments[i].verbatim = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/.gitattributes
vendored
Normal file
1
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* binary
|
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/78-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/78-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/78-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/78-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Add-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Add-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Add-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Add-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5-V.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5pc-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5pc-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5pc-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/B5pc-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS1-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS1-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS1-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS1-V.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS2-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/CNS2-H.bcmap
Normal file
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEáCNS2-H
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEá ETen-B5-H` ^
|
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/EUC-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/EUC-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/EUC-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/EUC-V.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Ext-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p2/bcmaps/Ext-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue