From 1cdae1d3ddd565f4aee3acb9714561d17604cc66 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 13 Dec 2018 14:23:51 +0100 Subject: [PATCH 01/44] update changelog for Opossum release --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e70921605..20ec08a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# Opossum release (v2.14.0) + +## Goals + +For this release we chose to focus on our in-pad chat functionality and the ability to show your cursor's position to other users in the same pad. + +## Update notes + +* We've released an updated version of a serverside dependency: `chainpad-server` + * this addresses a recently introduced bug which is capable of sending more history than clients require under certain circumstances + * to use this updated dependency, run `npm update` and restart your server + +## Features + +* Our code editor is now capable of displaying other user's cursors within your view of the document. + * this is enabled by default, but you can choose not to share your own cursor, and to disable the display of other users' cursors in your document + * your initial color is chosen randomly, but you can choose any color you like within the settings page alongside the other configuration options for cursors +* After some consideration, we have chosen to change the permissions around the chat functionality embedded within every pad. + * previously we had allowed viewers to participate in chat, even though they could not change the document. + * we decided that this was counter-intuitive + * in the event of an XSS vulnerability it could be used as a vector for privilege escalation + * as such, we have modified our embedded chat functionality to only allow editors to participate + * this change is not backwards-compatible, and so the embedded chat boxes will have dropped their older history + * our assumption is that this will be an improvement for the majority of our users, and that it's fairly safe to drop older history given that chat is a relatively new feature + * if this has affected you in an adverse way, the information is still accessible, and you can contact us if you need a way to recover that information +* Finally, it is now possible to print the rendered markdown content in our code editor, thanks to a contribution from [@joldie](https://github.com/joldie) + # Numbat release (v2.13.0) ## Goals From ee3102143f54ba77b6eb8139576a9708329340e8 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 14 Dec 2018 15:38:04 +0100 Subject: [PATCH 02/44] Remove debugging logs --- www/pad/inner.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/www/pad/inner.js b/www/pad/inner.js index 5828ca842..4b37167e1 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -493,11 +493,6 @@ define([ var $link = $(link); $inner.append(link); - console.log($inner[0].getBoundingClientRect()); - console.log(rect); - console.log($link.width(), $link.outerWidth()); - console.log($inner.width()); - console.log(l, t); if (rect.left + $link.outerWidth() - rect0.left > $inner.width()) { $link.css('left', 'unset'); $link.css('right', 0); From 2031d535d7e5e99a70b6ac72a769665d180f86eb Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 14 Dec 2018 16:40:15 +0100 Subject: [PATCH 03/44] Cursor in pads (again) --- customize.dist/ckeditor-contents.css | 8 +++--- customize.dist/src/less2/include/cursor.less | 2 ++ www/pad/inner.js | 30 ++++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index fe4a3799f..e49cd16fd 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -158,18 +158,18 @@ a > img { border: 2px solid red; border-right-color: transparent !important; border-left-color: transparent !important; - margin-left: -2px; - margin-right: -2px; + margin-left: -3px; + margin-right: -3px; } .cp-cursor-position[data-type="start"] { border-left: none; border-right-width: 4px; - margin-right: -4px; + margin-right: -5px; } .cp-cursor-position[data-type="end"] { border-right: none; border-left-width: 4px; - margin-left: -4px; + margin-left: -5px; } .cp-cursor-avatar { display: flex; diff --git a/customize.dist/src/less2/include/cursor.less b/customize.dist/src/less2/include/cursor.less index e70e877a1..355cf4076 100644 --- a/customize.dist/src/less2/include/cursor.less +++ b/customize.dist/src/less2/include/cursor.less @@ -8,6 +8,8 @@ border: 2px solid red; border-right-color: transparent !important; border-left-color: transparent !important; + margin-left: -3px; + margin-right: -3px; } .cp-codemirror-selection { background-color: rgba(255,0,0,0.3); diff --git a/www/pad/inner.js b/www/pad/inner.js index 4b37167e1..00113469e 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -567,9 +567,16 @@ define([ var DD = new DiffDom(mkDiffOptions(cursor, framework.isReadOnly())); var cursorStopped = false; + var cursorTo; var updateCursor = function () { - if (cursorStopped) { return; } - framework.updateCursor(); + if (cursorTo) { clearTimeout(cursorTo); } + + // If we're receiving content + if (cursorStopped) { return void setTimeout(updateCursor, 100); } + + cursorTo = setTimeout(function () { + framework.updateCursor(); + }, 500); // 500ms to make sure it is sent after chainpad sync }; // apply patches, and try not to lose the cursor in the process! @@ -604,8 +611,10 @@ define([ var ops = ChainPad.Diff.diff(oldText, newText); cursor.restoreOffset(ops); - cursorStopped = false; - updateCursor(); + setTimeout(function () { + cursorStopped = false; + updateCursor(); + }, 200); // MEDIATAG: Migrate old mediatags to the widget system $inner.find('media-tag:not(.cke_widget_element)').each(function (i, el) { @@ -780,10 +789,10 @@ define([ }); /* Display the cursor of other users and send our cursor */ - //framework.setCursorGetter(cursors.cursorGetter); - //framework.onCursorUpdate(cursors.onCursorUpdate); - //inner.addEventListener('click', updateCursor); - //inner.addEventListener('keyup', updateCursor); + framework.setCursorGetter(cursors.cursorGetter); + framework.onCursorUpdate(cursors.onCursorUpdate); + inner.addEventListener('click', updateCursor); + inner.addEventListener('keyup', updateCursor); /* hitting enter makes a new line, but places the cursor inside @@ -806,7 +815,10 @@ define([ The solution is the "input" event, triggered by the browser as soon as the character is inserted. */ - inner.addEventListener('input', framework.localChange); + inner.addEventListener('input', function () { + framework.localChange(); + updateCursor(); + }); editor.on('change', framework.localChange); // export the typing tests to the window. From d76c21fcf50f5acb1f9306850143e0adc8c62de9 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 14 Dec 2018 16:50:25 +0100 Subject: [PATCH 04/44] Register link in the login page --- customize.dist/pages/login.js | 3 ++- customize.dist/src/less2/pages/page-login.less | 10 ++++++++++ www/login/main.js | 3 --- www/register/main.js | 4 +--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/customize.dist/pages/login.js b/customize.dist/pages/login.js index f7b7a43d8..8efdb1f56 100644 --- a/customize.dist/pages/login.js +++ b/customize.dist/pages/login.js @@ -30,7 +30,8 @@ define([ UI.createCheckbox('import-recent', Msg.register_importRecent), ]), h('div.extra', [ - h('button.login.first.btn', Msg.login_login) + h('button.login.first.btn', Msg.login_login), + h('button#register.first.btn', Msg.login_register) ]) ]) ]), diff --git a/customize.dist/src/less2/pages/page-login.less b/customize.dist/src/less2/pages/page-login.less index c92a08bde..e4b3e96e0 100644 --- a/customize.dist/src/less2/pages/page-login.less +++ b/customize.dist/src/less2/pages/page-login.less @@ -61,6 +61,16 @@ transform: scale(1.05); } } + #register { + border-color: @cryptpad_color_blue; + background: #fff; + color: @cryptpad_color_blue; + padding: 10px; + border-radius: 0; + &:hover { + transform: scale(1.05); + } + } } } .cp-container { diff --git a/www/login/main.js b/www/login/main.js index b69a94323..229225ea8 100644 --- a/www/login/main.js +++ b/www/login/main.js @@ -71,9 +71,6 @@ define([ if ($uname.val()) { sessionStorage.login_user = $uname.val(); } - if ($passwd.val()) { - sessionStorage.login_pass = $passwd.val(); - } } window.location.href = '/register/'; }); diff --git a/www/register/main.js b/www/register/main.js index 643fa2462..d93726f9d 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -38,11 +38,9 @@ define([ var $confirm = $('#password-confirm'); if (sessionStorage.login_user) { + delete sessionStorage.login_user; $uname.val(sessionStorage.login_user); } - if (sessionStorage.login_pass) { - $passwd.val(sessionStorage.login_pass); - } [ $uname, $passwd, $confirm] .some(function ($el) { if (!$el.val()) { $el.focus(); return true; } }); From 1e07d3a1570de47cffbbecb621fda9f891daec5e Mon Sep 17 00:00:00 2001 From: interfect Date: Sat, 15 Dec 2018 10:10:01 -0800 Subject: [PATCH 05/44] Persist all directories with Docker --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 22cc3d59e..fab74870a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,3 +21,8 @@ services: volumes: - ./data/files:/cryptpad/datastore:rw - ./data/customize:/cryptpad/customize:rw + - ./data/pins:/cryptpad/pins:rw + - ./data/blob:/cryptpad/blob:rw + - ./data/blobstage:/cryptpad/blobstage:rw + - ./data/tasks:/cryptpad/tasks:rw + - ./data/block:/cryptpad/block:rw From b817ac1511895c99171c5011a6384b21444a170c Mon Sep 17 00:00:00 2001 From: interfect Date: Sat, 15 Dec 2018 10:20:03 -0800 Subject: [PATCH 06/44] Improve Docker documentation in the repo Now it will link to the wiki too. --- docs/cryptpad-docker.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/cryptpad-docker.md b/docs/cryptpad-docker.md index 027792c2e..a41e2ad75 100644 --- a/docs/cryptpad-docker.md +++ b/docs/cryptpad-docker.md @@ -1,5 +1,11 @@ # Cryptpad Docker Image +Cryptpad includes support for building a Docker image and running it to provide a Cryptpad instance. You can manage the container manually, or let Docker Compose manage it for you. + +A full tutorial is available [on the Cryptpad Github wiki](https://github.com/xwiki-labs/cryptpad/wiki/Docker-(with-Nginx-and-Traefik)). This document provides a brief overview. + +## Features + - Configuration via .env file - Ready for use with traffic - Using github master for now, release 0.3.0 too old @@ -9,14 +15,22 @@ ## Run -Run from the cryptpad source directory: +Run from the cryptpad source directory, keeping instance state in `/var/cryptpad`: ``` docker build -t xwiki/cryptpad . -docker run --restart=always -d --name cryptpad -p 3000:3000 -v /var/cryptpad:/cryptpad/datastore xwiki/cryptpad +docker run --restart=always -d --name cryptpad -p 3000:3000 \ +-v /var/cryptpad/files:/cryptpad/datastore \ +-v /var/cryptpad/customize:/cryptpad/customize +-v /var/cryptpad/blob:/cryptpad/blob \ +-v /var/cryptpad/blobstage:/cryptpad/blobstage \ +-v /var/cryptpad/pins:/cryptpad/pins \ +-v /var/cryptpad/tasks:/cryptpad/tasks \ +-v /var/cryptpad/block:/cryptpad/block \ +xwiki/cryptpad ``` -Or, using docker-compose +Or, using docker-compose and the included `docker-compose.yml`, keeping instance state in the current directory under `./data`: ``` docker-compose up -d @@ -39,10 +53,15 @@ On runtime, in `bin/container-start.sh` the settings are written to the `config. The docker-compose file is preconfigured to persist folders -- cryptpad/datastore --> ./data/customize +- cryptpad/datastore --> ./data/files - cryptpad/customize --> ./data/customize +- cryptpad/pins --> ./data/pins +- cryptpad/blob --> ./data/blob +- cryptpad/blobstage --> ./data/blobstage +- cryptpad/tasks --> ./data/tasks +- cryptpad/block --> ./data/block -In customize included find your configuration in `config.js`. +Your configuration file will be in `./data/customize/config.js`. The data folder is ignored by git, so if you want to add your customizations to git versioning change the volume: From a11a883cff6e2c5e135c0e6b16e7e0ecbb152cb9 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 17 Dec 2018 11:44:08 +0100 Subject: [PATCH 07/44] Cursor improvements in pad, code and slide --- customize.dist/ckeditor-contents.css | 2 ++ www/code/inner.js | 14 ++++++++++---- www/common/sframe-common-codemirror.js | 6 ++++++ www/pad/cursor.js | 9 +++++++++ www/slide/inner.js | 14 ++++++++++---- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index e49cd16fd..4d2abae08 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -165,11 +165,13 @@ a > img { border-left: none; border-right-width: 4px; margin-right: -5px; + margin-left: -1px; } .cp-cursor-position[data-type="end"] { border-right: none; border-left-width: 4px; margin-left: -5px; + margin-right: -1px; } .cp-cursor-avatar { display: flex; diff --git a/www/code/inner.js b/www/code/inner.js index 36137a6f3..9e4d0a207 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -310,18 +310,24 @@ define([ }); framework.setContentGetter(function () { + CodeMirror.removeCursors(); var content = CodeMirror.getContent(); content.highlightMode = CodeMirror.highlightMode; previewPane.draw(); return content; }); + var cursorTo; + var updateCursor = function () { + if (cursorTo) { clearTimeout(cursorTo); } + if (editor._noCursorUpdate) { return; } + cursorTo = setTimeout(function () { + framework.updateCursor(); + }, 500); // 500ms to make sure it is sent after chainpad sync + }; framework.onCursorUpdate(CodeMirror.setRemoteCursor); framework.setCursorGetter(CodeMirror.getCursor); - editor.on('cursorActivity', function () { - if (editor._noCursorUpdate) { return; } - framework.updateCursor(); - }); + editor.on('cursorActivity', updateCursor); framework.onEditableChange(function () { editor.setOption('readOnly', framework.isLocked() || framework.isReadOnly()); diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index b7dfb531e..f72289623 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -402,6 +402,12 @@ define([ return html; }; var marks = {}; + exp.removeCursors = function () { + for (var id in marks) { + marks[id].clear(); + delete marks[id]; + } + }; exp.setRemoteCursor = function (data) { if (data.leave) { $('.cp-codemirror-cursor[id^='+data.id+']').each(function (i, el) { diff --git a/www/pad/cursor.js b/www/pad/cursor.js index ea3d7760c..491deb306 100644 --- a/www/pad/cursor.js +++ b/www/pad/cursor.js @@ -136,6 +136,15 @@ define([ end: cursorObj.selectionEnd }, ops); var cursorEl = makeCursor(id, cursorObj); + ['start', 'end'].forEach(function (t) { + // Prevent the cursor from creating a new line at the beginning + if (r[t].el.nodeName.toUpperCase() === 'BODY') { + if (!r[t].el.childNodes.length) { r[t] = null; return; } + r[t].el = r[t].el.childNodes[0]; + r[t].offset = 0; + } + }); + if (!r.start || !r.end) { return; } if (r.start.el === r.end.el && r.start.offset === r.end.offset) { // Cursor addCursorAtRange(cursorEl, r, cursorObj, ''); diff --git a/www/slide/inner.js b/www/slide/inner.js index 74254c7b5..6da8072c3 100644 --- a/www/slide/inner.js +++ b/www/slide/inner.js @@ -466,17 +466,23 @@ define([ }); framework.setContentGetter(function () { + CodeMirror.removeCursors(); var content = CodeMirror.getContent(); Slide.update(content.content); return content; }); + var cursorTo; + var updateCursor = function () { + if (cursorTo) { clearTimeout(cursorTo); } + if (editor._noCursorUpdate) { return; } + cursorTo = setTimeout(function () { + framework.updateCursor(); + }, 500); // 500ms to make sure it is sent after chainpad sync + }; framework.onCursorUpdate(CodeMirror.setRemoteCursor); framework.setCursorGetter(CodeMirror.getCursor); - editor.on('cursorActivity', function () { - if (editor._noCursorUpdate) { return; } - framework.updateCursor(); - }); + editor.on('cursorActivity', updateCursor); framework.onEditableChange(function () { editor.setOption('readOnly', framework.isLocked() || framework.isReadOnly()); From bbc06f668f51c793195f562358e135b12ab4f2d9 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 17 Dec 2018 15:49:48 +0100 Subject: [PATCH 08/44] Fix undefined hash for deleted shared folders --- www/common/outer/userObject.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js index a59b7c02a..58b870afb 100644 --- a/www/common/outer/userObject.js +++ b/www/common/outer/userObject.js @@ -681,8 +681,20 @@ define([ var sf = files[SHARED_FOLDERS]; var rootFiles = exp.getFiles([ROOT]); var root = exp.find([ROOT]); + var parsed, secret, el; for (var id in sf) { + el = sf[id]; id = Number(id); + + // Fix undefined hash + parsed = Hash.parsePadUrl(el.href || el.roHref); + secret = Hash.getSecrets('drive', parsed.hash, el.password); + if (!secret.keys) { + delete sf[id]; + continue; + } + + // Fix shared folder not displayed in root if (rootFiles.indexOf(id) === -1) { console.log('missing' + id); var newName = Hash.createChannelId(); From f2f9b575056923e3e041d5240630215485ca5aad Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 19 Dec 2018 18:59:20 +0100 Subject: [PATCH 09/44] Own drive migration --- www/common/outer/async-store.js | 6 ++-- www/common/translations/messages.js | 1 + www/settings/app-settings.less | 2 +- www/settings/inner.js | 52 ++++++++++++++++++++--------- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 32b02eb23..3192bad97 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -456,7 +456,8 @@ define([ edPublic: store.proxy.edPublic, friends: store.proxy.friends || {}, settings: store.proxy.settings, - thumbnails: disableThumbnails === false + thumbnails: disableThumbnails === false, + isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])) } }; cb(JSON.parse(JSON.stringify(metadata))); @@ -1523,8 +1524,9 @@ define([ if (!data.userHash) { returned.anonHash = Hash.getEditHashFromKeys(secret); } - }).on('ready', function () { + }).on('ready', function (info) { if (store.userObject) { return; } // the store is already ready, it is a reconnection + store.driveMetadata = info.metadata; if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; } var drive = rt.proxy.drive; // Creating a new anon drive: import anon pads from localStorage diff --git a/www/common/translations/messages.js b/www/common/translations/messages.js index 10b7ff4fd..b84e42606 100644 --- a/www/common/translations/messages.js +++ b/www/common/translations/messages.js @@ -667,6 +667,7 @@ define(function () { out.settings_ownDriveHint = "Migrating your drive to the new version will give you access to new features..."; // XXX out.settings_ownDriveButton = "Migrate"; // XXX out.settings_ownDriveConfirm = "Are you sure?"; // XXX + out.settings_ownDrivePending = "Your account is being migrated. Please do not close or reload this page until the process has completed."; // XXX out.settings_changePasswordTitle = "Change your password"; out.settings_changePasswordHint = "Change your account's password. Enter your current password, and confirm the new password by typing it twice.
" + diff --git a/www/settings/app-settings.less b/www/settings/app-settings.less index 9c7bf0a7f..0c21bfed0 100644 --- a/www/settings/app-settings.less +++ b/www/settings/app-settings.less @@ -154,7 +154,7 @@ } } - .cp-settings-change-password { + .cp-settings-change-password, .cp-settings-migrate { [type="password"], [type="text"] { width: @sidebar_button-width; flex: unset; diff --git a/www/settings/inner.js b/www/settings/inner.js index 321a85263..9537a990c 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -52,6 +52,7 @@ define([ 'cp-settings-autostore', 'cp-settings-userfeedback', 'cp-settings-change-password', + 'cp-settings-migrate', 'cp-settings-backup', 'cp-settings-delete' ], @@ -473,10 +474,7 @@ define([ }; create['migrate'] = function () { - if (true) { return; } // STUBBED until we have a reason to deploy this - // TODO - // if (!loginBlock) { return; } - // if (alreadyMigrated) { return; } + if (privateData.isDriveOwned) { return; } if (!common.isLoggedIn()) { return; } var $div = $('
', { 'class': 'cp-settings-migrate cp-sidebarlayout-element'}); @@ -489,25 +487,49 @@ define([ var $ok = $('', {'class': 'fa fa-check', title: Messages.saved}); var $spinner = $('', {'class': 'fa fa-spinner fa-pulse'}); - var $button = $('