-
Notifications
You must be signed in to change notification settings - Fork 84
Fix #2500: File Manager UI/UX improvements #2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
bc0ea9a
dd1200f
1b6373b
2fd4b7c
c9a0eda
0286f5d
b7a3d4d
f48d839
f50a44b
68b4ef8
de5d602
978dec1
29c7fcc
c67c851
3404739
7a5109c
169bada
6aec7e8
3cd1b74
0d6be50
bdaee26
1e211e2
b3679d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| <? | ||
| $docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'); | ||
| require_once "$docroot/webGui/include/Helpers.php"; | ||
| require_once "$docroot/plugins/dynamix/include/PopularDestinations.php"; | ||
|
|
||
| // add translations | ||
| $_SERVER['REQUEST_URI'] = ''; | ||
|
|
@@ -57,13 +58,13 @@ function quoted($name) {return is_array($name) ? implode(' ',array_map('escape', | |
| chown($file,'nobody'); | ||
| chmod($file,0666); | ||
| } | ||
| $file = file_get_contents($local); | ||
| $targetFile = file_get_contents($local); | ||
| if ($_POST['cancel']==1) { | ||
| delete_file($file); | ||
| delete_file($targetFile); | ||
| die('stop'); | ||
| } | ||
| if (file_put_contents($file,base64_decode($_POST['data']),FILE_APPEND)===false) { | ||
| delete_file($file); | ||
| if (file_put_contents($targetFile,base64_decode($_POST['data']),FILE_APPEND)===false) { | ||
| delete_file($targetFile); | ||
| die('error'); | ||
| } | ||
| die(); | ||
|
|
@@ -109,11 +110,13 @@ function quoted($name) {return is_array($name) ? implode(' ',array_map('escape', | |
| $file = '/var/tmp/file.manager.jobs'; | ||
| $rows = file_exists($file) ? file($file,FILE_IGNORE_NEW_LINES) : []; | ||
| $job = 1; | ||
| for ($x = 0; $x < count($rows); $x+=9) { | ||
| $data = parse_ini_string(implode("\n",array_slice($rows,$x,9))); | ||
| $task = $data['task']; | ||
| $source = explode("\r",$data['source']); | ||
| $target = $data['target']; | ||
| foreach ($rows as $row) { | ||
| if (empty($row)) continue; | ||
| $data = json_decode($row, true); | ||
| if (!$data) continue; | ||
| $task = $data['task'] ?? ''; | ||
| $source = explode("\r",$data['source'] ?? ''); | ||
| $target = $data['target'] ?? ''; | ||
| $more = count($source) > 1 ? " (".sprintf("and %s more",count($source)-1).") " : ""; | ||
| $jobs[] = '<i id="queue_'.$job.'" class="fa fa-fw fa-square-o blue-text job" onclick="selectOne(this.id,false)"></i>'._('Job')." [".sprintf("%'.04d",$job++)."] - $task ".$source[0].$more.($target ? " --> $target" : ""); | ||
| } | ||
|
|
@@ -134,49 +137,92 @@ function quoted($name) {return is_array($name) ? implode(' ',array_map('escape', | |
| $jobs = '/var/tmp/file.manager.jobs'; | ||
| $start = '0'; | ||
| if (file_exists($jobs)) { | ||
| exec("sed -n '2,9 p' $jobs > $active"); | ||
| exec("sed -i '1,9 d' $jobs"); | ||
| $start = filesize($jobs) > 0 ? '2' : '1'; | ||
| if ($start=='1') delete_file($jobs); | ||
| // read first JSON line from jobs file and write to active | ||
| $lines = file($jobs, FILE_IGNORE_NEW_LINES); | ||
| if (!empty($lines)) { | ||
| // Skip invalid JSON entries (loop until we find valid JSON or run out of entries) | ||
| while (!empty($lines)) { | ||
| $data = json_decode($lines[0], true); | ||
| if ($data) { | ||
| // Valid JSON found, use it | ||
| break; | ||
| } | ||
| // Invalid JSON, remove this entry and try next | ||
| array_shift($lines); | ||
| } | ||
|
|
||
| if (empty($lines)) { | ||
| // No valid JSON entries found | ||
| delete_file($jobs); | ||
| die('0'); | ||
| } | ||
|
|
||
| file_put_contents($active, $lines[0]); | ||
| // remove first line from jobs file | ||
| array_shift($lines); | ||
| if (count($lines) > 0) { | ||
| file_put_contents($jobs, implode("\n", $lines)."\n"); | ||
| $start = '2'; | ||
| } else { | ||
| delete_file($jobs); | ||
| $start = '1'; | ||
| } | ||
| } | ||
| } | ||
| die($start); | ||
| case 'undo': | ||
| $jobs = '/var/tmp/file.manager.jobs'; | ||
| $undo = '0'; | ||
| if (file_exists($jobs)) { | ||
| $rows = array_reverse(explode(',',$_POST['row'])); | ||
| $rows = explode(',',$_POST['row']); | ||
| $lines = file($jobs, FILE_IGNORE_NEW_LINES); | ||
| foreach ($rows as $row) { | ||
| $end = $row + 8; | ||
| exec("sed -i '$row,$end d' $jobs"); | ||
| $line_number = $row - 1; // Convert 1-based job number to 0-based array index | ||
| if (isset($lines[$line_number])) { | ||
| unset($lines[$line_number]); | ||
| } | ||
| } | ||
| if (count($lines) > 0) { | ||
| file_put_contents($jobs, implode("\n", $lines)."\n"); | ||
| $undo = '2'; | ||
| } else { | ||
| delete_file($jobs); | ||
| $undo = '1'; | ||
| } | ||
|
Comment on lines
+177
to
191
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check how the undo case is called from the frontend
rg -n "mode.*undo" --type=php -A5 -B2 emhttp/plugins/dynamix/Repository: unraid/webgui Length of output: 39 🏁 Script executed: #!/bin/bash
# Search for input validation or sanitization of $_POST['row'] in Control.php
rg -n "\$_POST\['row'\]" --type=php -B5 -A10 emhttp/plugins/dynamix/Repository: unraid/webgui Length of output: 39 🏁 Script executed: #!/bin/bash
# Check if there are any numeric validation functions used in Control.php
rg -n "is_numeric|filter_var|ctype_digit|intval" --type=php emhttp/plugins/dynamix/include/Control.phpRepository: unraid/webgui Length of output: 101 🏁 Script executed: #!/bin/bash
# Get broader context of the undo functionality in Control.php
rg -n "undo\s*=" --type=php -B10 -A5 emhttp/plugins/dynamix/include/Control.phpRepository: unraid/webgui Length of output: 938 🏁 Script executed: #!/bin/bash
# Search for where the 'undo' mode is triggered from frontend
rg -n "undo" --type=html --type=js emhttp/plugins/dynamix/ -B2 -A2Repository: unraid/webgui Length of output: 50371 🏁 Script executed: #!/bin/bash
# Check if there's any validation of row parameter earlier in the script
head -100 emhttp/plugins/dynamix/include/Control.php | rg -n "row|POST"Repository: unraid/webgui Length of output: 493 Validate The 1-based to 0-based conversion is correct, but 🤖 Prompt for AI Agents |
||
| $undo = filesize($jobs) > 0 ? '2' : '1'; | ||
| if ($undo=='1') delete_file($jobs); | ||
| } | ||
| die($undo); | ||
| case 'read': | ||
| $active = '/var/tmp/file.manager.active'; | ||
| $read = file_exists($active) ? json_encode(parse_ini_file($active)) : ''; | ||
| $read = file_exists($active) ? file_get_contents($active) : ''; | ||
| die($read); | ||
| case 'file': | ||
| $active = '/var/tmp/file.manager.active'; | ||
| $jobs = '/var/tmp/file.manager.jobs'; | ||
| $data[] = 'action="'.($_POST['action']??'').'"'; | ||
| $data[] = 'title="'.rawurldecode($_POST['title']??'').'"'; | ||
| $data[] = 'source="'.htmlspecialchars_decode(rawurldecode($_POST['source']??'')).'"'; | ||
| $data[] = 'target="'.rawurldecode($_POST['target']??'').'"'; | ||
| $data[] = 'H="'.(empty($_POST['hdlink']) ? '' : 'H').'"'; | ||
| $data[] = 'sparse="'.(empty($_POST['sparse']) ? '' : '--sparse').'"'; | ||
| $data[] = 'exist="'.(empty($_POST['exist']) ? '--ignore-existing' : '').'"'; | ||
| $data[] = 'zfs="'.rawurldecode($_POST['zfs']??'').'"'; | ||
| $data = [ | ||
| 'action' => (int)($_POST['action'] ?? 0), | ||
| 'title' => rawurldecode($_POST['title'] ?? ''), | ||
| 'source' => htmlspecialchars_decode(rawurldecode($_POST['source'] ?? '')), | ||
| 'target' => htmlspecialchars_decode(rawurldecode($_POST['target'] ?? '')), | ||
| 'H' => empty($_POST['hdlink']) ? '' : 'H', | ||
| 'sparse' => empty($_POST['sparse']) ? '' : '--sparse', | ||
| 'exist' => empty($_POST['exist']) ? '--ignore-existing' : '', | ||
| 'zfs' => rawurldecode($_POST['zfs'] ?? '') | ||
| ]; | ||
| if (isset($_POST['task'])) { | ||
| // add task to queue | ||
| $task = rawurldecode($_POST['task']); | ||
| $data = "task=\"$task\"\n".implode("\n",$data)."\n"; | ||
| file_put_contents($jobs,$data,FILE_APPEND); | ||
| $data['task'] = rawurldecode($_POST['task']); | ||
| file_put_contents($jobs, json_encode($data)."\n", FILE_APPEND); | ||
| } else { | ||
| // start operation | ||
| file_put_contents($active,implode("\n",$data)); | ||
| file_put_contents($active, json_encode($data)); | ||
| } | ||
|
|
||
| // Update popular destinations for copy/move operations | ||
| // Action types: 3=copy file, 4=move file, 8=copy file (upload), 9=move file (upload) | ||
| if (in_array((int)$data['action'], [3, 4, 8, 9]) && !empty($data['target'])) { | ||
| updatePopularDestinations($data['target']); | ||
| } | ||
|
|
||
| die(); | ||
| } | ||
| ?> | ||
Uh oh!
There was an error while loading. Please reload this page.