diff --git a/build-plugin.sh b/build-plugin.sh index bcbf832..2f1a530 100755 --- a/build-plugin.sh +++ b/build-plugin.sh @@ -2,7 +2,7 @@ set -eu PLUGIN="u-navigator" -VERSION="2026.06.23.r041" +VERSION="2026.06.23.r042" PKG_DIR="packages" WORK_DIR=".plugin-build" PKG_NAME="${PLUGIN}-${VERSION}.tgz" diff --git a/packages/u-navigator-2026.06.23.r041.tgz b/packages/u-navigator-2026.06.23.r041.tgz deleted file mode 100644 index 4be5f6b..0000000 Binary files a/packages/u-navigator-2026.06.23.r041.tgz and /dev/null differ diff --git a/packages/u-navigator-2026.06.23.r042.tgz b/packages/u-navigator-2026.06.23.r042.tgz new file mode 100644 index 0000000..06295b0 Binary files /dev/null and b/packages/u-navigator-2026.06.23.r042.tgz differ diff --git a/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job.php b/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job.php index 685cf9b..b328ea0 100644 --- a/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job.php +++ b/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job.php @@ -6,7 +6,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } $action = $_GET['action'] ?? ''; -if (!in_array($action, ['move', 'copy', 'size', 'permissions'], true)) { +if (!in_array($action, ['move', 'copy', 'delete', 'size', 'permissions'], true)) { unav_error(404, 'Unknown job action'); } @@ -18,6 +18,8 @@ if (in_array($action, ['move', 'copy'], true)) { unav_assert_writable(); $destination = unav_destination_path($data['destination'] ?? null); unav_guard_destination($source['path'], $destination['path']); +} elseif ($action === 'delete') { + unav_assert_writable(); } elseif ($action === 'permissions') { unav_assert_writable(); $permissions = unav_permission_options($data); diff --git a/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job_worker.php b/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job_worker.php index 59cd125..17b13fe 100644 --- a/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job_worker.php +++ b/plugin-root/usr/local/emhttp/plugins/u-navigator/api/job_worker.php @@ -16,6 +16,8 @@ function unav_run_job(string $id): void { } elseif ($job['type'] === 'copy') { unav_worker_guard($job['source'], $job['destination']); unav_worker_copy($job['source'], $job['destination'], $job); + } elseif ($job['type'] === 'delete') { + unav_worker_delete_job($job['source'], $job); } elseif ($job['type'] === 'size') { $result = unav_worker_size($job['source'], $job); $job['result'] = $result; @@ -90,6 +92,12 @@ function unav_worker_move(string $source, string $destination, array &$job): voi unav_worker_delete($source); } +function unav_worker_delete_job(string $source, array &$job): void { + $sourceReal = unav_worker_guard_source($source); + unav_worker_update($job, 'running', 15, 'Deleting'); + unav_worker_delete($sourceReal); +} + function unav_worker_size(string $source, array &$job): array { $sourceReal = unav_worker_guard_source($source); unav_worker_update($job, 'running', 10, 'Calculating size'); diff --git a/server/public/app.js b/server/public/app.js index fc659e7..6761892 100644 --- a/server/public/app.js +++ b/server/public/app.js @@ -272,6 +272,7 @@ function renderExplorer(body, win) { +
@@ -293,6 +294,7 @@ function renderExplorer(body, win) { body.querySelector('[data-action="view-icons"]').addEventListener('click', () => setExplorerView(win, 'icons')); body.querySelector('[data-action="upload-files"]').addEventListener('click', () => body.querySelector('[data-upload="files"]').click()); body.querySelector('[data-action="upload-folder"]').addEventListener('click', () => body.querySelector('[data-upload="folder"]').click()); + body.querySelector('[data-action="delete-selected"]').addEventListener('click', () => deleteEntries(win)); for (const inputEl of body.querySelectorAll('.upload-input')) { inputEl.addEventListener('change', async () => { try { @@ -1042,6 +1044,25 @@ async function promptTransfer(win, entry, type) { await createJob(type, entry.path, `${targetDir.replace(/\/$/, '')}/${entry.name}`, []); } +async function deleteEntries(win, entry = null) { + if (isReadOnly()) { + showError(new Error('Read-only mode ist aktiv.')); + return; + } + const entries = entry && selectedPathSet(win).has(entry.path) ? selectedEntries(win) : (entry ? [entry] : selectedEntries(win)); + if (!entries.length) { + showError(new Error('Keine Datei ausgewählt.')); + return; + } + const label = entries.length === 1 ? `"${entries[0].name}" wirklich löschen?` : `${entries.length} Objekte wirklich löschen?`; + if (!confirm(`${label}\n\nDieser Vorgang löscht direkt vom Dateisystem.`)) { + return; + } + for (const item of entries) { + await createJob('delete', item.path, null, [win.id]); + } +} + function openPermissionsDialog(win, entry) { if (isReadOnly()) { showError(new Error('Read-only mode ist aktiv.')); @@ -1301,6 +1322,7 @@ function renderContextMenu() { actions.push(['Berechtigungen...', () => openPermissionsDialog(win, entry)]); actions.push(['Kopieren nach...', () => promptTransfer(win, entry, 'copy')]); actions.push(['Verschieben nach...', () => promptTransfer(win, entry, 'move')]); + actions.push(['Löschen', () => deleteEntries(win, entry)]); } } else { actions.push(['Aktuellen Ordner herunterladen', () => downloadEntries(win, null)]); diff --git a/server/public/styles.css b/server/public/styles.css index d68c0e8..49bea0d 100644 --- a/server/public/styles.css +++ b/server/public/styles.css @@ -57,6 +57,14 @@ color: #ffffff; } +.u-nav button.danger { + color: #c4382d; +} + +.u-nav button.danger:hover { + border-color: #c4382d; +} + .u-nav button.icon { align-items: center; display: inline-flex; diff --git a/u-navigator.plg b/u-navigator.plg index 8f63d42..671bd42 100644 --- a/u-navigator.plg +++ b/u-navigator.plg @@ -1,7 +1,7 @@ - + @@ -24,7 +24,7 @@ https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package; -381ba21ef4664561c9a69ddada5b5b15 +b6edc78a8c0336b58273aa31ab69a2af