diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee
index 7ae6cba36c..f2ce93f671 100644
--- a/services/web/Gruntfile.coffee
+++ b/services/web/Gruntfile.coffee
@@ -230,11 +230,11 @@ module.exports = (grunt) ->
sed:
version:
- path: "app/views/sentry.jade"
+ path: "app/views/sentry.pug"
pattern: '@@COMMIT@@',
replacement: '<%= commit %>',
release:
- path: "app/views/sentry.jade"
+ path: "app/views/sentry.pug"
pattern: "@@RELEASE@@"
replacement: process.env.BUILD_NUMBER || "(unknown build)"
@@ -397,5 +397,5 @@ module.exports = (grunt) ->
grunt.registerTask 'default', 'run'
- grunt.registerTask 'version', "Write the version number into sentry.jade", ['git-rev-parse', 'sed']
+ grunt.registerTask 'version', "Write the version number into sentry.pug", ['git-rev-parse', 'sed']
diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee
index 1fde81f5c9..a2314da57f 100644
--- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee
+++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee
@@ -23,7 +23,7 @@ module.exports = CollaboratorsInviteController =
return next(err)
res.json({invites: invites})
- _checkShouldInviteEmail: (sendingUser, email, callback=(err, shouldAllowInvite)->) ->
+ _checkShouldInviteEmail: (email, callback=(err, shouldAllowInvite)->) ->
if Settings.restrictInvitesToExistingAccounts == true
logger.log {email}, "checking if user exists with this email"
UserGetter.getUser {email: email}, {_id: 1}, (err, user) ->
@@ -31,19 +31,20 @@ module.exports = CollaboratorsInviteController =
userExists = user? and user?._id?
callback(null, userExists)
else
- UserGetter.getUser sendingUser._id, {features:1, _id:1}, (err, user)->
- if err?
- return callback(err)
- collabLimit = user?.features?.collaborators || 1
- if collabLimit == -1
- collabLimit = 20
- collabLimit = collabLimit * 10
- opts =
- endpointName: "invite_to_project"
- timeInterval: 60 * 30
- subjectName: sendingUser._id
- throttle: collabLimit
- rateLimiter.addCount opts, callback
+ callback(null, true)
+
+ _checkRateLimit: (user_id, callback = (error) ->) ->
+ LimitationsManager.allowedNumberOfCollaboratorsForUser user_id, (err, collabLimit = 1)->
+ return callback(err) if err?
+ if collabLimit == -1
+ collabLimit = 20
+ collabLimit = collabLimit * 10
+ opts =
+ endpointName: "invite-to-project-by-user-id"
+ timeInterval: 60 * 30
+ subjectName: user_id
+ throttle: collabLimit
+ rateLimiter.addCount opts, callback
inviteToProject: (req, res, next) ->
projectId = req.params.Project_id
@@ -64,20 +65,24 @@ module.exports = CollaboratorsInviteController =
if !email? or email == ""
logger.log {projectId, email, sendingUserId}, "invalid email address"
return res.sendStatus(400)
- CollaboratorsInviteController._checkShouldInviteEmail sendingUser, email, (err, shouldAllowInvite)->
- if err?
- logger.err {err, email, projectId, sendingUserId}, "error checking if we can invite this email address"
- return next(err)
- if !shouldAllowInvite
- logger.log {email, projectId, sendingUserId}, "not allowed to send an invite to this email address"
- return res.json {invite: null, error: 'cannot_invite_non_user'}
- CollaboratorsInviteHandler.inviteToProject projectId, sendingUser, email, privileges, (err, invite) ->
+ CollaboratorsInviteController._checkRateLimit sendingUserId, (error, underRateLimit) ->
+ return next(error) if error?
+ if !underRateLimit
+ return res.sendStatus(429)
+ CollaboratorsInviteController._checkShouldInviteEmail email, (err, shouldAllowInvite)->
if err?
- logger.err {projectId, email, sendingUserId}, "error creating project invite"
+ logger.err {err, email, projectId, sendingUserId}, "error checking if we can invite this email address"
return next(err)
- logger.log {projectId, email, sendingUserId}, "invite created"
- EditorRealTimeController.emitToRoom(projectId, 'project:membership:changed', {invites: true})
- return res.json {invite: invite}
+ if !shouldAllowInvite
+ logger.log {email, projectId, sendingUserId}, "not allowed to send an invite to this email address"
+ return res.json {invite: null, error: 'cannot_invite_non_user'}
+ CollaboratorsInviteHandler.inviteToProject projectId, sendingUser, email, privileges, (err, invite) ->
+ if err?
+ logger.err {projectId, email, sendingUserId}, "error creating project invite"
+ return next(err)
+ logger.log {projectId, email, sendingUserId}, "invite created"
+ EditorRealTimeController.emitToRoom(projectId, 'project:membership:changed', {invites: true})
+ return res.json {invite: invite}
revokeInvite: (req, res, next) ->
projectId = req.params.Project_id
diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee
index 0e6cd8876c..ecca8ab86f 100644
--- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee
+++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee
@@ -80,7 +80,7 @@ module.exports = CollaboratorsInviteHandler =
# Send email and notification in background
CollaboratorsInviteHandler._sendMessages projectId, sendingUser, invite, (err) ->
if err?
- logger.err {projectId, email}, "error sending messages for invite"
+ logger.err {err, projectId, email}, "error sending messages for invite"
callback(null, invite)
diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee
index 8b130d27db..ea7e1f89f8 100644
--- a/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee
+++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee
@@ -22,13 +22,13 @@ module.exports =
webRouter.post(
'/project/:Project_id/invite',
RateLimiterMiddlewear.rateLimit({
- endpointName: "invite-to-project"
+ endpointName: "invite-to-project-by-project-id"
params: ["Project_id"]
maxRequests: 100
timeInterval: 60 * 10
}),
RateLimiterMiddlewear.rateLimit({
- endpointName: "invite-to-project-ip"
+ endpointName: "invite-to-project-by-ip"
ipOnly:true
maxRequests: 100
timeInterval: 60 * 10
diff --git a/services/web/app/coffee/Features/Email/EmailBuilder.coffee b/services/web/app/coffee/Features/Email/EmailBuilder.coffee
index 5360adb7a8..0a06a2a175 100644
--- a/services/web/app/coffee/Features/Email/EmailBuilder.coffee
+++ b/services/web/app/coffee/Features/Email/EmailBuilder.coffee
@@ -97,7 +97,7 @@ Thank you
templates.projectInvite =
- subject: _.template "<%= project.name.slice(0, 40) %> - shared by <%= owner.email %>"
+ subject: _.template "<%= project.name %> - shared by <%= owner.email %>"
layout: BaseWithHeaderEmailLayout
type:"notification"
plainTextTemplate: _.template """
@@ -111,16 +111,16 @@ Thank you
"""
compiledTemplate: (opts) ->
SingleCTAEmailBody({
- title: "#{ opts.project.name.slice(0, 40) } – shared by #{ opts.owner.email }"
+ title: "#{ opts.project.name } – shared by #{ opts.owner.email }"
greeting: "Hi,"
- message: "#{ opts.owner.email } wants to share “#{ opts.project.name.slice(0, 40) }” with you."
+ message: "#{ opts.owner.email } wants to share “#{ opts.project.name }” with you."
secondaryMessage: null
ctaText: "View project"
ctaURL: opts.inviteUrl
gmailGoToAction:
target: opts.inviteUrl
name: "View project"
- description: "Join #{ opts.project.name.slice(0, 40) } at ShareLaTeX"
+ description: "Join #{ opts.project.name } at ShareLaTeX"
})
templates.completeJoinGroupAccount =
diff --git a/services/web/app/coffee/Features/StaticPages/HomeController.coffee b/services/web/app/coffee/Features/StaticPages/HomeController.coffee
index 6675d55333..c1a8c46323 100755
--- a/services/web/app/coffee/Features/StaticPages/HomeController.coffee
+++ b/services/web/app/coffee/Features/StaticPages/HomeController.coffee
@@ -7,7 +7,7 @@ fs = require "fs"
ErrorController = require "../Errors/ErrorController"
AuthenticationController = require('../Authentication/AuthenticationController')
-homepageExists = fs.existsSync Path.resolve(__dirname + "/../../../views/external/home.jade")
+homepageExists = fs.existsSync Path.resolve(__dirname + "/../../../views/external/home.pug")
module.exports = HomeController =
index : (req,res)->
@@ -28,10 +28,10 @@ module.exports = HomeController =
externalPage: (page, title) ->
return (req, res, next = (error) ->) ->
- path = Path.resolve(__dirname + "/../../../views/external/#{page}.jade")
+ path = Path.resolve(__dirname + "/../../../views/external/#{page}.pug")
fs.exists path, (exists) -> # No error in this callback - old method in Node.js!
if exists
- res.render "external/#{page}.jade",
+ res.render "external/#{page}.pug",
title: title
else
ErrorController.notFound(req, res, next)
diff --git a/services/web/app/coffee/Features/Subscription/LimitationsManager.coffee b/services/web/app/coffee/Features/Subscription/LimitationsManager.coffee
index 59a0748f36..ec29b9257a 100644
--- a/services/web/app/coffee/Features/Subscription/LimitationsManager.coffee
+++ b/services/web/app/coffee/Features/Subscription/LimitationsManager.coffee
@@ -1,20 +1,25 @@
logger = require("logger-sharelatex")
Project = require("../../models/Project").Project
-User = require("../../models/User").User
+UserGetter = require("../User/UserGetter")
SubscriptionLocator = require("./SubscriptionLocator")
Settings = require("settings-sharelatex")
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
CollaboratorsInvitesHandler = require("../Collaborators/CollaboratorsInviteHandler")
module.exports =
-
allowedNumberOfCollaboratorsInProject: (project_id, callback) ->
- getOwnerOfProject project_id, (error, owner)->
+ Project.findById project_id, 'owner_ref', (error, project) =>
return callback(error) if error?
- if owner.features? and owner.features.collaborators?
- callback null, owner.features.collaborators
+ @allowedNumberOfCollaboratorsForUser project.owner_ref, callback
+
+ allowedNumberOfCollaboratorsForUser: (user_id, callback) ->
+ UserGetter.getUser user_id, {features: 1}, (error, user) ->
+ return callback(error) if error?
+ if user.features? and user.features.collaborators?
+ callback null, user.features.collaborators
else
callback null, Settings.defaultPlanCode.collaborators
+
canAddXCollaborators: (project_id, x_collaborators, callback = (error, allowed)->) ->
@allowedNumberOfCollaboratorsInProject project_id, (error, allowed_number) =>
@@ -63,8 +68,4 @@ module.exports =
logger.log user_id:user_id, limitReached:limitReached, currentTotal: subscription.member_ids.length, membersLimit: subscription.membersLimit, "checking if subscription members limit has been reached"
callback(err, limitReached, subscription)
-getOwnerOfProject = (project_id, callback)->
- Project.findById project_id, 'owner_ref', (error, project) ->
- return callback(error) if error?
- User.findById project.owner_ref, (error, owner) ->
- callback(error, owner)
+getOwnerIdOfProject = (project_id, callback)->
diff --git a/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee b/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee
index 5e20c2b515..bce0befe22 100644
--- a/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee
+++ b/services/web/app/coffee/Features/ThirdPartyDataStore/UpdateMerger.coffee
@@ -4,7 +4,7 @@ editorController = require('../Editor/EditorController')
logger = require('logger-sharelatex')
Settings = require('settings-sharelatex')
FileTypeManager = require('../Uploads/FileTypeManager')
-uuid = require('node-uuid')
+uuid = require('uuid')
fs = require('fs')
module.exports =
diff --git a/services/web/app/coffee/infrastructure/Modules.coffee b/services/web/app/coffee/infrastructure/Modules.coffee
index 1b3c2ea9a5..e7d521fa56 100644
--- a/services/web/app/coffee/infrastructure/Modules.coffee
+++ b/services/web/app/coffee/infrastructure/Modules.coffee
@@ -1,6 +1,6 @@
fs = require "fs"
Path = require "path"
-jade = require "jade"
+pug = require "pug"
async = require "async"
MODULE_BASE_PATH = Path.resolve(__dirname + "/../../../modules")
@@ -29,7 +29,7 @@ module.exports = Modules =
for module in @modules
for view, partial of module.viewIncludes or {}
@viewIncludes[view] ||= []
- @viewIncludes[view].push jade.compile(fs.readFileSync(Path.join(MODULE_BASE_PATH, module.name, "app/views", partial + ".jade")), doctype: "html")
+ @viewIncludes[view].push pug.compile(fs.readFileSync(Path.join(MODULE_BASE_PATH, module.name, "app/views", partial + ".pug")), doctype: "html")
moduleIncludes: (view, locals) ->
compiledPartials = Modules.viewIncludes[view] or []
diff --git a/services/web/app/coffee/infrastructure/Server.coffee b/services/web/app/coffee/infrastructure/Server.coffee
index 01d431fa49..2218b72ecb 100644
--- a/services/web/app/coffee/infrastructure/Server.coffee
+++ b/services/web/app/coffee/infrastructure/Server.coffee
@@ -61,7 +61,7 @@ if Settings.behindProxy
webRouter.use express.static(__dirname + '/../../../public', {maxAge: staticCacheAge })
app.set 'views', __dirname + '/../../views'
-app.set 'view engine', 'jade'
+app.set 'view engine', 'pug'
Modules.loadViewIncludes app
diff --git a/services/web/app/coffee/models/User.coffee b/services/web/app/coffee/models/User.coffee
index c6e469a1f4..44cea29a70 100644
--- a/services/web/app/coffee/models/User.coffee
+++ b/services/web/app/coffee/models/User.coffee
@@ -2,7 +2,7 @@ Project = require('./Project').Project
Settings = require 'settings-sharelatex'
_ = require('underscore')
mongoose = require('mongoose')
-uuid = require('node-uuid')
+uuid = require('uuid')
Schema = mongoose.Schema
ObjectId = Schema.ObjectId
diff --git a/services/web/app/views/admin/index.jade b/services/web/app/views/admin/index.pug
similarity index 97%
rename from services/web/app/views/admin/index.jade
rename to services/web/app/views/admin/index.pug
index 0fa9017cc1..050829e9d7 100644
--- a/services/web/app/views/admin/index.jade
+++ b/services/web/app/views/admin/index.pug
@@ -28,10 +28,10 @@ block content
tab(heading="Open Sockets")
.row-spaced
ul
- -each agents, url in openSockets
+ each agents, url in openSockets
li #{url} - total : #{agents.length}
ul
- -each agent in agents
+ each agent in agents
li #{agent}
tab(heading="Close Editor")
diff --git a/services/web/app/views/admin/register.jade b/services/web/app/views/admin/register.pug
similarity index 100%
rename from services/web/app/views/admin/register.jade
rename to services/web/app/views/admin/register.pug
diff --git a/services/web/app/views/beta_program/opt_in.jade b/services/web/app/views/beta_program/opt_in.pug
similarity index 100%
rename from services/web/app/views/beta_program/opt_in.jade
rename to services/web/app/views/beta_program/opt_in.pug
diff --git a/services/web/app/views/blog/blog_holder.jade b/services/web/app/views/blog/blog_holder.pug
similarity index 100%
rename from services/web/app/views/blog/blog_holder.jade
rename to services/web/app/views/blog/blog_holder.pug
diff --git a/services/web/app/views/contact-us-modal.jade b/services/web/app/views/contact-us-modal.pug
similarity index 82%
rename from services/web/app/views/contact-us-modal.jade
rename to services/web/app/views/contact-us-modal.pug
index f4fd8938b7..d3e5aa0e87 100644
--- a/services/web/app/views/contact-us-modal.jade
+++ b/services/web/app/views/contact-us-modal.pug
@@ -24,10 +24,10 @@ script(type='text/ng-template', id='supportModalTemplate')
a.contact-suggestion-list-item(ng-href="{{ suggestion.url }}", ng-click="clickSuggestionLink(suggestion.url);" target="_blank")
span(ng-bind-html="suggestion.name")
i.fa.fa-angle-right
- label.desc(ng-show="'#{getUserEmail()}'.length < 1")
+ label.desc(ng-show="'"+getUserEmail()+"'.length < 1")
| #{translate("email")}
- .form-group(ng-show="'#{getUserEmail()}'.length < 1")
- input.field.text.medium.span8.form-control(ng-model="form.email", ng-init="form.email = '#{getUserEmail()}'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
+ .form-group(ng-show="'"+getUserEmail()+"'.length < 1")
+ input.field.text.medium.span8.form-control(ng-model="form.email", ng-init="form.email = '"+getUserEmail()+"'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
label#title12.desc
| #{translate("project_url")} (#{translate("optional")})
.form-group
@@ -37,6 +37,6 @@ script(type='text/ng-template', id='supportModalTemplate')
.form-group
textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', tabindex='4', onkeyup='')
.form-group.text-center
- input.btn-success.btn.btn-lg(type='submit', ng-disabled="sending", ng-click="contactUs()" value='#{translate("contact_us")}')
+ input.btn-success.btn.btn-lg(type='submit', ng-disabled="sending", ng-click="contactUs()" value=translate("contact_us"))
span(ng-show="sent")
- p #{translate("request_sent_thank_you")}
\ No newline at end of file
+ p #{translate("request_sent_thank_you")}
diff --git a/services/web/app/views/general/404.jade b/services/web/app/views/general/404.pug
similarity index 100%
rename from services/web/app/views/general/404.jade
rename to services/web/app/views/general/404.pug
diff --git a/services/web/app/views/general/500.jade b/services/web/app/views/general/500.pug
similarity index 100%
rename from services/web/app/views/general/500.jade
rename to services/web/app/views/general/500.pug
diff --git a/services/web/app/views/general/closed.jade b/services/web/app/views/general/closed.pug
similarity index 100%
rename from services/web/app/views/general/closed.jade
rename to services/web/app/views/general/closed.pug
diff --git a/services/web/app/views/layout.jade b/services/web/app/views/layout.pug
similarity index 97%
rename from services/web/app/views/layout.jade
rename to services/web/app/views/layout.pug
index 8f4d1263db..75c96ff276 100644
--- a/services/web/app/views/layout.jade
+++ b/services/web/app/views/layout.pug
@@ -21,6 +21,8 @@ html(itemscope, itemtype='http://schema.org/Product')
link(rel="icon", href="/favicon.ico")
link(rel='stylesheet', href=buildCssPath('/style.css'))
+ block _headLinks
+
if settings.i18n.subdomainLang
each subdomainDetails in settings.i18n.subdomainLang
if !subdomainDetails.hide
@@ -30,7 +32,7 @@ html(itemscope, itemtype='http://schema.org/Product')
meta(itemprop="name", content="ShareLaTeX, the Online LaTeX Editor")
-if (typeof(meta) == "undefined")
- meta(itemprop="description", name="description", content='#{translate("site_description")}')
+ meta(itemprop="description", name="description", content=translate("site_description"))
-else
meta(itemprop="description", name="description" , content=meta)
diff --git a/services/web/app/views/layout/footer.jade b/services/web/app/views/layout/footer.pug
similarity index 87%
rename from services/web/app/views/layout/footer.jade
rename to services/web/app/views/layout/footer.pug
index efd64b6f6e..62a98ecfaa 100644
--- a/services/web/app/views/layout/footer.jade
+++ b/services/web/app/views/layout/footer.pug
@@ -13,9 +13,9 @@ footer.site-footer
data-toggle="dropdown",
aria-haspopup="true",
aria-expanded="false",
- tooltip="#{translate('language')}"
+ tooltip=translate('language')
)
- figure(class="sprite-icon sprite-icon-lang sprite-icon-#{currentLngCode}")
+ figure(class="sprite-icon sprite-icon-lang sprite-icon-"+currentLngCode)
ul.dropdown-menu(role="menu")
li.dropdown-header #{translate("language")}
@@ -23,7 +23,7 @@ footer.site-footer
if !subdomainDetails.hide
li.lngOption
a.menu-indent(href=subdomainDetails.url+currentUrl)
- figure(class="sprite-icon sprite-icon-lang sprite-icon-#{subdomainDetails.lngCode}")
+ figure(class="sprite-icon sprite-icon-lang sprite-icon-"+subdomainDetails.lngCode)
| #{translate(subdomainDetails.lngCode)}
//- img(src="/img/flags/24/.png")
each item in nav.left_footer
diff --git a/services/web/app/views/layout/navbar.jade b/services/web/app/views/layout/navbar.pug
similarity index 96%
rename from services/web/app/views/layout/navbar.jade
rename to services/web/app/views/layout/navbar.pug
index 4d78d02fba..54509d6565 100644
--- a/services/web/app/views/layout/navbar.jade
+++ b/services/web/app/views/layout/navbar.pug
@@ -4,7 +4,7 @@ nav.navbar.navbar-default
button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}")
i.fa.fa-bars
if settings.nav.custom_logo
- a(href='/', style='background-image:url("#{settings.nav.custom_logo}")').navbar-brand
+ a(href='/', style='background-image:url("'+settings.nav.custom_logo+'")').navbar-brand
else if (nav.title)
a(href='/').navbar-title #{nav.title}
else
diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.pug
similarity index 100%
rename from services/web/app/views/project/editor.jade
rename to services/web/app/views/project/editor.pug
diff --git a/services/web/app/views/project/editor/binary-file.jade b/services/web/app/views/project/editor/binary-file.pug
similarity index 100%
rename from services/web/app/views/project/editor/binary-file.jade
rename to services/web/app/views/project/editor/binary-file.pug
diff --git a/services/web/app/views/project/editor/chat.jade b/services/web/app/views/project/editor/chat.pug
similarity index 97%
rename from services/web/app/views/project/editor/chat.jade
rename to services/web/app/views/project/editor/chat.pug
index 47a1752834..fcd47a81e3 100644
--- a/services/web/app/views/project/editor/chat.jade
+++ b/services/web/app/views/project/editor/chat.pug
@@ -50,7 +50,7 @@ aside.chat(
.new-message
textarea(
- placeholder="#{translate('your_message')}...",
+ placeholder=translate('your_message')+"...",
on-enter="sendMessage()",
ng-model="newMessageContent",
ng-click="resetUnreadMessages()"
diff --git a/services/web/app/views/project/editor/editor.jade b/services/web/app/views/project/editor/editor.pug
similarity index 96%
rename from services/web/app/views/project/editor/editor.jade
rename to services/web/app/views/project/editor/editor.pug
index bcb778fda4..98a2840069 100644
--- a/services/web/app/views/project/editor/editor.jade
+++ b/services/web/app/views/project/editor/editor.pug
@@ -70,7 +70,7 @@ div.full-size(
ng-controller="PdfSynctexController"
)
a.btn.btn-default.btn-xs(
- tooltip="#{translate('go_to_code_location_in_pdf')}"
+ tooltip=translate('go_to_code_location_in_pdf')
tooltip-placement="right"
tooltip-append-to-body="true"
ng-click="syncToPdf()"
@@ -78,7 +78,7 @@ div.full-size(
i.fa.fa-long-arrow-right
br
a.btn.btn-default.btn-xs(
- tooltip-html="'#{translate('go_to_pdf_location_in_code')}'"
+ tooltip-html="'"+translate('go_to_pdf_location_in_code')+"'"
tooltip-placement="right"
tooltip-append-to-body="true"
ng-click="syncToCode()"
@@ -90,4 +90,4 @@ div.full-size(
ng-show="ui.view == 'pdf'"
)
include ./pdf
-
\ No newline at end of file
+
diff --git a/services/web/app/views/project/editor/feature-onboarding.jade b/services/web/app/views/project/editor/feature-onboarding.pug
similarity index 100%
rename from services/web/app/views/project/editor/feature-onboarding.jade
rename to services/web/app/views/project/editor/feature-onboarding.pug
diff --git a/services/web/app/views/project/editor/file-tree.jade b/services/web/app/views/project/editor/file-tree.pug
similarity index 97%
rename from services/web/app/views/project/editor/file-tree.jade
rename to services/web/app/views/project/editor/file-tree.pug
index 92af8b627d..03c2bd79b7 100644
--- a/services/web/app/views/project/editor/file-tree.jade
+++ b/services/web/app/views/project/editor/file-tree.pug
@@ -3,21 +3,21 @@ aside#file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
a(
href,
ng-click="openNewDocModal()",
- tooltip-html="'#{translate('new_file').replace(' ', '
')}'",
+ tooltip-html="'"+translate('new_file').replace(' ', '
')+"'",
tooltip-placement="bottom"
)
i.fa.fa-file
a(
href,
ng-click="openNewFolderModal()",
- tooltip-html="'#{translate('new_folder').replace(' ', '
')}'",
+ tooltip-html="'"+translate('new_folder').replace(' ', '
')+"'",
tooltip-placement="bottom"
)
i.fa.fa-folder
a(
href,
ng-click="openUploadFileModal()",
- tooltip="#{translate('upload')}",
+ tooltip=translate('upload'),
tooltip-placement="bottom"
)
i.fa.fa-upload
@@ -26,7 +26,7 @@ aside#file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
a(
href,
ng-click="startRenamingSelected()",
- tooltip="#{translate('rename')}",
+ tooltip=translate('rename'),
tooltip-placement="bottom",
ng-show="multiSelectedCount == 0"
)
@@ -34,7 +34,7 @@ aside#file-tree(ng-controller="FileTreeController", ng-class="{ 'multi-selected'
a(
href,
ng-click="openDeleteModalForSelected()",
- tooltip="#{translate('delete')}",
+ tooltip=translate('delete'),
tooltip-placement="bottom",
tooltip-append-to-body="true"
)
@@ -431,4 +431,4 @@ script(type='text/ng-template', id='invalidFileNameModalTemplate')
.modal-footer
button.btn.btn-default(
ng-click="$close()"
- ) #{translate('ok')}
\ No newline at end of file
+ ) #{translate('ok')}
diff --git a/services/web/app/views/project/editor/header.jade b/services/web/app/views/project/editor/header.pug
similarity index 96%
rename from services/web/app/views/project/editor/header.jade
rename to services/web/app/views/project/editor/header.pug
index 601b18e9cf..475ba1da52 100644
--- a/services/web/app/views/project/editor/header.jade
+++ b/services/web/app/views/project/editor/header.pug
@@ -45,7 +45,7 @@ header.toolbar.toolbar-header.toolbar-with-labels(
ng-if="permissions.admin",
href='#',
tooltip-placement="bottom",
- tooltip="#{translate('rename')}",
+ tooltip=translate('rename'),
tooltip-append-to-body="true",
ng-click="startRenaming()",
ng-show="!state.renaming"
@@ -71,7 +71,7 @@ header.toolbar.toolbar-header.toolbar-with-labels(
span.dropdown(dropdown, ng-if="onlineUsersArray.length >= 4")
span.online-user.online-user-multi(
dropdown-toggle,
- tooltip="#{translate('connected_users')}",
+ tooltip=translate('connected_users'),
tooltip-placement="left"
)
strong {{ onlineUsersArray.length }}
@@ -121,4 +121,4 @@ header.toolbar.toolbar-header.toolbar-with-labels(
span.label.label-info(
ng-show="unreadMessages > 0"
) {{ unreadMessages }}
- p.toolbar-label #{translate("chat")}
\ No newline at end of file
+ p.toolbar-label #{translate("chat")}
diff --git a/services/web/app/views/project/editor/history.jade b/services/web/app/views/project/editor/history.pug
similarity index 100%
rename from services/web/app/views/project/editor/history.jade
rename to services/web/app/views/project/editor/history.pug
diff --git a/services/web/app/views/project/editor/hotkeys.jade b/services/web/app/views/project/editor/hotkeys.pug
similarity index 100%
rename from services/web/app/views/project/editor/hotkeys.jade
rename to services/web/app/views/project/editor/hotkeys.pug
diff --git a/services/web/app/views/project/editor/left-menu.jade b/services/web/app/views/project/editor/left-menu.pug
similarity index 96%
rename from services/web/app/views/project/editor/left-menu.jade
rename to services/web/app/views/project/editor/left-menu.pug
index 80d5c606ab..4e86b31620 100644
--- a/services/web/app/views/project/editor/left-menu.jade
+++ b/services/web/app/views/project/editor/left-menu.pug
@@ -24,7 +24,7 @@ aside#left-menu.full-size(
| PDF
div.link-disabled(
ng-if="!pdf.url"
- tooltip="#{translate('please_compile_pdf_before_download')}"
+ tooltip=translate('please_compile_pdf_before_download')
tooltip-placement="bottom"
)
i.fa.fa-file-pdf-o.fa-2x
@@ -47,7 +47,7 @@ aside#left-menu.full-size(
a(href, ng-if="pdf.url" ,ng-click="openWordCountModal()")
i.fa.fa-fw.fa-eye
span #{translate("word_count")}
- a.link-disabled(href, ng-if="!pdf.url" , tooltip="#{translate('please_compile_pdf_before_word_count')}")
+ a.link-disabled(href, ng-if="!pdf.url" , tooltip=translate('please_compile_pdf_before_word_count'))
i.fa.fa-fw.fa-eye
span.link-disabled #{translate("word_count")}
@@ -200,7 +200,7 @@ script(type='text/ng-template', id='wordCountModalTemplate')
span #{translate("loading")}...
div.pdf-disabled(
ng-if="!pdf.url"
- tooltip="#{translate('please_compile_pdf_before_word_count')}"
+ tooltip=translate('please_compile_pdf_before_word_count')
tooltip-placement="bottom"
)
div(ng-if="!status.loading")
diff --git a/services/web/app/views/project/editor/pdf.jade b/services/web/app/views/project/editor/pdf.pug
similarity index 97%
rename from services/web/app/views/project/editor/pdf.jade
rename to services/web/app/views/project/editor/pdf.pug
index 074856bd7c..ba03f068a6 100644
--- a/services/web/app/views/project/editor/pdf.jade
+++ b/services/web/app/views/project/editor/pdf.pug
@@ -2,7 +2,7 @@ div.full-size.pdf(ng-controller="PdfController")
.toolbar.toolbar-tall
.btn-group(
dropdown,
- tooltip-html="'#{translate('recompile_pdf')} ({{modifierKey}} + Enter)'"
+ tooltip-html="'"+translate('recompile_pdf')+" ({{modifierKey}} + Enter)'"
tooltip-class="keyboard-tooltip"
tooltip-popup-delay="500"
tooltip-append-to-body="true"
@@ -53,7 +53,7 @@ div.full-size.pdf(ng-controller="PdfController")
href
ng-click="stop()"
ng-show="pdf.compiling",
- tooltip="#{translate('stop_compile')}"
+ tooltip=translate('stop_compile')
tooltip-placement="bottom"
)
i.fa.fa-stop()
@@ -61,7 +61,7 @@ div.full-size.pdf(ng-controller="PdfController")
href
ng-click="toggleLogs()"
ng-class="{ 'active': shouldShowLogs == true }"
- tooltip="#{translate('logs_and_output_files')}"
+ tooltip=translate('logs_and_output_files')
tooltip-placement="bottom"
)
i.fa.fa-file-text-o
@@ -77,7 +77,7 @@ div.full-size.pdf(ng-controller="PdfController")
ng-href="{{pdf.downloadUrl || pdf.url}}"
target="_blank"
ng-if="pdf.url"
- tooltip="#{translate('download_pdf')}"
+ tooltip=translate('download_pdf')
tooltip-placement="bottom"
)
i.fa.fa-download
@@ -87,7 +87,7 @@ div.full-size.pdf(ng-controller="PdfController")
href,
ng-click="switchToFlatLayout()"
ng-show="ui.pdfLayout == 'sideBySide'"
- tooltip="#{translate('full_screen')}"
+ tooltip=translate('full_screen')
tooltip-placement="bottom"
tooltip-append-to-body="true"
)
@@ -96,7 +96,7 @@ div.full-size.pdf(ng-controller="PdfController")
href,
ng-click="switchToSideBySideLayout()"
ng-show="ui.pdfLayout == 'flat'"
- tooltip="#{translate('split_screen')}"
+ tooltip=translate('split_screen')
tooltip-placement="bottom"
tooltip-append-to-body="true"
)
@@ -233,7 +233,7 @@ div.full-size.pdf(ng-controller="PdfController")
.files-dropdown-container
a.btn.btn-default.btn-sm(
href,
- tooltip="#{translate('clear_cached_files')}",
+ tooltip=translate('clear_cached_files'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="openClearCacheModal()"
diff --git a/services/web/app/views/project/editor/publish-template.jade b/services/web/app/views/project/editor/publish-template.pug
similarity index 100%
rename from services/web/app/views/project/editor/publish-template.jade
rename to services/web/app/views/project/editor/publish-template.pug
diff --git a/services/web/app/views/project/editor/review-panel.jade b/services/web/app/views/project/editor/review-panel.pug
similarity index 98%
rename from services/web/app/views/project/editor/review-panel.jade
rename to services/web/app/views/project/editor/review-panel.pug
index 9b31516b3f..43a854b591 100644
--- a/services/web/app/views/project/editor/review-panel.jade
+++ b/services/web/app/views/project/editor/review-panel.pug
@@ -41,7 +41,6 @@
.rp-entry-list-inner
.rp-entry-wrapper(
ng-repeat="(entry_id, entry) in reviewPanel.entries[editor.open_doc_id]"
- ng-if="!(entry.type === 'comment' && reviewPanel.commentThreads[entry.thread_id].resolved === true)"
)
div(ng-if="entry.type === 'insert' || entry.type === 'delete'")
change-entry(
@@ -50,6 +49,7 @@
on-reject="rejectChange(entry_id);"
on-accept="acceptChange(entry_id);"
on-indicator-click="toggleReviewPanel();"
+ on-body-click="gotoEntry(editor.open_doc_id, entry)"
permissions="permissions"
)
@@ -62,6 +62,7 @@
on-indicator-click="toggleReviewPanel();"
on-save-edit="saveEdit(entry.thread_id, comment)"
on-delete="deleteComment(entry.thread_id, comment)"
+ on-body-click="gotoEntry(editor.open_doc_id, entry)"
permissions="permissions"
ng-if="!reviewPanel.loadingThreads"
)
@@ -191,7 +192,7 @@ script(type='text/ng-template', id='commentEntryTemplate')
.rp-loading(ng-if="!threads[entry.thread_id].submitting && (!threads[entry.thread_id] || threads[entry.thread_id].messages.length == 0)")
| No comments
- div
+ .rp-comment-loaded
.rp-comment(
ng-repeat="comment in threads[entry.thread_id].messages track by comment.id"
)
diff --git a/services/web/app/views/project/editor/share.jade b/services/web/app/views/project/editor/share.pug
similarity index 97%
rename from services/web/app/views/project/editor/share.jade
rename to services/web/app/views/project/editor/share.pug
index 78fb69c333..32e0404afc 100644
--- a/services/web/app/views/project/editor/share.jade
+++ b/services/web/app/views/project/editor/share.pug
@@ -38,7 +38,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
.col-xs-1
a(
href
- tooltip="#{translate('remove_collaborator')}"
+ tooltip=translate('remove_collaborator')
tooltip-placement="bottom"
ng-click="removeMember(member)"
)
@@ -55,7 +55,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
.col-xs-1
a(
href
- tooltip="#{translate('revoke_invite')}"
+ tooltip=translate('revoke_invite')
tooltip-placement="bottom"
ng-click="revokeInvite(invite)"
)
@@ -66,7 +66,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
.form-group
tags-input(
template="shareTagTemplate"
- placeholder="#{settings.customisation.shareProjectPlaceholder || 'joe@example.com, sue@example.com, ...'}"
+ placeholder=settings.customisation.shareProjectPlaceholder || 'joe@example.com, sue@example.com, ...'
ng-model="inputs.contacts"
focus-on="open"
display-property="display"
diff --git a/services/web/app/views/project/invite/not-valid.jade b/services/web/app/views/project/invite/not-valid.pug
similarity index 100%
rename from services/web/app/views/project/invite/not-valid.jade
rename to services/web/app/views/project/invite/not-valid.pug
diff --git a/services/web/app/views/project/invite/show.jade b/services/web/app/views/project/invite/show.pug
similarity index 83%
rename from services/web/app/views/project/invite/show.jade
rename to services/web/app/views/project/invite/show.pug
index eed30d3d19..d129fe015d 100644
--- a/services/web/app/views/project/invite/show.jade
+++ b/services/web/app/views/project/invite/show.pug
@@ -20,12 +20,12 @@ block content
form.form(
name="acceptForm",
method="POST",
- action="/project/#{invite.projectId}/invite/token/#{invite.token}/accept"
+ action="/project/"+invite.projectId+"/invite/token/"+invite.token+"/accept"
)
input(name='_csrf', type='hidden', value=csrfToken)
- input(name='token', type='hidden', value="#{invite.token}")
+ input(name='token', type='hidden', value=invite.token)
.form-group.text-center
button.btn.btn-lg.btn-primary(type="submit")
| #{translate("join_project")}
.form-group.text-center
-
\ No newline at end of file
+
diff --git a/services/web/app/views/project/list.jade b/services/web/app/views/project/list.pug
similarity index 100%
rename from services/web/app/views/project/list.jade
rename to services/web/app/views/project/list.pug
diff --git a/services/web/app/views/project/list/empty-project-list.jade b/services/web/app/views/project/list/empty-project-list.pug
similarity index 100%
rename from services/web/app/views/project/list/empty-project-list.jade
rename to services/web/app/views/project/list/empty-project-list.pug
diff --git a/services/web/app/views/project/list/modals.jade b/services/web/app/views/project/list/modals.pug
similarity index 100%
rename from services/web/app/views/project/list/modals.jade
rename to services/web/app/views/project/list/modals.pug
diff --git a/services/web/app/views/project/list/notifications.jade b/services/web/app/views/project/list/notifications.pug
similarity index 100%
rename from services/web/app/views/project/list/notifications.jade
rename to services/web/app/views/project/list/notifications.pug
diff --git a/services/web/app/views/project/list/project-list.jade b/services/web/app/views/project/list/project-list.pug
similarity index 96%
rename from services/web/app/views/project/list/project-list.jade
rename to services/web/app/views/project/list/project-list.pug
index 01007213d0..0bb93e8336 100644
--- a/services/web/app/views/project/list/project-list.jade
+++ b/services/web/app/views/project/list/project-list.pug
@@ -6,7 +6,7 @@
form.project-search.form-horizontal(role="form")
.form-group.has-feedback.has-feedback-left.col-md-7.col-xs-12
input.form-control.col-md-7.col-xs-12(
- placeholder="#{translate('search_projects')}…",
+ placeholder=translate('search_projects')+"…",
autofocus='autofocus',
ng-model="searchText.value",
focus-on='search:clear',
@@ -25,7 +25,7 @@
.btn-group(ng-hide="selectedProjects.length < 1")
a.btn.btn-default(
href,
- tooltip="#{translate('download')}",
+ tooltip=translate('download'),
tooltip-placement="bottom",
tooltip-append-to-body="true",
ng-click="downloadSelectedProjects()"
@@ -33,7 +33,7 @@
i.fa.fa-cloud-download
a.btn.btn-default(
href,
- tooltip="#{translate('delete')}",
+ tooltip=translate('delete'),
tooltip-placement="bottom",
tooltip-append-to-body="true",
ng-click="openArchiveProjectsModal()"
@@ -45,7 +45,7 @@
href,
data-toggle="dropdown",
dropdown-toggle,
- tooltip="#{translate('add_to_folders')}",
+ tooltip=translate('add_to_folders'),
tooltip-append-to-body="true",
tooltip-placement="bottom"
)
diff --git a/services/web/app/views/project/list/side-bar.jade b/services/web/app/views/project/list/side-bar.pug
similarity index 100%
rename from services/web/app/views/project/list/side-bar.jade
rename to services/web/app/views/project/list/side-bar.pug
diff --git a/services/web/app/views/referal/bonus.jade b/services/web/app/views/referal/bonus.pug
similarity index 87%
rename from services/web/app/views/referal/bonus.jade
rename to services/web/app/views/referal/bonus.pug
index 5b0183d9c1..f3a65c6bc4 100644
--- a/services/web/app/views/referal/bonus.jade
+++ b/services/web/app/views/referal/bonus.pug
@@ -24,7 +24,7 @@ block content
.row
.col-md-8.col-md-offset-2.bonus-banner
.title
- a(href='https://twitter.com/share?text=is%20trying%20out%20the%20online%20LaTeX%20Editor%20ShareLaTeX&url=#{encodeURIComponent(buildReferalUrl("t"))}&counturl=https://www.sharelatex.com', target="_blank").twitter Tweet
+ a(href='https://twitter.com/share?text=is%20trying%20out%20the%20online%20LaTeX%20Editor%20ShareLaTeX&url='+encodeURIComponent(buildReferalUrl("t"))+'&counturl=https://www.sharelatex.com', target="_blank").twitter Tweet
.row
.col-md-8.col-md-offset-2.bonus-banner
@@ -34,12 +34,12 @@ block content
.row
.col-md-8.col-md-offset-2.bonus-banner
.title
- a(href="https://plus.google.com/share?url=#{encodeURIComponent(buildReferalUrl('gp'))}", onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;").google-plus #{translate("share_us_on_googleplus")}
+ a(href="https://plus.google.com/share?url="+encodeURIComponent(buildReferalUrl('gp')), onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;").google-plus #{translate("share_us_on_googleplus")}
.row
.col-md-8.col-md-offset-2.bonus-banner
.title
- a(href='mailto:?subject=Online LaTeX editor you may like &body=Hey, I have been using the online LaTeX editor ShareLaTeX recently and thought you might like to check it out. #{encodeURIComponent(buildReferalUrl("e"))}', title='Share by Email').email #{translate("email_us_to_your_friends")}
+ a(href='mailto:?subject=Online LaTeX editor you may like &body=Hey, I have been using the online LaTeX editor ShareLaTeX recently and thought you might like to check it out. '+encodeURIComponent(buildReferalUrl("e")), title='Share by Email').email #{translate("email_us_to_your_friends")}
.row
.col-md-8.col-md-offset-2.bonus-banner
@@ -58,9 +58,9 @@ block content
.col-md-10.col-md-offset-1.bonus-banner(style="position: relative; height: 30px; margin-top: 20px;")
- for (var i = 0; i <= 10; i++) {
- if (refered_user_count == i)
- .number(style="left: #{i}0%").active #{i}
+ .number(style="left: "+i+"0%").active #{i}
- else
- .number(style="left: #{i}0%") #{i}
+ .number(style="left: "+i+"0%") #{i}
- }
.row.ab-bonus
@@ -68,7 +68,7 @@ block content
.progress
- if (refered_user_count == 0)
div(style="text-align: center; padding: 4px;") #{translate("spread_the_word_and_fill_bar")}
- .progress-bar.progress-bar-info(style="width: #{refered_user_count}0%")
+ .progress-bar.progress-bar-info(style="width: "+refered_user_count+"0%")
.row.ab-bonus
.col-md-10.col-md-offset-1.bonus-banner(style="position: relative; height: 70px;")
diff --git a/services/web/app/views/restore.jade b/services/web/app/views/restore.pug
similarity index 84%
rename from services/web/app/views/restore.jade
rename to services/web/app/views/restore.pug
index 51c0ee03fc..1cd7ad458a 100644
--- a/services/web/app/views/restore.jade
+++ b/services/web/app/views/restore.pug
@@ -19,11 +19,11 @@ block content
.row-fluid
table.table
- -each project in projects
+ each project in projects
tr
- project_id = project._id.toString()
td(width="50%") #{project.name}
td(width="25%")
- a.btn(href="/project/#{project_id}/zip") Download latest version as Zip
+ a.btn(href="/project/"+project_id+"/zip") Download latest version as Zip
include general/small-footer
diff --git a/services/web/app/views/scribtex-modal.jade b/services/web/app/views/scribtex-modal.pug
similarity index 71%
rename from services/web/app/views/scribtex-modal.jade
rename to services/web/app/views/scribtex-modal.pug
index 1d4e2ce005..3efbf19314 100644
--- a/services/web/app/views/scribtex-modal.jade
+++ b/services/web/app/views/scribtex-modal.pug
@@ -5,4 +5,4 @@ script(type='text/ng-template', id='scribtexModalTemplate')
p ScribTeX has moved to https://scribtex.sharelatex.com. Please update your bookmarks.
p(style="text-align: center") You can find the page you were looking for here:
p(style="text-align: center")
- a(href="https://scribtex.sharelatex.com#{scribtexPath}", style="font-size: 16px") https://scribtex.sharelatex.com#{scribtexPath}
\ No newline at end of file
+ a(href="https://scribtex.sharelatex.com"+scribtexPath, style="font-size: 16px") https://scribtex.sharelatex.com#{scribtexPath}
diff --git a/services/web/app/views/sentry.jade b/services/web/app/views/sentry.pug
similarity index 96%
rename from services/web/app/views/sentry.jade
rename to services/web/app/views/sentry.pug
index 0a51686015..9e10d7837e 100644
--- a/services/web/app/views/sentry.jade
+++ b/services/web/app/views/sentry.pug
@@ -1,8 +1,8 @@
- if (typeof(sentrySrc) != "undefined")
- if (sentrySrc.match(/^([a-z]+:)?\/\//i))
- script(src="#{sentrySrc}")
+ script(src=sentrySrc)
- else
- script(src=buildJsPath("libs/#{sentrySrc}", {fingerprint:false}))
+ script(src=buildJsPath("libs/"+sentrySrc, {fingerprint:false}))
- if (typeof(sentrySrc) != "undefined")
script(type="text/javascript").
if (typeof(Raven) != "undefined" && Raven.config) {
diff --git a/services/web/app/views/subscriptions/custom_account.jade b/services/web/app/views/subscriptions/custom_account.pug
similarity index 100%
rename from services/web/app/views/subscriptions/custom_account.jade
rename to services/web/app/views/subscriptions/custom_account.pug
diff --git a/services/web/app/views/subscriptions/dashboard.jade b/services/web/app/views/subscriptions/dashboard.pug
similarity index 91%
rename from services/web/app/views/subscriptions/dashboard.jade
rename to services/web/app/views/subscriptions/dashboard.pug
index 3448dc9072..fb4f834c34 100644
--- a/services/web/app/views/subscriptions/dashboard.jade
+++ b/services/web/app/views/subscriptions/dashboard.pug
@@ -12,7 +12,7 @@ block scripts
mixin printPlan(plan)
-if (!plan.hideFromUsers)
- tr(ng-controller="ChangePlanFormController", ng-init="plan=#{JSON.stringify(plan)}", ng-show="shouldShowPlan(plan.planCode)")
+ tr(ng-controller="ChangePlanFormController", ng-init="plan="+JSON.stringify(plan), ng-show="shouldShowPlan(plan.planCode)")
td
strong #{plan.name}
td {{refreshPrice(plan.planCode)}}
@@ -22,18 +22,18 @@ mixin printPlan(plan)
| {{prices[plan.planCode]}} / #{translate("month")}
td
-if (subscription.state == "free-trial")
- a(href="/user/subscription/new?planCode=#{plan.planCode}").btn.btn-success #{translate("subscribe_to_this_plan")}
+ a(href="/user/subscription/new?planCode="+plan.planCode).btn.btn-success #{translate("subscribe_to_this_plan")}
-else if (typeof(subscription.planCode) != "undefined" && plan.planCode == subscription.planCode.split("_")[0])
button.btn.disabled #{translate("your_plan")}
-else
form
- input(type="hidden", ng-model="plan_code", name="plan_code", value="#{plan.planCode}")
+ input(type="hidden", ng-model="plan_code", name="plan_code", value=plan.planCode)
input(type="submit", ng-click="changePlan()", value=translate("change_to_this_plan")).btn.btn-success
mixin printPlans(plans)
- -each plan in plans
- mixin printPlan(plan)
+ each plan in plans
+ +printPlan(plan)
block content
.content.content-alt(ng-cloak)
@@ -46,7 +46,7 @@ block content
|
| #{translate("your_billing_details_were_saved")}
.card(ng-if="view == 'overview'")
- .page-header(x-current-plan="#{subscription.planCode}")
+ .page-header(x-current-plan=subscription.planCode)
h1 #{translate("your_subscription")}
- if (subscription && user._id+'' == subscription.admin_id+'')
@@ -97,9 +97,9 @@ block content
th !{translate("name")}
th !{translate("price")}
th
- mixin printPlans(plans.studentAccounts)
- mixin printPlans(plans.individualMonthlyPlans)
- mixin printPlans(plans.individualAnnualPlans)
+ +printPlans(plans.studentAccounts)
+ +printPlans(plans.individualMonthlyPlans)
+ +printPlans(plans.individualAnnualPlans)
each groupSubscription in groupSubscriptions
@@ -107,7 +107,7 @@ block content
div
p !{translate("member_of_group_subscription", {admin_email: "" + groupSubscription.admin_id.email + ""})}
span
- button.btn.btn-danger(ng-click="removeSelfFromGroup('#{groupSubscription.admin_id._id}')") #{translate("leave_group")}
+ button.btn.btn-danger(ng-click="removeSelfFromGroup('"+groupSubscription.admin_id._id+"')") #{translate("leave_group")}
-if(subscription.groupPlan && user._id+'' == subscription.admin_id+'')
div
diff --git a/services/web/app/views/subscriptions/edit-billing-details.jade b/services/web/app/views/subscriptions/edit-billing-details.pug
similarity index 100%
rename from services/web/app/views/subscriptions/edit-billing-details.jade
rename to services/web/app/views/subscriptions/edit-billing-details.pug
diff --git a/services/web/app/views/subscriptions/group/invite.jade b/services/web/app/views/subscriptions/group/invite.pug
similarity index 100%
rename from services/web/app/views/subscriptions/group/invite.jade
rename to services/web/app/views/subscriptions/group/invite.pug
diff --git a/services/web/app/views/subscriptions/group/successful_join.jade b/services/web/app/views/subscriptions/group/successful_join.pug
similarity index 100%
rename from services/web/app/views/subscriptions/group/successful_join.jade
rename to services/web/app/views/subscriptions/group/successful_join.pug
diff --git a/services/web/app/views/subscriptions/group_admin.jade b/services/web/app/views/subscriptions/group_admin.pug
similarity index 100%
rename from services/web/app/views/subscriptions/group_admin.jade
rename to services/web/app/views/subscriptions/group_admin.pug
diff --git a/services/web/app/views/subscriptions/new.jade b/services/web/app/views/subscriptions/new.pug
similarity index 99%
rename from services/web/app/views/subscriptions/new.jade
rename to services/web/app/views/subscriptions/new.pug
index 1465b24c82..2d410f8fba 100644
--- a/services/web/app/views/subscriptions/new.jade
+++ b/services/web/app/views/subscriptions/new.pug
@@ -164,7 +164,7 @@ block content
ng-change="updateCountry()"
required
)
- mixin countries_options()
+ +countries_options()
span.input-feedback-message {{ simpleCCForm.country.$error.required ? 'This field is required' : '' }}
if (showVatField)
diff --git a/services/web/app/views/subscriptions/plans.jade b/services/web/app/views/subscriptions/plans.pug
similarity index 100%
rename from services/web/app/views/subscriptions/plans.jade
rename to services/web/app/views/subscriptions/plans.pug
diff --git a/services/web/app/views/subscriptions/successful_subscription.jade b/services/web/app/views/subscriptions/successful_subscription.pug
similarity index 100%
rename from services/web/app/views/subscriptions/successful_subscription.jade
rename to services/web/app/views/subscriptions/successful_subscription.pug
diff --git a/services/web/app/views/subscriptions/upgradeToAnnual.jade b/services/web/app/views/subscriptions/upgradeToAnnual.pug
similarity index 93%
rename from services/web/app/views/subscriptions/upgradeToAnnual.jade
rename to services/web/app/views/subscriptions/upgradeToAnnual.pug
index d92290ad0e..0ce41856f7 100644
--- a/services/web/app/views/subscriptions/upgradeToAnnual.jade
+++ b/services/web/app/views/subscriptions/upgradeToAnnual.pug
@@ -6,7 +6,7 @@ block content
.container(ng-controller="AnnualUpgradeController")
.row(ng-cloak)
.col-md-6.col-md-offset-3
- .card(ng-init="planName = #{JSON.stringify(planName)}")
+ .card(ng-init="planName = "+JSON.stringify(planName))
.page-header
h1.text-centered #{translate("move_to_annual_billing")}
div(ng-hide="upgradeComplete")
diff --git a/services/web/app/views/tests.jade b/services/web/app/views/tests.pug
similarity index 100%
rename from services/web/app/views/tests.jade
rename to services/web/app/views/tests.pug
diff --git a/services/web/app/views/translations/translation_message.jade b/services/web/app/views/translations/translation_message.pug
similarity index 100%
rename from services/web/app/views/translations/translation_message.jade
rename to services/web/app/views/translations/translation_message.pug
diff --git a/services/web/app/views/university/case_study.jade b/services/web/app/views/university/case_study.pug
similarity index 100%
rename from services/web/app/views/university/case_study.jade
rename to services/web/app/views/university/case_study.pug
diff --git a/services/web/app/views/university/university_holder.jade b/services/web/app/views/university/university_holder.pug
similarity index 100%
rename from services/web/app/views/university/university_holder.jade
rename to services/web/app/views/university/university_holder.pug
diff --git a/services/web/app/views/user/activate.jade b/services/web/app/views/user/activate.pug
similarity index 97%
rename from services/web/app/views/user/activate.jade
rename to services/web/app/views/user/activate.pug
index 7961876389..8b60b10471 100644
--- a/services/web/app/views/user/activate.jade
+++ b/services/web/app/views/user/activate.pug
@@ -36,7 +36,7 @@ block content
placeholder="email@example.com"
required,
ng-model="email",
- ng-init="email = #{JSON.stringify(email)}",
+ ng-init="email = "+JSON.stringify(email),
ng-model-options="{ updateOn: 'blur' }",
disabled
)
diff --git a/services/web/app/views/user/login.jade b/services/web/app/views/user/login.pug
similarity index 96%
rename from services/web/app/views/user/login.jade
rename to services/web/app/views/user/login.pug
index 8339f27189..823299d660 100644
--- a/services/web/app/views/user/login.jade
+++ b/services/web/app/views/user/login.pug
@@ -19,7 +19,7 @@ block content
placeholder='email@example.com',
ng-model="email",
ng-model-options="{ updateOn: 'blur' }",
- ng-init="email = #{JSON.stringify(email)}",
+ ng-init="email = "+JSON.stringify(email),
focus="true"
)
span.small.text-primary(ng-show="loginForm.email.$invalid && loginForm.email.$dirty")
diff --git a/services/web/app/views/user/passwordReset.jade b/services/web/app/views/user/passwordReset.pug
similarity index 100%
rename from services/web/app/views/user/passwordReset.jade
rename to services/web/app/views/user/passwordReset.pug
diff --git a/services/web/app/views/user/register.jade b/services/web/app/views/user/register.pug
similarity index 100%
rename from services/web/app/views/user/register.jade
rename to services/web/app/views/user/register.pug
diff --git a/services/web/app/views/user/restricted.jade b/services/web/app/views/user/restricted.pug
similarity index 100%
rename from services/web/app/views/user/restricted.jade
rename to services/web/app/views/user/restricted.pug
diff --git a/services/web/app/views/user/sessions.jade b/services/web/app/views/user/sessions.pug
similarity index 100%
rename from services/web/app/views/user/sessions.jade
rename to services/web/app/views/user/sessions.pug
diff --git a/services/web/app/views/user/setPassword.jade b/services/web/app/views/user/setPassword.pug
similarity index 100%
rename from services/web/app/views/user/setPassword.jade
rename to services/web/app/views/user/setPassword.pug
diff --git a/services/web/app/views/user/settings.jade b/services/web/app/views/user/settings.pug
similarity index 99%
rename from services/web/app/views/user/settings.jade
rename to services/web/app/views/user/settings.pug
index 310912cf07..a4217d8ca4 100644
--- a/services/web/app/views/user/settings.jade
+++ b/services/web/app/views/user/settings.pug
@@ -28,7 +28,7 @@ block content
placeholder="email@example.com"
required,
ng-model="email",
- ng-init="email = #{JSON.stringify(user.email)}",
+ ng-init="email = "+JSON.stringify(user.email),
ng-model-options="{ updateOn: 'blur' }"
)
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
diff --git a/services/web/app/views/view_templates/bonus_templates.jade b/services/web/app/views/view_templates/bonus_templates.pug
similarity index 100%
rename from services/web/app/views/view_templates/bonus_templates.jade
rename to services/web/app/views/view_templates/bonus_templates.pug
diff --git a/services/web/modules/.gitignore b/services/web/modules/.gitignore
index b90beee9f7..1d30263cb3 100644
--- a/services/web/modules/.gitignore
+++ b/services/web/modules/.gitignore
@@ -2,3 +2,11 @@
*/test/unit/js
*/index.js
ldap
+admin-panel
+groovehq
+launchpad
+learn-wiki
+references-search
+sharelatex-saml
+templates
+tpr-webmodule
diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json
index 03abe920f9..7f6d37910e 100644
--- a/services/web/npm-shrinkwrap.json
+++ b/services/web/npm-shrinkwrap.json
@@ -20,7 +20,8 @@
},
"isarray": {
"version": "0.0.1",
- "from": "isarray@0.0.1"
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
@@ -100,7 +101,7 @@
},
"glob": {
"version": "3.2.11",
- "from": "glob@~3.2.6",
+ "from": "glob@~3.2.9",
"dependencies": {
"inherits": {
"version": "2.0.3",
@@ -124,7 +125,7 @@
},
"minimatch": {
"version": "0.2.14",
- "from": "minimatch@~0.2.12",
+ "from": "minimatch@~0.2.9",
"dependencies": {
"lru-cache": {
"version": "2.7.3",
@@ -256,7 +257,8 @@
},
"setprototypeof": {
"version": "1.0.2",
- "from": "setprototypeof@1.0.2"
+ "from": "setprototypeof@1.0.2",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz"
},
"statuses": {
"version": "1.3.1",
@@ -266,7 +268,8 @@
},
"iconv-lite": {
"version": "0.4.15",
- "from": "iconv-lite@0.4.15"
+ "from": "iconv-lite@0.4.15",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz"
},
"on-finished": {
"version": "2.3.0",
@@ -280,7 +283,8 @@
},
"qs": {
"version": "6.2.1",
- "from": "qs@6.2.1"
+ "from": "qs@6.2.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.1.tgz"
},
"raw-body": {
"version": "2.2.0",
@@ -341,11 +345,12 @@
"from": "double-ended-queue@^2.1.0-0"
},
"redis-commands": {
- "version": "1.3.0",
- "from": "redis-commands@^1.2.0"
+ "version": "1.3.1",
+ "from": "redis-commands@^1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz"
},
"redis-parser": {
- "version": "2.3.0",
+ "version": "2.4.0",
"from": "redis-parser@^2.0.0"
}
}
@@ -446,15 +451,17 @@
},
"rndm": {
"version": "1.2.0",
- "from": "rndm@1.2.0"
+ "from": "rndm@1.2.0",
+ "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz"
},
"tsscmp": {
"version": "1.0.5",
- "from": "tsscmp@1.0.5"
+ "from": "tsscmp@1.0.5",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz"
},
"uid-safe": {
"version": "2.1.3",
- "from": "uid-safe@2.1.3",
+ "from": "uid-safe@~2.1.3",
"dependencies": {
"random-bytes": {
"version": "1.0.0",
@@ -466,7 +473,7 @@
},
"http-errors": {
"version": "1.5.1",
- "from": "http-errors@~1.5.1",
+ "from": "http-errors@~1.5.0",
"dependencies": {
"inherits": {
"version": "2.0.3",
@@ -474,7 +481,8 @@
},
"setprototypeof": {
"version": "1.0.2",
- "from": "setprototypeof@1.0.2"
+ "from": "setprototypeof@1.0.2",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz"
},
"statuses": {
"version": "1.3.1",
@@ -491,6 +499,7 @@
"express": {
"version": "4.13.0",
"from": "express@4.13.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.13.0.tgz",
"dependencies": {
"accepts": {
"version": "1.2.13",
@@ -696,7 +705,7 @@
},
"type-is": {
"version": "1.6.14",
- "from": "type-is@~1.6.3",
+ "from": "type-is@~1.6.14",
"dependencies": {
"media-typer": {
"version": "0.3.0",
@@ -738,11 +747,12 @@
},
"crc": {
"version": "3.4.4",
- "from": "crc@3.4.4"
+ "from": "crc@3.4.4",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz"
},
"debug": {
"version": "2.6.0",
- "from": "debug@^2.2.0",
+ "from": "debug@*",
"dependencies": {
"ms": {
"version": "0.7.2",
@@ -760,7 +770,7 @@
},
"parseurl": {
"version": "1.3.1",
- "from": "parseurl@~1.3.1"
+ "from": "parseurl@~1.3.0"
},
"uid-safe": {
"version": "2.1.3",
@@ -800,7 +810,8 @@
},
"dateformat": {
"version": "1.0.2-1.2.3",
- "from": "dateformat@1.0.2-1.2.3"
+ "from": "dateformat@1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
},
"eventemitter2": {
"version": "0.4.14",
@@ -990,7 +1001,7 @@
},
"debug": {
"version": "2.6.0",
- "from": "debug@^2.2.0",
+ "from": "debug@*",
"dependencies": {
"ms": {
"version": "0.7.2",
@@ -1007,8 +1018,9 @@
"from": "flexbuffer@0.0.6"
},
"redis-commands": {
- "version": "1.3.0",
- "from": "redis-commands@^1.2.0"
+ "version": "1.3.1",
+ "from": "redis-commands@^1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz"
},
"redis-parser": {
"version": "1.3.0",
@@ -1160,7 +1172,8 @@
},
"window-size": {
"version": "0.1.0",
- "from": "window-size@0.1.0"
+ "from": "window-size@0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
},
"wordwrap": {
"version": "0.0.2",
@@ -1178,7 +1191,7 @@
"dependencies": {
"uglify-js": {
"version": "2.4.24",
- "from": "uglify-js@~2.4.0",
+ "from": "uglify-js@~2.4.12",
"dependencies": {
"async": {
"version": "0.2.10",
@@ -1212,7 +1225,8 @@
},
"window-size": {
"version": "0.1.0",
- "from": "window-size@0.1.0"
+ "from": "window-size@0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
},
"wordwrap": {
"version": "0.0.2",
@@ -1388,7 +1402,7 @@
"dependencies": {
"core-util-is": {
"version": "1.0.2",
- "from": "core-util-is@1.0.2"
+ "from": "core-util-is@~1.0.0"
},
"extsprintf": {
"version": "1.3.0",
@@ -1402,7 +1416,7 @@
"dependencies": {
"nan": {
"version": "2.5.1",
- "from": "nan@^2.3.3"
+ "from": "nan@^2.0.8"
}
}
}
@@ -1519,7 +1533,8 @@
},
"coffee-script": {
"version": "1.4.0",
- "from": "coffee-script@1.4.0"
+ "from": "coffee-script@1.4.0",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz"
},
"raven": {
"version": "0.8.1",
@@ -1533,6 +1548,10 @@
"version": "0.0.3",
"from": "lsmod@~0.0.3"
},
+ "node-uuid": {
+ "version": "1.4.7",
+ "from": "node-uuid@~1.4.1"
+ },
"stack-trace": {
"version": "0.0.7",
"from": "stack-trace@0.0.7"
@@ -1594,7 +1613,8 @@
"dependencies": {
"coffee-script": {
"version": "1.6.0",
- "from": "coffee-script@1.6.0"
+ "from": "coffee-script@1.6.0",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz"
}
}
},
@@ -1608,7 +1628,8 @@
"dependencies": {
"iconv-lite": {
"version": "0.4.15",
- "from": "iconv-lite@~0.4.13"
+ "from": "iconv-lite@~0.4.13",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz"
}
}
},
@@ -1633,6 +1654,7 @@
"jade": {
"version": "0.26.3",
"from": "jade@0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
"dependencies": {
"commander": {
"version": "0.6.1",
@@ -1660,7 +1682,7 @@
},
"mkdirp": {
"version": "0.3.5",
- "from": "mkdirp@0.3.5"
+ "from": "mkdirp@~0.3.5"
},
"glob": {
"version": "3.2.3",
@@ -1695,6 +1717,7 @@
"mongojs": {
"version": "0.18.2",
"from": "mongojs@0.18.2",
+ "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-0.18.2.tgz",
"dependencies": {
"thunky": {
"version": "0.1.0",
@@ -1702,7 +1725,7 @@
},
"readable-stream": {
"version": "1.1.14",
- "from": "readable-stream@~1.1.9",
+ "from": "readable-stream@1.1.x",
"dependencies": {
"core-util-is": {
"version": "1.0.2",
@@ -1710,7 +1733,8 @@
},
"isarray": {
"version": "0.0.1",
- "from": "isarray@0.0.1"
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
@@ -1725,6 +1749,7 @@
"mongodb": {
"version": "1.4.32",
"from": "mongodb@1.4.32",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-1.4.32.tgz",
"dependencies": {
"bson": {
"version": "0.2.22",
@@ -1739,6 +1764,7 @@
"kerberos": {
"version": "0.0.9",
"from": "kerberos@0.0.9",
+ "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.9.tgz",
"dependencies": {
"nan": {
"version": "1.6.2",
@@ -1854,7 +1880,8 @@
},
"isarray": {
"version": "0.0.1",
- "from": "isarray@0.0.1"
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
@@ -1942,7 +1969,8 @@
},
"isarray": {
"version": "0.0.1",
- "from": "isarray@0.0.1"
+ "from": "isarray@0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
@@ -1950,7 +1978,7 @@
},
"inherits": {
"version": "2.0.3",
- "from": "inherits@~2.0.1"
+ "from": "inherits@2"
}
}
}
@@ -1986,10 +2014,6 @@
}
}
},
- "node-uuid": {
- "version": "1.4.1",
- "from": "node-uuid@1.4.1"
- },
"nodemailer": {
"version": "2.1.0",
"from": "nodemailer@2.1.0",
@@ -2127,7 +2151,7 @@
"from": "nodemailer-ses-transport@^1.3.0",
"dependencies": {
"aws-sdk": {
- "version": "2.7.28",
+ "version": "2.9.0",
"from": "aws-sdk@^2.6.12",
"dependencies": {
"buffer": {
@@ -2342,6 +2366,388 @@
}
}
},
+ "pug": {
+ "version": "2.0.0-beta9",
+ "from": "pug@^2.0.0-beta6",
+ "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta9.tgz",
+ "dependencies": {
+ "pug-code-gen": {
+ "version": "1.1.1",
+ "from": "pug-code-gen@^1.1.1",
+ "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz",
+ "dependencies": {
+ "constantinople": {
+ "version": "3.1.0",
+ "from": "constantinople@^3.0.1",
+ "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz",
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "from": "acorn@^3.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz"
+ },
+ "is-expression": {
+ "version": "2.1.0",
+ "from": "is-expression@^2.0.1",
+ "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz",
+ "dependencies": {
+ "object-assign": {
+ "version": "4.1.1",
+ "from": "object-assign@^4.0.1"
+ }
+ }
+ }
+ }
+ },
+ "doctypes": {
+ "version": "1.1.0",
+ "from": "doctypes@^1.1.0",
+ "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz"
+ },
+ "js-stringify": {
+ "version": "1.0.2",
+ "from": "js-stringify@^1.0.1",
+ "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz"
+ },
+ "pug-attrs": {
+ "version": "2.0.2",
+ "from": "pug-attrs@^2.0.2",
+ "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.2.tgz"
+ },
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "from": "void-elements@^2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz"
+ },
+ "with": {
+ "version": "5.1.1",
+ "from": "with@^5.0.0",
+ "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz",
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "from": "acorn@^3.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz"
+ },
+ "acorn-globals": {
+ "version": "3.0.0",
+ "from": "acorn-globals@^3.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "pug-filters": {
+ "version": "2.1.0",
+ "from": "pug-filters@^2.1.0",
+ "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.0.tgz",
+ "dependencies": {
+ "constantinople": {
+ "version": "3.1.0",
+ "from": "constantinople@^3.0.1",
+ "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz",
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "from": "acorn@^3.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz"
+ },
+ "is-expression": {
+ "version": "2.1.0",
+ "from": "is-expression@^2.0.1",
+ "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz",
+ "dependencies": {
+ "object-assign": {
+ "version": "4.1.1",
+ "from": "object-assign@^4.0.1"
+ }
+ }
+ }
+ }
+ },
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ },
+ "pug-walk": {
+ "version": "1.1.0",
+ "from": "pug-walk@^1.1.0",
+ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.0.tgz"
+ },
+ "jstransformer": {
+ "version": "1.0.0",
+ "from": "jstransformer@1.0.0",
+ "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
+ "dependencies": {
+ "is-promise": {
+ "version": "2.1.0",
+ "from": "is-promise@^2.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz"
+ },
+ "promise": {
+ "version": "7.1.1",
+ "from": "promise@^7.0.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz",
+ "dependencies": {
+ "asap": {
+ "version": "2.0.5",
+ "from": "asap@~2.0.3",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz"
+ }
+ }
+ }
+ }
+ },
+ "resolve": {
+ "version": "1.2.0",
+ "from": "resolve@^1.1.6",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.2.0.tgz"
+ },
+ "uglify-js": {
+ "version": "2.7.5",
+ "from": "uglify-js@^2.6.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "from": "async@~0.2.6"
+ },
+ "source-map": {
+ "version": "0.5.6",
+ "from": "source-map@~0.5.1"
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "from": "uglify-to-browserify@~1.0.0"
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "from": "yargs@~3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "from": "camelcase@^1.0.2"
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "from": "cliui@^2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "dependencies": {
+ "center-align": {
+ "version": "0.1.3",
+ "from": "center-align@^0.1.1",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "dependencies": {
+ "align-text": {
+ "version": "0.1.4",
+ "from": "align-text@^0.1.1",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "dependencies": {
+ "kind-of": {
+ "version": "3.1.0",
+ "from": "kind-of@^3.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
+ "dependencies": {
+ "is-buffer": {
+ "version": "1.1.4",
+ "from": "is-buffer@^1.0.2",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz"
+ }
+ }
+ },
+ "longest": {
+ "version": "1.0.1",
+ "from": "longest@^1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz"
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "from": "repeat-string@^1.5.2",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz"
+ }
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "from": "lazy-cache@^1.0.3",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz"
+ }
+ }
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "from": "right-align@^0.1.1",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "dependencies": {
+ "align-text": {
+ "version": "0.1.4",
+ "from": "align-text@^0.1.1",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "dependencies": {
+ "kind-of": {
+ "version": "3.1.0",
+ "from": "kind-of@^3.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
+ "dependencies": {
+ "is-buffer": {
+ "version": "1.1.4",
+ "from": "is-buffer@^1.0.2",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz"
+ }
+ }
+ },
+ "longest": {
+ "version": "1.0.1",
+ "from": "longest@^1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz"
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "from": "repeat-string@^1.5.2",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "wordwrap": {
+ "version": "0.0.2",
+ "from": "wordwrap@0.0.2"
+ }
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "from": "decamelize@^1.0.0"
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "from": "window-size@0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "pug-lexer": {
+ "version": "2.3.2",
+ "from": "pug-lexer@^2.3.1",
+ "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-2.3.2.tgz",
+ "dependencies": {
+ "character-parser": {
+ "version": "2.2.0",
+ "from": "character-parser@^2.1.1",
+ "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
+ "dependencies": {
+ "is-regex": {
+ "version": "1.0.3",
+ "from": "is-regex@^1.0.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.3.tgz"
+ }
+ }
+ },
+ "is-expression": {
+ "version": "3.0.0",
+ "from": "is-expression@^3.0.0",
+ "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
+ "dependencies": {
+ "acorn": {
+ "version": "4.0.4",
+ "from": "acorn@~4.0.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.4.tgz"
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "from": "object-assign@^4.0.1"
+ }
+ }
+ },
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ }
+ }
+ },
+ "pug-linker": {
+ "version": "2.0.1",
+ "from": "pug-linker@^2.0.1",
+ "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.1.tgz",
+ "dependencies": {
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ },
+ "pug-walk": {
+ "version": "1.1.0",
+ "from": "pug-walk@^1.1.0",
+ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.0.tgz"
+ }
+ }
+ },
+ "pug-load": {
+ "version": "2.0.4",
+ "from": "pug-load@^2.0.4",
+ "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.4.tgz",
+ "dependencies": {
+ "object-assign": {
+ "version": "4.1.1",
+ "from": "object-assign@^4.1.0"
+ },
+ "pug-walk": {
+ "version": "1.1.0",
+ "from": "pug-walk@^1.1.0",
+ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.0.tgz"
+ }
+ }
+ },
+ "pug-parser": {
+ "version": "2.0.2",
+ "from": "pug-parser@^2.0.2",
+ "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz",
+ "dependencies": {
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ },
+ "token-stream": {
+ "version": "0.0.1",
+ "from": "token-stream@0.0.1",
+ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz"
+ }
+ }
+ },
+ "pug-runtime": {
+ "version": "2.0.3",
+ "from": "pug-runtime@^2.0.3",
+ "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz"
+ },
+ "pug-strip-comments": {
+ "version": "1.0.2",
+ "from": "pug-strip-comments@^1.0.2",
+ "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz",
+ "dependencies": {
+ "pug-error": {
+ "version": "1.3.2",
+ "from": "pug-error@^1.3.2",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz"
+ }
+ }
+ }
+ }
+ },
"redis": {
"version": "0.10.1",
"from": "redis@0.10.1"
@@ -2349,6 +2755,7 @@
"redis-sharelatex": {
"version": "0.0.9",
"from": "redis-sharelatex@0.0.9",
+ "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz",
"dependencies": {
"chai": {
"version": "1.9.1",
@@ -2360,7 +2767,7 @@
},
"deep-eql": {
"version": "0.1.3",
- "from": "deep-eql@0.1.3",
+ "from": "deep-eql@^0.1.3",
"dependencies": {
"type-detect": {
"version": "0.1.1",
@@ -2456,7 +2863,7 @@
},
"mkdirp": {
"version": "0.5.1",
- "from": "mkdirp@^0.5.0",
+ "from": "mkdirp@~0.5.1",
"dependencies": {
"minimist": {
"version": "0.0.8",
@@ -2557,6 +2964,7 @@
"jade": {
"version": "0.26.3",
"from": "jade@0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
"dependencies": {
"commander": {
"version": "0.6.1",
@@ -2584,7 +2992,7 @@
},
"mkdirp": {
"version": "0.3.5",
- "from": "mkdirp@0.3.5"
+ "from": "mkdirp@~0.3.5"
},
"glob": {
"version": "3.2.3",
@@ -2618,11 +3026,13 @@
},
"redis": {
"version": "0.12.1",
- "from": "redis@0.12.1"
+ "from": "redis@0.12.1",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
},
"redis-sentinel": {
"version": "0.1.1",
"from": "redis-sentinel@0.1.1",
+ "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz",
"dependencies": {
"redis": {
"version": "0.11.0",
@@ -2955,10 +3365,6 @@
"tunnel-agent": {
"version": "0.4.3",
"from": "tunnel-agent@~0.4.1"
- },
- "uuid": {
- "version": "3.0.1",
- "from": "uuid@^3.0.0"
}
}
},
@@ -3039,7 +3445,7 @@
},
"depd": {
"version": "1.1.0",
- "from": "depd@^1.1.0"
+ "from": "depd@~1.1.0"
},
"dottie": {
"version": "1.1.1",
@@ -3047,10 +3453,11 @@
},
"generic-pool": {
"version": "2.4.2",
- "from": "generic-pool@2.4.2"
+ "from": "generic-pool@2.4.2",
+ "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz"
},
"inflection": {
- "version": "1.10.0",
+ "version": "1.12.0",
"from": "inflection@^1.6.0"
},
"lodash": {
@@ -3127,7 +3534,8 @@
},
"shimmer": {
"version": "1.1.0",
- "from": "shimmer@1.1.0"
+ "from": "shimmer@1.1.0",
+ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.1.0.tgz"
},
"terraformer-wkt-parser": {
"version": "1.1.2",
@@ -3149,7 +3557,8 @@
},
"wkx": {
"version": "0.2.0",
- "from": "wkx@0.2.0"
+ "from": "wkx@0.2.0",
+ "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.2.0.tgz"
}
}
},
@@ -3160,7 +3569,8 @@
"dependencies": {
"coffee-script": {
"version": "1.6.0",
- "from": "coffee-script@1.6.0"
+ "from": "coffee-script@1.6.0",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz"
}
}
},
@@ -3182,6 +3592,10 @@
"version": "1.6.0",
"from": "underscore@1.6.0"
},
+ "uuid": {
+ "version": "3.0.1",
+ "from": "uuid@^3.0.1"
+ },
"v8-profiler": {
"version": "5.6.5",
"from": "v8-profiler@^5.2.3",
@@ -3442,7 +3856,7 @@
},
"inherits": {
"version": "2.0.3",
- "from": "inherits@2"
+ "from": "inherits@~2.0.0"
}
}
},
@@ -3484,7 +3898,7 @@
},
"minimatch": {
"version": "3.0.3",
- "from": "minimatch@^3.0.2",
+ "from": "minimatch@^3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.6",
diff --git a/services/web/package.json b/services/web/package.json
index 3408ede468..100b55f75c 100644
--- a/services/web/package.json
+++ b/services/web/package.json
@@ -42,7 +42,6 @@
"mongojs": "0.18.2",
"mongoose": "4.1.0",
"multer": "^0.1.8",
- "node-uuid": "1.4.1",
"nodemailer": "2.1.0",
"nodemailer-sendgrid-transport": "^0.2.0",
"nodemailer-ses-transport": "^1.3.0",
@@ -64,6 +63,8 @@
"v8-profiler": "^5.2.3",
"xml2js": "0.2.0",
"passport-saml": "^0.15.0",
+ "pug": "^2.0.0-beta6",
+ "uuid": "^3.0.1",
"rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master"
},
"devDependencies": {
diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee
index 9d7eca813a..1de01b5467 100644
--- a/services/web/public/coffee/ide/editor/Document.coffee
+++ b/services/web/public/coffee/ide/editor/Document.coffee
@@ -84,6 +84,9 @@ define [
setTrackingChanges: (track_changes) ->
@doc.track_changes = track_changes
+ getTrackingChanges: () ->
+ !!@doc.track_changes
+
setTrackChangesIdSeeds: (id_seeds) ->
@doc.track_changes_id_seeds = id_seeds
diff --git a/services/web/public/coffee/ide/editor/EditorManager.coffee b/services/web/public/coffee/ide/editor/EditorManager.coffee
index 22fcef1a69..3b6672fae8 100644
--- a/services/web/public/coffee/ide/editor/EditorManager.coffee
+++ b/services/web/public/coffee/ide/editor/EditorManager.coffee
@@ -162,8 +162,9 @@ define [
@_syncTimeout = null
want = @$scope.editor.wantTrackChanges
- have = @$scope.editor.trackChanges
+ have = doc.getTrackingChanges()
if want == have
+ @$scope.editor.trackChanges = want
return
do tryToggle = () =>
diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
index 41c70b4dee..93775fd78f 100644
--- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
+++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
@@ -13,7 +13,7 @@ define [
EditSession = ace.require('ace/edit_session').EditSession
ModeList = ace.require('ace/ext/modelist')
- # set the path for ace workers if using a CDN (from editor.jade)
+ # set the path for ace workers if using a CDN (from editor.pug)
if window.aceWorkerPath != ""
syntaxValidationEnabled = true
ace.config.set('workerPath', "#{window.aceWorkerPath}")
@@ -279,7 +279,7 @@ define [
session.setUseWrapMode(true)
# use syntax validation only when explicitly set
- if scope.syntaxValidation? and syntaxValidationEnabled
+ if scope.syntaxValidation? and syntaxValidationEnabled and !scope.fileName.match(/\.bib$/)
session.setOption("useWorker", scope.syntaxValidation);
# now attach session to editor
diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
index 2d57100cc5..ed15da2958 100644
--- a/services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
+++ b/services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
@@ -35,8 +35,8 @@ define [
@$scope.$on "comment:remove", (e, comment_id) =>
@removeCommentId(comment_id)
- @$scope.$on "comment:resolve_thread", (e, thread_id) =>
- @resolveCommentByThreadId(thread_id)
+ @$scope.$on "comment:resolve_threads", (e, thread_ids) =>
+ @resolveCommentByThreadIds(thread_ids)
@$scope.$on "comment:unresolve_thread", (e, thread_id) =>
@unresolveCommentByThreadId(thread_id)
@@ -105,29 +105,45 @@ define [
# ace has updated
@rangesTracker.on "insert:added", (change) =>
sl_console.log "[insert:added]", change
- setTimeout () => @_onInsertAdded(change)
+ setTimeout () =>
+ @_onInsertAdded(change)
+ @broadcastChange()
@rangesTracker.on "insert:removed", (change) =>
sl_console.log "[insert:removed]", change
- setTimeout () => @_onInsertRemoved(change)
+ setTimeout () =>
+ @_onInsertRemoved(change)
+ @broadcastChange()
@rangesTracker.on "delete:added", (change) =>
sl_console.log "[delete:added]", change
- setTimeout () => @_onDeleteAdded(change)
+ setTimeout () =>
+ @_onDeleteAdded(change)
+ @broadcastChange()
@rangesTracker.on "delete:removed", (change) =>
sl_console.log "[delete:removed]", change
- setTimeout () => @_onDeleteRemoved(change)
+ setTimeout () =>
+ @_onDeleteRemoved(change)
+ @broadcastChange()
@rangesTracker.on "changes:moved", (changes) =>
sl_console.log "[changes:moved]", changes
- setTimeout () => @_onChangesMoved(changes)
+ setTimeout () =>
+ @_onChangesMoved(changes)
+ @broadcastChange()
@rangesTracker.on "comment:added", (comment) =>
sl_console.log "[comment:added]", comment
- setTimeout () => @_onCommentAdded(comment)
+ setTimeout () =>
+ @_onCommentAdded(comment)
+ @broadcastChange()
@rangesTracker.on "comment:moved", (comment) =>
sl_console.log "[comment:moved]", comment
- setTimeout () => @_onCommentMoved(comment)
+ setTimeout () =>
+ @_onCommentMoved(comment)
+ @broadcastChange()
@rangesTracker.on "comment:removed", (comment) =>
sl_console.log "[comment:removed]", comment
- setTimeout () => @_onCommentRemoved(comment)
+ setTimeout () =>
+ @_onCommentRemoved(comment)
+ @broadcastChange()
@rangesTracker.on "clear", () =>
@clearAnnotations()
@@ -150,6 +166,8 @@ define [
for comment in @rangesTracker.comments
@_onCommentAdded(comment)
+
+ @broadcastChange()
addComment: (offset, content, thread_id) ->
op = { c: content, p: offset, t: thread_id }
@@ -190,15 +208,20 @@ define [
removeCommentId: (comment_id) ->
@rangesTracker.removeCommentId(comment_id)
- resolveCommentByThreadId: (thread_id) ->
+ resolveCommentByThreadIds: (thread_ids) ->
+ resolve_ids = {}
+ for id in thread_ids
+ resolve_ids[id] = true
for comment in @rangesTracker?.comments or []
- if comment.op.t == thread_id
+ if resolve_ids[comment.op.t]
@_onCommentRemoved(comment)
+ @broadcastChange()
unresolveCommentByThreadId: (thread_id) ->
for comment in @rangesTracker?.comments or []
if comment.op.t == thread_id
@_onCommentAdded(comment)
+ @broadcastChange()
checkMapping: () ->
# TODO: reintroduce this check
@@ -303,7 +326,6 @@ define [
background_marker_id = session.addMarker background_range, "track-changes-marker track-changes-added-marker", "text"
callout_marker_id = @_createCalloutMarker(start, "track-changes-added-marker-callout")
@changeIdToMarkerIdMap[change.id] = { background_marker_id, callout_marker_id }
- @broadcastChange()
_onDeleteAdded: (change) ->
position = @_shareJsOffsetToAcePosition(change.op.p)
@@ -318,7 +340,6 @@ define [
callout_marker_id = @_createCalloutMarker(position, "track-changes-deleted-marker-callout")
@changeIdToMarkerIdMap[change.id] = { background_marker_id, callout_marker_id }
- @broadcastChange()
_onInsertRemoved: (change) ->
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id]
@@ -326,7 +347,6 @@ define [
session = @editor.getSession()
session.removeMarker background_marker_id
session.removeMarker callout_marker_id
- @broadcastChange()
_onDeleteRemoved: (change) ->
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id]
@@ -334,7 +354,6 @@ define [
session = @editor.getSession()
session.removeMarker background_marker_id
session.removeMarker callout_marker_id
- @broadcastChange()
_onCommentAdded: (comment) ->
if @rangesTracker.resolvedThreadIds[comment.op.t]
@@ -350,7 +369,6 @@ define [
background_marker_id = session.addMarker background_range, "track-changes-marker track-changes-comment-marker", "text"
callout_marker_id = @_createCalloutMarker(start, "track-changes-comment-marker-callout")
@changeIdToMarkerIdMap[comment.id] = { background_marker_id, callout_marker_id }
- @broadcastChange()
_onCommentRemoved: (comment) ->
if @changeIdToMarkerIdMap[comment.id]?
@@ -360,7 +378,6 @@ define [
session = @editor.getSession()
session.removeMarker background_marker_id
session.removeMarker callout_marker_id
- @broadcastChange()
_aceRangeToShareJs: (range) ->
lines = @editor.getSession().getDocument().getLines 0, range.row
@@ -385,14 +402,12 @@ define [
end = start
@_updateMarker(change.id, start, end)
@editor.renderer.updateBackMarkers()
- @broadcastChange()
_onCommentMoved: (comment) ->
start = @_shareJsOffsetToAcePosition(comment.op.p)
end = @_shareJsOffsetToAcePosition(comment.op.p + comment.op.c.length)
@_updateMarker(comment.id, start, end)
@editor.renderer.updateBackMarkers()
- @broadcastChange()
_updateMarker: (change_id, start, end) ->
return if !@changeIdToMarkerIdMap[change_id]?
diff --git a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
index 1a1a37cf21..5f346ec3d6 100644
--- a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
+++ b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
@@ -70,6 +70,10 @@ define [
ide.socket.on "reopen-thread", (thread_id) ->
_onCommentReopened(thread_id)
+ ide.socket.on "delete-thread", (thread_id) ->
+ _onThreadDeleted(thread_id)
+ $scope.$apply () ->
+
ide.socket.on "edit-message", (thread_id, message_id, content) ->
_onCommentEdited(thread_id, message_id, content)
$scope.$apply () ->
@@ -226,8 +230,10 @@ define [
delete delete_changes[comment.id]
if $scope.reviewPanel.resolvedThreadIds[comment.op.t]
new_comment = resolvedComments[comment.id] ?= {}
+ delete entries[comment.id]
else
new_comment = entries[comment.id] ?= {}
+ delete resolvedComments[comment.id]
new_entry = {
type: "comment"
thread_id: comment.op.t
@@ -360,7 +366,7 @@ define [
thread.resolved_by_user = formatUser(user)
thread.resolved_at = new Date()
$scope.reviewPanel.resolvedThreadIds[thread_id] = true
- $scope.$broadcast "comment:resolve_thread", thread_id
+ $scope.$broadcast "comment:resolve_threads", [thread_id]
_onCommentReopened = (thread_id) ->
thread = getThread(thread_id)
@@ -374,6 +380,7 @@ define [
_onThreadDeleted = (thread_id) ->
delete $scope.reviewPanel.resolvedThreadIds[thread_id]
delete $scope.reviewPanel.commentThreads[thread_id]
+ $scope.$broadcast "comment:remove", thread_id
_onCommentEdited = (thread_id, comment_id, content) ->
thread = getThread(thread_id)
@@ -398,7 +405,6 @@ define [
'X-CSRF-Token': window.csrfToken
}
})
- $scope.$broadcast "comment:remove", entry_id
event_tracking.sendMB "rp-comment-delete"
$scope.saveEdit = (thread_id, comment) ->
@@ -481,9 +487,9 @@ define [
for comment in thread.messages
formatComment(comment)
if thread.resolved_by_user?
- $scope.$broadcast "comment:resolve_thread", thread_id
thread.resolved_by_user = formatUser(thread.resolved_by_user)
$scope.reviewPanel.resolvedThreadIds[thread_id] = true
+ $scope.$broadcast "comment:resolve_threads", [thread_id]
$scope.reviewPanel.commentThreads = threads
$timeout () ->
$scope.$broadcast "review-panel:layout"
diff --git a/services/web/public/coffee/ide/review-panel/directives/changeEntry.coffee b/services/web/public/coffee/ide/review-panel/directives/changeEntry.coffee
index 96f5e50016..9dc1ef2a37 100644
--- a/services/web/public/coffee/ide/review-panel/directives/changeEntry.coffee
+++ b/services/web/public/coffee/ide/review-panel/directives/changeEntry.coffee
@@ -11,11 +11,16 @@ define [
onAccept: "&"
onReject: "&"
onIndicatorClick: "&"
+ onBodyClick: "&"
link: (scope, element, attrs) ->
scope.contentLimit = 40
scope.isCollapsed = true
scope.needsCollapsing = false
+ element.on "click", (e) ->
+ if $(e.target).is('.rp-entry, .rp-entry-description, .rp-entry-body, .rp-entry-action-icon i')
+ scope.onBodyClick()
+
scope.toggleCollapse = () ->
scope.isCollapsed = !scope.isCollapsed
$timeout () ->
diff --git a/services/web/public/coffee/ide/review-panel/directives/commentEntry.coffee b/services/web/public/coffee/ide/review-panel/directives/commentEntry.coffee
index 7c7811d553..b2f09c96af 100644
--- a/services/web/public/coffee/ide/review-panel/directives/commentEntry.coffee
+++ b/services/web/public/coffee/ide/review-panel/directives/commentEntry.coffee
@@ -13,10 +13,15 @@ define [
onIndicatorClick: "&"
onSaveEdit: "&"
onDelete: "&"
+ onBodyClick: "&"
link: (scope, element, attrs) ->
scope.state =
animating: false
+ element.on "click", (e) ->
+ if $(e.target).is('.rp-entry, .rp-comment-loaded, .rp-comment-content, .rp-comment-reply, .rp-entry-metadata')
+ scope.onBodyClick()
+
scope.handleCommentReplyKeyPress = (ev) ->
if ev.keyCode == 13 and !ev.shiftKey and !ev.ctrlKey and !ev.metaKey
ev.preventDefault()
diff --git a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee
index bc1cb2e3b4..453296b3d6 100644
--- a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee
@@ -114,7 +114,8 @@ describe "CollaboratorsInviteController", ->
describe 'when all goes well', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -128,7 +129,7 @@ describe "CollaboratorsInviteController", ->
it 'should have called _checkShouldInviteEmail', ->
@CollaboratorsInviteController._checkShouldInviteEmail.callCount.should.equal 1
- @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@sendingUser, @targetEmail).should.equal true
+ @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@targetEmail).should.equal true
it 'should have called inviteToProject', ->
@CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 1
@@ -141,7 +142,8 @@ describe "CollaboratorsInviteController", ->
describe 'when the user is not allowed to add more collaborators', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, false)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -159,7 +161,8 @@ describe "CollaboratorsInviteController", ->
describe 'when canAddXCollaborators produces an error', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@err = new Error('woops')
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, @err)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -178,7 +181,8 @@ describe "CollaboratorsInviteController", ->
describe 'when inviteToProject produces an error', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@err = new Error('woops')
@CollaboratorsInviteHandler.inviteToProject = sinon.stub().callsArgWith(4, @err)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -193,7 +197,7 @@ describe "CollaboratorsInviteController", ->
it 'should have called _checkShouldInviteEmail', ->
@CollaboratorsInviteController._checkShouldInviteEmail.callCount.should.equal 1
- @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@sendingUser, @targetEmail).should.equal true
+ @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@targetEmail).should.equal true
it 'should have called inviteToProject', ->
@CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 1
@@ -202,7 +206,8 @@ describe "CollaboratorsInviteController", ->
describe 'when _checkShouldInviteEmail disallows the invite', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, false)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, false)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -212,7 +217,7 @@ describe "CollaboratorsInviteController", ->
it 'should have called _checkShouldInviteEmail', ->
@CollaboratorsInviteController._checkShouldInviteEmail.callCount.should.equal 1
- @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@sendingUser, @targetEmail).should.equal true
+ @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@targetEmail).should.equal true
it 'should not have called inviteToProject', ->
@CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 0
@@ -220,7 +225,8 @@ describe "CollaboratorsInviteController", ->
describe 'when _checkShouldInviteEmail produces an error', ->
beforeEach ->
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, new Error('woops'))
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, new Error('woops'))
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -230,7 +236,7 @@ describe "CollaboratorsInviteController", ->
it 'should have called _checkShouldInviteEmail', ->
@CollaboratorsInviteController._checkShouldInviteEmail.callCount.should.equal 1
- @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@sendingUser, @targetEmail).should.equal true
+ @CollaboratorsInviteController._checkShouldInviteEmail.calledWith(@targetEmail).should.equal true
it 'should not have called inviteToProject', ->
@CollaboratorsInviteHandler.inviteToProject.callCount.should.equal 0
@@ -240,7 +246,8 @@ describe "CollaboratorsInviteController", ->
beforeEach ->
@req.session.user = {_id: 'abc', email: 'me@example.com'}
@req.body.email = 'me@example.com'
- @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, true)
@LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true)
@CollaboratorsInviteController.inviteToProject @req, @res, @next
@@ -261,6 +268,22 @@ describe "CollaboratorsInviteController", ->
it 'should not have called emitToRoom', ->
@EditorRealTimeController.emitToRoom.callCount.should.equal 0
+ describe 'when _checkRateLimit returns false', ->
+
+ beforeEach ->
+ @CollaboratorsInviteController._checkShouldInviteEmail = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit = sinon.stub().yields(null, false)
+ @LimitationsManager.canAddXCollaborators = sinon.stub().callsArgWith(2, null, true)
+ @CollaboratorsInviteController.inviteToProject @req, @res, @next
+
+ it 'should send a 429 response', ->
+ @res.sendStatus.calledWith(429).should.equal true
+
+ it 'should not call inviteToProject', ->
+ @CollaboratorsInviteHandler.inviteToProject.called.should.equal false
+
+ it 'should not call emitToRoom', ->
+ @EditorRealTimeController.emitToRoom.called.should.equal false
describe "viewInvite", ->
@@ -679,13 +702,12 @@ describe "CollaboratorsInviteController", ->
beforeEach ->
@email = 'user@example.com'
-
describe 'when we should be restricting to existing accounts', ->
beforeEach ->
@settings.restrictInvitesToExistingAccounts = true
@call = (callback) =>
- @CollaboratorsInviteController._checkShouldInviteEmail {}, @email, callback
+ @CollaboratorsInviteController._checkShouldInviteEmail @email, callback
describe 'when user account is present', ->
@@ -730,46 +752,43 @@ describe "CollaboratorsInviteController", ->
expect(shouldAllow).to.equal undefined
done()
- describe 'when we should not be restricting on only registered users but do rate limit', ->
+ describe '_checkRateLimit', ->
+ beforeEach ->
+ @settings.restrictInvitesToExistingAccounts = false
+ @sendingUserId = "32312313"
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser = sinon.stub()
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser.withArgs(@sendingUserId).yields(null, 17)
- beforeEach ->
- @settings.restrictInvitesToExistingAccounts = false
- @sendingUser =
- _id:"32312313"
- features:
- collaborators:17.8
- @UserGetter.getUser = sinon.stub().callsArgWith(2, null, @sendingUser)
+ it 'should callback with `true` when rate limit under', (done) ->
+ @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit @sendingUserId, (err, result)=>
+ @RateLimiter.addCount.called.should.equal true
+ result.should.equal true
+ done()
- it 'should callback with `true` when rate limit under', (done) ->
- @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
- @CollaboratorsInviteController._checkShouldInviteEmail @sendingUser, @email, (err, result)=>
- @RateLimiter.addCount.called.should.equal true
- result.should.equal true
- done()
+ it 'should callback with `false` when rate limit hit', (done) ->
+ @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, false)
+ @CollaboratorsInviteController._checkRateLimit @sendingUserId, (err, result)=>
+ @RateLimiter.addCount.called.should.equal true
+ result.should.equal false
+ done()
+
+ it 'should call rate limiter with 10x the collaborators', (done) ->
+ @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit @sendingUserId, (err, result)=>
+ @RateLimiter.addCount.args[0][0].throttle.should.equal(170)
+ done()
- it 'should callback with `false` when rate limit hit', (done) ->
- @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, false)
- @CollaboratorsInviteController._checkShouldInviteEmail @sendingUser, @email, (err, result)=>
- @RateLimiter.addCount.called.should.equal true
- result.should.equal false
- done()
-
- it 'should call rate limiter with 10x the collaborators', (done) ->
- @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
- @CollaboratorsInviteController._checkShouldInviteEmail @sendingUser, @email, (err, result)=>
- @RateLimiter.addCount.args[0][0].throttle.should.equal(178)
- done()
+ it 'should call rate limiter with 200 when collaborators is -1', (done) ->
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser.withArgs(@sendingUserId).yields(null, -1)
+ @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit @sendingUserId, (err, result)=>
+ @RateLimiter.addCount.args[0][0].throttle.should.equal(200)
+ done()
- it 'should call rate limiter with 200 when collaborators is -1', (done) ->
- @sendingUser.features.collaborators = -1
- @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
- @CollaboratorsInviteController._checkShouldInviteEmail @sendingUser, @email, (err, result)=>
- @RateLimiter.addCount.args[0][0].throttle.should.equal(200)
- done()
-
- it 'should call rate limiter with 10 when user has no collaborators set', (done) ->
- delete @sendingUser.features
- @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
- @CollaboratorsInviteController._checkShouldInviteEmail @sendingUser, @email, (err, result)=>
- @RateLimiter.addCount.args[0][0].throttle.should.equal(10)
- done()
\ No newline at end of file
+ it 'should call rate limiter with 10 when user has no collaborators set', (done) ->
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser.withArgs(@sendingUserId).yields(null)
+ @RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
+ @CollaboratorsInviteController._checkRateLimit @sendingUserId, (err, result)=>
+ @RateLimiter.addCount.args[0][0].throttle.should.equal(10)
+ done()
\ No newline at end of file
diff --git a/services/web/test/UnitTests/coffee/Subscription/LimitationsManagerTests.coffee b/services/web/test/UnitTests/coffee/Subscription/LimitationsManagerTests.coffee
index 93f00afad5..f81433e156 100644
--- a/services/web/test/UnitTests/coffee/Subscription/LimitationsManagerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Subscription/LimitationsManagerTests.coffee
@@ -6,17 +6,17 @@ Settings = require("settings-sharelatex")
describe "LimitationsManager", ->
beforeEach ->
- @project = { _id: "project-id" }
- @user = { _id: "user-id", features:{} }
+ @project = { _id: @project_id = "project-id" }
+ @user = { _id: @user_id = "user-id", features:{} }
@Project =
findById: (project_id, fields, callback) =>
if project_id == @project_id
callback null, @project
else
callback null, null
- @User =
- findById: (user_id, callback) =>
- if user_id == @user.id
+ @UserGetter =
+ getUser: (user_id, filter, callback) =>
+ if user_id == @user_id
callback null, @user
else
callback null, null
@@ -26,7 +26,7 @@ describe "LimitationsManager", ->
@LimitationsManager = SandboxedModule.require modulePath, requires:
'../../models/Project' : Project: @Project
- '../../models/User' : User: @User
+ '../User/UserGetter' : @UserGetter
'./SubscriptionLocator':@SubscriptionLocator
'settings-sharelatex' : @Settings = {}
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
@@ -37,6 +37,7 @@ describe "LimitationsManager", ->
describe "when the project is owned by a user without a subscription", ->
beforeEach ->
@Settings.defaultPlanCode = collaborators: 23
+ @project.owner_ref = @user_id
delete @user.features
@callback = sinon.stub()
@LimitationsManager.allowedNumberOfCollaboratorsInProject(@project_id, @callback)
@@ -46,6 +47,7 @@ describe "LimitationsManager", ->
describe "when the project is owned by a user with a subscription", ->
beforeEach ->
+ @project.owner_ref = @user_id
@user.features =
collaborators: 21
@callback = sinon.stub()
@@ -53,6 +55,27 @@ describe "LimitationsManager", ->
it "should return the number of collaborators the user is allowed", ->
@callback.calledWith(null, @user.features.collaborators).should.equal true
+
+ describe "allowedNumberOfCollaboratorsForUser", ->
+ describe "when the user has no features", ->
+ beforeEach ->
+ @Settings.defaultPlanCode = collaborators: 23
+ delete @user.features
+ @callback = sinon.stub()
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser(@user_id, @callback)
+
+ it "should return the default number", ->
+ @callback.calledWith(null, @Settings.defaultPlanCode.collaborators).should.equal true
+
+ describe "when the user has features", ->
+ beforeEach ->
+ @user.features =
+ collaborators: 21
+ @callback = sinon.stub()
+ @LimitationsManager.allowedNumberOfCollaboratorsForUser(@user_id, @callback)
+
+ it "should return the number of collaborators the user is allowed", ->
+ @callback.calledWith(null, @user.features.collaborators).should.equal true
describe "canAddXCollaborators", ->
beforeEach ->