From 52fc81ddc894d1431e720a1a502282bb8951ea46 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:32:42 +0200 Subject: [PATCH 01/20] Don't redraw kanban when the tab is not focused --- www/common/visible.js | 6 ++++-- www/kanban/jkanban_cp.js | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/www/common/visible.js b/www/common/visible.js index f73fa49bc..61512ff59 100644 --- a/www/common/visible.js +++ b/www/common/visible.js @@ -25,10 +25,12 @@ typeof document[hidden] === "undefined"); }; - Visible.onChange = function (f) { + Visible.onChange = function (f, once) { document.addEventListener(visibilityChange, function (ev) { f(!document[hidden], ev); - }, false); + }, { + once: once + }); }; Visible.currently = function () { diff --git a/www/kanban/jkanban_cp.js b/www/kanban/jkanban_cp.js index 54fb71f46..1604a1315 100644 --- a/www/kanban/jkanban_cp.js +++ b/www/kanban/jkanban_cp.js @@ -1,8 +1,9 @@ define([ 'jquery', '/customize/messages.js', + '/common/visible.js', '/bower_components/dragula.js/dist/dragula.min.js', -], function ($, Messages, Dragula) { +], function ($, Messages, Visible, Dragula) { /** * jKanban * Vanilla Javascript plugin for manage kanban boards @@ -739,6 +740,8 @@ define([ return self; }; + var todoOnVisible = function () {}; + var onVisibleHandler = false; this.setBoards = function (boards) { var scroll = {}; // Fix the tags @@ -754,15 +757,30 @@ define([ this.removeBoard(boardkey); } this.options.boards = boards; - // Add all new boards - this.addBoards(); - self.options.refresh(); - // Preserve scroll - this.options.boards.list.forEach(function (id) { - if (!scroll[id]) { return; } - $('.kanban-board[data-id="'+id+'"] .kanban-drag').scrollTop(scroll[id]); - }); - $el.scrollLeft(scrollLeft); + + todoOnVisible = function () { + // Add all new boards + self.addBoards(); + self.options.refresh(); + // Preserve scroll + self.options.boards.list.forEach(function (id) { + if (!scroll[id]) { return; } + $('.kanban-board[data-id="'+id+'"] .kanban-drag').scrollTop(scroll[id]); + }); + $el.scrollLeft(scrollLeft); + }; + + // If the tab is not focused, redraw on focus + if (!Visible.currently()) { + if (onVisibleHandler) { return; } + onVisibleHandler = true; + return void Visible.onChange(function (visible) { + if (!visible) { return; } + todoOnVisible(); + onVisibleHandler = false; + }, true); + } + todoOnVisible(); }; this.findBoard = function (id) { From 8aebfac7ee60315c63abd7d3d5f36c965f65eee3 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:35:35 +0200 Subject: [PATCH 02/20] Make sure toolbar dropdown entries are unselectable --- customize.dist/src/less2/include/toolbar.less | 1 + 1 file changed, 1 insertion(+) diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less index 985f6bd83..a5e7ea251 100644 --- a/customize.dist/src/less2/include/toolbar.less +++ b/customize.dist/src/less2/include/toolbar.less @@ -1033,6 +1033,7 @@ flex-flow: column; z-index: 10000; //Z cp-toolbar-drawer-content color: black; + .tools_unselectable(); .fa { font-size: 17px; } From 2d72b6821f5307d4c5d8efefd008b03335c11a80 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:51:45 +0200 Subject: [PATCH 03/20] Preserve 'select' dropdown annotations on click --- www/common/common-ui-elements.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 7d829bdb0..265a61639 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2294,7 +2294,9 @@ define([ value = val; var $val = $innerblock.find('[data-value="'+val+'"]'); var textValue = name || $val.html() || val; - $button.find('.cp-dropdown-button-title').html(textValue); + setTimeout(function () { + $button.find('.cp-dropdown-button-title').html(textValue); + }); }; $container.getValue = function () { return value || ''; From d0911028076ad571d6822b4ea4a9bfb4d7f5bbd2 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 15:00:39 +0200 Subject: [PATCH 04/20] Make search results sortable --- www/common/drive-ui.js | 129 ++++++++++++++++++++++++--------------- www/common/userObject.js | 8 ++- 2 files changed, 86 insertions(+), 51 deletions(-) diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index cd3759d8d..803e0a715 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -2779,30 +2779,34 @@ define([ $sortBlock.on('click', 'a', onSortByClick); return $fhSort; }; - var getFolderListHeader = function () { + var getFolderListHeader = function (clickable, small) { var $fohElement = $('
  • ', { 'class': 'cp-app-drive-element-header cp-app-drive-element-list' }); + var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : ''; + var onClick = clickable ? onSortByClick : function () {}; //var $fohElement = $('', {'class': 'element'}).appendTo($folderHeader); var $fhIcon = $('', {'class': 'cp-app-drive-content-icon'}); var $name = $('', { - 'class': 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + - 'cp-app-drive-sort-clickable' - }).text(Messages.fm_folderName).click(onSortByClick); + 'class': 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + clickCls + }).text(Messages.fm_folderName).click(onClick); var $state = $('', {'class': 'cp-app-drive-element-state'}); - var $subfolders = $('', { - 'class': 'cp-app-drive-element-folders cp-app-drive-element-list' - }).text(Messages.fm_numberOfFolders); - var $files = $('', { - 'class': 'cp-app-drive-element-files cp-app-drive-element-list' - }).text(Messages.fm_numberOfFiles); + var $subfolders, $files; + if (!small) { + $subfolders = $('', { + 'class': 'cp-app-drive-element-folders cp-app-drive-element-list' + }).text(Messages.fm_numberOfFolders); + $files = $('', { + 'class': 'cp-app-drive-element-files cp-app-drive-element-list' + }).text(Messages.fm_numberOfFiles); + } var $filler = $('', { 'class': 'cp-app-drive-element-filler cp-app-drive-element-list' }); $fohElement.append($fhIcon).append($name).append($state) .append($subfolders).append($files).append($filler); - addFolderSortIcon($fohElement); + if (clickable) { addFolderSortIcon($fohElement); } return $fohElement; }; var addFileSortIcon = function ($list) { @@ -2859,15 +2863,16 @@ define([ }); if (keys.length < 2) { return keys; } var mult = asc ? 1 : -1; - var getProp = function (el) { - if (folder && root[el] && manager.isSharedFolder(root[el])) { - var title = manager.getSharedFolderData(root[el]).title || el; + var getProp = function (_el) { + var el = useId ? _el : root[_el]; + var sfId = (el && el.root && el.key) ? el.root[el.key] : el; + if (folder && el && manager.isSharedFolder(sfId)) { + var title = manager.getSharedFolderData(sfId).title || el; return String(title).toLowerCase(); } else if (folder) { - return el.toLowerCase(); + return String((el && el.key) || el).toLowerCase(); } - var id = useId ? el : root[el]; - var data = manager.getFileData(id); + var data = manager.getFileData(el); if (!data) { return ''; } if (prop === 'type') { var hrefData = Hash.parsePadUrl(data.href || data.roHref); @@ -2876,15 +2881,19 @@ define([ if (prop === 'atime' || prop === 'ctime') { return typeof(data[prop]) === "number" ? data[prop] : new Date(data[prop]); } - return (manager.getTitle(id) || "").toLowerCase(); + return (manager.getTitle(el) || "").toLowerCase(); }; var props = {}; keys.forEach(function (k) { - props[k] = getProp(k); + var uid = k; + if (typeof(k) === "object") { + uid = k.uid = Util.uid(); + } + props[uid] = getProp(k); }); keys.sort(function(a, b) { - var _a = props[a]; - var _b = props[b]; + var _a = props[(a && a.uid) || a]; + var _b = props[(b && b.uid) || b]; if (_a < _b) { return mult * -1; } if (_b < _a) { return mult; } return 0; @@ -3233,9 +3242,10 @@ define([ $input.focus(); }); - getFileListHeader(false).appendTo($list); $list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list'); var filesList = manager.search(value); + var sortable = {}; + var sortableFolders = []; filesList.forEach(function (r) { // if r.id === null, then it's a folder, not a file r.paths.forEach(function (path) { @@ -3244,33 +3254,56 @@ define([ var _path = path.slice(); var key = path.pop(); var root = manager.find(path); - var isFolder = manager.isFolder(root[key]); - var $element = createElement(path, key, root, isFolder); - $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result'); - $element.off('contextmenu'); - $element.contextmenu(openContextMenu('default')); - $element.data('context', 'default'); - if (isFolder) { - $element.find('.cp-app-drive-element-list').css({ - visibility: 'hidden' - }).text(''); - $element.find('.cp-app-drive-element-folders').css({ - visibility: '' - }).text(Messages.fm_folder); - } - - if (manager.isPathIn(_path, ['hrefArray'])) { - _path.pop(); - _path.push(r.data.title); + var obj = { + path: path, + _path: _path, + key: key, + root: root, + data: r.data + }; + if (manager.isFolder(root[key])) { + sortableFolders.push(obj); + return; } - var $path = $('', { - 'class': 'cp-app-drive-search-path' - }).appendTo($element.find('.cp-app-drive-element-name')); - createTitle($path, _path); - - $list.append($element); + sortable[root[key]] = obj; }); }); + var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true); + var sortableKeys = Object.keys(sortable).map(Number); + var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true); + + var addEl = function (obj, folder) { + var $element = createElement(obj.path, obj.key, obj.root, folder); + $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result'); + $element.off('contextmenu'); + $element.contextmenu(openContextMenu('default')); + $element.data('context', 'default'); + if (folder) { + $element.find('.cp-app-drive-element-list').css({ + visibility: 'hidden' + }).text(''); + } + if (manager.isPathIn(obj._path, ['hrefArray'])) { + obj._path.pop(); + obj._path.push(obj.data.title); + } + var $path = $('', { + 'class': 'cp-app-drive-search-path' + }).appendTo($element.find('.cp-app-drive-element-name')); + createTitle($path, obj._path); + + $list.append($element); + }; + if (_folders.length) { getFolderListHeader(true, true).appendTo($list); } + _folders.forEach(function (el) { + var obj = el; + addEl(obj, true); + }); + if (_files.length) { getFileListHeader(true).appendTo($list); } + _files.forEach(function (el) { + var obj = sortable[el]; + addEl(obj, false); + }); setTimeout(collapseDrivePath); }; @@ -3415,7 +3448,7 @@ define([ } var fId = APP.newSharedFolder; var data = folders[fId]; - var $folderHeader = getFolderListHeader(); + var $folderHeader = getFolderListHeader(true); var $fileHeader = getFileListHeader(true); var path = currentPath.slice(1); var root = Util.find(data, path); @@ -3593,7 +3626,7 @@ define([ } updateContextButton(); - var $folderHeader = getFolderListHeader(); + var $folderHeader = getFolderListHeader(true); var $fileHeader = getFileListHeader(true); if (isTemplate) { diff --git a/www/common/userObject.js b/www/common/userObject.js index fbe28864a..e6309a88c 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -640,6 +640,7 @@ define([ var res = []; // Search title var allFilesList = files[FILES_DATA]; + var allSFList = files[SHARED_FOLDERS]; var lValue = value.toLowerCase(); // parse the search string into tags @@ -661,13 +662,14 @@ define([ }); }; - getFiles([FILES_DATA]).forEach(function (id) { - var data = allFilesList[id]; + getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) { + var data = allFilesList[id] || allSFList[id]; if (!data) { return; } if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) { res.push(id); } else - if ((data.title && data.title.toLowerCase().indexOf(lValue) !== -1) || + var title = data.title || data.lastTitle + if ((title && title.toLowerCase().indexOf(lValue) !== -1) || (data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) { res.push(id); } From 1d0c2bded6e3251d0d7f0c78d303f78ac9207467 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 15:02:39 +0200 Subject: [PATCH 05/20] Don't try to send a mailbox message as an anonymous user --- www/common/migrate-user-object.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/migrate-user-object.js b/www/common/migrate-user-object.js index 231c39ebd..ef73e5f3a 100644 --- a/www/common/migrate-user-object.js +++ b/www/common/migrate-user-object.js @@ -478,6 +478,7 @@ define([ store: store, }; var myData = Messaging.createData(userObject); + if (!myData.curvePublic) { return void done(); } Mailbox.sendTo(ctx, 'SAFE_LINKS_DEFAULT', { user: myData, From 33db6e0ce24d427f63f7a39109f95d8c682c1498 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 15:47:16 +0200 Subject: [PATCH 06/20] Fix margin issue in export modal --- customize.dist/src/less2/include/forms.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 7663bee51..96f419310 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -30,7 +30,7 @@ div.cp-alertify-type { display: flex; input { - margin: 0; + margin: 0 !important; flex: 1; min-width: 0; } From c5893cd78770be20e6cbd5f2416a39dce1b1ba0b Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 16:53:31 +0200 Subject: [PATCH 07/20] Move print button to our toolbar --- www/pad/app-pad.less | 3 +++ www/pad/inner.js | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less index 103241a52..e3de1fcf0 100644 --- a/www/pad/app-pad.less +++ b/www/pad/app-pad.less @@ -36,6 +36,9 @@ body.cp-app-pad { align-items: center; padding: 4px; } + .cke_button__print { + display: none !important; + } } .cke_wysiwyg_frame { width: 100%; diff --git a/www/pad/inner.js b/www/pad/inner.js index 05cf2c788..84fd311f8 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -456,6 +456,15 @@ define([ }); }; + var mkPrintButton = function (framework, editor, ckeditor) { + var $printButton = framework._.sfCommon.createButton('print', true); + $printButton.click(function () { + editor.execCommand('print'); + framework.feedback('PRINT_PAD'); + }); + framework._.toolbar.$drawer.append($printButton); + }; + var andThen2 = function(editor, Ckeditor, framework) { var mediaTagMap = {}; var $contentContainer = $('#cke_1_contents'); @@ -480,6 +489,8 @@ define([ framework._.sfCommon.addShortcuts(ifrWindow); + mkPrintButton(framework, editor, Ckeditor); + var documentBody = ifrWindow.document.body; var inner = window.inner = documentBody; var $inner = $(inner); From da0bb6f95b7de4263ce1d4cb9a9207436f1b41e0 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 23 Jun 2020 13:27:31 +0200 Subject: [PATCH 08/20] Fix 'change user password' overriding existing data --- www/common/cryptpad-common.js | 20 ++++++++++++++++++++ www/settings/inner.js | 1 + 2 files changed, 21 insertions(+) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 19faf940b..6b09656a0 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1604,6 +1604,26 @@ define([ var allocated = Login.allocateBytes(bytes); blockKeys = allocated.blockKeys; })); + }).nThen(function (waitFor) { + var blockUrl = Block.getBlockUrl(blockKeys); + // Check whether there is a block at that location + Util.fetch(blockUrl, waitFor(function (err, block) { + // If there is no block or the block is invalid, continue. + if (err) { + console.log("no block found"); + return; + } + + var decryptedBlock = Block.decrypt(block, blockKeys); + if (!decryptedBlock) { + console.error("Found a login block but failed to decrypt"); + return; + } + + // If there is already a valid block, abort! We risk overriding another user's data + waitFor.abort(); + cb({ error: 'EEXISTS' }); + })); }).nThen(function (waitFor) { // Write the new login block var temp = { diff --git a/www/settings/inner.js b/www/settings/inner.js index f66e1d002..67f35efc3 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -488,6 +488,7 @@ define([ UI.removeLoadingScreen(); if (obj && obj.error) { // TODO + // XXX EEXISTS error message? UI.alert(Messages.settings_changePasswordError); } }); From b9cacc5c94f5645d8dc8e513d17ee94fd18e7b30 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 23 Jun 2020 13:30:22 +0200 Subject: [PATCH 09/20] Fix change password modal UI --- www/settings/inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/settings/inner.js b/www/settings/inner.js index 67f35efc3..234402ab5 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -495,8 +495,8 @@ define([ }, { ok: Messages.register_writtenPassword, cancel: Messages.register_cancel, - cancelClass: 'safe', - okClass: 'danger', + cancelClass: 'btn.btn-safe', + okClass: 'btn.btn-danger', reverseOrder: true, done: function($dialog) { $dialog.find('> div').addClass('half'); From f1e6874eeb2f2ec1592f1613512d693f12fdb87d Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 23 Jun 2020 13:34:49 +0200 Subject: [PATCH 10/20] lint compliance --- www/common/userObject.js | 6 +++--- www/pad/inner.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/common/userObject.js b/www/common/userObject.js index e6309a88c..23978cb65 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -666,9 +666,9 @@ define([ var data = allFilesList[id] || allSFList[id]; if (!data) { return; } if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) { - res.push(id); - } else - var title = data.title || data.lastTitle + return void res.push(id); + } + var title = data.title || data.lastTitle; if ((title && title.toLowerCase().indexOf(lValue) !== -1) || (data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) { res.push(id); diff --git a/www/pad/inner.js b/www/pad/inner.js index 84fd311f8..f189d7447 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -456,7 +456,7 @@ define([ }); }; - var mkPrintButton = function (framework, editor, ckeditor) { + var mkPrintButton = function (framework, editor) { var $printButton = framework._.sfCommon.createButton('print', true); $printButton.click(function () { editor.execCommand('print'); From 49e6fdd99eb780c6772218a3c33ce6044aee15ea Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 22 Jun 2020 23:40:36 +0200 Subject: [PATCH 11/20] Translated using Weblate (English) Currently translated at 100.0% (1299 of 1299 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 d99fd8d18..ede0c1a5a 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -1394,5 +1394,6 @@ "notification_folderSharedTeam": "{0} has shared a folder with the team {2}: {1}", "oo_refresh": "Refresh", "oo_refreshText": "This document has been updated", - "support_category": "Choose a category" + "support_category": "Choose a category", + "support_formCategoryError": "Error: category is empty" } From d7dfe75746b8c5ec137f2fb07bf2aa065e9ac4af Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 22 Jun 2020 23:40:37 +0200 Subject: [PATCH 12/20] Translated using Weblate (German) Currently translated at 100.0% (1299 of 1299 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index 3ab3680ea..8992a1b0b 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1394,5 +1394,6 @@ "support_cat_bug": "Fehlerbericht", "oo_refresh": "Neu laden", "support_category": "Wähle eine Kategorie", - "oo_refreshText": "Dieses Dokument wurde aktualisiert" + "oo_refreshText": "Dieses Dokument wurde aktualisiert", + "support_formCategoryError": "Fehler: Kategorie ist leer" } From 95f4b2357d9f8c0937062807da697b3cb038ffd5 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:32:42 +0200 Subject: [PATCH 13/20] Don't redraw kanban when the tab is not focused --- www/common/visible.js | 6 ++++-- www/kanban/jkanban_cp.js | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/www/common/visible.js b/www/common/visible.js index f73fa49bc..61512ff59 100644 --- a/www/common/visible.js +++ b/www/common/visible.js @@ -25,10 +25,12 @@ typeof document[hidden] === "undefined"); }; - Visible.onChange = function (f) { + Visible.onChange = function (f, once) { document.addEventListener(visibilityChange, function (ev) { f(!document[hidden], ev); - }, false); + }, { + once: once + }); }; Visible.currently = function () { diff --git a/www/kanban/jkanban_cp.js b/www/kanban/jkanban_cp.js index 54fb71f46..1604a1315 100644 --- a/www/kanban/jkanban_cp.js +++ b/www/kanban/jkanban_cp.js @@ -1,8 +1,9 @@ define([ 'jquery', '/customize/messages.js', + '/common/visible.js', '/bower_components/dragula.js/dist/dragula.min.js', -], function ($, Messages, Dragula) { +], function ($, Messages, Visible, Dragula) { /** * jKanban * Vanilla Javascript plugin for manage kanban boards @@ -739,6 +740,8 @@ define([ return self; }; + var todoOnVisible = function () {}; + var onVisibleHandler = false; this.setBoards = function (boards) { var scroll = {}; // Fix the tags @@ -754,15 +757,30 @@ define([ this.removeBoard(boardkey); } this.options.boards = boards; - // Add all new boards - this.addBoards(); - self.options.refresh(); - // Preserve scroll - this.options.boards.list.forEach(function (id) { - if (!scroll[id]) { return; } - $('.kanban-board[data-id="'+id+'"] .kanban-drag').scrollTop(scroll[id]); - }); - $el.scrollLeft(scrollLeft); + + todoOnVisible = function () { + // Add all new boards + self.addBoards(); + self.options.refresh(); + // Preserve scroll + self.options.boards.list.forEach(function (id) { + if (!scroll[id]) { return; } + $('.kanban-board[data-id="'+id+'"] .kanban-drag').scrollTop(scroll[id]); + }); + $el.scrollLeft(scrollLeft); + }; + + // If the tab is not focused, redraw on focus + if (!Visible.currently()) { + if (onVisibleHandler) { return; } + onVisibleHandler = true; + return void Visible.onChange(function (visible) { + if (!visible) { return; } + todoOnVisible(); + onVisibleHandler = false; + }, true); + } + todoOnVisible(); }; this.findBoard = function (id) { From 0e7e94b00618167f6a52925f836059711e608f24 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:35:35 +0200 Subject: [PATCH 14/20] Make sure toolbar dropdown entries are unselectable --- customize.dist/src/less2/include/toolbar.less | 1 + 1 file changed, 1 insertion(+) diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less index 985f6bd83..a5e7ea251 100644 --- a/customize.dist/src/less2/include/toolbar.less +++ b/customize.dist/src/less2/include/toolbar.less @@ -1033,6 +1033,7 @@ flex-flow: column; z-index: 10000; //Z cp-toolbar-drawer-content color: black; + .tools_unselectable(); .fa { font-size: 17px; } From c32f0d61ce2c06e643735de4572d4394e118be22 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 11:51:45 +0200 Subject: [PATCH 15/20] Preserve 'select' dropdown annotations on click --- www/common/common-ui-elements.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 386353339..fc514ef3b 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2299,7 +2299,9 @@ define([ value = val; var $val = $innerblock.find('[data-value="'+val+'"]'); var textValue = name || $val.html() || val; - $button.find('.cp-dropdown-button-title').html(textValue); + setTimeout(function () { + $button.find('.cp-dropdown-button-title').html(textValue); + }); }; $container.getValue = function () { return value || ''; From b868acc6410ab416779057dcbfe48d846dd871b7 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 15:00:39 +0200 Subject: [PATCH 16/20] Make search results sortable --- www/common/drive-ui.js | 129 ++++++++++++++++++++++++--------------- www/common/userObject.js | 8 ++- 2 files changed, 86 insertions(+), 51 deletions(-) diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index cd3759d8d..803e0a715 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -2779,30 +2779,34 @@ define([ $sortBlock.on('click', 'a', onSortByClick); return $fhSort; }; - var getFolderListHeader = function () { + var getFolderListHeader = function (clickable, small) { var $fohElement = $('
  • ', { 'class': 'cp-app-drive-element-header cp-app-drive-element-list' }); + var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : ''; + var onClick = clickable ? onSortByClick : function () {}; //var $fohElement = $('', {'class': 'element'}).appendTo($folderHeader); var $fhIcon = $('', {'class': 'cp-app-drive-content-icon'}); var $name = $('', { - 'class': 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + - 'cp-app-drive-sort-clickable' - }).text(Messages.fm_folderName).click(onSortByClick); + 'class': 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + clickCls + }).text(Messages.fm_folderName).click(onClick); var $state = $('', {'class': 'cp-app-drive-element-state'}); - var $subfolders = $('', { - 'class': 'cp-app-drive-element-folders cp-app-drive-element-list' - }).text(Messages.fm_numberOfFolders); - var $files = $('', { - 'class': 'cp-app-drive-element-files cp-app-drive-element-list' - }).text(Messages.fm_numberOfFiles); + var $subfolders, $files; + if (!small) { + $subfolders = $('', { + 'class': 'cp-app-drive-element-folders cp-app-drive-element-list' + }).text(Messages.fm_numberOfFolders); + $files = $('', { + 'class': 'cp-app-drive-element-files cp-app-drive-element-list' + }).text(Messages.fm_numberOfFiles); + } var $filler = $('', { 'class': 'cp-app-drive-element-filler cp-app-drive-element-list' }); $fohElement.append($fhIcon).append($name).append($state) .append($subfolders).append($files).append($filler); - addFolderSortIcon($fohElement); + if (clickable) { addFolderSortIcon($fohElement); } return $fohElement; }; var addFileSortIcon = function ($list) { @@ -2859,15 +2863,16 @@ define([ }); if (keys.length < 2) { return keys; } var mult = asc ? 1 : -1; - var getProp = function (el) { - if (folder && root[el] && manager.isSharedFolder(root[el])) { - var title = manager.getSharedFolderData(root[el]).title || el; + var getProp = function (_el) { + var el = useId ? _el : root[_el]; + var sfId = (el && el.root && el.key) ? el.root[el.key] : el; + if (folder && el && manager.isSharedFolder(sfId)) { + var title = manager.getSharedFolderData(sfId).title || el; return String(title).toLowerCase(); } else if (folder) { - return el.toLowerCase(); + return String((el && el.key) || el).toLowerCase(); } - var id = useId ? el : root[el]; - var data = manager.getFileData(id); + var data = manager.getFileData(el); if (!data) { return ''; } if (prop === 'type') { var hrefData = Hash.parsePadUrl(data.href || data.roHref); @@ -2876,15 +2881,19 @@ define([ if (prop === 'atime' || prop === 'ctime') { return typeof(data[prop]) === "number" ? data[prop] : new Date(data[prop]); } - return (manager.getTitle(id) || "").toLowerCase(); + return (manager.getTitle(el) || "").toLowerCase(); }; var props = {}; keys.forEach(function (k) { - props[k] = getProp(k); + var uid = k; + if (typeof(k) === "object") { + uid = k.uid = Util.uid(); + } + props[uid] = getProp(k); }); keys.sort(function(a, b) { - var _a = props[a]; - var _b = props[b]; + var _a = props[(a && a.uid) || a]; + var _b = props[(b && b.uid) || b]; if (_a < _b) { return mult * -1; } if (_b < _a) { return mult; } return 0; @@ -3233,9 +3242,10 @@ define([ $input.focus(); }); - getFileListHeader(false).appendTo($list); $list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list'); var filesList = manager.search(value); + var sortable = {}; + var sortableFolders = []; filesList.forEach(function (r) { // if r.id === null, then it's a folder, not a file r.paths.forEach(function (path) { @@ -3244,33 +3254,56 @@ define([ var _path = path.slice(); var key = path.pop(); var root = manager.find(path); - var isFolder = manager.isFolder(root[key]); - var $element = createElement(path, key, root, isFolder); - $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result'); - $element.off('contextmenu'); - $element.contextmenu(openContextMenu('default')); - $element.data('context', 'default'); - if (isFolder) { - $element.find('.cp-app-drive-element-list').css({ - visibility: 'hidden' - }).text(''); - $element.find('.cp-app-drive-element-folders').css({ - visibility: '' - }).text(Messages.fm_folder); - } - - if (manager.isPathIn(_path, ['hrefArray'])) { - _path.pop(); - _path.push(r.data.title); + var obj = { + path: path, + _path: _path, + key: key, + root: root, + data: r.data + }; + if (manager.isFolder(root[key])) { + sortableFolders.push(obj); + return; } - var $path = $('', { - 'class': 'cp-app-drive-search-path' - }).appendTo($element.find('.cp-app-drive-element-name')); - createTitle($path, _path); - - $list.append($element); + sortable[root[key]] = obj; }); }); + var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true); + var sortableKeys = Object.keys(sortable).map(Number); + var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true); + + var addEl = function (obj, folder) { + var $element = createElement(obj.path, obj.key, obj.root, folder); + $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result'); + $element.off('contextmenu'); + $element.contextmenu(openContextMenu('default')); + $element.data('context', 'default'); + if (folder) { + $element.find('.cp-app-drive-element-list').css({ + visibility: 'hidden' + }).text(''); + } + if (manager.isPathIn(obj._path, ['hrefArray'])) { + obj._path.pop(); + obj._path.push(obj.data.title); + } + var $path = $('', { + 'class': 'cp-app-drive-search-path' + }).appendTo($element.find('.cp-app-drive-element-name')); + createTitle($path, obj._path); + + $list.append($element); + }; + if (_folders.length) { getFolderListHeader(true, true).appendTo($list); } + _folders.forEach(function (el) { + var obj = el; + addEl(obj, true); + }); + if (_files.length) { getFileListHeader(true).appendTo($list); } + _files.forEach(function (el) { + var obj = sortable[el]; + addEl(obj, false); + }); setTimeout(collapseDrivePath); }; @@ -3415,7 +3448,7 @@ define([ } var fId = APP.newSharedFolder; var data = folders[fId]; - var $folderHeader = getFolderListHeader(); + var $folderHeader = getFolderListHeader(true); var $fileHeader = getFileListHeader(true); var path = currentPath.slice(1); var root = Util.find(data, path); @@ -3593,7 +3626,7 @@ define([ } updateContextButton(); - var $folderHeader = getFolderListHeader(); + var $folderHeader = getFolderListHeader(true); var $fileHeader = getFileListHeader(true); if (isTemplate) { diff --git a/www/common/userObject.js b/www/common/userObject.js index fbe28864a..e6309a88c 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -640,6 +640,7 @@ define([ var res = []; // Search title var allFilesList = files[FILES_DATA]; + var allSFList = files[SHARED_FOLDERS]; var lValue = value.toLowerCase(); // parse the search string into tags @@ -661,13 +662,14 @@ define([ }); }; - getFiles([FILES_DATA]).forEach(function (id) { - var data = allFilesList[id]; + getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) { + var data = allFilesList[id] || allSFList[id]; if (!data) { return; } if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) { res.push(id); } else - if ((data.title && data.title.toLowerCase().indexOf(lValue) !== -1) || + var title = data.title || data.lastTitle + if ((title && title.toLowerCase().indexOf(lValue) !== -1) || (data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) { res.push(id); } From fe36d1ca125a34d6320486e19aa1c7016c6f085c Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 15:02:39 +0200 Subject: [PATCH 17/20] Don't try to send a mailbox message as an anonymous user --- www/common/migrate-user-object.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/migrate-user-object.js b/www/common/migrate-user-object.js index b13aacb1c..000744cc9 100644 --- a/www/common/migrate-user-object.js +++ b/www/common/migrate-user-object.js @@ -478,6 +478,7 @@ define([ store: store, }; var myData = Messaging.createData(userObject); + if (!myData.curvePublic) { return void done(); } Mailbox.sendTo(ctx, 'SAFE_LINKS_DEFAULT', { user: myData, From 6176d73c86f9bc9cbeeac7c925fda5ec98d677a6 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 22 Jun 2020 16:53:31 +0200 Subject: [PATCH 18/20] Move print button to our toolbar --- www/pad/app-pad.less | 3 +++ www/pad/inner.js | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/www/pad/app-pad.less b/www/pad/app-pad.less index 103241a52..e3de1fcf0 100644 --- a/www/pad/app-pad.less +++ b/www/pad/app-pad.less @@ -36,6 +36,9 @@ body.cp-app-pad { align-items: center; padding: 4px; } + .cke_button__print { + display: none !important; + } } .cke_wysiwyg_frame { width: 100%; diff --git a/www/pad/inner.js b/www/pad/inner.js index 05cf2c788..84fd311f8 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -456,6 +456,15 @@ define([ }); }; + var mkPrintButton = function (framework, editor, ckeditor) { + var $printButton = framework._.sfCommon.createButton('print', true); + $printButton.click(function () { + editor.execCommand('print'); + framework.feedback('PRINT_PAD'); + }); + framework._.toolbar.$drawer.append($printButton); + }; + var andThen2 = function(editor, Ckeditor, framework) { var mediaTagMap = {}; var $contentContainer = $('#cke_1_contents'); @@ -480,6 +489,8 @@ define([ framework._.sfCommon.addShortcuts(ifrWindow); + mkPrintButton(framework, editor, Ckeditor); + var documentBody = ifrWindow.document.body; var inner = window.inner = documentBody; var $inner = $(inner); From 7bbbbbad77742e78913844abe6e7030d3cabfdc9 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 23 Jun 2020 13:27:31 +0200 Subject: [PATCH 19/20] Fix 'change user password' overriding existing data --- www/common/cryptpad-common.js | 20 ++++++++++++++++++++ www/settings/inner.js | 1 + 2 files changed, 21 insertions(+) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 19faf940b..6b09656a0 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1604,6 +1604,26 @@ define([ var allocated = Login.allocateBytes(bytes); blockKeys = allocated.blockKeys; })); + }).nThen(function (waitFor) { + var blockUrl = Block.getBlockUrl(blockKeys); + // Check whether there is a block at that location + Util.fetch(blockUrl, waitFor(function (err, block) { + // If there is no block or the block is invalid, continue. + if (err) { + console.log("no block found"); + return; + } + + var decryptedBlock = Block.decrypt(block, blockKeys); + if (!decryptedBlock) { + console.error("Found a login block but failed to decrypt"); + return; + } + + // If there is already a valid block, abort! We risk overriding another user's data + waitFor.abort(); + cb({ error: 'EEXISTS' }); + })); }).nThen(function (waitFor) { // Write the new login block var temp = { diff --git a/www/settings/inner.js b/www/settings/inner.js index 3fc87d175..234402ab5 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -488,6 +488,7 @@ define([ UI.removeLoadingScreen(); if (obj && obj.error) { // TODO + // XXX EEXISTS error message? UI.alert(Messages.settings_changePasswordError); } }); From ff6d28a1e9a86f649f99ac551bc18d1e2da4f679 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 23 Jun 2020 13:34:49 +0200 Subject: [PATCH 20/20] lint compliance --- www/common/userObject.js | 6 +++--- www/pad/inner.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/common/userObject.js b/www/common/userObject.js index e6309a88c..23978cb65 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -666,9 +666,9 @@ define([ var data = allFilesList[id] || allSFList[id]; if (!data) { return; } if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) { - res.push(id); - } else - var title = data.title || data.lastTitle + return void res.push(id); + } + var title = data.title || data.lastTitle; if ((title && title.toLowerCase().indexOf(lValue) !== -1) || (data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) { res.push(id); diff --git a/www/pad/inner.js b/www/pad/inner.js index 84fd311f8..f189d7447 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -456,7 +456,7 @@ define([ }); }; - var mkPrintButton = function (framework, editor, ckeditor) { + var mkPrintButton = function (framework, editor) { var $printButton = framework._.sfCommon.createButton('print', true); $printButton.click(function () { editor.execCommand('print');