diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20ec08a00..aed467811 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,47 @@
+# Pademelon release (v2.15.0)
+
+## Goals
+
+For this release we planned to improve upon last release's introduction of the display of other users' cursors in our code and slide editors by adding the same functionality to our rich text editor.
+
+Beyond just producing software, the CryptPad team has also begun to produce peer-reviewed papers.
+We have previously published [Private Document Editing with Some Trust](https://dl.acm.org/citation.cfm?doid=3209280.3209535) as a part of the 2018 proceedings of the ACM Symposium on Document Engineering.
+We have recently been accepted for publication as a part of [HCI-CPT](http://2019.hci.international/hci-cpt): the first international conference on HCI (Human Computer Interaction) for cybersecurity, privacy and trust.
+In preparation for this publication we've begun to collect additional usage data in order to inform the wider community of our findings regarding usability of cryptography-based collaboration systems.
+
+## Update notes
+
+* Updating to version 2.15.0 from 2.14.0 should only require that update to the latest clientside code via git, and update any cache-busting parameters you've set.
+* Several of our third-party clientside dependencies have been updated, and you may optionally run `bower update` to receive their latest versions.
+* As explained above, we have added a number of new keys to our existing feedback system. The new keys are detailed below
+ * HOME_SUPPORT_CRYPTPAD informs us when users discover our opencollective campaign from the CryptPad home page
+ * UPGRADE_ACCOUNT informs us when someone clicks the upgrade account button from their CryptDrive or settings page
+ * SUPPORT_CRYPTPAD is not active on our CryptPad instance, since this key is only sent when clicking the _donate button_ which is shown when upgraded accounts are disabled
+ * DELETE_ACCOUNT_AUTOMATIC informs us when somebody deletes their account automatically from the settings page. Automatic account deletion is only available for accounts created since version 1.29.0
+ * DELETE_ACCOUNT_MANUAL informs us when a user generates the proof of their account ownership which is required for manual account deletion. This feature is available only for accounts predating version 1.29.0
+ * OWNED_DRIVE_MIGRATION informs us when a user migrates their CryptDrive from our legacy format (which does not support automatic deletion) to our newer format (which does) via the settings page
+ * PASSWORD_CHANGED informs us when a user changes their password from the settings page
+ * NO_WEBRTC informs us when a users browser does not support WebRTC at all via a crude test which never actually runs any WebRTC-based code
+ * SUBSCRIPTION_BUTTON informs us when a user navigates to our paid account administration panel from their settings page
+ * LOGOUT_EVERYWHERE informs us when a user executes the command to log out of their account on all remote devices from the settings page
+* We've implemented the ability to configure which applications are available on a particular CryptPad instance via `cryptpad/customize/application_config.js`. Two arrays (`config.availablePadTypes` and `config.registeredOnlyTypes`) define which applications are available to everyone, and which applications are available to registered users. Due to a bug which was discovered, this behaviour is incorrect for our encrypted file viewer, and as a result encrypted files cannot currently be disabled. This will be addressed in our next release.
+
+## Features
+
+* Our rich text editor now displays other users' cursors when editing with a group. Preferences for this behaviour can be defined via the settings page.
+* Links in our rich text editor can now be clicked more easily, as a small tooltip with a clickable link will be displayed above the editable link in the document.
+* Users who wish to be notified of spelling errors in their rich text pads can enable spellcheck via the settings page.
+* As noted above, various pad types can be disabled by instance administrators via `customize/application_config.js`.
+* We've enabled a feature in the settings page which will migrate users' CryptDrive from our legacy format to our latest format (which supports automatic deletion). Only users with accounts dating back to version 1.29.0 will notice any difference.
+* We've worked to improve some usability issues presented by the interaction of _owned files_ and _shared folders_. Since only the owner of an owned document can delete it the owner must keep a record of that document in their CryptDrive even if they place it in a shared folder (where someone else could delete it while they are offline). As such, owned documents were always copied to shared folders instead of being moved, and this proliferation of copies made it more difficult for users to organize their CryptDrives. Duplicated owned documents which are kept in your CryptDrive can now be hidden via the settings page. If those files are removed from a shared folder by another user, the hidden duplicate will be revealed in the root of your CryptDrive's tree.
+* Finally, we've implemented the ability to copy documents to multiple shared folders via an entry in the right-click menu for any such document.
+
+## Bugfixes
+
+* We've improved the styles for displaying other users' cursors in the code and slide editors to avoid moving your view of the text when someone else highlights it.
+* We've also changed some of the logic for how often other users' cursors are updated and displayed, so as to maximize the accuracy of their position and not show incorrect placements while you are typing.
+* We fixed a bug which caused errors while loading your CryptDrive after a shared folder had been deleted.
+
# Opossum release (v2.14.0)
## Goals
diff --git a/bower.json b/bower.json
index 27b1ea1f4..94c6af3a7 100644
--- a/bower.json
+++ b/bower.json
@@ -36,7 +36,7 @@
"alertifyjs": "1.0.11",
"scrypt-async": "1.2.0",
"require-css": "0.1.10",
- "less": "^3.7.1",
+ "less": "3.7.1",
"bootstrap": "^v4.0.0",
"diff-dom": "2.1.1",
"nthen": "^0.1.5",
diff --git a/www/common/common-feedback.js b/www/common/common-feedback.js
index f0b7d989b..d377bae5c 100644
--- a/www/common/common-feedback.js
+++ b/www/common/common-feedback.js
@@ -22,6 +22,7 @@ define([
http.send();
};
Feedback.send = function (action, force, cb) {
+ if (typeof(cb) !== 'function') { cb = function () {}; }
if (AppConfig.disableFeedback) { return void cb(); }
if (!action) { return void cb(); }
if (force !== true) {
diff --git a/www/common/common-hash.js b/www/common/common-hash.js
index e089852cc..0f5341d9b 100644
--- a/www/common/common-hash.js
+++ b/www/common/common-hash.js
@@ -76,9 +76,10 @@ define([
return s.replace(/\/+/g, '/');
};
- Hash.createChannelId = function () {
- var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
- if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
+ Hash.ephemeralChannelLength = 34;
+ Hash.createChannelId = function (ephemeral) {
+ var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(ephemeral? 17: 16));
+ if ([32, 34].indexOf(id.length) === -1 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters');
}
return id;
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 0b72dccbc..ab956ba74 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -1112,11 +1112,11 @@ define([
};
common.isWebRTCSupported = function () {
- return navigator.getUserMedia ||
+ return Boolean(navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia ||
- window.RTCPeerConnection;
+ window.RTCPeerConnection);
};
common.ready = (function () {
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index bbd2ba8f7..a329f5e33 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -958,12 +958,20 @@ define([
history: [],
pushHistory: function (msg, isCp) {
if (isCp) {
+ // the current message is a checkpoint.
+ // push it to your worker's history, prepending it with cp|
+ // cp| and anything else related to checkpoints has already
+ // been stripped by chainpad-netflux-worker or within async store
+ // when the message was outgoing.
channel.history.push('cp|' + msg);
+ // since the latest message is a checkpoint, we are able to drop
+ // some of the older history, but we can't rely on checkpoints being
+ // correct, as they might be checkpoints from different forks
var i;
- for (i = channel.history.length - 2; i > 0; i--) {
+ for (i = channel.history.length - 101; i > 0; i--) {
if (/^cp\|/.test(channel.history[i])) { break; }
}
- channel.history = channel.history.slice(i);
+ channel.history = channel.history.slice(Math.max(i, 0));
return;
}
channel.history.push(msg);
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index 468d724dd..bbbbe266b 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -197,7 +197,10 @@ define([
};
funcs.openCursorChannel = function (saveChanges) {
var md = JSON.parse(JSON.stringify(ctx.metadataMgr.getMetadata()));
- var channel = md.cursor || Hash.createChannelId();
+ var channel = md.cursor;
+ if (typeof(channel) !== 'string' || channel.length !== Hash.ephemeralChannelLength) {
+ channel = Hash.createChannelId(true); // true indicates that it's an ephemeral channel
+ }
if (!md.cursor) {
md.cursor = channel;
ctx.metadataMgr.updateMetadata(md);
diff --git a/www/pad/inner.js b/www/pad/inner.js
index 18cccfc6b..9b56e56bb 100644
--- a/www/pad/inner.js
+++ b/www/pad/inner.js
@@ -452,8 +452,8 @@ define([
var $iframe = $('html').find('iframe').contents();
var ifrWindow = $html.find('iframe')[0].contentWindow;
- var customCss = '/customize/ckeditor-contents.css?' + CKEDITOR.CRYPTPAD_URLARGS;
- $iframe.find('head').append('')
+ var customCss = '/customize/ckeditor-contents.css?' + window.CKEDITOR.CRYPTPAD_URLARGS;
+ $iframe.find('head').append('');
framework._.sfCommon.addShortcuts(ifrWindow);
diff --git a/www/settings/inner.js b/www/settings/inner.js
index 4b1836e64..ddc8c0745 100644
--- a/www/settings/inner.js
+++ b/www/settings/inner.js
@@ -930,6 +930,7 @@ define([
$(cancel).click(function () {
UI.confirm(Messages.settings_exportCancel, function (yes) {
if (!yes) { return; }
+ Feedback.send('FULL_DRIVE_EXPORT_CANCEL');
_onCancel.forEach(function (h) { h(); });
});
}).appendTo(actions);
@@ -1078,6 +1079,7 @@ define([
// Backup all the pads
var exportDrive = function () {
+ Feedback.send('FULL_DRIVE_EXPORT_START');
var todo = function (data, filename) {
var getPad = function (data, cb) {
sframeChan.query("Q_CRYPTGET", data, function (err, obj) {
@@ -1094,6 +1096,7 @@ define([
saveAs(blob, filename);
sframeChan.event('EV_CRYPTGET_DISCONNECT');
ui.complete(function () {
+ Feedback.send('FULL_DRIVE_EXPORT_COMPLETE');
saveAs(blob, filename);
}, errors);
}, ui.update);