summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md11
-rw-r--r--README.md4
-rwxr-xr-xbin/installDeps.sh4
-rw-r--r--doc/api/hooks_client-side.md23
-rw-r--r--doc/api/http_api.md29
-rw-r--r--settings.json.template3
-rw-r--r--src/locales/ar.json5
-rw-r--r--src/locales/awa.json69
-rw-r--r--src/locales/az.json2
-rw-r--r--src/locales/be-tarask.json2
-rw-r--r--src/locales/br.json3
-rw-r--r--src/locales/cs.json10
-rw-r--r--src/locales/de.json4
-rw-r--r--src/locales/el.json2
-rw-r--r--src/locales/en-gb.json127
-rw-r--r--src/locales/es.json9
-rw-r--r--src/locales/eu.json4
-rw-r--r--src/locales/fr.json2
-rw-r--r--src/locales/gl.json6
-rw-r--r--src/locales/he.json5
-rw-r--r--src/locales/hu.json2
-rw-r--r--src/locales/it.json5
-rw-r--r--src/locales/ksh.json5
-rw-r--r--src/locales/lv.json15
-rw-r--r--src/locales/mk.json2
-rw-r--r--src/locales/nl.json5
-rw-r--r--src/locales/oc.json11
-rw-r--r--src/locales/pt-br.json5
-rw-r--r--src/locales/ru.json1
-rw-r--r--src/locales/sr-ec.json59
-rw-r--r--src/locales/sv.json2
-rw-r--r--src/locales/tr.json2
-rw-r--r--src/locales/zh-hans.json8
-rw-r--r--src/node/db/API.js111
-rw-r--r--src/node/db/Pad.js15
-rw-r--r--src/node/handler/APIHandler.js3
-rw-r--r--src/node/hooks/express.js18
-rw-r--r--src/node/hooks/express/adminplugins.js4
-rw-r--r--src/node/hooks/express/socketio.js8
-rw-r--r--src/node/utils/ExportEtherpad.js14
-rw-r--r--src/node/utils/ImportEtherpad.js17
-rw-r--r--src/node/utils/Minify.js5
-rw-r--r--src/node/utils/Settings.js26
-rw-r--r--src/node/utils/padDiff.js16
-rw-r--r--src/package.json21
-rw-r--r--src/static/css/iframe_editor.css5
-rw-r--r--src/static/css/pad.css19
-rw-r--r--src/static/js/Changeset.js40
-rw-r--r--src/static/js/ace2_inner.js10
-rw-r--r--src/static/js/chat.js10
-rw-r--r--src/static/js/contentcollector.js23
-rw-r--r--src/static/js/pad_editbar.js19
-rw-r--r--src/static/js/pad_impexp.js3
-rw-r--r--src/templates/admin/plugins-info.html2
-rw-r--r--tests/backend/specs/api/pad.js143
55 files changed, 840 insertions, 138 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75a9f327..bf27f292 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+# 1.5.2
+ * NEW: Support for node version 0.12.x
+ * NEW: API endpoint saveRevision, getSavedRevisionCount and listSavedRevisions
+ * NEW: setting to allow load testing
+ * Fix: Rare scroll issue
+ * Fix: Handling of custom pad path
+ * Fix: Better error handling of imports and exports of type "etherpad"
+ * Fix: Walking caret in chrome
+ * Fix: Better handling for changeset problems
+ * SECURITY Fix: Information leak for etherpad exports (CVE-2015-2298)
+
# 1.5.1
* NEW: High resolution Icon
* NEW: Use HTTPS for plugins.json download
diff --git a/README.md b/README.md
index 0cddb0b0..48ff434e 100644
--- a/README.md
+++ b/README.md
@@ -50,8 +50,8 @@ Update to the latest version with `git pull origin`, then run `bin\installOnWind
## GNU/Linux and other UNIX-like systems
You'll need gzip, git, curl, libssl develop libraries, python and gcc.
-- *For Debian/Ubuntu*: `apt-get install gzip git-core curl python libssl-dev pkg-config build-essential`
-- *For Fedora/CentOS*: `yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"`
+- *For Debian/Ubuntu*: `apt-get install gzip git curl python libssl-dev pkg-config build-essential`
+- *For Fedora/CentOS*: `yum install gzip git curl python openssl-devel && yum groupinstall "Development Tools"`
- *For FreeBSD*: `portinstall node, npm, git (optional)`
Additionally, you'll need [node.js](http://nodejs.org) installed, Ideally the latest stable version, we recommend installing/compiling nodejs from source (avoiding apt).
diff --git a/bin/installDeps.sh b/bin/installDeps.sh
index fcf213e4..04c4a02a 100755
--- a/bin/installDeps.sh
+++ b/bin/installDeps.sh
@@ -50,9 +50,9 @@ NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2)
if hash iojs 2>/dev/null; then
IOJS_VERSION=$(iojs --version)
fi
-if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ]; then
+if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ]; then
if [ ! $IOJS_VERSION ]; then
- echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x or v0.11.x" >&2
+ echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.8.x, v0.10.x, v0.11.x or v0.12.x" >&2
exit 1
fi
fi
diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md
index ca429a07..8e2d3da7 100644
--- a/doc/api/hooks_client-side.md
+++ b/doc/api/hooks_client-side.md
@@ -203,6 +203,29 @@ Things in context:
This hook is called before the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original.
+## collectContentImage
+Called from: src/static/js/contentcollector.js
+
+Things in context:
+
+1. cc - the contentcollector object
+2. state - the current state of the change being made
+3. tname - the tag name of this node currently being processed
+4. style - the style applied to the node (probably CSS)
+5. cls - the HTML class string of the node
+6. node - the node being modified
+
+This hook is called before the content of an image node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad.
+
+Example:
+
+```
+exports.collectContentImage = function(name, context){
+ context.state.lineAttributes.img = context.node.outerHTML;
+}
+
+```
+
## collectContentPost
Called from: src/static/js/contentcollector.js
diff --git a/doc/api/http_api.md b/doc/api/http_api.md
index 6cbe6e6b..2ae674d8 100644
--- a/doc/api/http_api.md
+++ b/doc/api/http_api.md
@@ -61,7 +61,7 @@ Portal submits content into new blog post
## Usage
### API version
-The latest version is `1.2.9`
+The latest version is `1.2.11`
The current version can be queried via /api.
@@ -402,6 +402,33 @@ returns the number of revisions of this pad
* `{code: 0, message:"ok", data: {revisions: 56}}`
* `{code: 1, message:"padID does not exist", data: null}`
+#### getSavedRevisionsCount(padID)
+ * API >= 1.2.11
+
+returns the number of saved revisions of this pad
+
+*Example returns:*
+ * `{code: 0, message:"ok", data: {savedRevisions: 42}}`
+ * `{code: 1, message:"padID does not exist", data: null}`
+
+#### listSavedRevisions(padID)
+ * API >= 1.2.11
+
+returns the list of saved revisions of this pad
+
+*Example returns:*
+ * `{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}`
+ * `{code: 1, message:"padID does not exist", data: null}`
+
+#### saveRevision(padID [, rev])
+ * API >= 1.2.11
+
+saves a revision
+
+*Example returns:*
+ * `{code: 0, message:"ok", data: null}`
+ * `{code: 1, message:"padID does not exist", data: null}`
+
#### padUsersCount(padID)
* API >= 1
diff --git a/settings.json.template b/settings.json.template
index 3f84af9b..124345e2 100644
--- a/settings.json.template
+++ b/settings.json.template
@@ -108,6 +108,9 @@
// restrict socket.io transport methods
"socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"],
+
+ // Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance.
+ "loadTest": false,
/* The toolbar buttons configuration.
"toolbar": {
diff --git a/src/locales/ar.json b/src/locales/ar.json
index b0d19fcc..9d29b521 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -4,7 +4,8 @@
"Ali1",
"Tux-tn",
"Alami",
- "Meno25"
+ "Meno25",
+ "Test Create account"
]
},
"index.newPad": "باد جديد",
@@ -29,6 +30,7 @@
"pad.colorpicker.save": "تسجيل",
"pad.colorpicker.cancel": "إلغاء",
"pad.loading": "جاري التحميل...",
+ "pad.noCookie": "الكوكيز غير متاحة. الرجاء السماح بتحميل الكوكيز على متصفحك!",
"pad.passwordRequired": "تحتاج إلى كلمة مرور للوصول إلى هذا الباد",
"pad.permissionDenied": "ليس لديك إذن لدخول هذا الباد",
"pad.wrongPassword": "كانت كلمة المرور خاطئة",
@@ -118,6 +120,7 @@
"pad.impexp.importing": "الاستيراد...",
"pad.impexp.confirmimport": "استيراد ملف سيؤدي للكتابة فوق النص الحالي بالباد. هل أنت متأكد من أنك تريد المتابعة؟",
"pad.impexp.convertFailed": "لم نتمكن من استيراد هذا الملف. يرجى استخدام تنسيق مستند مختلف، أو النسخ واللصق يدوياً",
+ "pad.impexp.padHasData": "لا يمكننا استيراد هذا الملف لأن هذه اللوحة تم بالفعل تغييره, الرجاء استيراد لوحة جديد",
"pad.impexp.uploadFailed": "فشل التحميل، الرجاء المحاولة مرة أخرى",
"pad.impexp.importfailed": "فشل الاستيراد",
"pad.impexp.copypaste": "الرجاء نسخ/لصق",
diff --git a/src/locales/awa.json b/src/locales/awa.json
new file mode 100644
index 00000000..f8c0d501
--- /dev/null
+++ b/src/locales/awa.json
@@ -0,0 +1,69 @@
+{
+ "@metadata": {
+ "authors": [
+ "1AnuraagPandey"
+ ]
+ },
+ "index.newPad": "नयाँ प्याड",
+ "pad.toolbar.bold.title": "मोट (Ctrl-B)",
+ "pad.toolbar.italic.title": "तिरछा (Ctrl+I)",
+ "pad.toolbar.underline.title": "निम्न रेखाङ्कन (Ctrl-U)",
+ "pad.toolbar.indent.title": "इन्डेन्ट (TAB)",
+ "pad.toolbar.unindent.title": "आउटडेन्ट (Shift+TAB)",
+ "pad.toolbar.undo.title": "रद्द (Ctrl-Z)",
+ "pad.toolbar.redo.title": "पुन:लागु (Ctrl-Y)",
+ "pad.toolbar.timeslider.title": "टाइमस्लाइडर",
+ "pad.toolbar.savedRevision.title": "पुनरावलोकन संग्रह किहा जाय",
+ "pad.toolbar.settings.title": "सेटिङ्ग",
+ "pad.colorpicker.save": "सहेजा जाय",
+ "pad.colorpicker.cancel": "रद्द करा जाय",
+ "pad.loading": "लोड होत है...",
+ "pad.wrongPassword": "आप कय पासवर्ड गलत रहा",
+ "pad.settings.padSettings": "प्याड सेटिङ्ग",
+ "pad.settings.myView": "हमार दृष्य",
+ "pad.settings.colorcheck": "लेखकीय रङ्ग",
+ "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.importSuccessful": "सफल!",
+ "pad.importExport.exporthtml": "HTML",
+ "pad.importExport.exportplain": "सामान्य पाठ",
+ "pad.importExport.exportword": "माइक्रोसफ्ट वर्ड",
+ "pad.importExport.exportpdf": "पिडिएफ",
+ "pad.importExport.exportopen": "ओडिएफ(खुल्ला कागजात ढाँचा)",
+ "pad.modals.unauth": "अनाधिकृत",
+ "pad.modals.initsocketfail": "सर्भरमा पहुँच से बहरे है ।",
+ "pad.share.readonly": "पढय वाला खाली",
+ "pad.share.link": "लिङ्क",
+ "pad.share.emebdcode": "URL जोडा जाय",
+ "pad.chat": "बातचीत",
+ "timeslider.pageTitle": "{{appTitle}} समय रेखा",
+ "timeslider.toolbar.authors": "लेखक:",
+ "timeslider.toolbar.exportlink.title": "निर्यात",
+ "timeslider.version": "संस्करण {{version}}",
+ "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
+ "timeslider.month.january": "जनवरी",
+ "timeslider.month.february": "फेब्रुअरी",
+ "timeslider.month.march": "मार्च",
+ "timeslider.month.april": "अप्रैल",
+ "timeslider.month.may": "मई",
+ "timeslider.month.june": "जून",
+ "timeslider.month.july": "जुलाई",
+ "timeslider.month.august": "अगस्त",
+ "timeslider.month.september": "सेप्टेम्बर",
+ "timeslider.month.october": "अक्टूबर",
+ "timeslider.month.november": "नोभेम्बर",
+ "timeslider.month.december": "डिसेम्बर",
+ "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}",
+ "pad.userlist.unnamed": "बेनामी",
+ "pad.userlist.guest": "पहुना",
+ "pad.userlist.deny": "अस्वीकार",
+ "pad.userlist.approve": "स्वीकृत",
+ "pad.impexp.importing": "आयात होत है...",
+ "pad.impexp.importfailed": "आयात असफल रहा",
+ "pad.impexp.copypaste": "कृपया कपी पेस्ट कीन जाय"
+}
diff --git a/src/locales/az.json b/src/locales/az.json
index 99d3216a..f5968ee4 100644
--- a/src/locales/az.json
+++ b/src/locales/az.json
@@ -59,7 +59,7 @@
"pad.modals.forcereconnect": "Məcbur təkrarən bağlan",
"pad.modals.userdup": "Başqa pəncərədə artıq açıqdır",
"pad.modals.userdup.explanation": "Bu lövhə, ola bilsin ki, bu kompüterdəki brauzerin bir neçə pəncərəsində açılmışdır.",
- "pad.modals.userdup.advice": "Bu pəncərədən istifadəylə yenidən qoşulun.",
+ "pad.modals.userdup.advice": "Bu pəncərəni istifadə etmək üçün yenidən qoşul.",
"pad.modals.unauth": "İcazəli deyil",
"pad.modals.unauth.explanation": "Bu səhifəyə baxdığınız vaxt sizin icazəniz dəyişilib. Bərpa etmək üşün yenidən cəhd edin.",
"pad.modals.looping.explanation": "Sinxronlaşdırma serveri ilə kommunikasiya xətası var.",
diff --git a/src/locales/be-tarask.json b/src/locales/be-tarask.json
index f67d10fe..3c789858 100644
--- a/src/locales/be-tarask.json
+++ b/src/locales/be-tarask.json
@@ -35,6 +35,7 @@
"pad.settings.padSettings": "Налады дакумэнта",
"pad.settings.myView": "Мой выгляд",
"pad.settings.stickychat": "Заўсёды паказваць чат",
+ "pad.settings.chatandusers": "Паказаць чат і ўдзельнікаў",
"pad.settings.colorcheck": "Колеры аўтарства",
"pad.settings.linenocheck": "Нумары радкоў",
"pad.settings.rtlcheck": "Тэкст справа-налева",
@@ -108,6 +109,7 @@
"timeslider.month.december": "сьнежань",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: безыменны аўтар, few: безыменныя аўтары, many: безыменных аўтараў, other: безыменных аўтараў ]}",
"pad.savedrevs.marked": "Гэтая вэрсія цяпер пазначаная як захаваная",
+ "pad.savedrevs.timeslider": "Вы можаце пабачыць захаваныя вэрсіі з дапамогай шкалы часу",
"pad.userlist.entername": "Увядзіце вашае імя",
"pad.userlist.unnamed": "безыменны",
"pad.userlist.guest": "Госьць",
diff --git a/src/locales/br.json b/src/locales/br.json
index 6e455d23..6bbd56d2 100644
--- a/src/locales/br.json
+++ b/src/locales/br.json
@@ -29,6 +29,7 @@
"pad.colorpicker.save": "Enrollañ",
"pad.colorpicker.cancel": "Nullañ",
"pad.loading": "O kargañ...",
+ "pad.noCookie": "N'eus ket gallet kavout an toupin. Aotreit an toupinoù en ho merdeer, mar plij !",
"pad.passwordRequired": "Ezhomm ho peus ur ger-tremen evit mont d'ar Pad-se",
"pad.permissionDenied": "\nN'oc'h ket aotreet da vont d'ar pad-mañ",
"pad.wrongPassword": "Fazius e oa ho ker-tremen",
@@ -47,6 +48,7 @@
"pad.importExport.import": "Enkargañ un destenn pe ur restr",
"pad.importExport.importSuccessful": "Deuet eo ganeoc'h !",
"pad.importExport.export": "Ezporzhiañ ar pad bremañ evel :",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Testenn blaen",
"pad.importExport.exportword": "Microsoft Word",
@@ -117,6 +119,7 @@
"pad.impexp.importing": "Oc'h enporzhiañ...",
"pad.impexp.confirmimport": "Ma vez enporzhiet ur restr e vo diverket ar pezh zo en teul a-vremañ. Ha sur oc'h e fell deoc'h mont betek penn ?",
"pad.impexp.convertFailed": "N'eus ket bet gallet enporzhiañ ar restr. Ober gant ur furmad teul all pe eilañ/pegañ gant an dorn.",
+ "pad.impexp.padHasData": "N'hon eus ket gallet enporzhiañ ar restr-mañdre ma'z eus bet degaset kemmoù er bloc'h-se ; enporzhiit anezhi war-zu ur bloc'h nevez, mar plij.",
"pad.impexp.uploadFailed": "C'hwitet eo bet an enporzhiañ. Klaskit en-dro.",
"pad.impexp.importfailed": "C'hwitet eo an enporzhiadenn",
"pad.impexp.copypaste": "Eilit/pegit, mar plij",
diff --git a/src/locales/cs.json b/src/locales/cs.json
index 2a6b5fec..19552ffd 100644
--- a/src/locales/cs.json
+++ b/src/locales/cs.json
@@ -13,11 +13,11 @@
"pad.toolbar.bold.title": "Tučný text (Ctrl-B)",
"pad.toolbar.italic.title": "Kurzíva (Ctrl-I)",
"pad.toolbar.underline.title": "Podtržené písmo (Ctrl-U)",
- "pad.toolbar.strikethrough.title": "Přeskrtnuté písmo",
+ "pad.toolbar.strikethrough.title": "Přeškrtnuto (Ctrl+5)",
"pad.toolbar.ol.title": "Číslovaný seznam",
"pad.toolbar.ul.title": "Nečíslovaný seznam",
- "pad.toolbar.indent.title": "Odsazení",
- "pad.toolbar.unindent.title": "Předsazení",
+ "pad.toolbar.indent.title": "Odsazení (TAB)",
+ "pad.toolbar.unindent.title": "Předsazení (Shift+TAB)",
"pad.toolbar.undo.title": "Zpět (Ctrl-Z)",
"pad.toolbar.redo.title": "Opakovat (Ctrl-Y)",
"pad.toolbar.clearAuthorship.title": "Vymazat barvy autorů",
@@ -30,12 +30,14 @@
"pad.colorpicker.save": "Uložit",
"pad.colorpicker.cancel": "Zrušit",
"pad.loading": "Načítání...",
+ "pad.noCookie": "Nelze nalézt cookie. Povolte prosím cookie ve Vašem prohlížeči.",
"pad.passwordRequired": "Pro přístup k tomuto Padu je třeba znát heslo",
"pad.permissionDenied": "Nemáte oprávnění pro přístup k tomuto Padu",
"pad.wrongPassword": "Nesprávné heslo",
"pad.settings.padSettings": "Nastavení Padu",
"pad.settings.myView": "Vlastní pohled",
"pad.settings.stickychat": "Chat vždy na obrazovce",
+ "pad.settings.chatandusers": "Ukázat Chat a Uživatele",
"pad.settings.colorcheck": "Barvy autorů",
"pad.settings.linenocheck": "Čísla řádků",
"pad.settings.rtlcheck": "Číst obsah zprava doleva?",
@@ -109,6 +111,7 @@
"timeslider.month.december": "prosinec",
"timeslider.unnamedauthors": "{{num}} {[ plural(num) one: nejmenovaný Autor, few: nejmenovaní Autoři, other: nejmenovaných Autorů ]}",
"pad.savedrevs.marked": "Tato revize je nyní označena jako uložená",
+ "pad.savedrevs.timeslider": "Návštěvou časové osy zobrazíte uložené revize",
"pad.userlist.entername": "Zadejte své jméno",
"pad.userlist.unnamed": "nejmenovaný",
"pad.userlist.guest": "Host",
@@ -119,6 +122,7 @@
"pad.impexp.importing": "Importování…",
"pad.impexp.confirmimport": "Import souboru přepíše aktuální text v padu. Opravdu chcete tuto akci provést?",
"pad.impexp.convertFailed": "Tento soubor nelze importovat. Použijte prosím jiný formát dokumentu nebo nakopírujte text ručně",
+ "pad.impexp.padHasData": "Tento soubor jsme nebyly schopni importovat, protože tento Pad již obsahoval změny. Importujte ho prosím do nového padu",
"pad.impexp.uploadFailed": "Nahrávání selhalo, zkuste to znovu",
"pad.impexp.importfailed": "Import selhal",
"pad.impexp.copypaste": "Vložte prosím kopii",
diff --git a/src/locales/de.json b/src/locales/de.json
index a2bca723..4888b8e8 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -36,6 +36,7 @@
"pad.settings.padSettings": "Pad Einstellungen",
"pad.settings.myView": "Eigene Ansicht",
"pad.settings.stickychat": "Chat immer anzeigen",
+ "pad.settings.chatandusers": "Chat und Benutzer anzeigen",
"pad.settings.colorcheck": "Autorenfarben anzeigen",
"pad.settings.linenocheck": "Zeilennummern",
"pad.settings.rtlcheck": "Inhalt von rechts nach links lesen?",
@@ -109,6 +110,7 @@
"timeslider.month.december": "Dezember",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: unbenannter Autor, other: unbenannte Autoren ]}",
"pad.savedrevs.marked": "Diese Version wurde jetzt als gespeicherte Version gekennzeichnet",
+ "pad.savedrevs.timeslider": "Du kannst gespeicherte Versionen durch das Besuchen der Pad-Versionsgeschichte ansehen",
"pad.userlist.entername": "Geben Sie Ihren Namen ein",
"pad.userlist.unnamed": "unbenannt",
"pad.userlist.guest": "Gast",
@@ -119,7 +121,7 @@
"pad.impexp.importing": "Importiere …",
"pad.impexp.confirmimport": "Das Importieren einer Datei überschreibt den aktuellen Text des Pads. Wollen Sie wirklich fortfahren?",
"pad.impexp.convertFailed": "Wir können diese Datei nicht importieren. Bitte verwenden Sie ein anderes Dokumentformat oder übertragen Sie den Text manuell.",
- "pad.impexp.padHasData": "Wir konnten diese Datei nicht importieren, da dieses Pad bereits Änderungen hat. Bitte importiere zu einem neuen Pad.",
+ "pad.impexp.padHasData": "Wir konnten diese Datei nicht importieren, da dieses Pad bereits Änderungen enthält. Bitte importiere sie in ein neues Pad.",
"pad.impexp.uploadFailed": "Der Upload ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"pad.impexp.importfailed": "Import fehlgeschlagen",
"pad.impexp.copypaste": "Bitte kopieren und einfügen",
diff --git a/src/locales/el.json b/src/locales/el.json
index 740da95c..f18c71e4 100644
--- a/src/locales/el.json
+++ b/src/locales/el.json
@@ -37,6 +37,7 @@
"pad.settings.padSettings": "Ρυθμίσεις Pad",
"pad.settings.myView": "Η προβολή μου",
"pad.settings.stickychat": "Να είναι πάντα ορατή η συνομιλία",
+ "pad.settings.chatandusers": "Εμφάνιση Συνομιλίας και Χρηστών",
"pad.settings.colorcheck": "Χρώματα συντάκτη",
"pad.settings.linenocheck": "Αριθμοί γραμμών",
"pad.settings.rtlcheck": "Διαβάζεται το περιεχόμενο από δεξιά προς τα αριστερά;",
@@ -110,6 +111,7 @@
"timeslider.month.december": "Δεκεμβρίου",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: ανώνυμος συντάκτης, other: ανώνυμοι συντάκτες]}",
"pad.savedrevs.marked": "Αυτή η έκδοση επισημάνθηκε ως αποθηκευμένη έκδοση",
+ "pad.savedrevs.timeslider": "Μπορείτε να δείτε αποθηκευμένες αναθεωρήσεις στο χρονοδιάγραμμα",
"pad.userlist.entername": "Εισάγετε το όνομά σας",
"pad.userlist.unnamed": "ανώνυμος",
"pad.userlist.guest": "Επισκέπτης",
diff --git a/src/locales/en-gb.json b/src/locales/en-gb.json
new file mode 100644
index 00000000..258b4331
--- /dev/null
+++ b/src/locales/en-gb.json
@@ -0,0 +1,127 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chase me ladies, I'm the Cavalry",
+ "Shirayuki"
+ ]
+ },
+ "index.newPad": "New Pad",
+ "index.createOpenPad": "or create/open a Pad with the name:",
+ "pad.toolbar.bold.title": "Bold (Ctrl+B)",
+ "pad.toolbar.italic.title": "Italic (Ctrl+I)",
+ "pad.toolbar.underline.title": "Underline (Ctrl+U)",
+ "pad.toolbar.strikethrough.title": "Strikethrough (Ctrl+5)",
+ "pad.toolbar.ol.title": "Ordered list (Ctrl+Shift+N)",
+ "pad.toolbar.ul.title": "Unordered List (Ctrl+Shift+L)",
+ "pad.toolbar.indent.title": "Indent (Tab)",
+ "pad.toolbar.unindent.title": "Outdent (Shift+Tab)",
+ "pad.toolbar.undo.title": "Undo (Ctrl+Z)",
+ "pad.toolbar.redo.title": "Redo (Ctrl+Y)",
+ "pad.toolbar.clearAuthorship.title": "Clear Authorship Colours (Ctrl+Shift+C)",
+ "pad.toolbar.import_export.title": "Import/Export from/to different file formats",
+ "pad.toolbar.timeslider.title": "Timeslider",
+ "pad.toolbar.savedRevision.title": "Save Revision",
+ "pad.toolbar.settings.title": "Settings",
+ "pad.toolbar.embed.title": "Share and Embed this pad",
+ "pad.toolbar.showusers.title": "Show the users on this pad",
+ "pad.colorpicker.save": "Save",
+ "pad.colorpicker.cancel": "Cancel",
+ "pad.loading": "Loading...",
+ "pad.noCookie": "Cookie could not be found. Please allow cookies in your browser!",
+ "pad.passwordRequired": "You need a password to access this pad",
+ "pad.permissionDenied": "You do not have permission to access this pad",
+ "pad.wrongPassword": "Your password was wrong",
+ "pad.settings.padSettings": "Pad Settings",
+ "pad.settings.myView": "My View",
+ "pad.settings.stickychat": "Chat always on screen",
+ "pad.settings.chatandusers": "Show Chat and Users",
+ "pad.settings.colorcheck": "Authorship colours",
+ "pad.settings.linenocheck": "Line numbers",
+ "pad.settings.rtlcheck": "Read content from right to left?",
+ "pad.settings.fontType": "Font type:",
+ "pad.settings.fontType.normal": "Normal",
+ "pad.settings.fontType.monospaced": "Monospace",
+ "pad.settings.globalView": "Global View",
+ "pad.settings.language": "Language:",
+ "pad.importExport.import_export": "Import/Export",
+ "pad.importExport.import": "Upload any text file or document",
+ "pad.importExport.importSuccessful": "Successful!",
+ "pad.importExport.export": "Export current pad as:",
+ "pad.importExport.exportetherpad": "Etherpad",
+ "pad.importExport.exporthtml": "HTML",
+ "pad.importExport.exportplain": "Plain text",
+ "pad.importExport.exportword": "Microsoft Word",
+ "pad.importExport.exportpdf": "PDF",
+ "pad.importExport.exportopen": "ODF (Open Document Format)",
+ "pad.importExport.abiword.innerHTML": "You only can import from plain text or HTML formats. For more advanced import features please <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">install abiword</a>.",
+ "pad.modals.connected": "Connected.",
+ "pad.modals.reconnecting": "Reconnecting to your pad..",
+ "pad.modals.forcereconnect": "Force reconnect",
+ "pad.modals.userdup": "Opened in another window",
+ "pad.modals.userdup.explanation": "This pad seems to be opened in more than one browser window on this computer.",
+ "pad.modals.userdup.advice": "Reconnect to use this window instead.",
+ "pad.modals.unauth": "Not authorised",
+ "pad.modals.unauth.explanation": "Your permissions have changed while viewing this page. Try to reconnect.",
+ "pad.modals.looping.explanation": "There are communication problems with the synchronisation server.",
+ "pad.modals.looping.cause": "Perhaps you connected through an incompatible firewall or proxy.",
+ "pad.modals.initsocketfail": "Server is unreachable.",
+ "pad.modals.initsocketfail.explanation": "Couldn't connect to the synchronisation server.",
+ "pad.modals.initsocketfail.cause": "This is probably due to a problem with your browser or your internet connection.",
+ "pad.modals.slowcommit.explanation": "The server is not responding.",
+ "pad.modals.slowcommit.cause": "This could be due to problems with network connectivity.",
+ "pad.modals.badChangeset.explanation": "An edit you have made was classified illegal by the synchronisation server.",
+ "pad.modals.badChangeset.cause": "This could be due to a wrong server configuration or some other unexpected behaviour. Please contact the service administrator, if you feel this is an error. Try to reconnect in order to continue editing.",
+ "pad.modals.corruptPad.explanation": "The pad you are trying to access is corrupt.",
+ "pad.modals.corruptPad.cause": "This may be due to a wrong server configuration or some other unexpected behaviour. Please contact the service administrator.",
+ "pad.modals.deleted": "Deleted.",
+ "pad.modals.deleted.explanation": "This pad has been removed.",
+ "pad.modals.disconnected": "You have been disconnected.",
+ "pad.modals.disconnected.explanation": "The connection to the server was lost",
+ "pad.modals.disconnected.cause": "The server may be unavailable. Please notify the service administrator if this continues to happen.",
+ "pad.share": "Share this pad",
+ "pad.share.readonly": "Read only",
+ "pad.share.link": "Link",
+ "pad.share.emebdcode": "Embed URL",
+ "pad.chat": "Chat",
+ "pad.chat.title": "Open the chat for this pad.",
+ "pad.chat.loadmessages": "Load more messages",
+ "timeslider.pageTitle": "{{appTitle}} Timeslider",
+ "timeslider.toolbar.returnbutton": "Return to pad",
+ "timeslider.toolbar.authors": "Authors:",
+ "timeslider.toolbar.authorsList": "No Authors",
+ "timeslider.toolbar.exportlink.title": "Export",
+ "timeslider.exportCurrent": "Export current version as:",
+ "timeslider.version": "Version {{version}}",
+ "timeslider.saved": "Saved {{month}} {{day}}, {{year}}",
+ "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
+ "timeslider.month.january": "January",
+ "timeslider.month.february": "February",
+ "timeslider.month.march": "March",
+ "timeslider.month.april": "April",
+ "timeslider.month.may": "May",
+ "timeslider.month.june": "June",
+ "timeslider.month.july": "July",
+ "timeslider.month.august": "August",
+ "timeslider.month.september": "September",
+ "timeslider.month.october": "October",
+ "timeslider.month.november": "November",
+ "timeslider.month.december": "December",
+ "timeslider.unnamedauthors": "{{num}} unnamed {[plural(num) one: author, other: authors ]}",
+ "pad.savedrevs.marked": "This revision is now marked as a saved revision",
+ "pad.savedrevs.timeslider": "You can see saved revisions by visiting the timeslider",
+ "pad.userlist.entername": "Enter your name",
+ "pad.userlist.unnamed": "unnamed",
+ "pad.userlist.guest": "Guest",
+ "pad.userlist.deny": "Deny",
+ "pad.userlist.approve": "Approve",
+ "pad.editbar.clearcolors": "Clear authorship colours on entire document?",
+ "pad.impexp.importbutton": "Import Now",
+ "pad.impexp.importing": "Importing...",
+ "pad.impexp.confirmimport": "Importing a file will overwrite the current text of the pad. Are you sure you want to proceed?",
+ "pad.impexp.convertFailed": "We were not able to import this file. Please use a different document format or copy & paste manually",
+ "pad.impexp.padHasData": "We were not able to import this file because this Pad has already had changes, please import to a new pad",
+ "pad.impexp.uploadFailed": "The upload failed, please try again",
+ "pad.impexp.importfailed": "Import failed",
+ "pad.impexp.copypaste": "Please copy & paste",
+ "pad.impexp.exportdisabled": "Exporting as {{type}} format is disabled. Please contact your system administrator for details."
+}
diff --git a/src/locales/es.json b/src/locales/es.json
index 5547d327..21eb60a7 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -10,7 +10,8 @@
"VegaDark",
"Vivaelcelta",
"Xuacu",
- "Macofe"
+ "Macofe",
+ "Fitoschido"
]
},
"index.newPad": "Nuevo pad",
@@ -42,6 +43,7 @@
"pad.settings.padSettings": "Configuración del pad",
"pad.settings.myView": "Preferencias personales",
"pad.settings.stickychat": "Chat siempre en pantalla",
+ "pad.settings.chatandusers": "Mostrar el chat y los usuarios",
"pad.settings.colorcheck": "Colores de autoría",
"pad.settings.linenocheck": "Números de línea",
"pad.settings.rtlcheck": "¿Leer contenido de derecha a izquierda?",
@@ -56,11 +58,11 @@
"pad.importExport.export": "Exporta el pad actual como:",
"pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
- "pad.importExport.exportplain": "Texto plano",
+ "pad.importExport.exportplain": "Texto sin formato",
"pad.importExport.exportword": "Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (Open Document Format)",
- "pad.importExport.abiword.innerHTML": "Sólo puedes importar formatos de texto plano o html. Para funciones más avanzadas instala <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">abiword</a>.",
+ "pad.importExport.abiword.innerHTML": "Solo es posible importar texto sin formato o en HTML. Para obtener funciones de importación más avanzadas es necesario <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">instalar AbiWord</a>.",
"pad.modals.connected": "Conectado.",
"pad.modals.reconnecting": "Reconectando a tu pad..",
"pad.modals.forcereconnect": "Forzar reconexión",
@@ -115,6 +117,7 @@
"timeslider.month.december": "diciembre",
"timeslider.unnamedauthors": "{{num}} {[ plural(num) one: autor desconocido, other: autores desconocidos]}",
"pad.savedrevs.marked": "Revisión guardada",
+ "pad.savedrevs.timeslider": "Puedes ver revisiones guardadas visitando la línea de tiempo",
"pad.userlist.entername": "Escribe tu nombre",
"pad.userlist.unnamed": "anónimo",
"pad.userlist.guest": "Invitado",
diff --git a/src/locales/eu.json b/src/locales/eu.json
index f12b8d21..9cd06076 100644
--- a/src/locales/eu.json
+++ b/src/locales/eu.json
@@ -1,7 +1,8 @@
{
"@metadata": {
"authors": [
- "Theklan"
+ "Theklan",
+ "Subi"
]
},
"index.newPad": "Pad berria",
@@ -44,6 +45,7 @@
"pad.importExport.import": "Igo edozein testu fitxategi edo dokumentu",
"pad.importExport.importSuccessful": "Arrakastatsua!",
"pad.importExport.export": "Oraingo pad hau honela esportatu:",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Testu laua",
"pad.importExport.exportword": "Microsoft Word",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 92fcb193..ba289e3b 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -50,6 +50,7 @@
"pad.settings.padSettings": "Paramètres du pad",
"pad.settings.myView": "Ma vue",
"pad.settings.stickychat": "Toujours afficher le chat",
+ "pad.settings.chatandusers": "Afficher la discussion et les utilisateurs",
"pad.settings.colorcheck": "Couleurs d’identification",
"pad.settings.linenocheck": "Numéros de lignes",
"pad.settings.rtlcheck": "Le contenu doit-il être lu de droite à gauche ?",
@@ -123,6 +124,7 @@
"timeslider.month.december": "décembre",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: auteur anonyme, other: auteurs anonymes ]}",
"pad.savedrevs.marked": "Cette révision est maintenant marquée comme révision enregistrée",
+ "pad.savedrevs.timeslider": "Vous pouvez voir les révisions enregistrées en visitant l’ascenseur temporel",
"pad.userlist.entername": "Entrez votre nom",
"pad.userlist.unnamed": "anonyme",
"pad.userlist.guest": "Invité",
diff --git a/src/locales/gl.json b/src/locales/gl.json
index b0ca6532..381296aa 100644
--- a/src/locales/gl.json
+++ b/src/locales/gl.json
@@ -27,13 +27,14 @@
"pad.colorpicker.save": "Gardar",
"pad.colorpicker.cancel": "Cancelar",
"pad.loading": "Cargando...",
- "pad.noCookie": "A cookie non se puido atopar. Por favor, habilite as cookies no seu navegador!",
+ "pad.noCookie": "Non se puido atopar a cookie. Por favor, habilite as cookies no seu navegador!",
"pad.passwordRequired": "Cómpre un contrasinal para acceder a este documento",
"pad.permissionDenied": "Non ten permiso para acceder a este documento",
"pad.wrongPassword": "O contrasinal era incorrecto",
"pad.settings.padSettings": "Configuracións do documento",
"pad.settings.myView": "A miña vista",
"pad.settings.stickychat": "Chat sempre visible",
+ "pad.settings.chatandusers": "Mostrar o chat e os usuarios",
"pad.settings.colorcheck": "Cores de identificación",
"pad.settings.linenocheck": "Números de liña",
"pad.settings.rtlcheck": "Quere ler o contido da dereita á esquerda?",
@@ -107,6 +108,7 @@
"timeslider.month.december": "decembro",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: autor anónimo, other: autores anónimos ]}",
"pad.savedrevs.marked": "Esta revisión está agora marcada como revisión gardada",
+ "pad.savedrevs.timeslider": "Pode consultar as revisións gardadas visitando a liña do tempo",
"pad.userlist.entername": "Insira o seu nome",
"pad.userlist.unnamed": "anónimo",
"pad.userlist.guest": "Convidado",
@@ -117,7 +119,7 @@
"pad.impexp.importing": "Importando...",
"pad.impexp.confirmimport": "A importación dun ficheiro ha sobrescribir o texto actual do documento. Está seguro de querer continuar?",
"pad.impexp.convertFailed": "Non somos capaces de importar o ficheiro. Utilice un formato de documento diferente ou copie e pegue manualmente",
- "pad.impexp.padHasData": "Non puidemos importar este ficheiro porque este Pad xa tivo cambios, por favor, importe a un novo pad.",
+ "pad.impexp.padHasData": "Non puidemos importar este ficheiro porque este documento xa sufriu cambios; importe a un novo documento.",
"pad.impexp.uploadFailed": "Houbo un erro ao cargar o ficheiro; inténteo de novo",
"pad.impexp.importfailed": "Fallou a importación",
"pad.impexp.copypaste": "Copie e pegue",
diff --git a/src/locales/he.json b/src/locales/he.json
index 573bc5f6..4222e153 100644
--- a/src/locales/he.json
+++ b/src/locales/he.json
@@ -29,12 +29,14 @@
"pad.colorpicker.save": "שמירה",
"pad.colorpicker.cancel": "ביטול",
"pad.loading": "טעינה...",
+ "pad.noCookie": "העוגייה לא נמצאה. נא לאפשר עוגיות בדפדפן שלך!",
"pad.passwordRequired": "דרושה ססמה כדי לגשת לפנקס הזה",
"pad.permissionDenied": "אין לך הרשאה לגשת לפנקס הזה",
"pad.wrongPassword": "ססמתך הייתה שגויה",
"pad.settings.padSettings": "הגדרות פנקס",
"pad.settings.myView": "התצוגה שלי",
"pad.settings.stickychat": "השיחה תמיד על המסך",
+ "pad.settings.chatandusers": "הצגת צ'אט ומשתמשים",
"pad.settings.colorcheck": "צביעה לפי מחבר",
"pad.settings.linenocheck": "מספרי שורות",
"pad.settings.rtlcheck": "לקרוא את התוכן מימין לשמאל?",
@@ -47,6 +49,7 @@
"pad.importExport.import": "העלאת כל קובץ טקסט או מסמך",
"pad.importExport.importSuccessful": "זה עבד!",
"pad.importExport.export": "ייצוא הפנקס הנוכחי בתור:",
+ "pad.importExport.exportetherpad": "את'רפד",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "טקסט רגיל",
"pad.importExport.exportword": "מיקרוסופט וורד",
@@ -107,6 +110,7 @@
"timeslider.month.december": "דצמבר",
"timeslider.unnamedauthors": "{[plural(num) one: יוצר אחד, other: {{num}} יוצרים ]} ללא שם",
"pad.savedrevs.marked": "גרסה זו מסומנת כגרסה שמורה",
+ "pad.savedrevs.timeslider": "אפשר להציג גרסאות שמורות באמצעות ביקור בגולל הזמן",
"pad.userlist.entername": "נא להזין את שמך",
"pad.userlist.unnamed": "ללא שם",
"pad.userlist.guest": "אורח",
@@ -117,6 +121,7 @@
"pad.impexp.importing": "ייבוא...",
"pad.impexp.confirmimport": "ייבוא של קובץ יבטל את הטקסט הנוכחי בפנקס. האם ברצונך להמשיך?",
"pad.impexp.convertFailed": "לא הצלחנו לייבא את הקובץ הזה. נא להשתמש בתסדיר מסמך שונה או להעתיק ולהדביק ידנית",
+ "pad.impexp.padHasData": "לא הצלחנו לייבא את הקובץ הזה, כי בפנקס הזה כבר יש שינויים. נא לייבא לפנקס חדש.",
"pad.impexp.uploadFailed": "ההעלאה נכשלה, נא לנסות שוב",
"pad.impexp.importfailed": "הייבוא נכשל",
"pad.impexp.copypaste": "נא להעתיק ולהדביק",
diff --git a/src/locales/hu.json b/src/locales/hu.json
index 3102790d..287e7954 100644
--- a/src/locales/hu.json
+++ b/src/locales/hu.json
@@ -30,6 +30,7 @@
"pad.colorpicker.save": "Mentés",
"pad.colorpicker.cancel": "Mégsem",
"pad.loading": "Betöltés…",
+ "pad.noCookie": "Nem található a süti. Engedélyezd a böngésződben a sütik használatát!",
"pad.passwordRequired": "Jelszóra van szükséged ezen notesz eléréséhez",
"pad.permissionDenied": "Nincs engedélyed ezen notesz eléréséhez",
"pad.wrongPassword": "A jelszó rossz volt",
@@ -48,6 +49,7 @@
"pad.importExport.import": "Tetszőleges szövegfájl vagy dokumentum feltöltése",
"pad.importExport.importSuccessful": "Siker!",
"pad.importExport.export": "Jelenlegi notesz exportálása így:",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Sima szöveg",
"pad.importExport.exportword": "Microsoft Word",
diff --git a/src/locales/it.json b/src/locales/it.json
index 02ae8a9f..a6c30d96 100644
--- a/src/locales/it.json
+++ b/src/locales/it.json
@@ -5,7 +5,8 @@
"Gianfranco",
"Muxator",
"Vituzzu",
- "Macofe"
+ "Macofe",
+ "Nivit"
]
},
"index.newPad": "Nuovo Pad",
@@ -36,6 +37,7 @@
"pad.settings.padSettings": "Impostazioni del Pad",
"pad.settings.myView": "Mia visualizzazione",
"pad.settings.stickychat": "Chat sempre sullo schermo",
+ "pad.settings.chatandusers": "Mostra chat e utenti",
"pad.settings.colorcheck": "Colori che indicano gli autori",
"pad.settings.linenocheck": "Numeri di riga",
"pad.settings.rtlcheck": "Leggere il contenuto da destra a sinistra?",
@@ -48,6 +50,7 @@
"pad.importExport.import": "Carica un file di testo o un documento",
"pad.importExport.importSuccessful": "Riuscito!",
"pad.importExport.export": "Esportare il Pad corrente come:",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Solo testo",
"pad.importExport.exportword": "Microsoft Word",
diff --git a/src/locales/ksh.json b/src/locales/ksh.json
index 8443e0ec..f851bbf1 100644
--- a/src/locales/ksh.json
+++ b/src/locales/ksh.json
@@ -26,12 +26,14 @@
"pad.colorpicker.save": "Faßhallde",
"pad.colorpicker.cancel": "Ophüüre",
"pad.loading": "Ben aam Laade&nbsp;&hellip;",
+ "pad.noCookie": "Dat Pläzje wood nit jevonge. Don dat en Dingem Brauser zohlohße!",
"pad.passwordRequired": "Do bruchs e Paßwoot för heh dat Pädd.",
"pad.permissionDenied": "Do häs nit dat Rääsch, op heh dat Pädd zohzejriife.",
"pad.wrongPassword": "Ding Paßwoot wohr verkeht.",
"pad.settings.padSettings": "Däm Pädd sing Enschtällonge",
"pad.settings.myView": "Aanseesch",
"pad.settings.stickychat": "Donn der Klaaf emmer aanzeije",
+ "pad.settings.chatandusers": "Dunn de Metmaacher un der Klaaf aanzeije",
"pad.settings.colorcheck": "Färve för de Schriiver",
"pad.settings.linenocheck": "Nommere för de Reije",
"pad.settings.rtlcheck": "Schreff vun Rääschß noh Lenks?",
@@ -44,6 +46,7 @@
"pad.importExport.import": "Donn jeede Täx udder jeede Zoot Dokemänt huhlaade",
"pad.importExport.importSuccessful": "Jeschaff!",
"pad.importExport.export": "Don dat Pädd äxpoteere alß:",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Eijfach Täx",
"pad.importExport.exportword": "Microsoft Word",
@@ -104,6 +107,7 @@
"timeslider.month.december": "Dezämber",
"timeslider.unnamedauthors": "{[plural(num) zero: keine, one: eine, other: {{num}} ]} nahmeloose Schriiver",
"pad.savedrevs.marked": "Heh di Väsjohn es jäz faßjehallde.",
+ "pad.savedrevs.timeslider": "Mer kann de faßjehallde Väsjohne belohre beim Verjangeheid afschpelle",
"pad.userlist.entername": "Jif Dinge Nahme en",
"pad.userlist.unnamed": "nahmeloßß",
"pad.userlist.guest": "Jaßß",
@@ -114,6 +118,7 @@
"pad.impexp.importing": "Ben aam Empotteere&nbsp;&hellip;",
"pad.impexp.confirmimport": "En Dattei ze empotteere määt der janze Täx em Pädd fott. Wells De dat verfaftesch hann?",
"pad.impexp.convertFailed": "Mer kunnte di Dattei nit empoteere. Nemm en ander Dattei-Fommaat udder donn dä Täx vun Hand kopeere un ennföhje.",
+ "pad.impexp.padHasData": "Mer kunnte di Dattei nit empottehre weil et Pädd alt Veränderonge metjemaht hät. Donn se en e neu Pädd empottehre.",
"pad.impexp.uploadFailed": "Dat Huhlaade es donävve jejange. Bes esu johd un probeer et norr_ens.",
"pad.impexp.importfailed": "Et Empoteere es donävve jejange.",
"pad.impexp.copypaste": "Bes esu johd un donn et koppeere un enfööje",
diff --git a/src/locales/lv.json b/src/locales/lv.json
index acdaaccd..ee402d33 100644
--- a/src/locales/lv.json
+++ b/src/locales/lv.json
@@ -9,14 +9,14 @@
"pad.toolbar.bold.title": "Treknrakstā (CTRL + B)",
"pad.toolbar.italic.title": "Slīpraksta (Ctrl-es)",
"pad.toolbar.underline.title": "Pasvītrojuma (CTRL + U)",
- "pad.toolbar.strikethrough.title": "Pārsvītrojums",
- "pad.toolbar.ol.title": "Sakārtots saraksts",
- "pad.toolbar.ul.title": "Nesakārtots saraksts",
- "pad.toolbar.indent.title": "Atkāpe",
- "pad.toolbar.unindent.title": "Izkāpe",
+ "pad.toolbar.strikethrough.title": "Pārsvītrojums (Ctrl+5)",
+ "pad.toolbar.ol.title": "Sakārtots saraksts (Ctrl+Shift+N)",
+ "pad.toolbar.ul.title": "Nesakārtots saraksts (Ctrl+Shift+L)",
+ "pad.toolbar.indent.title": "Atkāpe (TAB)",
+ "pad.toolbar.unindent.title": "Izkāpe (Shift+TAB)",
"pad.toolbar.undo.title": "Atsaukt (CTRL + Z)",
"pad.toolbar.redo.title": "Atcelt atsaukšanu (CTRL + Y)",
- "pad.toolbar.clearAuthorship.title": "Notīrit autoru krāsas",
+ "pad.toolbar.clearAuthorship.title": "Notīrit autoru krāsas (Ctrl+Shift+C)",
"pad.toolbar.import_export.title": "Importēšanas/eksportēšanas no un uz citu failu formātiem",
"pad.toolbar.savedRevision.title": "Saglabāt pārskatīšanu",
"pad.toolbar.settings.title": "Iestatījumi",
@@ -46,6 +46,7 @@
"pad.importExport.exportword": "Programma Microsoft Word",
"pad.importExport.exportpdf": "PDF",
"pad.importExport.exportopen": "ODF (Open dokumenta formāts)",
+ "pad.modals.connected": "Pievienojies.",
"pad.modals.userdup": "Atvērts citā logā",
"pad.modals.unauth": "Nav atļauts",
"pad.modals.looping.explanation": "Pastāv sakaru problēmas ar sinhronizācijas servera.",
@@ -55,7 +56,7 @@
"pad.modals.deleted": "Dzēsts",
"pad.modals.disconnected": "Jūs esat atvienots.",
"pad.modals.disconnected.explanation": "Tika zaudēts savienojums ar serveri",
- "pad.modals.disconnected.cause": "Iespējams, ka serveris nav pieejams. Lūgums paziņot mums, ja tas turpina notikt.",
+ "pad.modals.disconnected.cause": "Iespējams, ka serveris nav pieejams. Lūgums paziņot pakalpojuma administratoram, ja tas turpina notikt.",
"pad.share": "Koplietot šo pad",
"pad.share.readonly": "Tikai lasāms",
"pad.share.link": "Saite",
diff --git a/src/locales/mk.json b/src/locales/mk.json
index 9fc6b817..a924875a 100644
--- a/src/locales/mk.json
+++ b/src/locales/mk.json
@@ -34,6 +34,7 @@
"pad.settings.padSettings": "Поставки на тетратката",
"pad.settings.myView": "Мој поглед",
"pad.settings.stickychat": "Разговорите секогаш на екранот",
+ "pad.settings.chatandusers": "Прикажи разговор и корисници",
"pad.settings.colorcheck": "Авторски бои",
"pad.settings.linenocheck": "Броеви на редовите",
"pad.settings.rtlcheck": "Содржините да се читаат од десно на лево?",
@@ -107,6 +108,7 @@
"timeslider.month.december": "декември",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: неименуван автор, other: неименувани автори ]}",
"pad.savedrevs.marked": "Оваа преработка сега е означена како зачувана",
+ "pad.savedrevs.timeslider": "Можете да ги погледате зачуваните преработки посетувајќи го времеследниот лизгач",
"pad.userlist.entername": "Внесете го вашето име",
"pad.userlist.unnamed": "без име",
"pad.userlist.guest": "Гостин",
diff --git a/src/locales/nl.json b/src/locales/nl.json
index ed69527e..183d0fa9 100644
--- a/src/locales/nl.json
+++ b/src/locales/nl.json
@@ -28,12 +28,14 @@
"pad.colorpicker.save": "Opslaan",
"pad.colorpicker.cancel": "Annuleren",
"pad.loading": "Bezig met laden…",
+ "pad.noCookie": "Er kon geen cookie gevonden worden. Zorg ervoor dat uw browser cookies accepteert.",
"pad.passwordRequired": "U hebt een wachtwoord nodig om toegang te krijgen tot deze pad",
"pad.permissionDenied": "U hebt geen rechten om deze pad te bekijken",
"pad.wrongPassword": "U hebt een onjuist wachtwoord ingevoerd",
"pad.settings.padSettings": "Padinstellingen",
"pad.settings.myView": "Mijn overzicht",
"pad.settings.stickychat": "Chat altijd zichtbaar",
+ "pad.settings.chatandusers": "Chat en gebruikers weergeven",
"pad.settings.colorcheck": "Kleuren auteurs",
"pad.settings.linenocheck": "Regelnummers",
"pad.settings.rtlcheck": "Inhoud van rechts naar links lezen?",
@@ -46,6 +48,7 @@
"pad.importExport.import": "Upload een tekstbestand of document",
"pad.importExport.importSuccessful": "Afgerond",
"pad.importExport.export": "Huidige pad exporteren als",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Tekst zonder opmaak",
"pad.importExport.exportword": "Microsoft Word",
@@ -106,6 +109,7 @@
"timeslider.month.december": "december",
"timeslider.unnamedauthors": "{{num}} onbekende {[plural(num) one: auteur, other: auteurs ]}",
"pad.savedrevs.marked": "Deze versie is nu gemarkeerd als opgeslagen versie",
+ "pad.savedrevs.timeslider": "U kunt opgeslagen versies bekijken via de tijdschuiver.",
"pad.userlist.entername": "Geef uw naam op",
"pad.userlist.unnamed": "zonder naam",
"pad.userlist.guest": "Gast",
@@ -116,6 +120,7 @@
"pad.impexp.importing": "Bezig met importeren…",
"pad.impexp.confirmimport": "Door een bestand te importeren overschrijft u de huidige tekst van de pad. Wilt u echt doorgaan?",
"pad.impexp.convertFailed": "Het was niet mogelijk dit bestand te importeren. Gebruik een andere documentopmaak of kopieer en plak de inhoud handmatig",
+ "pad.impexp.padHasData": "Het was niet mogelijk dit bestand te importeren omdat er al wijzigingen aan de etherpad zijn gemaakt. Importeer naar een nieuwe etherpad.",
"pad.impexp.uploadFailed": "Het uploaden is mislukt. Probeer het opnieuw",
"pad.impexp.importfailed": "Importeren is mislukt",
"pad.impexp.copypaste": "Gebruik kopiëren en plakken",
diff --git a/src/locales/oc.json b/src/locales/oc.json
index 54a375ba..e62d387a 100644
--- a/src/locales/oc.json
+++ b/src/locales/oc.json
@@ -9,14 +9,14 @@
"pad.toolbar.bold.title": "Gras (Ctrl-B)",
"pad.toolbar.italic.title": "Italica (Ctrl-I)",
"pad.toolbar.underline.title": "Soslinhat (Ctrl-U)",
- "pad.toolbar.strikethrough.title": "Raiat",
- "pad.toolbar.ol.title": "Lista ordenada",
- "pad.toolbar.ul.title": "Lista amb de piuses",
+ "pad.toolbar.strikethrough.title": "Raiat (Ctrl+5)",
+ "pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)",
+ "pad.toolbar.ul.title": "Lista pas ordenada (Ctrl+Shift+L)",
"pad.toolbar.indent.title": "Indentar (TAB)",
"pad.toolbar.unindent.title": "Desindentar (Maj+TAB)",
"pad.toolbar.undo.title": "Anullar (Ctrl-Z)",
"pad.toolbar.redo.title": "Restablir (Ctrl-Y)",
- "pad.toolbar.clearAuthorship.title": "Escafar las colors qu'identifican los autors",
+ "pad.toolbar.clearAuthorship.title": "Escafar las colors qu'identifican los autors (Ctrl+Shift+C)",
"pad.toolbar.import_export.title": "Importar/Exportar de/cap a un format de fichièr diferent",
"pad.toolbar.timeslider.title": "Istoric dinamic",
"pad.toolbar.savedRevision.title": "Enregistrar la revision",
@@ -26,6 +26,7 @@
"pad.colorpicker.save": "Enregistrar",
"pad.colorpicker.cancel": "Anullar",
"pad.loading": "Cargament...",
+ "pad.noCookie": "Lo cookie a pas pogut èsser trobat. Autorizatz los cookies dins vòstre navigador !",
"pad.passwordRequired": "Avètz besonh d'un senhal per accedir a aqueste Pad",
"pad.permissionDenied": "Vos es pas permés d’accedir a aqueste Pad.",
"pad.wrongPassword": "Senhal incorrècte",
@@ -44,6 +45,7 @@
"pad.importExport.import": "Cargar un tèxte o un document",
"pad.importExport.importSuccessful": "Capitat !",
"pad.importExport.export": "Exportar lo Pad actual coma :",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Tèxte brut",
"pad.importExport.exportword": "Microsoft Word",
@@ -114,6 +116,7 @@
"pad.impexp.importing": "Impòrt en cors...",
"pad.impexp.confirmimport": "Importar un fichièr espotirà lo tèxte actual del blòt. Sètz segur que lo volètz far ?",
"pad.impexp.convertFailed": "Podèm pas importar aqueste fichièr. Utilizatz un autre format de document o fasètz un copiar/pegar manual",
+ "pad.impexp.padHasData": "Avèm pas pogut importar aqueste fichièr perque aqueste blòt a ja agut de modificacions ; importatz cap a un blòt novèl",
"pad.impexp.uploadFailed": "Lo telecargament a fracassat, reensajatz",
"pad.impexp.importfailed": "Fracàs de l'importacion",
"pad.impexp.copypaste": "Copiatz/pegatz",
diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json
index e8eb79ee..1fd145bc 100644
--- a/src/locales/pt-br.json
+++ b/src/locales/pt-br.json
@@ -11,7 +11,8 @@
"Dianakc",
"Macofe",
"Rodrigo codignoli",
- "Webysther"
+ "Webysther",
+ "Fasouzafreitas"
]
},
"index.newPad": "Nova Nota",
@@ -43,6 +44,7 @@
"pad.settings.padSettings": "Configurações da Nota",
"pad.settings.myView": "Minha Visão",
"pad.settings.stickychat": "Conversa sempre visível",
+ "pad.settings.chatandusers": "Mostrar o chat e os usuários",
"pad.settings.colorcheck": "Cores de autoria",
"pad.settings.linenocheck": "Números de linha",
"pad.settings.rtlcheck": "Ler conteúdo da direita para esquerda?",
@@ -116,6 +118,7 @@
"timeslider.month.december": "Dezembro",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: autor anônimo, other: autores anônimos ]}",
"pad.savedrevs.marked": "Esta revisão foi marcada como salva",
+ "pad.savedrevs.timeslider": "Pode consultar as revisões salvas visitando a linha do tempo",
"pad.userlist.entername": "Insira o seu nome",
"pad.userlist.unnamed": "Sem título",
"pad.userlist.guest": "Convidado",
diff --git a/src/locales/ru.json b/src/locales/ru.json
index f96f2338..bd2143a1 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -48,6 +48,7 @@
"pad.importExport.import": "Загрузить любой текстовый файл или документ",
"pad.importExport.importSuccessful": "Успешно!",
"pad.importExport.export": "Экспортировать текущий документ как:",
+ "pad.importExport.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Обычный текст",
"pad.importExport.exportword": "Microsoft Word",
diff --git a/src/locales/sr-ec.json b/src/locales/sr-ec.json
new file mode 100644
index 00000000..9fd20b18
--- /dev/null
+++ b/src/locales/sr-ec.json
@@ -0,0 +1,59 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aktron",
+ "Milicevic01",
+ "Милан Јелисавчић"
+ ]
+ },
+ "index.newPad": "Нови Пад",
+ "pad.toolbar.bold.title": "Подебљано (Ctrl-B)",
+ "pad.toolbar.italic.title": "Искошено (Ctrl-I)",
+ "pad.toolbar.underline.title": "Подвучено (Ctrl-U)",
+ "pad.toolbar.strikethrough.title": "Прецртано",
+ "pad.toolbar.ol.title": "Уређен списак",
+ "pad.toolbar.ul.title": "Неуређен списак",
+ "pad.toolbar.indent.title": "Увлачење (TAB)",
+ "pad.toolbar.undo.title": "Опозови (Ctrl+Z)",
+ "pad.toolbar.settings.title": "Подешавања",
+ "pad.colorpicker.save": "Сачувај",
+ "pad.colorpicker.cancel": "Откажи",
+ "pad.loading": "Учитавање...",
+ "pad.wrongPassword": "Ваша лозинка није исправна",
+ "pad.settings.myView": "Мој приказ",
+ "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": "Отпремите било коју текстуалну датотеку или документ",
+ "pad.importExport.importSuccessful": "Успело!",
+ "pad.importExport.exporthtml": "HTML",
+ "pad.importExport.exportplain": "чист текст",
+ "pad.importExport.exportpdf": "PDF",
+ "pad.modals.connected": "Повезано.",
+ "pad.modals.slowcommit.explanation": "Сервер не одговара.",
+ "pad.modals.deleted": "Обрисано.",
+ "pad.share": "Дели овај пад",
+ "pad.share.readonly": "Само за читање",
+ "pad.share.link": "Веза",
+ "pad.chat": "Ћаскање",
+ "pad.chat.title": "Отворите ћаскање за овај пад.",
+ "pad.chat.loadmessages": "Учитајте више порука.",
+ "timeslider.month.january": "јануар",
+ "timeslider.month.february": "фебруар",
+ "timeslider.month.march": "март",
+ "timeslider.month.april": "април",
+ "timeslider.month.may": "мај",
+ "timeslider.month.june": "јун",
+ "timeslider.month.july": "јул",
+ "timeslider.month.august": "август",
+ "timeslider.month.september": "септембар",
+ "timeslider.month.october": "октобар",
+ "timeslider.month.november": "новембар",
+ "timeslider.month.december": "децембар",
+ "pad.userlist.approve": "одобрено",
+ "pad.impexp.importbutton": "Увези одмах",
+ "pad.impexp.importing": "Увожење..."
+}
diff --git a/src/locales/sv.json b/src/locales/sv.json
index a53146bf..ae9b1f9a 100644
--- a/src/locales/sv.json
+++ b/src/locales/sv.json
@@ -35,6 +35,7 @@
"pad.settings.padSettings": "Blockinställningar",
"pad.settings.myView": "Min vy",
"pad.settings.stickychat": "Chatten alltid på skärmen",
+ "pad.settings.chatandusers": "Visa chatt och användare",
"pad.settings.colorcheck": "Författarskapsfärger",
"pad.settings.linenocheck": "Radnummer",
"pad.settings.rtlcheck": "Vill du läsa innehållet från höger till vänster?",
@@ -108,6 +109,7 @@
"timeslider.month.december": "december",
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: namnlös författare, other: namnlösa författare]}",
"pad.savedrevs.marked": "Denna version är nu markerad som en sparad version",
+ "pad.savedrevs.timeslider": "Du kan se sparade versioner med tidsreglaget",
"pad.userlist.entername": "Ange ditt namn",
"pad.userlist.unnamed": "namnlös",
"pad.userlist.guest": "Gäst",
diff --git a/src/locales/tr.json b/src/locales/tr.json
index 54030fda..b0b6dbee 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -30,6 +30,7 @@
"pad.colorpicker.save": "Kaydet",
"pad.colorpicker.cancel": "İptal",
"pad.loading": "Yükleniyor...",
+ "pad.noCookie": "Çerez bulunamadı. Lütfen tarayıcınızda çerezlere izin veriniz!",
"pad.passwordRequired": "Bu bloknota erişebilmeniz için parolaya ihtiyacınız var",
"pad.permissionDenied": "Bu bloknota erişmeye izniniz yok",
"pad.wrongPassword": "Parolanız yanlış",
@@ -48,6 +49,7 @@
"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.exportetherpad": "Etherpad",
"pad.importExport.exporthtml": "HTML",
"pad.importExport.exportplain": "Düz metin",
"pad.importExport.exportword": "Microsoft Word",
diff --git a/src/locales/zh-hans.json b/src/locales/zh-hans.json
index bc1c97b5..4af3cb48 100644
--- a/src/locales/zh-hans.json
+++ b/src/locales/zh-hans.json
@@ -42,6 +42,7 @@
"pad.settings.padSettings": "记事本设置",
"pad.settings.myView": "我的视窗",
"pad.settings.stickychat": "总是显示聊天屏幕",
+ "pad.settings.chatandusers": "显示聊天和用户",
"pad.settings.colorcheck": "作者颜色",
"pad.settings.linenocheck": "行号",
"pad.settings.rtlcheck": "从右到左阅读内容吗?",
@@ -100,9 +101,9 @@
"timeslider.exportCurrent": "当前版本导出为:",
"timeslider.version": "版本 {{version}}",
"timeslider.saved": "在{{year}}年{{month}}{{day}}日保存",
- "timeslider.dateformat": "{{year}}年{{month}}{{day}}日 {{hours}}时:{{minutes}}分:{{seconds}}秒",
- "timeslider.month.january": "一月",
- "timeslider.month.february": "二月",
+ "timeslider.dateformat": "{{year}}年{{month}}月{{day}}日 {{hours}}时:{{minutes}}分:{{seconds}}秒",
+ "timeslider.month.january": "1月",
+ "timeslider.month.february": "2月",
"timeslider.month.march": "三月",
"timeslider.month.april": "四月",
"timeslider.month.may": "五月",
@@ -115,6 +116,7 @@
"timeslider.month.december": "十二月",
"timeslider.unnamedauthors": "{{num}}个匿名作者",
"pad.savedrevs.marked": "这一修订现在被标记为已保存的修订版本",
+ "pad.savedrevs.timeslider": "您可以使用时间滑块查阅已保存的版本",
"pad.userlist.entername": "输入您的姓名",
"pad.userlist.unnamed": "匿名",
"pad.userlist.guest": "访客",
diff --git a/src/node/db/API.js b/src/node/db/API.js
index 81dedcfe..69a380c7 100644
--- a/src/node/db/API.js
+++ b/src/node/db/API.js
@@ -518,6 +518,117 @@ exports.getRevisionsCount = function(padID, callback)
}
/**
+getSavedRevisionsCount(padID) returns the number of saved revisions of this pad
+
+Example returns:
+
+{code: 0, message:"ok", data: {savedRevisions: 42}}
+{code: 1, message:"padID does not exist", data: null}
+*/
+exports.getSavedRevisionsCount = function(padID, callback)
+{
+ //get the pad
+ getPadSafe(padID, true, function(err, pad)
+ {
+ if(ERR(err, callback)) return;
+
+ callback(null, {savedRevisions: pad.getSavedRevisionsNumber()});
+ });
+}
+
+/**
+listSavedRevisions(padID) returns the list of saved revisions of this pad
+
+Example returns:
+
+{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}
+{code: 1, message:"padID does not exist", data: null}
+*/
+exports.listSavedRevisions = function(padID, callback)
+{
+ //get the pad
+ getPadSafe(padID, true, function(err, pad)
+ {
+ if(ERR(err, callback)) return;
+
+ callback(null, {savedRevisions: pad.getSavedRevisionsList()});
+ });
+}
+
+/**
+saveRevision(padID) returns the list of saved revisions of this pad
+
+Example returns:
+
+{code: 0, message:"ok", data: null}
+{code: 1, message:"padID does not exist", data: null}
+*/
+exports.saveRevision = function(padID, rev, callback)
+{
+ //check if rev is set
+ if(typeof rev == "function")
+ {
+ callback = rev;
+ rev = undefined;
+ }
+
+ //check if rev is a number
+ if(rev !== undefined && typeof rev != "number")
+ {
+ //try to parse the number
+ if(!isNaN(parseInt(rev)))
+ {
+ rev = parseInt(rev);
+ }
+ else
+ {
+ callback(new customError("rev is not a number", "apierror"));
+ return;
+ }
+ }
+
+ //ensure this is not a negativ number
+ if(rev !== undefined && rev < 0)
+ {
+ callback(new customError("rev is a negativ number","apierror"));
+ return;
+ }
+
+ //ensure this is not a float value
+ if(rev !== undefined && !is_int(rev))
+ {
+ callback(new customError("rev is a float value","apierror"));
+ return;
+ }
+
+ //get the pad
+ getPadSafe(padID, true, function(err, pad)
+ {
+ if(ERR(err, callback)) return;
+
+ //the client asked for a special revision
+ if(rev !== undefined)
+ {
+ //check if this is a valid revision
+ if(rev > pad.getHeadRevisionNumber())
+ {
+ callback(new customError("rev is higher than the head revision of the pad","apierror"));
+ return;
+ }
+ } else {
+ rev = pad.getHeadRevisionNumber();
+ }
+
+ authorManager.createAuthor('API', function(err, author) {
+ if(ERR(err, callback)) return;
+
+ pad.addSavedRevision(rev, author.authorID, 'Saved through API call');
+ callback();
+ });
+ });
+}
+
+/**
getLastEdited(padID) returns the timestamp of the last revision of the pad
Example returns:
diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js
index 2f5860f8..53847600 100644
--- a/src/node/db/Pad.js
+++ b/src/node/db/Pad.js
@@ -54,6 +54,21 @@ Pad.prototype.getHeadRevisionNumber = function getHeadRevisionNumber() {
return this.head;
};
+Pad.prototype.getSavedRevisionsNumber = function getSavedRevisionsNumber() {
+ return this.savedRevisions.length;
+};
+
+Pad.prototype.getSavedRevisionsList = function getSavedRevisionsList() {
+ var savedRev = new Array();
+ for(var rev in this.savedRevisions){
+ savedRev.push(this.savedRevisions[rev].revNum);
+ }
+ savedRev.sort(function(a, b) {
+ return a - b;
+ });
+ return savedRev;
+};
+
Pad.prototype.getPublicStatus = function getPublicStatus() {
return this.publicStatus;
};
diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js
index a26dd2cf..232b0b46 100644
--- a/src/node/handler/APIHandler.js
+++ b/src/node/handler/APIHandler.js
@@ -368,6 +368,9 @@ var version =
, "setHTML" : ["padID", "html"]
, "getAttributePool" : ["padID"]
, "getRevisionsCount" : ["padID"]
+ , "getSavedRevisionsCount" : ["padID"]
+ , "listSavedRevisions" : ["padID"]
+ , "saveRevision" : ["padID", "rev"]
, "getRevisionChangeset" : ["padID", "rev"]
, "getLastEdited" : ["padID"]
, "deletePad" : ["padID"]
diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js
index e858b800..3275bd3f 100644
--- a/src/node/hooks/express.js
+++ b/src/node/hooks/express.js
@@ -10,24 +10,9 @@ var server;
var serverName;
exports.createServer = function () {
- //try to get the git version
- var version = "";
- try
- {
- var rootPath = path.resolve(npm.dir, '..');
- var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8");
- var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
- version = fs.readFileSync(refPath, "utf-8");
- version = version.substring(0, 7);
- console.log("Your Etherpad git version is " + version);
- }
- catch(e)
- {
- console.warn("Can't get git version for server header\n" + e.message)
- }
console.log("Report bugs at https://github.com/ether/etherpad-lite/issues")
- serverName = "Etherpad " + version + " (http://etherpad.org)";
+ serverName = "Etherpad " + settings.getGitCommit() + " (http://etherpad.org)";
exports.restartServer();
@@ -38,7 +23,6 @@ exports.createServer = function () {
else{
console.warn("Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json");
}
-
}
exports.restartServer = function () {
diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js
index 34eafd0b..5015cc5a 100644
--- a/src/node/hooks/express/adminplugins.js
+++ b/src/node/hooks/express/adminplugins.js
@@ -1,4 +1,5 @@
var eejs = require('ep_etherpad-lite/node/eejs');
+var settings = require('ep_etherpad-lite/node/utils/Settings');
var installer = require('ep_etherpad-lite/static/js/pluginfw/installer');
var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins');
var _ = require('underscore');
@@ -15,7 +16,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins.html", render_args) );
});
args.app.get('/admin/plugins/info', function(req, res) {
- res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {}) );
+ var gitCommit = settings.getGitCommit();
+ res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {gitCommit:gitCommit}) );
});
}
diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js
index b70aa50e..35d6d074 100644
--- a/src/node/hooks/express/socketio.js
+++ b/src/node/hooks/express/socketio.js
@@ -23,8 +23,12 @@ exports.expressCreateServer = function (hook_name, args, cb) {
io.use(function(socket, accept) {
var data = socket.request;
- if (!data.headers.cookie) return accept('No session cookie transmitted.', false);
-
+ // Use a setting if we want to allow load Testing
+ if(!data.headers.cookie && settings.loadTest){
+ accept(null, true);
+ }else{
+ if (!data.headers.cookie) return accept('No session cookie transmitted.', false);
+ }
// Use connect's cookie parser, because it knows how to parse signed cookies
connect.cookieParser(webaccess.secret)(data, {}, function(err){
if(err) {
diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js
index 4f91e4e3..46ae0d7a 100644
--- a/src/node/utils/ExportEtherpad.js
+++ b/src/node/utils/ExportEtherpad.js
@@ -23,9 +23,19 @@ exports.getPadRaw = function(padId, callback){
async.waterfall([
function(cb){
+ // Get the Pad
+ db.findKeys("pad:"+padId, null, function(err,padcontent){
+ if(!err){
+ cb(err, padcontent);
+ }
+ })
+ },
+ function(padcontent,cb){
+
// Get the Pad available content keys
- db.findKeys("pad:"+padId+"*", null, function(err,records){
+ db.findKeys("pad:"+padId+":*", null, function(err,records){
if(!err){
+ for (var key in padcontent) { records.push(padcontent[key]);}
cb(err, records);
}
})
@@ -48,7 +58,7 @@ exports.getPadRaw = function(padId, callback){
// Get the author info
db.get("globalAuthor:"+authorId, function(e, authorEntry){
- authorEntry.padIDs = padId;
+ if(authorEntry && authorEntry.padIDs) authorEntry.padIDs = padId;
if(!e) data["globalAuthor:"+authorId] = authorEntry;
});
diff --git a/src/node/utils/ImportEtherpad.js b/src/node/utils/ImportEtherpad.js
index 1574a3a9..37863bff 100644
--- a/src/node/utils/ImportEtherpad.js
+++ b/src/node/utils/ImportEtherpad.js
@@ -21,9 +21,22 @@ var db = require("../db/DB").db;
exports.setPadRaw = function(padId, records, callback){
records = JSON.parse(records);
+ // !! HACK !!
+ // If you have a really large pad it will cause a Maximum Range Stack crash
+ // This is a temporary patch for that so things are kept stable.
+ var recordCount = Object.keys(records).length;
+ if(recordCount >= 50000){
+ console.warn("Etherpad file is too large to import.. We need to fix this. See https://github.com/ether/etherpad-lite/issues/2524");
+ return callback("tooLarge", false);
+ }
+
async.eachSeries(Object.keys(records), function(key, cb){
var value = records[key]
+ if(!value){
+ cb(); // null values are bad.
+ }
+
// Author data
if(value.padIDs){
// rewrite author pad ids
@@ -34,7 +47,9 @@ exports.setPadRaw = function(padId, records, callback){
db.get(key, function(err, author){
if(author){
// Yes, add the padID to the author..
- author.padIDs.push(padId);
+ if( Object.prototype.toString.call(author) === '[object Array]'){
+ author.padIDs.push(padId);
+ }
value = author;
}else{
// No, create a new array with the author info in
diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js
index 7b868307..da101f8d 100644
--- a/src/node/utils/Minify.js
+++ b/src/node/utils/Minify.js
@@ -23,7 +23,7 @@ var ERR = require("async-stacktrace");
var settings = require('./Settings');
var async = require('async');
var fs = require('fs');
-var cleanCSS = require('clean-css');
+var CleanCSS = require('clean-css');
var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
var path = require('path');
@@ -410,7 +410,8 @@ function compressJS(values)
function compressCSS(values)
{
var complete = values.join("\n");
- return cleanCSS.process(complete);
+ var minimized = new CleanCSS().minify(complete).styles;
+ return minimized;
}
exports.minify = minify;
diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js
index 05ae3bd8..5382d819 100644
--- a/src/node/utils/Settings.js
+++ b/src/node/utils/Settings.js
@@ -144,6 +144,11 @@ exports.loglevel = "INFO";
*/
exports.disableIPlogging = false;
+/**
+ * Disable Load Testing
+ */
+exports.loadTest = false;
+
/*
* log4js appender configuration
*/
@@ -179,6 +184,25 @@ exports.abiwordAvailable = function()
}
};
+// Provide git version if available
+exports.getGitCommit = function() {
+ var version = "";
+ try
+ {
+ var rootPath = path.resolve(npm.dir, '..');
+ var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8");
+ var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
+ version = fs.readFileSync(refPath, "utf-8");
+ version = version.substring(0, 7);
+ console.log("Your Etherpad git version is " + version);
+ }
+ catch(e)
+ {
+ console.warn("Can't get git version for server header\n" + e.message)
+ }
+ return version;
+}
+
exports.reloadSettings = function reloadSettings() {
// Discover where the settings file lives
var settingsFilename = argv.settings || "settings.json";
@@ -261,3 +285,5 @@ exports.reloadSettings = function reloadSettings() {
// initially load settings
exports.reloadSettings();
+
+
diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js
index 88fa5cba..24d5bb0c 100644
--- a/src/node/utils/padDiff.js
+++ b/src/node/utils/padDiff.js
@@ -101,8 +101,12 @@ PadDiff.prototype._createClearStartAtext = function(rev, callback){
return callback(err);
}
+ try {
//apply the clearAuthorship changeset
var newAText = Changeset.applyToAText(changeset, atext, self._pad.pool);
+ } catch(err) {
+ return callback(err)
+ }
callback(null, newAText);
});
@@ -209,10 +213,14 @@ PadDiff.prototype._createDiffAtext = function(callback) {
if(superChangeset){
var deletionChangeset = self._createDeletionChangeset(superChangeset,atext,self._pad.pool);
- //apply the superChangeset, which includes all addings
- atext = Changeset.applyToAText(superChangeset,atext,self._pad.pool);
- //apply the deletionChangeset, which adds a deletions
- atext = Changeset.applyToAText(deletionChangeset,atext,self._pad.pool);
+ try {
+ //apply the superChangeset, which includes all addings
+ atext = Changeset.applyToAText(superChangeset,atext,self._pad.pool);
+ //apply the deletionChangeset, which adds a deletions
+ atext = Changeset.applyToAText(deletionChangeset,atext,self._pad.pool);
+ } catch(err) {
+ return callback(err)
+ }
}
callback(err, atext);
diff --git a/src/package.json b/src/package.json
index 0598a4b6..e2d9e299 100644
--- a/src/package.json
+++ b/src/package.json
@@ -13,22 +13,21 @@
],
"dependencies" : {
"etherpad-yajsml" : "0.0.2",
- "request" : "2.51.0",
- "etherpad-require-kernel" : "1.0.7",
- "resolve" : "1.0.0",
- "socket.io" : "1.3.2",
- "ueberDB" : "0.2.11",
+ "request" : "2.53.0",
+ "etherpad-require-kernel" : "1.0.8",
+ "resolve" : "1.1.0",
+ "socket.io" : "1.3.3",
+ "ueberDB" : "0.2.13",
"express" : "3.8.1",
"async" : "0.9.0",
"connect" : "2.7.11",
- "clean-css" : "0.3.2",
+ "clean-css" : "3.0.8",
"uglify-js" : "2.4.16",
"formidable" : "1.0.16",
"log4js" : "0.6.22",
- "nodemailer" : "0.3.44",
"cheerio" : "0.18.0",
"async-stacktrace" : "0.0.2",
- "npm" : "2.2.0",
+ "npm" : "2.4.1",
"ejs" : "1.0.0",
"graceful-fs" : "3.0.5",
"slide" : "1.1.6",
@@ -41,13 +40,13 @@
"swagger-node-express" : "2.1.3",
"channels" : "0.0.4",
"jsonminify" : "0.2.3",
- "measured" : "0.1.6",
+ "measured" : "1.0.0",
"mocha" : "2.1.0",
"supertest" : "0.15.0"
},
"bin": { "etherpad-lite": "./node/server.js" },
"devDependencies": {
- "wd" : "0.0.31"
+ "wd" : "0.3.11"
},
"engines" : { "node" : ">=0.6.3",
"npm" : ">=1.0"
@@ -55,5 +54,5 @@
"repository" : { "type" : "git",
"url" : "http://github.com/ether/etherpad-lite.git"
},
- "version" : "1.5.1"
+ "version" : "1.5.2"
}
diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css
index 1f247e59..eb69364b 100644
--- a/src/static/css/iframe_editor.css
+++ b/src/static/css/iframe_editor.css
@@ -11,7 +11,10 @@ span { cursor: auto; }
background: #acf;
}
-a { cursor: pointer !important; }
+a {
+ cursor: pointer !important;
+ white-space:pre-wrap;
+}
ul, ol, li {
padding: 0;
diff --git a/src/static/css/pad.css b/src/static/css/pad.css
index 70c15b51..c9ebff4a 100644
--- a/src/static/css/pad.css
+++ b/src/static/css/pad.css
@@ -373,7 +373,7 @@ li[data-key=showusers] > a #online_count {
border-radius: 5px;
}
#myusernameform {
- margin-left: 35px
+ margin-left: 30px
}
#myusernameedit {
font-size: 1.3em;
@@ -382,7 +382,7 @@ li[data-key=showusers] > a #online_count {
height: 18px;
margin: 0;
border: 0;
- width: 117px;
+ width: 122px;
background: transparent;
}
#myusernameform input.editable {
@@ -897,7 +897,7 @@ input[type=checkbox] {
#connectivity,
#users {
position: absolute;
- top: 36px;
+ top: 38px;
right: 20px;
display: none;
z-index: 500;
@@ -919,7 +919,8 @@ input[type=checkbox] {
right:0px !important;
border-radius:0px !important;
width:182px !important;
- margin:2px 0 0 0 !important;
+/* Below makes UI look weird when X makes editbar flow onto two lines */
+/* margin:2px 0 0 0 !important;*/
border: none !important;
border-bottom: 1px solid #ccc !important;
height:155px !important;
@@ -935,9 +936,8 @@ input[type=checkbox] {
.chatAndUsersChat{
bottom:0px !important;
padding:0 !important;
- margin:0 !important;
+ margin: 165px 0px 0px 0px;
right:0 !important;
- top: 200px !important;
width:182px !important;
border: none !important;
padding:5px !important;
@@ -1014,12 +1014,16 @@ input[type=checkbox] {
top: 72px !important;
}
}
+
+/* Mobile devices */
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
#users {
top: auto;
right:0px !important;
bottom: 33px;
border-radius: 0px !important;
+ height: 55px !important;
+ overflow: auto;
}
#mycolorpicker {
left: -73px;
@@ -1099,7 +1103,8 @@ input[type=checkbox] {
}
#chatbox{
position:absolute;
- bottom:33px;
+ bottom:33px !important;
+ margin: 65px 0 0 0;
}
#gritter-notice-wrapper{
bottom:43px !important;
diff --git a/src/static/js/Changeset.js b/src/static/js/Changeset.js
index 32da887d..df180f9c 100644
--- a/src/static/js/Changeset.js
+++ b/src/static/js/Changeset.js
@@ -507,6 +507,7 @@ exports.opAssembler = function () {
*/
exports.stringIterator = function (str) {
var curIndex = 0;
+ // newLines is the number of \n between curIndex and str.length
var newLines = str.split("\n").length - 1
function getnewLines(){
return newLines
@@ -909,42 +910,43 @@ exports.pack = function (oldLen, newLen, opsStr, bank) {
* @params str {string} String to which a Changeset should be applied
*/
exports.applyToText = function (cs, str) {
- var totalNrOfLines = str.split("\n").length;
- var removedLines = 0;
var unpacked = exports.unpack(cs);
exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen);
var csIter = exports.opIterator(unpacked.ops);
var bankIter = exports.stringIterator(unpacked.charBank);
var strIter = exports.stringIterator(str);
- var newlines = 0
- var newlinefail = false
var assem = exports.stringAssembler();
while (csIter.hasNext()) {
var op = csIter.next();
switch (op.opcode) {
case '+':
+ //op is + and op.lines 0: no newlines must be in op.chars
+ //op is + and op.lines >0: op.chars must include op.lines newlines
+ if(op.lines != bankIter.peek(op.chars).split("\n").length - 1){
+ throw new Error("newline count is wrong in op +; cs:"+cs+" and text:"+str);
+ }
assem.append(bankIter.take(op.chars));
break;
case '-':
- removedLines += op.lines;
- newlines = strIter.newlines()
- strIter.skip(op.chars);
- if(!(newlines - strIter.newlines() == 0) && (newlines - strIter.newlines() != op.lines)){
- newlinefail = true
+ //op is - and op.lines 0: no newlines must be in the deleted string
+ //op is - and op.lines >0: op.lines newlines must be in the deleted string
+ if(op.lines != strIter.peek(op.chars).split("\n").length - 1){
+ throw new Error("newline count is wrong in op -; cs:"+cs+" and text:"+str);
}
+ strIter.skip(op.chars);
break;
case '=':
- newlines = strIter.newlines()
- assem.append(strIter.take(op.chars));
- if(!(newlines - strIter.newlines() == op.lines)){
- newlinefail = true
+ //op is = and op.lines 0: no newlines must be in the copied string
+ //op is = and op.lines >0: op.lines newlines must be in the copied string
+ if(op.lines != strIter.peek(op.chars).split("\n").length - 1){
+ throw new Error("newline count is wrong in op =; cs:"+cs+" and text:"+str);
}
+ assem.append(strIter.take(op.chars));
break;
}
}
- exports.assert(totalNrOfLines >= removedLines,"cannot remove ", removedLines, " lines from text with ", totalNrOfLines, " lines");
assem.append(strIter.take(strIter.remaining()));
- return [assem.toString(),newlinefail];
+ return assem.toString();
};
/**
@@ -1236,7 +1238,7 @@ exports.mutateAttributionLines = function (cs, lines, pool) {
}
}
- exports.assert(!lineAssem, "line assembler not finished");
+ exports.assert(!lineAssem, "line assembler not finished:"+cs);
mut.close();
//dmesg("-> "+lines.toSource());
@@ -1615,12 +1617,8 @@ exports.makeAText = function (text, attribs) {
* @param pool {AttribPool} Attribute Pool to add to
*/
exports.applyToAText = function (cs, atext, pool) {
- var text = exports.applyToText(cs, atext.text)
- if(text[1]){
- throw new Error()
- }
return {
- text: text[0],
+ text: exports.applyToText(cs, atext.text),
attribs: exports.applyToAttribution(cs, atext.attribs, pool)
};
};
diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js
index 4b84e784..aa4bf6c7 100644
--- a/src/static/js/ace2_inner.js
+++ b/src/static/js/ace2_inner.js
@@ -3313,6 +3313,15 @@ function Ace2Inner(){
return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])];
}
+ function handleCut(evt)
+ {
+ inCallStackIfNecessary("handleCut", function()
+ {
+ doDeleteKey(evt);
+ });
+ return true;
+ }
+
function handleClick(evt)
{
inCallStackIfNecessary("handleClick", function()
@@ -4854,6 +4863,7 @@ function Ace2Inner(){
$(document).on("keypress", handleKeyEvent);
$(document).on("keyup", handleKeyEvent);
$(document).on("click", handleClick);
+ $(document).on("cut", handleCut);
$(root).on("blur", handleBlur);
if (browser.msie)
{
diff --git a/src/static/js/chat.js b/src/static/js/chat.js
index ce9a0033..811b1320 100644
--- a/src/static/js/chat.js
+++ b/src/static/js/chat.js
@@ -57,14 +57,18 @@ var chat = (function()
},
chatAndUsers: function(fromInitialCall)
{
- if(!userAndChat || fromInitialCall){
+ var toEnable = $('#options-chatandusers').is(":checked");
+ if(toEnable || !userAndChat || fromInitialCall){
padcookie.setPref("chatAndUsers", true);
chat.stickToScreen(true);
$('#options-stickychat').prop('checked', true)
+ $('#options-chatandusers').prop('checked', true)
$('#options-stickychat').prop("disabled", "disabled");
$('#users').addClass("chatAndUsers");
$("#chatbox").addClass("chatAndUsersChat");
+ // redraw
userAndChat = true;
+ padeditbar.redrawHeight()
}else{
padcookie.setPref("chatAndUsers", false);
$('#options-stickychat').prop("disabled", false);
@@ -91,7 +95,9 @@ var chat = (function()
{
if($('#chatbox').css("display") != "none"){
if(!self.lastMessage || !self.lastMessage.position() || self.lastMessage.position().top < $('#chattext').height()) {
- $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, "slow");
+ // if we use a slow animate here we can have a race condition when a users focus can not be moved away
+ // from the last message recieved.
+ $('#chattext').animate({scrollTop: $('#chattext')[0].scrollHeight}, { duration: 400, queue: false });
self.lastMessage = $('#chattext > p').eq(-1);
}
}
diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js
index 1f0620fe..e428c63f 100644
--- a/src/static/js/contentcollector.js
+++ b/src/static/js/contentcollector.js
@@ -87,6 +87,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
"li": 1
};
+ _.each(hooks.callAll('ccRegisterBlockElements'), function(element){
+ _blockElems[element] = 1;
+ });
+
function isBlockElement(n)
{
return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
@@ -320,7 +324,6 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
return [key, value];
})
);
-
lines.appendText('*', Changeset.makeAttribsString('+', attributes , apool));
}
cc.startNewLine = function(state)
@@ -458,8 +461,23 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
else
{
var tname = (dom.nodeTagName(node) || "").toLowerCase();
+
+ if (tname == "img"){
+ var collectContentImage = hooks.callAll('collectContentImage', {
+ cc: cc,
+ state: state,
+ tname: tname,
+ styl: styl,
+ cls: cls,
+ node: node
+ });
+ }else{
+ // THIS SEEMS VERY HACKY! -- Please submit a better fix!
+ delete state.lineAttributes.img
+ }
+
if (tname == "br")
- {
+ {
this.breakLine = true;
var tvalue = dom.nodeAttr(node, 'value');
var induceLineBreak = hooks.callAll('collectContentLineBreak', {
@@ -483,7 +501,6 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
{
var styl = dom.nodeAttr(node, "style");
var cls = dom.nodeProp(node, "className");
-
var isPre = (tname == "pre");
if ((!isPre) && abrowser.safari)
{
diff --git a/src/static/js/pad_editbar.js b/src/static/js/pad_editbar.js
index e874614f..7d0539af 100644
--- a/src/static/js/pad_editbar.js
+++ b/src/static/js/pad_editbar.js
@@ -186,9 +186,26 @@ var padeditbar = (function()
$('#editbar').css("height", editbarHeight);
$('#editorcontainer').css("top", containerTop);
+
+ // make sure pop ups are in the right place
+ if($('#editorcontainer').offset()){
+ $('.popup').css("top", $('#editorcontainer').offset().top + "px");
+ }
+
+ // If sticky chat is enabled..
if($('#options-stickychat').is(":checked")){
- $('#chatbox').css("top", $('#editorcontainer').offset().top + "px");
+ if($('#editorcontainer').offset()){
+ $('#chatbox').css("top", $('#editorcontainer').offset().top + "px");
+ }
};
+
+ // If chat and Users is enabled..
+ if($('#options-chatandusers').is(":checked")){
+ if($('#editorcontainer').offset()){
+ $('#users').css("top", $('#editorcontainer').offset().top + "px");
+ }
+ }
+
},
registerDropdownCommand: function (cmd, dropdown) {
dropdown = dropdown || cmd;
diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js
index 77f1eb28..96761570 100644
--- a/src/static/js/pad_impexp.js
+++ b/src/static/js/pad_impexp.js
@@ -188,7 +188,8 @@ var padimpexp = (function()
pad = _pad;
//get /p/padname
- var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname);
+ // if /p/ isn't available due to a rewrite we use the clientVars padId
+ var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId;
//get http://example.com/p/padname without Params
var pad_root_url = document.location.protocol + '//' + document.location.host + document.location.pathname;
diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html
index 0ca6d010..1b328a89 100644
--- a/src/templates/admin/plugins-info.html
+++ b/src/templates/admin/plugins-info.html
@@ -22,6 +22,8 @@
</div>
<div class="innerwrapper">
+ <h2>Etherpad Git Commit</h2>
+ <p><a href='https://github.com/ether/etherpad-lite/commit/<%= gitCommit %>'><%= gitCommit %></a></p>
<h2>Installed plugins</h2>
<pre><%- plugins.formatPlugins().replace(", ","\n") %></pre>
diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js
index 6010a11c..52849c2e 100644
--- a/tests/backend/specs/api/pad.js
+++ b/tests/backend/specs/api/pad.js
@@ -48,33 +48,38 @@ describe('Permission', function(){
-> deletePad -- This gives us a guaranteed clear environment
-> createPad
-> getRevisions -- Should be 0
- -> getHTML -- Should be the default pad text in HTML format
- -> deletePad -- Should just delete a pad
- -> getHTML -- Should return an error
- -> createPad(withText)
- -> getText -- Should have the text specified above as the pad text
- -> setText
- -> getText -- Should be the text set before
- -> getRevisions -- Should be 0 still?
- -> padUsersCount -- Should be 0
- -> getReadOnlyId -- Should be a value
- -> listAuthorsOfPad(padID) -- should be empty array?
- -> getLastEdited(padID) -- Should be when pad was made
- -> setText(padId)
- -> getLastEdited(padID) -- Should be when setText was performed
- -> padUsers(padID) -- Should be when setText was performed
-
- -> setText(padId, "hello world")
+ -> getSavedRevisionsCount(padID) -- Should be 0
+ -> listSavedRevisions(padID) -- Should be an empty array
+ -> getHTML -- Should be the default pad text in HTML format
+ -> deletePad -- Should just delete a pad
+ -> getHTML -- Should return an error
+ -> createPad(withText)
+ -> getText -- Should have the text specified above as the pad text
+ -> setText
+ -> getText -- Should be the text set before
+ -> getRevisions -- Should be 0 still?
+ -> saveRevision
+ -> getSavedRevisionsCount(padID) -- Should be 0 still?
+ -> listSavedRevisions(padID) -- Should be an empty array still ?
+ -> padUsersCount -- Should be 0
+ -> getReadOnlyId -- Should be a value
+ -> listAuthorsOfPad(padID) -- should be empty array?
-> getLastEdited(padID) -- Should be when pad was made
- -> getText(padId) -- Should be "hello world"
- -> movePad(padID, newPadId) -- Should provide consistant pad data
- -> getText(newPadId) -- Should be "hello world"
- -> movePad(newPadID, originalPadId) -- Should provide consistant pad data
- -> getText(originalPadId) -- Should be "hello world"
- -> getLastEdited(padID) -- Should not be 0
- -> setHTML(padID) -- Should fail on invalid HTML
- -> setHTML(padID) *3 -- Should fail on invalid HTML
- -> getHTML(padID) -- Should return HTML close to posted HTML
+ -> setText(padId)
+ -> getLastEdited(padID) -- Should be when setText was performed
+ -> padUsers(padID) -- Should be when setText was performed
+
+ -> setText(padId, "hello world")
+ -> getLastEdited(padID) -- Should be when pad was made
+ -> getText(padId) -- Should be "hello world"
+ -> movePad(padID, newPadId) -- Should provide consistant pad data
+ -> getText(newPadId) -- Should be "hello world"
+ -> movePad(newPadID, originalPadId) -- Should provide consistant pad data
+ -> getText(originalPadId) -- Should be "hello world"
+ -> getLastEdited(padID) -- Should not be 0
+ -> setHTML(padID) -- Should fail on invalid HTML
+ -> setHTML(padID) *3 -- Should fail on invalid HTML
+ -> getHTML(padID) -- Should return HTML close to posted HTML
*/
@@ -109,11 +114,35 @@ describe('getRevisionsCount', function(){
});
})
+describe('getSavedRevisionsCount', function(){
+ it('gets saved revisions count of Pad', function(done) {
+ api.get(endPoint('getSavedRevisionsCount')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions Count");
+ if(res.body.data.savedRevisions !== 0) throw new Error("Incorrect Saved Revisions Count");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('listSavedRevisions', function(){
+ it('gets saved revision list of Pad', function(done) {
+ api.get(endPoint('listSavedRevisions')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions List");
+ if(!res.body.data.savedRevisions.equals([])) throw new Error("Incorrect Saved Revisions List");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
describe('getHTML', function(){
it('get the HTML of Pad', function(done) {
api.get(endPoint('getHTML')+"&padID="+testPadId)
.expect(function(res){
- if(res.body.data.html.length <= 1) throw new Error("Unable to get Revision Count");
+ if(res.body.data.html.length <= 1) throw new Error("Unable to get the HTML");
})
.expect('Content-Type', /json/)
.expect(200, done)
@@ -187,16 +216,50 @@ describe('getText', function(){
})
describe('getRevisionsCount', function(){
- it('gets Revision Coutn of a Pad', function(done) {
+ it('gets Revision Count of a Pad', function(done) {
api.get(endPoint('getRevisionsCount')+"&padID="+testPadId)
.expect(function(res){
- if(res.body.data.revisions !== 1) throw new Error("Unable to set text revision count")
+ if(res.body.data.revisions !== 1) throw new Error("Unable to get text revision count")
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('saveRevision', function(){
+ it('saves Revision', function(done) {
+ api.get(endPoint('saveRevision')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to save Revision");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
+
+describe('getSavedRevisionsCount', function(){
+ it('gets saved revisions count of Pad', function(done) {
+ api.get(endPoint('getSavedRevisionsCount')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions Count");
+ if(res.body.data.savedRevisions !== 1) throw new Error("Incorrect Saved Revisions Count");
})
.expect('Content-Type', /json/)
.expect(200, done)
});
})
+describe('listSavedRevisions', function(){
+ it('gets saved revision list of Pad', function(done) {
+ api.get(endPoint('listSavedRevisions')+"&padID="+testPadId)
+ .expect(function(res){
+ if(res.body.code !== 0) throw new Error("Unable to get Saved Revisions List");
+ if(!res.body.data.savedRevisions.equals([1])) throw new Error("Incorrect Saved Revisions List");
+ })
+ .expect('Content-Type', /json/)
+ .expect(200, done)
+ });
+})
describe('padUsersCount', function(){
it('gets User Count of a Pad', function(done) {
api.get(endPoint('padUsersCount')+"&padID="+testPadId)
@@ -461,3 +524,25 @@ function generateLongText(){
}
return text;
}
+
+// Need this to compare arrays (listSavedRevisions test)
+Array.prototype.equals = function (array) {
+ // if the other array is a falsy value, return
+ if (!array)
+ return false;
+ // compare lengths - can save a lot of time
+ if (this.length != array.length)
+ return false;
+ for (var i = 0, l=this.length; i < l; i++) {
+ // Check if we have nested arrays
+ if (this[i] instanceof Array && array[i] instanceof Array) {
+ // recurse into the nested arrays
+ if (!this[i].equals(array[i]))
+ return false;
+ } else if (this[i] != array[i]) {
+ // Warning - two different object instances will never be equal: {x:20} != {x:20}
+ return false;
+ }
+ }
+ return true;
+}