From e216f7debe55b4141f84fc854d932d74fe9daa9d Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 1 Nov 2020 17:42:01 +0100 Subject: [PATCH 01/51] Translated using Weblate (German) Currently translated at 100.0% (1371 of 1371 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ Translated using Weblate (German) Currently translated at 99.9% (1370 of 1371 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index 781c20b65..6b5046aa0 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1459,5 +1459,13 @@ "admin_setlimitHint": "Lege individuelle Begrenzungen für Benutzer anhand ihrer öffentlichen Schlüssel fest. Du kannst bestehende Regeln aktualisieren oder entfernen.", "access_destroyPad": "Dokument oder Ordner endgültig zerstören", "fm_shareFolderPassword": "Diesen Ordner mit einem Passwort schützen (optional)", - "fm_deletedFolder": "Gelöschter Ordner" + "fm_deletedFolder": "Gelöschter Ordner", + "tag_edit": "Ändern", + "tag_add": "Hinzufügen", + "loading_state_4": "Teams laden", + "loading_state_3": "Geteilte Ordner laden", + "loading_state_2": "Inhalte aktualisieren", + "loading_state_1": "Drive laden", + "loading_state_0": "Oberfläche vorbereiten", + "loading_state_5": "Dokument rekonstruieren" } From 174c71d130f75abe003bc8f1bbb22967da59727f Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 3 Nov 2020 11:49:42 +0100 Subject: [PATCH 02/51] Translated using Weblate (French) Currently translated at 100.0% (1371 of 1371 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 7d8a84efb..70909c44d 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1465,5 +1465,7 @@ "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" + "loading_state_0": "Construction de l'interface", + "tag_edit": "Modifier", + "tag_add": "Ajouter" } From 26ef547e8479702663d6a3fdcc823919ec72c5ea Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 8 Oct 2020 17:53:39 +0200 Subject: [PATCH 03/51] 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 8b9415220..e5cdf873a 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 696627cbe..d7c580f90 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -637,6 +637,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 103381f62..11dfb0189 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -416,7 +416,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); }; @@ -426,14 +426,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); }; @@ -1296,6 +1297,13 @@ define([ } } + if (APP.template) { + getEditor().setViewModeDisconnect(); + UI.removeLoadingScreen(); + makeCheckpoint(true); + return; + } + if (APP.history) { try { getEditor().asc_setRestriction(true); @@ -1829,6 +1837,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(); @@ -1850,6 +1960,7 @@ define([ Title.setToolbar(toolbar); if (window.CP_DEV_MODE) { + var $save = common.createButton('save', true, {}, function () { makeCheckpoint(true); }); @@ -1866,18 +1977,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)); @@ -1971,6 +2070,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 935df47018ff92671ffc3bf311ddbfcf128fb0bd Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 15:24:32 +0200 Subject: [PATCH 04/51] 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 e5cdf873a..a2f965113 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2095,7 +2095,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 11dfb0189..8caddb18a 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -114,6 +114,7 @@ define([ }; var getEditor = function () { + if (!window.frames || !window.frames[0]) { return; } return window.frames[0].editor || window.frames[0].editorCell; }; @@ -1853,14 +1854,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'; @@ -2232,11 +2234,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); }); }; @@ -2360,8 +2369,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 c1b9ca56850f8d0442888abba40e30ab78df6482 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 15:51:57 +0200 Subject: [PATCH 05/51] 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 8caddb18a..c265d2d2b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1893,7 +1893,6 @@ define([ }); ooChannel.historyLastHash = ooChannel.lastHash; ooChannel.currentIndex = ooChannel.cpIndex; - console.error(ooChannel.historyLastHash); loadCp(lastCp, true); }); }; From ef506eec008eadd662d4d78fc46e8110eb504a37 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 16:10:39 +0200 Subject: [PATCH 06/51] Add error message on JS or LESS blocking error --- customize.dist/loading.js | 5 +++++ www/common/LessLoader.js | 7 ++++++- www/common/sframe-boot2.js | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 32841f253..fe86bf266 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -260,6 +260,11 @@ button.primary:hover{ '

', '' ].join(''); + window.CryptPad_loadingError = function (err) { + 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; + }; return function () { var intr; var append = function () { diff --git a/www/common/LessLoader.js b/www/common/LessLoader.js index b463a126c..2c3de14cc 100644 --- a/www/common/LessLoader.js +++ b/www/common/LessLoader.js @@ -167,7 +167,12 @@ define([ if (css) { return void loadSubmodulesAndInject(css, url, done, stack); } console.debug('CACHE MISS ' + url); ((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) { - if (!css) { return void console.error(err); } + if (!css) { + if (window.CryptPad_loadingError) { + window.CryptPad_loadingError('LESS: ' + (err && err.message)); + } + return void console.error(err); + } var output = fixAllURLs(css, url); cachePut(url, output); loadSubmodulesAndInject(output, url, done, stack); diff --git a/www/common/sframe-boot2.js b/www/common/sframe-boot2.js index 0d0eaa90c..5a68237ad 100644 --- a/www/common/sframe-boot2.js +++ b/www/common/sframe-boot2.js @@ -42,6 +42,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; }; From 03a7e0e8eb9b4d446dfc414af219faef98758ef2 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 15:20:56 +0200 Subject: [PATCH 07/51] New loading screen --- customize.dist/loading.js | 57 ++++++++++++++++++++- customize.dist/messages.js | 13 +++-- www/common/LessLoader.js | 7 +++ www/common/common-interface.js | 6 +++ www/common/outer/async-store.js | 32 ++++++++---- www/common/outer/sharedfolder.js | 13 ++++- www/common/outer/team.js | 20 +++++++- www/common/sframe-app-framework.js | 9 +--- www/common/sframe-chainpad-netflux-inner.js | 2 +- www/common/sframe-common-outer.js | 8 +-- www/common/sframe-common.js | 3 +- 11 files changed, 131 insertions(+), 39 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index fe86bf266..dd269fa60 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -1,6 +1,6 @@ // dark #326599 // light #4591c4 -define([], function () { +define(['/customize/messages.js'], function (Messages) { var loadingStyle = (function(){/* #cp-loading { visibility: visible; @@ -199,6 +199,13 @@ p.cp-password-info{ white-space: nowrap; text-overflow: ellipsis; } +.cp-loading-progress-list li i { + width: 22px; +} +.cp-loading-progress-list li span{ + margin-left: 20px; +} + .cp-loading-progress-bar { height: 24px; background: white; @@ -257,9 +264,57 @@ button.primary:hover{ '
', '', '
', + '
', + '
', + '
', + '
', '

', '' ].join(''); + + // XXX + var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad']; + 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); + current = c; + var getLi = function (i) { + var check = (i < c || (i === c && data.progress === 100)) ? 'fa-check-square-o' + : 'fa-square-o'; + var percent = i < c ? '(100%)' : (i === c ? '('+Math.floor(data.progress)+'%)' : '(0%)'); + return '
  • '+Messages['loading_state_'+i]+'' + + ''+percent+''; + }; + var list = '
      '; + types.forEach(function (el, i) { + list += getLi(i); + }); + list += '
    '; + return list; + }; + var makeBar = function (data) { + var c = types.indexOf(data.type); + var l = types.length; + var p = (data.progress / l) + (100 * c / l); + var bar = '
    '+ + '
    '+ + '
    '; + return bar; + }; + + var updateLoadingProgress = function (data) { + var c = types.indexOf(data.type); + if (c < current) { return console.error(data); } + document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); + document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + }; + window.CryptPad_updateLoadingProgress = updateLoadingProgress; window.CryptPad_loadingError = function (err) { document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 11d814540..40dbbfb95 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -52,13 +52,12 @@ require.config({ }); var req = [ - '/common/common-util.js', '/customize/application_config.js', '/customize/translations/messages.js' ]; if (language && map[language]) { req.push('/customize/translations/messages.' + language + '.js'); } -define(req, function(Util, AppConfig, Default, Language) { +define(req, function(AppConfig, Default, Language) { map.en = 'English'; var defaultLanguage = 'en'; @@ -78,15 +77,15 @@ define(req, function(Util, AppConfig, Default, Language) { var extend = function (a, b) { for (var k in b) { - if (Util.isObject(b[k])) { - a[k] = Util.isObject(a[k]) ? a[k] : {}; - extend(a[k], b[k]); - continue; - } if (Array.isArray(b[k])) { a[k] = b[k].slice(); continue; } + if (b[k] && typeof(b[k]) === "object") { + a[k] = (a[k] && typeof(a[k]) === "object" && !Array.isArray(a[k])) ? a[k] : {}; + extend(a[k], b[k]); + continue; + } a[k] = b[k] || a[k]; } }; diff --git a/www/common/LessLoader.js b/www/common/LessLoader.js index 2c3de14cc..0a8a1e914 100644 --- a/www/common/LessLoader.js +++ b/www/common/LessLoader.js @@ -150,6 +150,7 @@ define([ }).nThen(function () { cb(); }); }; + var idx = 0; module.exports.load = function (url /*:string*/, cb /*:()=>void*/, stack /*:?Array*/) { var btime = stack ? null : +new Date(); stack = stack || []; @@ -163,6 +164,12 @@ define([ cb(); }; stack.push(url); + if (window.CryptPad_updateLoadingProgress) { + window.CryptPad_updateLoadingProgress({ + type: 'less', + progress: idx++ + }); + } cacheGet(url, function (css) { if (css) { return void loadSubmodulesAndInject(css, url, done, stack); } console.debug('CACHE MISS ' + url); diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 2e6c3a609..368d8f017 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -896,6 +896,11 @@ define([ } }; UI.updateLoadingProgress = function (data, isDrive) { + if (window.CryptPad_updateLoadingProgress) { + window.CryptPad_updateLoadingProgress(data); + } + // XXX + /* var $loading = $('#' + LOADING); if (!$loading.length || loading.error) { return; } $loading.find('.cp-loading-progress').show(); @@ -947,6 +952,7 @@ define([ } } } + */ }; UI.removeLoadingScreen = function (cb) { // Release the test blocker, hopefully every test has been registered. diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index e849ce20d..b2b3b5f68 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1373,11 +1373,15 @@ define([ } } }; - var loadUniversal = function (Module, type, waitFor) { + var loadUniversal = function (Module, type, waitFor, clientId) { if (store.modules[type]) { return; } store.modules[type] = Module.init({ Store: Store, store: store, + updateLoadingProgress: function (data) { + data.type = "team"; + postMessage(clientId, 'LOADING_DRIVE', data); + }, updateMetadata: function () { broadcast([], "UPDATE_METADATA"); }, @@ -2480,9 +2484,6 @@ define([ addSharedFolderHandler(); nThen(function (waitFor) { - postMessage(clientId, 'LOADING_DRIVE', { - state: 2 - }); userObject.migrate(waitFor()); }).nThen(function (waitFor) { initAnonRpc(null, null, waitFor()); @@ -2490,22 +2491,25 @@ define([ }).nThen(function (waitFor) { Migrate(proxy, waitFor(), function (version, progress) { postMessage(clientId, 'LOADING_DRIVE', { - state: (2 + (version / 10)), + type: 'migrate', progress: progress }); }, store); }).nThen(function (waitFor) { - postMessage(clientId, 'LOADING_DRIVE', { - state: 3 - }); userObject.fixFiles(); - SF.loadSharedFolders(Store, store.network, store, userObject, waitFor); + SF.loadSharedFolders(Store, store.network, store, userObject, waitFor, function (obj) { + var data = { + type: 'sf', + progress: 100*obj.progress/obj.max + }; + postMessage(clientId, 'LOADING_DRIVE', data); + }); loadCursor(); loadOnlyOffice(); loadUniversal(Messenger, 'messenger', waitFor); store.messenger = store.modules['messenger']; loadUniversal(Profile, 'profile', waitFor); - loadUniversal(Team, 'team', waitFor); + loadUniversal(Team, 'team', waitFor, clientId); loadUniversal(History, 'history', waitFor); cleanFriendRequests(); }).nThen(function () { @@ -2607,6 +2611,12 @@ define([ if (!hash) { return void cb({error: '[Store.init] Unable to find or create a drive hash. Aborting...'}); } + + var updateProgress = function (data) { + data.type = 'drive'; + postMessage(clientId, 'LOADING_DRIVE', data); + }; + // No password for drive var secret = Hash.getSecrets('drive', hash); store.driveChannel = secret.channel; @@ -2620,6 +2630,7 @@ define([ userName: 'fs', logLevel: 1, ChainPad: ChainPad, + updateProgress: updateProgress, classic: true, }; var rt = window.rt = Listmap.create(listmapConfig); @@ -2643,7 +2654,6 @@ define([ && !drive['filesData']) { drive[Constants.oldStorageKey] = []; } - postMessage(clientId, 'LOADING_DRIVE', { state: 1 }); // Drive already exist: return the existing drive, don't load data from legacy store onReady(clientId, returned, cb); }) diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index f02ea9c5a..e34cd64df 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -320,9 +320,12 @@ define([ - userObject: userObject associated to the main drive - handler: a function (sfid, rt) called for each shared folder loaded */ - SF.loadSharedFolders = function (Store, network, store, userObject, waitFor) { + SF.loadSharedFolders = function (Store, network, store, userObject, waitFor, progress) { var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; + var steps = Object.keys(shared).length; + var i = 1; var w = waitFor(); + progress = progress || function () {}; nThen(function (waitFor) { Object.keys(shared).forEach(function (id) { var sf = shared[id]; @@ -330,7 +333,13 @@ define([ network: network, store: store, isNewChannel: Store.isNewChannel - }, id, sf, waitFor()); + }, id, sf, waitFor(function () { + progress({ + progress: i, + max: steps + }); + i++; + })); }); }).nThen(function () { setTimeout(w); diff --git a/www/common/outer/team.js b/www/common/outer/team.js index e5a40505e..de9206511 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -328,7 +328,13 @@ define([ ctx.teams[id] = team; registerChangeEvents(ctx, team, proxy); SF.checkMigration(team.secondaryKey, proxy, team.userObject, waitFor()); - SF.loadSharedFolders(ctx.Store, ctx.store.network, team, team.userObject, waitFor); + SF.loadSharedFolders(ctx.Store, ctx.store.network, team, + team.userObject, waitFor, function (data) { + ctx.progress += 70/(ctx.numberOfTeams * data.max); + ctx.updateProgress({ + progress: ctx.progress + }); + }); }).nThen(function () { if (!team.rpc) { return; } var list = getTeamChannelList(ctx, id); @@ -361,6 +367,9 @@ define([ }; + // Progress: + // One team = (30/(#teams))% + // One shared folder = (70/(#teams * #folders))% var openChannel = function (ctx, teamData, id, _cb) { var cb = Util.once(Util.mkAsync(_cb)); @@ -526,6 +535,10 @@ define([ Feedback.send("TEAM_RIGHTS_OWNER"); } }).nThen(function () { + ctx.progress += 30/ctx.numberOfTeams; + ctx.updateProgress({ + progress: ctx.progress + }); onReady(ctx, id, lm, roster, keys, null, cb); }); }; @@ -1686,10 +1699,13 @@ define([ emit: emit, onReadyHandlers: {}, teams: {}, - updateMetadata: cfg.updateMetadata + updateMetadata: cfg.updateMetadata, + updateProgress: cfg.updateLoadingProgress, + progress: 0 }; var teams = store.proxy.teams = store.proxy.teams || {}; + ctx.numberOfTeams = Object.keys(teams).length; // Listen for changes in our access rights (if another worker receives edit access) ctx.store.proxy.on('change', ['teams'], function (o, n, p) { diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index beaf20b10..3097cf6d2 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -441,9 +441,9 @@ define([ var versionHashEl; var onInit = function () { UI.updateLoadingProgress({ - state: 2, + type: 'pad', progress: 0.1 - }, false); + }); stateChange(STATE.INITIALIZING); if ($('.cp-help-container').length) { var privateDat = cpNfInner.metadataMgr.getPrivateData(); @@ -471,8 +471,6 @@ define([ var newContentStr = cpNfInner.chainpad.getUserDoc(); if (state === STATE.DELETED) { return; } - UI.updateLoadingProgress({ state: -1 }, false); - if (toolbar) { // Check if we have a new chainpad instance toolbar.resetChainpad(cpNfInner.chainpad); @@ -708,9 +706,6 @@ define([ nThen(function (waitFor) { UI.addLoadingScreen(); SFCommon.create(waitFor(function (c) { common = c; })); - UI.updateLoadingProgress({ - state: 1 - }, false); }).nThen(function (waitFor) { common.getSframeChannel().onReady(waitFor()); }).nThen(function (waitFor) { diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index b6f4c6aaf..8e8bb84f2 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -140,7 +140,7 @@ define([ chainpad.message(content); if (isHistory && updateLoadingProgress) { updateLoadingProgress({ - state: 2, + type: 'pad', progress: isHistory }, false); isHistory++; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index bc2175f62..94dc8ab14 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -131,13 +131,7 @@ define([ if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); } }); - Cryptpad.ready(waitFor(function () { - if (sframeChan) { - sframeChan.event('EV_LOADING_INFO', { - state: -1 - }); - } - }), { + Cryptpad.ready(waitFor(), { driveEvents: cfg.driveEvents, currentPad: currentPad }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 8d4898911..c5f0ad26a 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -686,7 +686,8 @@ define([ }); ctx.sframeChan.on('EV_LOADING_INFO', function (data) { - UI.updateLoadingProgress(data, 'drive'); + //UI.updateLoadingProgress(data, 'drive'); + UI.updateLoadingProgress(data); }); ctx.sframeChan.on('EV_NEW_VERSION', function () { From 3171864a892add4d8e72326fff0bb2bf303ed50e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 15:21:26 +0200 Subject: [PATCH 08/51] lint compliance --- www/common/common-interface.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 368d8f017..9f1f08634 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -895,7 +895,7 @@ define([ todo(); } }; - UI.updateLoadingProgress = function (data, isDrive) { + UI.updateLoadingProgress = function (data) { if (window.CryptPad_updateLoadingProgress) { window.CryptPad_updateLoadingProgress(data); } From 4985ffae7503433adedab7ace300387f8fa18cbc Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 17:32:15 +0200 Subject: [PATCH 09/51] Fix FOUC in the loading screen --- customize.dist/loading.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index dd269fa60..e35a5e409 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -2,6 +2,17 @@ // light #4591c4 define(['/customize/messages.js'], function (Messages) { var loadingStyle = (function(){/* +@font-face { + font-family: 'Open Sans'; + src: url('/bower_components/open-sans-fontface/fonts/Regular/OpenSans-Regular.eot'); + src: url('/bower_components/open-sans-fontface/fonts/Regular/OpenSans-Regular.eot?#iefix') format('embedded-opentype'), + url('/bower_components/open-sans-fontface/fonts/Regular/OpenSans-Regular.woff') format('woff'), + url('/bower_components/open-sans-fontface/fonts/Regular/OpenSans-Regular.ttf') format('truetype'), + url('/bower_components/open-sans-fontface/fonts/Regular/OpenSans-Regular.svg#OpenSansRegular') format('svg'); + font-weight: normal; + font-style: normal; +} + #cp-loading { visibility: visible; position: fixed; @@ -19,6 +30,7 @@ define(['/customize/messages.js'], function (Messages) { flex-flow: column; justify-content: center; align-items: center; + font: 20px 'Open Sans', 'Helvetica Neue', sans-serif !important; } #cp-loading.cp-loading-hidden { opacity: 0; @@ -199,6 +211,17 @@ p.cp-password-info{ white-space: nowrap; text-overflow: ellipsis; } +.cp-loading-progress-list ul { + list-style: none; + padding-left: 0; +} +.cp-loading-progress-list li { + padding: 0px 5px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} .cp-loading-progress-list li i { width: 22px; } From a9e2ebc72a40a61ffa71828df8930d91011477c8 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 17:53:13 +0200 Subject: [PATCH 10/51] Fix percent > 100 --- customize.dist/loading.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index e35a5e409..cf9af5e39 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -310,7 +310,8 @@ button.primary:hover{ var getLi = function (i) { var check = (i < c || (i === c && data.progress === 100)) ? 'fa-check-square-o' : 'fa-square-o'; - var percent = i < c ? '(100%)' : (i === c ? '('+Math.floor(data.progress)+'%)' : '(0%)'); + var p = Math.min(Math.floor(data.progress), 100); + var percent = i < c ? '(100%)' : (i === c ? '('+p+'%)' : '(0%)'); return '
  • '+Messages['loading_state_'+i]+'' + ''+percent+''; }; @@ -324,7 +325,8 @@ button.primary:hover{ var makeBar = function (data) { var c = types.indexOf(data.type); var l = types.length; - var p = (data.progress / l) + (100 * c / l); + var progress = Math.min(data.progress, 100); + var p = (progress / l) + (100 * c / l); var bar = '
    '+ '
    '+ '
    '; From c4aa9ff8dc95c20e7992d03ca8ba1b416f46bcf1 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 15 Oct 2020 14:48:17 +0200 Subject: [PATCH 11/51] Add more steps to the loading progress bar --- customize.dist/loading.js | 18 +++++++++++++----- www/common/outer/async-store.js | 8 ++++++++ www/common/sframe-common.js | 7 +++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index cf9af5e39..38eae2515 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -294,6 +294,7 @@ button.primary:hover{ '

    ', '' ].join(''); + var built = false; // XXX var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad']; @@ -334,18 +335,25 @@ button.primary:hover{ }; var updateLoadingProgress = function (data) { + if (!built) { return; } var c = types.indexOf(data.type); if (c < current) { return console.error(data); } - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + try { + document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); + document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + } catch (e) { console.error(e); } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; window.CryptPad_loadingError = function (err) { - 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; + if (!built) { return; } + try { + 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; + } catch (e) { console.error(e); } }; return function () { + built = true; var intr; var append = function () { if (!document.body) { return; } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index b2b3b5f68..64cb068b1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2488,6 +2488,10 @@ define([ }).nThen(function (waitFor) { initAnonRpc(null, null, waitFor()); initRpc(null, null, waitFor()); + postMessage(clientId, 'LOADING_DRIVE', { + type: 'migrate', + progress: 0 + }); }).nThen(function (waitFor) { Migrate(proxy, waitFor(), function (version, progress) { postMessage(clientId, 'LOADING_DRIVE', { @@ -2496,6 +2500,10 @@ define([ }); }, store); }).nThen(function (waitFor) { + postMessage(clientId, 'LOADING_DRIVE', { + type: 'sf', + progress: 0 + }); userObject.fixFiles(); SF.loadSharedFolders(Store, store.network, store, userObject, waitFor, function (obj) { var data = { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index c5f0ad26a..82b52c9f7 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -619,6 +619,13 @@ define([ } window.CryptPad_sframe_common = true; + if (window.CryptPad_updateLoadingProgress) { + window.CryptPad_updateLoadingProgress({ + type: 'drive', + progress: 0 + }); + } + nThen(function (waitFor) { var msgEv = Util.mkEvent(); var iframe = window.parent; From bafd3405ae333c168589c8506b443608a7776acf Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 15 Oct 2020 14:58:56 +0200 Subject: [PATCH 12/51] More improvements --- customize.dist/loading.js | 5 +++-- www/common/LessLoader.js | 2 +- www/common/sframe-chainpad-netflux-inner.js | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 38eae2515..338b5882e 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -297,7 +297,7 @@ button.primary:hover{ var built = false; // XXX - var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad']; + var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end']; Messages.loading_state_0 = "Less"; Messages.loading_state_1 = "Drive"; Messages.loading_state_2 = "Migrate"; @@ -309,7 +309,7 @@ button.primary:hover{ var c = types.indexOf(data.type); current = c; var getLi = function (i) { - var check = (i < c || (i === c && data.progress === 100)) ? 'fa-check-square-o' + var check = (i < c || (i === c && data.progress >= 100)) ? 'fa-check-square-o' : 'fa-square-o'; var p = Math.min(Math.floor(data.progress), 100); var percent = i < c ? '(100%)' : (i === c ? '('+p+'%)' : '(0%)'); @@ -318,6 +318,7 @@ button.primary:hover{ }; var list = '
      '; types.forEach(function (el, i) { + if (i >= 6) { return; } list += getLi(i); }); list += '
    '; diff --git a/www/common/LessLoader.js b/www/common/LessLoader.js index 0a8a1e914..5b52b3d2c 100644 --- a/www/common/LessLoader.js +++ b/www/common/LessLoader.js @@ -167,7 +167,7 @@ define([ if (window.CryptPad_updateLoadingProgress) { window.CryptPad_updateLoadingProgress({ type: 'less', - progress: idx++ + progress: 4*idx++ }); } cacheGet(url, function (css) { diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 8e8bb84f2..abb1cebdf 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -149,6 +149,13 @@ define([ }); sframeChan.on('EV_RT_READY', function () { if (isReady) { return; } + if (updateLoadingProgress) { + updateLoadingProgress({ + type: 'end', + progress: 0 + }, false); + isHistory++; + } isReady = true; isHistory = false; chainpad.start(); From c9dae93c04af9f18752cdbe80f47127d0364741c Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:15:41 +0530 Subject: [PATCH 13/51] 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 106fb5edb47e99af70daf4830cf64f21dabd1c0f Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:20:24 +0530 Subject: [PATCH 14/51] 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 abb2a568bbf35f4e616610719eb49d3c4f748f3f Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 23 Oct 2020 10:35:29 +0530 Subject: [PATCH 15/51] 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 c9a3cad78b35407c1b65934d4653a64c70447e1f Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 23 Oct 2020 15:44:47 +0200 Subject: [PATCH 16/51] Fix various issues with restricted pads --- lib/historyKeeper.js | 4 ++ www/common/cryptget.js | 73 ++++++++++++++++++++++-- www/common/cryptpad-common.js | 93 ++++++++++++++++++++++++------- www/common/sframe-common-outer.js | 28 ++++++---- 4 files changed, 162 insertions(+), 36 deletions(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index d95c0e99f..cfdb14717 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -80,6 +80,10 @@ module.exports.create = function (Env, cb) { return void cb(); } + // If the channel is restricted, send the history keeper ID so that they + // can try to authenticate + allowed.unshift(Env.id); + // otherwise they're not allowed. // respond with a special error that includes the list of keys // which would be allowed... diff --git a/www/common/cryptget.js b/www/common/cryptget.js index ab707a49b..e394788d7 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -1,12 +1,15 @@ define([ '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', + '/bower_components/netflux-websocket/netflux-client.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', '/common/outer/network-config.js', + '/common/pinpad.js', + '/bower_components/nthen/index.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Crypto, CPNetflux, Util, Hash, Realtime, NetConfig) { +], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Pinpad, nThen) { var finish = function (S, err, doc) { if (S.done) { return; } S.cb(err, doc); @@ -28,6 +31,50 @@ define([ } }; + var makeNetwork = function (cb) { + var wsUrl = NetConfig.getWebsocketURL(); + Netflux.connect(wsUrl).then(function (network) { + cb(null, network); + }, function (err) { + cb(err); + }); + }; + + var start = function (Session, config) { + // Create a network and authenticate with all our keys if necessary, + // then start chainpad-netflux + nThen(function (waitFor) { + if (Session.hasNetwork) { return; } + makeNetwork(waitFor(function (err, network) { + if (err) { return; } + config.network = network; + })); + }).nThen(function () { + Session.realtime = CPNetflux.start(config); + }); + }; + + var onRejected = function (config, Session, data, cb) { + // Check if we can authenticate + if (!Array.isArray(data) || !data.length || data[0].length !== 16) { + return void cb(true); + } + if (!Array.isArray(Session.accessKeys)) { return void cb(true); } + + // Authenticate + config.network.historyKeeper = data[0]; + nThen(function (waitFor) { + Session.accessKeys.forEach(function (obj) { + Pinpad.create(config.network, obj, waitFor(function (e) { + console.log('done', obj); + if (e) { console.error(e); } + })); + }); + }).nThen(function () { + cb(); + }); + }; + var makeConfig = function (hash, opt) { var secret; if (typeof(hash) === 'string') { @@ -67,7 +114,15 @@ define([ progress = progress || function () {}; var config = makeConfig(hash, opt); - var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; + var Session = { + cb: cb, + accessKeys: opt.accessKeys, + hasNetwork: Boolean(opt.network) + }; + + config.onRejected = function (data, cb) { + onRejected(config, Session, data, cb); + }; config.onReady = function (info) { var rt = Session.session = info.realtime; @@ -95,7 +150,7 @@ define([ overwrite(config, opt); - Session.realtime = CPNetflux.start(config); + start(Session, config); }; var put = function (hash, doc, cb, opt) { @@ -105,7 +160,15 @@ define([ opt = opt || {}; var config = makeConfig(hash, opt); - var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; + var Session = { + cb: cb, + accessKeys: opt.accessKeys, + hasNetwork: Boolean(opt.network) + }; + + config.onRejected = function (data, cb) { + onRejected(config, Session, data, cb); + }; config.onReady = function (info) { var realtime = Session.session = info.realtime; @@ -126,7 +189,7 @@ define([ }; overwrite(config, opt); - Session.session = CPNetflux.start(config); + start(Session, config); }; return { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index d7c580f90..4613e5756 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -68,6 +68,38 @@ define([ }, cb); }; + common.getAccessKeys = function (cb) { + var keys = []; + Nthen(function (waitFor) { + // Push account keys + postMessage("GET", { + key: ['edPrivate'], + }, waitFor(function (obj) { + if (obj.error) { return; } + try { + keys.push({ + edPrivate: obj, + edPublic: Hash.getSignPublicFromPrivate(obj) + }); + } catch (e) { console.error(e); } + })); + // Push teams keys + postMessage("GET", { + key: ['teams'], + }, waitFor(function (obj) { + if (obj.error) { return; } + Object.keys(obj || {}).forEach(function (id) { + var t = obj[id]; + var _keys = t.keys.drive || {}; + if (!_keys.edPrivate) { return; } + keys.push(t.keys.drive); + }); + })); + }).nThen(function () { + cb(keys); + }); + }; + common.makeNetwork = function (cb) { require([ '/bower_components/netflux-websocket/netflux-client.js', @@ -629,6 +661,10 @@ define([ optsPut.password = password; })); } + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function () { Crypt.get(parsed.hash, function (err, val) { if (err) { @@ -667,19 +703,28 @@ define([ password: data.password, initialState: parsed.type === 'poll' ? '{}' : undefined }; - Crypt.get(parsed.hash, _waitFor(function (err, _val) { - if (err) { - _waitFor.abort(); - return void cb(err); - } - try { - val = JSON.parse(_val); - fixPadMetadata(val, true); - } catch (e) { - _waitFor.abort(); - return void cb(e.message); - } - }), optsGet); + var next = _waitFor(); + Nthen(function (waitFor) { + // Authenticate in case the pad os restricted + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + })); + }).nThen(function () { + Crypt.get(parsed.hash, function (err, _val) { + if (err) { + _waitFor.abort(); + return void cb(err); + } + try { + val = JSON.parse(_val); + fixPadMetadata(val, true); + next(); + } catch (e) { + _waitFor.abort(); + return void cb(e.message); + } + }, optsGet); + }); return; } @@ -742,9 +787,6 @@ define([ }).nThen(function () { Crypt.put(parsed2.hash, JSON.stringify(val), function () { cb(); - Crypt.get(parsed2.hash, function (err, val) { - console.warn(val); - }); }, optsPut); }); @@ -1007,7 +1049,7 @@ define([ oldSecret = Hash.getSecrets(parsed.type, parsed.hash, optsGet.password); oldChannel = oldSecret.channel; common.getPadMetadata({channel: oldChannel}, waitFor(function (metadata) { - oldMetadata = metadata; + oldMetadata = metadata || {}; })); common.getMetadata(waitFor(function (err, data) { if (err) { @@ -1059,6 +1101,11 @@ define([ if (expire) { optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds } + }).nThen(function (waitFor) { + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function (waitFor) { Crypt.get(parsed.hash, waitFor(function (err, val) { if (err) { @@ -1075,6 +1122,8 @@ define([ } }), optsGet); }).nThen(function (waitFor) { + optsPut.metadata.restricted = oldMetadata.restricted; + optsPut.metadata.allowed = oldMetadata.allowed; Crypt.put(newHash, cryptgetVal, waitFor(function (err) { if (err) { waitFor.abort(); @@ -1310,11 +1359,17 @@ define([ validateKey: newSecret.keys.validateKey }, }; + var optsGet = {}; Nthen(function (waitFor) { common.getPadAttribute('', waitFor(function (err, _data) { padData = _data; + optsGet.password = padData.password; }), href); + common.getAccessKeys(waitFor(function (keys) { + optsGet.accessKeys = keys; + optsPut.accessKeys = keys; + })); }).nThen(function (waitFor) { oldSecret = Hash.getSecrets(parsed.type, parsed.hash, padData.password); @@ -1393,9 +1448,7 @@ define([ waitFor.abort(); return void cb({ error: 'CANT_PARSE' }); } - }), { - password: padData.password - }); + }), optsGet); }).nThen(function (waitFor) { // Re-encrypt rtchannel oldRtChannel = Util.find(cryptgetVal, ['content', 'channel']); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 94dc8ab14..c837a9b72 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1382,8 +1382,10 @@ define([ }; var i = 0; sframeChan.on('Q_CRYPTGET', function (data, cb) { + var keys; var todo = function () { data.opts.network = cgNetwork; + data.opts.accessKeys = keys; Cryptget.get(data.hash, function (err, val) { cb({ error: err, @@ -1402,17 +1404,21 @@ define([ cgNetwork = undefined; } i++; - if (!cgNetwork) { - cgNetwork = true; - return void Cryptpad.makeNetwork(function (err, nw) { - console.log(nw); - cgNetwork = nw; - todo(); - }); - } else if (cgNetwork === true) { - return void whenCGReady(todo); - } - todo(); + + Cryptpad.getAccessKeys(function (_keys) { + keys = _keys; + if (!cgNetwork) { + cgNetwork = true; + return void Cryptpad.makeNetwork(function (err, nw) { + console.log(nw); + cgNetwork = nw; + todo(); + }); + } else if (cgNetwork === true) { + return void whenCGReady(todo); + } + todo(); + }); }); sframeChan.on('EV_CRYPTGET_DISCONNECT', function () { if (!cgNetwork) { return; } From d3dd5f991d6df93ef1944678d09475bcdfa053fd Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Oct 2020 17:24:35 +0530 Subject: [PATCH 17/51] proposed nginx configuration to enable XLSX export without disabling print from other apps --- docs/example.nginx.conf | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index 117eb2cc4..324df90c0 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -57,11 +57,6 @@ server { add_header Access-Control-Allow-Origin "*"; # add_header X-Frame-Options "SAMEORIGIN"; - # Enable SharedArrayBuffer in Firefox (for .xlsx export) - add_header Cross-Origin-Resource-Policy cross-origin; - add_header Cross-Origin-Opener-Policy same-origin; - add_header Cross-Origin-Embedder-Policy require-corp; - # Insert the path to your CryptPad repository root here root /home/cryptpad/cryptpad; index index.html; @@ -113,6 +108,14 @@ server { if ($uri = "/sheet/inner.html") { set $unsafe 1; } if ($uri ~ ^\/common\/onlyoffice\/.*\/index\.html.*$) { set $unsafe 1; } + set $coop ''; + if ($uri ~ ^\/sheet\/.*$) { set $coop 'same-origin'; } + + # Enable SharedArrayBuffer in Firefox (for .xlsx export) + add_header Cross-Origin-Resource-Policy cross-origin; + add_header Cross-Origin-Opener-Policy $coop; + add_header Cross-Origin-Embedder-Policy require-corp; + # everything except the sandbox domain is a privileged scope, as they might be used to handle keys if ($host != $sandbox_domain) { set $unsafe 0; } From 5cf8ca70e684100a42d88839a3f7c5203ea04140 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Oct 2020 17:34:34 +0530 Subject: [PATCH 18/51] don't pin falsey document ids --- lib/commands/pin-rpc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/commands/pin-rpc.js b/lib/commands/pin-rpc.js index 8b111438d..93d922620 100644 --- a/lib/commands/pin-rpc.js +++ b/lib/commands/pin-rpc.js @@ -162,7 +162,7 @@ Pinning.pinChannel = function (Env, safeKey, channels, cb) { // only pin channels which are not already pinned var toStore = channels.filter(function (channel) { - return pinned.indexOf(channel) === -1; + return channel && pinned.indexOf(channel) === -1; }); if (toStore.length === 0) { @@ -204,7 +204,7 @@ Pinning.unpinChannel = function (Env, safeKey, channels, cb) { // only unpin channels which are pinned var toStore = channels.filter(function (channel) { - return pinned.indexOf(channel) !== -1; + return channel && pinned.indexOf(channel) !== -1; }); if (toStore.length === 0) { From 6f76ee445c650207eb328ffe0e9ac23668e90e35 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 26 Oct 2020 14:57:54 +0100 Subject: [PATCH 19/51] 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 9c16312dad4cb8780d58ad6144929c1a0ed447a2 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 26 Oct 2020 16:45:36 +0100 Subject: [PATCH 20/51] Improve tag UI --- .../src/less2/include/tokenfield.less | 16 ++++ www/common/common-interface.js | 76 ++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/customize.dist/src/less2/include/tokenfield.less b/customize.dist/src/less2/include/tokenfield.less index 42a06624c..e3d2c3e5d 100644 --- a/customize.dist/src/less2/include/tokenfield.less +++ b/customize.dist/src/less2/include/tokenfield.less @@ -20,6 +20,22 @@ margin: 0 10px; padding: 0; width: ~"calc(100% - 20px)"; + span.tokenfield-empty { + font-size: 14px; + font-style: italic; + color: lighten(@cryptpad_text_col, 10%); + } + .cp-tokenfield-container { + width: 100%; + } + .cp-tokenfield-form { + display: flex; + width: 100%; + input { + flex: 1; + min-width: 0 !important; + } + } .token { box-sizing: border-box; display: inline-flex; diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 9f1f08634..244b6856f 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -280,8 +280,82 @@ 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) + ])); + + + $button.click(function () { + $t.tokenfield('createToken', $input.val()); + }); + + var $container = $(h('span.cp-tokenfield-container')); + var $form = $(h('span.cp-tokenfield-form')); + $container.insertAfter($input); + + // Fix the UI to keep the "add" or "edit" button at the correct location + var isEdit = false; + var called = false; + var resetUI = function () { + called = true; + setTimeout(function () { + $container.find('.tokenfield-empty').remove(); + var $tokens = $root.find('.token').prependTo($container); + if (!$tokens.length) { + $container.prepend(h('span.tokenfield-empty', Messages.kanban_noTags)); + } + $form.append($input); + $form.append($button); + if (isEdit) { $button.find('span').text(Messages.edit); } + else { $button.find('span').text(Messages.add); } + $container.append($form); + $input.focus(); + isEdit = false; + called = false; + }); + }; + resetUI(); + + $t.on('tokenfield:removedtoken', function () { + resetUI(); + }); + $t.on('tokenfield:editedtoken', function () { + resetUI(); + }); + $t.on('tokenfield:createdtoken', function () { + $input.val(''); + resetUI(); + }); + $t.on('tokenfield:edittoken', function () { + isEdit = true; + }); + + // Fix UI issue where the input could go outside of the container + var MutationObserver = window.MutationObserver; + var observer = new MutationObserver(function(mutations) { + if (called) { return; } + mutations.forEach(function(mutation) { + for (var i = 0; i < mutation.addedNodes.length; i++) { + if (mutation.addedNodes[i].classList && + mutation.addedNodes[i].classList.contains('token-input')) { + resetUI(); + break; + } + } + }); + }); + observer.observe($root[0], { + childList: true, + subtree: false + }); + $t.on('tokenfield:removetoken', function () { - $root.find('.token-input').focus(); + $input.focus(); }); t.preventDuplicates = function (cb) { From aa547a7b762e2e2ac0861585c1f2b347a8bfb63b Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 08:12:23 +0530 Subject: [PATCH 21/51] align nodejs http headers with example nginx --- docs/example.nginx.conf | 16 ++++++++-------- lib/defaults.js | 3 --- server.js | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index 324df90c0..8319c657b 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -57,6 +57,14 @@ server { add_header Access-Control-Allow-Origin "*"; # add_header X-Frame-Options "SAMEORIGIN"; + set $coop ''; + if ($uri ~ ^\/sheet\/.*$) { set $coop 'same-origin'; } + + # Enable SharedArrayBuffer in Firefox (for .xlsx export) + add_header Cross-Origin-Resource-Policy cross-origin; + add_header Cross-Origin-Opener-Policy $coop; + add_header Cross-Origin-Embedder-Policy require-corp; + # Insert the path to your CryptPad repository root here root /home/cryptpad/cryptpad; index index.html; @@ -108,14 +116,6 @@ server { if ($uri = "/sheet/inner.html") { set $unsafe 1; } if ($uri ~ ^\/common\/onlyoffice\/.*\/index\.html.*$) { set $unsafe 1; } - set $coop ''; - if ($uri ~ ^\/sheet\/.*$) { set $coop 'same-origin'; } - - # Enable SharedArrayBuffer in Firefox (for .xlsx export) - add_header Cross-Origin-Resource-Policy cross-origin; - add_header Cross-Origin-Opener-Policy $coop; - add_header Cross-Origin-Embedder-Policy require-corp; - # everything except the sandbox domain is a privileged scope, as they might be used to handle keys if ($host != $sandbox_domain) { set $unsafe 0; } diff --git a/lib/defaults.js b/lib/defaults.js index 329e16f4c..4110e63d4 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -48,9 +48,6 @@ Default.httpHeaders = function () { "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "Access-Control-Allow-Origin": "*", - "Cross-Origin-Resource-Policy": 'cross-origin', - "Cross-Origin-Opener-Policy": 'same-origin', - "Cross-Origin-Embedder-Policy": 'require-corp', }; }; diff --git a/server.js b/server.js index 0e0c2d79e..60247f47a 100644 --- a/server.js +++ b/server.js @@ -60,6 +60,10 @@ var app = Express(); } }()); +var applyHeaderMap = function (res, map) { + for (let header in map) { res.setHeader(header, map[header]); } +}; + var setHeaders = (function () { // load the default http headers unless the admin has provided their own via the config file var headers; @@ -96,14 +100,21 @@ var setHeaders = (function () { } if (Object.keys(headers).length) { return function (req, res) { + // apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere + applyHeaderMap(res, { + "Cross-Origin-Resource-Policy": 'cross-origin', + "Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '', + "Cross-Origin-Embedder-Policy": 'require-corp', + }); + + // targeted CSP, generic policies, maybe custom headers const h = [ - ///^\/pad\/inner\.html.*/, /^\/common\/onlyoffice\/.*\/index\.html.*/, /^\/(sheet|ooslide|oodoc)\/inner\.html.*/, ].some((regex) => { return regex.test(req.url); }) ? padHeaders : headers; - for (let header in h) { res.setHeader(header, h[header]); } + applyHeaderMap(res, h); }; } return function () {}; @@ -139,6 +150,7 @@ app.use(function (req, res, next) { setHeaders(req, res); if (/[\?\&]ver=[^\/]+$/.test(req.url)) { res.setHeader("Cache-Control", "max-age=31536000"); } + else { res.setHeader("Cache-Control", "no-cache"); } next(); }); From 12c4451f532ac747ff94e1b066ce7a123d10a24a Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 12:46:27 +0530 Subject: [PATCH 22/51] lint compliance --- 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 1f1b40b83db9b7eabe56c6bb26c63181a8aeac4e Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 12:47:15 +0530 Subject: [PATCH 23/51] move some implictly global state to env.js --- lib/commands/metadata.js | 4 +--- lib/env.js | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/commands/metadata.js b/lib/commands/metadata.js index 1d758564b..3d20f36e6 100644 --- a/lib/commands/metadata.js +++ b/lib/commands/metadata.js @@ -2,7 +2,6 @@ const Data = module.exports; const Meta = require("../metadata"); -const WriteQueue = require("../write-queue"); const Core = require("./core"); const Util = require("../common-util"); const HK = require("../hk-util"); @@ -53,7 +52,6 @@ Data.getMetadata = function (Env, channel, cb, Server, netfluxId) { value: value } */ -var queueMetadata = WriteQueue(); Data.setMetadata = function (Env, safeKey, data, cb, Server) { var unsafeKey = Util.unescapeKeyCharacters(safeKey); @@ -63,7 +61,7 @@ Data.setMetadata = function (Env, safeKey, data, cb, Server) { if (!command || typeof (command) !== 'string') { return void cb('INVALID_COMMAND'); } if (Meta.commands.indexOf(command) === -1) { return void cb('UNSUPPORTED_COMMAND'); } - queueMetadata(channel, function (next) { + Env.queueMetadata(channel, function (next) { Data.getMetadataRaw(Env, channel, function (err, metadata) { if (err) { cb(err); diff --git a/lib/env.js b/lib/env.js index 97ebd20ce..b1fc6680b 100644 --- a/lib/env.js +++ b/lib/env.js @@ -45,6 +45,7 @@ module.exports.create = function (config) { queueStorage: WriteQueue(), queueDeletes: WriteQueue(), queueValidation: WriteQueue(), + queueMetadata: WriteQueue(), batchIndexReads: BatchRead("HK_GET_INDEX"), batchMetadata: BatchRead('GET_METADATA'), From 8de5ce43518ab927f1805eccfd837c3f9d05e4df Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 27 Oct 2020 10:41:35 +0100 Subject: [PATCH 24/51] Fix Shared FOlder issues in the drive --- www/common/drive-ui.js | 3 +++ www/common/userObject.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 649b5fe86..5d6a71b9d 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -4549,6 +4549,9 @@ define([ var rEl = manager.find(restorePath); if (manager.isFile(rEl)) { restoreName = manager.getTitle(rEl); + } else if (manager.isSharedFolder(rEl)) { + var sfData = manager.getSharedFolderData(rEl); + restoreName = sfData.title || sfData.lastTitle || Messages.fm_deletedFolder; } else { restoreName = restorePath[1]; } diff --git a/www/common/userObject.js b/www/common/userObject.js index 680ef3d0b..ca716f25f 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -846,7 +846,8 @@ define([ }; exp.ownedInTrash = function (isOwned) { return getFiles([TRASH]).map(function (id) { - var data = exp.getFileData(id); + var data = isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id); + if (!data) { return; } return isOwned(data.owners) ? data.channel : undefined; }).filter(Boolean); }; From fda0cda1de22cf702522cd248557572f4f152c68 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 28 Oct 2020 16:10:34 +0100 Subject: [PATCH 25/51] Fix new loading screen errors --- customize.dist/loading.js | 27 +------- www/common/common-interface.js | 105 ++++++------------------------- www/common/common-ui-elements.js | 10 ++- www/common/sframe-common.js | 8 ++- 4 files changed, 36 insertions(+), 114 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 338b5882e..92c645acb 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -175,32 +175,6 @@ p.cp-password-info{ #cp-loading .cp-loading-spinner-container > div { height: 100px; } -#cp-loading-tip { - position: fixed; - z-index: 10000000; - top: 80%; - left: 0; - right: 0; - text-align: center; - transition: opacity 750ms; - transition-delay: 3000ms; -} -@media screen and (max-height: 600px) { - #cp-loading-tip { - display: none; - } -} -#cp-loading-tip span { - background: #222; - color: #fafafa; - text-align: center; - font-size: 1.3em; - opacity: 0.7; - font-family: 'Open Sans', 'Helvetica Neue', sans-serif; - padding: 15px; - max-width: 60%; - display: inline-block; -} .cp-loading-progress { width: 100%; margin: 20px; @@ -340,6 +314,7 @@ button.primary:hover{ var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { + document.querySelector('.cp-loading-spinner-container').style.display = 'none'; document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); } catch (e) { console.error(e); } diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 244b6856f..d423b48ee 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -932,35 +932,33 @@ define([ var LOADING = 'cp-loading'; - var loading = { - error: false, - driveState: 0, - padState: 0 - }; UI.addLoadingScreen = function (config) { config = config || {}; var loadingText = config.loadingText; var todo = function () { var $loading = $('#' + LOADING); + // Show the loading screen $loading.css('display', ''); $loading.removeClass('cp-loading-hidden'); - $('.cp-loading-spinner-container').show(); - if (!config.noProgress && !$loading.find('.cp-loading-progress').length) { + 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-drive'), - h('p.cp-loading-progress-pad') + h('p.cp-loading-progress-list'), + h('p.cp-loading-progress-container') ]); - $(progress).hide(); - $loading.find('.cp-loading-container').append(progress); - } else if (config.noProgress) { - $loading.find('.cp-loading-progress').remove(); + $loading.find('.cp-loading-spinner-container').after(progress); + } + if (!$loading.find('.cp-loading-progress').length) { + // Add spinner + $('.cp-loading-spinner-container').show(); } + // Add loading text if (loadingText) { $('#' + LOADING).find('#cp-loading-message').show().text(loadingText); } else { $('#' + LOADING).find('#cp-loading-message').hide().text(''); } - loading.error = false; }; if ($('#' + LOADING).length) { todo(); @@ -973,60 +971,6 @@ define([ if (window.CryptPad_updateLoadingProgress) { window.CryptPad_updateLoadingProgress(data); } - // XXX - /* - var $loading = $('#' + LOADING); - if (!$loading.length || loading.error) { return; } - $loading.find('.cp-loading-progress').show(); - var $progress; - if (isDrive) { - // Drive state - if (loading.driveState === -1) { return; } // Already loaded - $progress = $loading.find('.cp-loading-progress-drive'); - if (!$progress.length) { return; } // Can't find the box to display data - - // If state is -1, remove the box, drive is loaded - if (data.state === -1) { - loading.driveState = -1; - $progress.remove(); - } else { - if (data.state < loading.driveState) { return; } // We should not display old data - // Update the current state - loading.driveState = data.state; - data.progress = data.progress || 100; - data.msg = Messages['loading_drive_'+ Math.floor(data.state)] || ''; - $progress.html(data.msg); - if (data.progress) { - $progress.append(h('div.cp-loading-progress-bar', [ - h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'}) - ])); - } - } - } else { - // Pad state - if (loading.padState === -1) { return; } // Already loaded - $progress = $loading.find('.cp-loading-progress-pad'); - if (!$progress.length) { return; } // Can't find the box to display data - - // If state is -1, remove the box, pad is loaded - if (data.state === -1) { - loading.padState = -1; - $progress.remove(); - } else { - if (data.state < loading.padState) { return; } // We should not display old data - // Update the current state - loading.padState = data.state; - data.progress = data.progress || 100; - data.msg = Messages['loading_pad_'+data.state] || ''; - $progress.html(data.msg); - if (data.progress) { - $progress.append(h('div.cp-loading-progress-bar', [ - h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'}) - ])); - } - } - } - */ }; UI.removeLoadingScreen = function (cb) { // Release the test blocker, hopefully every test has been registered. @@ -1034,31 +978,23 @@ define([ cb = cb || function () {}; if (Test.__ASYNC_BLOCKER__) { Test.__ASYNC_BLOCKER__.pass(); } - $('#' + LOADING).addClass("cp-loading-hidden"); + var $loading = $('#' + LOADING); + $loading.addClass("cp-loading-hidden"); // Hide the loading screen + $loading.find('.cp-loading-progress').remove(); // Remove the progress list setTimeout(cb, 750); - loading.error = false; - var $tip = $('#cp-loading-tip').css('top', '') - // loading.less sets transition-delay: $wait-time - // and transition: opacity $fadeout-time - .css({ - 'opacity': 0, - 'pointer-events': 'none', - }); - window.setTimeout(function () { - $tip.remove(); - }, 3750); - // jquery.fadeout can get stuck }; UI.errorLoadingScreen = function (error, transparent, exitable) { var $loading = $('#' + LOADING); if (!$loading.is(':visible') || $loading.hasClass('cp-loading-hidden')) { - UI.addLoadingScreen({hideTips: true}); + UI.addLoadingScreen(); } - loading.error = true; + // Remove the progress list $loading.find('.cp-loading-progress').remove(); + // Hide the spinner $('.cp-loading-spinner-container').hide(); - $('#cp-loading-tip').remove(); if (transparent) { $loading.css('opacity', 0.9); } + + // Add the error message var $error = $loading.find('#cp-loading-message').show(); if (error instanceof Element) { $error.html('').append(error); @@ -1070,7 +1006,6 @@ define([ $(window).keydown(function (e) { if (e.which === 27) { $loading.hide(); - loading.error = false; if (typeof(exitable) === "function") { exitable(); } } }); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index a2f965113..bc548d8f7 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2532,10 +2532,16 @@ define([ var submit = function () { var value = $password.find('.cp-password-input').val(); - UI.addLoadingScreen(); + UI.addLoadingScreen({newProgress: true}); + if (window.CryptPad_updateLoadingProgress) { + window.CryptPad_updateLoadingProgress({ + type: 'pad', + progress: 0 + }); + } common.getSframeChannel().query('Q_PAD_PASSWORD_VALUE', value, function (err, data) { if (!data) { - UIElements.displayPasswordPrompt(common, cfg, true); + return void UIElements.displayPasswordPrompt(common, cfg, true); } }); }; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 82b52c9f7..f7a2c41c3 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -345,7 +345,13 @@ define([ } if (priv.burnAfterReading) { UIElements.displayBurnAfterReadingPage(funcs, waitFor(function () { - UI.addLoadingScreen(); + UI.addLoadingScreen({newProgress: true}); + if (window.CryptPad_updateLoadingProgress) { + window.CryptPad_updateLoadingProgress({ + type: 'pad', + progress: 0 + }); + } ctx.sframeChan.event('EV_BURN_AFTER_READING'); })); } From da8e2416a179c0eeca652a0e6b91dbe6035f3bee Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 28 Oct 2020 16:32:22 +0100 Subject: [PATCH 26/51] Center loading steps --- customize.dist/loading.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 92c645acb..56e9a4cab 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -178,6 +178,7 @@ p.cp-password-info{ .cp-loading-progress { width: 100%; margin: 20px; + text-align: center; } .cp-loading-progress p { margin: 5px; @@ -185,6 +186,10 @@ p.cp-password-info{ white-space: nowrap; text-overflow: ellipsis; } +.cp-loading-progress-list { + text-align: left; + display: inline-block; +} .cp-loading-progress-list ul { list-style: none; padding-left: 0; @@ -200,7 +205,10 @@ p.cp-password-info{ width: 22px; } .cp-loading-progress-list li span{ - margin-left: 20px; + margin-left: 10px; +} +.cp-loading-progress-list li span.percent { + position: absolute; } .cp-loading-progress-bar { @@ -285,10 +293,12 @@ button.primary:hover{ var getLi = function (i) { var check = (i < c || (i === c && data.progress >= 100)) ? 'fa-check-square-o' : 'fa-square-o'; - var p = Math.min(Math.floor(data.progress), 100); - var percent = i < c ? '(100%)' : (i === c ? '('+p+'%)' : '(0%)'); - return '
  • '+Messages['loading_state_'+i]+'' + - ''+percent+''; + var percentStr = ''; + if (i === c) { + var p = Math.min(Math.floor(data.progress), 100); + percentStr = '('+p+'%)'; + } + return '
  • '+Messages['loading_state_'+i]+'' + percentStr; }; var list = '
      '; types.forEach(function (el, i) { From 00694e8ebdf840563517a8fb03d733edc7400f2c Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 14:06:16 +0100 Subject: [PATCH 27/51] Stop using sessionStorage when creating/opening pads --- www/common/common-hash.js | 29 +++++++++++ www/common/cryptpad-common.js | 35 ++++++------- www/common/drive-ui.js | 87 +++++++++++++------------------ www/common/notifications.js | 24 ++++----- www/common/sframe-common-outer.js | 81 +++++++++++++++------------- 5 files changed, 137 insertions(+), 119 deletions(-) diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 0b584d4c3..ff8764da4 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -169,6 +169,17 @@ Version 1 /code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI */ + var getNewPadOpts = function (hashArr) { + var k; + // Check if we have a ownerKey for this pad + hashArr.some(function (data) { + if (/^newpad=/.test(data)) { + k = data.slice(7); + return true; + } + }); + return k || ''; + }; var getVersionHash = function (hashArr) { var k; // Check if we have a ownerKey for this pad @@ -202,6 +213,7 @@ Version 1 parsed.present = options.indexOf('present') !== -1; parsed.embed = options.indexOf('embed') !== -1; parsed.versionHash = getVersionHash(options); + parsed.newPadOpts = getNewPadOpts(options); parsed.ownerKey = getOwnerKey(options); }; @@ -217,6 +229,13 @@ Version 1 password: parsed.password }; }; + + if (/^\/newpad=/.test(hash)) { + return { + newPadOpts: hash.slice(8, -1) + }; + } + if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0 // Old hash parsed.channel = hash.slice(0, 32); @@ -237,6 +256,9 @@ Version 1 if (versionHash) { hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/'; } + if (opts.newPadOpts) { + hash += 'newpad=' + opts.newPadOpts + '/'; + } return hash; }; @@ -372,6 +394,10 @@ Version 1 var url = '/'; if (!ret.type) { return url; } url += ret.type + '/'; + // New pad with options: append the options to the hash + if (!ret.hashData && options.newPadOpts) { + return url + '#/newpad=' + options.newPadOpts + '/'; + } if (!ret.hashData) { return url; } if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; } if (ret.hashData.version === 0) { return url + '#' + ret.hash; } @@ -575,6 +601,9 @@ Version 1 // Valid hash? if (parsed.hash) { if (!parsed.hashData) { return; } + // New pad: only newPadOpts allowed + if (Object.keys(parsed.hashData).length === 1 && + parsed.hashData.newPadOpts) { return true; } // Version should be a number if (typeof(parsed.hashData.version) === "undefined") { return; } // pads and files should have a base64 (or hex) key diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 4613e5756..70ebe5bcc 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -148,6 +148,21 @@ define([ send(); }; + common.setTabHref = function (href) { + var ohc = window.onhashchange; + window.onhashchange = function () {}; + window.location.href = href; + window.onhashchange = ohc; + ohc({reset: true}); + }; + common.setTabHash = function (hash) { + var ohc = window.onhashchange; + window.onhashchange = function () {}; + window.location.hash = hash; + window.onhashchange = ohc; + ohc({reset: true}); + }; + // RESTRICTED // Settings only common.resetDrive = function (cb) { @@ -2090,26 +2105,6 @@ define([ language: common.getLanguage(), driveEvents: true //rdyCfg.driveEvents // Boolean }; - // if a pad is created from a file - if (sessionStorage[Constants.newPadFileData]) { - common.fromFileData = JSON.parse(sessionStorage[Constants.newPadFileData]); - var _parsed1 = Hash.parsePadUrl(common.fromFileData.href); - var _parsed2 = Hash.parsePadUrl(window.location.href); - if (_parsed1.hashData.type === 'pad') { - if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; } - } - delete sessionStorage[Constants.newPadFileData]; - } - - if (sessionStorage[Constants.newPadPathKey]) { - common.initialPath = sessionStorage[Constants.newPadPathKey]; - delete sessionStorage[Constants.newPadPathKey]; - } - - if (sessionStorage[Constants.newPadTeamKey]) { - common.initialTeam = sessionStorage[Constants.newPadTeamKey]; - delete sessionStorage[Constants.newPadTeamKey]; - } var channelIsReady = waitFor(); diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 5d6a71b9d..08a3f1279 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -1125,6 +1125,18 @@ define([ var hiddenHref = Hash.hashToHref(hash, parsed.type); window.open(APP.origin + hiddenHref); }; + var openIn = function (type, path, team, fData) { + var obj = JSON.stringify({ + p: path, + t: team, + d: fData + }); + var str = encodeURIComponent(obj); + var parsed = Hash.parsePadUrl(Hash.hashToHref('', type)); + var opts = parsed.getOptions(); + opts.newPadOpts = str; + common.openURL(parsed.getUrl(opts)); + }; var refresh = APP.refresh = function () { APP.displayDirectory(currentPath); @@ -2667,12 +2679,7 @@ define([ .click(function () { var type = $(this).attr('data-type') || 'pad'; var path = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath; - nThen(function (waitFor) { - common.sessionStorage.put(Constants.newPadPathKey, path, waitFor()); - common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor()); - }).nThen(function () { - common.openURL('/' + type + '/'); - }); + openIn(type, path, APP.team); }); }; var createNewButton = function (isInRoot, $container) { @@ -4227,31 +4234,19 @@ define([ else if ($this.hasClass('cp-app-drive-context-makeacopy')) { if (paths.length !== 1) { return; } el = manager.find(paths[0].path); - var _metadata = manager.getFileData(el); - var _simpleData = { - title: _metadata.filename || _metadata.title, - href: _metadata.href || _metadata.roHref, - password: _metadata.password, - channel: _metadata.channel, - }; - nThen(function (waitFor) { + (function () { var path = currentPath; if (path[0] !== ROOT) { path = [ROOT]; } - common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(_simpleData), waitFor()); - common.sessionStorage.put(Constants.newPadPathKey, path, waitFor()); - common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor()); - }).nThen(function () { + var _metadata = manager.getFileData(el); + var _simpleData = { + title: _metadata.filename || _metadata.title, + href: _metadata.href || _metadata.roHref, + password: _metadata.password, + channel: _metadata.channel, + }; var parsed = Hash.parsePadUrl(_metadata.href || _metadata.roHref); - common.openURL(Hash.hashToHref('', parsed.type)); - // We need to restore sessionStorage for the next time we want to create a pad from this tab - // NOTE: the 100ms timeout is to fix a race condition in firefox where sessionStorage - // would be deleted before the new tab was created - setTimeout(function () { - common.sessionStorage.put(Constants.newPadFileData, '', function () {}); - common.sessionStorage.put(Constants.newPadPathKey, '', function () {}); - common.sessionStorage.put(Constants.newPadTeamKey, '', function () {}); - }, 100); - }); + openIn(parsed.type, path, APP.team, _simpleData); + })(); } else if ($this.hasClass('cp-app-drive-context-openincode')) { if (paths.length !== 1) { return; } @@ -4264,23 +4259,20 @@ define([ password: metadata.password, channel: metadata.channel, }; - nThen(function (waitFor) { - common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(simpleData), waitFor()); - common.sessionStorage.put(Constants.newPadPathKey, currentPath, waitFor()); - common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor()); - }).nThen(function () { - common.openURL('/code/'); - // We need to restore sessionStorage for the next time we want to create a pad from this tab - // NOTE: the 100ms timeout is to fix a race condition in firefox where sessionStorage - // would be deleted before the new tab was created - setTimeout(function () { - common.sessionStorage.put(Constants.newPadFileData, '', function () {}); - common.sessionStorage.put(Constants.newPadPathKey, '', function () {}); - common.sessionStorage.put(Constants.newPadTeamKey, '', function () {}); - }, 100); - }); + (function () { + var path = currentPath; + if (path[0] !== ROOT) { path = [ROOT]; } + var _metadata = manager.getFileData(el); + var _simpleData = { + title: _metadata.filename || _metadata.title, + href: _metadata.href || _metadata.roHref, + password: _metadata.password, + channel: _metadata.channel, + }; + var parsed = Hash.parsePadUrl(_metadata.href || _metadata.roHref); + openIn('code', path, APP.team, _simpleData); + })(); } - else if ($this.hasClass('cp-app-drive-context-expandall') || $this.hasClass('cp-app-drive-context-collapseall')) { if (paths.length !== 1) { return; } @@ -4482,12 +4474,7 @@ define([ else if ($this.hasClass("cp-app-drive-context-newdoc")) { var ntype = $this.data('type') || 'pad'; var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath; - nThen(function (waitFor) { - common.sessionStorage.put(Constants.newPadPathKey, path2, waitFor()); - common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor()); - }).nThen(function () { - common.openURL('/' + ntype + '/'); - }); + openIn(ntype, path2, APP.team); } else if ($this.hasClass("cp-app-drive-context-properties")) { if (type === 'trash') { diff --git a/www/common/notifications.js b/www/common/notifications.js index 44125ed71..1686b87f9 100644 --- a/www/common/notifications.js +++ b/www/common/notifications.js @@ -108,21 +108,17 @@ define([ return Messages._getKey(key, [name, title, teamName]); }; content.handler = function() { - var todo = function() { - common.openURL(msg.content.href); - defaultDismiss(common, data)(); - }; - nThen(function(waitFor) { - if (msg.content.isTemplate) { - common.sessionStorage.put(Constants.newPadPathKey, ['template'], waitFor()); - } - if (teamNotification) { - common.sessionStorage.put(Constants.newPadTeamKey, teamNotification, waitFor()); - } - common.sessionStorage.put('newPadPassword', msg.content.password || '', waitFor()); - }).nThen(function() { - todo(); + var obj = JSON.stringify({ + p: msg.content.isTemplate ? ['template'] : undefined, + t: teamNotification || undefined, + pw: msg.content.password || '' }); + var str = encodeURIComponent(obj); + var parsed = Hash.parsePadUrl(msg.content.href); + var opts = parsed.getOptions(); + opts.newPadOpts = str; + common.openURL(parsed.getUrl(opts)); + defaultDismiss(common, data)(); }; if (!content.archived) { content.dismissHandler = defaultDismiss(common, data); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index c837a9b72..1e149abeb 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -26,7 +26,7 @@ define([ }; var AppConfig; var Test; - var password; + var password, newPadPassword; var initialPathInDrive; var burnAfterReading; @@ -196,15 +196,47 @@ define([ } // Rendered (maybe hidden) hash var renderedParsed = Utils.Hash.parsePadUrl(window.location.href); - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.href = renderedParsed.getUrl(opts); - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHref(renderedParsed.getUrl(opts)); } })); }; + // New pad options + if (parsed.hashData && parsed.hashData.newPadOpts) { + try { + var newPad = JSON.parse(decodeURIComponent(parsed.hashData.newPadOpts)); + Cryptpad.initialTeam = newPad.t; + Cryptpad.initialPath = newPad.p; + newPadPassword = newPad.pw; + if (newPad.d) { + Cryptpad.fromFileData = newPad.d; + var _parsed1 = Utils.Hash.parsePadUrl(Cryptpad.fromFileData.href); + if (_parsed1.hashData.type === 'pad' && _parsed1.type !== parsed.type) { + delete Cryptpad.fromFileData; + } + } + } catch (e) { + console.error(e, parsed.hashData.newPadOpts); + } + delete parsed.hashData.newPadOpts; + + // If it's a new pad, don't check password + if (!Object.keys(parsed.hashData).length) { + delete parsed.hashData; + parsed.hash = ''; + currentPad.hash = ''; + Cryptpad.setTabHash(''); + return void todo(); + } + // Otherwise, existing pad (new for us) + var opts = parsed.getOptions(); + delete opts.newPadOpts; + parsed = Utils.Hash.parsePadUrl(parsed.getUrl(opts)); + currentPad.hash = parsed.hash; + Cryptpad.setTabHash(parsed.hash); + } + + if (!parsed.hashData) { // No hash, no need to check for a password return void todo(); } @@ -334,9 +366,8 @@ define([ password = val; }), parsed.getUrl()); }).nThen(function (w) { - if (!password && !stored && sessionStorage.newPadPassword) { - passwordCfg.value = sessionStorage.newPadPassword; - delete sessionStorage.newPadPassword; + if (!password && !stored && newPadPassword) { + passwordCfg.value = newPadPassword; } if (parsed.type === "file") { @@ -356,7 +387,7 @@ define([ waitFor.abort(); return; } - if (!isNew) { return void todo(); } + if (!e && !isNew) { return void todo(); } if (parsed.hashData.mode === 'view' && (password || !parsed.hashData.password)) { // Error, wrong password stored, the view seed has changed with the password // password will never work @@ -507,18 +538,10 @@ define([ sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); Utils.LocalStore.onLogin(function () { - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.hash = currentPad.hash; - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHash(currentPad.hash); }); Utils.LocalStore.onLogout(function () { - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.hash = currentPad.hash; - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHash(currentPad.hash); sframeChan.event('EV_LOGOUT'); }); @@ -1166,11 +1189,7 @@ define([ var hiddenParsed = Utils.Hash.parsePadUrl(window.location.href); // Update the hash in the address bar - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.href = hiddenParsed.getUrl(opts); - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHref(hiddenParsed.getUrl(opts)); }); @@ -1521,11 +1540,7 @@ define([ // in the address bar Cryptpad.padRpc.onChannelDeleted.reg(function (channel) { if (channel !== secret.channel) { return; } - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.href = currentPad.href; - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHref(currentPad.href); }); // Join the netflux channel @@ -1609,13 +1624,9 @@ define([ Utils.crypto = Utils.Crypto.createEncryptor(Utils.secret.keys); // Update the hash in the address bar - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.hash = newHash; currentPad.hash = newHash; currentPad.href = '/' + parsed.type + '/#' + newHash; - window.onhashchange = ohc; - ohc({reset: true}); + Cryptpad.setTabHash(newHash); // Update metadata values and send new metadata inside parsed = Utils.Hash.parsePadUrl(currentPad.href); From 19f3dfa6771f414d4205741667c56b5c3939aa9d Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 14:39:23 +0100 Subject: [PATCH 28/51] Prompt to store edit link to your drive when you're a team viewer --- www/common/outer/async-store.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 64cb068b1..3e1a5bb69 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1080,6 +1080,11 @@ define([ if (data.teamId && s.id !== data.teamId) { return; } if (storeLocally && s.id) { return; } + // If this is an edit link but we don't have edit rights, this entry is not useful + if (h.mode === "edit" && !s.secondaryKey) { + return; + } + var res = s.manager.findChannel(channel, true); if (res.length) { sendTo.push(s.id); From da31b714822e70af44f5d59a3043064f4fa0e150 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 14:44:14 +0100 Subject: [PATCH 29/51] Fix safe links with wrong access rights --- www/common/sframe-common-outer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 1e149abeb..901577179 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -343,10 +343,11 @@ define([ return void noPadData('NO_RESULT'); } // Data found but weaker? warn + expire = res.expire; if (edit && !res.href) { newHref = res.roHref; + return; } - expire = res.expire; // We have good data, keep the hash in memory newHref = edit ? res.href : (res.roHref || res.href); })); From 7eea957a4d2e3a17b6983323a5c9a0c8768d4bb1 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 15:10:39 +0100 Subject: [PATCH 30/51] Fix issue newPadPath with new templates --- www/common/common-ui-elements.js | 14 ++++++++------ www/common/sframe-common-outer.js | 3 +++ www/secureiframe/main.js | 9 ++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index bc548d8f7..2d0e02b14 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2270,12 +2270,14 @@ define([ icon: h('span.cptools.cptools-new-template') }); } - allData.unshift({ - name: Messages.creation_noTemplate, - id: 0, - //icon: h('span.fa.fa-file') - icon: UI.getFileIcon({type: type}) - }); + if (!privateData.newTemplate) { + allData.unshift({ + name: Messages.creation_noTemplate, + id: 0, + //icon: h('span.fa.fa-file') + icon: UI.getFileIcon({type: type}) + }); + } var redraw = function (index) { if (index < 0) { i = 0; } else if (index > allData.length - 1) { return; } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 901577179..382d6f710 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -488,6 +488,8 @@ define([ fileHost: ApiConfig.fileHost, readOnly: readOnly, isTemplate: isTemplate, + newTemplate: Array.isArray(Cryptpad.initialPath) + && Cryptpad.initialPath[0] === "template", feedbackAllowed: Utils.Feedback.state, isPresent: parsed.hashData && parsed.hashData.present, isEmbed: parsed.hashData && parsed.hashData.embed, @@ -1662,6 +1664,7 @@ define([ nThen(function(waitFor) { if (data.templateId) { if (data.templateId === -1) { + isTemplate = true; initialPathInDrive = ['template']; return; } diff --git a/www/secureiframe/main.js b/www/secureiframe/main.js index eca166592..060248097 100644 --- a/www/secureiframe/main.js +++ b/www/secureiframe/main.js @@ -74,6 +74,7 @@ define([ }; window.addEventListener('message', whenReady); }).nThen(function () { + var isTemplate = config.data.isTemplate; var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); var metaObj; @@ -85,6 +86,12 @@ define([ } metaObj = n; })); + if (typeof(isTemplate) === "undefined") { + Cryptpad.isTemplate(currentPad.href, waitFor(function (err, t) { + if (err) { console.log(err); } + isTemplate = t; + })); + } }).nThen(function (/*waitFor*/) { metaObj.doc = {}; var additionalPriv = { @@ -96,7 +103,7 @@ define([ feedbackAllowed: Utils.Feedback.state, hashes: config.data.hashes, password: config.data.password, - isTemplate: config.data.isTemplate, + isTemplate: isTemplate, file: config.data.file, }; for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; } From 47eace89bfe05cb8d6f4fdd7a435c1b7d579d6e9 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:14:39 +0100 Subject: [PATCH 31/51] Encrypt password before putting it in the hash --- www/common/outer/async-store.js | 1 + www/common/outer/mailbox-handlers.js | 8 +++++++- www/common/sframe-common-outer.js | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 3e1a5bb69..138993607 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2647,6 +2647,7 @@ define([ classic: true, }; var rt = window.rt = Listmap.create(listmapConfig); + store.driveSecret = secret; store.proxy = rt.proxy; store.loggedIn = typeof(data.userHash) !== "undefined"; diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 573879061..e5080b8e5 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -2,7 +2,8 @@ define([ '/common/common-messaging.js', '/common/common-hash.js', '/common/common-util.js', -], function (Messaging, Hash, Util) { + '/bower_components/chainpad-crypto/crypto.js', +], function (Messaging, Hash, Util, Crypto) { // Random timeout between 10 and 30 times your sync time (lag + chainpad sync) var getRandomTimeout = function (ctx) { @@ -221,6 +222,11 @@ define([ toRemove = old.data; } + if (content.password) { + var key = ctx.store.driveSecret.keys.cryptKey; + content.password = Crypto.encrypt(content.password, key); + } + // Update the data channels[channel] = { mode: mode, diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 382d6f710..0e51b1794 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -207,7 +207,14 @@ define([ var newPad = JSON.parse(decodeURIComponent(parsed.hashData.newPadOpts)); Cryptpad.initialTeam = newPad.t; Cryptpad.initialPath = newPad.p; - newPadPassword = newPad.pw; + if (newPad.pw) { + try { + var uHash = Utils.LocalStore.getUserHash(); + var uSecret = Utils.Hash.getSecrets('drive', uHash); + var uKey = uSecret.keys.cryptKey; + newPadPassword = Crypto.decrypt(newPad.pw, uKey); + } catch (e) { console.error(e); } + } if (newPad.d) { Cryptpad.fromFileData = newPad.d; var _parsed1 = Utils.Hash.parsePadUrl(Cryptpad.fromFileData.href); From f0eda8b795f1dbaba525460244de979909a6811d Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:16:41 +0100 Subject: [PATCH 32/51] Fix 'store pad prompt' always displayed --- www/common/outer/async-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 138993607..653c4d1f5 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1081,7 +1081,7 @@ define([ if (storeLocally && s.id) { return; } // If this is an edit link but we don't have edit rights, this entry is not useful - if (h.mode === "edit" && !s.secondaryKey) { + if (h.mode === "edit" && s.id && !s.secondaryKey) { return; } From 85f83eb97672e29972017f80b6ae7e9db809c8d8 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:26:23 +0100 Subject: [PATCH 33/51] 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 02966d0a5d463b7287f6a4da21dc9012e445318f Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 13:44:51 +0530 Subject: [PATCH 34/51] 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 a579b13ab36cf1b780367378f12cd19da657cdec Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 17:03:51 +0530 Subject: [PATCH 35/51] 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 589d32c0629c6a98cc5e3efaee6398468808e657 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 30 Oct 2020 15:00:12 +0100 Subject: [PATCH 36/51] Stop using sessionStorage for login redirect and new pad options --- customize.dist/login.js | 31 +++++++-- customize.dist/pages/features.js | 8 --- customize.dist/pages/index.js | 8 ++- www/common/common-hash.js | 97 +++++++++++++++++++++++----- www/common/common-ui-elements.js | 46 +++---------- www/common/cryptpad-common.js | 20 ++---- www/common/drive-ui.js | 19 ++---- www/common/notifications.js | 13 ++-- www/common/onlyoffice/inner.js | 8 +-- www/common/outer/mailbox-handlers.js | 5 ++ www/common/sframe-common-outer.js | 65 +++++++++++-------- www/common/sframe-common.js | 9 +-- www/common/toolbar.js | 8 +-- www/debug/main.js | 7 +- www/drive/main.js | 27 ++++---- www/file/inner.js | 4 +- www/profile/inner.js | 8 +-- www/secureiframe/main.js | 6 -- www/teams/inner.js | 12 +--- 19 files changed, 207 insertions(+), 194 deletions(-) diff --git a/customize.dist/login.js b/customize.dist/login.js index 35121f113..d07d4c4c3 100644 --- a/customize.dist/login.js +++ b/customize.dist/login.js @@ -28,6 +28,19 @@ define([ }; var Nacl = window.nacl; + + var redirectTo = '/drive/'; + var setRedirectTo = function () { + var parsed = Hash.parsePadUrl(window.location.href); + if (parsed.hashData && parsed.hashData.newPadOpts) { + var newPad = Hash.decodeDataOptions(parsed.hashData.newPadOpts); + redirectTo = newPad.href; + } + }; + if (window.location.hash) { + setRedirectTo(); + } + var allocateBytes = Exports.allocateBytes = function (bytes) { var dispense = Cred.dispenser(bytes); @@ -118,11 +131,11 @@ define([ }; var setMergeAnonDrive = function () { - sessionStorage.migrateAnonDrive = 1; + Exports.mergeAnonDrive = 1; }; var setCreateReadme = function () { - sessionStorage.createReadme = 1; + Exports.createReadme = 1; }; Exports.loginOrRegister = function (uname, passwd, isRegister, shouldImport, cb) { @@ -416,12 +429,20 @@ define([ }); }; Exports.redirect = function () { - if (sessionStorage.redirectTo) { - var h = sessionStorage.redirectTo; + if (redirectTo) { + var h = redirectTo; + var loginOpts = {}; + if (Exports.mergeAnonDrive) { + loginOpts.mergeAnonDrive = 1; + } + if (Exports.createReadme) { + loginOpts.createReadme = 1; + } + h = Hash.getLoginURL(h, loginOpts); + var parser = document.createElement('a'); parser.href = h; if (parser.origin === window.location.origin) { - delete sessionStorage.redirectTo; window.location.href = h; return; } diff --git a/customize.dist/pages/features.js b/customize.dist/pages/features.js index 6403ec36a..56bb29664 100644 --- a/customize.dist/pages/features.js +++ b/customize.dist/pages/features.js @@ -22,14 +22,6 @@ define([ target: '_blank', rel: 'noopener noreferrer' }, h('button.cp-features-register-button', Msg.features_f_subscribe)); - /*$(premiumButton).click(function (e) { - if (LocalStore.isLoggedIn()) { return; } - // Not logged in: go to /login with a redirect to this page - e.preventDefault(); - e.stopPropagation(); - sessionStorage.redirectTo = '/features.html'; - window.location.href = '/login/'; - });*/ var anonymousFeatures = h('div.col-12.col-sm-4.cp-anon-user',[ diff --git a/customize.dist/pages/index.js b/customize.dist/pages/index.js index 300637748..c718b363f 100644 --- a/customize.dist/pages/index.js +++ b/customize.dist/pages/index.js @@ -4,12 +4,13 @@ define([ '/common/hyperscript.js', '/common/common-feedback.js', '/common/common-interface.js', + '/common/common-hash.js', '/common/textFit.min.js', '/customize/messages.js', '/customize/application_config.js', '/common/outer/local-store.js', '/customize/pages.js' -], function ($, Config, h, Feedback, UI, TextFit, Msg, AppConfig, LocalStore, Pages) { +], function ($, Config, h, Feedback, UI, Hash, TextFit, Msg, AppConfig, LocalStore, Pages) { var urlArgs = Config.requireConf.urlArgs; var isAvailableType = function (x) { @@ -46,8 +47,9 @@ define([ var href = '/'+ x[0] +'/'; var attr = isEnabled ? { href: href } : { onclick: function () { - sessionStorage.redirectTo = href; - window.location.href = '/login/'; + var href = Hash.hashToHref('', 'login'); + var url = Hash.getNewPadURL(href, { href: href }); + window.location.href = url; } }; if (!isEnabled) { diff --git a/www/common/common-hash.js b/www/common/common-hash.js index ff8764da4..06a1d7fef 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -169,6 +169,17 @@ Version 1 /code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI */ + var getLoginOpts = function (hashArr) { + var k; + // Check if we have a ownerKey for this pad + hashArr.some(function (data) { + if (/^login=/.test(data)) { + k = data.slice(6); + return true; + } + }); + return k || ''; + }; var getNewPadOpts = function (hashArr) { var k; // Check if we have a ownerKey for this pad @@ -214,28 +225,51 @@ Version 1 parsed.embed = options.indexOf('embed') !== -1; parsed.versionHash = getVersionHash(options); parsed.newPadOpts = getNewPadOpts(options); + parsed.loginOpts = getLoginOpts(options); parsed.ownerKey = getOwnerKey(options); }; + // Version 4: only login or newpad options, smae for all the apps + if (hashArr[1] && hashArr[1] === '4') { + parsed.getHash = function (opts) { + if (!opts || !Object.keys(opts).length) { return ''; } + var hash = '/4/'; + if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } + if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } + return hash; + }; + parsed.getOptions = function () { + var options = {}; + if (parsed.newPadOpts) { options.newPadOpts = parsed.newPadOpts; } + if (parsed.loginOpts) { options.loginOpts = parsed.loginOpts; } + return options; + }; + + parsed.version = 4; + options = hashArr.slice(2); + addOptions(); + + return parsed; + } + + // The other versions depends on the type if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) { parsed.type = 'pad'; - parsed.getHash = function () { return hash; }; + parsed.getHash = function () { + return hash; + }; parsed.getOptions = function () { return { embed: parsed.embed, present: parsed.present, ownerKey: parsed.ownerKey, versionHash: parsed.versionHash, + newPadOpts: parsed.newPadOpts, + loginOpts: parsed.loginOpts, password: parsed.password }; }; - if (/^\/newpad=/.test(hash)) { - return { - newPadOpts: hash.slice(8, -1) - }; - } - if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0 // Old hash parsed.channel = hash.slice(0, 32); @@ -256,9 +290,8 @@ Version 1 if (versionHash) { hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/'; } - if (opts.newPadOpts) { - hash += 'newpad=' + opts.newPadOpts + '/'; - } + if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } + if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } return hash; }; @@ -306,6 +339,8 @@ Version 1 embed: parsed.embed, present: parsed.present, ownerKey: parsed.ownerKey, + newPadOpts: parsed.newPadOpts, + loginOpts: parsed.loginOpts, password: parsed.password }; }; @@ -317,6 +352,8 @@ Version 1 if (parsed.password || opts.password) { hash += 'p/'; } if (opts.embed) { hash += 'embed/'; } if (opts.present) { hash += 'present/'; } + if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } + if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } return hash; }; @@ -389,18 +426,26 @@ Version 1 var idx; + // When we start without a hash, use version 4 links to add login or newpad options + var getHash = function (opts) { + if (!opts || !Object.keys(opts).length) { return ''; } + var hash = '/4/'; + if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } + if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } + return hash; + }; ret.getUrl = function (options) { options = options || {}; var url = '/'; if (!ret.type) { return url; } url += ret.type + '/'; // New pad with options: append the options to the hash - if (!ret.hashData && options.newPadOpts) { - return url + '#/newpad=' + options.newPadOpts + '/'; + if (!ret.hashData && options && Object.keys(options).length) { + return url + '#' + getHash(options); } if (!ret.hashData) { return url; } - if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; } - if (ret.hashData.version === 0) { return url + '#' + ret.hash; } + //if (ret.hashData.version === 0) { return url + '#' + ret.hash; } + //if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; } var hash = ret.hashData.getHash(options); url += '#' + hash; return url; @@ -549,7 +594,6 @@ Version 1 secret = JSON.parse(JSON.stringify(secret)); if (!secret.keys && !secret.key) { - console.error('e'); return hashes; } else if (!secret.keys) { secret.keys = {}; @@ -615,6 +659,29 @@ Version 1 return true; }; + Hash.decodeDataOptions = function (opts) { + var b64 = decodeURIComponent(opts); + var str = Nacl.util.encodeUTF8(Nacl.util.decodeBase64(b64)); + return JSON.parse(str); + }; + Hash.encodeDataOptions = function (opts) { + var str = JSON.stringify(opts); + var b64 = Nacl.util.encodeBase64(Nacl.util.decodeUTF8(str)); + return encodeURIComponent(b64); + }; + Hash.getNewPadURL = function (href, opts) { + var parsed = Hash.parsePadUrl(href); + var options = parsed.getOptions(); + options.newPadOpts = Hash.encodeDataOptions(opts); + return parsed.getUrl(options); + }; + Hash.getLoginURL = function (href, opts) { + var parsed = Hash.parsePadUrl(href); + var options = parsed.getOptions(); + options.loginOpts = Hash.encodeDataOptions(opts); + return parsed.getUrl(options); + }; + return Hash; }; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 2d0e02b14..e977f1f49 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -254,20 +254,15 @@ define([ className: 'secondary', name: Messages.login_register, onClick: function () { - common.setLoginRedirect(function () { - common.gotoURL('/register/'); - }); + common.setLoginRedirect('register'); } }, { className: 'secondary', name: Messages.login_login, onClick: function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); } - } - ] + }] }; } }; @@ -1788,9 +1783,7 @@ define([ attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'}, content: h('span', Messages.login_login), action: function () { - Common.setLoginRedirect(function () { - window.parent.location = origin+'/login/'; - }); + Common.setLoginRedirect('login'); }, }); options.push({ @@ -1798,9 +1791,7 @@ define([ attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'}, content: h('span', Messages.login_register), action: function () { - Common.setLoginRedirect(function () { - window.parent.location = origin+'/register/'; - }); + Common.setLoginRedirect('register'); }, }); } @@ -1925,8 +1916,6 @@ define([ $modal.find('.cp-modal').append($title); $modal.find('.cp-modal').append($description); - var $advanced; - var $container = $('
      '); var i = 0; var types = AppConfig.availablePadTypes.filter(function (p) { @@ -1950,15 +1939,7 @@ define([ $element.attr('data-type', p); $element.click(function () { $modal.hide(); - if ($advanced && Util.isChecked($advanced)) { - common.sessionStorage.put(Constants.displayPadCreationScreen, true, function (){ - common.openURL('/' + p + '/'); - }); - return; - } - common.sessionStorage.put(Constants.displayPadCreationScreen, "", function () { - common.openURL('/' + p + '/'); - }); + common.openURL('/' + p + '/'); }); }); @@ -1983,12 +1964,6 @@ define([ } return; } - if (e.which === 32 && $advanced) { - $advanced.prop('checked', !$advanced.prop('checked')); - $modal.focus(); - e.stopPropagation(); - e.preventDefault(); - } }); @@ -2753,13 +2728,8 @@ define([ $(link).click(function (e) { e.preventDefault(); e.stopPropagation(); - if (msg.content.password) { - common.sessionStorage.put('newPadPassword', msg.content.password, function () { - common.openURL(msg.content.href); - }); - return; - } - common.openURL(msg.content.href); + var obj = { pw: msg.content.password || '' }; + common.openURL(Hash.getNewPadURL(msg.content.href, obj)); }); var div = h('div', [ diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 70ebe5bcc..36413d89d 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1880,8 +1880,9 @@ define([ LocalStore.logout(); // redirect them to log in, and come back when they're done. - sessionStorage.redirectTo = currentPad.href; - window.location.href = '/login/'; + var href = Hash.hashToHref('', 'login'); + var url = Hash.getNewPadURL(href, { href: currentPad.href }); + window.location.href = url; }; common.startAccountDeletion = function (data, cb) { @@ -2277,12 +2278,6 @@ define([ if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); } - /*if (cfg.userHash && sessionStorage) { - // copy User_hash into sessionStorage because cross-domain iframes - // on safari replaces localStorage with sessionStorage or something - sessionStorage.setItem(Constants.userHashKey, cfg.userHash); - }*/ - if (cfg.userHash) { var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); if (localToken === null) { @@ -2337,21 +2332,18 @@ define([ postMessage("DISCONNECT"); }); }).nThen(function (waitFor) { - if (sessionStorage.createReadme) { + if (common.createReadme) { var data = { driveReadme: Messages.driveReadme, driveReadmeTitle: Messages.driveReadmeTitle, }; postMessage("CREATE_README", data, waitFor(function (e) { if (e && e.error) { return void console.error(e.error); } - delete sessionStorage.createReadme; })); } }).nThen(function (waitFor) { - if (sessionStorage.migrateAnonDrive) { - common.mergeAnonDrive(waitFor(function() { - delete sessionStorage.migrateAnonDrive; - })); + if (common.migrateAnonDrive) { + common.mergeAnonDrive(waitFor()); } }).nThen(function (waitFor) { if (AppConfig.afterLogin) { diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 08a3f1279..53e496ef4 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -1126,16 +1126,13 @@ define([ window.open(APP.origin + hiddenHref); }; var openIn = function (type, path, team, fData) { - var obj = JSON.stringify({ + var obj = { p: path, t: team, d: fData - }); - var str = encodeURIComponent(obj); - var parsed = Hash.parsePadUrl(Hash.hashToHref('', type)); - var opts = parsed.getOptions(); - opts.newPadOpts = str; - common.openURL(parsed.getUrl(opts)); + }; + var href = Hash.hashToHref('', type); + common.openURL(Hash.getNewPadURL(href, obj)); }; var refresh = APP.refresh = function () { @@ -4252,13 +4249,6 @@ define([ if (paths.length !== 1) { return; } var p = paths[0]; el = manager.find(p.path); - var metadata = manager.getFileData(el); - var simpleData = { - title: metadata.filename || metadata.title, - href: metadata.href, - password: metadata.password, - channel: metadata.channel, - }; (function () { var path = currentPath; if (path[0] !== ROOT) { path = [ROOT]; } @@ -4269,7 +4259,6 @@ define([ password: _metadata.password, channel: _metadata.channel, }; - var parsed = Hash.parsePadUrl(_metadata.href || _metadata.roHref); openIn('code', path, APP.team, _simpleData); })(); } diff --git a/www/common/notifications.js b/www/common/notifications.js index 1686b87f9..caf5bfe00 100644 --- a/www/common/notifications.js +++ b/www/common/notifications.js @@ -7,8 +7,7 @@ define([ '/common/common-util.js', '/common/common-constants.js', '/customize/messages.js', - '/bower_components/nthen/index.js' -], function($, h, Hash, UI, UIElements, Util, Constants, Messages, nThen) { +], function($, h, Hash, UI, UIElements, Util, Constants, Messages) { var handlers = {}; @@ -108,16 +107,12 @@ define([ return Messages._getKey(key, [name, title, teamName]); }; content.handler = function() { - var obj = JSON.stringify({ + var obj = { p: msg.content.isTemplate ? ['template'] : undefined, t: teamNotification || undefined, pw: msg.content.password || '' - }); - var str = encodeURIComponent(obj); - var parsed = Hash.parsePadUrl(msg.content.href); - var opts = parsed.getOptions(); - opts.newPadOpts = str; - common.openURL(parsed.getUrl(opts)); + }; + common.openURL(Hash.getNewPadURL(msg.content.href, obj)); defaultDismiss(common, data)(); }; if (!content.archived) { diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index c265d2d2b..ce0dbcc3f 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -460,15 +460,11 @@ define([ var actions = h('div', [cancel, register, login]); var modal = UI.cornerPopup(Messages.oo_login, actions, '', {alt: true}); $(register).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/register/'); - }); + common.setLoginRedirect('register'); modal.delete(); }); $(login).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); modal.delete(); }); $(cancel).click(function () { diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index e5080b8e5..326b4afc7 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -321,6 +321,11 @@ define([ var channel = content.channel || content.teamChannel; + if (content.password) { + var key = ctx.store.driveSecret.keys.cryptKey; + content.password = Crypto.encrypt(content.password, key); + } + if (addOwners[channel]) { return void cb(true); } addOwners[channel] = { type: box.type, diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 0e51b1794..01a5ceaf8 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -131,12 +131,28 @@ define([ if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); } }); + try { + var parsed = Utils.Hash.parsePadUrl(currentPad.href); + var options = parsed.getOptions(); + if (options.loginOpts) { + var loginOpts = Utils.Hash.decodeDataOptions(options.loginOpts); + if (loginOpts.createReadme) { Cryptpad.createReadme = true; } + if (loginOpts.mergeAnonDrive) { Cryptpad.migrateAnonDrive = true; } + // Remove newPadOpts from the hash + delete options.loginOpts; + currentPad.href = parsed.getUrl(options); + currentPad.hash = parsed.hashData.getHash ? parsed.hashData.getHash(options) + : ''; + } + } catch (e) { console.error(e); } + Cryptpad.ready(waitFor(), { driveEvents: cfg.driveEvents, currentPad: currentPad }); - if (window.history && window.history.replaceState && currentPad.hash) { + // Remove the login hash if needed + if (window.history && window.history.replaceState && (currentPad.hash || window.location.hash)) { var nHash = currentPad.hash; if (!/^#/.test(nHash)) { nHash = '#' + nHash; } window.history.replaceState({}, window.document.title, nHash); @@ -202,9 +218,10 @@ define([ }; // New pad options - if (parsed.hashData && parsed.hashData.newPadOpts) { + var options = parsed.getOptions(); + if (options.newPadOpts) { try { - var newPad = JSON.parse(decodeURIComponent(parsed.hashData.newPadOpts)); + var newPad = Utils.Hash.decodeDataOptions(options.newPadOpts); Cryptpad.initialTeam = newPad.t; Cryptpad.initialPath = newPad.p; if (newPad.pw) { @@ -225,22 +242,20 @@ define([ } catch (e) { console.error(e, parsed.hashData.newPadOpts); } - delete parsed.hashData.newPadOpts; + delete options.newPadOpts; + + currentPad.href = parsed.getUrl(options); + currentPad.hash = parsed.hashData.getHash ? parsed.hashData.getHash(options) + : ''; + var version = parsed.hashData.version; + parsed = Utils.Hash.parsePadUrl(currentPad.href); + Cryptpad.setTabHash(currentPad.hash); // If it's a new pad, don't check password - if (!Object.keys(parsed.hashData).length) { - delete parsed.hashData; - parsed.hash = ''; - currentPad.hash = ''; - Cryptpad.setTabHash(''); + if (version === 4) { return void todo(); } - // Otherwise, existing pad (new for us) - var opts = parsed.getOptions(); - delete opts.newPadOpts; - parsed = Utils.Hash.parsePadUrl(parsed.getUrl(opts)); - currentPad.hash = parsed.hash; - Cryptpad.setTabHash(parsed.hash); + // Otherwise, continue } @@ -460,9 +475,6 @@ define([ var defaultTitle = Utils.UserObject.getDefaultName(parsed); var edPublic, curvePublic, notifications, isTemplate; var settings = {}; - var forceCreationScreen = cfg.useCreationScreen && - sessionStorage[Utils.Constants.displayPadCreationScreen]; - delete sessionStorage[Utils.Constants.displayPadCreationScreen]; var isSafe = ['debug', 'profile', 'drive', 'teams'].indexOf(currentPad.app) !== -1; var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); @@ -507,7 +519,6 @@ define([ }, isNewFile: isNewFile, isDeleted: isNewFile && currentPad.hash.length > 0, - forceCreationScreen: forceCreationScreen, password: password, channel: secret.channel, enableSF: localStorage.CryptPad_SF === "1", // TODO to remove when enabled by default @@ -635,9 +646,10 @@ define([ Cryptpad.mailbox.execCommand(data, cb); }); - sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) { - sessionStorage.redirectTo = currentPad.href; - cb(); + sframeChan.on('EV_SET_LOGIN_REDIRECT', function (page) { + var href = Utils.Hash.hashToHref('', page); + var url = Utils.Hash.getNewPadURL(href, { href: currentPad.href }); + window.location.href = url; }); sframeChan.on('Q_STORE_IN_TEAM', function (data, cb) { @@ -1065,11 +1077,10 @@ define([ password: password, title: currentTitle }; - sessionStorage[Utils.Constants.newPadFileData] = JSON.stringify(data); - window.open(window.location.pathname); - setTimeout(function () { - delete sessionStorage[Utils.Constants.newPadFileData]; - }, 100); + var obj = { d: data }; + var href = window.location.pathname; + var url = Utils.Hash.getNewPadURL(href, obj); + window.open(url); }); // Messaging diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index f7a2c41c3..ffff122b5 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -307,9 +307,8 @@ define([ ctx.sframeChan.event('EV_SET_HASH', hash); }; - funcs.setLoginRedirect = function (cb) { - cb = cb || $.noop; - ctx.sframeChan.query('Q_SET_LOGIN_REDIRECT', null, cb); + funcs.setLoginRedirect = function (page) { + ctx.sframeChan.query('EV_SET_LOGIN_REDIRECT', page); }; funcs.isPresentUrl = function (cb) { @@ -758,9 +757,7 @@ define([ var mustLogin = privateData.registeredOnly; if (mustLogin) { UI.alert(Messages.mustLogin, function () { - funcs.setLoginRedirect(function () { - funcs.gotoURL('/login/'); - }); + funcs.setLoginRedirect('login'); }, {forefront: true}); return; } diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 9a96039f6..78e4e91f3 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -773,15 +773,11 @@ MessengerUI, Messages) { ]); $msg.find('a.cp-pnp-login').click(function (ev) { ev.preventDefault(); - Common.setLoginRedirect(function () { - window.parent.location = o + '/login/'; - }); + Common.setLoginRedirect('login'); }); $msg.find('a.cp-pnp-register').click(function (ev) { ev.preventDefault(); - Common.setLoginRedirect(function () { - window.parent.location = o + '/register/'; - }); + Common.setLoginRedirect('register'); }); $('.cp-toolbar-top').append($msg); //UI.addTooltips(); diff --git a/www/debug/main.js b/www/debug/main.js index 68055b1d4..b901beec0 100644 --- a/www/debug/main.js +++ b/www/debug/main.js @@ -53,14 +53,9 @@ define([ }; window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { - var hash = localStorage[Constants.userHashKey]; + var hash = localStorage[Constants.userHashKey] || localStorage[Constants.fileHashKey]; var drive = hash && ('#'+hash === window.location.hash); if (!window.location.hash) { - if (!hash) { - sessionStorage.redirectTo = '/debug/'; - window.location.href = '/login/'; - return; - } drive = true; window.location.hash = hash; } else { diff --git a/www/drive/main.js b/www/drive/main.js index 8db013c0f..5eb9dbc67 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -46,9 +46,12 @@ define([ window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { var afterSecrets = function (Cryptpad, Utils, secret, cb, sframeChan) { - var _hash = hash.slice(1); - if (_hash && Utils.LocalStore.isLoggedIn()) { - // Add a shared folder! + var parsed = Utils.Hash.parsePadUrl(href); + var isSf = parsed.hashData && parsed.hashData.type === 'pad'; + if (!isSf) { return void cb(); } + + // SF and logged in: add shared folder + if (Utils.LocalStore.isLoggedIn()) { Cryptpad.addSharedFolder(null, secret, function (id) { if (id && typeof(id) === "object" && id.error) { sframeChan.event("EV_RESTRICTED_ERROR"); @@ -65,16 +68,16 @@ define([ cb(); }); return; - } else if (_hash) { - var id = Utils.Util.createRandomInteger(); - window.CryptPad_newSharedFolder = id; - var data = { - href: Utils.Hash.getRelativeHref(Cryptpad.currentPad.href), - password: secret.password - }; - return void Cryptpad.loadSharedFolder(id, data, cb); } - cb(); + + // Anon shared folder + var id = Utils.Util.createRandomInteger(); + window.CryptPad_newSharedFolder = id; + var data = { + href: Utils.Hash.getRelativeHref(Cryptpad.currentPad.href), + password: secret.password + }; + Cryptpad.loadSharedFolder(id, data, cb); }; var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('EV_BURN_ANON_DRIVE', function () { diff --git a/www/file/inner.js b/www/file/inner.js index 81019d0be..b2aef53ed 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -231,9 +231,7 @@ define([ if (!common.isLoggedIn()) { UI.removeLoadingScreen(); return UI.alert(Messages.upload_mustLogin, function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); }); } diff --git a/www/profile/inner.js b/www/profile/inner.js index cddae4611..7a872fdd0 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -611,15 +611,11 @@ define([ var actions = h('div', [cancel, register, login]); var modal = UI.cornerPopup(Messages.profile_login, actions, '', {alt: true}); $(register).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/register/'); - }); + common.setLoginRedirect('register'); modal.delete(); }); $(login).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); modal.delete(); }); $(cancel).click(function () { diff --git a/www/secureiframe/main.js b/www/secureiframe/main.js index 060248097..49e94a252 100644 --- a/www/secureiframe/main.js +++ b/www/secureiframe/main.js @@ -86,12 +86,6 @@ define([ } metaObj = n; })); - if (typeof(isTemplate) === "undefined") { - Cryptpad.isTemplate(currentPad.href, waitFor(function (err, t) { - if (err) { console.log(err); } - isTemplate = t; - })); - } }).nThen(function (/*waitFor*/) { metaObj.doc = {}; var additionalPriv = { diff --git a/www/teams/inner.js b/www/teams/inner.js index 2eb234b12..a75ed94db 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -1267,14 +1267,10 @@ define([ anonRegister = h('button.btn.btn-secondary', Messages.login_register), ])); $(anonLogin).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); }); $(anonRegister).click(function () { - common.setLoginRedirect(function () { - common.gotoURL('/register/'); - }); + common.setLoginRedirect('register'); }); waitFor.abort(); }).nThen(function () { @@ -1402,9 +1398,7 @@ define([ var hash = privateData.teamInviteHash; if (!hash && !driveAPP.loggedIn) { UI.alert(Messages.mustLogin, function () { - common.setLoginRedirect(function () { - common.gotoURL('/login/'); - }); + common.setLoginRedirect('login'); }, {forefront: true}); return; } From 0e0c224ab187187c03d80c27e779bf10b5926086 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 30 Oct 2020 15:05:28 +0100 Subject: [PATCH 37/51] 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 01a5ceaf8..d476d02d4 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); From 926b1d842f5ee84ef6bb0aba16fe93549ba2134a Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 30 Oct 2020 15:13:22 +0100 Subject: [PATCH 38/51] Stop using sessionStorage in CryptPad --- www/auth/index.html | 9 - www/auth/main.js | 193 --------------------- www/common/common-constants.js | 4 - www/common/onlyoffice/main.js | 2 +- www/common/outer/async-store.js | 1 - www/common/outer/local-store.js | 16 -- www/common/sframe-channel.js | 166 ------------------ www/common/sframe-common-outer.js | 9 - www/common/sframe-common.js | 9 - www/common/sframe-protocol.js | 271 ------------------------------ www/login/main.js | 6 +- www/logout/main.js | 1 - www/register/main.js | 6 +- 13 files changed, 6 insertions(+), 687 deletions(-) delete mode 100644 www/auth/index.html delete mode 100644 www/auth/main.js delete mode 100644 www/common/sframe-channel.js delete mode 100644 www/common/sframe-protocol.js diff --git a/www/auth/index.html b/www/auth/index.html deleted file mode 100644 index 685ca37c4..000000000 --- a/www/auth/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/www/auth/main.js b/www/auth/main.js deleted file mode 100644 index fcbeaf3af..000000000 --- a/www/auth/main.js +++ /dev/null @@ -1,193 +0,0 @@ -define([ - 'jquery', - '/api/config', - '/common/cryptget.js', - '/common/pinpad.js', - '/common/common-constants.js', - '/common/common-hash.js', - '/common/outer/local-store.js', - '/common/outer/login-block.js', - '/common/outer/network-config.js', - '/customize/login.js', - '/common/test.js', - '/bower_components/nthen/index.js', - '/bower_components/netflux-websocket/netflux-client.js', - '/bower_components/tweetnacl/nacl-fast.min.js' -], function ($, ApiConfig, Crypt, Pinpad, Constants, Hash, LocalStore, Block, NetConfig, Login, Test, nThen, Netflux) { - var Nacl = window.nacl; - - var signMsg = function (msg, privKey) { - var signKey = Nacl.util.decodeBase64(privKey); - var buffer = Nacl.util.decodeUTF8(msg); - return Nacl.util.encodeBase64(Nacl.sign(buffer, signKey)); - }; - - // TODO: Allow authing for any domain as long as the user clicks an "accept" button - // inside of the iframe. - var AUTHORIZED_DOMAINS = [ - /\.cryptpad\.fr$/, - /^http(s)?:\/\/localhost\:/ - ]; - - // Safari is weird about localStorage in iframes but seems to let sessionStorage slide. - localStorage[Constants.userHashKey] = localStorage[Constants.userHashKey] || - sessionStorage[Constants.userHashKey]; - - var proxy; - var rpc; - var network; - var rpcError; - var contacts = {}; - - var loadProxy = function (hash) { - nThen(function (waitFor) { - var wsUrl = NetConfig.getWebsocketURL(); - var w = waitFor(); - Netflux.connect(wsUrl).then(function (_network) { - network = _network; - w(); - }, function (err) { - rpcError = err; - console.error(err); - }); - }).nThen(function (waitFor) { - Crypt.get(hash, waitFor(function (err, val) { - if (err) { - waitFor.abort(); - console.error(err); - return; - } - try { - var parsed = JSON.parse(val); - proxy = parsed; - } catch (e) { - console.log("Can't parse user drive", e); - } - }), { - network: network - }); - }).nThen(function () { - var origin = ApiConfig.fileHost || window.location.origin; - // Get contacts and extract their avatar channel and key - var getData = function (obj, href) { - var parsed = Hash.parsePadUrl(href); - if (!parsed || parsed.type !== "file") { return; } - var secret = Hash.getSecrets('file', parsed.hash); - if (!secret.keys || !secret.channel) { return; } - obj.avatarKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey); - obj.avatarSrc = origin + Hash.getBlobPathFromHex(secret.channel); - }; - contacts.teams = proxy.teams || {}; - contacts.friends = proxy.friends || {}; - Object.keys(contacts.friends).map(function (key) { - var friend = contacts.friends[key]; - if (!friend) { return; } - var ret = { - edPublic: friend.edPublic, - name: friend.displayName, - }; - getData(ret, friend.avatar); - contacts.friends[key] = ret; - }); - Object.keys(contacts.teams).map(function (key) { - var team = contacts.teams[key]; - if (!team) { return; } - var avatar = team.metadata && team.metadata.avatar; - var ret = { - edPublic: team.keys && team.keys.drive && team.keys.drive.edPublic, - name: team.metadata && team.metadata.name - }; - getData(ret, avatar); - contacts.teams[key] = ret; - }); - contacts.origin = window.location.origin; - }).nThen(function (waitFor) { - if (!network) { return void waitFor.abort(); } - Pinpad.create(network, proxy, waitFor(function (e, call) { - if (e) { - rpcError = e; - return void waitFor.abort(); - } - rpc = call; - })); - }).nThen(function () { - Test(function () { - // This is only here to maybe trigger an error. - window.drive = proxy['drive']; - Test.passed(); - }); - }); - }; - - var whenReady = function (cb) { - if (proxy && (rpc || rpcError)) { return void cb(); } - console.log('CryptPad not ready...'); - setTimeout(function () { - whenReady(cb); - }, 100); - }; - - $(window).on("message", function (jqe) { - var evt = jqe.originalEvent; - var data = JSON.parse(evt.data); - var domain = evt.origin; - var srcWindow = evt.source; - var ret = { txid: data.txid }; - console.log('CP receiving', data); - if (data.cmd === 'PING') { - ret.res = 'PONG'; - } else if (data.cmd === 'LOGIN') { - Login.loginOrRegister(data.data.name, data.data.password, false, false, function (err) { - if (err) { - ret.error = 'LOGIN_ERROR'; - srcWindow.postMessage(JSON.stringify(ret), domain); - return; - } - loadProxy(LocalStore.getUserHash()); - srcWindow.postMessage(JSON.stringify(ret), domain); - }); - return; - } else if (data.cmd === 'SIGN') { - if (!AUTHORIZED_DOMAINS.filter(function (x) { return x.test(domain); }).length) { - ret.error = "UNAUTH_DOMAIN"; - } else if (!LocalStore.isLoggedIn()) { - ret.error = "NOT_LOGGED_IN"; - } else { - return void whenReady(function () { - var sig = signMsg(data.data, proxy.edPrivate); - ret.res = { - uname: proxy.login_name, - edPublic: proxy.edPublic, - sig: sig - }; - ret.contacts = contacts; - srcWindow.postMessage(JSON.stringify(ret), domain); - }); - } - } else if (data.cmd === 'UPDATE_LIMIT') { - return void whenReady(function () { - if (rpcError) { - // Tell the user on accounts that there was an issue and they need to wait maximum 24h or contact an admin - ret.warning = true; - srcWindow.postMessage(JSON.stringify(ret), domain); - return; - } - rpc.updatePinLimits(function (e, limit, plan, note) { - if (e) { - ret.warning = true; - } - ret.res = [limit, plan, note]; - srcWindow.postMessage(JSON.stringify(ret), domain); - }); - }); - } else { - ret.error = "UNKNOWN_CMD"; - } - srcWindow.postMessage(JSON.stringify(ret), domain); - }); - - var userHash = LocalStore.getUserHash(); - if (userHash) { - loadProxy(userHash); - } -}); diff --git a/www/common/common-constants.js b/www/common/common-constants.js index 553665574..17db2302c 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -5,10 +5,6 @@ define(['/customize/application_config.js'], function (AppConfig) { userNameKey: 'User_name', blockHashKey: 'Block_hash', fileHashKey: 'FS_hash', - // sessionStorage - newPadPathKey: "newPadPath", - newPadTeamKey: "newPadTeam", - newPadFileData: "newPadFileData", // Store displayNameKey: 'cryptpad.username', oldStorageKey: 'CryptPad_RECENTPADS', diff --git a/www/common/onlyoffice/main.js b/www/common/onlyoffice/main.js index 83d887548..0560b95bd 100644 --- a/www/common/onlyoffice/main.js +++ b/www/common/onlyoffice/main.js @@ -55,7 +55,7 @@ define([ var addData = function (obj) { obj.ooType = window.location.pathname.replace(/^\//, '').replace(/\/$/, ''); obj.ooVersionHash = version; - obj.ooForceVersion = localStorage.CryptPad_ooVersion || sessionStorage.CryptPad_ooVersion || ""; + obj.ooForceVersion = localStorage.CryptPad_ooVersion || ""; }; var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('Q_OO_SAVE', function (data, cb) { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 653c4d1f5..62e078050 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2730,7 +2730,6 @@ define([ * - userHash or anonHash * Todo in cb * - LocalStore.setFSHash if needed - * - sessionStorage.User_Hash * - stuff with tokenKey * Event to outer * - requestLogin diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 774924148..d15c2e8c6 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -82,19 +82,6 @@ define([ localStorage.setItem(Constants.userNameKey, name); if (cb) { cb(); } }; - var eraseTempSessionValues = LocalStore.eraseTempSessionValues = function () { - // delete sessionStorage values that might have been left over - // from the main page's /user redirect - [ - 'login', - 'login_user', - 'login_pass', - 'login_rmb', - 'register' - ].forEach(function (k) { - delete sessionStorage[k]; - }); - }; var logoutHandlers = []; LocalStore.logout = function (cb, isDeletion) { [ @@ -104,10 +91,8 @@ define([ 'loginToken', 'plan', ].forEach(function (k) { - sessionStorage.removeItem(k); localStorage.removeItem(k); delete localStorage[k]; - delete sessionStorage[k]; }); try { Object.keys(localStorage || {}).forEach(function (k) { @@ -122,7 +107,6 @@ define([ if (!LocalStore.getFSHash()) { LocalStore.setFSHash(Hash.createRandomHash('drive')); } - eraseTempSessionValues(); if (!isDeletion) { logoutHandlers.forEach(function (h) { diff --git a/www/common/sframe-channel.js b/www/common/sframe-channel.js deleted file mode 100644 index a7edf0814..000000000 --- a/www/common/sframe-channel.js +++ /dev/null @@ -1,166 +0,0 @@ -// This file provides the API for the channel for talking to and from the sandbox iframe. -define([ - '/common/sframe-protocol.js', - '/common/common-util.js' -], function (SFrameProtocol, Util) { - - var mkTxid = function () { - return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', ''); - }; - - var create = function (ow, cb, isSandbox, sendData) { - var otherWindow; - var evReady = Util.mkEvent(true); - var handlers = {}; - var queries = {}; - - // list of handlers which are registered from the other side... - var insideHandlers = []; - var callWhenRegistered = {}; - - var chan = {}; - - // Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... }); - chan.query = function (q, content, cb, opts) { - if (!otherWindow) { throw new Error('not yet initialized'); } - if (!SFrameProtocol[q]) { - throw new Error('please only make queries are defined in sframe-protocol.js'); - } - opts = opts || {}; - var txid = mkTxid(); - var to = opts.timeout || 30000; - var timeout = setTimeout(function () { - delete queries[txid]; - console.log("Timeout making query " + q); - }, to); - queries[txid] = function (data, msg) { - clearTimeout(timeout); - delete queries[txid]; - cb(undefined, data.content, msg); - }; - evReady.reg(function () { - otherWindow.postMessage(JSON.stringify({ - txid: txid, - content: content, - q: q - }), '*'); - }); - }; - - // Fire an event. channel.event('EV_SOMETHING', { args: "whatever" }); - var event = chan.event = function (e, content) { - if (!SFrameProtocol[e]) { - throw new Error('please only fire events that are defined in sframe-protocol.js'); - } - if (e.indexOf('EV_') !== 0) { - throw new Error('please only use events (starting with EV_) for event messages'); - } - evReady.reg(function () { - otherWindow.postMessage(JSON.stringify({ content: content, q: e }), '*'); - }); - }; - - // Be notified on query or event. channel.on('EV_SOMETHING', function (args, reply) { ... }); - // If the type is a query, your handler will be invoked with a reply function that takes - // one argument (the content to reply with). - chan.on = function (queryType, handler, quiet) { - if (!SFrameProtocol[queryType]) { - throw new Error('please only register handlers which are defined in sframe-protocol.js'); - } - (handlers[queryType] = handlers[queryType] || []).push(function (data, msg) { - handler(data.content, function (replyContent) { - if (queryType.indexOf('Q_') !== 0) { throw new Error("replies to events are invalid"); } - msg.source.postMessage(JSON.stringify({ - txid: data.txid, - content: replyContent - }), '*'); - }, msg); - }); - if (!quiet) { - event('EV_REGISTER_HANDLER', queryType); - } - }; - - // If a particular handler is registered, call the callback immediately, otherwise it will be called - // when that handler is first registered. - // channel.whenReg('Q_SOMETHING', function () { ...query Q_SOMETHING?... }); - chan.whenReg = function (queryType, cb, always) { - if (!SFrameProtocol[queryType]) { - throw new Error('please only register handlers which are defined in sframe-protocol.js'); - } - var reg = always; - if (insideHandlers.indexOf(queryType) > -1) { - cb(); - } else { - reg = true; - } - if (reg) { - (callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(cb); - } - }; - - // Same as whenReg except it will invoke every time there is another registration, not just once. - chan.onReg = function (queryType, cb) { chan.whenReg(queryType, cb, true); }; - - chan.on('EV_REGISTER_HANDLER', function (content) { - if (callWhenRegistered[content]) { - callWhenRegistered[content].forEach(function (f) { f(); }); - delete callWhenRegistered[content]; - } - insideHandlers.push(content); - }); - chan.whenReg('EV_REGISTER_HANDLER', evReady.fire); - - // Make sure both iframes are ready - var isReady =false; - chan.onReady = function (h) { - if (isReady) { - return void h(); - } - if (typeof(h) !== "function") { return; } - chan.on('EV_RPC_READY', function () { isReady = true; h(); }); - }; - chan.ready = function () { - chan.whenReg('EV_RPC_READY', function () { - chan.event('EV_RPC_READY'); - }); - }; - - var txid; - window.addEventListener('message', function (msg) { - var data = JSON.parse(msg.data); - if (ow !== msg.source) { - return; - //console.log("DROP Message from unexpected source"); - //console.log(msg); - } else if (!otherWindow) { - otherWindow = ow; - sendData = sendData || {}; - sendData.txid = data.txid; - ow.postMessage(JSON.stringify(sendData), '*'); - cb(chan); - } else if (typeof(data.q) === 'string' && handlers[data.q]) { - handlers[data.q].forEach(function (f) { - f(data || JSON.parse(msg.data), msg); - data = undefined; - }); - } else if (typeof(data.q) === 'undefined' && queries[data.txid]) { - queries[data.txid](data, msg); - } else if (data.txid === txid) { - // stray message from init - return; - } else { - console.log("DROP Unhandled message"); - console.log(msg); - } - }); - if (isSandbox) { - // we're in the sandbox - otherWindow = ow; - evReady.fire(); - cb(chan); - } - }; - - return { create: create }; -}); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index d476d02d4..f1fd0ac8d 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1181,15 +1181,6 @@ define([ }); }); - sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) { - if (typeof (data.value) === "undefined") { - delete sessionStorage[data.key]; - } else { - sessionStorage[data.key] = data.value; - } - cb(); - }); - sframeChan.on('Q_IS_ONLY_IN_SHARED_FOLDER', function (data, cb) { Cryptpad.isOnlyInSharedFolder(secret.channel, function (err, t) { if (err) { return void cb({error: err}); } diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index ffff122b5..3eb86ff93 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -462,15 +462,6 @@ define([ }); }; - funcs.sessionStorage = { - put: function (key, value, cb) { - ctx.sframeChan.query('Q_SESSIONSTORAGE_PUT', { - key: key, - value: value - }, cb); - } - }; - funcs.setDisplayName = function (name, cb) { cb = cb || $.noop; ctx.sframeChan.query('Q_SETTINGS_SET_DISPLAY_NAME', name, cb); diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js deleted file mode 100644 index 5eb9d829e..000000000 --- a/www/common/sframe-protocol.js +++ /dev/null @@ -1,271 +0,0 @@ -// This file defines all of the RPC calls which are used between the inner and outer iframe. -// Define *querys* (which expect a response) using Q_ -// Define *events* (which expect no response) using EV_ -// Please document the queries and events you create, and please please avoid making generic -// "do stuff" events/queries which are used for many different things because it makes the -// protocol unclear. -// -// WARNING: At this point, this protocol is still EXPERIMENTAL. This is not it's final form. -// We need to define protocol one piece at a time and then when we are satisfied that we -// fully understand the problem, we will define the *right* protocol and this file will be dynomited. -// -define({ - // When the iframe first launches, this query is sent repeatedly by the controller - // to wait for it to awake and give it the requirejs config to use. - 'Q_INIT': true, - - // When either the outside or inside registers a query handler, this is sent. - 'EV_REGISTER_HANDLER': true, - - // When an iframe is ready to receive messages - 'EV_RPC_READY': true, - - // Realtime events called from the outside. - // When someone joins the pad, argument is a string with their netflux id. - 'EV_RT_JOIN': true, - // When someone leaves the pad, argument is a string with their netflux id. - 'EV_RT_LEAVE': true, - // When you have been disconnected, no arguments. - 'EV_RT_DISCONNECT': true, - // When you have connected, argument is an object with myID: string, members: list, readOnly: boolean. - 'EV_RT_CONNECT': true, - // Called after the history is finished synchronizing, no arguments. - 'EV_RT_READY': true, - // Called when the server returns an error in a pad (EEXPIRED, EDELETED). - 'EV_RT_ERROR': true, - // Called from both outside and inside, argument is a (string) chainpad message. - 'Q_RT_MESSAGE': true, - - // Called from the outside, this informs the inside whenever the user's data has been changed. - // The argument is the object representing the content of the user profile minus the netfluxID - // which changes per-reconnect. - 'EV_METADATA_UPDATE': true, - - // Takes one argument only, the title to set for the CURRENT pad which the user is looking at. - // This changes the pad title in drive ONLY, the pad title needs to be changed inside of the - // iframe and synchronized with the other users. This will not trigger a EV_METADATA_UPDATE - // because the metadata contained in EV_METADATA_UPDATE does not contain the pad title. - // It also sets the page (tab) title to the selected title, unles it is overridden by - // the EV_SET_TAB_TITLE event. - 'Q_SET_PAD_TITLE_IN_DRIVE': true, - // Set the page title (tab title) to the selected value which will override the pad title. - // The new title value can contain {title}, which will be replaced by the pad title when it - // is set or modified. - 'EV_SET_TAB_TITLE': true, - - // Update the user's display-name which will be shown to contacts and people in the same pads. - 'Q_SETTINGS_SET_DISPLAY_NAME': true, - - // Log the user out in all the tabs - 'Q_LOGOUT': true, - // Tell the user that he has been logged out from outside (probably from another tab) - 'EV_LOGOUT': true, - - // When moving to the login or register page from a pad, we need to redirect to that pad at the - // end of the login process. This query set the current href to the sessionStorage. - 'Q_SET_LOGIN_REDIRECT': true, - - // Store the editing or readonly link of the current pad to the clipboard (share button). - 'Q_STORE_LINK_TO_CLIPBOARD': true, - - // Use anonymous rpc from inside the iframe (for avatars & pin usage). - 'Q_ANON_RPC_MESSAGE': true, - - // Get the user's pin limit, usage and plan - 'Q_PIN_GET_USAGE': true, - - // Write/update the login block when the account password is changed - 'Q_WRITE_LOGIN_BLOCK': true, - - // Remove login blocks - 'Q_REMOVE_LOGIN_BLOCK': true, - - // Check the pin limit to determine if we can store the pad in the drive or if we should. - // display a warning - 'Q_GET_PIN_LIMIT_STATUS': true, - - // Move a pad to the trash when using the forget button. - 'Q_MOVE_TO_TRASH': true, - - // Request the full history from the server when the users clicks on the history button. - // Callback is called when the FULL_HISTORY_END message is received in the outside. - 'Q_GET_FULL_HISTORY': true, - 'Q_GET_HISTORY_RANGE': true, - // When a (full) history message is received from the server. - 'EV_RT_HIST_MESSAGE': true, - - // Save a pad as a template using the toolbar button - 'Q_SAVE_AS_TEMPLATE': true, - - // Friend requests from the userlist - 'Q_SEND_FRIEND_REQUEST': true, // Up query - 'Q_INCOMING_FRIEND_REQUEST': true, // Down query - 'EV_FRIEND_REQUEST': true, // Down event when the request is complete - - // Set the tab notification when the content of the pad changes - 'EV_NOTIFY': true, - - // Send the new settings to the inner iframe when they are changed in the proxy - 'EV_SETTINGS_UPDATE': true, - - // Get and set (pad) attributes stored in the drive from the inner iframe - 'Q_GET_ATTRIBUTE': true, - 'Q_SET_ATTRIBUTE': true, - 'Q_GET_PAD_ATTRIBUTE': true, - 'Q_SET_PAD_ATTRIBUTE': true, - - // Check if a pad is only in a shared folder or (also) in the main drive. - // This allows us to change the behavior of some buttons (trash icon...) - 'Q_IS_ONLY_IN_SHARED_FOLDER': true, - - // Open/close the File picker (sent from the iframe to the outside) - 'EV_FILE_PICKER_OPEN': true, - 'EV_FILE_PICKER_CLOSE': true, - 'EV_FILE_PICKER_REFRESH': true, - // File selected in the file picker: sent from the filepicker iframe to the outside - // and then send to the inner iframe - 'EV_FILE_PICKED': true, - - // Get all the files from the drive to display them in a file picker secure app - 'Q_GET_FILES_LIST': true, - - // Template picked, replace the content of the pad - 'Q_TEMPLATE_USE': true, - // Check if we have template(s) for the selected pad type - 'Q_TEMPLATE_EXIST': true, - - // File upload queries and events - 'Q_UPLOAD_FILE': true, - 'EV_FILE_UPLOAD_STATE': true, - 'Q_CANCEL_PENDING_FILE_UPLOAD': true, - - // Make the browser window navigate to a given URL, if no URL is passed then it will reload. - 'EV_GOTO_URL': true, - // Make the parent window open a given URL in a new tab. It allows us to keep sessionStorage - // form the parent window. - 'EV_OPEN_URL': true, - - // Present mode URL - 'Q_PRESENT_URL_GET_VALUE': true, - 'EV_PRESENT_URL_SET_VALUE': true, - - // Put one or more entries to the cache which will go in localStorage. - // Cache is wiped after each new release - 'EV_CACHE_PUT': true, - - // Chat - 'EV_CHAT_EVENT': true, - 'Q_CHAT_COMMAND': true, - 'Q_CHAT_OPENPADCHAT': true, - - // Cursor - 'EV_CURSOR_EVENT': true, - 'Q_CURSOR_COMMAND': true, - 'Q_CURSOR_OPENCHANNEL': true, - - // Put one or more entries to the localStore which will go in localStorage. - 'EV_LOCALSTORE_PUT': true, - // Put one entry in the parent sessionStorage - 'Q_SESSIONSTORAGE_PUT': true, - - // Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads - // in the drive at registration. - 'Q_MERGE_ANON_DRIVE': true, - - // Add or remove the avatar from the profile. - // We have to pin/unpin the avatar and store/remove the value from the user object - 'Q_PROFILE_AVATAR_ADD': true, - 'Q_PROFILE_AVATAR_REMOVE': true, - - // Store outside and get thumbnails inside (stored with localForage (indexedDB) outside) - 'Q_THUMBNAIL_SET': true, - 'Q_THUMBNAIL_GET': true, - - // Settings app only - // Clear all thumbnails - 'Q_THUMBNAIL_CLEAR': true, - // Backup and restore a drive - 'Q_SETTINGS_DRIVE_GET': true, - 'Q_SETTINGS_DRIVE_SET': true, - 'Q_SETTINGS_DRIVE_RESET': true, - // Logout from all the devices where the account is logged in - 'Q_SETTINGS_LOGOUT': true, - // Import pads from this computer's anon session into the current user account - 'Q_SETTINGS_IMPORT_LOCAL': true, - 'Q_SETTINGS_DELETE_ACCOUNT': true, - - // Store the language selected in the iframe into localStorage outside - 'Q_LANGUAGE_SET': true, - - // Anonymous users can empty their drive and remove FS_hash from localStorage - 'EV_BURN_ANON_DRIVE': true, - // Inner drive needs to send command and receive updates from the async store - 'Q_DRIVE_USEROBJECT': true, - 'Q_DRIVE_GETOBJECT': true, - 'Q_DRIVE_RESTORE': true, - // Get the pads deleted from the server by other users to remove them from the drive - 'Q_DRIVE_GETDELETED': true, - // Store's userObject need to send log messages to inner to display them in the UI - 'EV_DRIVE_LOG': true, - // Refresh the drive when the drive has changed ('change' or 'remove' events) - 'EV_DRIVE_CHANGE': true, - 'EV_DRIVE_REMOVE': true, - // Set shared folder hash in the address bar - 'EV_DRIVE_SET_HASH': true, - - // Remove an owned pad from the server - 'Q_REMOVE_OWNED_CHANNEL': true, - // Clear an owned pad from the server (preserve metadata) - 'Q_CLEAR_OWNED_CHANNEL': true, - - // Notifications about connection and disconnection from the network - 'EV_NETWORK_DISCONNECT': true, - 'EV_NETWORK_RECONNECT': true, - // Reload on new version - 'EV_NEW_VERSION': true, - - // Pad creation screen: create a pad with the selected attributes (owned, expire) - 'Q_CREATE_PAD': true, - // Get the available templates - 'Q_CREATE_TEMPLATES': true, - - // This is for sending data out of the iframe when we are in testing mode - // The exact protocol is defined in common/test.js - 'EV_TESTDATA': true, - - // OnlyOffice: save a new version - 'Q_OO_SAVE': true, - - // Ask for the pad password when a pad is protected - 'EV_PAD_PASSWORD': true, - 'Q_PAD_PASSWORD_VALUE': true, - // Change pad password - 'Q_PAD_PASSWORD_CHANGE': true, - - // Migrate drive to owned drive - 'Q_CHANGE_USER_PASSWORD': true, - - // Loading events to display in the loading screen - 'EV_LOADING_INFO': true, - // Critical error outside the iframe during loading screen - 'EV_LOADING_ERROR': true, - - // Chrome 68 bug... - 'EV_CHROME_68': true, - - // Get all existing tags - 'Q_GET_ALL_TAGS': true, - - // Store pads in the drive - 'EV_AUTOSTORE_DISPLAY_POPUP': true, - 'Q_AUTOSTORE_STORE': true, - 'Q_IS_PAD_STORED': true, - - // Import mediatag from a pad - 'Q_IMPORT_MEDIATAG': true, - - // Ability to get a pad's content from its hash - 'Q_CRYPTGET': true, - 'EV_CRYPTGET_DISCONNECT': true, - -}); diff --git a/www/login/main.js b/www/login/main.js index 40a82b27e..768c012a8 100644 --- a/www/login/main.js +++ b/www/login/main.js @@ -67,10 +67,8 @@ define([ }); }); $('#register').on('click', function () { - if (sessionStorage) { - if ($uname.val()) { - sessionStorage.login_user = $uname.val(); - } + if ($uname.val()) { + localStorage.login_user = $uname.val(); } window.location.href = '/register/'; }); diff --git a/www/logout/main.js b/www/logout/main.js index acd8e8b02..d23bb8969 100644 --- a/www/logout/main.js +++ b/www/logout/main.js @@ -1,5 +1,4 @@ define(['/bower_components/localforage/dist/localforage.min.js'], function (localForage) { localForage.clear(); - sessionStorage.clear(); localStorage.clear(); }); diff --git a/www/register/main.js b/www/register/main.js index e2c30ea64..39069bd94 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -37,9 +37,9 @@ define([ var $passwd = $('#password'); var $confirm = $('#password-confirm'); - if (sessionStorage.login_user) { - delete sessionStorage.login_user; - $uname.val(sessionStorage.login_user); + if (localStorage.login_user) { + $uname.val(localStorage.login_user); + delete loginStorage.login_user; } [ $uname, $passwd, $confirm] From 2e56e07c2487cedb275e800b92fc81b41d69786f Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Nov 2020 11:02:33 +0100 Subject: [PATCH 39/51] Keep the app name in version 4 hashes --- www/common/common-hash.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 06a1d7fef..42925d6d6 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -165,8 +165,15 @@ var factory = function (Util, Crypto, Keys, Nacl) { /* Version 0 /pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy -Version 1 +Version 1: Add support for read-only access /code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI +Version 2: Add support for password-protection + /code/#/2/code/edit/u5ACvxAYmhvG0FtrNn9FJQcf/p/ +Version 3: Safe links + /code/#/3/code/edit/f0d8055aa640a97e7fd25020ca4e93b3/ +Version 4: Data URL when not a realtime link yet (new pad or "static" app) + /login/#/4/login/newpad=eyJocmVmIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2NvZGUvIy8yL2NvZGUvZWRpdC91NUFDdnhBWW1odkcwRnRyTm45RklRY2YvIn0%3D/ + /drive/#/4/drive/login=e30%3D/ */ var getLoginOpts = function (hashArr) { @@ -233,7 +240,7 @@ Version 1 if (hashArr[1] && hashArr[1] === '4') { parsed.getHash = function (opts) { if (!opts || !Object.keys(opts).length) { return ''; } - var hash = '/4/'; + var hash = '/4/' + type + '/'; if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } return hash; @@ -246,7 +253,8 @@ Version 1 }; parsed.version = 4; - options = hashArr.slice(2); + parsed.app = hashArr[2]; + options = hashArr.slice(3); addOptions(); return parsed; @@ -429,7 +437,7 @@ Version 1 // When we start without a hash, use version 4 links to add login or newpad options var getHash = function (opts) { if (!opts || !Object.keys(opts).length) { return ''; } - var hash = '/4/'; + var hash = '/4/' + ret.type + '/'; if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } return hash; From 864beb713634632847da9fff071f8e553df17f77 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Nov 2020 12:46:44 +0100 Subject: [PATCH 40/51] Fix various issues --- customize.dist/pages/index.js | 4 ++-- www/common/common-hash.js | 7 ++----- www/common/outer/local-store.js | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/customize.dist/pages/index.js b/customize.dist/pages/index.js index c718b363f..8a8fa3773 100644 --- a/customize.dist/pages/index.js +++ b/customize.dist/pages/index.js @@ -47,8 +47,8 @@ define([ var href = '/'+ x[0] +'/'; var attr = isEnabled ? { href: href } : { onclick: function () { - var href = Hash.hashToHref('', 'login'); - var url = Hash.getNewPadURL(href, { href: href }); + var loginURL = Hash.hashToHref('', 'login'); + var url = Hash.getNewPadURL(loginURL, { href: href }); window.location.href = url; } }; diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 42925d6d6..199c2cb54 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -236,7 +236,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) parsed.ownerKey = getOwnerKey(options); }; - // Version 4: only login or newpad options, smae for all the apps + // Version 4: only login or newpad options, same for all the apps if (hashArr[1] && hashArr[1] === '4') { parsed.getHash = function (opts) { if (!opts || !Object.keys(opts).length) { return ''; } @@ -653,9 +653,6 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) // Valid hash? if (parsed.hash) { if (!parsed.hashData) { return; } - // New pad: only newPadOpts allowed - if (Object.keys(parsed.hashData).length === 1 && - parsed.hashData.newPadOpts) { return true; } // Version should be a number if (typeof(parsed.hashData.version) === "undefined") { return; } // pads and files should have a base64 (or hex) key @@ -670,7 +667,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) Hash.decodeDataOptions = function (opts) { var b64 = decodeURIComponent(opts); var str = Nacl.util.encodeUTF8(Nacl.util.decodeBase64(b64)); - return JSON.parse(str); + return Util.tryParse(str) || {}; }; Hash.encodeDataOptions = function (opts) { var str = JSON.stringify(opts); diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index d15c2e8c6..0aff1f9ce 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -94,6 +94,7 @@ define([ localStorage.removeItem(k); delete localStorage[k]; }); + sessionStorage.clear(); try { Object.keys(localStorage || {}).forEach(function (k) { // Remvoe everything in localStorage except CACHE and FS_hash From f2faeaad07e8edccbe3ac412f90f0ef571c5c7c9 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Nov 2020 12:55:40 +0100 Subject: [PATCH 41/51] Backward compatiility with sessionStorage --- www/common/cryptpad-common.js | 26 ++++++++++++++++++++++++-- www/common/sframe-common-outer.js | 7 ++++++- www/register/main.js | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 36413d89d..8874871d8 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2107,6 +2107,28 @@ define([ driveEvents: true //rdyCfg.driveEvents // Boolean }; + // FIXME Backward compatibility + if (sessionStorage.newPadFileData) { + common.fromFileData = JSON.parse(sessionStorage.newPadFileData); + var _parsed1 = Hash.parsePadUrl(common.fromFileData.href); + var _parsed2 = Hash.parsePadUrl(window.location.href); + if (_parsed1.hashData.type === 'pad') { + if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; } + } + delete sessionStorage.newPadFileData; + } + + if (sessionStorage.newPadPath) { + common.initialPath = sessionStorage.newPadPath; + delete sessionStorage.newPadPath; + } + + if (sessionStorage.newPadTeam) { + common.initialTeam = sessionStorage.newPadTeam; + delete sessionStorage.newPadTeam; + } + + var channelIsReady = waitFor(); var msgEv = Util.mkEvent(); @@ -2332,7 +2354,7 @@ define([ postMessage("DISCONNECT"); }); }).nThen(function (waitFor) { - if (common.createReadme) { + if (common.createReadme || sessionStorage.createReadme) { var data = { driveReadme: Messages.driveReadme, driveReadmeTitle: Messages.driveReadmeTitle, @@ -2342,7 +2364,7 @@ define([ })); } }).nThen(function (waitFor) { - if (common.migrateAnonDrive) { + if (common.migrateAnonDrive || sessionStorage.migrateAnonDrive) { common.mergeAnonDrive(waitFor()); } }).nThen(function (waitFor) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index f1fd0ac8d..b7ce64d4d 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -124,7 +124,7 @@ define([ SFrameChannel.create(msgEv, postMsg, waitFor(function (sfc) { Utils.sframeChan = sframeChan = sfc; window.CryptPad_loadingError = function (e) { - sfc.event('EV_LOADING_ERROR', e) + sfc.event('EV_LOADING_ERROR', e); }; })); }); @@ -260,6 +260,11 @@ define([ } // Otherwise, continue } + // FIXME Backward compatibility + if (sessionStorage.newPadPassword && !newPadPassword) { + newPadPassword = sessionStorage.newPadPassword; + delete sessionStorage.newPadPassword; + } if (!parsed.hashData) { // No hash, no need to check for a password diff --git a/www/register/main.js b/www/register/main.js index 39069bd94..2bf9d9f39 100644 --- a/www/register/main.js +++ b/www/register/main.js @@ -39,7 +39,7 @@ define([ if (localStorage.login_user) { $uname.val(localStorage.login_user); - delete loginStorage.login_user; + delete localStorage.login_user; } [ $uname, $passwd, $confirm] From 4e4147f021c8749091553637c46146fd9ba94239 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 3 Nov 2020 16:48:12 +0530 Subject: [PATCH 42/51] update version string for v3.24.0 (YunnanLakeNewt) --- customize.dist/pages.js | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 1db764bcc..74c46c4ec 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.2 (XerusDaamsi reloaded)"; + Pages.versionString = "CryptPad v3.24.0 (YunnanLakeNewt)"; // used for the about menu Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined; diff --git a/package-lock.json b/package-lock.json index a9ee6e5e7..775f42f68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "3.23.2", + "version": "3.24.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a849b2c2d..37fd6c686 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.2", + "version": "3.24.0", "license": "AGPL-3.0+", "repository": { "type": "git", From 3db6d2fff90aa578f4c5529e6dad06879e690e3d Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 4 Nov 2020 08:32:14 +0530 Subject: [PATCH 43/51] scroll pad table of contents if necessary --- www/pad/app-pad.less | 1 + 1 file changed, 1 insertion(+) diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less index b413eed6a..489cbb17d 100644 --- a/www/pad/app-pad.less +++ b/www/pad/app-pad.less @@ -27,6 +27,7 @@ body.cp-app-pad { #cp-app-pad-toc { @toc-level-indent: 15px; + overflow-y: auto; margin-top: 10px; margin-left: 10px; width: 200px; From 016bdc73b6cbe02557fbe9cdfb4dd32becd0cce9 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 4 Nov 2020 05:04:22 +0100 Subject: [PATCH 44/51] Translated using Weblate (English) Currently translated at 100.0% (1372 of 1372 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index fa96fc5a7..c76b9a7c1 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1467,5 +1467,6 @@ "loading_state_4": "Load Teams", "loading_state_5": "Reconstruct document", "tag_add": "Add", - "tag_edit": "Edit" + "tag_edit": "Edit", + "error_unhelpfulScriptError": "Script Error: See browser console for details" } From 553f29b43163db4b7af20120729c45266faa68f7 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 4 Nov 2020 05:04:23 +0100 Subject: [PATCH 45/51] Translated using Weblate (French) Currently translated at 100.0% (1372 of 1372 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 70909c44d..1d1779035 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1467,5 +1467,6 @@ "loading_state_1": "Chargement du drive", "loading_state_0": "Construction de l'interface", "tag_edit": "Modifier", - "tag_add": "Ajouter" + "tag_add": "Ajouter", + "error_unhelpfulScriptError": "Erreur de script : consultez la console du navigateur pour plus de détails" } From 59eaf8187c9a097fc0f679da784ed2615e9b1bd8 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 4 Nov 2020 10:28:52 +0530 Subject: [PATCH 46/51] handle unhelpful 'Script error.' message --- customize.dist/loading.js | 16 +++++++++++++--- www/common/sframe-boot2.js | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 4f8b79125..740c66081 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -312,25 +312,35 @@ button.primary:hover{ return bar; }; + var hasErrored = false; var updateLoadingProgress = function (data) { - if (!built) { return; } + if (!built || !data) { return; } var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { document.querySelector('.cp-loading-spinner-container').style.display = 'none'; document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); - } catch (e) { console.error(e); } + } catch (e) { + if (!hasErrored) { console.error(e); } + } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; + window.CryptPad_loadingError = function (err) { if (!built) { return; } + hasErrored = true; + var err2; + if (err === 'Script error.') { + err2 = Messages.error_unhelpfulScriptError; + } + 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; + document.querySelector('#cp-loading-message').innerText = err2 || err; } catch (e) { console.error(e); } }; return function () { diff --git a/www/common/sframe-boot2.js b/www/common/sframe-boot2.js index 5a68237ad..66a93545f 100644 --- a/www/common/sframe-boot2.js +++ b/www/common/sframe-boot2.js @@ -43,7 +43,7 @@ define([ return void console.log(); } if (window.CryptPad_loadingError) { - window.CryptPad_loadingError(e); + return void window.CryptPad_loadingError(e); } throw e; }; From c4e1b5f7d42ffaf6fd8c931dbb28d562ba53db54 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 4 Nov 2020 10:32:45 +0530 Subject: [PATCH 47/51] refactor two mostly-identical functions --- www/common/cryptpad-common.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 8874871d8..ca65ce77b 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -148,20 +148,19 @@ define([ send(); }; - common.setTabHref = function (href) { - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.href = href; - window.onhashchange = ohc; - ohc({reset: true}); - }; - common.setTabHash = function (hash) { - var ohc = window.onhashchange; - window.onhashchange = function () {}; - window.location.hash = hash; - window.onhashchange = ohc; - ohc({reset: true}); - }; + (function () { + var bypassHashChange = function (key) { + return function (value) { + var ohc = window.onhashchange; + window.onhashchange = function () {}; + window.location[key] = value; + window.onhashchange = ohc; + ohc({reset: true}); + }; + }; + common.setTabHref = bypassHashChange('href'); + common.setTabHash = bypassHashChange('hash'); + }()); // RESTRICTED // Settings only From 0f4013505d2707c226dc223df76ee700f6963a00 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 4 Nov 2020 12:26:53 +0530 Subject: [PATCH 48/51] prevent typeError for undefined button when logged out --- www/common/onlyoffice/inner.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index ce0dbcc3f..28d06dd32 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -2070,7 +2070,9 @@ define([ // Import template var $template = common.createButton('importtemplate', true, {}, openTemplatePicker); - $template.appendTo(toolbar.$drawer); + if ($template && typeof($template.appendTo) === 'function') { + $template.appendTo(toolbar.$drawer); + } })(); } From 9203b88538834cefdd1a89e817c373e862a2607e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Nov 2020 11:22:31 +0100 Subject: [PATCH 49/51] Fix UI language --- customize.dist/loading.js | 1 + customize.dist/messages.js | 8 +++-- www/admin/inner.html | 2 +- www/admin/main.js | 28 ++---------------- www/code/inner.html | 2 +- www/common/onlyoffice/main.js | 39 +++--------------------- www/common/sframe-app-outer.js | 38 +++--------------------- www/common/sframe-boot.js | 2 +- www/common/sframe-common-outer.js | 49 ++++++++++++++++++++++++++++++- www/contacts/inner.html | 2 +- www/contacts/main.js | 28 ++---------------- www/debug/inner.html | 2 +- www/drive/inner.html | 2 +- www/drive/main.js | 38 +++--------------------- www/file/inner.html | 2 +- www/file/main.js | 38 +++--------------------- www/kanban/inner.html | 2 +- www/notifications/inner.html | 2 +- www/notifications/main.js | 28 ++---------------- www/oodoc/inner.html | 2 +- www/ooslide/inner.html | 2 +- www/pad/inner.html | 2 +- www/poll/inner.html | 2 +- www/poll/main.js | 38 +++--------------------- www/profile/inner.html | 2 +- www/profile/main.js | 28 ++---------------- www/secureiframe/inner.html | 2 +- www/secureiframe/main.js | 7 +++-- www/settings/inner.html | 2 +- www/settings/main.js | 28 ++---------------- www/sheet/inner.html | 2 +- www/slide/inner.html | 2 +- www/support/inner.html | 2 +- www/support/main.js | 28 ++---------------- www/teams/inner.html | 2 +- www/teams/main.js | 38 +++--------------------- www/todo/inner.html | 2 +- www/whiteboard/inner.html | 2 +- www/worker/inner.html | 2 +- www/worker/main.js | 28 ++---------------- 40 files changed, 121 insertions(+), 415 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 740c66081..edb66ef0a 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -337,6 +337,7 @@ button.primary:hover{ try { var node = document.querySelector('.cp-loading-progress'); + if (!node) { return; } 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;'); diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 40dbbfb95..303375e4e 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -26,7 +26,9 @@ var getStoredLanguage = function () { return localStorage && localStorage.getIte var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; }; var getLanguage = messages._getLanguage = function () { if (window.cryptpadLanguage) { return window.cryptpadLanguage; } - if (getStoredLanguage()) { return getStoredLanguage(); } + try { + if (getStoredLanguage()) { return getStoredLanguage(); } + } catch (e) { console.log(e); } var l = getBrowserLanguage(); // Edge returns 'fr-FR' --> transform it to 'fr' and check again return map[l] ? l : @@ -65,7 +67,9 @@ define(req, function(AppConfig, Default, Language) { if (AppConfig.availableLanguages.indexOf(language) === -1) { language = defaultLanguage; Language = Default; - localStorage.setItem(LS_LANG, language); + try { + localStorage.setItem(LS_LANG, language); + } catch (e) { console.log(e); } } Object.keys(map).forEach(function (l) { if (l === defaultLanguage) { return; } diff --git a/www/admin/inner.html b/www/admin/inner.html index 01bda5fab..eeb234d0c 100644 --- a/www/admin/inner.html +++ b/www/admin/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/admin/main.js b/www/admin/main.js index 817d2bd2e..8a6ec7a70 100644 --- a/www/admin/main.js +++ b/www/admin/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/admin/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { var addRpc = function (sframeChan, Cryptpad/*, Utils*/) { // Adding a new avatar from the profile: pin it and store it in the object diff --git a/www/code/inner.html b/www/code/inner.html index a4ea56206..b25534297 100644 --- a/www/code/inner.html +++ b/www/code/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/contacts/main.js b/www/contacts/main.js index 38d6c5e71..faf92f94e 100644 --- a/www/contacts/main.js +++ b/www/contacts/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/contacts/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { SFCommonO.start({ noRealtime: true, diff --git a/www/debug/inner.html b/www/debug/inner.html index 2ac53948c..7936c04f2 100644 --- a/www/debug/inner.html +++ b/www/debug/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/notifications/main.js b/www/notifications/main.js index 20c8653f9..785fb3b5d 100644 --- a/www/notifications/main.js +++ b/www/notifications/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/notifications/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { var category; if (window.location.hash) { diff --git a/www/oodoc/inner.html b/www/oodoc/inner.html index 529c5a8d9..884ae5a00 100644 --- a/www/oodoc/inner.html +++ b/www/oodoc/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/ooslide/inner.html b/www/ooslide/inner.html index d06820db2..e7c4e111f 100644 --- a/www/ooslide/inner.html +++ b/www/ooslide/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/pad/inner.html b/www/pad/inner.html index e4dbcdf95..17bfec308 100644 --- a/www/pad/inner.html +++ b/www/pad/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/profile/main.js b/www/profile/main.js index 92b24b3fc..b041d926a 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/profile/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { var getSecrets = function (Cryptpad, Utils, cb) { var Hash = Utils.Hash; diff --git a/www/secureiframe/inner.html b/www/secureiframe/inner.html index 29c3cf797..97bfb3930 100644 --- a/www/secureiframe/inner.html +++ b/www/secureiframe/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/settings/main.js b/www/settings/main.js index bbc0f87d3..750423a1d 100644 --- a/www/settings/main.js +++ b/www/settings/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/settings/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('Q_THUMBNAIL_CLEAR', function (d, cb) { diff --git a/www/sheet/inner.html b/www/sheet/inner.html index 07d21904d..68949568f 100644 --- a/www/sheet/inner.html +++ b/www/sheet/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/slide/inner.html b/www/slide/inner.html index c04091cf7..f067e8a2f 100644 --- a/www/slide/inner.html +++ b/www/slide/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/support/main.js b/www/support/main.js index b5ca65126..1dc8c0e56 100644 --- a/www/support/main.js +++ b/www/support/main.js @@ -3,40 +3,16 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js', '/common/outer/local-store.js', '/common/outer/login-block.js', -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO, LocalStore, Block) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO, LocalStore, Block) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/support/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { var category; if (window.location.hash) { diff --git a/www/teams/inner.html b/www/teams/inner.html index 243a74edf..5ec12c287 100644 --- a/www/teams/inner.html +++ b/www/teams/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/whiteboard/inner.html b/www/whiteboard/inner.html index 533b4568f..4b56440e3 100644 --- a/www/whiteboard/inner.html +++ b/www/whiteboard/inner.html @@ -2,7 +2,7 @@ - + diff --git a/www/worker/main.js b/www/worker/main.js index 04dffa748..633982146 100644 --- a/www/worker/main.js +++ b/www/worker/main.js @@ -3,38 +3,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', '/common/dom-ready.js', - '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); +], function (nThen, ApiConfig, DomReady, SFCommonO) { // Loaded in load #2 nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/worker/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); + SFCommonO.initIframe(waitFor); }).nThen(function (/*waitFor*/) { SFCommonO.start({ noRealtime: true, From 2844505593249b66ad8ef8dcdfe9558e5f02346e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Nov 2020 13:09:49 +0100 Subject: [PATCH 50/51] Remove double slash in iframe URL --- www/common/sframe-common-outer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 24496dd53..f6f24c5bf 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -31,7 +31,7 @@ define([ } document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + window.location.pathname + '/inner.html?' + + ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); // This is a cheap trick to avoid loading sframe-channel in parallel with the From 830739c901aa2623ee4aa0831cebc9443673b4ac Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 11:28:20 +0100 Subject: [PATCH 51/51] Fix teams APP issue after login redirect --- www/common/sframe-common-outer.js | 3 ++- www/teams/main.js | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index f6f24c5bf..ad128db33 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -121,6 +121,7 @@ define([ Utils.Feedback = _Feedback; Utils.LocalStore = _LocalStore; Utils.UserObject = _UserObject; + Utils.currentPad = currentPad; AppConfig = _AppConfig; Test = _Test; @@ -604,7 +605,7 @@ define([ for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; } if (cfg.addData) { - cfg.addData(metaObj.priv, Cryptpad, metaObj.user); + cfg.addData(metaObj.priv, Cryptpad, metaObj.user, Utils); } sframeChan.event('EV_METADATA_UPDATE', metaObj); diff --git a/www/teams/main.js b/www/teams/main.js index 722cd7c1b..044419dfc 100644 --- a/www/teams/main.js +++ b/www/teams/main.js @@ -97,9 +97,12 @@ define([ var secret = Hash.getSecrets('team', hash); cb(null, secret); }; - var addData = function (meta) { - if (!hash) { return; } - meta.teamInviteHash = hash.slice(1); + var addData = function (meta, Cryptpad, user, Utils) { + if (!Utils.currentPad.hash) { return; } + var _hash = Utils.currentPad.hash.replace(/^#/, ''); + var parsed = Utils.Hash.parseTypeHash('invite', _hash); + if (parsed.app !== 'invite') { return; } + meta.teamInviteHash = _hash; }; SFCommonO.start({ getSecrets: getSecrets,