mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge branch 'master' into pr-overflowing-images-wiki
This commit is contained in:
commit
4263d3f8c6
15 changed files with 4718 additions and 196 deletions
|
@ -1,8 +1,11 @@
|
||||||
|
Settings = require "settings-sharelatex"
|
||||||
User = require("../../models/User").User
|
User = require("../../models/User").User
|
||||||
{db, ObjectId} = require("../../infrastructure/mongojs")
|
{db, ObjectId} = require("../../infrastructure/mongojs")
|
||||||
crypto = require 'crypto'
|
crypto = require 'crypto'
|
||||||
bcrypt = require 'bcrypt'
|
bcrypt = require 'bcrypt'
|
||||||
|
|
||||||
|
BCRYPT_ROUNDS = Settings?.security?.bcryptRounds or 12
|
||||||
|
|
||||||
module.exports = AuthenticationManager =
|
module.exports = AuthenticationManager =
|
||||||
authenticate: (query, password, callback = (error, user) ->) ->
|
authenticate: (query, password, callback = (error, user) ->) ->
|
||||||
# Using Mongoose for legacy reasons here. The returned User instance
|
# Using Mongoose for legacy reasons here. The returned User instance
|
||||||
|
@ -15,6 +18,8 @@ module.exports = AuthenticationManager =
|
||||||
bcrypt.compare password, user.hashedPassword, (error, match) ->
|
bcrypt.compare password, user.hashedPassword, (error, match) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
if match
|
if match
|
||||||
|
AuthenticationManager.checkRounds user, user.hashedPassword, password, (err) ->
|
||||||
|
return callback(err) if err?
|
||||||
callback null, user
|
callback null, user
|
||||||
else
|
else
|
||||||
callback null, null
|
callback null, null
|
||||||
|
@ -24,7 +29,7 @@ module.exports = AuthenticationManager =
|
||||||
callback null, null
|
callback null, null
|
||||||
|
|
||||||
setUserPassword: (user_id, password, callback = (error) ->) ->
|
setUserPassword: (user_id, password, callback = (error) ->) ->
|
||||||
bcrypt.genSalt 7, (error, salt) ->
|
bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
bcrypt.hash password, salt, (error, hash) ->
|
bcrypt.hash password, salt, (error, hash) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
@ -35,3 +40,10 @@ module.exports = AuthenticationManager =
|
||||||
$unset: password: true
|
$unset: password: true
|
||||||
}, callback)
|
}, callback)
|
||||||
|
|
||||||
|
checkRounds: (user, hashedPassword, password, callback = (error) ->) ->
|
||||||
|
# check current number of rounds and rehash if necessary
|
||||||
|
currentRounds = bcrypt.getRounds hashedPassword
|
||||||
|
if currentRounds < BCRYPT_ROUNDS
|
||||||
|
AuthenticationManager.setUserPassword user._id, password, callback
|
||||||
|
else
|
||||||
|
callback()
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
request = require("request")
|
|
||||||
settings = require("settings-sharelatex")
|
|
||||||
logger = require("logger-sharelatex")
|
|
||||||
ErrorController = require "../Errors/ErrorController"
|
|
||||||
_ = require("underscore")
|
|
||||||
AuthenticationController = require("../Authentication/AuthenticationController")
|
|
||||||
async = require("async")
|
|
||||||
other_lngs = ["es"]
|
|
||||||
|
|
||||||
module.exports = WikiController =
|
|
||||||
|
|
||||||
|
|
||||||
_checkIfLoginIsNeeded: (req, res, next)->
|
|
||||||
if settings.apis.wiki.requireLogin
|
|
||||||
AuthenticationController.requireLogin()(req, res, next)
|
|
||||||
else
|
|
||||||
next()
|
|
||||||
|
|
||||||
getPage: (req, res, next) ->
|
|
||||||
WikiController._checkIfLoginIsNeeded req, res, ->
|
|
||||||
|
|
||||||
page = req.url.replace(/^\/learn/, "").replace(/^\//, "")
|
|
||||||
if page == ""
|
|
||||||
page = "Main_Page"
|
|
||||||
|
|
||||||
logger.log page: page, "getting page from wiki"
|
|
||||||
if _.include(other_lngs, req.lng)
|
|
||||||
lngPage = "#{page}_#{req.lng}"
|
|
||||||
else
|
|
||||||
lngPage = page
|
|
||||||
jobs =
|
|
||||||
contents: (cb)->
|
|
||||||
WikiController._getPageContent "Contents", cb
|
|
||||||
pageData: (cb)->
|
|
||||||
WikiController._getPageContent lngPage, cb
|
|
||||||
async.parallel jobs, (error, results)->
|
|
||||||
return next(error) if error?
|
|
||||||
{pageData, contents} = results
|
|
||||||
if pageData.content?.length > 280
|
|
||||||
if _.include(other_lngs, req.lng)
|
|
||||||
pageData.title = pageData.title.slice(0, pageData.title.length - (req.lng.length+1) )
|
|
||||||
|
|
||||||
if pageData.title?.toLowerCase()?.indexOf("kb") == 0
|
|
||||||
pageData.title = pageData.title.slice(3)
|
|
||||||
|
|
||||||
if pageData.title?.toLowerCase()?.indexOf("errors") == 0
|
|
||||||
pageData.title = pageData.title.slice(7)
|
|
||||||
|
|
||||||
WikiController._renderPage(pageData, contents, res)
|
|
||||||
else
|
|
||||||
WikiController._getPageContent page, (error, pageData) ->
|
|
||||||
return next(error) if error?
|
|
||||||
WikiController._renderPage(pageData, contents, res)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_getPageContent: (page, callback = (error, data = { content: "", title: "" }) ->) ->
|
|
||||||
request {
|
|
||||||
url: "#{settings.apis.wiki.url}/learn-scripts/api.php"
|
|
||||||
qs: {
|
|
||||||
page: decodeURI(page)
|
|
||||||
action: "parse"
|
|
||||||
format: "json"
|
|
||||||
}
|
|
||||||
}, (err, response, data)->
|
|
||||||
return callback(err) if err?
|
|
||||||
try
|
|
||||||
data = JSON.parse(data)
|
|
||||||
catch err
|
|
||||||
logger.err err:err, data:data, "error parsing data from wiki"
|
|
||||||
result =
|
|
||||||
content: data?.parse?.text?['*']
|
|
||||||
title: data?.parse?.title
|
|
||||||
callback null, result
|
|
||||||
|
|
||||||
|
|
||||||
_renderPage: (page, contents, res)->
|
|
||||||
if page.title == "Main Page"
|
|
||||||
title = "Documentation"
|
|
||||||
else
|
|
||||||
title = page.title
|
|
||||||
|
|
||||||
res.render "wiki/page", {
|
|
||||||
page: page
|
|
||||||
contents: contents
|
|
||||||
title: title
|
|
||||||
}
|
|
|
@ -30,7 +30,6 @@ PasswordResetRouter = require("./Features/PasswordReset/PasswordResetRouter")
|
||||||
StaticPagesRouter = require("./Features/StaticPages/StaticPagesRouter")
|
StaticPagesRouter = require("./Features/StaticPages/StaticPagesRouter")
|
||||||
ChatController = require("./Features/Chat/ChatController")
|
ChatController = require("./Features/Chat/ChatController")
|
||||||
BlogController = require("./Features/Blog/BlogController")
|
BlogController = require("./Features/Blog/BlogController")
|
||||||
WikiController = require("./Features/Wiki/WikiController")
|
|
||||||
Modules = require "./infrastructure/Modules"
|
Modules = require "./infrastructure/Modules"
|
||||||
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
||||||
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
||||||
|
@ -204,13 +203,6 @@ module.exports = class Router
|
||||||
webRouter.get "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.getMessages
|
webRouter.get "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.getMessages
|
||||||
webRouter.post "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.sendMessage
|
webRouter.post "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.sendMessage
|
||||||
|
|
||||||
webRouter.get /learn(\/.*)?/, RateLimiterMiddlewear.rateLimit({
|
|
||||||
endpointName: "wiki"
|
|
||||||
params: []
|
|
||||||
maxRequests: 60
|
|
||||||
timeInterval: 60
|
|
||||||
}), WikiController.getPage
|
|
||||||
|
|
||||||
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
|
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
|
||||||
webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@ block content
|
||||||
window.csrfToken = "!{csrfToken}";
|
window.csrfToken = "!{csrfToken}";
|
||||||
window.anonymous = #{anonymous};
|
window.anonymous = #{anonymous};
|
||||||
window.maxDocLength = #{maxDocLength};
|
window.maxDocLength = #{maxDocLength};
|
||||||
|
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
|
||||||
window.requirejs = {
|
window.requirejs = {
|
||||||
"paths" : {
|
"paths" : {
|
||||||
"mathjax": "/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML",
|
"mathjax": "/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML",
|
||||||
|
|
|
@ -103,6 +103,8 @@ div.full-size.pdf(ng-controller="PdfController")
|
||||||
ng-init="feedbackSent = false;"
|
ng-init="feedbackSent = false;"
|
||||||
)
|
)
|
||||||
span.line-no
|
span.line-no
|
||||||
|
i.fa.fa-link(aria-hidden="true")
|
||||||
|
|
|
||||||
span(ng-show="entry.file") {{ entry.file }}
|
span(ng-show="entry.file") {{ entry.file }}
|
||||||
span(ng-show="entry.line") , line {{ entry.line }}
|
span(ng-show="entry.line") , line {{ entry.line }}
|
||||||
p.entry-message(ng-show="entry.message") {{ entry.message }}
|
p.entry-message(ng-show="entry.message") {{ entry.message }}
|
||||||
|
@ -112,9 +114,11 @@ div.full-size.pdf(ng-controller="PdfController")
|
||||||
)
|
)
|
||||||
figure.card-hint-icon-container
|
figure.card-hint-icon-container
|
||||||
i.fa.fa-lightbulb-o(aria-hidden="true")
|
i.fa.fa-lightbulb-o(aria-hidden="true")
|
||||||
p.card-hint-text(ng-show="entry.humanReadableHint", ng-bind-html="entry.humanReadableHint")
|
p.card-hint-text(
|
||||||
.card-hint-actions
|
ng-show="entry.humanReadableHint",
|
||||||
.card-hint-ext-link
|
ng-bind-html="wikiEnabled ? entry.humanReadableHint : stripHTMLFromString(entry.humanReadableHint)")
|
||||||
|
.card-hint-actions.clearfix
|
||||||
|
.card-hint-ext-link(ng-if="wikiEnabled")
|
||||||
a(ng-href="{{ entry.extraInfoURL }}", target="_blank")
|
a(ng-href="{{ entry.extraInfoURL }}", target="_blank")
|
||||||
i.fa.fa-external-link
|
i.fa.fa-external-link
|
||||||
| #{translate("log_hint_extra_info")}
|
| #{translate("log_hint_extra_info")}
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
extends ../layout
|
|
||||||
|
|
||||||
block content
|
|
||||||
.content.content-alt(ng-cloak)
|
|
||||||
.container.wiki
|
|
||||||
.row.template-page-header
|
|
||||||
.col-md-8(ng-cloak)
|
|
||||||
|
|
||||||
.row
|
|
||||||
.col-xs-3.contents(ng-non-bindable)
|
|
||||||
| !{contents.content}
|
|
||||||
|
|
||||||
.col-xs-9.page
|
|
||||||
- if(typeof(settings.algolia) != "undefined" && typeof(settings.algolia.indexes) != "undefined" && typeof(settings.algolia.indexes.wiki) != "undefined")
|
|
||||||
span(ng-controller="SearchWikiController")
|
|
||||||
.row
|
|
||||||
form.project-search.form-horizontal.col-md-9(role="form")
|
|
||||||
.form-group.has-feedback.has-feedback-left.col-md-12
|
|
||||||
input.form-control.col-md-12(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search help library....")
|
|
||||||
i.fa.fa-search.form-control-feedback-left
|
|
||||||
i.fa.fa-times.form-control-feedback(
|
|
||||||
ng-click="clearSearchText()",
|
|
||||||
style="cursor: pointer;",
|
|
||||||
ng-show="searchQueryText.length > 0"
|
|
||||||
)
|
|
||||||
.col-md-3.text-right
|
|
||||||
a.btn.btn-primary(ng-click="showMissingTemplateModal()") #{translate("suggest_new_doc")}
|
|
||||||
|
|
||||||
.row
|
|
||||||
.col-md-12(ng-cloak)
|
|
||||||
a(ng-href='{{hit.url}}',ng-repeat='hit in hits').search-result.card.card-thin
|
|
||||||
span(ng-bind-html='hit.name')
|
|
||||||
div.search-result-content(ng-show="hit.content != ''", ng-bind-html='hit.content')
|
|
||||||
|
|
||||||
.card.row-spaced(ng-non-bindable)
|
|
||||||
.page-header
|
|
||||||
h1 #{title}
|
|
||||||
|
|
||||||
| !{page.content}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
script(type="text/ng-template", id="missingWikiPageModal")
|
|
||||||
.modal-header
|
|
||||||
button.close(
|
|
||||||
type="button"
|
|
||||||
data-dismiss="modal"
|
|
||||||
ng-click="close()"
|
|
||||||
) ×
|
|
||||||
h3 #{translate("suggest_new_doc")}
|
|
||||||
.modal-body.contact-us-modal
|
|
||||||
span(ng-show="sent == false")
|
|
||||||
label.desc
|
|
||||||
| #{translate("email")} (#{translate("optional")})
|
|
||||||
.form-group
|
|
||||||
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.desc
|
|
||||||
| #{translate("suggestion")}
|
|
||||||
.form-group
|
|
||||||
textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', maxlength='255', tabindex='4', onkeyup='')
|
|
||||||
span(ng-show="sent")
|
|
||||||
p #{translate("request_sent_thank_you")}
|
|
||||||
.modal-footer
|
|
||||||
button.btn.btn-default(ng-click="close()")
|
|
||||||
span #{translate("dismiss")}
|
|
||||||
button.btn-success.btn(type='submit', ng-disabled="sending", ng-click="contactUs()") #{translate("contact_us")}
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ module.exports = settings =
|
||||||
# --------
|
# --------
|
||||||
security:
|
security:
|
||||||
sessionSecret: sessionSecret
|
sessionSecret: sessionSecret
|
||||||
|
bcryptRounds: 12 # number of rounds used to hash user passwords (raised to power 2)
|
||||||
|
|
||||||
httpAuthUsers: httpAuthUsers
|
httpAuthUsers: httpAuthUsers
|
||||||
|
|
||||||
|
|
4602
services/web/npm-shrinkwrap.json
generated
Normal file
4602
services/web/npm-shrinkwrap.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -76,6 +76,7 @@
|
||||||
"grunt-contrib-watch": "^1.0.0",
|
"grunt-contrib-watch": "^1.0.0",
|
||||||
"grunt-env": "0.4.4",
|
"grunt-env": "0.4.4",
|
||||||
"grunt-exec": "^0.4.7",
|
"grunt-exec": "^0.4.7",
|
||||||
|
"grunt-execute": "^0.2.2",
|
||||||
"grunt-file-append": "0.0.6",
|
"grunt-file-append": "0.0.6",
|
||||||
"grunt-git-rev-parse": "^0.1.4",
|
"grunt-git-rev-parse": "^0.1.4",
|
||||||
"grunt-mocha-test": "0.9.0",
|
"grunt-mocha-test": "0.9.0",
|
||||||
|
|
|
@ -74,15 +74,17 @@ define [
|
||||||
editor.commands.removeCommand "foldall"
|
editor.commands.removeCommand "foldall"
|
||||||
|
|
||||||
# For European keyboards, the / is above 7 so needs Shift pressing.
|
# For European keyboards, the / is above 7 so needs Shift pressing.
|
||||||
# This comes through as Ctrl-Shift-/ which is mapped to toggleBlockComment.
|
# This comes through as Command-Shift-/ on OS X, which is mapped to
|
||||||
|
# toggleBlockComment.
|
||||||
# This doesn't do anything for LaTeX, so remap this to togglecomment to
|
# This doesn't do anything for LaTeX, so remap this to togglecomment to
|
||||||
# work for European keyboards as normal.
|
# work for European keyboards as normal.
|
||||||
|
# On Windows, the key combo comes as Ctrl-Shift-7.
|
||||||
editor.commands.removeCommand "toggleBlockComment"
|
editor.commands.removeCommand "toggleBlockComment"
|
||||||
editor.commands.removeCommand "togglecomment"
|
editor.commands.removeCommand "togglecomment"
|
||||||
|
|
||||||
editor.commands.addCommand {
|
editor.commands.addCommand {
|
||||||
name: "togglecomment",
|
name: "togglecomment",
|
||||||
bindKey: { win: "Ctrl-/|Ctrl-Shift-/", mac: "Command-/|Command-Shift-/" },
|
bindKey: { win: "Ctrl-/|Ctrl-Shift-7", mac: "Command-/|Command-Shift-/" },
|
||||||
exec: (editor) -> editor.toggleCommentLines(),
|
exec: (editor) -> editor.toggleCommentLines(),
|
||||||
multiSelectAction: "forEachLine",
|
multiSelectAction: "forEachLine",
|
||||||
scrollIntoView: "selectionPart"
|
scrollIntoView: "selectionPart"
|
||||||
|
|
|
@ -1,90 +1,90 @@
|
||||||
define -> [
|
define -> [
|
||||||
regexToMatch: /Misplaced alignment tab character \&/
|
regexToMatch: /Misplaced alignment tab character \&/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Misplaced_alignment_tab_character_%26"
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Misplaced_alignment_tab_character_%26"
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have placed an alignment tab character '&' in the wrong place. If you want to align something, you must write it inside an align environment such as \\begin{align} \u2026 \\end{align}, \\begin{tabular} \u2026 \\end{tabular}, etc. If you want to write an ampersand '&' in text, you must write \\& instead.
|
You have placed an alignment tab character '&' in the wrong place. If you want to align something, you must write it inside an align environment such as \\begin{align} \u2026 \\end{align}, \\begin{tabular} \u2026 \\end{tabular}, etc. If you want to write an ampersand '&' in text, you must write \\& instead.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /Extra alignment tab has been changed to \\cr/
|
regexToMatch: /Extra alignment tab has been changed to \\cr/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Extra_alignment_tab_has_been_changed_to_%5Ccr"
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Extra_alignment_tab_has_been_changed_to_%5Ccr"
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have written too many alignment tabs in a table, causing one of them to be turned into a line break. Make sure you have specified the correct number of columns in your <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Tables\">table</a>.
|
You have written too many alignment tabs in a table, causing one of them to be turned into a line break. Make sure you have specified the correct number of columns in your <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Tables\">table</a>.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /Display math should end with \$\$/
|
regexToMatch: /Display math should end with \$\$/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Display_math_should_end_with_$$."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Display_math_should_end_with_$$."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have forgotten a $ sign at the end of 'display math' mode. When writing in display math mode, you must always math write inside $$ \u2026 $$. Check that the number of $s match around each math expression.
|
You have forgotten a $ sign at the end of 'display math' mode. When writing in display math mode, you must always math write inside $$ \u2026 $$. Check that the number of $s match around each math expression.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /Missing [{$] inserted./
|
regexToMatch: /Missing [{$] inserted./
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Missing_$_inserted"
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Missing_$_inserted"
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:Missing_$_inserted\">here</a>.
|
Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/Missing_$_inserted \">here</a>.If you intended to use mathematics mode, then use $ \u2026 $ for 'inline math mode', $$ \u2026 $$ for 'display math mode' or alternatively \begin{math} \u2026 \end{math}.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /(undefined )?[rR]eference(s)?.+(undefined)?/
|
regexToMatch: /(undefined )?[rR]eference(s)?.+(undefined)?/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_undefined_references."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have referenced something which has not yet been labelled. If you have labelled it already, make sure that what is written inside \\ref{...} is the same as what is written inside \\label{...}.
|
You have referenced something which has not yet been labelled. If you have labelled it already, make sure that what is written inside \\ref{...} is the same as what is written inside \\label{...}.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /Citation .+ on page .+ undefined on input line .+/
|
regexToMatch: /Citation .+ on page .+ undefined on input line .+/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have cited something which is not included in your bibliography. Make sure that the citation (\\cite{...}) has a corresponding key in your bibliography, and that both are spelled the same way.
|
You have cited something which is not included in your bibliography. Make sure that the citation (\\cite{...}) has a corresponding key in your bibliography, and that both are spelled the same way.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /(Label .+)? multiply[ -]defined( labels)?/
|
regexToMatch: /(Label .+)? multiply[ -]defined( labels)?/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_multiply-defined_labels."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_multiply-defined_labels."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have used the same label more than once. Check that each \\label{...} labels only one item.
|
You have used the same label more than once. Check that each \\label{...} labels only one item.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /`!?h' float specifier changed to `!?ht'/
|
regexToMatch: /`!?h' float specifier changed to `!?ht'/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:%60!h%27_float_specifier_changed_to_%60!ht%27."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/%60!h%27_float_specifier_changed_to_%60!ht%27."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
The float specifier 'h' is too strict of a demand for LaTeX to place your float in a nice way here. Try relaxing it by using 'ht', or even 'htbp' if necessary. If you want to try keep the float here anyway, check out the <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">float package</a>.
|
The float specifier 'h' is too strict of a demand for LaTeX to place your float in a nice way here. Try relaxing it by using 'ht', or even 'htbp' if necessary. If you want to try keep the float here anyway, check out the <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">float package</a>.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /No positions in optional float specifier/
|
regexToMatch: /No positions in optional float specifier/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:No_positions_in_optional_float_specifier."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have forgotten to include a float specifier, which tells LaTeX where to position your figure. Find out more about float specifiers <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
|
You have forgotten to include a float specifier, which tells LaTeX where to position your figure. Find out more about float specifiers <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /Undefined control sequence/
|
regexToMatch: /Undefined control sequence/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Undefined_control_sequence."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
The compiler is having trouble understanding a command you have used. Check that the command is spelled correctly. If the command is part of a package, make sure you have included the package in your preamble using \\usepackage{...}.
|
The compiler is having trouble understanding a command you have used. Check that the command is spelled correctly. If the command is part of a package, make sure you have included the package in your preamble using \\usepackage{...}.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /File .+ not found/
|
regexToMatch: /File .+ not found/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:File_XXX_not_found_on_input_line_XXX."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
The compiler cannot find the file you want to include. Make sure that you have <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Including_images_in_ShareLaTeX\">uploaded the file</a> and <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:File_XXX_not_found_on_input_line_XXX.\">specified the file location correctly</a>.
|
The compiler cannot find the file you want to include. Make sure that you have <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Including_images_in_ShareLaTeX\">uploaded the file</a> and <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX.\">specified the file location correctly</a>.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /LaTeX Error: Unknown graphics extension: \..+/
|
regexToMatch: /LaTeX Error: Unknown graphics extension: \..+/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_graphics_extension:_.gif."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
The compiler does not recognise the file type of one of your images. Make sure you are using a <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_graphics_extension:_.gif.\">supported image format</a> for your choice of compiler, and check that there are no periods (.) in the name of your image.
|
The compiler does not recognise the file type of one of your images. Make sure you are using a <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif.\">supported image format</a> for your choice of compiler, and check that there are no periods (.) in the name of your image.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /LaTeX Error: Unknown float option `H'/
|
regexToMatch: /LaTeX Error: Unknown float option `H'/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
|
The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /LaTeX Error: Unknown float option `.+'/
|
regexToMatch: /LaTeX Error: Unknown float option `.+'/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have used a float specifier which the compiler does not understand. You can learn more about the different float options available for placing figures <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
|
You have used a float specifier which the compiler does not understand. You can learn more about the different float options available for placing figures <a target=\"_blank\" href=\"https://www.sharelatex.com/learn/Positioning_of_Figures\">here</a>.
|
||||||
"""
|
"""
|
||||||
,
|
,
|
||||||
regexToMatch: /LaTeX Error: \\math.+ allowed only in math mode/
|
regexToMatch: /LaTeX Error: \\math.+ allowed only in math mode/
|
||||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
|
extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
|
||||||
humanReadableHint: """
|
humanReadableHint: """
|
||||||
You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
|
You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,13 +6,14 @@ define [
|
||||||
], (App, Ace, HumanReadableLogs, BibLogParser) ->
|
], (App, Ace, HumanReadableLogs, BibLogParser) ->
|
||||||
App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
|
App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
|
||||||
|
|
||||||
# enable per-user containers if querystring includes isolated=true
|
# enable per-user containers by default
|
||||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
perUserCompile = true
|
||||||
autoCompile = true
|
autoCompile = true
|
||||||
|
|
||||||
# pdf.view = uncompiled | pdf | errors
|
# pdf.view = uncompiled | pdf | errors
|
||||||
$scope.pdf.view = if $scope?.pdf?.url then 'pdf' else 'uncompiled'
|
$scope.pdf.view = if $scope?.pdf?.url then 'pdf' else 'uncompiled'
|
||||||
$scope.shouldShowLogs = false
|
$scope.shouldShowLogs = false
|
||||||
|
$scope.wikiEnabled = window.wikiEnabled;
|
||||||
|
|
||||||
if ace.require("ace/lib/useragent").isMac
|
if ace.require("ace/lib/useragent").isMac
|
||||||
$scope.modifierKey = "Cmd"
|
$scope.modifierKey = "Cmd"
|
||||||
|
@ -24,6 +25,11 @@ define [
|
||||||
qs_args = ("#{k}=#{v}" for k, v of args)
|
qs_args = ("#{k}=#{v}" for k, v of args)
|
||||||
if qs_args.length then "?" + qs_args.join("&") else ""
|
if qs_args.length then "?" + qs_args.join("&") else ""
|
||||||
|
|
||||||
|
$scope.stripHTMLFromString = (htmlStr) ->
|
||||||
|
tmp = document.createElement("DIV")
|
||||||
|
tmp.innerHTML = htmlStr
|
||||||
|
return tmp.textContent || tmp.innerText || ""
|
||||||
|
|
||||||
$scope.$on "project:joined", () ->
|
$scope.$on "project:joined", () ->
|
||||||
return if !autoCompile
|
return if !autoCompile
|
||||||
autoCompile = false
|
autoCompile = false
|
||||||
|
@ -319,8 +325,8 @@ define [
|
||||||
$scope.startedFreeTrial = true
|
$scope.startedFreeTrial = true
|
||||||
|
|
||||||
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
||||||
# enable per-user containers if querystring includes isolated=true
|
# enable per-user containers by default
|
||||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
perUserCompile = true
|
||||||
|
|
||||||
synctex =
|
synctex =
|
||||||
syncToPdf: (cursorPosition) ->
|
syncToPdf: (cursorPosition) ->
|
||||||
|
|
|
@ -5,7 +5,9 @@ define [
|
||||||
$scope.status =
|
$scope.status =
|
||||||
loading:true
|
loading:true
|
||||||
|
|
||||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
# enable per-user containers by default
|
||||||
|
perUserCompile = true
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
url:"/project/#{ide.project_id}/wordcount"
|
url:"/project/#{ide.project_id}/wordcount"
|
||||||
method:"GET"
|
method:"GET"
|
||||||
|
|
|
@ -120,6 +120,10 @@
|
||||||
float: right;
|
float: right;
|
||||||
color: @gray;
|
color: @gray;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.entry-message {
|
.entry-message {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -130,6 +134,26 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
//font-family: @font-family-monospace;
|
//font-family: @font-family-monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover .line-no {
|
||||||
|
color: inherit;
|
||||||
|
.fa {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert-danger:hover {
|
||||||
|
background-color: darken(@alert-danger-bg, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert-warning:hover {
|
||||||
|
background-color: darken(@alert-warning-bg, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert-info:hover {
|
||||||
|
background-color: darken(@alert-info-bg, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
|
@ -16,6 +16,7 @@ describe "AuthenticationManager", ->
|
||||||
users: {}
|
users: {}
|
||||||
ObjectId: ObjectId
|
ObjectId: ObjectId
|
||||||
"bcrypt": @bcrypt = {}
|
"bcrypt": @bcrypt = {}
|
||||||
|
"settings-sharelatex": { security: { bcryptRounds: 12 } }
|
||||||
@callback = sinon.stub()
|
@callback = sinon.stub()
|
||||||
|
|
||||||
describe "authenticate", ->
|
describe "authenticate", ->
|
||||||
|
@ -31,6 +32,7 @@ describe "AuthenticationManager", ->
|
||||||
beforeEach (done) ->
|
beforeEach (done) ->
|
||||||
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
|
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
|
||||||
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
|
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
|
||||||
|
@bcrypt.getRounds = sinon.stub().returns 12
|
||||||
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
|
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
|
||||||
@callback(error, user)
|
@callback(error, user)
|
||||||
done()
|
done()
|
||||||
|
@ -54,6 +56,35 @@ describe "AuthenticationManager", ->
|
||||||
it "should not return the user", ->
|
it "should not return the user", ->
|
||||||
@callback.calledWith(null, null).should.equal true
|
@callback.calledWith(null, null).should.equal true
|
||||||
|
|
||||||
|
describe "when the hashed password matches but the number of rounds is too low", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
|
||||||
|
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
|
||||||
|
@bcrypt.getRounds = sinon.stub().returns 7
|
||||||
|
@AuthenticationManager.setUserPassword = sinon.stub().callsArgWith(2, null)
|
||||||
|
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
|
||||||
|
@callback(error, user)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should look up the correct user in the database", ->
|
||||||
|
@User.findOne.calledWith(email: @email).should.equal true
|
||||||
|
|
||||||
|
it "should check that the passwords match", ->
|
||||||
|
@bcrypt.compare
|
||||||
|
.calledWith(@unencryptedPassword, @hashedPassword)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should check the number of rounds", ->
|
||||||
|
@bcrypt.getRounds.called.should.equal true
|
||||||
|
|
||||||
|
it "should set the users password (with a higher number of rounds)", ->
|
||||||
|
@AuthenticationManager.setUserPassword
|
||||||
|
.calledWith("user-id", @unencryptedPassword)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should return the user", ->
|
||||||
|
@callback.calledWith(null, @user).should.equal true
|
||||||
|
|
||||||
describe "when the user does not exist in the database", ->
|
describe "when the user does not exist in the database", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@User.findOne = sinon.stub().callsArgWith(1, null, null)
|
@User.findOne = sinon.stub().callsArgWith(1, null, null)
|
||||||
|
@ -87,7 +118,7 @@ describe "AuthenticationManager", ->
|
||||||
|
|
||||||
it "should hash the password", ->
|
it "should hash the password", ->
|
||||||
@bcrypt.genSalt
|
@bcrypt.genSalt
|
||||||
.calledWith(7)
|
.calledWith(12)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
@bcrypt.hash
|
@bcrypt.hash
|
||||||
.calledWith(@password, @salt)
|
.calledWith(@password, @salt)
|
||||||
|
|
Loading…
Reference in a new issue