hello world
-``` - -## Syntax Highlighting - -Marked has an interface that allows for a syntax highlighter to highlight code -blocks before they're output. - -Example implementation: - -``` js -var highlight = require('my-syntax-highlighter') - , marked_ = require('marked'); - -var marked = function(text) { - var tokens = marked_.lexer(text) - , l = tokens.length - , i = 0 - , token; - - for (; i < l; i++) { - token = tokens[i]; - if (token.type === 'code') { - token.text = highlight(token.text, token.lang); - // marked should not escape this - token.escaped = true; - } - } - - text = marked_.parser(tokens); - - return text; -}; - -module.exports = marked; -``` - -## License - -Copyright (c) 2011-2012, Christopher Jeffrey. (MIT License) - -See LICENSE for more info. diff --git a/bin/doc/node_modules/marked/bin/marked b/bin/doc/node_modules/marked/bin/marked deleted file mode 100644 index 7d00504ed..000000000 --- a/bin/doc/node_modules/marked/bin/marked +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env node - -/** - * Marked CLI - * Copyright (c) 2011-2012, Christopher Jeffrey (MIT License) - */ - -var fs = require('fs') - , util = require('util') - , marked = require('../'); - -/** - * Man Page - */ - -var help = function() { - var spawn = require('child_process').spawn; - - var options = { - cwd: process.cwd(), - env: process.env, - setsid: false, - customFds: [0, 1, 2] - }; - - spawn('man', - [__dirname + '/../man/marked.1'], - options); -}; - -/** - * Main - */ - -var main = function(argv) { - var files = [] - , data = '' - , input - , output - , arg - , tokens; - - var getarg = function() { - var arg = argv.shift(); - arg = arg.split('='); - if (arg.length > 1) { - argv.unshift(arg.slice(1).join('=')); - } - return arg[0]; - }; - - while (argv.length) { - arg = getarg(); - switch (arg) { - case '-o': - case '--output': - output = argv.shift(); - break; - case '-i': - case '--input': - input = argv.shift(); - break; - case '-t': - case '--tokens': - tokens = true; - break; - case '-h': - case '--help': - return help(); - default: - files.push(arg); - break; - } - } - - if (!input) { - if (files.length <= 2) { - var stdin = process.stdin; - - stdin.setEncoding('utf8'); - stdin.resume(); - - stdin.on('data', function(text) { - data += text; - }); - - stdin.on('end', write); - - return; - } - input = files.pop(); - } - - data = fs.readFileSync(input, 'utf8'); - write(); - - function write() { - data = tokens - ? JSON.stringify(marked.lexer(data), null, 2) - : marked(data); - - if (!output) { - process.stdout.write(data + '\n'); - } else { - fs.writeFileSync(output, data); - } - } -}; - -if (!module.parent) { - process.title = 'marked'; - main(process.argv.slice()); -} else { - module.exports = main; -} diff --git a/bin/doc/node_modules/marked/index.js b/bin/doc/node_modules/marked/index.js deleted file mode 100644 index a12f90569..000000000 --- a/bin/doc/node_modules/marked/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/marked'); diff --git a/bin/doc/node_modules/marked/lib/marked.js b/bin/doc/node_modules/marked/lib/marked.js deleted file mode 100644 index c6f3d0ac8..000000000 --- a/bin/doc/node_modules/marked/lib/marked.js +++ /dev/null @@ -1,662 +0,0 @@ -/** - * marked - A markdown parser (https://github.com/chjj/marked) - * Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed) - */ - -;(function() { - -/** - * Block-Level Grammar - */ - -var block = { - newline: /^\n+/, - code: /^ {4,}[^\n]*(?:\n {4,}[^\n]*|\n)*(?:\n+|$)/, - gfm_code: /^ *``` *(\w+)? *\n([^\0]+?)\s*``` *(?:\n+|$)/, - hr: /^( *[\-*_]){3,} *(?:\n+|$)/, - heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, - lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, - blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, - list: /^( *)([*+-]|\d+\.) [^\0]+?(?:\n{2,}(?! )|\s*$)(?!\1bullet)\n*/, - html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/, - def: /^ *\[([^\]]+)\]: *([^\s]+)(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, - paragraph: /^([^\n]+\n?(?!body))+\n*/, - text: /^[^\n]+/ -}; - -block.list = (function() { - var list = block.list.source; - - list = list - .replace('bullet', /(?:[*+-](?!(?: *[-*]){2,})|\d+\.)/.source); - - return new RegExp(list); -})(); - -block.html = (function() { - var html = block.html.source; - - html = html - .replace('comment', //.source) - .replace('closed', /<(tag)[^\0]+?<\/\1>/.source) - .replace('closing', /'
- + escape(cap[2], true)
- + '
';
- continue;
- }
-
- // br
- if (cap = inline.br.exec(src)) {
- src = src.substring(cap[0].length);
- out += ''
- + (token.escaped
- ? token.text
- : escape(token.text, true))
- + '
\n';
- }
- case 'blockquote_start': {
- var body = '';
-
- while (next().type !== 'blockquote_end') {
- body += tok();
- }
-
- return '\n' - + body - + '\n'; - } - case 'list_start': { - var type = token.ordered ? 'ol' : 'ul' - , body = ''; - - while (next().type !== 'list_end') { - body += tok(); - } - - return '<' - + type - + '>\n' - + body - + '' - + type - + '>\n'; - } - case 'list_item_start': { - var body = ''; - - while (next().type !== 'list_item_end') { - body += token.type === 'text' - ? parseText() - : tok(); - } - - return '
' - + inline.lexer(token.text) - + '
\n'; - } - case 'text': { - return '' - + parseText() - + '
\n'; - } - } -}; - -var parseText = function() { - var body = token.text - , top; - - while ((top = tokens[tokens.length-1]) - && top.type === 'text') { - body += '\n' + next().text; - } - - return inline.lexer(body); -}; - -var parse = function(src) { - tokens = src.reverse(); - - var out = ''; - while (next()) { - out += tok(); - } - - tokens = null; - token = null; - - return out; -}; - -/** - * Helpers - */ - -var escape = function(html, encode) { - return html - .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -}; - -var mangle = function(text) { - var out = '' - , l = text.length - , i = 0 - , ch; - - for (; i < l; i++) { - ch = text.charCodeAt(i); - if (Math.random() > 0.5) { - ch = 'x' + ch.toString(16); - } - out += '' + ch + ';'; - } - - return out; -}; - -function tag() { - var tag = '(?!(?:' - + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' - + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' - + '|span|br|wbr|ins|del|img)\\b)\\w+'; - - return tag; -} - -/** - * Expose - */ - -var marked = function(src) { - return parse(block.lexer(src)); -}; - -marked.parser = parse; -marked.lexer = block.lexer; - -marked.parse = marked; - -if (typeof module !== 'undefined') { - module.exports = marked; -} else { - this.marked = marked; -} - -}).call(this); diff --git a/bin/doc/node_modules/marked/man/marked.1 b/bin/doc/node_modules/marked/man/marked.1 deleted file mode 100644 index 214533390..000000000 --- a/bin/doc/node_modules/marked/man/marked.1 +++ /dev/null @@ -1,39 +0,0 @@ -.ds q \N'34' -.TH marked 1 -.SH NAME -marked \- a javascript markdown parser -.SH SYNOPSIS -.nf -.B marked [\-o output] [\-i input] [\-th] -.fi -.SH DESCRIPTION -.B marked -is a full-featured javascript markdown parser, built for speed. It also includes -multiple GFM features. -.SH OPTIONS -.TP -.BI \-o,\ \-\-output\ [output] -Specify file output. If none is specified, write to stdout. -.TP -.BI \-i,\ \-\-input\ [input] -Specify file input, otherwise use last argument as input file. If no input file -is specified, read from stdin. -.TP -.BI \-t,\ \-\-tokens -Output a token stream instead of html. -.TP -.BI \-h,\ \-\-help -Display help information. -.SH EXAMPLES -.TP -cat in.md | marked > out.html -.TP -echo "hello *world*" | marked -.TP -marked -o out.html in.md -.TP -marked --output="hello world.html" -i in.md -.SH BUGS -Please report any bugs to https://github.com/chjj/marked. -.SH LICENSE -Copyright (c) 2011-2012, Christopher Jeffrey (MIT License) diff --git a/bin/doc/node_modules/marked/package.json b/bin/doc/node_modules/marked/package.json deleted file mode 100644 index f47a9e125..000000000 --- a/bin/doc/node_modules/marked/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "marked", - "description": "A markdown parser built for speed", - "author": "Christopher Jeffrey", - "version": "0.1.9", - "main": "./lib/marked.js", - "bin": "./bin/marked", - "man": "./man/marked.1", - "preferGlobal": false, - "repository": "git://github.com/chjj/marked.git", - "homepage": "https://github.com/chjj/marked", - "bugs": "http://github.com/chjj/marked/issues", - "keywords": [ "markdown", "markup", "html" ], - "tags": [ "markdown", "markup", "html" ] -} diff --git a/bin/doc/package-lock.json b/bin/doc/package-lock.json new file mode 100644 index 000000000..eb5526135 --- /dev/null +++ b/bin/doc/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "node-doc-generator", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "marked": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" + } + } +} diff --git a/bin/doc/package.json b/bin/doc/package.json index 5aba79e05..1a29f1b1c 100644 --- a/bin/doc/package.json +++ b/bin/doc/package.json @@ -7,7 +7,7 @@ "node": ">=0.6.10" }, "dependencies": { - "marked": ">=0.3.6" + "marked": "0.8.2" }, "devDependencies": {}, "optionalDependencies": {}, diff --git a/bin/fastRun.sh b/bin/fastRun.sh new file mode 100755 index 000000000..e00bb8c72 --- /dev/null +++ b/bin/fastRun.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Run Etherpad directly, assuming all the dependencies are already installed. +# +# Useful for developers, or users that know what they are doing. If you just +# upgraded Etherpad version, installed a new dependency, or are simply unsure +# of what to do, please execute bin/installDeps.sh once before running this +# script. + +set -eu + +# source: https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128 +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" + +echo "Running directly, without checking/installing dependencies" + +# move to the base Etherpad directory. This will be necessary until Etherpad +# learns to run from arbitrary CWDs. +cd "${DIR}/.." + +# run Etherpad main class +node "${DIR}/../node_modules/ep_etherpad-lite/node/server.js" "${@}" diff --git a/bin/installDeps.sh b/bin/installDeps.sh index 49c996f46..50310d9a1 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -1,8 +1,8 @@ #!/bin/sh # minimum required node version -REQUIRED_NODE_MAJOR=8 -REQUIRED_NODE_MINOR=9 +REQUIRED_NODE_MAJOR=10 +REQUIRED_NODE_MINOR=13 # minimum required npm version REQUIRED_NPM_MAJOR=5 @@ -49,7 +49,7 @@ require_minimal_version() { } #Move to the folder where ep-lite is installed -cd `dirname $0` +cd $(dirname $0) #Was this script started in the bin folder? if yes move out if [ -d "../bin" ]; then diff --git a/bin/run.sh b/bin/run.sh index e9ea62440..74fa56d68 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -1,7 +1,7 @@ #!/bin/sh #Move to the folder where ep-lite is installed -cd `dirname $0` +cd $(dirname $0) #Was this script started in the bin folder? if yes move out if [ -d "../bin" ]; then @@ -34,6 +34,6 @@ bin/installDeps.sh "$@" || exit 1 #Move to the node folder and start echo "Started Etherpad..." -SCRIPTPATH=`pwd -P` +SCRIPTPATH=$(pwd -P) exec node "$SCRIPTPATH/node_modules/ep_etherpad-lite/node/server.js" "$@" diff --git a/bin/safeRun.sh b/bin/safeRun.sh index 484c325ea..99a72bcc0 100755 --- a/bin/safeRun.sh +++ b/bin/safeRun.sh @@ -19,7 +19,7 @@ LAST_EMAIL_SEND=0 LOG="$1" #Move to the folder where ep-lite is installed -cd `dirname $0` +cd $(dirname $0) #Was this script started in the bin folder? if yes move out if [ -d "../bin" ]; then diff --git a/bin/updatePlugins.sh b/bin/updatePlugins.sh index 63c447edf..ce7a46f6b 100755 --- a/bin/updatePlugins.sh +++ b/bin/updatePlugins.sh @@ -1,7 +1,7 @@ #!/bin/sh #Move to the folder where ep-lite is installed -cd `dirname $0` +cd $(dirname $0) #Was this script started in the bin folder? if yes move out if [ -d "../bin" ]; then @@ -9,7 +9,7 @@ if [ -d "../bin" ]; then fi # npm outdated --depth=0 | grep -v "^Package" | awk '{print $1}' | xargs npm install $1 --save-dev -OUTDATED=`npm outdated --depth=0 | grep -v "^Package" | awk '{print $1}'` +OUTDATED=$(npm outdated --depth=0 | grep -v "^Package" | awk '{print $1}') # echo $OUTDATED if test -n "$OUTDATED"; then echo "Plugins require update, doing this now..." diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md old mode 100644 new mode 100755 index f30578d7e..799fd5ce5 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -193,6 +193,16 @@ Called from: src/static/js/timeslider.js There doesn't appear to be any example available of this particular hook being used, but it gets fired after the timeslider is all set up. +## goToRevisionEvent +Called from: src/static/js/broadcast.js + +Things in context: + +1. rev - The newRevision + +This hook gets fired both on timeslider load (as timeslider shows a new revision) and when the new revision is showed to a user. +There doesn't appear to be any example available of this particular hook being used. + ## userJoinOrUpdate Called from: src/static/js/pad_userlist.js @@ -209,10 +219,11 @@ Things in context: 1. authorName - The user that wrote this message 2. author - The authorID of the user that wrote the message -2. text - the message text -3. sticky (boolean) - if you want the gritter notification bubble to fade out on its own or just sit there -3. timestamp - the timestamp of the chat message -4. timeStr - the timestamp as a formatted string +3. text - the message text +4. sticky (boolean) - if you want the gritter notification bubble to fade out on its own or just sit there +5. timestamp - the timestamp of the chat message +6. timeStr - the timestamp as a formatted string +7. duration - for how long in milliseconds should the gritter notification appear (0 to disable) This hook is called on the client side whenever a chat message is received from the server. It can be used to create different notifications for chat messages. diff --git a/doc/api/http_api.md b/doc/api/http_api.md index 2105e4fde..5be9cb3a4 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -11,6 +11,10 @@ The API is designed in a way, so you can reuse your existing user system with th Take a look at [HTTP API client libraries](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) to check if a library in your favorite programming language is available. +### OpenAPI + +OpenAPI (formerly swagger) definitions are exposed under `/api/openapi.json` (latest) and `/api/{version}/openapi.json`. You can use official tools like [Swagger Editor](https://editor.swagger.io/) to view and explore them. + ## Examples ### Example 1 @@ -61,7 +65,7 @@ Portal submits content into new blog post ## Usage ### API version -The latest version is `1.2.13` +The latest version is `1.2.14` The current version can be queried via /api. @@ -630,3 +634,14 @@ lists all pads on this epl instance *Example returns:* * `{code: 0, message:"ok", data: {padIDs: ["testPad", "thePadsOfTheOthers"]}}` + +### Global + +#### getStats() + * API >= 1.2.14 + +get stats of the etherpad instance + +*Example returns* + * `{"code":0,"message":"ok","data":{"totalPads":3,"totalSessions": 2,"totalActivePads": 1}}` + diff --git a/doc/cookies.md b/doc/cookies.md new file mode 100644 index 000000000..541849596 --- /dev/null +++ b/doc/cookies.md @@ -0,0 +1,18 @@ +# Cookies + +Cookies used by Etherpad. + +| Name | Sample value | Domain | Path | Expires/max-age | Http-only| Secure | Usage description | +|-----------------|------------------------------------|-------------|------|-----------------|----------|--------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|express_sid | s%3A7yCNjRmTW8ylGQ53I2IhOwYF9... | example.org | / | Session | true | true | Session ID of the [Express web framework](https://expressjs.com). When Etherpad is behind a reverse proxy, and an administrator wants to use session stickiness, he may use this cookie. If you are behind a reverse proxy, please remember to set `trustProxy: true` in `settings.json`. Set in [webaccess.js#L131](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/hooks/express/webaccess.js#L131). | +|language | en | example.org | / | Session | false | true | The language of the UI (e.g.: `en-GB`, `it`). Set in [pad_editor.js#L111](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_editor.js#L111). | +|prefs / prefsHttp| %7B%22epThemesExtTheme%22... | example.org | /p | year 3000 | false | true | Client-side preferences (e.g.: font family, chat always visible, show authorship colors, ...). Set in [pad_cookie.js#L49](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_cookie.js#L49). `prefs` is used if Etherpad is accessed over HTTPS, `prefsHttp` if accessed over HTTP. For more info see https://github.com/ether/etherpad-lite/issues/3179. | +|token | t.tFzkihhhBf4xKEpCK3PU | example.org | / | 60 days | false | true | A random token representing the author, of the form `t.randomstring_of_lenght_20`. The random string is generated by the client, at ([pad.js#L55-L66](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L55-L66)). This cookie is always set by the client (at [pad.js#L153-L158](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L153-L158)) without any solicitation from the server. It is used for all the pads accessed via the web UI (not used for the HTTP API). On the server side, its value is accessed at [SecurityManager.js#L33](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/db/SecurityManager.js#L33).| + +For more info, visit the related discussion at https://github.com/ether/etherpad-lite/issues/3563. + +Etherpad HTTP API clients may make use (if they choose so) to send another cookie: + +| Name | Sample value | Domain | Usage description | +|-----------------|------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|sessionID | s.1c70968b333b25476a2c7bdd0e0bed17 | example.org | Sessions can be created between a group and an author. This allows an author to access more than one group. The sessionID will be set as a cookie to the client and is valid until a certain date. The session cookie can also contain multiple comma-separated sessionIDs, allowing a user to edit pads in different groups at the same time. More info - https://github.com/ether/etherpad-lite/blob/develop/doc/api/http_api.md#session | diff --git a/doc/docker.md b/doc/docker.md index e77cde409..9a991a602 100644 --- a/doc/docker.md +++ b/doc/docker.md @@ -16,6 +16,7 @@ docker pull etherpad/etherpad:1.8.0 If you want to use a personalized settings file, **you will have to rebuild your image**. All of the following instructions are as a member of the `docker` group. +By default, the Etherpad Docker image is built and run in `production` mode: no development dependencies are installed, and asset bundling speeds up page load time. ### Rebuilding with custom settings Edit `
heading of window.\n\nFollowed by the child heading {{msg-etherpadlite|Pad.importExport.import}}.",
"pad.importExport.import": "Used as HTML
heading.\n\nPreceded by the parent heading {{msg-etherpadlite|Pad.importExport.import_export}}.",
@@ -113,7 +111,7 @@
"pad.userlist.guest": "Preceded by the link text which is labeled {{msg-etherpadlite|Pad.userlist.approve}}.\n{{Identical|Guest}}",
"pad.userlist.deny": "Used as link text.\n\nFollowed by the link which is labeled {{msg-etherpadlite|Pad.userlist.approve}}.",
"pad.userlist.approve": "Used as link text.\n\nPreceded by the link which is labeled {{msg-etherpadlite|Pad.userlist.deny}}.\n\nFollowed by the message {{msg-etherpadlite|Pad.userlist.guest}}.\n{{Identical|Approve}}",
- "pad.editbar.clearcolors": "Used as confirmation message (JavaScript confirm()
function).\n\nThis message means \"Are you sure you want to clear authorship colors on entire document?\".",
+ "pad.editbar.clearcolors": "Used as confirmation message (JavaScript confirm()
function).\n\nThis message means \"Are you sure you want to clear authorship colors on entire document? This cannot be undone\".",
"pad.impexp.importbutton": "Used as label for the Submit button.",
"pad.impexp.importing": "Used to indicate that the file is being imported.\n{{Identical|Importing}}",
"pad.impexp.confirmimport": "Used as confirmation message (JavaScript confirm()
function).",
diff --git a/src/locales/ro.json b/src/locales/ro.json
index a73bfafc3..91aecd157 100644
--- a/src/locales/ro.json
+++ b/src/locales/ro.json
@@ -35,7 +35,6 @@
"pad.settings.colorcheck": "Culorile autorilor",
"pad.settings.linenocheck": "Numere de linie",
"pad.settings.fontType": "Tipul fontului:",
- "pad.settings.globalView": "Vedere generală",
"pad.settings.language": "Limbă:",
"pad.importExport.import_export": "Import/Export",
"pad.importExport.import": "Încarcă orice fișier text sau document",
diff --git a/src/locales/ru.json b/src/locales/ru.json
index c7b425cbb..49d0bcee9 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -3,14 +3,14 @@
"authors": [
"Amire80",
"DCamer",
+ "Diralik",
"Eleferen",
- "Okras",
- "Volkov",
- "Nzeemin",
"Facenapalm",
- "Patrick Star",
"Movses",
- "Diralik"
+ "Nzeemin",
+ "Okras",
+ "Patrick Star",
+ "Volkov"
]
},
"index.newPad": "Создать",
@@ -48,8 +48,6 @@
"pad.settings.rtlcheck": "Читать содержимое справа налево?",
"pad.settings.fontType": "Тип шрифта:",
"pad.settings.fontType.normal": "Обычный",
- "pad.settings.fontType.monospaced": "Моноширинный",
- "pad.settings.globalView": "Общий вид",
"pad.settings.language": "Язык:",
"pad.importExport.import_export": "Импорт/экспорт",
"pad.importExport.import": "Загрузить любой текстовый файл или документ",
diff --git a/src/locales/sco.json b/src/locales/sco.json
index 274a85b48..eaad6147f 100644
--- a/src/locales/sco.json
+++ b/src/locales/sco.json
@@ -1,8 +1,8 @@
{
"@metadata": {
"authors": [
- "John Reid",
- "AmaryllisGardener"
+ "AmaryllisGardener",
+ "John Reid"
]
},
"index.newPad": "New Pad",
@@ -40,8 +40,6 @@
"pad.settings.rtlcheck": "Read content fae richt til cair?",
"pad.settings.fontType": "Font type:",
"pad.settings.fontType.normal": "Normal",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "The Global Luik",
"pad.settings.language": "Leid:",
"pad.importExport.import_export": "Import/Export",
"pad.importExport.import": "Upload oni tex file or document",
diff --git a/src/locales/sd.json b/src/locales/sd.json
index 87ca2cb18..3b159f01f 100644
--- a/src/locales/sd.json
+++ b/src/locales/sd.json
@@ -31,7 +31,6 @@
"pad.settings.linenocheck": "سٽ جا انگ",
"pad.settings.rtlcheck": "مواد ساڄي کان کاٻي طرف پڙهندئو؟",
"pad.settings.fontType": "اکرن جو قسم:",
- "pad.settings.globalView": "عالمي نظارو",
"pad.settings.language": "ٻولي:",
"pad.importExport.import_export": "برآمد/درآمد",
"pad.importExport.import": "ڪو به متن وارو فائيل يا دستاويز چاڙهيو",
diff --git a/src/locales/sh.json b/src/locales/sh.json
index f3dd2bbac..db60b3108 100644
--- a/src/locales/sh.json
+++ b/src/locales/sh.json
@@ -39,7 +39,6 @@
"pad.settings.linenocheck": "Brojevi redova",
"pad.settings.rtlcheck": "Da prikažem sadržaj zdesna ulijevo?",
"pad.settings.fontType": "Tip fonta:",
- "pad.settings.globalView": "Globalni prikaz",
"pad.settings.language": "Jezik:",
"pad.importExport.import_export": "Uvoz/Izvoz",
"pad.importExport.import": "Otpremanje bilo koje tekstualne datoteke ili dokumenta",
diff --git a/src/locales/shn.json b/src/locales/shn.json
index c5c817519..67cf557b4 100644
--- a/src/locales/shn.json
+++ b/src/locales/shn.json
@@ -1,9 +1,9 @@
{
"@metadata": {
"authors": [
- "Saosukham",
"Ninjastrikers",
- "Saimawnkham"
+ "Saimawnkham",
+ "Saosukham"
]
},
"index.newPad": "ၽႅတ်ႉမႂ်ႇ",
@@ -40,7 +40,6 @@
"pad.settings.linenocheck": "တူဝ်လိၵ်ႈႁေႈႁၢႆး",
"pad.settings.rtlcheck": "လူၵႂၢမ်းၼၢမ်း တႄႇၶႂႃတေႃႇသၢႆႉ",
"pad.settings.fontType": "ၾွၼ်ႉတူဝ်လိၵ်ႈ",
- "pad.settings.globalView": "ဝိဝ်းတင်းလူၵ်ႈ",
"pad.settings.language": "ၽႃႇသႃႇၵႂၢမ်း:",
"pad.importExport.import_export": "သူင်ႇၶဝ်/သူင်ႇဢွၵ်ႇ",
"pad.importExport.import": "လူတ်ႇၶိုၼ်ႈ ၾၢႆႇလိၵ်ႈၵမ်ႈၽွင်ႈ ဢမ်ႇၼၼ် ပွင်ႈလိၵ်ႈ",
diff --git a/src/locales/sk.json b/src/locales/sk.json
index e199945a8..67ac5ff31 100644
--- a/src/locales/sk.json
+++ b/src/locales/sk.json
@@ -1,11 +1,11 @@
{
"@metadata": {
"authors": [
- "Teslaton",
"Kusavica",
- "Rudko",
+ "Lexected",
"Mark",
- "Lexected"
+ "Rudko",
+ "Teslaton"
]
},
"index.newPad": "Nový Pad",
@@ -43,8 +43,6 @@
"pad.settings.rtlcheck": "Čítať obsah sprava doľava?",
"pad.settings.fontType": "Typ písma:",
"pad.settings.fontType.normal": "Normálne",
- "pad.settings.fontType.monospaced": "Strojové",
- "pad.settings.globalView": "Globálny pohľad",
"pad.settings.language": "Jazyk:",
"pad.importExport.import_export": "Import/Export",
"pad.importExport.import": "Nahrať ľubovoľný textový súbor alebo dokument",
diff --git a/src/locales/skr-arab.json b/src/locales/skr-arab.json
index 2130b5431..9941b6926 100644
--- a/src/locales/skr-arab.json
+++ b/src/locales/skr-arab.json
@@ -16,7 +16,6 @@
"pad.wrongPassword": "تہاݙ پاسورڈ غلط ہے",
"pad.settings.padSettings": "پیڈ ترتیباں",
"pad.settings.fontType": "فونٹ قسم:",
- "pad.settings.globalView": "عالمی منظر",
"pad.settings.language": "زبان:",
"pad.importExport.importSuccessful": "کامیاب!",
"pad.importExport.exportetherpad": "ایتھرپیڈ",
diff --git a/src/locales/sl.json b/src/locales/sl.json
index 37f716cd2..7eb021e07 100644
--- a/src/locales/sl.json
+++ b/src/locales/sl.json
@@ -2,9 +2,9 @@
"@metadata": {
"authors": [
"Dbc334",
+ "HairyFotr",
"Mateju",
"Skalcaa",
- "HairyFotr",
"Upwinxp"
]
},
@@ -43,8 +43,6 @@
"pad.settings.rtlcheck": "Ali naj se vsebina prebira od desne proti levi?",
"pad.settings.fontType": "Vrsta pisave:",
"pad.settings.fontType.normal": "Običajno",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "Splošni pogled",
"pad.settings.language": "Jezik:",
"pad.importExport.import_export": "Uvoz/Izvoz",
"pad.importExport.import": "Naloži katerokoli besedilno datoteko ali dokument.",
diff --git a/src/locales/sq.json b/src/locales/sq.json
index ad2dde8d8..0eb2c2bf4 100644
--- a/src/locales/sq.json
+++ b/src/locales/sq.json
@@ -41,8 +41,6 @@
"pad.settings.rtlcheck": "Të lexohet lënda nga e djathta në të majtë?",
"pad.settings.fontType": "Lloj shkronjash:",
"pad.settings.fontType.normal": "Normale",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "Pamje e Përgjithshme",
"pad.settings.language": "Gjuhë:",
"pad.importExport.import_export": "Import/Eksport",
"pad.importExport.import": "Ngarkoni cilëndo kartelë teksti ose dokument",
diff --git a/src/locales/sr-ec.json b/src/locales/sr-ec.json
index 15c553f55..109668def 100644
--- a/src/locales/sr-ec.json
+++ b/src/locales/sr-ec.json
@@ -1,13 +1,13 @@
{
"@metadata": {
"authors": [
- "Aktron",
- "Milicevic01",
- "Милан Јелисавчић",
- "Srdjan m",
- "Obsuser",
"Acamicamacaraca",
- "BadDog"
+ "Aktron",
+ "BadDog",
+ "Milicevic01",
+ "Obsuser",
+ "Srdjan m",
+ "Милан Јелисавчић"
]
},
"index.newPad": "Нови Пад",
@@ -45,8 +45,6 @@
"pad.settings.rtlcheck": "Читај садржај с десна на лево?",
"pad.settings.fontType": "Врста фонта:",
"pad.settings.fontType.normal": "Нормално",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "Глобални приказ",
"pad.settings.language": "Језик:",
"pad.importExport.import_export": "Увоз/извоз",
"pad.importExport.import": "Отпремите било коју текстуалну датотеку или документ",
diff --git a/src/locales/sr-el.json b/src/locales/sr-el.json
index 78771f07f..3c4bff17a 100644
--- a/src/locales/sr-el.json
+++ b/src/locales/sr-el.json
@@ -1,5 +1,7 @@
{
- "@metadata": [],
+ "@metadata": {
+ "authors": []
+ },
"index.newPad": "Novi Pad",
"index.createOpenPad": "ili napravite/otvorite pad sledećeg naziva:",
"pad.toolbar.bold.title": "Podebljano (Ctrl+B)",
@@ -35,8 +37,6 @@
"pad.settings.rtlcheck": "Čitaj sadržaj s desna na levo?",
"pad.settings.fontType": "Vrsta fonta:",
"pad.settings.fontType.normal": "Normalno",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "Globalni prikaz",
"pad.settings.language": "Jezik:",
"pad.importExport.import_export": "Uvoz/izvoz",
"pad.importExport.import": "Otpremite bilo koju tekstualnu datoteku ili dokument",
diff --git a/src/locales/sv.json b/src/locales/sv.json
index 7b03c12b2..69b086a16 100644
--- a/src/locales/sv.json
+++ b/src/locales/sv.json
@@ -1,9 +1,9 @@
{
"@metadata": {
"authors": [
+ "Jopparn",
"Lokal Profil",
- "WikiPhoenix",
- "Jopparn"
+ "WikiPhoenix"
]
},
"index.newPad": "Nytt block",
@@ -41,8 +41,6 @@
"pad.settings.rtlcheck": "Vill du läsa innehållet från höger till vänster?",
"pad.settings.fontType": "Typsnitt:",
"pad.settings.fontType.normal": "Normal",
- "pad.settings.fontType.monospaced": "Fast breddsteg",
- "pad.settings.globalView": "Global vy",
"pad.settings.language": "Språk:",
"pad.importExport.import_export": "Importera/Exportera",
"pad.importExport.import": "Ladda upp textfiler eller dokument",
diff --git a/src/locales/ta.json b/src/locales/ta.json
index c0a7cdc08..6f08c3bc6 100644
--- a/src/locales/ta.json
+++ b/src/locales/ta.json
@@ -29,7 +29,6 @@
"pad.settings.colorcheck": "ஆசிரியர் நிறங்கள்",
"pad.settings.linenocheck": "வரி எண்கள்",
"pad.settings.fontType": "எழுத்துரு வகை:",
- "pad.settings.globalView": "உலக பார்வை",
"pad.settings.language": "மொழி:",
"pad.importExport.import_export": "இறக்குமதி/ஏற்றுமதி",
"pad.importExport.importSuccessful": "வெற்றி!",
diff --git a/src/locales/te.json b/src/locales/te.json
index 13af39709..b34b93f2f 100644
--- a/src/locales/te.json
+++ b/src/locales/te.json
@@ -1,12 +1,12 @@
{
"@metadata": {
"authors": [
- "JVRKPRASAD",
- "Malkum",
- "Veeven",
"Chaduvari",
+ "JVRKPRASAD",
+ "Kiranmayee",
+ "Malkum",
"Ravichandra",
- "Kiranmayee"
+ "Veeven"
]
},
"index.newPad": "కొత్త పలక",
@@ -38,8 +38,6 @@
"pad.settings.linenocheck": "వరుస సంఖ్యలు",
"pad.settings.fontType": "అక్షరశైలి రకం:",
"pad.settings.fontType.normal": "సాధారణ",
- "pad.settings.fontType.monospaced": "మోనోస్పేస్",
- "pad.settings.globalView": "బయటకి దర్శనం",
"pad.settings.language": "భాష",
"pad.importExport.import_export": "దిగుమతి/ఎగుమతి",
"pad.importExport.import": "పాఠము దస్త్రము లేదా పత్రమును దిగుమతి చేయుము",
diff --git a/src/locales/th.json b/src/locales/th.json
index d5c24374b..60030ec33 100644
--- a/src/locales/th.json
+++ b/src/locales/th.json
@@ -38,7 +38,6 @@
"pad.settings.linenocheck": "เลขบรรทัด",
"pad.settings.rtlcheck": "อ่านเนื้อหาจากขวาไปซ้ายหรือไม่?",
"pad.settings.fontType": "ชนิดแบบอักษร:",
- "pad.settings.globalView": "มุมมองสากล",
"pad.settings.language": "ภาษา:",
"pad.importExport.import_export": "นำเข้า/ส่งออก",
"pad.importExport.import": "อัปโหลดไฟล์ข้อความหรือเอกสารใดๆ",
diff --git a/src/locales/tr.json b/src/locales/tr.json
index 1358bdeda..b27aa3d28 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -1,31 +1,32 @@
{
"@metadata": {
"authors": [
+ "BaRaN6161 TURK",
"Emperyan",
"Erdemaslancan",
+ "Grkn gll",
+ "Hedda",
"Joseph",
+ "McAang",
"Meelo",
"Trockya",
- "McAang",
- "Vito Genovese",
- "Hedda",
- "Grkn gll"
+ "Vito Genovese"
]
},
"index.newPad": "Yeni Bloknot",
- "index.createOpenPad": "ya da şu isimle bir Bloknot oluştur/aç:",
- "pad.toolbar.bold.title": "Kalın (Ctrl-B)",
- "pad.toolbar.italic.title": "Eğik (Ctrl-I)",
- "pad.toolbar.underline.title": "Altı Çizili (Ctrl-U)",
+ "index.createOpenPad": "veya şu adla bir Bloknot oluşturun/açın:",
+ "pad.toolbar.bold.title": "Kalın (Ctrl+B)",
+ "pad.toolbar.italic.title": "Eğik (Ctrl+I)",
+ "pad.toolbar.underline.title": "Altı Çizili (Ctrl+U)",
"pad.toolbar.strikethrough.title": "Üstü Çizili (Ctrl+5)",
"pad.toolbar.ol.title": "Sıralı liste (Ctrl+Shift+N)",
"pad.toolbar.ul.title": "Sırasız Liste (Ctrl+Shift+L)",
"pad.toolbar.indent.title": "Girintiyi arttır (TAB)",
"pad.toolbar.unindent.title": "Girintiyi azalt (Shift+TAB)",
- "pad.toolbar.undo.title": "Geri Al (Ctrl-Z)",
+ "pad.toolbar.undo.title": "Geri Al (Ctrl+Z)",
"pad.toolbar.redo.title": "Yinele (Ctrl+Y)",
"pad.toolbar.clearAuthorship.title": "Yazarlık Renklerini Temizle (Ctrl+Shift+C)",
- "pad.toolbar.import_export.title": "Farklı dosya biçimlerini içeri/dışarı aktar",
+ "pad.toolbar.import_export.title": "Farklı dosya biçimlerini içe/dışa aktar",
"pad.toolbar.timeslider.title": "Zaman Çizelgesi",
"pad.toolbar.savedRevision.title": "Düzeltmeyi Kaydet",
"pad.toolbar.settings.title": "Ayarlar",
@@ -47,13 +48,11 @@
"pad.settings.rtlcheck": "İçerik sağdan sola doğru okunsun mu?",
"pad.settings.fontType": "Yazı tipi:",
"pad.settings.fontType.normal": "Olağan",
- "pad.settings.fontType.monospaced": "Tek aralıklı",
- "pad.settings.globalView": "Genel Görünüm",
"pad.settings.language": "Dil:",
- "pad.importExport.import_export": "İçeri aktar/Dışarı aktar",
+ "pad.importExport.import_export": "İçe/Dışa aktar",
"pad.importExport.import": "Herhangi bir metin dosyası ya da belgesi yükle",
"pad.importExport.importSuccessful": "Başarılı!",
- "pad.importExport.export": "Mevcut bloknotu şu olarak dışa aktar:",
+ "pad.importExport.export": "Mevcut bloknotu şuraya dışa aktar:",
"pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Düz metin",
@@ -79,16 +78,16 @@
"pad.modals.slowcommit.explanation": "Sunucu yanıt vermiyor.",
"pad.modals.slowcommit.cause": "Bu hata ağ bağlantısı sebebiyle olabilir.",
"pad.modals.badChangeset.explanation": "Yaptığınız bir düzenleme eşitleme sunucusu tarafından kullanışsız/kural dışı olarak sınıflandırıldı.",
- "pad.modals.badChangeset.cause": "Bu, yanlış sunucu yapılandırması ya da başka bir beklenmedik davranışlar sonucunda olmuş olabilir. Bu size bir hataymış gibi geliyorsa lütfen servis yöneticisiyle iletişime geçin. Düzenlemeye devam etmek için yeniden bağlanmayı deneyin.",
+ "pad.modals.badChangeset.cause": "Bunun nedeni, yanlış bir sunucu yapılandırması veya beklenmeyen başka bir davranış olabilir. Bunun bir hata olduğunu düşünüyorsanız lütfen servis yöneticisine başvurun. Düzenlemeye devam etmek için yeniden bağlanmayı deneyin.",
"pad.modals.corruptPad.explanation": "Erişmeye çalıştığınız bloknot bozuk.",
- "pad.modals.corruptPad.cause": "Bu, yanlış sunucu yapılandırması ya da başka bir beklenmedik davranışlardan kaynaklanabilir. Lütfen servis yöneticisiyle iletişime geçin.",
+ "pad.modals.corruptPad.cause": "Bunun nedeni yanlış bir sunucu yapılandırması veya beklenmeyen başka bir davranış olabilir. Lütfen servis yöneticisine başvurun.",
"pad.modals.deleted": "Silindi.",
"pad.modals.deleted.explanation": "Bu bloknot kaldırılmış.",
"pad.modals.disconnected": "Bağlantınız koptu.",
"pad.modals.disconnected.explanation": "Sunucu bağlantısı kaybedildi",
"pad.modals.disconnected.cause": "Sunucu kullanılamıyor olabilir. Bunun devam etmesi durumunda servis yöneticisine bildirin.",
"pad.share": "Bu bloknotu paylaş",
- "pad.share.readonly": "Salt okunur",
+ "pad.share.readonly": "Sadece oku",
"pad.share.link": "Bağlantı",
"pad.share.emebdcode": "URL'yi göm",
"pad.chat": "Sohbet",
@@ -128,12 +127,12 @@
"pad.userlist.guest": "Misafir",
"pad.userlist.deny": "Reddet",
"pad.userlist.approve": "Onayla",
- "pad.editbar.clearcolors": "Bütün belgedeki yazarlık renkleri silinsin mi?",
+ "pad.editbar.clearcolors": "Bütün belgedeki yazarlık renkleri silinsin mi? Bu işlem geri alınamaz",
"pad.impexp.importbutton": "Şimdi İçe Aktar",
"pad.impexp.importing": "İçe aktarıyor...",
"pad.impexp.confirmimport": "Bir dosya içe aktarılırken bloknotun mevcut metninin üzerine yazdırılır. Devam etmek istediğinizden emin misiniz?",
"pad.impexp.convertFailed": "Bu dosyayı içe aktarmak mümkün değil. Lütfen farklı bir belge biçimi kullanın ya da elle kopyala yapıştır yapın",
- "pad.impexp.padHasData": "Biz bu dosyayı içe aktaramadık çünkü bu Bloknot zaten değiştirilmiş, lütfen yeni bir bloknot içe aktarın.",
+ "pad.impexp.padHasData": "Bu Pad'in zaten değişiklikleri olduğu için bu dosyayı içe aktaramadık, lütfen yeni bir bloknota aktarın",
"pad.impexp.uploadFailed": "Yükleme başarısız, lütfen tekrar deneyin",
"pad.impexp.importfailed": "İçe aktarım başarısız oldu",
"pad.impexp.copypaste": "Lütfen kopyala yapıştır yapın",
diff --git a/src/locales/uk.json b/src/locales/uk.json
index 9a6ab8e71..968920d24 100644
--- a/src/locales/uk.json
+++ b/src/locales/uk.json
@@ -3,14 +3,14 @@
"authors": [
"Andriykopanytsia",
"Base",
+ "Bunyk",
+ "Lxlalexlxl",
+ "Movses",
"Olvin",
+ "Piramidion",
"Steve.rusyn",
"SteveR",
- "Lxlalexlxl",
- "Григорій Пугач",
- "Bunyk",
- "Piramidion",
- "Movses"
+ "Григорій Пугач"
]
},
"index.newPad": "Створити",
@@ -48,8 +48,6 @@
"pad.settings.rtlcheck": "Читати вміст з права на ліво?",
"pad.settings.fontType": "Тип шрифту:",
"pad.settings.fontType.normal": "Звичайний",
- "pad.settings.fontType.monospaced": "Моноширинний",
- "pad.settings.globalView": "Загальний вигляд",
"pad.settings.language": "Мова:",
"pad.importExport.import_export": "Імпорт/Експорт",
"pad.importExport.import": "Завантажити будь-який текстовий файл або документ",
@@ -129,7 +127,7 @@
"pad.userlist.guest": "Гість",
"pad.userlist.deny": "Заборонити",
"pad.userlist.approve": "Підтвердити",
- "pad.editbar.clearcolors": "Очистити кольори у всьому документі?",
+ "pad.editbar.clearcolors": "Очистити кольори у всьому документі? Це не можна буде відкотити",
"pad.impexp.importbutton": "Імпортувати зараз",
"pad.impexp.importing": "Імпорт...",
"pad.impexp.confirmimport": "Імпортування файлу перезапише поточний текст документа. Ви дійсно хочете продовжити?",
diff --git a/src/locales/vec.json b/src/locales/vec.json
new file mode 100644
index 000000000..375390d7d
--- /dev/null
+++ b/src/locales/vec.json
@@ -0,0 +1,42 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fierodelveneto"
+ ]
+ },
+ "index.newPad": "Novo Pad",
+ "index.createOpenPad": "O creare o verxare on Pad co'l nome:",
+ "pad.toolbar.bold.title": "Groseto (Ctrl-B)",
+ "pad.toolbar.italic.title": "Corsivo (Ctrl-I)",
+ "pad.toolbar.underline.title": "Sotolineà (Ctrl-U)",
+ "pad.toolbar.strikethrough.title": "Barà (Ctrl+5)",
+ "pad.toolbar.ol.title": "Ełenco numarà (Ctrl+Shift+N)",
+ "pad.toolbar.ul.title": "Ełenco pontà (Ctrl+Shift+L)",
+ "pad.toolbar.indent.title": "Indentasion (TAB)",
+ "pad.toolbar.unindent.title": "Scursa indentasion (Shift+TAB)",
+ "pad.toolbar.undo.title": "Dasa stare (Ctrl-Z)",
+ "pad.toolbar.redo.title": "Ripeti (Ctrl-Y)",
+ "pad.toolbar.clearAuthorship.title": "Cava i cołori che i indega i autori (Ctrl+Shift+C)",
+ "pad.toolbar.import_export.title": "Inporta/esporta da/a fidarenti formati de file",
+ "pad.toolbar.timeslider.title": "Prexentasion storego",
+ "pad.toolbar.savedRevision.title": "Version salvada",
+ "pad.toolbar.settings.title": "Inpostasion",
+ "pad.toolbar.embed.title": "Spartisi e incorpora sto Pad",
+ "pad.toolbar.showusers.title": "Varda i utenti so sto Pad",
+ "pad.colorpicker.save": "Salva",
+ "pad.colorpicker.cancel": "Descançełare",
+ "pad.loading": "Drio cargar...",
+ "pad.noCookie": "El cookie no el xé sta catà. Cosenti i cookie n'tel to navegadore web.",
+ "timeslider.month.january": "Xenaro",
+ "timeslider.month.march": "Marso",
+ "timeslider.month.april": "Apriłe",
+ "timeslider.month.may": "Majo",
+ "timeslider.month.june": "Xugno",
+ "timeslider.month.july": "Lujo",
+ "timeslider.month.august": "Agosto",
+ "timeslider.month.september": "Setenbre",
+ "timeslider.month.october": "Otobre",
+ "timeslider.month.november": "Novenbre",
+ "timeslider.month.december": "Disenbre",
+ "timeslider.unnamedauthors": "{{num}} {[plural(num) one: autore, other: autori ]} sensa nome"
+}
diff --git a/src/locales/vi.json b/src/locales/vi.json
index 57f58e0cb..42bc84806 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -2,9 +2,9 @@
"@metadata": {
"authors": [
"Baonguyen21022003",
+ "Max20091",
"Minh Nguyen",
- "Tuankiet65",
- "Max20091"
+ "Tuankiet65"
]
},
"index.newPad": "Tạo một Pad mới",
@@ -40,8 +40,6 @@
"pad.settings.rtlcheck": "Đọc nội dung từ phải sang trái?",
"pad.settings.fontType": "Kiểu phông chữ:",
"pad.settings.fontType.normal": "Thường",
- "pad.settings.fontType.monospaced": "Monospace",
- "pad.settings.globalView": "Toàn cầu",
"pad.settings.language": "Ngôn ngữ:",
"pad.importExport.import_export": "Xuất/Nhập",
"pad.importExport.import": "Tải lên bất kỳ tập tin văn bản hoặc tài liệu",
diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json
index 1d456ff76..7c0cd2d70 100644
--- a/src/locales/zh-hans.json
+++ b/src/locales/zh-hans.json
@@ -1,19 +1,19 @@
{
"@metadata": {
"authors": [
+ "94rain",
"Dimension",
"Hydra",
"Hzy980512",
+ "JuneAugust",
"Liuxinyu970226",
"Qiyue2001",
"Shangkuanlc",
"Shizhao",
+ "VulpesVulpes825",
"Yfdyh000",
"乌拉跨氪",
- "燃玉",
- "JuneAugust",
- "94rain",
- "VulpesVulpes825"
+ "燃玉"
]
},
"index.newPad": "新记事本",
@@ -51,8 +51,6 @@
"pad.settings.rtlcheck": "从右到左阅读内容吗?",
"pad.settings.fontType": "字体类型:",
"pad.settings.fontType.normal": "正常",
- "pad.settings.fontType.monospaced": "等宽字体",
- "pad.settings.globalView": "所有人视窗",
"pad.settings.language": "语言:",
"pad.importExport.import_export": "导入/导出",
"pad.importExport.import": "上载任何文本文件或档案",
diff --git a/src/locales/zh-hant.json b/src/locales/zh-hant.json
index 6d43f67d7..438e6475a 100644
--- a/src/locales/zh-hant.json
+++ b/src/locales/zh-hant.json
@@ -2,52 +2,51 @@
"@metadata": {
"authors": [
"Justincheng12345",
+ "Kly",
+ "LNDDYL",
"Liuxinyu970226",
+ "Pan93412",
"Shangkuanlc",
"Shirayuki",
"Simon Shek",
- "LNDDYL",
- "Wehwei",
- "Kly"
+ "Wehwei"
]
},
"index.newPad": "新記事本",
- "index.createOpenPad": "或創建/開啟以下名稱的記事本:",
- "pad.toolbar.bold.title": "粗體(Ctrl-B)",
- "pad.toolbar.italic.title": "斜體(Ctrl-I)",
- "pad.toolbar.underline.title": "底線(Ctrl-U)",
+ "index.createOpenPad": "或建立/開啟以下名稱的記事本:",
+ "pad.toolbar.bold.title": "粗體(Ctrl+B)",
+ "pad.toolbar.italic.title": "斜體(Ctrl+I)",
+ "pad.toolbar.underline.title": "底線(Ctrl+U)",
"pad.toolbar.strikethrough.title": "刪除線(Ctrl+5)",
"pad.toolbar.ol.title": "有序清單(Ctrl+Shift+N)",
"pad.toolbar.ul.title": "無序清單(Ctrl+Shift+L)",
"pad.toolbar.indent.title": "縮排(TAB)",
"pad.toolbar.unindent.title": "凸排(Shift+TAB)",
- "pad.toolbar.undo.title": "撤銷(Ctrl-Z)",
+ "pad.toolbar.undo.title": "復原(Ctrl+Z)",
"pad.toolbar.redo.title": "重做 (Ctrl+Y)",
"pad.toolbar.clearAuthorship.title": "清除協作者顏色區別 (Ctrl+Shift+C)",
"pad.toolbar.import_export.title": "以其他檔案格式匯入/匯出",
"pad.toolbar.timeslider.title": "時間軸",
- "pad.toolbar.savedRevision.title": "儲存修訂",
+ "pad.toolbar.savedRevision.title": "儲存修訂版",
"pad.toolbar.settings.title": "設定",
"pad.toolbar.embed.title": "分享和嵌入此記事本",
"pad.toolbar.showusers.title": "顯示此記事本的使用者",
"pad.colorpicker.save": "儲存",
"pad.colorpicker.cancel": "取消",
"pad.loading": "載入中...",
- "pad.noCookie": "找不到 Cookie。請讓你的瀏覽器允許 Cookie!",
- "pad.passwordRequired": "您需要密碼才能訪問這個記事本",
- "pad.permissionDenied": "你沒有訪問這個記事本的權限",
+ "pad.noCookie": "找不到 Cookie。請允許瀏覽器使用 Cookie!",
+ "pad.passwordRequired": "您需要密碼才能存取這個記事本",
+ "pad.permissionDenied": "你沒有存取這個記事本的權限",
"pad.wrongPassword": "密碼錯誤",
"pad.settings.padSettings": "記事本設定",
"pad.settings.myView": "我的視窗",
- "pad.settings.stickychat": "永遠在屏幕上顯示聊天",
+ "pad.settings.stickychat": "永遠在螢幕上顯示聊天",
"pad.settings.chatandusers": "顯示聊天與使用者",
"pad.settings.colorcheck": "協作者顏色",
"pad.settings.linenocheck": "行號",
"pad.settings.rtlcheck": "從右至左讀取內容?",
- "pad.settings.fontType": "字體類型:",
+ "pad.settings.fontType": "字型類型:",
"pad.settings.fontType.normal": "正常",
- "pad.settings.fontType.monospaced": "等寬",
- "pad.settings.globalView": "所有人的視窗",
"pad.settings.language": "語言:",
"pad.importExport.import_export": "匯入/匯出",
"pad.importExport.import": "上載任何文字檔或文件",
@@ -59,21 +58,21 @@
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF(開放文件格式)",
- "pad.importExport.abiword.innerHTML": "您只可以純文字或 HTML 格式檔匯入。安裝\n AbiWord 取得更多進階的匯入功能。",
+ "pad.importExport.abiword.innerHTML": "您只可以從純文字或 HTML 格式檔匯入。安裝\n AbiWord 以取得更多進階的匯入功能。",
"pad.modals.connected": "已連線。",
- "pad.modals.reconnecting": "重新連接到您的記事本...",
+ "pad.modals.reconnecting": "重新連線到您的記事本…",
"pad.modals.forcereconnect": "強制重新連線",
- "pad.modals.reconnecttimer": "嘗試重新連接在",
+ "pad.modals.reconnecttimer": "下一次重新連線會在",
"pad.modals.cancel": "取消",
"pad.modals.userdup": "在另一個視窗中開啟",
"pad.modals.userdup.explanation": "此記事本似乎在此電腦上的多個瀏覽器視窗中開啟。",
- "pad.modals.userdup.advice": "重新連接到此視窗。",
+ "pad.modals.userdup.advice": "重新連線,並改用這個視窗。",
"pad.modals.unauth": "未授權",
- "pad.modals.unauth.explanation": "您的權限在查看此頁時發生更改。請嘗試重新連接。",
- "pad.modals.looping.explanation": "與同步伺服器間有通信問題。",
- "pad.modals.looping.cause": "也許您是通過不相容的防火牆或代理伺服器連線。",
- "pad.modals.initsocketfail": "無法訪問伺服器。",
- "pad.modals.initsocketfail.explanation": "無法連接到同步伺服器。",
+ "pad.modals.unauth.explanation": "您的權限在變更此頁時變更了。請嘗試重新連線。",
+ "pad.modals.looping.explanation": "與同步伺服器間有通訊問題。",
+ "pad.modals.looping.cause": "也許您是透過不相容的防火牆或代理伺服器連線。",
+ "pad.modals.initsocketfail": "無法存取伺服器。",
+ "pad.modals.initsocketfail.explanation": "無法連線到同步伺服器。",
"pad.modals.initsocketfail.cause": "這可能是因為瀏覽器或網際網路連線問題所造成。",
"pad.modals.slowcommit.explanation": "伺服器沒有回應。",
"pad.modals.slowcommit.cause": "這可能是因為網路連線問題所造成。",
@@ -127,7 +126,7 @@
"pad.userlist.guest": "訪客",
"pad.userlist.deny": "拒絕",
"pad.userlist.approve": "批准",
- "pad.editbar.clearcolors": "清除整個文檔的協作者顏色區別嗎?",
+ "pad.editbar.clearcolors": "清除整個文檔的協作者顏色區別嗎?此操作無法還原",
"pad.impexp.importbutton": "現在匯入",
"pad.impexp.importing": "匯入中...",
"pad.impexp.confirmimport": "匯入的檔案將會覆蓋記事本內目前的文字。您確定要繼續嗎?",
diff --git a/src/node/db/API.js b/src/node/db/API.js
index 998d9acd1..71bd09eca 100644
--- a/src/node/db/API.js
+++ b/src/node/db/API.js
@@ -837,6 +837,34 @@ exports.createDiffHTML = async function(padID, startRev, endRev) {
return { html, authors };
}
+/**********************/
+/** GLOBAL FUNCTIONS **/
+/**********************/
+
+/**
+ getStats() returns an json object with some instance stats
+
+ Example returns:
+
+ {"code":0,"message":"ok","data":{"totalPads":3,"totalSessions": 2,"totalActivePads": 1}}
+ {"code":4,"message":"no or wrong API Key","data":null}
+ */
+
+exports.getStats = async function() {
+ const sessionInfos = padMessageHandler.sessioninfos;
+
+ const sessionKeys = Object.keys(sessionInfos);
+ const activePads = new Set(Object.entries(sessionInfos).map(k => k[1].padId));
+
+ const { padIDs } = await padManager.listAllPads();
+
+ return {
+ totalPads: padIDs.length,
+ totalSessions: sessionKeys.length,
+ totalActivePads: activePads.size,
+ }
+}
+
/******************************/
/** INTERNAL HELPER FUNCTIONS */
/******************************/
diff --git a/src/node/db/DB.js b/src/node/db/DB.js
index 573563665..4150743de 100644
--- a/src/node/db/DB.js
+++ b/src/node/db/DB.js
@@ -45,29 +45,29 @@ exports.init = function() {
console.error("ERROR: Problem while initalizing the database");
console.error(err.stack ? err.stack : err);
process.exit(1);
- } else {
- // everything ok, set up Promise-based methods
- ['get', 'set', 'findKeys', 'getSub', 'setSub', 'remove', 'doShutdown'].forEach(fn => {
- exports[fn] = util.promisify(db[fn].bind(db));
- });
-
- // set up wrappers for get and getSub that can't return "undefined"
- let get = exports.get;
- exports.get = async function(key) {
- let result = await get(key);
- return (result === undefined) ? null : result;
- };
-
- let getSub = exports.getSub;
- exports.getSub = async function(key, sub) {
- let result = await getSub(key, sub);
- return (result === undefined) ? null : result;
- };
-
- // exposed for those callers that need the underlying raw API
- exports.db = db;
- resolve();
}
+
+ // everything ok, set up Promise-based methods
+ ['get', 'set', 'findKeys', 'getSub', 'setSub', 'remove', 'doShutdown'].forEach(fn => {
+ exports[fn] = util.promisify(db[fn].bind(db));
+ });
+
+ // set up wrappers for get and getSub that can't return "undefined"
+ let get = exports.get;
+ exports.get = async function(key) {
+ let result = await get(key);
+ return (result === undefined) ? null : result;
+ };
+
+ let getSub = exports.getSub;
+ exports.getSub = async function(key, sub) {
+ let result = await getSub(key, sub);
+ return (result === undefined) ? null : result;
+ };
+
+ // exposed for those callers that need the underlying raw API
+ exports.db = db;
+ resolve();
});
});
}
diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js
index cf016444c..158bfafb5 100644
--- a/src/node/db/Pad.js
+++ b/src/node/db/Pad.js
@@ -16,6 +16,7 @@ var readOnlyManager = require("./ReadOnlyManager");
var crypto = require("crypto");
var randomString = require("../utils/randomstring");
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
+var promises = require('../utils/promises')
// serialization/deserialization attributes
var attributeBlackList = ["id"];
@@ -482,14 +483,14 @@ Pad.prototype.remove = async function remove() {
db.remove("readonly2pad:" + readonlyID);
// delete all chat messages
- for (let i = 0, n = this.chatHead; i <= n; ++i) {
- db.remove("pad:" + padID + ":chat:" + i);
- }
+ promises.timesLimit(this.chatHead + 1, 500, function (i) {
+ return db.remove("pad:" + padID + ":chat:" + i, null);
+ })
// delete all revisions
- for (let i = 0, n = this.head; i <= n; ++i) {
- db.remove("pad:" + padID + ":revs:" + i);
- }
+ promises.timesLimit(this.head + 1, 500, function (i) {
+ return db.remove("pad:" + padID + ":revs:" + i, null);
+ })
// remove pad from all authors who contributed
this.getAllAuthors().forEach(authorID => {
diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js
index 23164a7a9..89e6a84ab 100644
--- a/src/node/db/PadManager.js
+++ b/src/node/db/PadManager.js
@@ -49,8 +49,8 @@ var globalPads = {
* Updated without db access as new pads are created/old ones removed.
*/
let padList = {
- list: [],
- sorted : false,
+ list: new Set(),
+ cachedList: undefined,
initiated: false,
init: async function() {
let dbData = await db.findKeys("pad:*", "*:*:*");
@@ -59,7 +59,7 @@ let padList = {
this.initiated = true;
for (let val of dbData) {
- this.addPad(val.replace(/pad:/,""), false);
+ this.addPad(val.replace(/^pad:/,""), false);
}
}
@@ -78,29 +78,26 @@ let padList = {
getPads: async function() {
await this.load();
- if (!this.sorted) {
- this.list.sort();
- this.sorted = true;
+ if (!this.cachedList) {
+ this.cachedList = Array.from(this.list).sort();
}
- return this.list;
+ return this.cachedList;
},
addPad: function(name) {
if (!this.initiated) return;
- if (this.list.indexOf(name) == -1) {
- this.list.push(name);
- this.sorted = false;
+ if (!this.list.has(name)) {
+ this.list.add(name);
+ this.cachedList = undefined;
}
},
removePad: function(name) {
if (!this.initiated) return;
- var index = this.list.indexOf(name);
-
- if (index > -1) {
- this.list.splice(index, 1);
- this.sorted = false;
+ if (this.list.has(name)) {
+ this.list.delete(name);
+ this.cachedList = undefined;
}
}
};
diff --git a/src/node/db/SecurityManager.js b/src/node/db/SecurityManager.js
index 23af82836..d0fc6645a 100644
--- a/src/node/db/SecurityManager.js
+++ b/src/node/db/SecurityManager.js
@@ -30,7 +30,10 @@ var authLogger = log4js.getLogger("auth");
* This function controlls the access to a pad, it checks if the user can access a pad.
* @param padID the pad the user wants to access
* @param sessionCookie the session the user has (set via api)
- * @param token the token of the author (randomly generated at client side, used for public pads)
+ * @param token a random token representing the author, of the form
+ * t.randomstring_of_lenght_20. The random string is generated by
+ * the client.
+ * Used for every pad in the web UI. Not used for the HTTP API.
* @param password the password the user has given to access this pad, can be null
* @return {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx})
*/
@@ -96,16 +99,39 @@ exports.checkAccess = async function(padID, sessionCookie, token, password)
// get information about all sessions contained in this cookie
if (sessionCookie) {
let groupID = padID.split("$")[0];
- let sessionIDs = sessionCookie.split(',');
+
+ /*
+ * Sometimes, RFC 6265-compliant web servers may send back a cookie whose
+ * value is enclosed in double quotes, such as:
+ *
+ * Set-Cookie: sessionCookie="s.37cf5299fbf981e14121fba3a588c02b,s.2b21517bf50729d8130ab85736a11346"; Version=1; Path=/; Domain=localhost; Discard
+ *
+ * Where the double quotes at the start and the end of the header value are
+ * just delimiters. This is perfectly legal: Etherpad parsing logic should
+ * cope with that, and remove the quotes early in the request phase.
+ *
+ * Somehow, this does not happen, and in such cases the actual value that
+ * sessionCookie ends up having is:
+ *
+ * sessionCookie = '"s.37cf5299fbf981e14121fba3a588c02b,s.2b21517bf50729d8130ab85736a11346"'
+ *
+ * As quick measure, let's strip the double quotes (when present).
+ * Note that here we are being minimal, limiting ourselves to just removing
+ * quotes at the start and the end of the string.
+ *
+ * Fixes #3819.
+ * Also, see #3820.
+ */
+ let sessionIDs = sessionCookie.replace(/^"|"$/g, '').split(',');
// was previously iterated in parallel using async.forEach
- let sessionInfos = await Promise.all(sessionIDs.map(sessionID => {
- return sessionManager.getSessionInfo(sessionID);
- }));
+ try {
+ let sessionInfos = await Promise.all(sessionIDs.map(sessionID => {
+ return sessionManager.getSessionInfo(sessionID);
+ }));
- // seperated out the iteration of sessioninfos from the (parallel) fetches from the DB
- for (let sessionInfo of sessionInfos) {
- try {
+ // seperated out the iteration of sessioninfos from the (parallel) fetches from the DB
+ for (let sessionInfo of sessionInfos) {
// is it for this group?
if (sessionInfo.groupID != groupID) {
authLogger.debug("Auth failed: wrong group");
@@ -123,13 +149,13 @@ exports.checkAccess = async function(padID, sessionCookie, token, password)
validSession = true;
sessionAuthor = sessionInfo.authorID;
break;
- } catch (err) {
- // skip session if it doesn't exist
- if (err.message == "sessionID does not exist") {
- authLogger.debug("Auth failed: unknown session");
- } else {
- throw err;
- }
+ }
+ } catch (err) {
+ // skip session if it doesn't exist
+ if (err.message == "sessionID does not exist") {
+ authLogger.debug("Auth failed: unknown session");
+ } else {
+ throw err;
}
}
}
diff --git a/src/node/db/SessionStore.js b/src/node/db/SessionStore.js
index 647cbbc8d..263c7d6ee 100644
--- a/src/node/db/SessionStore.js
+++ b/src/node/db/SessionStore.js
@@ -38,6 +38,11 @@ SessionStore.prototype.get = function(sid, fn) {
SessionStore.prototype.set = function(sid, sess, fn) {
messageLogger.debug('SET ' + sid);
+ // don't store passwords in DB
+ if (sess.user && sess.user.password) {
+ sess.user.password = "PASSWORD_HIDDEN";
+ }
+
db.set("sessionstorage:" + sid, sess);
if (fn) {
process.nextTick(fn);
diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js
index 3898daaf5..cac8c15c5 100644
--- a/src/node/handler/APIHandler.js
+++ b/src/node/handler/APIHandler.js
@@ -25,6 +25,7 @@ var log4js = require('log4js');
var padManager = require("../db/PadManager");
var randomString = require("../utils/randomstring");
var argv = require('../utils/Cli').argv;
+var createHTTPError = require('http-errors');
var apiHandlerLogger = log4js.getLogger('APIHandler');
@@ -136,8 +137,13 @@ version["1.2.13"] = Object.assign({}, version["1.2.12"],
}
);
+version["1.2.14"] = Object.assign({}, version["1.2.13"],
+ { "getStats" : []
+ }
+);
+
// set the latest available API version here
-exports.latestApiVersion = '1.2.13';
+exports.latestApiVersion = '1.2.14';
// exports the versions so it can be used by the new Swagger endpoint
exports.version = version;
@@ -153,25 +159,19 @@ exports.handle = async function(apiVersion, functionName, fields, req, res)
{
// say goodbye if this is an unknown API version
if (!(apiVersion in version)) {
- res.statusCode = 404;
- res.send({code: 3, message: "no such api version", data: null});
- return;
+ throw new createHTTPError.NotFound('no such api version');
}
// say goodbye if this is an unknown function
if (!(functionName in version[apiVersion])) {
- // no status code?!
- res.send({code: 3, message: "no such function", data: null});
- return;
+ throw new createHTTPError.NotFound('no such function');
}
// check the api key!
fields["apikey"] = fields["apikey"] || fields["api_key"];
if (fields["apikey"] !== apikey.trim()) {
- res.statusCode = 401;
- res.send({code: 4, message: "no or wrong API Key", data: null});
- return;
+ throw new createHTTPError.Unauthorized('no or wrong API Key');
}
// sanitize any padIDs before continuing
@@ -185,37 +185,11 @@ exports.handle = async function(apiVersion, functionName, fields, req, res)
fields["padName"] = await padManager.sanitizePadId(fields["padName"]);
}
- // no need to await - callAPI returns a promise
- return callAPI(apiVersion, functionName, fields, req, res);
-}
-
-// calls the api function
-async function callAPI(apiVersion, functionName, fields, req, res)
-{
// put the function parameters in an array
var functionParams = version[apiVersion][functionName].map(function (field) {
return fields[field]
});
- try {
- // call the api function
- let data = await api[functionName].apply(this, functionParams);
-
- if (!data) {
- data = null;
- }
-
- res.send({code: 0, message: "ok", data: data});
- } catch (err) {
- if (err.name == "apierror") {
- // parameters were wrong and the api stopped execution, pass the error
-
- res.send({code: 1, message: err.message, data: null});
- } else {
- // an unknown error happened
-
- res.send({code: 2, message: "internal error", data: null});
- throw err;
- }
- }
+ // call the api function
+ return api[functionName].apply(this, functionParams);
}
diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js
index d2bd05289..e62cb9417 100644
--- a/src/node/handler/ImportHandler.js
+++ b/src/node/handler/ImportHandler.js
@@ -72,6 +72,7 @@ async function doImport(req, res, padId)
let form = new formidable.IncomingForm();
form.keepExtensions = true;
form.uploadDir = tmpDirectory;
+ form.maxFileSize = settings.importMaxFileSize;
// locally wrapped Promise, since form.parse requires a callback
let srcFile = await new Promise((resolve, reject) => {
@@ -81,9 +82,19 @@ async function doImport(req, res, padId)
if (err) {
console.warn("Uploading Error: " + err.stack);
}
+
+ // I hate doing indexOf here but I can't see anything to use...
+ if (err.stack.indexOf("maxFileSize") !== -1) {
+ reject("maxFileSize");
+ }
+
reject("uploadFailed");
}
- resolve(files.file.path);
+ if(!files.file){ // might not be a graceful fix but it works
+ reject("uploadFailed");
+ }else{
+ resolve(files.file.path);
+ }
});
});
@@ -101,7 +112,7 @@ async function doImport(req, res, padId)
let oldSrcFile = srcFile;
srcFile = path.join(path.dirname(srcFile), path.basename(srcFile, fileEnding) + ".txt");
- await fs.rename(oldSrcFile, srcFile);
+ await fsp_rename(oldSrcFile, srcFile);
} else {
console.warn("Not allowing unknown file type to be imported", fileEnding);
throw "uploadFailed";
@@ -111,7 +122,7 @@ async function doImport(req, res, padId)
let destFile = path.join(tmpDirectory, "etherpad_import_" + randNum + "." + exportExtension);
// Logic for allowing external Import Plugins
- let result = await hooks.aCallAll("import", { srcFile, destFile });
+ let result = await hooks.aCallAll("import", { srcFile, destFile, fileEnding });
let importHandledByPlugin = (result.length > 0); // This feels hacky and wrong..
let fileIsEtherpad = (fileEnding === ".etherpad");
@@ -181,8 +192,15 @@ async function doImport(req, res, padId)
if (!req.directDatabaseAccess) {
text = await fsp_readFile(destFile, "utf8");
- // Title needs to be stripped out else it appends it to the pad..
- text = text.replace("