Merge branch 'master' into support-cached-pdfs

This commit is contained in:
Brian Gough 2015-03-02 09:20:00 +00:00
commit 0315954b47
31 changed files with 240 additions and 166 deletions

View file

@ -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) ->) ->

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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])

View file

@ -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?

View file

@ -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

View file

@ -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 {

View file

@ -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 =

View file

@ -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

View file

@ -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")

View file

@ -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"

View file

@ -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")}

View file

@ -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").

View file

@ -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')

View file

@ -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'

View file

@ -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",

View file

@ -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.

View file

@ -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()

View file

@ -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)

View file

@ -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
} }

View file

@ -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

View file

@ -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

View file

@ -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 = () ->

View file

@ -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 = []

View file

@ -24,6 +24,6 @@ define [
clearHighlights: () -> clearHighlights: () ->
for h in @highlightElements for h in @highlightElements
h.remove() h?.remove()
@highlightElements = [] @highlightElements = []
] ]

View file

@ -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 = ""

View file

@ -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;

View file

@ -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()

View file

@ -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', ()->

View file

@ -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 = """