mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into support-cached-pdfs
This commit is contained in:
commit
0315954b47
31 changed files with 240 additions and 166 deletions
|
@ -6,7 +6,6 @@ Metrics = require('../../infrastructure/Metrics')
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
querystring = require('querystring')
|
querystring = require('querystring')
|
||||||
Url = require("url")
|
Url = require("url")
|
||||||
uid = require "uid"
|
|
||||||
|
|
||||||
module.exports = AuthenticationController =
|
module.exports = AuthenticationController =
|
||||||
login: (req, res, next = (error) ->) ->
|
login: (req, res, next = (error) ->) ->
|
||||||
|
|
|
@ -24,5 +24,5 @@ module.exports =
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, query:query, "problem getting messages from chat api"
|
logger.err err:err, query:query, "problem getting messages from chat api"
|
||||||
return res.send 500
|
return res.send 500
|
||||||
logger.log messages:messages, "sending messages to client"
|
logger.log length:messages?.length, "sending messages to client"
|
||||||
res.send messages
|
res.send messages
|
||||||
|
|
|
@ -71,9 +71,9 @@ module.exports = CompileManager =
|
||||||
return callback(null, true)
|
return callback(null, true)
|
||||||
opts =
|
opts =
|
||||||
endpointName:"auto_compile"
|
endpointName:"auto_compile"
|
||||||
timeInterval:15
|
timeInterval:20
|
||||||
subjectName:"everyone"
|
subjectName:"everyone"
|
||||||
throttle: 15
|
throttle: 25
|
||||||
rateLimiter.addCount opts, (err, canCompile)->
|
rateLimiter.addCount opts, (err, canCompile)->
|
||||||
if err?
|
if err?
|
||||||
canCompile = false
|
canCompile = false
|
||||||
|
|
|
@ -48,12 +48,13 @@ module.exports = ProjectEntityHandler =
|
||||||
for folderPath, folder of folders
|
for folderPath, folder of folders
|
||||||
for doc in folder.docs
|
for doc in folder.docs
|
||||||
content = docContents[doc._id.toString()]
|
content = docContents[doc._id.toString()]
|
||||||
docs[path.join(folderPath, doc.name)] = {
|
if content?
|
||||||
_id: doc._id
|
docs[path.join(folderPath, doc.name)] = {
|
||||||
name: doc.name
|
_id: doc._id
|
||||||
lines: content.lines
|
name: doc.name
|
||||||
rev: content.rev
|
lines: content.lines
|
||||||
}
|
rev: content.rev
|
||||||
|
}
|
||||||
logger.log count:_.keys(docs).length, project_id:project_id, "returning docs for project"
|
logger.log count:_.keys(docs).length, project_id:project_id, "returning docs for project"
|
||||||
callback null, docs
|
callback null, docs
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,11 @@ module.exports =
|
||||||
|
|
||||||
|
|
||||||
Project.getProject project_or_id, "", (err, project)->
|
Project.getProject project_or_id, "", (err, project)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, project_or_id:project_or_id, "error getting project for finding element"
|
||||||
|
return callback(err)
|
||||||
|
if !project?
|
||||||
|
return callback("project could not be found for finding a element #{project_or_id}")
|
||||||
if needlePath == '' || needlePath == '/'
|
if needlePath == '' || needlePath == '/'
|
||||||
return callback(null, project.rootFolder[0])
|
return callback(null, project.rootFolder[0])
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ module.exports =
|
||||||
self = @
|
self = @
|
||||||
clientTokenId = ""
|
clientTokenId = ""
|
||||||
RecurlyWrapper.createSubscription user, subscriptionDetails, recurly_token_id, (error, recurlySubscription)->
|
RecurlyWrapper.createSubscription user, subscriptionDetails, recurly_token_id, (error, recurlySubscription)->
|
||||||
console.log recurlySubscription
|
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
SubscriptionUpdater.syncSubscription recurlySubscription, user._id, (error) ->
|
SubscriptionUpdater.syncSubscription recurlySubscription, user._id, (error) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
|
|
@ -53,7 +53,9 @@ module.exports =
|
||||||
async.series [
|
async.series [
|
||||||
(cb)-> User.update {_id: user._id}, {"$set":{holdingAccount:false}}, cb
|
(cb)-> User.update {_id: user._id}, {"$set":{holdingAccount:false}}, cb
|
||||||
(cb)-> AuthenticationManager.setUserPassword user._id, userDetails.password, cb
|
(cb)-> AuthenticationManager.setUserPassword user._id, userDetails.password, cb
|
||||||
(cb)-> NewsLetterManager.subscribe user, cb
|
(cb)->
|
||||||
|
NewsLetterManager.subscribe user, ->
|
||||||
|
cb() #this can be slow, just fire it off
|
||||||
(cb)->
|
(cb)->
|
||||||
emailOpts =
|
emailOpts =
|
||||||
first_name:user.first_name
|
first_name:user.first_name
|
||||||
|
|
|
@ -3,37 +3,47 @@ settings = require("settings-sharelatex")
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
ErrorController = require "../Errors/ErrorController"
|
ErrorController = require "../Errors/ErrorController"
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
|
AuthenticationController = require("../Authentication/AuthenticationController")
|
||||||
|
|
||||||
other_lngs = ["es"]
|
other_lngs = ["es"]
|
||||||
|
|
||||||
module.exports = WikiController =
|
module.exports = WikiController =
|
||||||
getPage: (req, res, next) ->
|
|
||||||
|
|
||||||
page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
|
|
||||||
if page == ""
|
|
||||||
page = "Main_Page"
|
|
||||||
|
|
||||||
logger.log page: page, "getting page from wiki"
|
|
||||||
if _.include(other_lngs, req.lng)
|
_checkIfLoginIsNeeded: (req, res, next)->
|
||||||
lngPage = "#{page}_#{req.lng}"
|
if settings.apis.wiki.requireLogin
|
||||||
|
AuthenticationController.requireLogin()(req, res, next)
|
||||||
else
|
else
|
||||||
lngPage = page
|
next()
|
||||||
|
|
||||||
WikiController._getPageContent "Contents", (error, contents) ->
|
getPage: (req, res, next) ->
|
||||||
return next(error) if error?
|
WikiController._checkIfLoginIsNeeded req, res, ->
|
||||||
WikiController._getPageContent lngPage, (error, pageData) ->
|
|
||||||
|
page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
|
||||||
|
if page == ""
|
||||||
|
page = "Main_Page"
|
||||||
|
|
||||||
|
logger.log page: page, "getting page from wiki"
|
||||||
|
if _.include(other_lngs, req.lng)
|
||||||
|
lngPage = "#{page}_#{req.lng}"
|
||||||
|
else
|
||||||
|
lngPage = page
|
||||||
|
|
||||||
|
WikiController._getPageContent "Contents", (error, contents) ->
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
if pageData.content?.length > 280
|
WikiController._getPageContent lngPage, (error, pageData) ->
|
||||||
if _.include(other_lngs, req.lng)
|
return next(error) if error?
|
||||||
pageData.title = pageData.title.slice(0, pageData.title.length - (req.lng.length+1) )
|
if pageData.content?.length > 280
|
||||||
WikiController._renderPage(pageData, contents, res)
|
if _.include(other_lngs, req.lng)
|
||||||
else
|
pageData.title = pageData.title.slice(0, pageData.title.length - (req.lng.length+1) )
|
||||||
WikiController._getPageContent page, (error, pageData) ->
|
|
||||||
return next(error) if error?
|
|
||||||
WikiController._renderPage(pageData, contents, res)
|
WikiController._renderPage(pageData, contents, res)
|
||||||
|
else
|
||||||
|
WikiController._getPageContent page, (error, pageData) ->
|
||||||
|
return next(error) if error?
|
||||||
|
WikiController._renderPage(pageData, contents, res)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_getPageContent: (page, callback = (error, data = { content: "", title: "" }) ->) ->
|
_getPageContent: (page, callback = (error, data = { content: "", title: "" }) ->) ->
|
||||||
request {
|
request {
|
||||||
|
|
|
@ -112,6 +112,11 @@ module.exports = (app)->
|
||||||
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
res.locals.formatPrice = SubscriptionFormatters.formatPrice
|
||||||
next()
|
next()
|
||||||
|
|
||||||
|
app.use (req, res, next)->
|
||||||
|
res.locals.externalAuthenticationSystemUsed = ->
|
||||||
|
Settings.ldap?
|
||||||
|
next()
|
||||||
|
|
||||||
app.use (req, res, next)->
|
app.use (req, res, next)->
|
||||||
if req.session.user?
|
if req.session.user?
|
||||||
res.locals.user =
|
res.locals.user =
|
||||||
|
|
|
@ -27,11 +27,11 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
meta(itemprop="name", content="ShareLaTeX, the Online LaTeX Editor")
|
meta(itemprop="name", content="ShareLaTeX, the Online LaTeX Editor")
|
||||||
|
|
||||||
-if (typeof(meta) == "undefined")
|
-if (typeof(meta) == "undefined")
|
||||||
meta(itemprop="description", content="An online LaTeX editor that's easy to use. No installation, real-time collaboration, version control, hundreds of LaTeX templates, and more.")
|
meta(itemprop="description", name="description", content="An online LaTeX editor that's easy to use. No installation, real-time collaboration, version control, hundreds of LaTeX templates, and more.")
|
||||||
-else
|
-else
|
||||||
meta(itemprop="description", content=meta)
|
meta(itemprop="description", name="description" , content=meta)
|
||||||
|
|
||||||
meta(itemprop="image", content="https://www.sharelatex.com/favicon.ico")
|
meta(itemprop="image", name="image", content="https://www.sharelatex.com/favicon.ico")
|
||||||
|
|
||||||
- if (typeof(gaToken) != "undefined")
|
- if (typeof(gaToken) != "undefined")
|
||||||
script(type='text/javascript').
|
script(type='text/javascript').
|
||||||
|
@ -98,7 +98,10 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
// Other plugins
|
// Other plugins
|
||||||
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
|
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
|
||||||
/webappstoolbarba\.texthelp\.com\//i,
|
/webappstoolbarba\.texthelp\.com\//i,
|
||||||
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i
|
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
|
||||||
|
/a\.disquscdn\.com/i,
|
||||||
|
/platform\.twitter\.com/i,
|
||||||
|
/pstatic\.datafastguru\.info/i
|
||||||
],
|
],
|
||||||
shouldSendCallback: function(data) {
|
shouldSendCallback: function(data) {
|
||||||
// only send a fraction of errors
|
// only send a fraction of errors
|
||||||
|
|
|
@ -41,12 +41,12 @@ aside#left-menu.full-size(
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-copy
|
i.fa.fa-fw.fa-copy
|
||||||
| #{translate("copy_project")}
|
| #{translate("copy_project")}
|
||||||
!{moduleIncludes("editorLeftMenu:actions", locals)}
|
!= moduleIncludes("editorLeftMenu:actions", locals)
|
||||||
|
|
||||||
if (moduleIncludesAvailable("editorLeftMenu:sync"))
|
if (moduleIncludesAvailable("editorLeftMenu:sync"))
|
||||||
div(ng-show="!anonymous")
|
div(ng-show="!anonymous")
|
||||||
h4() #{translate("sync")}
|
h4() #{translate("sync")}
|
||||||
!{moduleIncludes("editorLeftMenu:sync", locals)}
|
!= moduleIncludes("editorLeftMenu:sync", locals)
|
||||||
|
|
||||||
h4(ng-show="!anonymous") #{translate("settings")}
|
h4(ng-show="!anonymous") #{translate("settings")}
|
||||||
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
|
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
|
||||||
|
|
|
@ -8,9 +8,14 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
|
||||||
ng-class="buttonClass"
|
ng-class="buttonClass"
|
||||||
ng-click="startFreeTrial('track-changes')"
|
ng-click="startFreeTrial('track-changes')"
|
||||||
) #{translate("start_free_trial")}
|
) #{translate("start_free_trial")}
|
||||||
|
|
|
||||||
|
a.small(href, ng-click="toggleTrackChanges()") #{translate("cancel")}
|
||||||
p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||||
|
|
||||||
.message(ng-show="project.owner._id != user.id")
|
.message(ng-show="project.owner._id != user.id")
|
||||||
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
||||||
|
p
|
||||||
|
a.small(href, ng-click="toggleTrackChanges()") #{translate("cancel")}
|
||||||
|
|
||||||
aside.change-list(
|
aside.change-list(
|
||||||
ng-controller="TrackChangesListController"
|
ng-controller="TrackChangesListController"
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
href,
|
href,
|
||||||
ng-click="openUploadProjectModal()"
|
ng-click="openUploadProjectModal()"
|
||||||
) #{translate("upload_project")}
|
) #{translate("upload_project")}
|
||||||
!{moduleIncludes("newProjectMenu", locals)}
|
!= moduleIncludes("newProjectMenu", locals)
|
||||||
if (templates)
|
if (templates)
|
||||||
li.divider
|
li.divider
|
||||||
li.dropdown-header #{translate("templates")}
|
li.dropdown-header #{translate("templates")}
|
||||||
|
|
|
@ -143,8 +143,9 @@ block content
|
||||||
function callback(response) {
|
function callback(response) {
|
||||||
// document.getElementById('msg').innerHTML = "Post ID: " + response['post_id'];
|
// document.getElementById('msg').innerHTML = "Post ID: " + response['post_id'];
|
||||||
}
|
}
|
||||||
|
if (typeof FB !== "undefined" && FB !== null) {
|
||||||
FB.ui(obj, callback);
|
FB.ui(obj, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
script(type="text/javascript").
|
script(type="text/javascript").
|
||||||
|
|
|
@ -19,19 +19,20 @@ block content
|
||||||
h3 #{translate("update_account_info")}
|
h3 #{translate("update_account_info")}
|
||||||
form(async-form="settings", name="settingsForm", method="POST", action="/user/settings", novalidate)
|
form(async-form="settings", name="settingsForm", method="POST", action="/user/settings", novalidate)
|
||||||
input(type="hidden", name="_csrf", value=csrfToken)
|
input(type="hidden", name="_csrf", value=csrfToken)
|
||||||
.form-group
|
if !externalAuthenticationSystemUsed()
|
||||||
label(for='email') #{translate("email")}
|
.form-group
|
||||||
input.form-control(
|
label(for='email') #{translate("email")}
|
||||||
type='email',
|
input.form-control(
|
||||||
name='email',
|
type='email',
|
||||||
placeholder="email@example.com"
|
name='email',
|
||||||
required,
|
placeholder="email@example.com"
|
||||||
ng-model="email",
|
required,
|
||||||
ng-init="email = #{JSON.stringify(user.email)}",
|
ng-model="email",
|
||||||
ng-model-options="{ updateOn: 'blur' }"
|
ng-init="email = #{JSON.stringify(user.email)}",
|
||||||
)
|
ng-model-options="{ updateOn: 'blur' }"
|
||||||
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
|
)
|
||||||
| #{translate("must_be_email_address")}
|
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
|
||||||
|
| #{translate("must_be_email_address")}
|
||||||
.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(
|
||||||
|
@ -51,75 +52,77 @@ block content
|
||||||
type='submit',
|
type='submit',
|
||||||
ng-disabled="settingsForm.$invalid"
|
ng-disabled="settingsForm.$invalid"
|
||||||
) #{translate("update")}
|
) #{translate("update")}
|
||||||
|
if !externalAuthenticationSystemUsed()
|
||||||
.col-md-5.col-md-offset-1
|
.col-md-5.col-md-offset-1
|
||||||
h3 #{translate("change_password")}
|
h3 #{translate("change_password")}
|
||||||
form(async-form="changepassword", name="changePasswordForm", action="/user/password/update", method="POST", novalidate)
|
form(async-form="changepassword", name="changePasswordForm", action="/user/password/update", method="POST", novalidate)
|
||||||
input(type="hidden", name="_csrf", value=csrfToken)
|
input(type="hidden", name="_csrf", value=csrfToken)
|
||||||
.form-group
|
.form-group
|
||||||
label(for='currentPassword') #{translate("current_password")}
|
label(for='currentPassword') #{translate("current_password")}
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='password',
|
type='password',
|
||||||
name='currentPassword',
|
name='currentPassword',
|
||||||
placeholder='*********',
|
placeholder='*********',
|
||||||
ng-model="currentPassword",
|
ng-model="currentPassword",
|
||||||
required
|
required
|
||||||
)
|
)
|
||||||
span.small.text-primary(ng-show="changePasswordForm.currentPassword.$invalid && changePasswordForm.currentPassword.$dirty")
|
span.small.text-primary(ng-show="changePasswordForm.currentPassword.$invalid && changePasswordForm.currentPassword.$dirty")
|
||||||
| #{translate("required")}
|
| #{translate("required")}
|
||||||
.form-group
|
.form-group
|
||||||
label(for='newPassword1') #{translate("new_password")}
|
label(for='newPassword1') #{translate("new_password")}
|
||||||
input.form-control(
|
input.form-control(
|
||||||
id='newPassword1',
|
id='newPassword1',
|
||||||
type='password',
|
type='password',
|
||||||
name='newPassword1',
|
name='newPassword1',
|
||||||
placeholder='*********',
|
placeholder='*********',
|
||||||
ng-model="newPassword1",
|
ng-model="newPassword1",
|
||||||
required
|
required
|
||||||
)
|
)
|
||||||
span.small.text-primary(ng-show="changePasswordForm.newPassword1.$invalid && changePasswordForm.newPassword1.$dirty")
|
span.small.text-primary(ng-show="changePasswordForm.newPassword1.$invalid && changePasswordForm.newPassword1.$dirty")
|
||||||
| #{translate("required")}
|
| #{translate("required")}
|
||||||
.form-group
|
.form-group
|
||||||
label(for='newPassword2') #{translate("confirm_new_password")}
|
label(for='newPassword2') #{translate("confirm_new_password")}
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='password',
|
type='password',
|
||||||
name='newPassword2',
|
name='newPassword2',
|
||||||
placeholder='*********',
|
placeholder='*********',
|
||||||
ng-model="newPassword2",
|
ng-model="newPassword2",
|
||||||
equals="newPassword1"
|
equals="newPassword1"
|
||||||
)
|
)
|
||||||
span.small.text-primary(ng-show="changePasswordForm.newPassword2.$invalid && changePasswordForm.newPassword2.$dirty")
|
span.small.text-primary(ng-show="changePasswordForm.newPassword2.$invalid && changePasswordForm.newPassword2.$dirty")
|
||||||
| #{translate("doesnt_match")}
|
| #{translate("doesnt_match")}
|
||||||
.actions
|
.actions
|
||||||
button.btn.btn-primary(
|
button.btn.btn-primary(
|
||||||
type='submit',
|
type='submit',
|
||||||
ng-disabled="changePasswordForm.$invalid"
|
ng-disabled="changePasswordForm.$invalid"
|
||||||
) #{translate("change")}
|
) #{translate("change")}
|
||||||
|
|
||||||
| !{moduleIncludes("userSettings", locals)}
|
| !{moduleIncludes("userSettings", locals)}
|
||||||
|
|
||||||
hr
|
hr
|
||||||
|
|
||||||
p.small
|
if !externalAuthenticationSystemUsed()
|
||||||
| #{translate("newsletter_info_and_unsubscribe")}
|
|
||||||
a(
|
|
||||||
href,
|
|
||||||
ng-click="unsubscribe()",
|
|
||||||
ng-show="subscribed && !unsubscribing"
|
|
||||||
) #{translate("unsubscribe")}
|
|
||||||
span(
|
|
||||||
ng-show="unsubscribing"
|
|
||||||
)
|
|
||||||
i.fa.fa-spin.fa-refresh
|
|
||||||
| #{translate("unsubscribing")}
|
|
||||||
span.text-success(
|
|
||||||
ng-show="!subscribed"
|
|
||||||
)
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("unsubscribed")}
|
|
||||||
|
|
||||||
p #{translate("need_to_leave")}
|
p.small
|
||||||
a(href, ng-click="deleteAccount()") #{translate("delete_your_account")}
|
| #{translate("newsletter_info_and_unsubscribe")}
|
||||||
|
a(
|
||||||
|
href,
|
||||||
|
ng-click="unsubscribe()",
|
||||||
|
ng-show="subscribed && !unsubscribing"
|
||||||
|
) #{translate("unsubscribe")}
|
||||||
|
span(
|
||||||
|
ng-show="unsubscribing"
|
||||||
|
)
|
||||||
|
i.fa.fa-spin.fa-refresh
|
||||||
|
| #{translate("unsubscribing")}
|
||||||
|
span.text-success(
|
||||||
|
ng-show="!subscribed"
|
||||||
|
)
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("unsubscribed")}
|
||||||
|
|
||||||
|
p #{translate("need_to_leave")}
|
||||||
|
a(href, ng-click="deleteAccount()") #{translate("delete_your_account")}
|
||||||
|
|
||||||
|
|
||||||
script(type='text/ng-template', id='deleteAccountModalTemplate')
|
script(type='text/ng-template', id='deleteAccountModalTemplate')
|
||||||
|
|
|
@ -331,3 +331,19 @@ module.exports =
|
||||||
proxyUrls: {}
|
proxyUrls: {}
|
||||||
|
|
||||||
reloadModuleViewsOnEachRequest: true
|
reloadModuleViewsOnEachRequest: true
|
||||||
|
|
||||||
|
# ShareLaTeX Server Pro options (https://www.sharelatex.com/university/onsite.html)
|
||||||
|
# ----------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ldap:
|
||||||
|
# host: 'ldap://ldap.forumsys.com'
|
||||||
|
# dnObj: 'uid'
|
||||||
|
# dnSuffix: 'dc=example,dc=com'
|
||||||
|
# failMessage: 'LDAP User Fail'
|
||||||
|
# fieldName: 'LDAP User'
|
||||||
|
# placeholder: 'LDAP User ID'
|
||||||
|
# emailAtt: 'mail'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "web-sharelatex",
|
"name": "web-sharelatex",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"description": "The HTTP front end for ShareLaTeX",
|
"description": "The HTTP front end for ShareLaTeX",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
"express": "3.3.4",
|
"express": "3.3.4",
|
||||||
"fairy": "0.0.2",
|
"fairy": "0.0.2",
|
||||||
"jade": "~1.3.1",
|
"jade": "~1.3.1",
|
||||||
|
"ldapjs": "^0.7.1",
|
||||||
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
|
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
|
||||||
"lynx": "0.1.1",
|
"lynx": "0.1.1",
|
||||||
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0",
|
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0",
|
||||||
|
@ -39,7 +40,6 @@
|
||||||
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
||||||
"socket.io": "0.9.16",
|
"socket.io": "0.9.16",
|
||||||
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
|
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
|
||||||
"uid": "0.0.2",
|
|
||||||
"underscore": "1.6.0",
|
"underscore": "1.6.0",
|
||||||
"underscore.string": "^3.0.2",
|
"underscore.string": "^3.0.2",
|
||||||
"v8-profiler": "^5.2.3",
|
"v8-profiler": "^5.2.3",
|
||||||
|
|
|
@ -83,7 +83,7 @@ define [
|
||||||
$scope.$on "project:joined", () ->
|
$scope.$on "project:joined", () ->
|
||||||
return if inited
|
return if inited
|
||||||
inited = true
|
inited = true
|
||||||
if $scope.project.deletedByExternalDataSource
|
if $scope?.project?.deletedByExternalDataSource
|
||||||
ide.showGenericMessageModal("Project Renamed or Deleted", """
|
ide.showGenericMessageModal("Project Renamed or Deleted", """
|
||||||
This project has either been renamed or deleted by an external data source such as Dropbox.
|
This project has either been renamed or deleted by an external data source such as Dropbox.
|
||||||
We don't want to delete your data on ShareLaTeX, so this project still contains your history and collaborators.
|
We don't want to delete your data on ShareLaTeX, so this project still contains your history and collaborators.
|
||||||
|
|
|
@ -15,7 +15,7 @@ define [
|
||||||
|
|
||||||
$scope.$on "chat:newMessage", (e, message) ->
|
$scope.$on "chat:newMessage", (e, message) ->
|
||||||
if message?
|
if message?
|
||||||
if message.user.id != ide.$scope.user.id
|
if message?.user?.id != ide.$scope.user.id
|
||||||
if !$scope.ui.chatOpen
|
if !$scope.ui.chatOpen
|
||||||
$scope.unreadMessages += 1
|
$scope.unreadMessages += 1
|
||||||
flashTitle()
|
flashTitle()
|
||||||
|
|
|
@ -30,7 +30,7 @@ define [
|
||||||
$scope.loadMoreMessages = ->
|
$scope.loadMoreMessages = ->
|
||||||
chatMessages.loadMoreMessages()
|
chatMessages.loadMoreMessages()
|
||||||
|
|
||||||
$scope.linkify = (message)->
|
$scope.linkify = (message = "")->
|
||||||
return autolinker.link(message)
|
return autolinker.link(message)
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,12 @@ define [
|
||||||
processEscapes: true
|
processEscapes: true
|
||||||
skipStartupTypeset: true
|
skipStartupTypeset: true
|
||||||
|
|
||||||
MathJax.Hub.Config(mathjaxConfig);
|
MathJax?.Hub?.Config(mathjaxConfig);
|
||||||
|
|
||||||
App.directive "mathjax", () ->
|
App.directive "mathjax", () ->
|
||||||
return {
|
return {
|
||||||
link: (scope, element, attrs) ->
|
link: (scope, element, attrs) ->
|
||||||
setTimeout () ->
|
setTimeout () ->
|
||||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, element.get(0)])
|
MathJax?.Hub?.Queue(["Typeset", MathJax?.Hub, element.get(0)])
|
||||||
, 0
|
, 0
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@ define [
|
||||||
|
|
||||||
justSent = false
|
justSent = false
|
||||||
ide.socket.on "new-chat-message", (message) =>
|
ide.socket.on "new-chat-message", (message) =>
|
||||||
if message.user.id == ide.$scope.user.id and justSent
|
if message?.user?.id == ide.$scope.user.id and justSent
|
||||||
# Nothing to do
|
# Nothing to do
|
||||||
else
|
else
|
||||||
ide.$scope.$apply () ->
|
ide.$scope.$apply () ->
|
||||||
|
@ -51,9 +51,9 @@ define [
|
||||||
if messages.length < MESSAGE_LIMIT
|
if messages.length < MESSAGE_LIMIT
|
||||||
chat.state.atEnd = true
|
chat.state.atEnd = true
|
||||||
if !messages.reverse?
|
if !messages.reverse?
|
||||||
Raven?.captureException(new Error("messages has no reverse property #{JSON.stringify(messages)}"))
|
Raven?.captureException(new Error("messages has no reverse property #{typeof(messages)}"))
|
||||||
if typeof messages.reverse isnt 'function'
|
if typeof messages.reverse isnt 'function'
|
||||||
Raven?.captureException(new Error("messages.reverse not a function #{typeof(messages.reverse)} #{JSON.stringify(messages)}"))
|
Raven?.captureException(new Error("messages.reverse not a function #{typeof(messages.reverse)} #{typeof(messages)}"))
|
||||||
chat.state.errored = true
|
chat.state.errored = true
|
||||||
else
|
else
|
||||||
messages.reverse()
|
messages.reverse()
|
||||||
|
@ -65,7 +65,7 @@ define [
|
||||||
prependMessage = (message) ->
|
prependMessage = (message) ->
|
||||||
firstMessage = chat.state.messages[0]
|
firstMessage = chat.state.messages[0]
|
||||||
shouldGroup = firstMessage? and
|
shouldGroup = firstMessage? and
|
||||||
firstMessage.user.id == message.user.id and
|
firstMessage.user.id == message?.user?.id and
|
||||||
firstMessage.timestamp - message.timestamp < TIMESTAMP_GROUP_SIZE
|
firstMessage.timestamp - message.timestamp < TIMESTAMP_GROUP_SIZE
|
||||||
if shouldGroup
|
if shouldGroup
|
||||||
firstMessage.timestamp = message.timestamp
|
firstMessage.timestamp = message.timestamp
|
||||||
|
@ -86,7 +86,7 @@ define [
|
||||||
|
|
||||||
lastMessage = chat.state.messages[chat.state.messages.length - 1]
|
lastMessage = chat.state.messages[chat.state.messages.length - 1]
|
||||||
shouldGroup = lastMessage? and
|
shouldGroup = lastMessage? and
|
||||||
lastMessage.user.id == message.user.id and
|
lastMessage.user.id == message?.user?.id and
|
||||||
message.timestamp - lastMessage.timestamp < TIMESTAMP_GROUP_SIZE
|
message.timestamp - lastMessage.timestamp < TIMESTAMP_GROUP_SIZE
|
||||||
if shouldGroup
|
if shouldGroup
|
||||||
lastMessage.timestamp = message.timestamp
|
lastMessage.timestamp = message.timestamp
|
||||||
|
|
|
@ -162,13 +162,8 @@ define [
|
||||||
return if !path?
|
return if !path?
|
||||||
return path.split("/").slice(0, -1).join("/")
|
return path.split("/").slice(0, -1).join("/")
|
||||||
|
|
||||||
# forEachFolder: (callback) ->
|
|
||||||
# @forEachEntity (entity) ->
|
|
||||||
# if entity.type == "folder"
|
|
||||||
# callback(entity)
|
|
||||||
|
|
||||||
loadRootFolder: () ->
|
loadRootFolder: () ->
|
||||||
@$scope.rootFolder = @_parseFolder(@$scope.project.rootFolder[0])
|
@$scope.rootFolder = @_parseFolder(@$scope?.project?.rootFolder[0])
|
||||||
|
|
||||||
_parseFolder: (rawFolder) ->
|
_parseFolder: (rawFolder) ->
|
||||||
folder = {
|
folder = {
|
||||||
|
@ -306,6 +301,7 @@ define [
|
||||||
return (child_path.slice(0, parent_path.length) == parent_path)
|
return (child_path.slice(0, parent_path.length) == parent_path)
|
||||||
|
|
||||||
_deleteEntityFromScope: (entity, options = { moveToDeleted: true }) ->
|
_deleteEntityFromScope: (entity, options = { moveToDeleted: true }) ->
|
||||||
|
return if !entity?
|
||||||
parent_folder = null
|
parent_folder = null
|
||||||
@forEachEntity (possible_entity, folder) ->
|
@forEachEntity (possible_entity, folder) ->
|
||||||
if possible_entity == entity
|
if possible_entity == entity
|
||||||
|
|
|
@ -17,7 +17,7 @@ define [
|
||||||
# We need this here as well as in FileTreeController
|
# We need this here as well as in FileTreeController
|
||||||
# since the file-entity diretive creates a new scope
|
# since the file-entity diretive creates a new scope
|
||||||
# that doesn't inherit from previous scopes.
|
# that doesn't inherit from previous scopes.
|
||||||
return '0' if entity.type == "folder"
|
return '0' if entity?.type == "folder"
|
||||||
return '1'
|
return '1'
|
||||||
|
|
||||||
$scope.openNewDocModal = () ->
|
$scope.openNewDocModal = () ->
|
||||||
|
|
|
@ -60,6 +60,9 @@ define [
|
||||||
|
|
||||||
IGNORE_FILES = ["output.fls", "output.fdb_latexmk"]
|
IGNORE_FILES = ["output.fls", "output.fdb_latexmk"]
|
||||||
$scope.pdf.outputFiles = []
|
$scope.pdf.outputFiles = []
|
||||||
|
|
||||||
|
if !response.outputFiles?
|
||||||
|
return
|
||||||
for file in response.outputFiles
|
for file in response.outputFiles
|
||||||
if IGNORE_FILES.indexOf(file.path) == -1
|
if IGNORE_FILES.indexOf(file.path) == -1
|
||||||
# Turn 'output.blg' into 'blg file'.
|
# Turn 'output.blg' into 'blg file'.
|
||||||
|
@ -80,16 +83,17 @@ define [
|
||||||
|
|
||||||
$scope.pdf.logEntryAnnotations = {}
|
$scope.pdf.logEntryAnnotations = {}
|
||||||
for entry in logEntries.all
|
for entry in logEntries.all
|
||||||
entry.file = normalizeFilePath(entry.file)
|
if entry.file?
|
||||||
|
entry.file = normalizeFilePath(entry.file)
|
||||||
|
|
||||||
entity = ide.fileTreeManager.findEntityByPath(entry.file)
|
entity = ide.fileTreeManager.findEntityByPath(entry.file)
|
||||||
if entity?
|
if entity?
|
||||||
$scope.pdf.logEntryAnnotations[entity.id] ||= []
|
$scope.pdf.logEntryAnnotations[entity.id] ||= []
|
||||||
$scope.pdf.logEntryAnnotations[entity.id].push {
|
$scope.pdf.logEntryAnnotations[entity.id].push {
|
||||||
row: entry.line - 1
|
row: entry.line - 1
|
||||||
type: if entry.level == "error" then "error" else "warning"
|
type: if entry.level == "error" then "error" else "warning"
|
||||||
text: entry.message
|
text: entry.message
|
||||||
}
|
}
|
||||||
|
|
||||||
.error () ->
|
.error () ->
|
||||||
$scope.pdf.logEntries = []
|
$scope.pdf.logEntries = []
|
||||||
|
|
|
@ -24,6 +24,6 @@ define [
|
||||||
|
|
||||||
clearHighlights: () ->
|
clearHighlights: () ->
|
||||||
for h in @highlightElements
|
for h in @highlightElements
|
||||||
h.remove()
|
h?.remove()
|
||||||
@highlightElements = []
|
@highlightElements = []
|
||||||
]
|
]
|
||||||
|
|
|
@ -45,7 +45,10 @@ define [
|
||||||
pricing.plan(planCode, { quantity: 1 }).currency(MultiCurrencyPricing.currencyCode).done (price)->
|
pricing.plan(planCode, { quantity: 1 }).currency(MultiCurrencyPricing.currencyCode).done (price)->
|
||||||
totalPriceExTax = parseFloat(price.next.total)
|
totalPriceExTax = parseFloat(price.next.total)
|
||||||
$scope.$evalAsync () ->
|
$scope.$evalAsync () ->
|
||||||
$scope.prices[planCode] = $scope.currencySymbol + (totalPriceExTax + (totalPriceExTax * taxRate))
|
taxAmmount = totalPriceExTax * taxRate
|
||||||
|
if isNaN(taxAmmount)
|
||||||
|
taxAmmount = 0
|
||||||
|
$scope.prices[planCode] = $scope.currencySymbol + (totalPriceExTax + taxAmmount)
|
||||||
|
|
||||||
price = ""
|
price = ""
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,6 @@
|
||||||
}
|
}
|
||||||
.qq-upload-spinner {
|
.qq-upload-spinner {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: url("loading.gif");
|
|
||||||
width: 15px;
|
width: 15px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
|
|
|
@ -241,9 +241,9 @@ describe "CompileManager", ->
|
||||||
@ratelimiter.addCount.callsArgWith(1, null, true)
|
@ratelimiter.addCount.callsArgWith(1, null, true)
|
||||||
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
|
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
|
||||||
args = @ratelimiter.addCount.args[0][0]
|
args = @ratelimiter.addCount.args[0][0]
|
||||||
args.throttle.should.equal 15
|
args.throttle.should.equal 25
|
||||||
args.subjectName.should.equal "everyone"
|
args.subjectName.should.equal "everyone"
|
||||||
args.timeInterval.should.equal 15
|
args.timeInterval.should.equal 20
|
||||||
args.endpointName.should.equal "auto_compile"
|
args.endpointName.should.equal "auto_compile"
|
||||||
canCompile.should.equal true
|
canCompile.should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
|
@ -6,7 +6,7 @@ modulePath = "../../../../app/js/Features/Project/ProjectLocator"
|
||||||
SandboxedModule = require('sandboxed-module')
|
SandboxedModule = require('sandboxed-module')
|
||||||
sinon = require('sinon')
|
sinon = require('sinon')
|
||||||
Errors = require "../../../../app/js/errors"
|
Errors = require "../../../../app/js/errors"
|
||||||
|
expect = require("chai").expect
|
||||||
Project = class Project
|
Project = class Project
|
||||||
|
|
||||||
project = _id : "1234566", rootFolder:[]
|
project = _id : "1234566", rootFolder:[]
|
||||||
|
@ -221,6 +221,7 @@ describe 'project model', ->
|
||||||
assert.equal element, undefined
|
assert.equal element, undefined
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
||||||
describe "where duplicate folder exists", ->
|
describe "where duplicate folder exists", ->
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
@ -266,7 +267,23 @@ describe 'project model', ->
|
||||||
element.name.should.equal "other.tex"
|
element.name.should.equal "other.tex"
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
describe "with a null project", ->
|
||||||
|
beforeEach ->
|
||||||
|
@project =
|
||||||
|
rootFolder:[
|
||||||
|
folders: []
|
||||||
|
fileRefs: []
|
||||||
|
docs: [{name:"main.tex"}, null, {name:"other.tex"}]
|
||||||
|
]
|
||||||
|
Project.getProject = sinon.stub()
|
||||||
|
Project.getProject.callsArgWith(2, null)
|
||||||
|
|
||||||
|
it "should not crash with a null", (done)->
|
||||||
|
callback = sinon.stub()
|
||||||
|
@locator.findElementByPath project._id, "/other.tex", (err, element)->
|
||||||
|
expect(err).to.exist
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
describe 'finding a project by user_id and project name', ()->
|
describe 'finding a project by user_id and project name', ()->
|
||||||
|
|
|
@ -10,6 +10,18 @@ cookeFilePath = "/tmp/smoke-test-cookie-#{port}.txt"
|
||||||
buildUrl = (path) -> " -b #{cookeFilePath} --resolve 'smoke#{Settings.cookieDomain}:#{port}:127.0.0.1' http://smoke#{Settings.cookieDomain}:#{port}/#{path}?setLng=en"
|
buildUrl = (path) -> " -b #{cookeFilePath} --resolve 'smoke#{Settings.cookieDomain}:#{port}:127.0.0.1' http://smoke#{Settings.cookieDomain}:#{port}/#{path}?setLng=en"
|
||||||
logger = require "logger-sharelatex"
|
logger = require "logger-sharelatex"
|
||||||
|
|
||||||
|
# Change cookie to be non secure so curl will send it
|
||||||
|
convertCookieFile = (callback) ->
|
||||||
|
fs = require("fs")
|
||||||
|
fs.readFile cookeFilePath, "utf8", (err, data) ->
|
||||||
|
return callback(err) if err
|
||||||
|
firstTrue = data.indexOf("TRUE")
|
||||||
|
secondTrue = data.indexOf("TRUE", firstTrue+4)
|
||||||
|
result = data.slice(0, secondTrue)+"FALSE"+data.slice(secondTrue+4)
|
||||||
|
fs.writeFile cookeFilePath, result, "utf8", (err) ->
|
||||||
|
return callback(err) if err
|
||||||
|
callback()
|
||||||
|
|
||||||
describe "Opening", ->
|
describe "Opening", ->
|
||||||
|
|
||||||
before (done) ->
|
before (done) ->
|
||||||
|
@ -26,20 +38,14 @@ describe "Opening", ->
|
||||||
return done("smoke test: does not have csrf token")
|
return done("smoke test: does not have csrf token")
|
||||||
csrf = csrfMatches[1]
|
csrf = csrfMatches[1]
|
||||||
|
|
||||||
# Change cookie to be non secure so curl will send it
|
convertCookieFile (err) ->
|
||||||
fs = require("fs")
|
return done(err) if err?
|
||||||
fs.readFile cookeFilePath, "utf8", (err, data) ->
|
command = """
|
||||||
return done(err) if err
|
curl -c #{cookeFilePath} -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d '{"_csrf":"#{csrf}", "email":"#{Settings.smokeTest.user}", "password":"#{Settings.smokeTest.password}"}' #{buildUrl('register')}
|
||||||
firstTrue = data.indexOf("TRUE")
|
"""
|
||||||
secondTrue = data.indexOf("TRUE", firstTrue+4)
|
child.exec command, (err) ->
|
||||||
result = data.slice(0, secondTrue)+"FALSE"+data.slice(secondTrue+4)
|
return done(err) if err?
|
||||||
fs.writeFile cookeFilePath, result, "utf8", (err) ->
|
convertCookieFile done
|
||||||
return done(err) if err
|
|
||||||
|
|
||||||
command = """
|
|
||||||
curl -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d '{"_csrf":"#{csrf}", "email":"#{Settings.smokeTest.user}", "password":"#{Settings.smokeTest.password}"}' #{buildUrl('register')}
|
|
||||||
"""
|
|
||||||
child.exec command, done
|
|
||||||
|
|
||||||
after (done)->
|
after (done)->
|
||||||
command = """
|
command = """
|
||||||
|
|
Loading…
Reference in a new issue