From 636f801bd7667d4389813eef2b81c6efbe54132d Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 13 Jul 2022 20:23:31 -0700 Subject: [PATCH] Added file edit feature to My Files tab (#4243) --- meshuser.js | 39 +++++++++++++++++++++++++++-------- views/default.handlebars | 44 ++++++++++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/meshuser.js b/meshuser.js index ac0248dd..36e8b575 100644 --- a/meshuser.js +++ b/meshuser.js @@ -785,15 +785,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Check permissions if ((user.siteadmin & 8) != 0) { // Perform a file operation (Create Folder, Delete Folder, Delete File...) - if (common.validateString(command.fileop, 4, 16) == false) return; + if (common.validateString(command.fileop, 3, 16) == false) return; var sendUpdate = true, path = meshPathToRealPath(command.path, user); // This will also check access rights if (path == null) break; if ((command.fileop == 'createfolder') && (common.IsFilenameValid(command.newfolder) == true)) { // Create a new folder - try { fs.mkdirSync(path + '/' + command.newfolder); } catch (ex) { + try { fs.mkdirSync(parent.path.join(path, command.newfolder)); } catch (ex) { try { fs.mkdirSync(path); } catch (ex) { } - try { fs.mkdirSync(path + '/' + command.newfolder); } catch (ex) { } + try { fs.mkdirSync(parent.path.join(path, command.newfolder)); } catch (ex) { } } } else if (command.fileop == 'delete') { @@ -822,14 +822,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } else if ((command.fileop == 'rename') && (common.IsFilenameValid(command.oldname) === true) && (common.IsFilenameValid(command.newname) === true)) { // Rename - try { fs.renameSync(path + '/' + command.oldname, path + '/' + command.newname); } catch (e) { } + try { fs.renameSync(parent.path.join(path, command.oldname), parent.path.join(path, command.newname)); } catch (e) { } } else if ((command.fileop == 'copy') || (command.fileop == 'move')) { // Copy or move of one or many files - if (common.validateArray(command.names, 1) == false) return; - var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights + if (common.validateArray(command.name, 1) == false) return; + var scpath = meshPathToRealPath(command.path, user); // This will also check access rights if (scpath == null) break; - // TODO: Check quota if this is a copy!!!!!!!!!!!!!!!! + // TODO: Check quota if this is a copy for (i in command.names) { if (common.IsFilenameValid(command.names[i]) === true) { var s = parent.path.join(scpath, command.names[i]), d = parent.path.join(path, command.names[i]); @@ -837,8 +837,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use copyFile(s, d, function (op) { if (op != null) { fs.unlink(op, function (err) { parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null)); } } + } else if (command.fileop == 'get') { + // Get a short file and send it back on the web socket + if (common.validateString(command.file, 1, 4096) == false) return; + const scpath = meshPathToRealPath(command.path, user); // This will also check access rights + if (scpath == null) break; + const filePath = parent.path.join(scpath, command.file); + fs.stat(filePath, function (err, stat) { + if ((err != null) || (stat == null) || (stat.size >= 204800)) return; + fs.readFile(filePath, function (err, data) { + if ((err != null) || (data == null)) return; + command.data = data.toString('base64'); + ws.send(JSON.stringify(command)); // Send the file data back, base64 encoded. + }); + }); + } else if (command.fileop == 'set') { + // Set a short file transfered on the web socket + if (common.validateString(command.file, 1, 4096) == false) return; + if (typeof command.data != 'string') return; + const scpath = meshPathToRealPath(command.path, user); // This will also check access rights + if (scpath == null) break; + const filePath = parent.path.join(scpath, command.file); + var data = null; + try { data = Buffer.from(command.data, 'base64'); } catch (ex) { return; } + fs.writeFile(filePath, data, function (err) { if (err == null) { parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }); } - if (sendUpdate == true) { parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } // Fire an event causing this user to update this files } break; diff --git a/views/default.handlebars b/views/default.handlebars index 28d29a73..623a1350 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -506,7 +506,7 @@       - +         @@ -2915,6 +2915,30 @@ account_managePhoneCodeValidate(); break; } + case 'fileoperation': { + // View the file in the dialog box + var p5editSaveBack = function(b, tag) { + var data; + if (d4EditEncodingVal == 1) { + data = encode_utf8(Q('d4editorarea').value); // UTF8 encoding + } else { + data = Q('d4editorarea').value; // RAW encoding + } + meshserver.send({ action: 'fileoperation', fileop: 'set', path: tag.path, file: tag.file, data: btoa(data) }); + } + setDialogMode(4, EscapeHtml(message.file), 3, p5editSaveBack, null, message); + QS('dialog').width = 'auto'; + QS('dialog').bottom = '80px'; + QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px'; + if (d4EditEncodingVal == 1) { + // UTF8 Encoding + Q('d4editorarea').value = decode_utf8(atob(message.data)); + } else { + // RAW Encoding + Q('d4editorarea').value = atob(message.data); + } + break; + } case 'event': { if (!message.event.nolog) { if (currentNode && (message.event.nodeid == currentNode._id) && (currentDeviceEvents != null)) { @@ -13656,10 +13680,10 @@ function p5setActions() { var cc = getFileSelCount(), tc = getFileCount(), sfc = getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders) QE('p5DeleteFileButton', (cc > 0) && (filetreelocation.length > 0)); + QE('p5ViewFileButton', (cc == 1) && (sfc == 1) && (filetreelocation.length > 0)); QE('p5NewFolderButton', filetreelocation.length > 0); QE('p5UploadButton', filetreelocation.length > 0); QE('p5RenameFileButton', (cc == 1) && (filetreelocation.length > 0)); - //QE('p5ViewFileButton', (cc == 1) && (sfc == 1) && (filetreelocation.length > 0)); QE('p5SelectAllButton', tc > 0); Q('p5SelectAllButton').value = (cc > 0 ? "Select None" : "Select All"); QE('p5CutButton', (sfc > 0) && (cc == sfc)); @@ -13685,6 +13709,7 @@ var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })(); function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '
'); p5updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } + function p5GetFileInfo(name) { var filetreex = filetree; for (var i in filetreelocation) { if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; } } return filetreex.f[name]; } function p5updateUploadDialogOk() { // Check if these are files we can upload, remove all folders. var xallfiles = Q('p5uploadinput').files, files = []; @@ -13712,27 +13737,20 @@ } } } - /* function p5viewfile() { var checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { - console.log(filetreelocation.join('/') + '/' + checkboxes[i].value); // TODO: Download and show this file - break; + var f = p5GetFileInfo(checkboxes[i].value); if (f == null) return; + if (f.s <= 204800) { meshserver.send({ action: 'fileoperation', fileop: 'get', path: filetreelocation, file: checkboxes[i].value, tag: 'edit' }); } else { messagebox("File Editor", "Only files less than 200k can be edited."); } + return; } } } - */ - var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); - for (var i = 0; i < checkboxes.length; i++) { - if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { - console.log('yy', checkboxes[i].value); - p5clipboard.push(checkboxes[i].value); - } - } + for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confirm {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); }