mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -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
|
||||
{db, ObjectId} = require("../../infrastructure/mongojs")
|
||||
crypto = require 'crypto'
|
||||
bcrypt = require 'bcrypt'
|
||||
|
||||
BCRYPT_ROUNDS = Settings?.security?.bcryptRounds or 12
|
||||
|
||||
module.exports = AuthenticationManager =
|
||||
authenticate: (query, password, callback = (error, user) ->) ->
|
||||
# Using Mongoose for legacy reasons here. The returned User instance
|
||||
|
@ -15,7 +18,9 @@ module.exports = AuthenticationManager =
|
|||
bcrypt.compare password, user.hashedPassword, (error, match) ->
|
||||
return callback(error) if error?
|
||||
if match
|
||||
callback null, user
|
||||
AuthenticationManager.checkRounds user, user.hashedPassword, password, (err) ->
|
||||
return callback(err) if err?
|
||||
callback null, user
|
||||
else
|
||||
callback null, null
|
||||
else
|
||||
|
@ -24,7 +29,7 @@ module.exports = AuthenticationManager =
|
|||
callback null, null
|
||||
|
||||
setUserPassword: (user_id, password, callback = (error) ->) ->
|
||||
bcrypt.genSalt 7, (error, salt) ->
|
||||
bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
|
||||
return callback(error) if error?
|
||||
bcrypt.hash password, salt, (error, hash) ->
|
||||
return callback(error) if error?
|
||||
|
@ -35,3 +40,10 @@ module.exports = AuthenticationManager =
|
|||
$unset: password: true
|
||||
}, 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")
|
||||
ChatController = require("./Features/Chat/ChatController")
|
||||
BlogController = require("./Features/Blog/BlogController")
|
||||
WikiController = require("./Features/Wiki/WikiController")
|
||||
Modules = require "./infrastructure/Modules"
|
||||
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
|
||||
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
|
||||
|
@ -204,13 +203,6 @@ module.exports = class Router
|
|||
webRouter.get "/project/:Project_id/messages", AuthorizationMiddlewear.ensureUserCanReadProject, ChatController.getMessages
|
||||
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/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ block content
|
|||
window.csrfToken = "!{csrfToken}";
|
||||
window.anonymous = #{anonymous};
|
||||
window.maxDocLength = #{maxDocLength};
|
||||
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
|
||||
window.requirejs = {
|
||||
"paths" : {
|
||||
"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;"
|
||||
)
|
||||
span.line-no
|
||||
i.fa.fa-link(aria-hidden="true")
|
||||
|
|
||||
span(ng-show="entry.file") {{ entry.file }}
|
||||
span(ng-show="entry.line") , line {{ entry.line }}
|
||||
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
|
||||
i.fa.fa-lightbulb-o(aria-hidden="true")
|
||||
p.card-hint-text(ng-show="entry.humanReadableHint", ng-bind-html="entry.humanReadableHint")
|
||||
.card-hint-actions
|
||||
.card-hint-ext-link
|
||||
p.card-hint-text(
|
||||
ng-show="entry.humanReadableHint",
|
||||
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")
|
||||
i.fa.fa-external-link
|
||||
| #{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:
|
||||
sessionSecret: sessionSecret
|
||||
bcryptRounds: 12 # number of rounds used to hash user passwords (raised to power 2)
|
||||
|
||||
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-env": "0.4.4",
|
||||
"grunt-exec": "^0.4.7",
|
||||
"grunt-execute": "^0.2.2",
|
||||
"grunt-file-append": "0.0.6",
|
||||
"grunt-git-rev-parse": "^0.1.4",
|
||||
"grunt-mocha-test": "0.9.0",
|
||||
|
|
|
@ -74,15 +74,17 @@ define [
|
|||
editor.commands.removeCommand "foldall"
|
||||
|
||||
# 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
|
||||
# work for European keyboards as normal.
|
||||
# On Windows, the key combo comes as Ctrl-Shift-7.
|
||||
editor.commands.removeCommand "toggleBlockComment"
|
||||
editor.commands.removeCommand "togglecomment"
|
||||
|
||||
editor.commands.addCommand {
|
||||
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(),
|
||||
multiSelectAction: "forEachLine",
|
||||
scrollIntoView: "selectionPart"
|
||||
|
|
|
@ -1,90 +1,90 @@
|
|||
define -> [
|
||||
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: """
|
||||
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/
|
||||
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: """
|
||||
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 \$\$/
|
||||
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: """
|
||||
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./
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Missing_$_inserted"
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Missing_$_inserted"
|
||||
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)?/
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_undefined_references."
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references."
|
||||
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{...}.
|
||||
"""
|
||||
,
|
||||
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: """
|
||||
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)?/
|
||||
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: """
|
||||
You have used the same label more than once. Check that each \\label{...} labels only one item.
|
||||
"""
|
||||
,
|
||||
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: """
|
||||
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/
|
||||
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: """
|
||||
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/
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors:Undefined_control_sequence."
|
||||
extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence."
|
||||
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{...}.
|
||||
"""
|
||||
,
|
||||
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: """
|
||||
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: \..+/
|
||||
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: """
|
||||
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'/
|
||||
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: """
|
||||
The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
|
||||
"""
|
||||
,
|
||||
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: """
|
||||
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/
|
||||
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: """
|
||||
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.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
|
||||
|
||||
# enable per-user containers if querystring includes isolated=true
|
||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
||||
# enable per-user containers by default
|
||||
perUserCompile = true
|
||||
autoCompile = true
|
||||
|
||||
# pdf.view = uncompiled | pdf | errors
|
||||
$scope.pdf.view = if $scope?.pdf?.url then 'pdf' else 'uncompiled'
|
||||
$scope.shouldShowLogs = false
|
||||
$scope.wikiEnabled = window.wikiEnabled;
|
||||
|
||||
if ace.require("ace/lib/useragent").isMac
|
||||
$scope.modifierKey = "Cmd"
|
||||
|
@ -24,6 +25,11 @@ define [
|
|||
qs_args = ("#{k}=#{v}" for k, v of args)
|
||||
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", () ->
|
||||
return if !autoCompile
|
||||
autoCompile = false
|
||||
|
@ -173,7 +179,7 @@ define [
|
|||
accumulateResults = (newEntries) ->
|
||||
for key in ['all', 'errors', 'warnings']
|
||||
logEntries[key] = logEntries[key].concat newEntries[key]
|
||||
|
||||
|
||||
# use the parsers for each file type
|
||||
processLog = (log) ->
|
||||
$scope.pdf.rawLog = log
|
||||
|
@ -319,8 +325,8 @@ define [
|
|||
$scope.startedFreeTrial = true
|
||||
|
||||
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
||||
# enable per-user containers if querystring includes isolated=true
|
||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
||||
# enable per-user containers by default
|
||||
perUserCompile = true
|
||||
|
||||
synctex =
|
||||
syncToPdf: (cursorPosition) ->
|
||||
|
|
|
@ -5,7 +5,9 @@ define [
|
|||
$scope.status =
|
||||
loading:true
|
||||
|
||||
perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
|
||||
# enable per-user containers by default
|
||||
perUserCompile = true
|
||||
|
||||
opts =
|
||||
url:"/project/#{ide.project_id}/wordcount"
|
||||
method:"GET"
|
||||
|
|
|
@ -120,6 +120,10 @@
|
|||
float: right;
|
||||
color: @gray;
|
||||
font-weight: 700;
|
||||
|
||||
.fa {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.entry-message {
|
||||
font-weight: 700;
|
||||
|
@ -130,6 +134,26 @@
|
|||
font-size: 0.8rem;
|
||||
//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 {
|
||||
font-size: 12px;
|
||||
|
|
|
@ -16,6 +16,7 @@ describe "AuthenticationManager", ->
|
|||
users: {}
|
||||
ObjectId: ObjectId
|
||||
"bcrypt": @bcrypt = {}
|
||||
"settings-sharelatex": { security: { bcryptRounds: 12 } }
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe "authenticate", ->
|
||||
|
@ -31,6 +32,7 @@ describe "AuthenticationManager", ->
|
|||
beforeEach (done) ->
|
||||
@user.hashedPassword = @hashedPassword = "asdfjadflasdf"
|
||||
@bcrypt.compare = sinon.stub().callsArgWith(2, null, true)
|
||||
@bcrypt.getRounds = sinon.stub().returns 12
|
||||
@AuthenticationManager.authenticate email: @email, @unencryptedPassword, (error, user) =>
|
||||
@callback(error, user)
|
||||
done()
|
||||
|
@ -54,6 +56,35 @@ describe "AuthenticationManager", ->
|
|||
it "should not return the user", ->
|
||||
@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", ->
|
||||
beforeEach ->
|
||||
@User.findOne = sinon.stub().callsArgWith(1, null, null)
|
||||
|
@ -87,7 +118,7 @@ describe "AuthenticationManager", ->
|
|||
|
||||
it "should hash the password", ->
|
||||
@bcrypt.genSalt
|
||||
.calledWith(7)
|
||||
.calledWith(12)
|
||||
.should.equal true
|
||||
@bcrypt.hash
|
||||
.calledWith(@password, @salt)
|
||||
|
|
Loading…
Reference in a new issue