Merge branch 'master' into pr-overflowing-images-wiki

This commit is contained in:
Paulo Reis 2016-06-28 11:58:58 +01:00
commit 4263d3f8c6
15 changed files with 4718 additions and 196 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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")
| &nbsp;
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
|&nbsp;#{translate("log_hint_extra_info")} |&nbsp;#{translate("log_hint_extra_info")}

View file

@ -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()"
) &times;
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")}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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