From: Alexander Moisseev Date: Mon, 26 May 2025 10:04:30 +0000 (+0300) Subject: [WebUI] Rework file upload JS implementation X-Git-Tag: 3.12.0~12^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F5483%2Fhead;p=thirdparty%2Frspamd.git [WebUI] Rework file upload JS implementation Original code, which seems AI-generated, was broken Closes #5381 --- diff --git a/interface/js/app/common.js b/interface/js/app/common.js index b5d3cbbab9..44e0dcf778 100644 --- a/interface/js/app/common.js +++ b/interface/js/app/common.js @@ -262,53 +262,42 @@ define(["jquery", "nprogress"], }; ui.fileUtils = { - files: null, - filesIdx: 0, - - enableButton(button, textArea, validator) { - const hasText = $.trim($(textArea).val()).length > 0; - const needsValidation = validator !== "none"; - - $(button).prop("disabled", !hasText || (needsValidation && !$(validator).hasClass("is-valid"))); - }, - - readFile(callback, textArea, button, validator) { - if (!this.files || this.files.length === 0) { - alertMessage("alert-error", "No files selected."); - return; - } - + readFile(files, callback, index = 0) { + const file = files[index]; const reader = new FileReader(); - - reader.onload = () => { - $(textArea).val(reader.result).trigger("input"); - this.enableButton(button, textArea, validator); - - if (callback) callback(reader.result); - }; - reader.onerror = () => alertMessage("alert-error", "Error reading file."); - reader.readAsText(this.files[this.filesIdx]); + reader.onerror = () => alertMessage("alert-error", `Error reading file: ${file.name}`); + reader.onloadend = () => callback(reader.result); + reader.readAsText(file); }, - handleFileInput(fileSrc, textArea, button, fileInput, validator) { - ({ - files: this.files - } = fileSrc); - this.filesIdx = 0; - - if (!this.files.length) { - alertMessage("alert-warning", "No file was provided."); - return; - } - $(fileInput)[0].files = this.files; - // eslint-disable-next-line no-alert - if (this.files.length === 1 || confirm(`Are you sure you want to process ${this.files.length} files?`)) { - this.readFile(null, textArea, button, validator); - } + setFileInputFiles(fileInput, files, i) { + const dt = new DataTransfer(); + if (arguments.length > 2) dt.items.add(files[i]); + $(fileInput).prop("files", dt.files); }, - setupFileHandling(textArea, fileInput, button, validator) { + setupFileHandling(textArea, fileInput, fileSet, enable_btn_cb, multiple_files_cb) { const dragoverClassList = "outline-dashed-primary bg-primary-subtle"; + const {readFile, setFileInputFiles} = ui.fileUtils; + + function handleFileInput(fileSource) { + fileSet.files = fileSource.files; + fileSet.index = 0; + const {files} = fileSet; + + if (files.length === 1) { + setFileInputFiles(fileInput, files, 0); + enable_btn_cb(); + readFile(files, (result) => { + $(textArea).val(result); + enable_btn_cb(); + }); + } else if (multiple_files_cb) { + multiple_files_cb(files); + } else { + alertMessage("alert-warning", "Multiple files processing is not supported."); + } + } $(textArea) .on("dragenter dragover dragleave drop", (e) => { @@ -317,11 +306,16 @@ define(["jquery", "nprogress"], }) .on("dragenter dragover", () => $(textArea).addClass(dragoverClassList)) .on("dragleave drop", () => $(textArea).removeClass(dragoverClassList)) - .on("drop", (e) => { - this.handleFileInput(e.originalEvent.dataTransfer, textArea, button, fileInput, validator); + .on("drop", (e) => handleFileInput(e.originalEvent.dataTransfer)) + .on("input", () => { + enable_btn_cb(); + if (fileSet.files) { + fileSet.files = null; + setFileInputFiles(fileInput, fileSet.files); + } }); - $(fileInput).on("change", (e) => this.handleFileInput(e.target, textArea, button, fileInput, validator)); - $(textArea).on("input", () => this.enableButton(button, textArea, validator)); + + $(fileInput).on("change", (e) => handleFileInput(e.target)); } }; diff --git a/interface/js/app/selectors.js b/interface/js/app/selectors.js index 788ceeacb6..4a1c6d0d05 100644 --- a/interface/js/app/selectors.js +++ b/interface/js/app/selectors.js @@ -2,6 +2,7 @@ define(["jquery", "app/common"], ($, common) => { "use strict"; const ui = {}; + const fileSet = {files: null, index: null}; function enable_disable_check_btn() { $("#selectorsChkMsgBtn").prop("disabled", ( @@ -129,9 +130,6 @@ define(["jquery", "app/common"], return false; }); - $("#selectorsMsgArea").on("input", () => { - enable_disable_check_btn(); - }); $("#selectorsSelArea").on("input", () => { checkSelectors(); }); @@ -140,7 +138,7 @@ define(["jquery", "app/common"], $("#selectorsFile").val(""); }); - common.fileUtils.setupFileHandling("#selectorsMsgArea", "#selectorsFile", "#selectorsChkMsgBtn", "#selectorsSelArea"); + common.fileUtils.setupFileHandling("#selectorsMsgArea", "#selectorsFile", fileSet, enable_disable_check_btn); return ui; }); diff --git a/interface/js/app/upload.js b/interface/js/app/upload.js index 2ba4b61679..a5d30b59ef 100644 --- a/interface/js/app/upload.js +++ b/interface/js/app/upload.js @@ -28,8 +28,7 @@ define(["jquery", "app/common", "app/libft"], ($, common, libft) => { "use strict"; const ui = {}; - let filesIdx = null; - let files = null; + const fileSet = {files: null, index: null}; let scanTextHeaders = {}; function uploadText(data, url, headers, method = "POST") { @@ -67,15 +66,7 @@ define(["jquery", "app/common", "app/libft"], function enable_disable_scan_btn(disable) { $("#scan button:not(#cleanScanHistory, #deleteHashesBtn, #scanOptionsToggle, .ft-columns-btn)") - .prop("disabled", (disable || $.trim($("textarea").val()).length === 0)); - } - - function updateFileInput(i) { - const dt = new DataTransfer(); - if (arguments.length > 0 && files && files[i]) { - dt.items.add(files[i]); - } - $("#formFile").prop("files", dt.files); + .prop("disabled", (disable || $.trim($("#scanMsgSource").val()).length === 0)); } function scanText(data) { @@ -96,7 +87,7 @@ define(["jquery", "app/common", "app/libft"], const {items} = o; common.symbols.scan.push(o.symbols[0]); - if (files) items[0].file = files[filesIdx].name; + if (fileSet.files) items[0].file = fileSet.files[fileSet.index].name; if (Object.prototype.hasOwnProperty.call(common.tables, "scan")) { common.tables.scan.rows.load(items, true); @@ -104,16 +95,16 @@ define(["jquery", "app/common", "app/libft"], require(["footable"], () => { libft.initHistoryTable(data, items, "scan", libft.columns_v2("scan"), true, () => { - if (files && filesIdx < files.length - 1) { - common.fileUtils.files = files; - common.fileUtils.filesIdx = ++filesIdx; - common.fileUtils.readFile((result) => { - if (filesIdx === files.length - 1) { + const {files} = fileSet; + if (files && fileSet.index < files.length - 1) { + common.fileUtils.readFile(files, (result) => { + const {index} = fileSet; + if (index === files.length - 1) { $("#scanMsgSource").val(result); - updateFileInput(filesIdx); + common.fileUtils.setFileInputFiles("#formFile", files, index); } scanText(result); - }, "#scanMsgSource", "#scanCheckMsgBtn", "none"); + }, ++fileSet.index); } else { enable_disable_scan_btn(); $("#cleanScanHistory, #scan .ft-columns-dropdown .btn-dropdown-apply") @@ -146,7 +137,6 @@ define(["jquery", "app/common", "app/libft"], }); } - function getFuzzyHashes(data) { function fillHashTable(rules) { $("#hashTable tbody").empty(); @@ -186,8 +176,7 @@ define(["jquery", "app/common", "app/libft"], $("#cleanScanHistory").off("click"); $("#cleanScanHistory").on("click", (e) => { e.preventDefault(); - // eslint-disable-next-line no-alert - if (!confirm("Are you sure you want to clean scan history?")) { + if (!confirm("Are you sure you want to clean scan history?")) { // eslint-disable-line no-alert return; } libft.destroyTable("scan"); @@ -196,13 +185,6 @@ define(["jquery", "app/common", "app/libft"], }); enable_disable_scan_btn(); - $("textarea").on("input", () => { - enable_disable_scan_btn(); - if (files) { - files = null; - updateFileInput(); - } - }); $("#scanClean").on("click", () => { enable_disable_scan_btn(true); @@ -304,7 +286,15 @@ define(["jquery", "app/common", "app/libft"], }); - common.fileUtils.setupFileHandling("#scanMsgSource", "#formFile", "#scanCheckMsgBtn", "none"); + function multiple_files_cb(files) { + // eslint-disable-next-line no-alert + if (files.length < 10 || confirm("Are you sure you want to scan " + files.length + " files?")) { + getScanTextHeaders(); + common.fileUtils.readFile(files, (result) => scanText(result)); + } + } + + common.fileUtils.setupFileHandling("#scanMsgSource", "#formFile", fileSet, enable_disable_scan_btn, multiple_files_cb); return ui; });