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) <noreply@anthropic.com>
This commit is contained in:
jelveh
2026-07-16 18:23:29 -07:00
co-authored by Claude Opus 4.8
parent ea063c7413
commit dec62b116a
2 changed files with 27 additions and 24 deletions
+25 -24
View File
@@ -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 = {
>
</div>
<div class="item-name-wrapper">
<pre class="item-name">${displayName}</pre>
<textarea class="item-name-editor hide-scrollbar" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" data-gramm_editor="false">${displayName}</textarea>
<pre class="item-name">${html_encode(displayName)}</pre>
<textarea class="item-name-editor hide-scrollbar" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" data-gramm_editor="false">${html_encode(displayName)}</textarea>
</div>
<div class="col-spacer"></div>
<div class="item-metadata">
@@ -2583,7 +2581,7 @@ const TabFiles = {
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);
});
@@ -3199,11 +3197,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);
}
@@ -3354,6 +3352,9 @@ const TabFiles = {
window.copy_clipboard_items(targetPath, null);
} else if ( window.clipboard_op === 'move' ) {
await _this.moveClipboardItems(targetPath);
// Refresh so moved-away source rows disappear and any
// items pasted into the current folder show up.
_this.renderDirectory(_this.currentPath);
}
},
});
+2
View File
@@ -309,6 +309,8 @@ async function UIDashboard (options) {
});
window.socket.on('item.updated', async (item) => {
if ( item.original_client_socket_id === window.socket.id ) return;
const $el = $(`.item[data-uid='${item.uid}']`);
if ( $el.length === 0 ) return;