From dec62b116a7b15bff8bb1253ab956d4f092e0847 Mon Sep 17 00:00:00 2001 From: jelveh Date: Thu, 16 Jul 2026 18:23:29 -0700 Subject: [PATCH] Fix Dashboard Files tab bugs - Escape filenames in renderItem to prevent stored XSS via HTML in file names (matches UIItem); raw name is still preserved for display/rename. - Read clone data-id from the nested .row in drag drop handlers so multi-select drag moves all selected items, not just the grabbed one. - Resolve sidebar drop target from the element's data-path so dragging onto Public/Home works (they aren't keyed in user.directories); fix the matching UID-vs-path comparison in the drag-out handler. - Set data-is_worker/data-worker_url from is_worker itself instead of an always-true "!== undefined" check, so worker context-menu behavior only applies to actual workers. - Give delete-confirmation buttons explicit values so permanent delete works in non-English locales. - Refresh the view after a context-menu move-paste, mirroring Ctrl+V. - Guard the item.updated socket handler against the client's own echo, matching the other handlers and UIDesktop. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/gui/src/UI/Dashboard/TabFiles.js | 49 +++++++++++++------------ src/gui/src/UI/Dashboard/UIDashboard.js | 2 + 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/gui/src/UI/Dashboard/TabFiles.js b/src/gui/src/UI/Dashboard/TabFiles.js index 3474280e0..2f40296b0 100644 --- a/src/gui/src/UI/Dashboard/TabFiles.js +++ b/src/gui/src/UI/Dashboard/TabFiles.js @@ -291,10 +291,10 @@ const TabFiles = { ui.helper.data('dropped', true); - // Get target folder path - const folderName = folderElement.getAttribute('data-folder'); - const directories = Object.keys(window.user.directories); - const targetPath = directories.find(f => f.endsWith(folderName)); + // Get target folder path from the element itself. Using the + // element's own data-path covers folders (Public, Home) that + // aren't keyed in window.user.directories. + const targetPath = folderElement.getAttribute('data-path'); if ( ! targetPath ) return; @@ -303,7 +303,7 @@ const TabFiles = { // Add other selected items $('.item-selected-clone').each(function () { - const sourceId = $(this).attr('data-id'); + const sourceId = $(this).find('.row').attr('data-id'); const sourceItem = document.querySelector(`.row[data-id="${sourceId}"]`); if ( sourceItem ) itemsToMove.push(sourceItem); }); @@ -384,11 +384,9 @@ const TabFiles = { $(folderElement).removeClass('dwell-opening'); // Only remove active if it's not the currently selected folder - const folderName = folderElement.getAttribute('data-folder'); - const directories = Object.keys(window.user.directories); - const folderUid = window.user.directories[directories.find(f => f.endsWith(folderName))]; + const folderPath = folderElement.getAttribute('data-path'); - if ( folderUid !== _this.currentPath ) { + if ( folderPath !== _this.currentPath ) { $(folderElement).removeClass('active'); } } @@ -672,11 +670,11 @@ const TabFiles = { const alert_resp = await UIAlert({ message: i18n('confirm_delete_multiple_items'), buttons: [ - { label: i18n('delete'), type: 'primary' }, - { label: i18n('cancel') }, + { label: i18n('delete'), value: 'delete', type: 'primary' }, + { label: i18n('cancel'), value: 'cancel' }, ], }); - if ( alert_resp === 'Delete' ) { + if ( alert_resp === 'delete' ) { for ( const row of trashedItems.toArray() ) { await window.delete_item(row); } @@ -1328,11 +1326,11 @@ const TabFiles = { const confirmed = await UIAlert({ message: i18n('confirm_delete_multiple_items'), buttons: [ - { label: i18n('delete'), type: 'primary' }, - { label: i18n('cancel') }, + { label: i18n('delete'), value: 'delete', type: 'primary' }, + { label: i18n('cancel'), value: 'cancel' }, ], }); - if ( confirmed === 'Delete' ) { + if ( confirmed === 'delete' ) { for ( const row of selectedRows ) { await window.delete_item(row); } @@ -1900,7 +1898,7 @@ const TabFiles = { // Collect all items to move (primary + any selected clones) const itemsToMove = [ui.draggable[0]]; $('.item-selected-clone').each(function () { - const sourceId = $(this).attr('data-id'); + const sourceId = $(this).find('.row').attr('data-id'); const sourceItem = document.querySelector(`.row[data-id="${sourceId}"]`); if ( sourceItem ) itemsToMove.push(sourceItem); }); @@ -1999,8 +1997,8 @@ const TabFiles = { row.setAttribute("data-is_shortcut", file.is_shortcut); row.setAttribute("data-shortcut_to", html_encode(file.shortcut_to)); row.setAttribute("data-shortcut_to_path", html_encode(file.shortcut_to_path)); - row.setAttribute("data-is_worker", is_worker !== undefined ? "1" : "0"); - row.setAttribute("data-worker_url", is_worker !== undefined ? worker_url : "0"); + row.setAttribute("data-is_worker", is_worker ? "1" : "0"); + row.setAttribute("data-worker_url", is_worker ? worker_url : "0"); row.setAttribute("data-sortable", file.sortable ?? 'true'); row.setAttribute("data-metadata", JSON.stringify(metadata)); row.setAttribute("data-sort_by", html_encode(file.sort_by) ?? 'name'); @@ -2038,8 +2036,8 @@ const TabFiles = { >
-
${displayName}
- +
${html_encode(displayName)}
+