From 073025b4e4643addbc3271edef9ca4b80a221311 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 8 Oct 2020 17:53:39 +0200 Subject: [PATCH 01/17] Import a template into a spreadsheet --- www/common/common-ui-elements.js | 1 + www/common/cryptpad-common.js | 1 + www/common/onlyoffice/inner.js | 133 ++++++++++++++++++++++++++---- www/common/sframe-common-outer.js | 8 ++ www/secureiframe/inner.js | 1 + 5 files changed, 129 insertions(+), 15 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 35ab93cda..1a78f3bc2 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -639,6 +639,7 @@ define([ button .click(common.prepareFeedback(type)) .click(function () { + if (callback) { return void callback(); } UIElements.openTemplatePicker(common, true); }); break; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 85de0b006..ddf63389c 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -638,6 +638,7 @@ define([ if (!val) { return void cb('ENOENT'); } + if (data.oo) { return void cb(val); } // OnlyOffice template: are handled in inner try { // Try to fix the title before importing the template var parsed = JSON.parse(val); diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index ea0e51bfc..92931411b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -415,7 +415,7 @@ define([ clearTimeout(pendingChanges[key]); delete pendingChanges[key]; }); - if (APP.stopHistory) { APP.history = false; } + if (APP.stopHistory || APP.template) { APP.history = false; } startOO(blob, type, true); }; @@ -425,14 +425,15 @@ define([ var file = getFileType(); blob.name = (metadataMgr.getMetadataLazy().title || file.doc) + '.' + file.type; var data = { - hash: APP.history ? ooChannel.historyLastHash : ooChannel.lastHash, - index: APP.history ? ooChannel.currentIndex : ooChannel.cpIndex + hash: (APP.history || APP.template) ? ooChannel.historyLastHash : ooChannel.lastHash, + index: (APP.history || APP.template) ? ooChannel.currentIndex : ooChannel.cpIndex }; fixSheets(); ooChannel.ready = false; ooChannel.queue = []; data.callback = function () { + if (APP.template) { APP.template = false; } resetData(blob, file); }; @@ -1295,6 +1296,13 @@ define([ } } + if (APP.template) { + getEditor().setViewModeDisconnect(); + UI.removeLoadingScreen(); + makeCheckpoint(true); + return; + } + if (APP.history) { try { getEditor().asc_setRestriction(true); @@ -1828,6 +1836,108 @@ define([ pinImages(); }; + var loadCp = function (cp, keepQueue) { + loadLastDocument(cp, function () { + var file = getFileType(); + var type = common.getMetadataMgr().getPrivateData().ooType; + var blob = loadInitDocument(type, true); + if (!keepQueue) { ooChannel.queue = []; } + resetData(blob, file); + }, function (blob, file) { + if (!keepQueue) { ooChannel.queue = []; } + resetData(blob, file); + }); + }; + + var loadTemplate = function (href, pw, parsed) { + APP.history = true; + APP.template = true; + getEditor().setViewModeDisconnect(); + var content = parsed.content; + + // Get checkpoint + var hashes = content.hashes || {}; + var idx = sortCpIndex(hashes); + var lastIndex = idx[idx.length - 1]; + var lastCp = hashes[lastIndex]; + + // Current cp or initial hash (invalid hash ==> initial hash) + var toHash = lastCp.hash || 'NONE'; + // Last hash + var fromHash = 'NONE'; + + sframeChan.query('Q_GET_HISTORY_RANGE', { + href: href, + password: pw, + channel: content.channel, + lastKnownHash: fromHash, + toHash: toHash, + }, function (err, data) { + if (err) { return void console.error(err); } + if (!Array.isArray(data.messages)) { return void console.error('Not an array!'); } + + // The first "cp" in history is the empty doc. It doesn't include the first patch + // of the history + var initialCp = !lastCp.hash; + + var messages = (data.messages || []).slice(initialCp ? 0 : 1); + + ooChannel.queue = messages.map(function (obj) { + return { + hash: obj.serverHash, + msg: JSON.parse(obj.msg) + }; + }); + ooChannel.historyLastHash = ooChannel.lastHash; + ooChannel.currentIndex = ooChannel.cpIndex; + console.error(ooChannel.historyLastHash); + loadCp(lastCp, true); + }); + }; + + var openTemplatePicker = function () { + var metadataMgr = common.getMetadataMgr(); + var type = metadataMgr.getPrivateData().app; + var sframeChan = common.getSframeChannel(); + var pickerCfgInit = { + types: [type], + where: ['template'], + hidden: true + }; + var pickerCfg = { + types: [type], + where: ['template'], + }; + var onConfirm = function () { + common.openFilePicker(pickerCfg, function (data) { + if (data.type !== type) { return; } + UI.addLoadingScreen({hideTips: true}); + sframeChan.query('Q_OO_TEMPLATE_USE', { + href: data.href, + }, function (err, val) { + var parsed; + try { + parsed = JSON.parse(val); + } catch (e) { + console.error(e, val); + UI.removeLoadingScreen(); + return void UI.warn(Messages.error); + } + console.error(data); + loadTemplate(data.href, data.password, parsed); + }); + }); + }; + sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) { + if (data) { + common.openFilePicker(pickerCfgInit); + onConfirm(); + } else { + UI.alert(Messages.template_empty); + } + }); + }; + config.onInit = function (info) { var privateData = metadataMgr.getPrivateData(); @@ -1849,6 +1959,7 @@ define([ Title.setToolbar(toolbar); if (window.CP_DEV_MODE) { + var $save = common.createButton('save', true, {}, function () { makeCheckpoint(true); }); @@ -1865,18 +1976,6 @@ define([ APP.stopHistory = true; makeCheckpoint(true); }; - var loadCp = function (cp, keepQueue) { - loadLastDocument(cp, function () { - var file = getFileType(); - var type = common.getMetadataMgr().getPrivateData().ooType; - var blob = loadInitDocument(type, true); - if (!keepQueue) { ooChannel.queue = []; } - resetData(blob, file); - }, function (blob, file) { - if (!keepQueue) { ooChannel.queue = []; } - resetData(blob, file); - }); - }; var onPatch = function (patch) { // Patch on the current cp ooChannel.send(JSON.parse(patch.msg)); @@ -1970,6 +2069,10 @@ define([ load: loadSnapshot }); toolbar.$drawer.append($snapshot); + + // Import template + var $template = common.createButton('importtemplate', true, {}, openTemplatePicker); + $template.appendTo(toolbar.$drawer); })(); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index bba393afb..b0feaee4f 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1102,6 +1102,10 @@ define([ nSecret = Utils.Hash.getSecrets('drive', hash, password); } } + if (data.href) { + var _parsed = Utils.Hash.parsePadUrl(data.href); + nSecret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, data.password); + } var channel = nSecret.channel; var validate = nSecret.keys.validateKey; var crypto = Crypto.createEncryptor(nSecret.keys); @@ -1282,6 +1286,10 @@ define([ sframeChan.on('Q_TEMPLATE_USE', function (data, cb) { Cryptpad.useTemplate(data, Cryptget, cb); }); + sframeChan.on('Q_OO_TEMPLATE_USE', function (data, cb) { + data.oo = true; + Cryptpad.useTemplate(data, Cryptget, cb); + }); sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) { Cryptpad.listTemplates(type, function (err, templates) { cb(templates.length > 0); diff --git a/www/secureiframe/inner.js b/www/secureiframe/inner.js index 344a4fb39..c20d53a80 100644 --- a/www/secureiframe/inner.js +++ b/www/secureiframe/inner.js @@ -124,6 +124,7 @@ define([ } sframeChan.event("EV_SECURE_ACTION", { type: parsed.type, + password: data.password, href: data.url, name: data.name }); From a16b43b25c86e3139e4377e0865ace86a0721bde Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 15:24:32 +0200 Subject: [PATCH 02/17] Add templates for OnlyOffice in the PCS --- www/common/common-ui-elements.js | 2 +- www/common/onlyoffice/inner.js | 22 +++++++++++++++++----- www/common/sframe-common-outer.js | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 1a78f3bc2..882153c99 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2094,7 +2094,7 @@ define([ var sframeChan = common.getSframeChannel(); var metadataMgr = common.getMetadataMgr(); var privateData = metadataMgr.getPrivateData(); - var type = metadataMgr.getMetadataLazy().type; + var type = metadataMgr.getMetadataLazy().type || privateData.app; var fromFileData = privateData.fromFileData; var $body = $('body'); diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 92931411b..14b03935b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -113,6 +113,7 @@ define([ }; var getEditor = function () { + if (!window.frames || !window.frames[0]) { return; } return window.frames[0].editor || window.frames[0].editorCell; }; @@ -1852,14 +1853,15 @@ define([ var loadTemplate = function (href, pw, parsed) { APP.history = true; APP.template = true; - getEditor().setViewModeDisconnect(); + var editor = getEditor(); + if (editor) { editor.setViewModeDisconnect(); } var content = parsed.content; // Get checkpoint var hashes = content.hashes || {}; var idx = sortCpIndex(hashes); var lastIndex = idx[idx.length - 1]; - var lastCp = hashes[lastIndex]; + var lastCp = hashes[lastIndex] || {}; // Current cp or initial hash (invalid hash ==> initial hash) var toHash = lastCp.hash || 'NONE'; @@ -2231,11 +2233,18 @@ define([ openRtChannel(function () { setMyId(); oldHashes = JSON.parse(JSON.stringify(content.hashes)); - loadDocument(newDoc, useNewDefault); initializing = false; + common.openPadChat(APP.onLocal); + + if (APP.startWithTemplate) { + var template = APP.startWithTemplate; + loadTemplate(template.href, template.password, template.content); + return; + } + + loadDocument(newDoc, useNewDefault); setEditable(!readOnly); UI.removeLoadingScreen(); - common.openPadChat(APP.onLocal); }); }; @@ -2359,8 +2368,11 @@ define([ })); SFCommon.create(waitFor(function (c) { APP.common = common = c; })); }).nThen(function (waitFor) { + common.getSframeChannel().on('EV_OO_TEMPLATE', function (data) { + APP.startWithTemplate = data; + }); common.handleNewFile(waitFor, { - noTemplates: true + //noTemplates: true }); }).nThen(function (/*waitFor*/) { andThen(common); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index b0feaee4f..bc2175f62 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1646,6 +1646,7 @@ define([ rtConfig.metadata.validateKey = (secret.keys && secret.keys.validateKey) || undefined; Utils.rtConfig = rtConfig; + var templatePw; nThen(function(waitFor) { if (data.templateId) { if (data.templateId === -1) { @@ -1654,11 +1655,34 @@ define([ } Cryptpad.getPadData(data.templateId, waitFor(function (err, d) { data.template = d.href; + templatePw = d.password; })); } }).nThen(function () { var cryptputCfg = $.extend(true, {}, rtConfig, {password: password}); if (data.template) { + // Start OO with a template... + // Cryptget and give href, password and content to inner + if (parsed.type === "sheet") { + var then = function () { + startRealtime(rtConfig); + cb(); + }; + var _parsed = Utils.Hash.parsePadUrl(data.template); + Cryptget.get(_parsed.hash, function (err, val) { + if (err || !val) { return void then(); } + try { + var parsed = JSON.parse(val); + sframeChan.event('EV_OO_TEMPLATE', { + href: data.template, + password: templatePw, + content: parsed + }); + } catch (e) { console.error(e); } + then(); + }, {password: templatePw}); + return; + } // Pass rtConfig to useTemplate because Cryptput will create the file and // we need to have the owners and expiration time in the first line on the // server From 7588dcd631f6561073f0011e14128769da17ed0f Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 15:51:57 +0200 Subject: [PATCH 03/17] Remove dev log --- www/common/onlyoffice/inner.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 14b03935b..eca46476b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1892,7 +1892,6 @@ define([ }); ooChannel.historyLastHash = ooChannel.lastHash; ooChannel.currentIndex = ooChannel.cpIndex; - console.error(ooChannel.historyLastHash); loadCp(lastCp, true); }); }; From 4c206ec10181e14cfd9c5c912e2d6d41ab712ec5 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:15:41 +0530 Subject: [PATCH 04/17] optimize Util.throttle for extremely high call frequencies --- www/common/common-util.js | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index 0e86ecc8c..eba7ad5f9 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -306,11 +306,44 @@ }; Util.throttle = function (f, ms) { + var last = 0; var to; + var args; + + var defer = function (delay) { + console.log("setTimeout(stuff, %s)", delay); + // no timeout: run function `f` in `ms` milliseconds + // unless `g` is called again in the meantime + to = setTimeout(function () { + // wipe the current timeout handler + to = undefined; + + // take the current time + var now = +new Date(); + // compute time passed since `last` + var diff = now - last; + if (diff < ms) { + // don't run `f` if `g` was called since this timeout was set + // instead calculate how much further in the future your next + // timeout should be scheduled + return void defer(ms - diff); + } + + // else run `f` with the most recently supplied arguments + f.apply(null, args); + }, ms); + }; + var g = function () { - clearTimeout(to); - to = setTimeout(Util.bake(f, Util.slice(arguments)), ms); + // every time you call this function store the time + last = +new Date(); + // remember what arguments were passed + args = Util.slice(arguments); + // if there is a pending timeout then do nothing + if (to) { return; } + defer(ms); }; + g.clear = function () { clearTimeout(to); to = undefined; From d1e263f0e8de42f87f48891e5e44ca0061920b46 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:20:24 +0530 Subject: [PATCH 05/17] remove annoying console.log --- www/common/common-util.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index eba7ad5f9..e781bc2eb 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -311,7 +311,6 @@ var args; var defer = function (delay) { - console.log("setTimeout(stuff, %s)", delay); // no timeout: run function `f` in `ms` milliseconds // unless `g` is called again in the meantime to = setTimeout(function () { From 08d889b8a470a59ede4371a341a777f7240b721f Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:35:29 +0530 Subject: [PATCH 06/17] fix an incorrect reference and add a small script to help profile timer accuracy --- scripts/tests/throttle-test.js | 34 ++++++++++++++++++++++++++++++++++ www/common/common-util.js | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 scripts/tests/throttle-test.js diff --git a/scripts/tests/throttle-test.js b/scripts/tests/throttle-test.js new file mode 100644 index 000000000..e84fc4a8a --- /dev/null +++ b/scripts/tests/throttle-test.js @@ -0,0 +1,34 @@ +var Util = require("../../lib/common-util"); + +(function (throttle) { + var last = 0; + var last_call = 0; + var f = Util.throttle(function (boop) { + var now = +new Date(); + if (last) { + console.log("last execution was %sms ago", now - last); + } else { + console.log("this is the first execution"); + } + last = now; + + //console.log('time of execution:', now); + console.log(boop); + }, 1000); + + [150, 250, 580, 850, 1500, 2200, 3990, 5000].forEach(function (delay) { + setTimeout(function () { + var now = +new Date(); + + if (last_call) { + console.log("last call was %sms ago", now - last_call); + } + + last_call = now; + //console.log("time of call for delay(%s):", delay, now); + f(delay); + }, delay); + }); +}(Util.throttle2)); + + diff --git a/www/common/common-util.js b/www/common/common-util.js index e781bc2eb..41797e7c8 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -330,7 +330,7 @@ // else run `f` with the most recently supplied arguments f.apply(null, args); - }, ms); + }, delay); }; var g = function () { From d2dce35f9b9ac2cf808ceb3c50400653063e785a Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 12:35:44 +0530 Subject: [PATCH 07/17] update changelog and version string for 3.23.2 (XerusDaamsi reloaded) --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ customize.dist/pages.js | 2 +- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30661d523..9f649883d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +# XerusDaamsi reloaded (3.23.2) + +A number of instance administrators reported issues following our 3.23.1 release. We suspect the issues were caused by applying the recommended update steps out of order which would result in the incorrect HTTP header values getting cached for the most recent version of a file. Since the most recently updated headers modified some security settings, this caused a catastrophic error on clients receiving the incorrect headers which caused them to fail to load under certain circumstances. + +Regardless of the reasons behind this, we want CryptPad to be resilient against misconfiguration. This minor release includes a number of measures to override the unruly caching mechanisms employed internally by two of our most stubborn dependencies (CKEditor and OnlyOffice). Deploying 3.23.2 should force these editors to load the most recent versions of these dependencies according to the same policies as the rest of CryptPad and instruct clients to ignore any incorrect server responses they might have cached over the last few updates. + +This release also includes a number of bug fixes which had been tested in the meantime. + +Other bug fixes + +* We removed a hardcoded translation pertaining to the recently introduced "snapshot" functionality. +* Inspection of our server logs revealed a number of rare race conditions and type errors that have since been addressed. These included: + * multiple invocations of a callback when iterating over the list of all encrypted blobs + * a type error when recovering from the crash of one of the database worker processes + * premature closure of filesystem read-streams due to a timeout when the server was under heavy load +* A thorough review of our teams functionality revealed the possibility of some similarly rare issues that have since been corrected: + * it was possible to click the buttons on the "team invitation response dialog" multiple times before the first action completed. In some cases this could result in attempting to join a single team multiple times. + * it was also possible to activate trigger several actions that would modify your access rights for a team when the team had not fully synchronized with the server. Some of the time this was recoverable, but it could occasionally result in your team membership getting stuck in a bad state. + +We've implemented some measures to correct any team data that might have become corrupted due to the issues described above. Access rights from duplicated teams should be merged back into one set of cryptographic keys wherever possible. In cases where this isn't possible your role in the team will be automatically downgraded to the rank conferred by the keys you still have. For instance, somebody listed as an administrator who only has the keys required to view the team will downgrade themself to be a viewer. Subsequent promotions back to your previous team role should restore your possession of the required keys. + +To update to 3.23.2 from 3.23.0 or 3.23.1: + +Perform the same upgrade steps listed for 3.23.0 including the most recent configuration changes listed in `cryptpad/docs/example.nginx.conf... + +1. Modify your server's NGINX config file (but don't apply its changes until step 6) +2. Stop CryptPad's nodejs server +3. Get the latest platform code with git +4. Install client-side dependencies with `bower update` +5. Install server-side dependencies with `npm install` +6. Reload NGINX with `service nginx reload` to apply its config changes +7. Restart the CryptPad API server + # XerusDaamsi's revenge (3.23.1) We discovered a number of minor bugs after deploying 3.23.0. This minor release addresses them. diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 2bd67b906..1db764bcc 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -62,7 +62,7 @@ define([ var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ? '/imprint.html' : AppConfig.imprint); - Pages.versionString = "CryptPad v3.23.1 (XerusDaamsi's revenge)"; + Pages.versionString = "CryptPad v3.23.2 (XerusDaamsi reloaded)"; // used for the about menu Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined; diff --git a/package-lock.json b/package-lock.json index 40d00411f..a9ee6e5e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "3.23.1", + "version": "3.23.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6f4fa275e..a849b2c2d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "3.23.1", + "version": "3.23.2", "license": "AGPL-3.0+", "repository": { "type": "git", From d25d9c4f83570d3da60ad85f7ef80cc74373d195 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 26 Oct 2020 14:57:54 +0100 Subject: [PATCH 08/17] Fix UI issues when printing code --- customize.dist/src/less2/include/app-print.less | 9 +++++---- customize.dist/src/print-landscape.css | 5 +++++ customize.dist/src/print.css | 4 ++++ www/code/app-code.less | 1 - www/code/inner.js | 1 + www/pad/inner.html | 2 +- www/pad/inner.js | 7 +++++++ www/slide/inner.js | 1 + 8 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 customize.dist/src/print-landscape.css create mode 100644 customize.dist/src/print.css diff --git a/customize.dist/src/less2/include/app-print.less b/customize.dist/src/less2/include/app-print.less index e4c344b20..7f42f100b 100644 --- a/customize.dist/src/less2/include/app-print.less +++ b/customize.dist/src/less2/include/app-print.less @@ -9,10 +9,6 @@ max-height: none; overflow: visible; display: block; - @page { - margin: 0; - size: landscape; - } // Slide app body.cp-app-slide { display: block; @@ -48,11 +44,15 @@ // Code app body.cp-app-code { display: block; + height: auto; * { visibility: hidden; height: auto; max-height: none; } + .cp-toolbar-userlist-drawer { + display: none; + } #cme_toolbox { display: none; } @@ -64,6 +64,7 @@ #cp-app-code-preview { display: block; #cp-app-code-print { + font-size: 20px; display: block; overflow: visible !important; width: 100%; diff --git a/customize.dist/src/print-landscape.css b/customize.dist/src/print-landscape.css new file mode 100644 index 000000000..26a9d495e --- /dev/null +++ b/customize.dist/src/print-landscape.css @@ -0,0 +1,5 @@ +@page { + margin: 0; + size: A4 landscape; +} + diff --git a/customize.dist/src/print.css b/customize.dist/src/print.css new file mode 100644 index 000000000..1baf803d4 --- /dev/null +++ b/customize.dist/src/print.css @@ -0,0 +1,4 @@ +@page { + margin: 3cm; + size: A4 portrait; +} diff --git a/www/code/app-code.less b/www/code/app-code.less index 0ca86fd8b..6557d39f6 100644 --- a/www/code/app-code.less +++ b/www/code/app-code.less @@ -136,7 +136,6 @@ #cp-app-code-print { position: relative; display: none; - margin: 50px; .markdown_preformatted-code; .markdown_gfm-table(black); } diff --git a/www/code/inner.js b/www/code/inner.js index 1956ab3f4..68b58bce4 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -42,6 +42,7 @@ define([ 'cm/addon/fold/comment-fold', 'cm/addon/display/placeholder', + 'css!/customize/src/print.css', 'less!/code/app-code.less' ], function ( diff --git a/www/pad/inner.html b/www/pad/inner.html index b7ccd4d00..e4dbcdf95 100644 --- a/www/pad/inner.html +++ b/www/pad/inner.html @@ -1,5 +1,5 @@ - + diff --git a/www/pad/inner.js b/www/pad/inner.js index 19c2063d8..eeabbb337 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -47,6 +47,7 @@ define([ '/bower_components/diff-dom/diffDOM.js', + 'css!/customize/src/print.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/pad/app-pad.less' @@ -500,6 +501,12 @@ define([ var mkPrintButton = function (framework, editor) { var $printButton = framework._.sfCommon.createButton('print', true); $printButton.click(function () { + /* + // NOTE: alternative print system in case we keep having more issues on Firefox + var $iframe = $('html').find('iframe'); + var iframe = $iframe[0].contentWindow; + iframe.print(); + */ editor.execCommand('print'); framework.feedback('PRINT_PAD'); }); diff --git a/www/slide/inner.js b/www/slide/inner.js index 3af7185c5..de30eb9fc 100644 --- a/www/slide/inner.js +++ b/www/slide/inner.js @@ -15,6 +15,7 @@ define([ 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', + 'css!/customize/src/print-landscape.css', 'less!/slide/app-slide.less', 'css!cm/lib/codemirror.css', From 3f9c2bc96048f5a0faae2fbd2fc47140962e563a Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:09 +0100 Subject: [PATCH 09/17] Translated using Weblate (French) Currently translated at 100.0% (1369 of 1369 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index ce19c96ad..7d8a84efb 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1459,5 +1459,11 @@ "admin_limitUser": "Clé publique de l'utilisateur", "fm_shareFolderPassword": "Protéger ce dossier avec un mot de passe (optionnel)", "access_destroyPad": "Détruire ce document ou dossier définitivement", - "fm_deletedFolder": "Dossier supprimé" + "fm_deletedFolder": "Dossier supprimé", + "loading_state_5": "Reconstruction du document", + "loading_state_4": "Chargement des équipes", + "loading_state_3": "Chargement des dossiers partagés", + "loading_state_2": "Mise à jour du contenu", + "loading_state_1": "Chargement du drive", + "loading_state_0": "Construction de l'interface" } From 2940fa6a0e793b395272f680d20f3f8b16144fec Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:09 +0100 Subject: [PATCH 10/17] Translated using Weblate (English) Currently translated at 100.0% (1369 of 1369 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1368 of 1368 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1367 of 1367 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1366 of 1366 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1365 of 1365 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1364 of 1364 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 010287444..e3b84adb7 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1459,5 +1459,11 @@ "admin_limitUser": "User's public key", "fm_deletedFolder": "Deleted folder", "access_destroyPad": "Destroy this document or folder permanently", - "fm_shareFolderPassword": "Protect this folder with a password (optional)" + "fm_shareFolderPassword": "Protect this folder with a password (optional)", + "loading_state_0": "Build interface", + "loading_state_1": "Load drive", + "loading_state_2": "Update content", + "loading_state_3": "Load shared folders", + "loading_state_4": "Load Teams", + "loading_state_5": "Reconstruct document" } From 14f785aa5eab28668e8cbcfdc6478e3b612b8de2 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:10 +0100 Subject: [PATCH 11/17] Translated using Weblate (Finnish) Currently translated at 100.0% (1363 of 1363 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fi/ --- www/common/translations/messages.fi.json | 69 +++++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/www/common/translations/messages.fi.json b/www/common/translations/messages.fi.json index 3b5835ff7..fe4ee1f64 100644 --- a/www/common/translations/messages.fi.json +++ b/www/common/translations/messages.fi.json @@ -188,12 +188,12 @@ "help_button": "Ohje", "historyText": "Historia", "historyButton": "Näytä asiakirjan historia", - "history_next": "Uudempi versio", - "history_prev": "Vanhempi versio", + "history_next": "Seuraava versio", + "history_prev": "Edellinen versio", "history_loadMore": "Lataa lisää historiatietoja", "history_closeTitle": "Sulje historia", "history_restoreTitle": "Palauta asiakirjan valittu versio", - "history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan nykyisen version esitetyllä versiolla?", + "history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan tämänhetkisen version näytetyllä versiolla?", "history_restoreDone": "Asiakirja palautettu", "history_version": "Versio:", "openLinkInNewTab": "Avaa linkki uuteen välilehteen", @@ -452,7 +452,7 @@ "settings_backupHint": "Varmuuskopioi tai palauta CryptDrivesi sisältö kokonaisuudessaan. Varmuuskopio ei sisällä padiesi sisältöä, ainoastaan niiden käyttöön tarvittavat avaimet.", "settings_backup": "Varmuuskopioi", "settings_restore": "Palauta", - "settings_backupHint2": "Lataa kaikkien padiesi nykyinen sisältö. Padit ladataan luettavassa tiedostomuodossa, jos sellainen on saatavilla.", + "settings_backupHint2": "Lataa kaikkien asiakirjojen nykyinen sisältö tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.", "settings_backup2": "Lataa oma CryptDrive tietokoneellesi", "settings_backup2Confirm": "Tämä lataa kaikki CryptDrivesi padit ja tiedostot tietokoneellesi. Jos haluat jatkaa, valitse nimi ja paina OK", "settings_exportTitle": "Vie oma CryptDrive", @@ -1176,9 +1176,9 @@ "settings_safeLinksTitle": "Turvalliset linkit", "settings_cat_security": "Luottamuksellisuus", "imprint": "Oikeudellinen huomautus", - "oo_sheetMigration_anonymousEditor": "Taulukon muokkaaminen on poistettu käytöstä anonyymeille käyttäjille, kunnes rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.", + "oo_sheetMigration_anonymousEditor": "Rekisteröitymättömät käyttäjät eivät voi muokata taulukkoa ennen kuin rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.", "oo_sheetMigration_complete": "Päivitetty versio saatavilla, paina OK ladataksesi uudelleen.", - "oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon", + "oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon. Ole hyvä ja odota noin 1 minuutti.", "oo_exportInProgress": "Vienti menossa", "oo_importInProgress": "Tuonti menossa", "oo_invalidFormat": "Tätä tiedostoa ei voida tuoda", @@ -1404,5 +1404,60 @@ "readme_cat1_l2": "Avaa padeja CryptDrivestasi: kaksoisnapsauta padin kuvaketta avataksesi sen.", "readme_cat1_l1": "Luo padi: Siirry CryptDriveesi, napsauta {0} ja sitten {1}, ja voit luoda padin.", "readme_cat1": "Tutustu CryptDriveesi", - "readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa." + "readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa.", + "fm_shareFolderPassword": "Suojaa kansio salasanalla (vapaaehtoinen)", + "access_destroyPad": "Tuhoa tämä asiakirja tai kansio lopullisesti", + "fm_deletedFolder": "Poistettu kansio", + "admin_limitUser": "Käyttäjän julkinen avain", + "team_exportButton": "Lataa", + "team_exportHint": "Lataa kaikki tiimin CryptDriveen tallennetut asiakirjat tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.", + "team_exportTitle": "Lataa tiimin CryptDrive", + "admin_cat_quota": "Käyttäjätallennustila", + "admin_invalLimit": "Virheellinen arvo kiintiökentässä", + "admin_invalKey": "Virheellinen julkinen avain", + "admin_limitSetNote": "Huomautus", + "admin_limitMB": "Kiintiö (megatavuissa)", + "admin_setlimitTitle": "Ota mukautettu kiintiö käyttöön", + "admin_setlimitHint": "Aseta mukautettu tallennustilakiintiö käyttäjälle tämän julkisen avaimen avulla. Voit päivittää tai poistaa olemassaolevan tallennustilakiintiön.", + "admin_limitNote": "Huomautus: {0}", + "admin_limitPlan": "Tilaus: {0}", + "admin_defaultlimitHint": "Tallennustilakiintiö käyttäjien ja tiimien CryptDriveille, joilla ei ole erikseen määriteltyjä sääntöjä", + "admin_defaultlimitTitle": "Tallennustilakiintiö (Mt)", + "admin_getlimitsHint": "Listaa muokatut tallennustilakiintiöt, jotka ovat käytössä CryptPad-palvelimellasi.", + "admin_getlimitsTitle": "Muokatut tallennustilakiintiöt", + "admin_limit": "Nykyinen rajoitus: {0}", + "admin_setlimitButton": "Aseta rajoitus", + "admin_registrationAllow": "Avaa", + "admin_registrationButton": "Sulje", + "admin_registrationTitle": "Sulje rekisteröityminen", + "admin_registrationHint": "Älä salli uusien käyttäjien rekisteröitymistä", + "snapshots_cantMake": "Tilannevedoksen luominen epäonnistui. Yhteytesi on katkennut.", + "snapshots_notFound": "Tämä tilannevedos ei ole enää saatavilla, koska asiakirjan historia on poistettu.", + "snapshot_error_exists": "Tästä versiosta on jo olemassa tilannevedos", + "snapshots_ooPickVersion": "Valitse versio, jotta voit luoda tilannevedoksen", + "oo_version": "Versio: ", + "oo_version_latest": "Viimeisin", + "snapshots_delete": "Poista", + "oo_deletedVersion": "Tämä versio ei ole enää saatavilla historiassa.", + "snapshots_close": "Sulje", + "snapshots_restore": "Palauta", + "snapshots_open": "Avaa", + "snapshots_placeholder": "Tilannevedoksen otsikko", + "snapshots_new": "Uusi tilannevedos", + "snapshots_button": "Tilannevedokset", + "snaphot_title": "Tilannevedos", + "infobar_versionHash": "Katselet asiakirjan vanhaa versiota ({0}).", + "history_restoreDriveDone": "CryptDrive palautettu", + "history_restoreDrivePrompt": "Oletko varma, että haluat korvata CryptDriven nykyisen version valitulla versiolla?", + "history_restoreDriveTitle": "Palauta CryptDrive valitsemaasi versioon", + "history_userNext": "Seuraava laatija", + "history_fastNext": "Seuraava muokkaussessio", + "history_userPrev": "Edellinen laatija", + "history_fastPrev": "Edellinen muokkaussessio", + "share_versionHash": "Olet jakamassa valitsemasi historiaversion asiakirjastasi vain luku-tilassa. Tämä antaa myös katseluoikeuden asiakirjan kaikkiin versioihin.", + "history_shareTitle": "Jaa linkki tähän versioon", + "history_cantRestore": "Palauttaminen epäonnistui. Yhteytesi on katkennut.", + "history_close": "Sulje", + "history_restore": "Palauta", + "share_bar": "Luo linkki" } From a3bb622df64ccea358debfe2c0ad1c76ec75004a Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:26:23 +0100 Subject: [PATCH 12/17] Fix loading screen error --- customize.dist/loading.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 56e9a4cab..525f58e45 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -333,6 +333,8 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } try { + var node = document.querySelector('.cp-loading-progress'); + if (node.parentNode) { node.parentNode.removeChild(node); } document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); document.querySelector('#cp-loading-message').innerText = err; From db1973131f280fdefa4ede71fa83aba4736f4359 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 13:44:51 +0530 Subject: [PATCH 13/17] remove hardcoded translations --- customize.dist/loading.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 525f58e45..4f8b79125 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -278,14 +278,7 @@ button.primary:hover{ ].join(''); var built = false; - // XXX var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end']; - Messages.loading_state_0 = "Less"; - Messages.loading_state_1 = "Drive"; - Messages.loading_state_2 = "Migrate"; - Messages.loading_state_3 = "SF"; - Messages.loading_state_4 = "Team"; - Messages.loading_state_5 = "Pad"; var current; var makeList = function (data) { var c = types.indexOf(data.type); From e7b06ee0c9466c8dbe24805c57022e6b45bd9303 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 14:06:48 +0530 Subject: [PATCH 14/17] remove unnecessary test comparing throttle implementations --- scripts/tests/throttle-test.js | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 scripts/tests/throttle-test.js diff --git a/scripts/tests/throttle-test.js b/scripts/tests/throttle-test.js deleted file mode 100644 index e84fc4a8a..000000000 --- a/scripts/tests/throttle-test.js +++ /dev/null @@ -1,34 +0,0 @@ -var Util = require("../../lib/common-util"); - -(function (throttle) { - var last = 0; - var last_call = 0; - var f = Util.throttle(function (boop) { - var now = +new Date(); - if (last) { - console.log("last execution was %sms ago", now - last); - } else { - console.log("this is the first execution"); - } - last = now; - - //console.log('time of execution:', now); - console.log(boop); - }, 1000); - - [150, 250, 580, 850, 1500, 2200, 3990, 5000].forEach(function (delay) { - setTimeout(function () { - var now = +new Date(); - - if (last_call) { - console.log("last call was %sms ago", now - last_call); - } - - last_call = now; - //console.log("time of call for delay(%s):", delay, now); - f(delay); - }, delay); - }); -}(Util.throttle2)); - - From e995ea4c72fdbb84fd789862ff7e4e91aec02a1d Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 30 Oct 2020 12:31:44 +0100 Subject: [PATCH 15/17] Translated using Weblate (English) Currently translated at 100.0% (1371 of 1371 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1370 of 1370 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index e3b84adb7..fa96fc5a7 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1465,5 +1465,7 @@ "loading_state_2": "Update content", "loading_state_3": "Load shared folders", "loading_state_4": "Load Teams", - "loading_state_5": "Reconstruct document" + "loading_state_5": "Reconstruct document", + "tag_add": "Add", + "tag_edit": "Edit" } From 958f8becc2e9abd458f8687b793a6822d5195387 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 17:03:51 +0530 Subject: [PATCH 16/17] remove hardcoded translations and resolved notes --- www/common/common-interface.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/www/common/common-interface.js b/www/common/common-interface.js index d423b48ee..6d682afa7 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -281,12 +281,10 @@ define([ var $root = $t.parent(); - Messages.add = "Add"; // XXX - Messages.edit = "Edit"; // XXX var $input = $root.find('.token-input'); var $button = $(h('button.btn.btn-primary', [ h('i.fa.fa-plus'), - h('span', Messages.add) + h('span', Messages.tag_add) ])); @@ -311,7 +309,7 @@ define([ } $form.append($input); $form.append($button); - if (isEdit) { $button.find('span').text(Messages.edit); } + if (isEdit) { $button.find('span').text(Messages.tag_edit); } else { $button.find('span').text(Messages.add); } $container.append($form); $input.focus(); @@ -941,8 +939,6 @@ define([ $loading.css('display', ''); $loading.removeClass('cp-loading-hidden'); if (config.newProgress) { - // XXX re-add progress bar for step 6 after password prompt for PPP - // XXX also burn after reading var progress = h('div.cp-loading-progress', [ h('p.cp-loading-progress-list'), h('p.cp-loading-progress-container') From 86e20b7395d3e15926a21862e7b048b956fb5498 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 30 Oct 2020 15:05:28 +0100 Subject: [PATCH 17/17] Catch errors in outer too --- www/common/boot2.js | 3 +++ www/common/sframe-common-outer.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/www/common/boot2.js b/www/common/boot2.js index fc3968fb3..30f776a25 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -43,6 +43,9 @@ define([ console.error("Require.js threw a Script Error. This probably means you're missing a dependency for CryptPad.\nIt is recommended that the admin of this server runs `bower install && bower update` to get the latest code, then modify their cache version.\nBest of luck,\nThe CryptPad Developers"); return void console.log(); } + if (window.CryptPad_loadingError) { + window.CryptPad_loadingError(e); + } throw e; }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 6114b5d1a..3a9d532a8 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -123,6 +123,9 @@ define([ }); SFrameChannel.create(msgEv, postMsg, waitFor(function (sfc) { Utils.sframeChan = sframeChan = sfc; + window.CryptPad_loadingError = function (e) { + sfc.event('EV_LOADING_ERROR', e) + }; })); }); window.addEventListener('message', whenReady);