From 9a25ac537a1bfd009f9a67eb021fc961ae2ad70f Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 22 Jul 2019 12:02:24 +0200 Subject: [PATCH 01/30] Add file plugin to display plain text files --- www/common/media-tag.js | 33 +++++++++++++++++++++++++++++++++ www/file/app-file.less | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index c820d9201..95830e296 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -30,9 +30,22 @@ }; + var isplainTextFile = function (metadata) { + // does it type begins with "text/" + if (metadata.type.indexOf("text/") === 0) { return true; } + // no type and no file extension -> let's guess it's plain text + var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || []; + if (!metadata.type && !parsedName[2]) { return true; } + // other exceptions + if (metadata.type === 'application/x-javascript') { return true; } + if (metadata.type === 'application/xml') { return true; } + return false; + } + // Default config, can be overriden per media-tag call var config = { allowed: [ + 'text/plain', 'image/png', 'image/jpeg', 'image/jpg', @@ -53,6 +66,23 @@ text: "Download" }, Plugins: { + /** + * @param {object} metadataObject {name, metadatatype, owners} containing metadata of the file + * @param {strint} url Url of the blob object + * @param {Blob} content Blob object containing the data of the file + * @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins + * @param {function} cb Callback function: (err, pluginElement) => {} + */ + text: function (metadata, url, content, cfg, cb) { + var plainText = document.createElement('div'); + plainText.className = "plain-text-reader"; + var reader = new FileReader(); + reader.addEventListener('loadend', function (e) { + plainText.innerText = e.srcElement.result; + cb(void 0, plainText); + }); + reader.readAsText(content); + }, image: function (metadata, url, content, cfg, cb) { var img = document.createElement('img'); img.setAttribute('src', url); @@ -271,6 +301,9 @@ var blob = decrypted.content; var mediaType = getType(mediaObject, metadata, cfg); + if (isplainTextFile(metadata)) { + mediaType = "text"; + } if (mediaType === 'application') { mediaType = mediaObject.extension; diff --git a/www/file/app-file.less b/www/file/app-file.less index 20428b382..4ac94ca33 100644 --- a/www/file/app-file.less +++ b/www/file/app-file.less @@ -52,6 +52,16 @@ max-width: 100%; max-height: ~"calc(100vh - 96px)"; } + .plain-text-reader { + align-self: flex-start; + width: 90vw; + height: 100%; + padding: 2em; + background-color: white; + overflow-y: auto; + word-wrap: break-word; + white-space: pre-wrap; + } } #cp-app-file-upload-form, #cp-app-file-download-form { From ff66c4162bb1ad62332828c808f531c26eee53b4 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 22 Jul 2019 12:02:24 +0200 Subject: [PATCH 02/30] Add file plugin to display plain text files --- www/common/media-tag.js | 33 +++++++++++++++++++++++++++++++++ www/file/app-file.less | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index c820d9201..86bf27b03 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -30,9 +30,22 @@ }; + var isplainTextFile = function (metadata) { + // does its type begins with "text/" + if (metadata.type.indexOf("text/") === 0) { return true; } + // no type and no file extension -> let's guess it's plain text + var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || []; + if (!metadata.type && !parsedName[2]) { return true; } + // other exceptions + if (metadata.type === 'application/x-javascript') { return true; } + if (metadata.type === 'application/xml') { return true; } + return false; + } + // Default config, can be overriden per media-tag call var config = { allowed: [ + 'text/plain', 'image/png', 'image/jpeg', 'image/jpg', @@ -53,6 +66,23 @@ text: "Download" }, Plugins: { + /** + * @param {object} metadataObject {name, metadatatype, owners} containing metadata of the file + * @param {strint} url Url of the blob object + * @param {Blob} content Blob object containing the data of the file + * @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins + * @param {function} cb Callback function: (err, pluginElement) => {} + */ + text: function (metadata, url, content, cfg, cb) { + var plainText = document.createElement('div'); + plainText.className = "plain-text-reader"; + var reader = new FileReader(); + reader.addEventListener('loadend', function (e) { + plainText.innerText = e.srcElement.result; + cb(void 0, plainText); + }); + reader.readAsText(content); + }, image: function (metadata, url, content, cfg, cb) { var img = document.createElement('img'); img.setAttribute('src', url); @@ -271,6 +301,9 @@ var blob = decrypted.content; var mediaType = getType(mediaObject, metadata, cfg); + if (isplainTextFile(metadata)) { + mediaType = "text"; + } if (mediaType === 'application') { mediaType = mediaObject.extension; diff --git a/www/file/app-file.less b/www/file/app-file.less index 20428b382..4ac94ca33 100644 --- a/www/file/app-file.less +++ b/www/file/app-file.less @@ -52,6 +52,16 @@ max-width: 100%; max-height: ~"calc(100vh - 96px)"; } + .plain-text-reader { + align-self: flex-start; + width: 90vw; + height: 100%; + padding: 2em; + background-color: white; + overflow-y: auto; + word-wrap: break-word; + white-space: pre-wrap; + } } #cp-app-file-upload-form, #cp-app-file-download-form { From cb76211a3875aaf9cf3487448e87811487b91a70 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 24 Jul 2019 14:00:00 +0200 Subject: [PATCH 03/30] Fix missing semi-colon --- www/common/media-tag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/media-tag.js b/www/common/media-tag.js index 86bf27b03..186fe4632 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -40,7 +40,7 @@ if (metadata.type === 'application/x-javascript') { return true; } if (metadata.type === 'application/xml') { return true; } return false; - } + }; // Default config, can be overriden per media-tag call var config = { From fe2539394bd2215a8c8125d0365261c87329234b Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 24 Jul 2019 14:06:04 +0200 Subject: [PATCH 04/30] Add dummy open-in-code in drive context menu --- www/common/common-util.js | 19 +++++++++++++++++++ www/drive/inner.js | 23 ++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/www/common/common-util.js b/www/common/common-util.js index 9fd2305c3..124a72344 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -319,6 +319,25 @@ define([], function () { return window.innerHeight < 800 || window.innerWidth < 800; }; + Util.isPlainTextFile = function (metadata) { + if (!metadata || !metadata.href) { return; } + console.log("%c" + metadata.title + " : " + metadata.fileType, "color: #3a7"); + console.log(metadata); + var href = metadata.roHref || metadata.href; + // is it a file ? + if (!href || href.indexOf("/file/") === -1) { return false; } + // does its type begins with "text/" + if (metadata.fileType.indexOf("text/") === 0) { return true; } + // no type and no file extension -> let's guess it's plain text + var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.title) || []; + if (!metadata.fileType && !parsedName[2]) { return true; } + // other exceptions + if (metadata.fileType === 'application/x-javascript') { return true; } + if (metadata.fileType === 'application/xml') { return true; } + return false; + }; + + return Util; }); }(self)); diff --git a/www/drive/inner.js b/www/drive/inner.js index f393cac3f..d3e06e39b 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -81,6 +81,7 @@ define([ var faCollapseAll = 'fa-minus-square-o'; var faShared = 'fa-shhare-alt'; var faReadOnly = 'fa-eye'; + var faOpenInCode = 'cptools-code'; var faRename = 'fa-pencil'; var faColor = 'cptools-palette'; var faTrash = 'fa-trash'; @@ -315,6 +316,10 @@ define([ 'tabindex': '-1', 'data-icon': faReadOnly, }, Messages.fc_open_ro)), + h('li', h('a.cp-app-drive-context-openincode.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faOpenInCode, + }, Messages.fc_openInCode || "Open in Code")), $separator.clone()[0], h('li', h('a.cp-app-drive-context-expandall.dropdown-item', { 'tabindex': '-1', @@ -1019,6 +1024,11 @@ define([ } else if ($element.is('.cp-app-drive-element-noreadonly')) { hide.push('openro'); // Remove open 'view' mode } + // if it's not a plain text file + var metadata = manager.getFileData(manager.find(path)); + if (!metadata || !Util.isPlainTextFile(metadata)) { + hide.push('openincode'); + } } else if ($element.is('.cp-app-drive-element-sharedf')) { if (containsFolder) { // More than 1 folder selected: cannot create a new subfolder @@ -1028,6 +1038,7 @@ define([ } containsFolder = true; hide.push('openro'); + hide.push('openincode'); hide.push('hashtag'); hide.push('delete'); //hide.push('deleteowned'); @@ -1040,6 +1051,7 @@ define([ } containsFolder = true; hide.push('openro'); + hide.push('openincode'); hide.push('properties'); hide.push('share'); hide.push('hashtag'); @@ -1089,7 +1101,7 @@ define([ show = ['newfolder', 'newsharedfolder', 'newdoc']; break; case 'tree': - show = ['open', 'openro', 'expandall', 'collapseall', 'color', 'download', 'share', 'rename', 'delete', 'deleteowned', 'removesf', 'properties', 'hashtag']; + show = ['open', 'openro', 'openincode', 'expandall', 'collapseall', 'color', 'download', 'share', 'rename', 'delete', 'deleteowned', 'removesf', 'properties', 'hashtag']; break; case 'default': show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag']; @@ -3525,6 +3537,15 @@ define([ openFile(null, href); }); } + else if ($(this).hasClass('cp-app-drive-context-openincode')) { + paths.forEach(function (p) { + console.info("p", p); + var el = manager.find(p.path); + var metadata = manager.getFileData(el); + console.log(el); + // open code from template + }); + } else if ($(this).hasClass('cp-app-drive-context-expandall') || $(this).hasClass('cp-app-drive-context-collapseall')) { if (paths.length !== 1) { return; } From b49777cd379896d57234a3a1caa47d4261b6f121 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 2 Aug 2019 16:54:44 +0200 Subject: [PATCH 05/30] Move make-backup.js to common --- www/{settings => common}/make-backup.js | 0 www/settings/inner.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename www/{settings => common}/make-backup.js (100%) diff --git a/www/settings/make-backup.js b/www/common/make-backup.js similarity index 100% rename from www/settings/make-backup.js rename to www/common/make-backup.js diff --git a/www/settings/inner.js b/www/settings/inner.js index d785391d0..37143a617 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -12,7 +12,7 @@ define([ '/customize/credential.js', '/customize/application_config.js', '/api/config', - '/settings/make-backup.js', + '/common/make-backup.js', '/common/common-feedback.js', '/bower_components/file-saver/FileSaver.min.js', From 043808d9dbb0f8197510de1f7bc00ba990b06b33 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 5 Aug 2019 10:12:32 +0200 Subject: [PATCH 06/30] Add dummy download folders and pads --- www/drive/inner.js | 89 ++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index fd7723bca..0fdcd4491 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -8,6 +8,7 @@ define([ '/common/common-interface.js', '/common/common-constants.js', '/common/common-feedback.js', + '/common/make-backup.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', '/common/common-realtime.js', @@ -32,6 +33,7 @@ define([ UI, Constants, Feedback, + Backup, nThen, SFCommon, CommonRealtime, @@ -90,7 +92,7 @@ define([ var faEmpty = 'fa-trash-o'; var faRestore = 'fa-repeat'; var faShowParent = 'fa-location-arrow'; - var faDownload = 'cptools-file'; + var faDownload = 'fa-download'; var $folderIcon = $('', { "class": faFolder + " cptools cp-app-drive-icon-folder cp-app-drive-content-icon" }); @@ -325,22 +327,20 @@ define([ 'data-icon': faCollapseAll, }, Messages.fc_collapseAll)), $separator.clone()[0], - h('li', h('a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable', { - 'tabindex': '-1', - 'data-icon': faColor, - }, Messages.fc_color)), - h('li', h('a.cp-app-drive-context-download.dropdown-item', { - 'tabindex': '-1', - 'data-icon': faDownload, - }, Messages.download_mt_button)), - h('li', h('a.cp-app-drive-context-share.dropdown-item', { - 'tabindex': '-1', - 'data-icon': 'fa-shhare-alt', - }, Messages.shareButton)), h('li', h('a.cp-app-drive-context-openparent.dropdown-item', { 'tabindex': '-1', 'data-icon': faShowParent, }, Messages.fm_openParent)), + $separator.clone()[0], + h('li', h('a.cp-app-drive-context-share.dropdown-item', { + 'tabindex': '-1', + 'data-icon': 'fa-shhare-alt', + }, Messages.shareButton)), + h('li', h('a.cp-app-drive-context-download.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faDownload, + }, Messages.download_mt_button)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-newfolder.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', 'data-icon': faFolder, @@ -349,10 +349,6 @@ define([ 'tabindex': '-1', 'data-icon': faSharedFolder, }, Messages.fc_newsharedfolder)), - h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', { - 'tabindex': '-1', - 'data-icon': faTags, - }, Messages.fc_hashtag)), $separator.clone()[0], h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', @@ -393,6 +389,19 @@ define([ 'tabindex': '-1', 'data-icon': faRename, }, Messages.fc_rename)), + h('li', h('a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable', { + 'tabindex': '-1', + 'data-icon': faColor, + }, Messages.fc_color)), + h('li', h('a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable', { + 'tabindex': '-1', + 'data-icon': faTags, + }, Messages.fc_hashtag)), + h('li', h('a.cp-app-drive-context-properties.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faProperties, + }, Messages.fc_prop)), + $separator.clone()[0], h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', 'data-icon': faTrash, @@ -409,10 +418,6 @@ define([ 'tabindex': '-1', 'data-icon': faDelete, }, Messages.fc_remove_sharedfolder)), - h('li', h('a.cp-app-drive-context-properties.dropdown-item', { - 'tabindex': '-1', - 'data-icon': faProperties, - }, Messages.fc_prop)), ]) ]); $(menu).find("li a.dropdown-item").each(function (i, el) { @@ -1008,9 +1013,6 @@ define([ // We can only open parent in virtual categories hide.push('openparent'); } - if (!$element.is('.cp-border-color-file')) { - hide.push('download'); - } if ($element.is('.cp-app-drive-element-file')) { // No folder in files hide.push('color'); @@ -3554,13 +3556,38 @@ define([ } else if ($(this).hasClass('cp-app-drive-context-download')) { if (paths.length !== 1) { return; } - el = manager.find(paths[0].path); - if (!manager.isFile(el)) { return; } - data = manager.getFileData(el); - APP.FM.downloadFile(data, function (err, obj) { - console.log(err, obj); - console.log('DONE'); - }); + var path = paths[0]; + el = manager.find(path.path); + console.log("paths", paths); + console.log("el", el); + + // folder + if (manager.isFolder(el)) { + // folder + if (!manager.isSharedFolder(el)) { + console.log("--isFolder--"); + } + // shared folder + else { + console.log("--isSharedFolder--"); + } + } + // file + else if (manager.isFile(el)) { + // imported file + if (path.element.is(".cp-border-color-file")) { + console.log("--isFile--"); + data = manager.getFileData(el); + APP.FM.downloadFile(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); + } + // pad + else { + console.log("--isPad--"); + } + } } else if ($(this).hasClass('cp-app-drive-context-share')) { if (paths.length !== 1) { return; } From 4f26986fde95b96c5349ecf357c94e910b6353c0 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 6 Aug 2019 12:27:54 +0200 Subject: [PATCH 07/30] Download pads in drive - test --- www/common/sframe-common-file.js | 172 +++++++++++++++++++++++++++++++ www/drive/inner.js | 9 ++ 2 files changed, 181 insertions(+) diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 099176afd..d374737ca 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -447,14 +447,26 @@ define([ createUploader(config.dropArea, config.hoverArea, config.body); File.downloadFile = function (fData, cb) { + console.error("SFC FILE -DOWNLOAD FILE"); + console.log("fData", fData); var parsed = Hash.parsePadUrl(fData.href || fData.roHref); var hash = parsed.hash; var name = fData.filename || fData.title; var secret = Hash.getSecrets('file', hash, fData.password); var src = Hash.getBlobPathFromHex(secret.channel); var key = secret.keys && secret.keys.cryptKey; + console.log('key', key); + console.log('src', src); + console.log('secret', secret); + console.log('name', name); + console.log('hash', hash); + console.log('parsed', parsed); common.getFileSize(secret.channel, function (e, data) { + console.warn("GET FILE SIZE"); + console.log("data", data); var todo = function (file) { + console.warn("TODO"); + console.log("file", file); if (queue.inProgress) { return; } queue.inProgress = true; var id = file.id; @@ -522,6 +534,76 @@ define([ }); }; + File.downloadPad = function (pData, cb) { + console.error("SFC FILE -DOWNLOAD PAD"); + console.log("pData", pData); + var todo = function (file) { + console.warn("TODO"); + console.log("file", file); + if (queue.inProgress) { return; } + queue.inProgress = true; + var id = file.id; + + var $row = $table.find('tr[id="'+id+'"]'); + var $pv = $row.find('.cp-fileupload-table-progress-value'); + var $pb = $row.find('.cp-fileupload-table-progress-container'); + var $pc = $row.find('.cp-fileupload-table-progress'); + var $link = $row.find('.cp-fileupload-table-link'); + + var done = function () { + $row.find('.cp-fileupload-table-cancel').text('-'); + queue.inProgress = false; + queue.next(); + }; + + var updateDLProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + text += ' ('+ Messages.download_step1 +'...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; + var updateProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + text += progressValue === 1 ? '' : ' ('+ Messages.download_step2 +'...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; + + var dl = module.downloadPad(pData, function (err, obj) { + $link.prepend($('', {'class': 'fa fa-external-link'})) + .attr('href', '#') + .click(function (e) { + e.preventDefault(); + obj.download(); + }); + done(); + if (obj) { obj.download(); } + cb(err, obj); + }, { + common: common, + progress: updateDLProgress, + progress2: updateProgress, + }); + + var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { + dl.cancel(); + $cancel.remove(); + $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); + done(); + }); + $row.find('.cp-fileupload-table-cancel').html('').append($cancel); + }; + queue.push({ + dl: todo, + size: 0, + name: pData.title, + }); + }; + return File; }; @@ -565,5 +647,95 @@ define([ }; }; + + var getPad = function (common, data, cb) { + var sframeChan = common.getSframeChannel(); + sframeChan.query("Q_CRYPTGET", data, function (err, obj) { + if (err) { return void cb(err); } + if (obj.error) { return void cb(obj.error); } + cb(null, obj.data); + }, { timeout: 60000 }); + }; + + var transform = function (type, sjson, cb) { + console.error("SFCfile - transform"); + console.log('type', type); + console.log('sjson', sjson); + + var result = { + data: sjson, + ext: '.json', + }; + console.log('result', result); + var json; + try { + json = JSON.parse(sjson); + } catch (e) { + return void cb(result); + } + console.log('json', json); + var path = '/' + type + '/export.js'; + require([path], function (Exporter) { + Exporter.main(json, function (data) { + result.ext = Exporter.ext || ''; + result.data = data; + cb(result); + }); + }, function () { + cb(result); + }); + }; + + module.downloadPad = function (pData, cb, obj) { + console.error("SFC file - downloadPad"); + console.log(pData, pData); + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + + var parsed = Hash.parsePadUrl(pData.href || pData.roHref); + var hash = parsed.hash; + var name = pData.filename || pData.title; + var opts = { + password: pData.password + }; + console.log('parsed', parsed); + console.log('hash', hash); + console.log('name', name); + console.log('opts', opts); + obj.progress(0); + getPad(obj.common, { + hash: parsed.hash, + opts: opts + }, function (err, val) { + if (cancelled) { return; } + if (err) { return; } + if (!val) { return; } + console.log('val', val); + obj.progress(1); + + transform(parsed.type, val, function (res) { + console.error("transform callback"); + console.log('res', res); + if (cancelled) { return; } + if (!res.data) { return; } + obj.progress2(1); + var dl = function () { + saveAs(res.data, Util.fixFileName(name)); + }; + cb(null, { + metadata: res.metadata, + content: res.data, + download: dl + }); + console.log('DONE ---- ' + name); + }); + }); + return { + cancel: cancel + }; + }; + return module; }); diff --git a/www/drive/inner.js b/www/drive/inner.js index 0fdcd4491..4726d7633 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3555,11 +3555,14 @@ define([ refresh(); } else if ($(this).hasClass('cp-app-drive-context-download')) { + console.error('DOWNLOAD'); if (paths.length !== 1) { return; } var path = paths[0]; el = manager.find(path.path); console.log("paths", paths); console.log("el", el); + console.log('path', path); + console.log("APP", APP); // folder if (manager.isFolder(el)) { @@ -3586,6 +3589,12 @@ define([ // pad else { console.log("--isPad--"); + data = manager.getFileData(el); + console.log("data:", data); + APP.FM.downloadPad(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); } } } From 4b027e83ad56030206c49ce5c9befabcba870527 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 6 Aug 2019 13:48:55 +0200 Subject: [PATCH 08/30] Download folders in drive - test (no UI) --- www/common/make-backup.js | 12 +++++++++- www/drive/inner.js | 48 ++++++++++++++++++++++++++++++++++++++- www/drive/main.js | 25 ++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/www/common/make-backup.js b/www/common/make-backup.js index b86f1f3b2..38a47f308 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -21,16 +21,22 @@ define([ }; var transform = function (ctx, type, sjson, cb) { + console.error("backup - transform"); + console.log('type', type); + console.log('sjson', sjson); + var result = { data: sjson, ext: '.json', }; + console.log('result', result); var json; try { json = JSON.parse(sjson); } catch (e) { return void cb(result); } + console.log('json', json); var path = '/' + type + '/export.js'; require([path], function (Exporter) { Exporter.main(json, function (data) { @@ -46,6 +52,8 @@ define([ // Add a file to the zip. We have to cryptget&transform it if it's a pad // or fetch&decrypt it if it's a file. var addFile = function (ctx, zip, fData, existingNames) { + console.error('backup - addFile'); + console.log('fData', fData); if (!fData.href && !fData.roHref) { return void ctx.errors.push({ error: 'EINVAL', @@ -54,6 +62,7 @@ define([ } var parsed = Hash.parsePadUrl(fData.href || fData.roHref); + console.log('parsed', parsed); if (['pad', 'file'].indexOf(parsed.hashData.type) === -1) { return; } // waitFor is used to make sure all the pads and files are process before downloading the zip. @@ -189,6 +198,7 @@ define([ var ctx = { get: getPad, data: data.uo.drive, + folder: data.folder || ctx.data.root, sf: data.sf, zip: new JsZip(), errors: [], @@ -201,7 +211,7 @@ define([ nThen(function (waitFor) { ctx.waitFor = waitFor; var zipRoot = ctx.zip.folder('Root'); - makeFolder(ctx, ctx.data.root, zipRoot, ctx.data.filesData); + makeFolder(ctx, ctx.folder, zipRoot, ctx.data.filesData); progress('download', {}); }).nThen(function () { console.log(ctx.zip); diff --git a/www/drive/inner.js b/www/drive/inner.js index 4726d7633..a611a7829 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3467,6 +3467,41 @@ define([ }); }); }; + + + var downloadFolder = function (folderElement, folderName) { + console.warn("downloadFolder"); + var todo = function (data) { + var getPad = function (data, cb) { + sframeChan.query("Q_CRYPTGET", data, function (err, obj) { + if (err) { return void cb(err); } + if (obj.error) { return void cb(obj.error); } + cb(null, obj.data); + }, { timeout: 60000 }); + }; + + data.folder = folderElement; + folderName = Util.fixFileName(folderName) + '.zip'; + console.log("data", data); + console.log("folderName", folderName); + + Backup.create(data, getPad, function (blob, errors) { + console.log("blob", blob); + window.saveAs(blob, folderName); + console.error(errors); + }, function () {}); + }; + sframeChan.query("Q_SETTINGS_DRIVE_GET", "full", function (err, data) { + console.warn("sframeChan.query Q_SETTINGS_DRIVE_GET callback"); + console.log("err", err); + console.log("data", data); + if (err) { return void console.error(err); } + if (data.error) { return void console.error(data.error); } + todo(data); + }); + }; + + $contextMenu.on("click", "a", function(e) { e.stopPropagation(); var paths = $contextMenu.data('paths'); @@ -3563,16 +3598,27 @@ define([ console.log("el", el); console.log('path', path); console.log("APP", APP); - // folder if (manager.isFolder(el)) { // folder + var name, folderEl; if (!manager.isSharedFolder(el)) { console.log("--isFolder--"); + name = path.path[path.path.length - 1]; + console.log('name', name); + folderEl = el; + downloadFolder(folderEl, name); } // shared folder else { console.log("--isSharedFolder--"); + data = manager.getSharedFolderData(el); + name = data.title; + folderEl = manager.find(path.path.concat("root")); + console.log("folderEl", folderEl); + console.log("data:", data); + console.log('name', name); + downloadFolder(folderEl, name); } } // file diff --git a/www/drive/main.js b/www/drive/main.js index 907be7451..c4918cf9d 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -63,6 +63,31 @@ define([ Utils.LocalStore.clearThumbnail(); window.location.reload(); }); + sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) { + Cryptpad.getUserObject(function (obj) { + if (obj.error) { return void cb(obj); } + if (d === "full") { + // We want shared folders too + var result = { + uo: obj, + sf: {} + }; + if (!obj.drive || !obj.drive.sharedFolders) { return void cb(result); } + Utils.nThen(function (waitFor) { + Object.keys(obj.drive.sharedFolders).forEach(function (id) { + Cryptpad.getSharedFolder(id, waitFor(function (obj) { + result.sf[id] = obj; + })); + }); + }).nThen(function () { + cb(result); + }); + return; + } + // We want only the user object + cb(obj); + }); + }); sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) { Cryptpad.userObjectCommand(data, cb); }); From 095dfadbb0513078cf738dab71dfc948b987a572 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 9 Aug 2019 11:48:57 +0200 Subject: [PATCH 09/30] Revert some pending stuff to be able to merge to staging --- www/drive/inner.js | 43 +++++++++---------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 0fdcd4491..5fa82edfb 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -8,7 +8,6 @@ define([ '/common/common-interface.js', '/common/common-constants.js', '/common/common-feedback.js', - '/common/make-backup.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', '/common/common-realtime.js', @@ -33,7 +32,6 @@ define([ UI, Constants, Feedback, - Backup, nThen, SFCommon, CommonRealtime, @@ -1013,6 +1011,9 @@ define([ // We can only open parent in virtual categories hide.push('openparent'); } + if (!$element.is('.cp-border-color-file')) { + hide.push('download'); + } if ($element.is('.cp-app-drive-element-file')) { // No folder in files hide.push('color'); @@ -3556,38 +3557,12 @@ define([ } else if ($(this).hasClass('cp-app-drive-context-download')) { if (paths.length !== 1) { return; } - var path = paths[0]; - el = manager.find(path.path); - console.log("paths", paths); - console.log("el", el); - - // folder - if (manager.isFolder(el)) { - // folder - if (!manager.isSharedFolder(el)) { - console.log("--isFolder--"); - } - // shared folder - else { - console.log("--isSharedFolder--"); - } - } - // file - else if (manager.isFile(el)) { - // imported file - if (path.element.is(".cp-border-color-file")) { - console.log("--isFile--"); - data = manager.getFileData(el); - APP.FM.downloadFile(data, function (err, obj) { - console.log(err, obj); - console.log('DONE'); - }); - } - // pad - else { - console.log("--isPad--"); - } - } + el = manager.find(paths[0].path); + data = manager.getFileData(el); + APP.FM.downloadFile(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); } else if ($(this).hasClass('cp-app-drive-context-share')) { if (paths.length !== 1) { return; } From e1655ccd5cd70073e6fca2e85747afeb5e98d7fb Mon Sep 17 00:00:00 2001 From: ClemDee Date: Mon, 12 Aug 2019 15:40:26 +0200 Subject: [PATCH 10/30] Drive contextmenu handlers optimisation --- www/drive/inner.js | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index c7c48668a..94c84ad81 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3598,6 +3598,7 @@ define([ var paths = $contextMenu.data('paths'); var pathsList = []; var type = $contextMenu.attr('data-menu-type'); + var $this = $(this); var el, data; if (paths.length === 0) { @@ -3606,11 +3607,11 @@ define([ return; } - if ($(this).hasClass("cp-app-drive-context-rename")) { + if ($this.hasClass("cp-app-drive-context-rename")) { if (paths.length !== 1) { return; } displayRenameInput(paths[0].element, paths[0].path); } - else if ($(this).hasClass("cp-app-drive-context-color")) { + else if ($this.hasClass("cp-app-drive-context-color")) { var currentColor = getFolderColor(paths[0].path); pickFolderColor(paths[0].element, currentColor, function (color) { paths.forEach(function (p) { @@ -3619,24 +3620,24 @@ define([ refresh(); }); } - else if($(this).hasClass("cp-app-drive-context-delete")) { + else if($this.hasClass("cp-app-drive-context-delete")) { if (!APP.loggedIn) { return void deletePaths(paths); } paths.forEach(function (p) { pathsList.push(p.path); }); moveElements(pathsList, [TRASH], false, refresh); } - else if ($(this).hasClass('cp-app-drive-context-deleteowned')) { + else if ($this.hasClass('cp-app-drive-context-deleteowned')) { deleteOwnedPaths(paths); } - else if ($(this).hasClass('cp-app-drive-context-open')) { + else if ($this.hasClass('cp-app-drive-context-open')) { paths.forEach(function (p) { var $element = p.element; $element.click(); $element.dblclick(); }); } - else if ($(this).hasClass('cp-app-drive-context-openro')) { + else if ($this.hasClass('cp-app-drive-context-openro')) { paths.forEach(function (p) { var el = manager.find(p.path); if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) { @@ -3654,10 +3655,10 @@ define([ openFile(null, href); }); } - else if ($(this).hasClass('cp-app-drive-context-expandall') || - $(this).hasClass('cp-app-drive-context-collapseall')) { + else if ($this.hasClass('cp-app-drive-context-expandall') || + $this.hasClass('cp-app-drive-context-collapseall')) { if (paths.length !== 1) { return; } - var opened = $(this).hasClass('cp-app-drive-context-expandall'); + var opened = $this.hasClass('cp-app-drive-context-expandall'); var openRecursive = function (path) { setFolderOpened(path, opened); var folderContent = manager.find(path); @@ -3680,7 +3681,7 @@ define([ openRecursive(paths[0].path); refresh(); } - else if ($(this).hasClass('cp-app-drive-context-download')) { + else if ($this.hasClass('cp-app-drive-context-download')) { if (paths.length !== 1) { return; } el = manager.find(paths[0].path); if (!manager.isFile(el)) { return; } @@ -3690,7 +3691,7 @@ define([ console.log('DONE'); }); } - else if ($(this).hasClass('cp-app-drive-context-share')) { + else if ($this.hasClass('cp-app-drive-context-share')) { if (paths.length !== 1) { return; } el = manager.find(paths[0].path); var parsed, modal; @@ -3741,7 +3742,7 @@ define([ wide: Object.keys(friends).length !== 0 }); } - else if ($(this).hasClass('cp-app-drive-context-newfolder')) { + else if ($this.hasClass('cp-app-drive-context-newfolder')) { if (paths.length !== 1) { return; } var onFolderCreated = function (err, info) { if (err) { return void logError(err); } @@ -3754,21 +3755,21 @@ define([ } manager.addFolder(paths[0].path, null, onFolderCreated); } - else if ($(this).hasClass('cp-app-drive-context-newsharedfolder')) { + else if ($this.hasClass('cp-app-drive-context-newsharedfolder')) { if (paths.length !== 1) { return; } addSharedFolderModal(function (obj) { if (!obj) { return; } manager.addSharedFolder(paths[0].path, obj, refresh); }); } - else if ($(this).hasClass("cp-app-drive-context-newdoc")) { - var ntype = $(this).data('type') || 'pad'; + else if ($this.hasClass("cp-app-drive-context-newdoc")) { + var ntype = $this.data('type') || 'pad'; var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath; common.sessionStorage.put(Constants.newPadPathKey, path2, function () { common.openURL('/' + ntype + '/'); }); } - else if ($(this).hasClass("cp-app-drive-context-properties")) { + else if ($this.hasClass("cp-app-drive-context-properties")) { if (type === 'trash') { var pPath = paths[0].path; if (paths.length !== 1 || pPath.length !== 4) { return; } @@ -3788,7 +3789,7 @@ define([ UI.alert($prop[0], undefined, true); }); } - else if ($(this).hasClass("cp-app-drive-context-hashtag")) { + else if ($this.hasClass("cp-app-drive-context-hashtag")) { if (paths.length !== 1) { return; } el = manager.find(paths[0].path); data = manager.getFileData(el); @@ -3796,7 +3797,7 @@ define([ var href = data.href || data.roHref; common.updateTags(href); } - else if ($(this).hasClass("cp-app-drive-context-empty")) { + else if ($this.hasClass("cp-app-drive-context-empty")) { if (paths.length !== 1 || !paths[0].element || !manager.comparePath(paths[0].path, [TRASH])) { log(Messages.fm_forbidden); @@ -3807,13 +3808,13 @@ define([ manager.emptyTrash(refresh); }); } - else if ($(this).hasClass("cp-app-drive-context-remove")) { + else if ($this.hasClass("cp-app-drive-context-remove")) { return void deletePaths(paths); } - else if ($(this).hasClass("cp-app-drive-context-removesf")) { + else if ($this.hasClass("cp-app-drive-context-removesf")) { return void deletePaths(paths); } - else if ($(this).hasClass("cp-app-drive-context-restore")) { + else if ($this.hasClass("cp-app-drive-context-restore")) { if (paths.length !== 1) { return; } var restorePath = paths[0].path; var restoreName = paths[0].path[paths[0].path.length - 1]; @@ -3830,7 +3831,7 @@ define([ manager.restore(restorePath, refresh); }); } - else if ($(this).hasClass("cp-app-drive-context-openparent")) { + else if ($this.hasClass("cp-app-drive-context-openparent")) { if (paths.length !== 1) { return; } var parentPath = paths[0].path.slice(); if (manager.isInTrashRoot(parentPath)) { parentPath = [TRASH]; } From f55c8c6b0458b2eac483a832d23be2ba6a0e5ff2 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 13 Aug 2019 16:09:06 +0200 Subject: [PATCH 11/30] Open plain text files in code app && plain text files thumbnail --- www/common/common-constants.js | 1 + www/common/common-thumbnail.js | 30 ++++++++++++- www/common/common-ui-elements.js | 29 +++++++++++-- www/common/common-util.js | 29 +++++++------ www/common/cryptpad-common.js | 67 +++++++++++++++++++++++++++++- www/common/sframe-app-framework.js | 2 +- www/common/sframe-common-file.js | 2 +- www/common/sframe-common-outer.js | 31 +++++++++++++- www/drive/inner.js | 24 ++++++++--- 9 files changed, 190 insertions(+), 25 deletions(-) diff --git a/www/common/common-constants.js b/www/common/common-constants.js index 986e115e2..ac51dfbca 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -7,6 +7,7 @@ define(function () { fileHashKey: 'FS_hash', // sessionStorage newPadPathKey: "newPadPath", + newPadFileData: "newPadFileData", // Store displayNameKey: 'cryptpad.username', oldStorageKey: 'CryptPad_RECENTPADS', diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 34d44daf5..4b9d7e595 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -15,6 +15,7 @@ define([ }; var supportedTypes = [ + 'text/plain', 'image/png', 'image/jpeg', 'image/jpg', @@ -23,7 +24,11 @@ define([ 'application/pdf' ]; - Thumb.isSupportedType = function (type) { + Thumb.isSupportedType = function (file) { + var type = file.type; + if (Util.isPlainTextFile(file.type, file.name)) { + type = "text/plain"; + } return supportedTypes.some(function (t) { return type.indexOf(t) !== -1; }); @@ -164,6 +169,26 @@ define([ }); }); }; + Thumb.fromPlainTextBlob = function (blob, cb) { + var canvas = document.createElement("canvas"); + canvas.width = canvas.height = Thumb.dimension; + var reader = new FileReader(); + reader.addEventListener('loadend', function (e) { + var content = e.srcElement.result; + var lines = content.split("\n"); + var canvasContext = canvas.getContext("2d"); + var fontSize = 4; + canvas.height = (lines.length) * (fontSize + 1); + canvasContext.font = fontSize + 'px monospace'; + lines.forEach(function (text, i) { + + canvasContext.fillText(text, 5, i * (fontSize + 1)); + }); + var D = getResizedDimensions(canvas, "txt"); + Thumb.fromCanvas(canvas, D, cb); + }); + reader.readAsText(blob); + }; Thumb.fromBlob = function (blob, cb) { if (blob.type.indexOf('video/') !== -1) { return void Thumb.fromVideoBlob(blob, cb); @@ -171,6 +196,9 @@ define([ if (blob.type.indexOf('application/pdf') !== -1) { return void Thumb.fromPdfBlob(blob, cb); } + if (Util.isPlainTextFile(blob.type, blob.name)) { + return void Thumb.fromPlainTextBlob(blob, cb); + } Thumb.fromImageBlob(blob, cb); }; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index bc465cdfb..831f1bb71 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2274,7 +2274,10 @@ define([ if (!common.isLoggedIn()) { return void cb(); } var sframeChan = common.getSframeChannel(); var metadataMgr = common.getMetadataMgr(); + var privateData = metadataMgr.getPrivateData(); var type = metadataMgr.getMetadataLazy().type; + var fromFileData = privateData.fromFileData; + var $body = $('body'); var $creationContainer = $('
', { id: 'cp-creation-container' }).appendTo($body); @@ -2286,7 +2289,8 @@ define([ // Title //var colorClass = 'cp-icon-color-'+type; //$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle)); - $creation.append(h('h3.cp-creation-title', Messages['button_new'+type])); + var newPadH3Title = Messages['button_new' + type]; + $creation.append(h('h3.cp-creation-title', newPadH3Title)); //$creation.append(h('h2.cp-creation-title.'+colorClass, Messages.newButtonTitle)); // Deleted pad warning @@ -2296,7 +2300,7 @@ define([ )); } - var origin = common.getMetadataMgr().getPrivateData().origin; + var origin = privateData.origin; var createHelper = function (href, text) { var q = h('a.cp-creation-help.fa.fa-question-circle', { title: text, @@ -2453,7 +2457,26 @@ define([ }); if (i < TEMPLATES_DISPLAYED) { $(left).addClass('hidden'); } }; - redraw(0); + if (fromFileData) { + var todo = function (thumbnail) { + allData = [{ + name: fromFileData.title, + id: 0, + thumbnail: thumbnail, + icon: h('span.cptools.cptools-file'), + }]; + redraw(0); + }; + todo(); + sframeChan.query("Q_GET_FILE_THUMBNAIL", null, function (err, res) { + if (err || (res && res.error)) { return; } + todo(res.data); + }); + } + else { + redraw(0); + } + // Change template selection when Tab is pressed next = function (revert) { diff --git a/www/common/common-util.js b/www/common/common-util.js index 124a72344..171d37624 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -319,21 +319,26 @@ define([], function () { return window.innerHeight < 800 || window.innerWidth < 800; }; - Util.isPlainTextFile = function (metadata) { - if (!metadata || !metadata.href) { return; } - console.log("%c" + metadata.title + " : " + metadata.fileType, "color: #3a7"); - console.log(metadata); - var href = metadata.roHref || metadata.href; - // is it a file ? - if (!href || href.indexOf("/file/") === -1) { return false; } + // return an + Util.parseFilename = function (filename) { + if (!filename || !filename.trim()) { return {}; } + var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(filename) || []; + return { + name: parsedName[1], + ext: parsedName[2], + } + } + + // Tell if a file is plain text from its metadata={title, fileType} + Util.isPlainTextFile = function (type, name) { // does its type begins with "text/" - if (metadata.fileType.indexOf("text/") === 0) { return true; } + if (type && type.indexOf("text/") === 0) { return true; } // no type and no file extension -> let's guess it's plain text - var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.title) || []; - if (!metadata.fileType && !parsedName[2]) { return true; } + var parsedName = Util.parseFilename(name); + if (!type && name && !parsedName.ext) { return true; } // other exceptions - if (metadata.fileType === 'application/x-javascript') { return true; } - if (metadata.fileType === 'application/xml') { return true; } + if (type === 'application/x-javascript') { return true; } + if (type === 'application/xml') { return true; } return false; }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index fef46951c..f6527bf26 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -9,11 +9,12 @@ define([ '/common/outer/local-store.js', '/common/outer/worker-channel.js', '/common/outer/login-block.js', + '/file/file-crypto.js', '/customize/application_config.js', '/bower_components/nthen/index.js', ], function (Config, Messages, Util, Hash, - Messaging, Constants, Feedback, LocalStore, Channel, Block, + Messaging, Constants, Feedback, LocalStore, Channel, Block, FileCrypto, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -567,6 +568,64 @@ define([ }); }; + common.useFile = function (Crypt, cb, optsPut) { + var data = common.fromFileData; + var parsed = Hash.parsePadUrl(data.href); + var parsed2 = Hash.parsePadUrl(window.location.href); + var hash = parsed.hash; + var name = data.title; + var secret = Hash.getSecrets('file', hash, data.password); + var src = Hash.getBlobPathFromHex(secret.channel); + var key = secret.keys && secret.keys.cryptKey; + + var u8; + var res; + var mode; + var val; + Nthen(function(waitFor) { + Util.fetch(src, waitFor(function (err, _u8) { + if (err) { return void waitFor.abort(); } + u8 = _u8; + })); + }).nThen(function (waitFor) { + FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { + if (err || !_res.content) { return void waitFor.abort(); } + res = _res; + })); + }).nThen(function (waitFor) { + var ext = Util.parseFilename(data.title).ext; + if (!ext) { + mode = "text"; + return; + } + require(["/common/modes.js"], waitFor(function (Modes) { + var fileType = Modes.list.some(function (fType) { + if (fType.ext === ext) { + mode = fType.mode; + return true; + } + }); + })); + }).nThen(function (waitFor) { + var reader = new FileReader(); + reader.addEventListener('loadend', waitFor(function (e) { + val = { + content: e.srcElement.result, + highlightMode: mode, + metadata: { + defaultTitle: name, + title: name, + type: "code", + }, + }; + })); + reader.readAsText(res.content); + }).nThen(function () { + Crypt.put(parsed2.hash, JSON.stringify(val), cb, optsPut); + }); + + }; + // Forget button common.moveToTrash = function (cb, href) { href = href || window.location.href; @@ -1263,6 +1322,12 @@ define([ messenger: rdyCfg.messenger, // Boolean driveEvents: rdyCfg.driveEvents // Boolean }; + // if a pad is created from a file + if (sessionStorage[Constants.newPadFileData]) { + common.fromFileData = JSON.parse(sessionStorage[Constants.newPadFileData]); + delete sessionStorage[Constants.newPadFileData]; + } + if (sessionStorage[Constants.newPadPathKey]) { common.initialPath = sessionStorage[Constants.newPadPathKey]; delete sessionStorage[Constants.newPadPathKey]; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index f1068a6de..92311b44b 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -454,7 +454,7 @@ define([ return; } if (!mediaTagEmbedder) { console.log('mediaTagEmbedder missing'); return; } - if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; } + if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; } var privateDat = cpNfInner.metadataMgr.getPrivateData(); var origin = privateDat.fileHost || privateDat.origin; var src = data.src = origin + data.src; diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 099176afd..573f39357 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -367,7 +367,7 @@ define([ blobToArrayBuffer(file, function (e, buffer) { if (e) { console.error(e); } file_arraybuffer = buffer; - if (!Thumb.isSupportedType(file.type)) { return getName(); } + if (!Thumb.isSupportedType(file)) { return getName(); } // make a resized thumbnail from the image.. Thumb.fromBlob(file, function (e, thumb64) { if (e) { console.error(e); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index fba08279a..588c2bfad 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -317,6 +317,9 @@ define([ channel: secret.channel, enableSF: localStorage.CryptPad_SF === "1", // TODO to remove when enabled by default devMode: localStorage.CryptPad_dev === "1", + fromFileData: Cryptpad.fromFileData ? { + title: Cryptpad.fromFileData.title + } : undefined, }; if (window.CryptPad_newSharedFolder) { additionalPriv.newSharedFolder = window.CryptPad_newSharedFolder; @@ -357,6 +360,8 @@ define([ sframeChan.event("EV_NEW_VERSION"); }); + + // Put in the following function the RPC queries that should also work in filepicker var addCommonRpc = function (sframeChan) { sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) { @@ -808,6 +813,22 @@ define([ }); }); + sframeChan.on('Q_GET_FILE_THUMBNAIL', function (data, cb) { + if (!Cryptpad.fromFileData.href) { + return void cb({ + error: "EINVAL", + }); + } + var key = getKey(Cryptpad.fromFileData.href, Cryptpad.fromFileData.channel); + Utils.LocalStore.getThumbnail(key, function (e, data) { + if (data === "EMPTY") { data = null; } + cb({ + error: e, + data: data + }); + }); + }); + sframeChan.on('EV_GOTO_URL', function (url) { if (url) { window.location.href = url; @@ -1080,11 +1101,11 @@ define([ })); } }).nThen(function () { + var cryptputCfg = $.extend(true, {}, rtConfig, {password: password}); if (data.template) { // Pass rtConfig to useTemplate because Cryptput will create the file and // we need to have the owners and expiration time in the first line on the // server - var cryptputCfg = $.extend(true, {}, rtConfig, {password: password}); Cryptpad.useTemplate({ href: data.template }, Cryptget, function () { @@ -1093,6 +1114,14 @@ define([ }, cryptputCfg); return; } + // if we open a new code from a file + if (Cryptpad.fromFileData) { + Cryptpad.useFile(Cryptget, function () { + startRealtime(); + cb(); + }, cryptputCfg); + return; + } // Start realtime outside the iframe and callback startRealtime(rtConfig); cb(); diff --git a/www/drive/inner.js b/www/drive/inner.js index d3e06e39b..c6fdc6067 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -1025,10 +1025,15 @@ define([ hide.push('openro'); // Remove open 'view' mode } // if it's not a plain text file + var isPlainTextFile = false; var metadata = manager.getFileData(manager.find(path)); - if (!metadata || !Util.isPlainTextFile(metadata)) { - hide.push('openincode'); + if (metadata) { + var href = metadata.roHref || metadata.href; + if (href && Hash.parsePadUrl(href).type === "file") { + isPlainTextFile = Util.isPlainTextFile(metadata.fileType, metadata.title); + } } + if (!isPlainTextFile) { hide.push('openincode'); } } else if ($element.is('.cp-app-drive-element-sharedf')) { if (containsFolder) { // More than 1 folder selected: cannot create a new subfolder @@ -3539,11 +3544,20 @@ define([ } else if ($(this).hasClass('cp-app-drive-context-openincode')) { paths.forEach(function (p) { - console.info("p", p); var el = manager.find(p.path); var metadata = manager.getFileData(el); - console.log(el); - // open code from template + var simpleData = { + title: metadata.filename || metadata.title, + href: metadata.href, + password: metadata.password, + channel: metadata.channel, + }; + nThen(function (waitFor) { + common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(simpleData), waitFor()); + common.sessionStorage.put(Constants.newPadPathKey, currentPath, waitFor()); + }).nThen(function () { + common.openURL('/code/'); + }); }); } else if ($(this).hasClass('cp-app-drive-context-expandall') || From 4f84a928a9d98d67a90ee289a557bb52d05d355d Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 13 Aug 2019 18:05:03 +0200 Subject: [PATCH 12/30] Small fixes and improvements --- www/common/common-thumbnail.js | 1 + www/common/cryptpad-common.js | 11 ++++---- www/common/sframe-common-outer.js | 2 +- www/drive/inner.js | 42 +++++++++++++++---------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 4b9d7e595..89f381eea 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -25,6 +25,7 @@ define([ ]; Thumb.isSupportedType = function (file) { + if (!file) { return false; } var type = file.type; if (Util.isPlainTextFile(file.type, file.name)) { type = "text/plain"; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index f6527bf26..208396d34 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -9,12 +9,11 @@ define([ '/common/outer/local-store.js', '/common/outer/worker-channel.js', '/common/outer/login-block.js', - '/file/file-crypto.js', '/customize/application_config.js', '/bower_components/nthen/index.js', ], function (Config, Messages, Util, Hash, - Messaging, Constants, Feedback, LocalStore, Channel, Block, FileCrypto, + Messaging, Constants, Feedback, LocalStore, Channel, Block, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -588,9 +587,11 @@ define([ u8 = _u8; })); }).nThen(function (waitFor) { - FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { - if (err || !_res.content) { return void waitFor.abort(); } - res = _res; + require(["/file/file-crypto.js"], waitFor(function (FileCrypto) { + FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { + if (err || !_res.content) { return void waitFor.abort(); } + res = _res; + })); })); }).nThen(function (waitFor) { var ext = Util.parseFilename(data.title).ext; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 588c2bfad..a8283c9f3 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -814,7 +814,7 @@ define([ }); sframeChan.on('Q_GET_FILE_THUMBNAIL', function (data, cb) { - if (!Cryptpad.fromFileData.href) { + if (!Cryptpad.fromFileData || !Cryptpad.fromFileData.href) { return void cb({ error: "EINVAL", }); diff --git a/www/drive/inner.js b/www/drive/inner.js index c6fdc6067..baafae0f4 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -319,7 +319,7 @@ define([ h('li', h('a.cp-app-drive-context-openincode.dropdown-item', { 'tabindex': '-1', 'data-icon': faOpenInCode, - }, Messages.fc_openInCode || "Open in Code")), + }, Messages.fc_openInCode || "Open in Code")), // XXX $separator.clone()[0], h('li', h('a.cp-app-drive-context-expandall.dropdown-item', { 'tabindex': '-1', @@ -1014,6 +1014,7 @@ define([ } if (!$element.is('.cp-border-color-file')) { hide.push('download'); + hide.push('openincode'); } if ($element.is('.cp-app-drive-element-file')) { // No folder in files @@ -1027,13 +1028,9 @@ define([ // if it's not a plain text file var isPlainTextFile = false; var metadata = manager.getFileData(manager.find(path)); - if (metadata) { - var href = metadata.roHref || metadata.href; - if (href && Hash.parsePadUrl(href).type === "file") { - isPlainTextFile = Util.isPlainTextFile(metadata.fileType, metadata.title); - } + if (!metadata || !Util.isPlainTextFile(metadata.fileType, metadata.title)) { + hide.push('openincode'); } - if (!isPlainTextFile) { hide.push('openincode'); } } else if ($element.is('.cp-app-drive-element-sharedf')) { if (containsFolder) { // More than 1 folder selected: cannot create a new subfolder @@ -1090,6 +1087,7 @@ define([ hide.push('openparent'); hide.push('hashtag'); hide.push('download'); + hide.push('openincode'); // can't because of race condition } if (containsFolder && paths.length > 1) { // Cannot open multiple folders @@ -3543,21 +3541,21 @@ define([ }); } else if ($(this).hasClass('cp-app-drive-context-openincode')) { - paths.forEach(function (p) { - var el = manager.find(p.path); - var metadata = manager.getFileData(el); - var simpleData = { - title: metadata.filename || metadata.title, - href: metadata.href, - password: metadata.password, - channel: metadata.channel, - }; - nThen(function (waitFor) { - common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(simpleData), waitFor()); - common.sessionStorage.put(Constants.newPadPathKey, currentPath, waitFor()); - }).nThen(function () { - common.openURL('/code/'); - }); + if (paths.length !== 1) { return; } + var p = paths[0]; + var el = manager.find(p.path); + var metadata = manager.getFileData(el); + var simpleData = { + title: metadata.filename || metadata.title, + href: metadata.href, + password: metadata.password, + channel: metadata.channel, + }; + nThen(function (waitFor) { + common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(simpleData), waitFor()); + common.sessionStorage.put(Constants.newPadPathKey, currentPath, waitFor()); + }).nThen(function () { + common.openURL('/code/'); }); } else if ($(this).hasClass('cp-app-drive-context-expandall') || From a5405cb6a012ad668acd2e1a3e78809af1da8d3c Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 14 Aug 2019 10:30:32 +0200 Subject: [PATCH 13/30] Set properties at the bottom of the contextmenu --- www/drive/inner.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 5fa82edfb..b3c6a71f0 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -395,10 +395,6 @@ define([ 'tabindex': '-1', 'data-icon': faTags, }, Messages.fc_hashtag)), - h('li', h('a.cp-app-drive-context-properties.dropdown-item', { - 'tabindex': '-1', - 'data-icon': faProperties, - }, Messages.fc_prop)), $separator.clone()[0], h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', @@ -416,6 +412,11 @@ define([ 'tabindex': '-1', 'data-icon': faDelete, }, Messages.fc_remove_sharedfolder)), + $separator.clone()[0], + h('li', h('a.cp-app-drive-context-properties.dropdown-item', { + 'tabindex': '-1', + 'data-icon': faProperties, + }, Messages.fc_prop)), ]) ]); $(menu).find("li a.dropdown-item").each(function (i, el) { @@ -1077,6 +1078,7 @@ define([ hide.push('openparent'); hide.push('hashtag'); hide.push('download'); + hide.push('share'); } if (containsFolder && paths.length > 1) { // Cannot open multiple folders From eaeb995661da8e17c4f6eaf8970d787eb3b674b5 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Aug 2019 10:38:42 +0200 Subject: [PATCH 14/30] Remove debugging code --- www/common/outer/async-store.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 8564189fd..e793cbcb3 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1108,7 +1108,6 @@ define([ var channels = Store.channels = store.channels = {}; Store.joinPad = function (clientId, data) { - console.log('joining', data.channel); var isNew = typeof channels[data.channel] === "undefined"; var channel = channels[data.channel] = channels[data.channel] || { queue: [], From bb9898fa35c623c84501137f7bc82191c5f24a0e Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 14 Aug 2019 14:49:40 +0200 Subject: [PATCH 15/30] Download files code refractoring --- www/common/make-backup.js | 101 +++++++-- www/common/sframe-common-file.js | 340 ++++++------------------------- www/common/sframe-common.js | 8 + www/drive/inner.js | 25 +-- www/drive/main.js | 25 --- www/settings/inner.js | 11 +- 6 files changed, 168 insertions(+), 342 deletions(-) diff --git a/www/common/make-backup.js b/www/common/make-backup.js index 38a47f308..0d50dfde4 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -1,11 +1,13 @@ define([ '/common/cryptget.js', + '/file/file-crypto.js', '/common/common-hash.js', - '/common/sframe-common-file.js', + '/common/common-util.js', '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', '/bower_components/jszip/dist/jszip.min.js', -], function (Crypt, Hash, SFCFile, nThen, Saferphore, JsZip) { +], function (Crypt, FileCrypto, Hash, Util, nThen, Saferphore, JsZip) { + var saveAs = window.saveAs; var sanitize = function (str) { return str.replace(/[\\/?%*:|"<>]/gi, '_')/*.toLowerCase()*/; @@ -21,22 +23,16 @@ define([ }; var transform = function (ctx, type, sjson, cb) { - console.error("backup - transform"); - console.log('type', type); - console.log('sjson', sjson); - var result = { data: sjson, ext: '.json', }; - console.log('result', result); var json; try { json = JSON.parse(sjson); } catch (e) { return void cb(result); } - console.log('json', json); var path = '/' + type + '/export.js'; require([path], function (Exporter) { Exporter.main(json, function (data) { @@ -49,11 +45,86 @@ define([ }); }; + + var _downloadFile = function (ctx, fData, cb, updateProgress) { + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + var parsed = Hash.parsePadUrl(fData.href || fData.roHref); + var hash = parsed.hash; + var name = fData.filename || fData.title; + var secret = Hash.getSecrets('file', hash, fData.password); + var src = Hash.getBlobPathFromHex(secret.channel); + var key = secret.keys && secret.keys.cryptKey; + Util.fetch(src, function (err, u8) { + if (cancelled) { return; } + if (err) { return void cb('E404'); } + FileCrypto.decrypt(u8, key, function (err, res) { + if (cancelled) { return; } + if (err) { return void cb(err); } + if (!res.content) { return void cb('EEMPTY'); } + var dl = function () { + saveAs(res.content, name || res.metadata.name); + }; + cb(null, { + metadata: res.metadata, + content: res.content, + download: dl + }); + }, updateProgress && updateProgress.progress2); + }, updateProgress && updateProgress.progress); + return { + cancel: cancel + }; + + }; + + + var _downloadPad = function (ctx, pData, cb, updateProgress) { + var cancelled = false; + var cancel = function () { + cancelled = true; + }; + + var parsed = Hash.parsePadUrl(pData.href || pData.roHref); + var name = pData.filename || pData.title; + var opts = { + password: pData.password + }; + updateProgress.progress(0); + ctx.get({ + hash: parsed.hash, + opts: opts + }, function (err, val) { + if (cancelled) { return; } + if (err) { return; } + if (!val) { return; } + updateProgress.progress(1); + + transform(ctx, parsed.type, val, function (res) { + if (cancelled) { return; } + if (!res.data) { return; } + updateProgress.progress2(1); + var dl = function () { + saveAs(res.data, Util.fixFileName(name)); + }; + cb(null, { + metadata: res.metadata, + content: res.data, + download: dl + }); + }); + }); + return { + cancel: cancel + }; + + }; + // Add a file to the zip. We have to cryptget&transform it if it's a pad // or fetch&decrypt it if it's a file. var addFile = function (ctx, zip, fData, existingNames) { - console.error('backup - addFile'); - console.log('fData', fData); if (!fData.href && !fData.roHref) { return void ctx.errors.push({ error: 'EINVAL', @@ -62,7 +133,6 @@ define([ } var parsed = Hash.parsePadUrl(fData.href || fData.roHref); - console.log('parsed', parsed); if (['pad', 'file'].indexOf(parsed.hashData.type) === -1) { return; } // waitFor is used to make sure all the pads and files are process before downloading the zip. @@ -135,7 +205,7 @@ define([ // Files (mediatags...) var todoFile = function () { var it; - var dl = SFCFile.downloadFile(fData, function (err, res) { + var dl = _downloadFile(ctx, fData, function (err, res) { if (it) { clearInterval(it); } if (err) { return void error(err); } var opts = { @@ -232,7 +302,12 @@ define([ }; }; + + return { - create: create + create: create, + downloadFile: _downloadFile, + downloadPad: _downloadPad, + }; }); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index d374737ca..da26531c1 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -1,6 +1,7 @@ define([ 'jquery', '/file/file-crypto.js', + '/common/make-backup.js', '/common/common-thumbnail.js', '/common/common-interface.js', '/common/common-ui-elements.js', @@ -11,9 +12,8 @@ define([ '/bower_components/file-saver/FileSaver.min.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, FileCrypto, Thumb, UI, UIElements, Util, Hash, h, Messages) { +], function ($, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages) { var Nacl = window.nacl; - var saveAs = window.saveAs; var module = {}; var blobToArrayBuffer = function (blob, cb) { @@ -446,88 +446,73 @@ define([ createUploader(config.dropArea, config.hoverArea, config.body); + var updateProgressbar = function (file, data, downloadFunction, cb) { + if (queue.inProgress) { return; } + queue.inProgress = true; + var id = file.id; + + var $row = $table.find('tr[id="'+id+'"]'); + var $pv = $row.find('.cp-fileupload-table-progress-value'); + var $pb = $row.find('.cp-fileupload-table-progress-container'); + var $pc = $row.find('.cp-fileupload-table-progress'); + var $link = $row.find('.cp-fileupload-table-link'); + + var done = function () { + $row.find('.cp-fileupload-table-cancel').text('-'); + queue.inProgress = false; + queue.next(); + }; + + var updateDLProgress = function (progressValue) { + var text = Math.round(progressValue * 100) + '%'; + text += ' (' + Messages.download_step1 + '...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width() + 'px' + }); + }; + var updateProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + text += progressValue === 1 ? '' : ' (' + Messages.download_step2 + '...)'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; + + var ctx = { + get: common.getPad, + }; + var dl = downloadFunction(ctx, data, function (err, obj) { + $link.prepend($('', {'class': 'fa fa-external-link'})) + .attr('href', '#') + .click(function (e) { + e.preventDefault(); + obj.download(); + }); + done(); + if (obj) { obj.download(); } + cb(err, obj); + }, { + progress: updateDLProgress, + progress2: updateProgress, + }); + + var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { + dl.cancel(); + $cancel.remove(); + $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); + done(); + }); + $row.find('.cp-fileupload-table-cancel').html('').append($cancel); + }; + + File.downloadFile = function (fData, cb) { - console.error("SFC FILE -DOWNLOAD FILE"); - console.log("fData", fData); - var parsed = Hash.parsePadUrl(fData.href || fData.roHref); - var hash = parsed.hash; var name = fData.filename || fData.title; - var secret = Hash.getSecrets('file', hash, fData.password); - var src = Hash.getBlobPathFromHex(secret.channel); - var key = secret.keys && secret.keys.cryptKey; - console.log('key', key); - console.log('src', src); - console.log('secret', secret); - console.log('name', name); - console.log('hash', hash); - console.log('parsed', parsed); - common.getFileSize(secret.channel, function (e, data) { - console.warn("GET FILE SIZE"); - console.log("data", data); - var todo = function (file) { - console.warn("TODO"); - console.log("file", file); - if (queue.inProgress) { return; } - queue.inProgress = true; - var id = file.id; - - var $row = $table.find('tr[id="'+id+'"]'); - var $pv = $row.find('.cp-fileupload-table-progress-value'); - var $pb = $row.find('.cp-fileupload-table-progress-container'); - var $pc = $row.find('.cp-fileupload-table-progress'); - var $link = $row.find('.cp-fileupload-table-link'); - - var done = function () { - $row.find('.cp-fileupload-table-cancel').text('-'); - queue.inProgress = false; - queue.next(); - }; - - var updateDLProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += ' ('+ Messages.download_step1 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; - var updateProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += progressValue === 1 ? '' : ' ('+ Messages.download_step2 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; - - var dl = module.downloadFile(fData, function (err, obj) { - $link.prepend($('', {'class': 'fa fa-external-link'})) - .attr('href', '#') - .click(function (e) { - e.preventDefault(); - obj.download(); - }); - done(); - if (obj) { obj.download(); } - cb(err, obj); - }, { - src: src, - key: key, - name: name, - progress: updateDLProgress, - progress2: updateProgress, - }); - - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - done(); - }); - $row.find('.cp-fileupload-table-cancel').html('').append($cancel); - }; + common.getFileSize(fData.channel, function (e, data) { queue.push({ - dl: todo, + dl: function (file) { updateProgressbar(file, fData, MakeBackup.downloadFile, cb); }, size: data, name: name }); @@ -535,70 +520,8 @@ define([ }; File.downloadPad = function (pData, cb) { - console.error("SFC FILE -DOWNLOAD PAD"); - console.log("pData", pData); - var todo = function (file) { - console.warn("TODO"); - console.log("file", file); - if (queue.inProgress) { return; } - queue.inProgress = true; - var id = file.id; - - var $row = $table.find('tr[id="'+id+'"]'); - var $pv = $row.find('.cp-fileupload-table-progress-value'); - var $pb = $row.find('.cp-fileupload-table-progress-container'); - var $pc = $row.find('.cp-fileupload-table-progress'); - var $link = $row.find('.cp-fileupload-table-link'); - - var done = function () { - $row.find('.cp-fileupload-table-cancel').text('-'); - queue.inProgress = false; - queue.next(); - }; - - var updateDLProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += ' ('+ Messages.download_step1 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; - var updateProgress = function (progressValue) { - var text = Math.round(progressValue*100) + '%'; - text += progressValue === 1 ? '' : ' ('+ Messages.download_step2 +'...)'; - $pv.text(text); - $pb.css({ - width: progressValue * $pc.width()+'px' - }); - }; - - var dl = module.downloadPad(pData, function (err, obj) { - $link.prepend($('', {'class': 'fa fa-external-link'})) - .attr('href', '#') - .click(function (e) { - e.preventDefault(); - obj.download(); - }); - done(); - if (obj) { obj.download(); } - cb(err, obj); - }, { - common: common, - progress: updateDLProgress, - progress2: updateProgress, - }); - - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - done(); - }); - $row.find('.cp-fileupload-table-cancel').html('').append($cancel); - }; queue.push({ - dl: todo, + dl: function (file) { updateProgressbar(file, pData, MakeBackup.downloadPad, cb); }, size: 0, name: pData.title, }); @@ -607,135 +530,6 @@ define([ return File; }; - module.downloadFile = function (fData, cb, obj) { - var cancelled = false; - var cancel = function () { - cancelled = true; - }; - var src, key, name; - if (obj && obj.src && obj.key && obj.name) { - src = obj.src; - key = obj.key; - name = obj.name; - } else { - var parsed = Hash.parsePadUrl(fData.href || fData.roHref); - var hash = parsed.hash; - name = fData.filename || fData.title; - var secret = Hash.getSecrets('file', hash, fData.password); - src = Hash.getBlobPathFromHex(secret.channel); - key = secret.keys && secret.keys.cryptKey; - } - Util.fetch(src, function (err, u8) { - if (cancelled) { return; } - if (err) { return void cb('E404'); } - FileCrypto.decrypt(u8, key, function (err, res) { - if (cancelled) { return; } - if (err) { return void cb(err); } - if (!res.content) { return void cb('EEMPTY'); } - var dl = function () { - saveAs(res.content, name || res.metadata.name); - }; - cb(null, { - metadata: res.metadata, - content: res.content, - download: dl - }); - }, obj && obj.progress2); - }, obj && obj.progress); - return { - cancel: cancel - }; - }; - - - var getPad = function (common, data, cb) { - var sframeChan = common.getSframeChannel(); - sframeChan.query("Q_CRYPTGET", data, function (err, obj) { - if (err) { return void cb(err); } - if (obj.error) { return void cb(obj.error); } - cb(null, obj.data); - }, { timeout: 60000 }); - }; - - var transform = function (type, sjson, cb) { - console.error("SFCfile - transform"); - console.log('type', type); - console.log('sjson', sjson); - - var result = { - data: sjson, - ext: '.json', - }; - console.log('result', result); - var json; - try { - json = JSON.parse(sjson); - } catch (e) { - return void cb(result); - } - console.log('json', json); - var path = '/' + type + '/export.js'; - require([path], function (Exporter) { - Exporter.main(json, function (data) { - result.ext = Exporter.ext || ''; - result.data = data; - cb(result); - }); - }, function () { - cb(result); - }); - }; - - module.downloadPad = function (pData, cb, obj) { - console.error("SFC file - downloadPad"); - console.log(pData, pData); - var cancelled = false; - var cancel = function () { - cancelled = true; - }; - - var parsed = Hash.parsePadUrl(pData.href || pData.roHref); - var hash = parsed.hash; - var name = pData.filename || pData.title; - var opts = { - password: pData.password - }; - console.log('parsed', parsed); - console.log('hash', hash); - console.log('name', name); - console.log('opts', opts); - obj.progress(0); - getPad(obj.common, { - hash: parsed.hash, - opts: opts - }, function (err, val) { - if (cancelled) { return; } - if (err) { return; } - if (!val) { return; } - console.log('val', val); - obj.progress(1); - - transform(parsed.type, val, function (res) { - console.error("transform callback"); - console.log('res', res); - if (cancelled) { return; } - if (!res.data) { return; } - obj.progress2(1); - var dl = function () { - saveAs(res.data, Util.fixFileName(name)); - }; - cb(null, { - metadata: res.metadata, - content: res.data, - download: dl - }); - console.log('DONE ---- ' + name); - }); - }); - return { - cancel: cancel - }; - }; return module; }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index f93021bd3..82c71ace2 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -459,6 +459,14 @@ define([ }); }; */ + funcs.getPad = function (data, cb) { + ctx.sframeChan.query("Q_CRYPTGET", data, function (err, obj) { + if (err) { return void cb(err); } + if (obj.error) { return void cb(obj.error); } + cb(null, obj.data); + }, { timeout: 60000 }); + }; + funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.openUnsafeURL = function (url) { diff --git a/www/drive/inner.js b/www/drive/inner.js index a611a7829..b6e480a9d 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3472,32 +3472,20 @@ define([ var downloadFolder = function (folderElement, folderName) { console.warn("downloadFolder"); var todo = function (data) { - var getPad = function (data, cb) { - sframeChan.query("Q_CRYPTGET", data, function (err, obj) { - if (err) { return void cb(err); } - if (obj.error) { return void cb(obj.error); } - cb(null, obj.data); - }, { timeout: 60000 }); - }; - data.folder = folderElement; folderName = Util.fixFileName(folderName) + '.zip'; console.log("data", data); console.log("folderName", folderName); - Backup.create(data, getPad, function (blob, errors) { + Backup.create(data, common.getPad, function (blob, errors) { console.log("blob", blob); window.saveAs(blob, folderName); console.error(errors); }, function () {}); }; - sframeChan.query("Q_SETTINGS_DRIVE_GET", "full", function (err, data) { - console.warn("sframeChan.query Q_SETTINGS_DRIVE_GET callback"); - console.log("err", err); - console.log("data", data); - if (err) { return void console.error(err); } - if (data.error) { return void console.error(data.error); } - todo(data); + todo({ + uo: proxy, + sf: folders, }); }; @@ -3594,10 +3582,8 @@ define([ if (paths.length !== 1) { return; } var path = paths[0]; el = manager.find(path.path); - console.log("paths", paths); console.log("el", el); console.log('path', path); - console.log("APP", APP); // folder if (manager.isFolder(el)) { // folder @@ -3625,7 +3611,6 @@ define([ else if (manager.isFile(el)) { // imported file if (path.element.is(".cp-border-color-file")) { - console.log("--isFile--"); data = manager.getFileData(el); APP.FM.downloadFile(data, function (err, obj) { console.log(err, obj); @@ -3634,9 +3619,7 @@ define([ } // pad else { - console.log("--isPad--"); data = manager.getFileData(el); - console.log("data:", data); APP.FM.downloadPad(data, function (err, obj) { console.log(err, obj); console.log('DONE'); diff --git a/www/drive/main.js b/www/drive/main.js index c4918cf9d..907be7451 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -63,31 +63,6 @@ define([ Utils.LocalStore.clearThumbnail(); window.location.reload(); }); - sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) { - Cryptpad.getUserObject(function (obj) { - if (obj.error) { return void cb(obj); } - if (d === "full") { - // We want shared folders too - var result = { - uo: obj, - sf: {} - }; - if (!obj.drive || !obj.drive.sharedFolders) { return void cb(result); } - Utils.nThen(function (waitFor) { - Object.keys(obj.drive.sharedFolders).forEach(function (id) { - Cryptpad.getSharedFolder(id, waitFor(function (obj) { - result.sf[id] = obj; - })); - }); - }).nThen(function () { - cb(result); - }); - return; - } - // We want only the user object - cb(obj); - }); - }); sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) { Cryptpad.userObjectCommand(data, cb); }); diff --git a/www/settings/inner.js b/www/settings/inner.js index 37143a617..a7a006b23 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -1082,18 +1082,9 @@ define([ var exportDrive = function () { Feedback.send('FULL_DRIVE_EXPORT_START'); var todo = function (data, filename) { - var getPad = function (data, cb) { - sframeChan.query("Q_CRYPTGET", data, function (err, obj) { - if (err) { return void cb(err); } - if (obj.error) { return void cb(obj.error); } - cb(null, obj.data); - }, { timeout: 60000 }); - }; - var ui = createExportUI(); - var bu = Backup.create(data, getPad, function (blob, errors) { - console.log(blob); + var bu = Backup.create(data, common.getPad, function (blob, errors) { saveAs(blob, filename); sframeChan.event('EV_CRYPTGET_DISCONNECT'); ui.complete(function () { From 3e56a88173aed5f6c82d8a6bda3e4fc58e4efade Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Aug 2019 14:57:14 +0200 Subject: [PATCH 16/30] Add progress data when using cryptget --- www/common/cryptget.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/www/common/cryptget.js b/www/common/cryptget.js index d08b4b297..6d18b2083 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -52,11 +52,12 @@ define([ Object.keys(b).forEach(function (k) { a[k] = b[k]; }); }; - var get = function (hash, cb, opt) { + var get = function (hash, cb, opt, progress) { if (typeof(cb) !== 'function') { throw new Error('Cryptget expects a callback'); } opt = opt || {}; + progress = progress || function () {}; var config = makeConfig(hash, opt); var Session = { cb: cb, hasNetwork: Boolean(opt.network) }; @@ -65,6 +66,7 @@ define([ var rt = Session.session = info.realtime; Session.network = info.network; Session.leave = info.leave; + progress(1); finish(Session, void 0, rt.getUserDoc()); }; @@ -72,6 +74,16 @@ define([ finish(Session, info.error); }; + // We use the new onMessage handler to compute the progress: + // we should receive 2 checkpoints max, so 100 messages max + // We're going to consider that 1 message = 1%, and we'll send 100% + // at the end + var i = 0; + config.onMessage = function () { + i++; + progress(Math.min(0.99, i/100)); + }; + overwrite(config, opt); Session.realtime = CPNetflux.start(config); From 9c26df42130a80a7706dd5651fd1556e48f593a4 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Aug 2019 15:12:44 +0200 Subject: [PATCH 17/30] Add comments --- www/common/sframe-common-outer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 0103cdbaf..153126f7c 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -864,6 +864,9 @@ define([ Cryptpad.removeLoginBlock(data, cb); }); + // It seems we have performance issues when we open and close a lot of channels over + // the same network, maybe a memory leak. To fix this, we kill and create a new + // network every 30 cryptget calls (1 call = 1 channel) var cgNetwork; var whenCGReady = function (cb) { if (cgNetwork && cgNetwork !== true) { console.log(cgNetwork); return void cb(); } From f017215300733b23fd19288e6e77f914c42c3144 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 14 Aug 2019 15:42:32 +0200 Subject: [PATCH 18/30] Add progress bar for pad download --- www/common/make-backup.js | 13 +++++++++---- www/common/sframe-common-file.js | 1 + www/common/sframe-common-outer.js | 7 ++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/www/common/make-backup.js b/www/common/make-backup.js index 0d50dfde4..3e9426be5 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -92,7 +92,15 @@ define([ var opts = { password: pData.password }; - updateProgress.progress(0); + var done = false; + ctx.sframeChan.on("EV_CRYPTGET_PROGRESS", function (data) { + if (done || data.hash !== parsed.hash) { return; } + updateProgress.progress(data.progress); + if (data.progress === 1) { + done = true; + updateProgress.progress2(1); + } + }); ctx.get({ hash: parsed.hash, opts: opts @@ -100,12 +108,9 @@ define([ if (cancelled) { return; } if (err) { return; } if (!val) { return; } - updateProgress.progress(1); - transform(ctx, parsed.type, val, function (res) { if (cancelled) { return; } if (!res.data) { return; } - updateProgress.progress2(1); var dl = function () { saveAs(res.data, Util.fixFileName(name)); }; diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index da26531c1..0836a8553 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -482,6 +482,7 @@ define([ var ctx = { get: common.getPad, + sframeChan: sframeChan, }; var dl = downloadFunction(ctx, data, function (err, obj) { $link.prepend($('', {'class': 'fa fa-external-link'})) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 0103cdbaf..44fcde159 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -880,7 +880,12 @@ define([ error: err, data: val }); - }, data.opts); + }, data.opts, function (progress) { + sframeChan.event("EV_CRYPTGET_PROGRESS", { + hash: data.hash, + progress: progress, + }); + }); }; //return void todo(); if (i > 30) { From a327f4a0bf9693b52c1356c307532954c682972e Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 14 Aug 2019 15:59:16 +0200 Subject: [PATCH 19/30] Fix cryptget memory issues --- www/common/cryptget.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 6d18b2083..9ca05164c 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -21,6 +21,11 @@ define([ S.leave(); } catch (e) { console.log(e); } } + if (S.session && S.session.stop) { + try { + S.session.stop(); + } catch (e) { console.error(e); } + } var abort = Util.find(S, ['session', 'realtime', 'abort']); if (typeof(abort) === 'function') { S.session.realtime.sync(); From a0d85c6e34e69b03b67daafb667295ba3e6e8562 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 14 Aug 2019 17:27:37 +0200 Subject: [PATCH 20/30] Export folder is now working in drive --- www/common/make-backup.js | 26 ++++++++++++++++++++-- www/common/sframe-common-file.js | 37 +++++++++++++++++++++++--------- www/drive/inner.js | 27 +++++++---------------- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/www/common/make-backup.js b/www/common/make-backup.js index 3e9426be5..d9fb62d96 100644 --- a/www/common/make-backup.js +++ b/www/common/make-backup.js @@ -282,11 +282,12 @@ define([ max: 0, done: 0 }; + var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData; progress('reading', -1); nThen(function (waitFor) { ctx.waitFor = waitFor; var zipRoot = ctx.zip.folder('Root'); - makeFolder(ctx, ctx.folder, zipRoot, ctx.data.filesData); + makeFolder(ctx, ctx.folder, zipRoot, filesData); progress('download', {}); }).nThen(function () { console.log(ctx.zip); @@ -308,11 +309,32 @@ define([ }; + var _downloadFolder = function (ctx, data, cb, updateProgress) { + create(data, ctx.get, function (blob, errors) { + console.error(errors); // TODO show user errors + var dl = function () { + saveAs(blob, data.folderName); + }; + cb(null, {download: dl}); + }, function (state, progress) { + if (state === "reading") { + updateProgress.folderProgress(0); + } + if (state === "download") { + if (typeof progress.current !== "number") { return; } + updateProgress.folderProgress(progress.current / progress.max); + } + else if (state === "done") { + updateProgress.folderProgress(1); + } + }); + }; + return { create: create, downloadFile: _downloadFile, downloadPad: _downloadPad, - + downloadFolder: _downloadFolder, }; }); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 0836a8553..32f031010 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -471,7 +471,7 @@ define([ width: progressValue * $pc.width() + 'px' }); }; - var updateProgress = function (progressValue) { + var updateDecryptProgress = function (progressValue) { var text = Math.round(progressValue*100) + '%'; text += progressValue === 1 ? '' : ' (' + Messages.download_step2 + '...)'; $pv.text(text); @@ -479,12 +479,19 @@ define([ width: progressValue * $pc.width()+'px' }); }; + var updateProgress = function (progressValue) { + var text = Math.round(progressValue*100) + '%'; + $pv.text(text); + $pb.css({ + width: progressValue * $pc.width()+'px' + }); + }; var ctx = { get: common.getPad, sframeChan: sframeChan, }; - var dl = downloadFunction(ctx, data, function (err, obj) { + downloadFunction(ctx, data, function (err, obj) { $link.prepend($('', {'class': 'fa fa-external-link'})) .attr('href', '#') .click(function (e) { @@ -496,16 +503,18 @@ define([ cb(err, obj); }, { progress: updateDLProgress, - progress2: updateProgress, + progress2: updateDecryptProgress, + folderProgress: updateProgress, }); - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - done(); - }); - $row.find('.cp-fileupload-table-cancel').html('').append($cancel); +// var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { +// dl.cancel(); +// $cancel.remove(); +// $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); +// done(); +// }); +// $row.find('.cp-fileupload-table-cancel').html('').append($cancel); + $row.find('.cp-fileupload-table-cancel').html(''); }; @@ -528,6 +537,14 @@ define([ }); }; + File.downloadFolder = function (data, cb) { + queue.push({ + dl: function (file) { updateProgressbar(file, data, MakeBackup.downloadFolder, cb); }, + size: 0, + name: data.folderName, + }); + }; + return File; }; diff --git a/www/drive/inner.js b/www/drive/inner.js index 1745fa2f7..c75a9a1a6 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3597,19 +3597,16 @@ define([ }; - var downloadFolder = function (folderElement, folderName) { - console.warn("downloadFolder"); + var downloadFolder = function (folderElement, folderName, sfId) { var todo = function (data) { data.folder = folderElement; - folderName = Util.fixFileName(folderName) + '.zip'; - console.log("data", data); - console.log("folderName", folderName); + data.sharedFolderId = sfId; + data.folderName = Util.fixFileName(folderName) + '.zip'; - Backup.create(data, common.getPad, function (blob, errors) { - console.log("blob", blob); - window.saveAs(blob, folderName); - console.error(errors); - }, function () {}); + APP.FM.downloadFolder(data, function (err, obj) { + console.log(err, obj); + console.log('DONE'); + }); }; todo({ uo: proxy, @@ -3711,29 +3708,21 @@ define([ if (paths.length !== 1) { return; } var path = paths[0]; el = manager.find(path.path); - console.log("el", el); - console.log('path', path); // folder if (manager.isFolder(el)) { // folder var name, folderEl; if (!manager.isSharedFolder(el)) { - console.log("--isFolder--"); name = path.path[path.path.length - 1]; - console.log('name', name); folderEl = el; downloadFolder(folderEl, name); } // shared folder else { - console.log("--isSharedFolder--"); data = manager.getSharedFolderData(el); name = data.title; folderEl = manager.find(path.path.concat("root")); - console.log("folderEl", folderEl); - console.log("data:", data); - console.log('name', name); - downloadFolder(folderEl, name); + downloadFolder(folderEl, name, el); } } // file From 792ce4133b89db06882bca23e123f9a7bf0bc5fb Mon Sep 17 00:00:00 2001 From: ClemDee Date: Wed, 14 Aug 2019 17:54:01 +0200 Subject: [PATCH 21/30] Fix a little thing that disappeared with the merge --- www/drive/inner.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 456b3588d..e0c0167de 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -1095,9 +1095,6 @@ define([ // We can only open parent in virtual categories hide.push('openparent'); } - if (!$element.is('.cp-border-color-file')) { - hide.push('download'); - } if ($element.is('.cp-app-drive-element-file')) { // No folder in files hide.push('color'); From ba24cde76dd6e4042486c0878b87a730702d4efb Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 16 Aug 2019 10:23:58 +0200 Subject: [PATCH 22/30] Fix cryptget not stopping chainpad-netflux when done --- www/common/cryptget.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 9ca05164c..1fc248a19 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -21,9 +21,9 @@ define([ S.leave(); } catch (e) { console.log(e); } } - if (S.session && S.session.stop) { + if (S.realtime && S.realtime.stop) { try { - S.session.stop(); + S.realtime.stop(); } catch (e) { console.error(e); } } var abort = Util.find(S, ['session', 'realtime', 'abort']); From c4a90a90f209d4ba2cce5c9281efd09cd7ad959a Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 16 Aug 2019 11:33:57 +0200 Subject: [PATCH 23/30] Fix disconnection event not propagating to the UI --- www/common/outer/async-store.js | 9 +++++---- www/common/toolbar3.js | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index e793cbcb3..6b7942d13 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1188,9 +1188,6 @@ define([ onLeave: function (m) { channel.bcast("PAD_LEAVE", m); }, - onAbort: function () { - channel.bcast("PAD_DISCONNECT"); - }, onError: function (err) { channel.bcast("PAD_ERROR", err); delete channels[data.channel]; @@ -1199,7 +1196,11 @@ define([ channel.bcast("PAD_ERROR", err); delete channels[data.channel]; }, - onConnectionChange: function () {}, + onConnectionChange: function (info) { + if (!info.state) { + channel.bcast("PAD_DISCONNECT"); + } + }, crypto: { // The encryption and decryption is done in the outer window. // This async-store only deals with already encrypted messages. diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 6c262e2d1..a2547d223 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -575,7 +575,6 @@ MessengerUI, Messages) { }; var createRequest = function (toolbar, config) { - console.error('test'); if (!config.metadataMgr) { throw new Error("You must provide a `metadataMgr` to display the request access button"); } From 79b667c8352e0babbf5cd1cc377d13b50ab87085 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 16 Aug 2019 13:51:16 +0200 Subject: [PATCH 24/30] Remove translation fallback --- www/drive/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index baafae0f4..f1bf3e0be 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -319,7 +319,7 @@ define([ h('li', h('a.cp-app-drive-context-openincode.dropdown-item', { 'tabindex': '-1', 'data-icon': faOpenInCode, - }, Messages.fc_openInCode || "Open in Code")), // XXX + }, Messages.fc_openInCode)), $separator.clone()[0], h('li', h('a.cp-app-drive-context-expandall.dropdown-item', { 'tabindex': '-1', From a00ac64e0ea05c88eef9c5f75eb973052719b6a1 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 16 Aug 2019 13:51:55 +0200 Subject: [PATCH 25/30] Remove translation fallback --- www/drive/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 94c84ad81..5dfdd80e8 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -401,7 +401,7 @@ define([ h('a.cp-app-drive-context-newdocmenu.dropdown-item', { 'tabindex': '-1', 'data-icon': "fa-plus", - }, Messages.fm_morePads || "More pads"), //XXX + }, Messages.fm_morePads), h("ul.dropdown-menu", [ h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', From 3511f3b8021e076479f03bc6036737a8412dcf6e Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 16 Aug 2019 13:37:15 +0000 Subject: [PATCH 26/30] Translated using Weblate (French) Currently translated at 100.0% (1030 of 1030 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/fr/ --- www/common/translations/messages.fr.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.fr.json b/www/common/translations/messages.fr.json index 8d2edb610..958acaeee 100644 --- a/www/common/translations/messages.fr.json +++ b/www/common/translations/messages.fr.json @@ -1111,5 +1111,18 @@ "admin_supportInitHint": "Vous pouvez configurer une messagerie de support afin de fournir aux utilisateurs de votre instance CryptPad un moyen de vous contacter de manière sécurisée en cas de problème avec leur compte.", "admin_supportListHint": "Voici la liste des tickets envoyés par les utilisateurs au support. Tous les administrateurs peuvent voir les tickets et leurs réponses. Un ticket fermé ne peut pas être ré-ouvert. Vous ne pouvez supprimer (ou cacher) que les tickets fermés, et les tickets supprimés restent visible par les autres administrateurs.", "support_formHint": "Ce formulaire peut être utilisé pour créer un nouveau ticket de support. Utilisez-le pour contacter les administrateurs de manière sécurisée afin de résoudre un problème ou d'obtenir des renseignements. Merci de ne pas créer de nouveau ticket si vous avez déjà un ticket ouvert concernant le même problème, vous pouvez utiliser le bouton \"Répondre\" dans ce cas.", - "support_listHint": "Voici la liste des tickets envoyés au support, ainsi que les réponses. Un ticket fermé ne peut pas être ré-ouvert, mais il est possible d'en créer un nouveau. Vous pouvez cacher les tickets qui ont été fermés." + "support_listHint": "Voici la liste des tickets envoyés au support, ainsi que les réponses. Un ticket fermé ne peut pas être ré-ouvert, mais il est possible d'en créer un nouveau. Vous pouvez cacher les tickets qui ont été fermés.", + "support_notification": "Un administrateur a répondu à votre ticket de support", + "requestEdit_button": "Demander les droits d'édition", + "requestEdit_dialog": "Êtes-vous sûr de vouloir demander les droits d'édition de ce pad au propriétaire ?", + "requestEdit_confirm": "{1} a demandé les droits d'édition pour le pad {1}. Souhaitez-vous leur accorder les droits ?", + "requestEdit_fromFriend": "Vous êtes amis avec {0}", + "requestEdit_fromStranger": "Vous n'êtes pas amis avec {0}", + "requestEdit_viewPad": "Ouvrir le pad dans un nouvel onglet", + "later": "Décider plus tard", + "requestEdit_request": "{1} souhaite éditer le pad {0}", + "requestEdit_accepted": "{1} vous a accordé les droits d'édition du pad {0}", + "requestEdit_sent": "Demande envoyée", + "uploadFolderButton": "Importer un dossier", + "properties_unknownUser": "{0} utilisateur(s) inconnu(s)" } From 242677300b8a32ad17fc98eeb44c55cd2c91cf49 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 16 Aug 2019 13:37:15 +0000 Subject: [PATCH 27/30] Translated using Weblate (German) Currently translated at 100.0% (1030 of 1030 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/de/ --- www/common/translations/messages.de.json | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/www/common/translations/messages.de.json b/www/common/translations/messages.de.json index 30afbad07..208363e60 100644 --- a/www/common/translations/messages.de.json +++ b/www/common/translations/messages.de.json @@ -1112,16 +1112,17 @@ "notifications_cat_pads": "Mit mir geteilt", "notifications_cat_archived": "Verlauf", "notifications_dismissAll": "Alle verbergen", - "support_notification": "", - "requestEdit_button": "", - "requestEdit_dialog": "", - "requestEdit_confirm": "", - "requestEdit_fromFriend": "", - "requestEdit_fromStranger": "", - "requestEdit_viewPad": "", - "later": "", - "requestEdit_request": "", - "requestEdit_accepted": "", - "requestEdit_sent": "", - "uploadFolderButton": "" + "support_notification": "Ein Administrator hat dein Support-Ticket beantwortet", + "requestEdit_button": "Bearbeitungsrechte anfragen", + "requestEdit_dialog": "Bist du sicher, dass du den Eigentümer um Bearbeitungsrechte für das Pad bitten möchtest?", + "requestEdit_confirm": "{1} hat Bearbeitungsrechte für das Pad {0} angefragt. Möchtest du die Rechte vergeben?", + "requestEdit_fromFriend": "Du bist mit {0} befreundet", + "requestEdit_fromStranger": "Du bist nicht mit {0} befreundet", + "requestEdit_viewPad": "Pad in neuem Tab öffnen", + "later": "Später entscheiden", + "requestEdit_request": "{1} möchte das Pad {0} bearbeiten", + "requestEdit_accepted": "{1} hat dir Bearbeitungsrechte für das Pad {0} gegeben", + "requestEdit_sent": "Anfrage gesendet", + "uploadFolderButton": "Ordner hochladen", + "properties_unknownUser": "{0} unbekannte(r) Benutzer" } From c555a0f500cca8465f4ca0db0cad1c2f6d226480 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 16 Aug 2019 13:37:15 +0000 Subject: [PATCH 28/30] Translated using Weblate (Portuguese (Brazil)) Currently translated at 32.3% (333 of 1030 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/pt_BR/ --- www/common/translations/messages.pt-br.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/www/common/translations/messages.pt-br.json b/www/common/translations/messages.pt-br.json index a9128e707..9799b671c 100644 --- a/www/common/translations/messages.pt-br.json +++ b/www/common/translations/messages.pt-br.json @@ -8,7 +8,11 @@ "drive": "Drive", "whiteboard": "Whiteboard", "file": "File", - "media": "Media" + "media": "Media", + "kanban": "Kanban", + "todo": "A Fazer", + "contacts": "Contactos", + "sheet": "SpreadSheet (Beta)" }, "button_newpad": "Novo bloco RTF", "button_newcode": "Novo bloco de código", From c9569a136dd6d5086603c81912f704792605074f Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 16 Aug 2019 13:37:15 +0000 Subject: [PATCH 29/30] Translated using Weblate (Russian) Currently translated at 48.7% (502 of 1030 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ru/ Translated using Weblate (Russian) Currently translated at 47.1% (485 of 1030 strings) Translation: CryptPad/App Translate-URL: http://weblate.cryptpad.fr/projects/cryptpad/app/ru/ --- www/common/translations/messages.ru.json | 118 +++++++++++++++++------ 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/www/common/translations/messages.ru.json b/www/common/translations/messages.ru.json index ca1a1f143..62914d82a 100644 --- a/www/common/translations/messages.ru.json +++ b/www/common/translations/messages.ru.json @@ -102,7 +102,7 @@ "forgetPrompt": "Нажав ОК, вы удалите документ в корзину. Уверены?", "movedToTrash": "Документ был удалён в корзину.
Доступ к диску", "shareButton": "Поделиться", - "shareSuccess": "Ссылка скопирована в буфер обмена.", + "shareSuccess": "Ссылка скопирована в буфер обмена", "userListButton": "Список пользователей", "chatButton": "Чат", "userAccountButton": "Ваш профиль", @@ -139,36 +139,36 @@ "or": "или", "tags_title": "Теги (только для вас)", "tags_add": "Обновить теги страницы", - "tags_searchHint": "Начните поиск в вашем CryptDrive при помощи # чтобы найти пэды с тегами", + "tags_searchHint": "Начните поиск в вашем CryptDrive при помощи # чтобы найти пэды с тегами.", "tags_notShared": "Ваши теги не разделяются с другими пользователями", "button_newsheet": "Новый Лист", - "newButtonTitle": "Создать новый блокнот", + "newButtonTitle": "Создать новый документ", "useTemplateCancel": "Начать заново (Esc)", - "previewButtonTitle": "Отобразить или скрыть режим предпросмотра разметки", - "printOptions": "Опции расположения", + "previewButtonTitle": "Показать или скрыть просмотр Маркдаун разметки", + "printOptions": "Настройки размещения", "printBackgroundValue": "Текущий фон: {0}", - "printBackgroundNoValue": "Нет отображаемого фонового изображения", - "tags_duplicate": "Скопировать тег: {0}", - "tags_noentry": "Вы не можете присвоить тег удалённому блокноту!", - "slideOptionsText": "Опции", - "slideOptionsTitle": "Настроить ваши слайды", - "slideOptionsButton": "Сохранить (Enter)", - "slide_invalidLess": "Неверный настраиваемый стиль", + "printBackgroundNoValue": "Фоновое изображение не показано", + "tags_duplicate": "Скопировать метку: {0}", + "tags_noentry": "Вы не можете присвоить метку удалённому документу!", + "slideOptionsText": "Настройки", + "slideOptionsTitle": "Настройте ваши слайды", + "slideOptionsButton": "Сохранить (Ввод)", + "slide_invalidLess": "Неверный пользовательский стиль", "languageButton": "Язык", - "languageButtonTitle": "Выберите язык, используемый для подсветки синтаксиса", + "languageButtonTitle": "Выберите язык для использования подсветки слов", "themeButton": "Тема", - "themeButtonTitle": "Выберите цветовую тему, используемую в редакторе кода и слайдов", - "editShare": "Редактирование ссылки", - "editShareTitle": "Скопировать редактируемую ссылку", + "themeButtonTitle": "Выберите цветовую тему для использования в редакторе кода и слайдов", + "editShare": "Редактируемая ссылка", + "editShareTitle": "Скопировать редактируемую ссылку в буфер обмена", "editOpen": "Открыть редактируемую ссылку в новой вкладке", - "editOpenTitle": "Открыть блокнот в режиме редактирования в новой вкладке", + "editOpenTitle": "Открыть данный документ для редактирования в новой вкладке", "viewShare": "Ссылка только для чтения", - "viewShareTitle": "Скопировать ссылку для чтения в буфер обмена", - "viewOpen": "Открыть ссылку в режиме чтения в новой вкладке", - "viewOpenTitle": "Открыть блокнот в режиме чтения в новой вкладке", + "viewShareTitle": "Скопировать ссылку только для чтения в буфер обмена", + "viewOpen": "Открыть ссылку только для чтения в новой вкладке", + "viewOpenTitle": "Открыть данный документ для чтения в новой вкладке", "fileShare": "Скопировать ссылку", "getEmbedCode": "Получить код для встраивания", - "viewEmbedTitle": "Встроить блокнот на внешнюю страницу", + "viewEmbedTitle": "Встроить документ во внешнюю страницу", "notifyJoined": "{0} присоединился к совместной сессии", "notifyRenamed": "{0} теперь известен как {1}", "notifyLeft": "{0} покинул совместную сессию", @@ -258,7 +258,7 @@ "profile_fieldSaved": "Сохранено новое значение: {0}", "profile_viewMyProfile": "Посмотреть мой профиль", "contacts_title": "Контакты", - "contacts_added": "Приглашение принято контактом", + "contacts_added": "Приглашение принято контактом.", "contacts_rejected": "Контакт не принял приглашение", "contacts_send": "Отправить", "contacts_remove": "Убрать этот контакт", @@ -302,22 +302,22 @@ "crowdfunding_popup_no": "Не сейчас", "crowdfunding_popup_never": "Не спрашивать меня снова", "markdown_toc": "Содержимое", - "fm_expirablePad": "Этот блокнот удалится через {0}", - "fileEmbedTitle": "Вставить файл во внешнюю страницу", + "fm_expirablePad": "Этот блокнот истечет {0}", + "fileEmbedTitle": "Встроить файл во внешнюю страницу", "kanban_removeItemConfirm": "Вы уверенны, что хотите удалить этот пункт?", "settings_backup2": "Скачать мой CryptDrive", "settings_backup2Confirm": "Это позволит скачать все пэды и файлы с вашего CryptDrive. Если вы хотите продолжить, выберите имя и нажмите OK", "settings_exportTitle": "Экспортировать Ваш CryptDrive", "fileEmbedScript": "Чтобы вставить этот файл, включите этот скрипт один раз на своей странице, чтобы загрузить медиатег:", - "fileEmbedTag": "Затем поместите медиатег в любое место на странице, куда вы хотите его вставить:", + "fileEmbedTag": "Затем поместите медиатег в любое место на странице,в которое вы хотите его вставить:", "pad_mediatagRatio": "Оставить соотношение", "kanban_item": "Элемент {0}", "poll_p_encryption": "Все ваши данные зашифрованы, доступ к ним имеют только пользователи, имеющие доступ к этой ссылке. Даже сервер не видит, что вы меняете.", "wizardLog": "Нажмите кнопку в левом верхнем углу, чтобы вернуться к опросу", "poll_bookmark_col": "Добавить этот столбец в закладку, чтобы он всегда был разблокирован и отображался для вас в начале", "poll_bookmarked_col": "Это твоя колонка закладок. Она всегда будет разблокирована и отображаться для вас в начале.", - "poll_wizardDescription": "Автоматическое создавайте несколько опций путем ввода произвольного количества дат и временных сегментов", - "poll_comment_disabled": "Опубликуйте этот опрос с помощью кнопки ✓ для включения комментариев", + "poll_wizardDescription": "Автоматически создавайте несколько опций путем ввода произвольного количества дат и временных сегментов", + "poll_comment_disabled": "Опубликуйте этот опрос с помощью кнопки ✓ для включения комментариев.", "oo_cantUpload": "Загрузка запрещена, если присутствуют другие пользователи.", "oo_uploaded": "Ваша загрузка завершена. Нажмите OK, чтобы перезагрузить страницу или отменить, чтобы остаться в режиме чтения.", "canvas_imageEmbed": "Вставьте изображение с вашего компьютера", @@ -367,13 +367,13 @@ "fm_padIsOwnedOther": "Этот пэд принадлежит другому пользователю", "fm_deletedPads": "Эти пэды больше не существуют на сервере, они были удалены с вашего CryptDrive: {0}", "fm_tags_name": "Имя тэга", - "printCSS": "Пользовательские настройки вида (CSS)", - "viewEmbedTag": "Чтобы встроить данный документ вставьте iframe в нужную страницу. Вы можете настроить внешний вид используя CSS и HTML атрибуты. ", + "printCSS": "Пользовательские настройки вида (CSS):", + "viewEmbedTag": "Чтобы встроить данный документ, вставьте iframe в нужную страницу. Вы можете настроить внешний вид используя CSS и HTML атрибуты.", "debug_getGraphText": "Это код DOT для генерации графика истории этого документа:", "fm_ownedPadsName": "Собственный", "fm_info_anonymous": "Вы не вошли в учетную запись, поэтому срок действия ваших пэдов истечет через 3 месяца (find out more). Они хранятся в вашем браузере, поэтому очистка истории может привести к их исчезновению..
Sign up or Log in to keep them alive.
", "fm_backup_title": "Резервная ссылка", - "fm_burnThisDriveButton": "Удалить всю информацию, хранящуюся на CryptPad в браузере.", + "fm_burnThisDriveButton": "Удалить всю информацию, хранящуюся от CryptPad в браузере", "fm_tags_used": "Количество использований", "fm_restoreDrive": "Восстановление прежнего состояния диска. Для достижения наилучших результатов не вносите изменения в диск, пока этот процесс не будет завершен.", "fm_passwordProtected": "Этот документ защищен паролем", @@ -415,7 +415,7 @@ "fm_info_sharedFolder": "Это общая папка. Вы не вошли в систему, поэтому можете получить к ней доступ только в режиме только для чтения.
Sign up или Log in для импорта на CryptDrive и его изменения.", "fo_moveFolderToChildError": "Вы не можете переместить папку в одну из нее следующую", "fo_unavailableName": "Файл или папка с таким же именем уже существуют в новом месте. Переименуйте элемент и повторите попытку.", - "fs_migration": "Ваш CryptDrive обновляется до новой версии. В результате, текущая страница должна быть перезагружена.
перезагрузите эту страницу, чтобы продолжить ей пользоваться..", + "fs_migration": "Ваш CryptDrive обновляется до новой версии. В результате, текущая страница должна быть перезагружена.
перезагрузите эту страницу, чтобы продолжить ей пользоваться.", "login_accessDrive": "Доступ к хранилищу", "login_orNoLogin": "или", "login_noSuchUser": "Неверный логин или пароль. Попробуйте еще раз или зарегистрируйтесь", @@ -448,5 +448,59 @@ "settings_exportWarning": "Примечание: этот инструмент все еще находится в бета-версии и может иметь проблемы со масштабируемостью. Для повышения производительности рекомендуется оставить данную вкладку сфокусированной.", "settings_exportCancel": "Вы уверены, что хотите отменить экспорт? В следующий раз вам придется начинать все сначала.", "settings_export_reading": "Читаем ваше хранилище...", - "settings_export_download": "Скачиваем и расшифровываем ваши документы..." + "settings_export_download": "Скачиваем и расшифровываем ваши документы...", + "contacts_request": "{0} хотел бы добавить вас в список контактов. Принять ?", + "contacts_confirmRemove": "Вы уверены, что хотите удалить 1{0}2 из ваших контактов?", + "register_acceptTerms": "Я принимаю 1 условия пользования", + "register_warning": "Мы не сможем восстановить ваши данные, если вы потеряете пароль, так как мы не имеем доступа к ним.", + "settings_backupCategory": "Резервное копирование", + "settings_backup": "Резервная копия", + "settings_backupHint2": "Загрузите текущее содержимое всех ваших пэдов. Пэды будут загружены в читаемом формате, если такой формат доступен.", + "settings_export_compressing": "Данные сжимаются..", + "settings_export_done": "Ваше скачивание завершено!", + "settings_exportError": "Посмотреть ошибки", + "settings_exportErrorDescription": "Мы не смогли добавить в экспорт следующие документы:", + "settings_exportErrorEmpty": "Этот документ не может быть экспортирован (пустое или недостоверное содержимое).", + "settings_exportErrorMissing": "Этот документ отсутствует на наших серверах (истек или удален владельцем)", + "settings_exportErrorOther": "При попытке экспорта данного документа возникла ошибка: {0}", + "settings_resetNewTitle": "Очистить хриналище", + "settings_resetButton": "Удалить", + "settings_reset": "Удалите все файлы и папки с CryptDrive", + "settings_resetDone": "Ваше хранилище теперь пустое!", + "settings_resetError": "Неправильный текст верификации. Ваш CryptDrive не был изменен.", + "settings_resetTipsAction": "Сброс", + "settings_resetTips": "Подсказки", + "settings_resetTipsButton": "Сброс доступных подсказок в CryptDrive", + "settings_resetTipsDone": "Теперь все подсказки снова видны.", + "settings_thumbnails": "Иконки", + "settings_disableThumbnailsAction": "Отключить создание иконок в вашем хранилище", + "settings_disableThumbnailsDescription": "Иконки автоматически создаются и сохраняются в браузере при посещении нового документа. Вы можете отключить эту функцию здесь.", + "settings_resetThumbnailsAction": "Очистить", + "settings_resetThumbnailsDescription": "Очистить все иконки документов, хранящиеся в вашем браузере.", + "settings_resetThumbnailsDone": "Все иконки были удалены.", + "settings_import": "Импортировать", + "settings_importDone": "Импортирование завершено", + "settings_autostoreTitle": "Хранилище документов в CryptDrive", + "settings_autostoreYes": "Автоматически", + "settings_autostoreNo": "Вручную (никогда не спрашивать)", + "settings_autostoreMaybe": "Вручную (всегда спрашивать)", + "settings_userFeedbackTitle": "Обратная связь", + "settings_userFeedbackHint2": "Содержимое вашего документа никогда не будет передаваться на сервер.", + "fm_alert_anonymous": "Здравствуйте, в настоящее время вы используете CryptPad анонимно, это нормально, но ваши пэды могут быть удалены после периода бездействия. Мы отключили расширенные возможности хранилища для анонимных пользователей, потому что хотим быть уверенными, что это небезопасное место для хранения вещей. Вы можете 1читать далее2 о том, почему мы это делаем и почему вам стоит зарегистрироваться4 and 5Log in6.", + "settings_resetPrompt": "Это действие удалит все документы с диска.
Вы уверены, что хотите продолжить?
Напишите \"Я люблю CryptPad\" для подтверждения.", + "settings_importTitle": "Импортируйте последние документы данного браузера в ваше хранилище", + "settings_importConfirm": "Вы уверены, что хотите импортировать последние документы из этого браузера в хранилище вашего пользователя?", + "settings_userFeedbackHint1": "CryptPad держит очень простую обратную связь с сервером, чтобы мы знали, как улучшить ваше пользование.\n", + "settings_userFeedback": "Включить телеметрию", + "settings_deleteTitle": "Удаление аккаунта", + "settings_deleteHint": "Удаление аккаунта является постоянным. Ваш CryptDrive и список пэдов будут удалены с сервера. Остальные ваши пэды будут удалены через 90 дней, если никто другой не сохранил их в CryptDrive.", + "settings_deleteButton": "Удалить ваш аккаунт", + "settings_deleteModal": "Обменивайтесь следующей информацией с администратором CryptPad, чтобы удалить ваши данные с сервера.", + "settings_deleteConfirm": "Нажмите OK, чтобы удалить ваш аккаунт навсегда. Вы уверены?", + "settings_deleted": "Ваша учетная запись пользователя удалена. Нажмите OK, чтобы перейти на главную страницу.", + "settings_publicSigningKey": "Публичный ключ подписи", + "settings_usage": "Использование", + "settings_usageTitle": "Смотрите общий размер ваших прикрепленных документов в мегабайтах", + "settings_pinningNotAvailable": "Прикрепленные документы доступны только зарегистрированным пользователям.", + "settings_pinningError": "Что-то пошло не так" } From bd717f16e3935e8d5f90455441de181e07df2838 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 16 Aug 2019 16:02:29 +0200 Subject: [PATCH 30/30] Add translation keys --- www/common/translations/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json index 0678c459b..46d24d9bf 100644 --- a/www/common/translations/messages.json +++ b/www/common/translations/messages.json @@ -321,6 +321,7 @@ "fm_newButtonTitle": "Create a new pad or folder, import a file in the current folder", "fm_newFolder": "New folder", "fm_newFile": "New pad", + "fm_morePads": "More pads", "fm_folder": "Folder", "fm_sharedFolder": "Shared folder", "fm_folderName": "Folder name", @@ -383,6 +384,7 @@ "fc_color": "Change color", "fc_open": "Open", "fc_open_ro": "Open (read-only)", + "fc_openInCode": "Open in Code editor", "fc_expandAll": "Expand All", "fc_collapseAll": "Collapse All", "fc_delete": "Move to trash", @@ -554,6 +556,10 @@ "upload_modal_title": "File upload options", "upload_modal_filename": "File name (extension {0} added automatically)", "upload_modal_owner": "Owned file", + "uploadFolder_modal_title": "Folder upload options", + "uploadFolder_modal_filesPassword": "Files password", + "uploadFolder_modal_owner": "Owned files", + "uploadFolder_modal_forceSave": "Store files in your CryptDrive", "upload_serverError": "Server Error: unable to upload your file at this time.", "upload_uploadPending": "You already have an upload in progress. Cancel it and upload your new file?", "upload_success": "Your file ({0}) has been successfully uploaded and added to your drive.", @@ -989,6 +995,9 @@ "sharedFolders_create_owned": "Owned folder", "sharedFolders_create_password": "Folder password", "sharedFolders_share": "Share this URL with other registered users to give them access to the shared folder. Once they open this URL, the shared folder will be added to the root directory of their CryptDrive.", + "convertFolderToSF_SFParent": "This folder cannot be converted to a shared folder in its current location. Move it outside of the containing shared folder to continue.", + "convertFolderToSF_SFChildren": "This folder cannot be converted to a shared folder because it already contains shared folders. Move those Shared folders elsewhere to continue.", + "convertFolderToSF_confirm": "This folder must be converted to a Shared folder for others to view it. Continue?", "chrome68": "It seems that you're using the browser Chrome or Chromium version 68. It contains a bug resulting in the page turning completely white after a few seconds or the page being unresponsive to clicks. To fix this issue, you can switch to another tab and come back, or try to scroll in the page. This bug should be fixed in the next version of your browser.", "autostore_file": "file", "autostore_sf": "folder",