Merge branch 'master' into pr-expandable-text-area-fixes

This commit is contained in:
Paulo Reis
2017-02-02 14:38:09 +00:00
83 changed files with 778 additions and 269 deletions
+3 -3
View File
@@ -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']
@@ -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
@@ -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)
@@ -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
@@ -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) } &ndash; shared by #{ opts.owner.email }"
title: "#{ opts.project.name } &ndash; shared by #{ opts.owner.email }"
greeting: "Hi,"
message: "#{ opts.owner.email } wants to share &ldquo;#{ opts.project.name.slice(0, 40) }&rdquo; with you."
message: "#{ opts.owner.email } wants to share &ldquo;#{ opts.project.name }&rdquo; 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 =
@@ -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)
@@ -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)->
@@ -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 =
@@ -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 []
@@ -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
+1 -1
View File
@@ -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
@@ -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")
@@ -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")}
p #{translate("request_sent_thank_you")}
@@ -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)
@@ -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
@@ -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
@@ -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()"
@@ -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
@@ -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(' ', '<br>')}'",
tooltip-html="'"+translate('new_file').replace(' ', '<br>')+"'",
tooltip-placement="bottom"
)
i.fa.fa-file
a(
href,
ng-click="openNewFolderModal()",
tooltip-html="'#{translate('new_folder').replace(' ', '<br>')}'",
tooltip-html="'"+translate('new_folder').replace(' ', '<br>')+"'",
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')}
) #{translate('ok')}
@@ -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")}
p.toolbar-label #{translate("chat")}
@@ -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 &nbsp;&nbsp; #{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 &nbsp;&nbsp; #{translate("word_count")}
@@ -200,7 +200,7 @@ script(type='text/ng-template', id='wordCountModalTemplate')
span &nbsp; #{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")
@@ -2,7 +2,7 @@ div.full-size.pdf(ng-controller="PdfController")
.toolbar.toolbar-tall
.btn-group(
dropdown,
tooltip-html="'#{translate('recompile_pdf')} <span class=\"keyboard-shortcut\">({{modifierKey}} + Enter)</span>'"
tooltip-html="'"+translate('recompile_pdf')+" <span class=\"keyboard-shortcut\">({{modifierKey}} + Enter)</span>'"
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()"
@@ -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"
)
@@ -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"
@@ -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
@@ -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"
)
@@ -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;")
@@ -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
@@ -5,4 +5,4 @@ script(type='text/ng-template', id='scribtexModalTemplate')
p ScribTeX has moved to <strong>https://scribtex.sharelatex.com</strong>. 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}
a(href="https://scribtex.sharelatex.com"+scribtexPath, style="font-size: 16px") https://scribtex.sharelatex.com#{scribtexPath}
@@ -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) {
@@ -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
| &nbsp;
| #{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: "<strong>" + groupSubscription.admin_id.email + "</strong>"})}
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
@@ -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)
@@ -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")
@@ -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
)
@@ -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")
@@ -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")
+8
View File
@@ -2,3 +2,11 @@
*/test/unit/js
*/index.js
ldap
admin-panel
groovehq
launchpad
learn-wiki
references-search
sharelatex-saml
templates
tpr-webmodule
+471 -57
View File
@@ -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",
+2 -1
View File
@@ -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": {
@@ -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
@@ -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 = () =>
@@ -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
@@ -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]?
@@ -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"
@@ -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 () ->
@@ -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()
@@ -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()
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()
@@ -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 ->