From 753644f6385681fb4f85cfc1f341b832fc339f3f Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 9 Oct 2020 16:10:39 +0200 Subject: [PATCH 01/27] 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 c79daf3ba25a727ecfa1409a6f8580d36e98b9de Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 13 Oct 2020 10:30:01 +0200 Subject: [PATCH 02/27] Fix OO CSP cache --- www/common/onlyoffice/inner.js | 4 ++-- www/sheet/inner.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index ea0e51bfc..f39a85482 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -408,7 +408,7 @@ define([ myUniqueOOId = undefined; setMyId(); if (APP.docEditor) { APP.docEditor.destroyEditor(); } // Kill the old editor - $('iframe[name="frameEditor"]').after(h('div#cp-app-oo-placeholder')).remove(); + $('iframe[name="frameEditor"]').after(h('div#cp-app-oo-placeholder-a')).remove(); ooLoaded = false; oldLocks = {}; Object.keys(pendingChanges).forEach(function (key) { @@ -1406,7 +1406,7 @@ define([ }); }; - APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder", APP.ooconfig); + APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder-a", APP.ooconfig); ooLoaded = true; makeChannel(); }; diff --git a/www/sheet/inner.html b/www/sheet/inner.html index 4d7ca6009..07d21904d 100644 --- a/www/sheet/inner.html +++ b/www/sheet/inner.html @@ -11,7 +11,7 @@
-
+
From 0e1588a883b685e0ca3a57523ee9f9a31d290a03 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 15:20:56 +0200 Subject: [PATCH 03/27] 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 76dec3dd8..b6e58e527 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -888,6 +888,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(); @@ -939,6 +944,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 df05d8a3e..7e57398c5 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 2ba4e30ee..e1a85d5d6 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)); @@ -501,6 +510,10 @@ define([ waitFor.abort(); } }).nThen(function () { + ctx.progress += 30/ctx.numberOfTeams; + ctx.updateProgress({ + progress: ctx.progress + }); onReady(ctx, id, lm, roster, keys, null, cb); }); }; @@ -1653,10 +1666,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 bba393afb..165b0b6f7 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 fffd4aff3c3b1c67fcb3cade545aefaf1dffd4d3 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 15:21:26 +0200 Subject: [PATCH 04/27] 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 b6e58e527..fd5ceb162 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -887,7 +887,7 @@ define([ todo(); } }; - UI.updateLoadingProgress = function (data, isDrive) { + UI.updateLoadingProgress = function (data) { if (window.CryptPad_updateLoadingProgress) { window.CryptPad_updateLoadingProgress(data); } From b55566254fa82837b355dd67f3e2255649601a90 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 17:32:15 +0200 Subject: [PATCH 05/27] 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 20f32634295562b6076730bcc73600592f702274 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Oct 2020 17:53:13 +0200 Subject: [PATCH 06/27] 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 6bdebad352851a9dc94356be7e36f65345847eb1 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 15 Oct 2020 14:48:17 +0200 Subject: [PATCH 07/27] 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 7e57398c5..cde9f72a4 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 854ab77f14e657f5f22ccbf805cb7206f2292895 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 15 Oct 2020 14:58:56 +0200 Subject: [PATCH 08/27] 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 69c26fe8c720101ed4090e592bab68916356e972 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 23 Oct 2020 15:44:47 +0200 Subject: [PATCH 09/27] 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 696627cbe..a9b2923a9 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) { @@ -666,19 +702,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; } @@ -741,9 +786,6 @@ define([ }).nThen(function () { Crypt.put(parsed2.hash, JSON.stringify(val), function () { cb(); - Crypt.get(parsed2.hash, function (err, val) { - console.warn(val); - }); }, optsPut); }); @@ -1006,7 +1048,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) { @@ -1058,6 +1100,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) { @@ -1074,6 +1121,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(); @@ -1309,11 +1358,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); @@ -1392,9 +1447,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 bba393afb..aebafa834 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1380,8 +1380,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, @@ -1400,17 +1402,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 a8f53d04fc1825c06aabd9ca4d0c513ffe1b4b02 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Oct 2020 17:24:35 +0530 Subject: [PATCH 10/27] 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 f7bd3bdc23c57a6c9cb29b0c73cdc3ae0a9ba232 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 26 Oct 2020 17:34:34 +0530 Subject: [PATCH 11/27] 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 a1ee5943b46e714162a28163377783c6caf84c08 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 26 Oct 2020 16:45:36 +0100 Subject: [PATCH 12/27] 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 2e6c3a609..118931148 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 a2b79d84b8c525a5b49e360e252b7543a85ad1cb Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 08:12:23 +0530 Subject: [PATCH 13/27] 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 b46aaaed7b0a340c12f8710bd2deaeac6e264e9e Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 09:01:38 +0530 Subject: [PATCH 14/27] optimize Util.throttle to create fewer timeouts --- scripts/tests/throttle-test.js | 34 ++++++++++++++++++++++++++++++++ www/common/common-util.js | 36 ++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) 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 0e86ecc8c..41797e7c8 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -306,11 +306,43 @@ }; Util.throttle = function (f, ms) { + var last = 0; var to; + var args; + + var defer = function (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); + }, delay); + }; + 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 c3fbbec72b04f8aa8fc057e0469c24fe6222f892 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 12:46:27 +0530 Subject: [PATCH 15/27] 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 034472d3ddd6a4b6c242f99f7c2826bdf3ec0df2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 27 Oct 2020 12:47:15 +0530 Subject: [PATCH 16/27] 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 1c986a81d1f2146016080ef0c572b4c0e5ec4f68 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 27 Oct 2020 10:41:35 +0100 Subject: [PATCH 17/27] 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 448f294b1438574e3788c04a2636c23811c15bc3 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 28 Oct 2020 16:10:34 +0100 Subject: [PATCH 18/27] 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 fd5ceb162..cd47e218d 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -850,35 +850,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(); @@ -891,60 +889,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. @@ -952,31 +896,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); @@ -988,7 +924,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 f4630ae17..b28622cb7 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2530,10 +2530,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 d754ae96e5457a175a508a4076138f91bc691e91 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 28 Oct 2020 16:32:22 +0100 Subject: [PATCH 19/27] 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 63502abce834a1aeb6b0afb5b81bddd52aec1a7b Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 14:39:23 +0100 Subject: [PATCH 20/27] 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 e849ce20d..290147427 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 83b318f156e57408617eb27e1f8e4c4139dc95aa Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 14:44:14 +0100 Subject: [PATCH 21/27] 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 aebafa834..a03be3f64 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -317,10 +317,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 3f9c2bc96048f5a0faae2fbd2fc47140962e563a Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:09 +0100 Subject: [PATCH 22/27] Translated using Weblate (French) Currently translated at 100.0% (1369 of 1369 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index ce19c96ad..7d8a84efb 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1459,5 +1459,11 @@ "admin_limitUser": "Clé publique de l'utilisateur", "fm_shareFolderPassword": "Protéger ce dossier avec un mot de passe (optionnel)", "access_destroyPad": "Détruire ce document ou dossier définitivement", - "fm_deletedFolder": "Dossier supprimé" + "fm_deletedFolder": "Dossier supprimé", + "loading_state_5": "Reconstruction du document", + "loading_state_4": "Chargement des équipes", + "loading_state_3": "Chargement des dossiers partagés", + "loading_state_2": "Mise à jour du contenu", + "loading_state_1": "Chargement du drive", + "loading_state_0": "Construction de l'interface" } From 2940fa6a0e793b395272f680d20f3f8b16144fec Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:09 +0100 Subject: [PATCH 23/27] Translated using Weblate (English) Currently translated at 100.0% (1369 of 1369 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1368 of 1368 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1367 of 1367 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1366 of 1366 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1365 of 1365 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ Translated using Weblate (English) Currently translated at 100.0% (1364 of 1364 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/en/ --- www/common/translations/messages.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 010287444..e3b84adb7 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1459,5 +1459,11 @@ "admin_limitUser": "User's public key", "fm_deletedFolder": "Deleted folder", "access_destroyPad": "Destroy this document or folder permanently", - "fm_shareFolderPassword": "Protect this folder with a password (optional)" + "fm_shareFolderPassword": "Protect this folder with a password (optional)", + "loading_state_0": "Build interface", + "loading_state_1": "Load drive", + "loading_state_2": "Update content", + "loading_state_3": "Load shared folders", + "loading_state_4": "Load Teams", + "loading_state_5": "Reconstruct document" } From 14f785aa5eab28668e8cbcfdc6478e3b612b8de2 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 29 Oct 2020 16:48:10 +0100 Subject: [PATCH 24/27] Translated using Weblate (Finnish) Currently translated at 100.0% (1363 of 1363 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fi/ --- www/common/translations/messages.fi.json | 69 +++++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/www/common/translations/messages.fi.json b/www/common/translations/messages.fi.json index 3b5835ff7..fe4ee1f64 100644 --- a/www/common/translations/messages.fi.json +++ b/www/common/translations/messages.fi.json @@ -188,12 +188,12 @@ "help_button": "Ohje", "historyText": "Historia", "historyButton": "Näytä asiakirjan historia", - "history_next": "Uudempi versio", - "history_prev": "Vanhempi versio", + "history_next": "Seuraava versio", + "history_prev": "Edellinen versio", "history_loadMore": "Lataa lisää historiatietoja", "history_closeTitle": "Sulje historia", "history_restoreTitle": "Palauta asiakirjan valittu versio", - "history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan nykyisen version esitetyllä versiolla?", + "history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan tämänhetkisen version näytetyllä versiolla?", "history_restoreDone": "Asiakirja palautettu", "history_version": "Versio:", "openLinkInNewTab": "Avaa linkki uuteen välilehteen", @@ -452,7 +452,7 @@ "settings_backupHint": "Varmuuskopioi tai palauta CryptDrivesi sisältö kokonaisuudessaan. Varmuuskopio ei sisällä padiesi sisältöä, ainoastaan niiden käyttöön tarvittavat avaimet.", "settings_backup": "Varmuuskopioi", "settings_restore": "Palauta", - "settings_backupHint2": "Lataa kaikkien padiesi nykyinen sisältö. Padit ladataan luettavassa tiedostomuodossa, jos sellainen on saatavilla.", + "settings_backupHint2": "Lataa kaikkien asiakirjojen nykyinen sisältö tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.", "settings_backup2": "Lataa oma CryptDrive tietokoneellesi", "settings_backup2Confirm": "Tämä lataa kaikki CryptDrivesi padit ja tiedostot tietokoneellesi. Jos haluat jatkaa, valitse nimi ja paina OK", "settings_exportTitle": "Vie oma CryptDrive", @@ -1176,9 +1176,9 @@ "settings_safeLinksTitle": "Turvalliset linkit", "settings_cat_security": "Luottamuksellisuus", "imprint": "Oikeudellinen huomautus", - "oo_sheetMigration_anonymousEditor": "Taulukon muokkaaminen on poistettu käytöstä anonyymeille käyttäjille, kunnes rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.", + "oo_sheetMigration_anonymousEditor": "Rekisteröitymättömät käyttäjät eivät voi muokata taulukkoa ennen kuin rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.", "oo_sheetMigration_complete": "Päivitetty versio saatavilla, paina OK ladataksesi uudelleen.", - "oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon", + "oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon. Ole hyvä ja odota noin 1 minuutti.", "oo_exportInProgress": "Vienti menossa", "oo_importInProgress": "Tuonti menossa", "oo_invalidFormat": "Tätä tiedostoa ei voida tuoda", @@ -1404,5 +1404,60 @@ "readme_cat1_l2": "Avaa padeja CryptDrivestasi: kaksoisnapsauta padin kuvaketta avataksesi sen.", "readme_cat1_l1": "Luo padi: Siirry CryptDriveesi, napsauta {0} ja sitten {1}, ja voit luoda padin.", "readme_cat1": "Tutustu CryptDriveesi", - "readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa." + "readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa.", + "fm_shareFolderPassword": "Suojaa kansio salasanalla (vapaaehtoinen)", + "access_destroyPad": "Tuhoa tämä asiakirja tai kansio lopullisesti", + "fm_deletedFolder": "Poistettu kansio", + "admin_limitUser": "Käyttäjän julkinen avain", + "team_exportButton": "Lataa", + "team_exportHint": "Lataa kaikki tiimin CryptDriveen tallennetut asiakirjat tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.", + "team_exportTitle": "Lataa tiimin CryptDrive", + "admin_cat_quota": "Käyttäjätallennustila", + "admin_invalLimit": "Virheellinen arvo kiintiökentässä", + "admin_invalKey": "Virheellinen julkinen avain", + "admin_limitSetNote": "Huomautus", + "admin_limitMB": "Kiintiö (megatavuissa)", + "admin_setlimitTitle": "Ota mukautettu kiintiö käyttöön", + "admin_setlimitHint": "Aseta mukautettu tallennustilakiintiö käyttäjälle tämän julkisen avaimen avulla. Voit päivittää tai poistaa olemassaolevan tallennustilakiintiön.", + "admin_limitNote": "Huomautus: {0}", + "admin_limitPlan": "Tilaus: {0}", + "admin_defaultlimitHint": "Tallennustilakiintiö käyttäjien ja tiimien CryptDriveille, joilla ei ole erikseen määriteltyjä sääntöjä", + "admin_defaultlimitTitle": "Tallennustilakiintiö (Mt)", + "admin_getlimitsHint": "Listaa muokatut tallennustilakiintiöt, jotka ovat käytössä CryptPad-palvelimellasi.", + "admin_getlimitsTitle": "Muokatut tallennustilakiintiöt", + "admin_limit": "Nykyinen rajoitus: {0}", + "admin_setlimitButton": "Aseta rajoitus", + "admin_registrationAllow": "Avaa", + "admin_registrationButton": "Sulje", + "admin_registrationTitle": "Sulje rekisteröityminen", + "admin_registrationHint": "Älä salli uusien käyttäjien rekisteröitymistä", + "snapshots_cantMake": "Tilannevedoksen luominen epäonnistui. Yhteytesi on katkennut.", + "snapshots_notFound": "Tämä tilannevedos ei ole enää saatavilla, koska asiakirjan historia on poistettu.", + "snapshot_error_exists": "Tästä versiosta on jo olemassa tilannevedos", + "snapshots_ooPickVersion": "Valitse versio, jotta voit luoda tilannevedoksen", + "oo_version": "Versio: ", + "oo_version_latest": "Viimeisin", + "snapshots_delete": "Poista", + "oo_deletedVersion": "Tämä versio ei ole enää saatavilla historiassa.", + "snapshots_close": "Sulje", + "snapshots_restore": "Palauta", + "snapshots_open": "Avaa", + "snapshots_placeholder": "Tilannevedoksen otsikko", + "snapshots_new": "Uusi tilannevedos", + "snapshots_button": "Tilannevedokset", + "snaphot_title": "Tilannevedos", + "infobar_versionHash": "Katselet asiakirjan vanhaa versiota ({0}).", + "history_restoreDriveDone": "CryptDrive palautettu", + "history_restoreDrivePrompt": "Oletko varma, että haluat korvata CryptDriven nykyisen version valitulla versiolla?", + "history_restoreDriveTitle": "Palauta CryptDrive valitsemaasi versioon", + "history_userNext": "Seuraava laatija", + "history_fastNext": "Seuraava muokkaussessio", + "history_userPrev": "Edellinen laatija", + "history_fastPrev": "Edellinen muokkaussessio", + "share_versionHash": "Olet jakamassa valitsemasi historiaversion asiakirjastasi vain luku-tilassa. Tämä antaa myös katseluoikeuden asiakirjan kaikkiin versioihin.", + "history_shareTitle": "Jaa linkki tähän versioon", + "history_cantRestore": "Palauttaminen epäonnistui. Yhteytesi on katkennut.", + "history_close": "Sulje", + "history_restore": "Palauta", + "share_bar": "Luo linkki" } From fe30d5243cce101f52492e0da7ff738160df69f1 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:16:41 +0100 Subject: [PATCH 25/27] 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 290147427..d87bb955e 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 a3bb622df64ccea358debfe2c0ad1c76ec75004a Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Oct 2020 17:26:23 +0100 Subject: [PATCH 26/27] Fix loading screen error --- customize.dist/loading.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 56e9a4cab..525f58e45 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -333,6 +333,8 @@ button.primary:hover{ window.CryptPad_loadingError = function (err) { if (!built) { return; } try { + var node = document.querySelector('.cp-loading-progress'); + if (node.parentNode) { node.parentNode.removeChild(node); } document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); document.querySelector('#cp-loading-message').innerText = err; From db1973131f280fdefa4ede71fa83aba4736f4359 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 30 Oct 2020 13:44:51 +0530 Subject: [PATCH 27/27] 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);