Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions emhttp/plugins/dynamix/Browse.page
Original file line number Diff line number Diff line change
Expand Up @@ -874,8 +874,13 @@ function doAction(action, title, id) {
dfm_fileManager('start');
$.post('/webGui/include/Control.php',{mode:'file',action:action,title:encodeURIComponent(title),source:encodeURIComponent(source),target:encodeURIComponent(target),hdlink:hdlink,sparse:dfm.window.find('#dfm_sparse').val(),exist:dfm.window.find('#dfm_exist').val(),zfs:encodeURIComponent(zfs)},function(){
$.post('/webGui/include/Control.php',{mode:'read'},function(data){
dfm_read = JSON.parse(data);
dfm_read.action = parseInt(dfm_read.action);
try {
dfm_read = data ? JSON.parse(data) : {};
dfm_read.action = parseInt(dfm_read.action);
} catch(e) {
console.error('Failed to parse Control.php response:', e, data);
dfm_read = {};
}
});
});
},
Expand Down Expand Up @@ -1158,8 +1163,13 @@ function doActions(action, title) {
dfm_fileManager('start');
$.post('/webGui/include/Control.php',{mode:'file',action:action,title:encodeURIComponent(title),source:encodeURIComponent(source.join('\r')),target:encodeURIComponent(target),hdlink:hdlink,sparse:dfm.window.find('#dfm_sparse').val(),exist:dfm.window.find('#dfm_exist').val(),zfs:encodeURIComponent(zfs.join('\r'))},function(){
$.post('/webGui/include/Control.php',{mode:'read'},function(data){
dfm_read = JSON.parse(data);
dfm_read.action = parseInt(dfm_read.action);
try {
dfm_read = data ? JSON.parse(data) : {};
dfm_read.action = parseInt(dfm_read.action);
} catch(e) {
console.error('Failed to parse Control.php response:', e, data);
dfm_read = {};
}
});
});
},
Expand Down
82 changes: 38 additions & 44 deletions emhttp/plugins/dynamix/BrowseButton.page
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,31 @@ function dfm_showProgress(data) {
try {
let parsed = JSON.parse(data);

// Handle search results (action 15 with results array)
if (parsed.action === 15 && Array.isArray(parsed.results)) {
if (parsed.results.length > 0) {
// Build HTML as pairs of location/path elements
let html = '';
for (let result of parsed.results) {
html += '<dt>' + dfm_escapeHTML(result.location) + '</dt>';
html += '<dd>' + dfm_escapeHTML(result.path.dfm_wedge(80)) + '</dd>';
}
// Remove all existing result dt/dd pairs (keep only the first 2 dt/dd which are the form fields)
let $dl = dfm.window.find('dl');
$dl.find('dt:gt(1), dd:gt(1)').remove();
// Append the new results
$dl.append(html);
dfm.window.find('#dfm_files').html(parsed.results.length+" "+"_(files)_");
// Clear stale progress text
dfm.window.find('.dfm_text').html('');
} else {
dfm.window.find('.dfm_loc').html('');
dfm.window.find('.dfm_text').html('_(No files found)_');
dfm.window.find('#dfm_files').html('0 '+"_(files)_");
}
return parsed.results.length;
}

// Universal JSON format: {action: int, text: [text0, text1]}
// text[0] = file/main text, text[1] = progress info (optional)
if (parsed.action !== undefined && Array.isArray(parsed.text)) {
Expand Down Expand Up @@ -212,54 +237,23 @@ function dfm_showProgress(data) {
}

// Build footer text (progress info only)
let footerText = parsed.text[1] || parsed.text[0] || '';
dfm_footer('write', footerText, $icon);
dfm.previous = footerText;
// Show footer for actions with actual progress: delete (1,6), copy (3,8), move (4,9), search (15)
// No footer for quick operations: create (0), rename (2,7), chown (11), chmod (12)
let showFooter = [1, 3, 4, 6, 8, 9, 15].includes(parsed.action);
if (showFooter) {
let footerText = parsed.text[1] || parsed.text[0] || '';
dfm_footer('write', footerText, $icon);
dfm.previous = footerText;
}

return 0;

}
} catch(e) {
// Not JSON, fallback to old string parsing
}

// Legacy string parsing for non-migrated operations
let file = null;
let text = data.split('\n');
let line = text[0].split('... ');
let strict = /^mnt|^boot/;
let footer = false;
if (text[0] == '#cat#') {
let loc = [], cat = [];
for (let i=1,row; row=text[i]; i++) {
if (!row) continue;
row = row.split('\0');
loc.push(row[0]);
cat.push(row[1].dfm_wedge(80));
}
if (cat.length > 0) {
dfm.window.find('.dfm_loc').html(loc.join('<br>'));
dfm.window.find('.dfm_text').html(cat.join('<br>'));
dfm.window.find('#dfm_files').html(loc.length+" "+"_(files)_");
}
return cat.length;
} else if (text.length == 1) {
text = text[0].dfm_wedge(80);
footer = text.indexOf("_(Searching)_") != -1;
} else {
if (strict.test(text[1])) {
file = text[1];
text = dfm.previous;
} else {
file = line[1];
text = text[1].split(/\s+/);
text = "<i class='fa fa-circle-o-notch fa-spin dfm'></i>_(Completed)_: "+text[1]+",&nbsp;&nbsp;_(Speed)_: "+text[2]+",&nbsp;&nbsp;_(ETA)_: "+text[3];
dfm.previous = text;
footer = true;
}
// Not JSON, log error
console.error('Failed to parse status as JSON:', e, data);
return 0;
}
if (file == null || strict.test(file)) dfm.window.find('.dfm_text').html((file?line[0]+'... /'+dfm_escapeHTML(file.dfm_wedge())+'<br>':'')+text);
if (footer) dfm_footer('write',text);
return 0;
}

function dfm_fileManager(action) {
Expand Down Expand Up @@ -420,7 +414,7 @@ nchan_filemanager.on('message', function(msg) {
let data = $.parseJSON(msg);
if (data.error) {
dfm_fileManager('stop');
dfm.window.find('.dfm_text').addClass('orange-text').html(data.error);
dfm.window.find('.dfm_text').addClass('orange-text').css('white-space', 'pre-line').text(data.error);
dfm.window.find('#dfm_target').prop('disabled',false);
dfm.window.find('#dfm_sparse').prop('disabled',false);
dfm.window.find('#dfm_exist').prop('disabled',false);
Expand Down
83 changes: 68 additions & 15 deletions emhttp/plugins/dynamix/nchan/file_manager
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ function parse_rsync_progress($status, $action_label, $reset = false) {
// note: -v is used to obtain the opposite of the progress line regex and to filter out empty lines
$file_line = exec("tac $status | grep -m1 -v -E '(^\$|^ .*[0-9]+%.*[0-9]+:[0-9]+:[0-9]+)' | tr -s ' ' || echo ''");
if ($file_line) {
$text[0] .= htmlspecialchars(mb_strimhalf($file_line, 70, '...'), ENT_QUOTES, 'UTF-8');
$text[0] .= mb_strimhalf($file_line, 70, '...');
}

return $text;
Expand Down Expand Up @@ -297,7 +297,8 @@ function update_translation($locale) {

function cat($file) {
global $null;
$cat = $set = [];
$results = [];
$set = [];
$rows = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (count($rows) > 0) {
natcasesort($rows);
Expand All @@ -313,12 +314,20 @@ function cat($file) {
}
foreach (array_diff($rows, $user) as $row) {
[$none, $root, $main] = explode('/',$row,4);
$cat[] = ($root == 'mnt' ? $main : ($root == 'boot' ? 'flash' : '---'))."\0".$row;
$results[] = [
'location' => ($root == 'mnt' ? $main : ($root == 'boot' ? 'flash' : '---')),
'path' => $row
];
}
$i = 0;
foreach ($user as $row) $cat[] = $set[++$i]."\0".$row;
foreach ($user as $row) {
$results[] = [
'location' => $set[++$i] ?? '---',
'path' => $row
];
}
}
return "#cat#\n".implode("\n",$cat)."\n";
return $results;
}

function escape($name) {return escapeshellarg(validname($name));}
Expand Down Expand Up @@ -388,8 +397,15 @@ while (true) {
$source = explode("\r", $source);
switch ($action) {
case 0: // create folder

// return status of running action
if (!empty($pid)) {
$reply['status'] = '<i class="fa fa-circle-o-notch fa-spin dfm"></i>'._('Creating').'...';
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Creating').'...']
]);

// start action
} else {
$dir = $source[0].'/'.$target;
exec("mkdir -pm0777 ".quoted($dir)." 1>$null 2>$error & echo $!", $pid);
Expand All @@ -403,7 +419,7 @@ while (true) {
if (!empty($pid)) {
$reply['status'] = json_encode([
'action' => $action,
'text' => [htmlspecialchars(mb_strimhalf(exec("tail -1 $status"), 70, '...'), ENT_QUOTES, 'UTF-8')]
'text' => [mb_strimhalf(exec("tail -1 $status"), 70, '...')]
]);

// start action
Expand All @@ -413,8 +429,15 @@ while (true) {
break;
case 2: // rename folder
case 7: // rename file

// return status of running action
if (!empty($pid)) {
$reply['status'] = '<i class="fa fa-circle-o-notch fa-spin dfm"></i>'._('Renaming').'...';
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Renaming').'...']
]);

// start action
} else {
$path = dirname($source[0]);
exec("mv -f ".quoted($source)." ".quoted("$path/$target")." 1>$null 2>$error & echo \$!", $pid);
Expand Down Expand Up @@ -463,7 +486,7 @@ while (true) {
if ($delete_empty_dirs === false) {
$reply['status'] = json_encode([
'action' => $action,
'text' => [htmlspecialchars(mb_strimhalf(exec("tail -1 $status"), 70, '...'), ENT_QUOTES, 'UTF-8')]
'text' => [mb_strimhalf(exec("tail -1 $status"), 70, '...')]
]);

// moving: progress
Expand Down Expand Up @@ -605,22 +628,46 @@ while (true) {
}
break;
case 11: // change owner

// return status of running action
if (!empty($pid)) {
$reply['status'] = '<i class="fa fa-circle-o-notch fa-spin dfm"></i>'._('Updating').'... '.exec("tail -2 $status | grep -Pom1 \"^.+ of '\\K[^']+\"");
$file_line = exec("tail -2 $status | grep -Pom1 \"^.+ of '\\K[^']+\"");
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Updating').'... '.mb_strimhalf($file_line, 70, '...')]
]);

// start action
} else {
exec("chown -Rfv $target ".quoted($source)." 1>$status 2>$error & echo \$!", $pid);
}
break;
case 12: // change permission

// return status of running action
if (!empty($pid)) {
$reply['status'] = '<i class="fa fa-circle-o-notch fa-spin dfm"></i>'._('Updating').'... '.exec("tail -2 $status | grep -Pom1 \"^.+ of '\\K[^']+\"");
$file_line = exec("tail -2 $status | grep -Pom1 \"^.+ of '\\K[^']+\"");
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Updating').'... '.mb_strimhalf($file_line, 70, '...')]
]);

// start action
} else {
exec("chmod -Rfv $target ".quoted($source)." 1>$status 2>$error & echo \$!", $pid);
}
break;
case 15: // search

// return status of running action
if (!empty($pid)) {
$reply['status'] = '<i class="fa fa-circle-o-notch fa-spin dfm"></i>'._('Searching').'... '.exec("wc -l $status | grep -Pom1 '^[0-9]+'");
$count = exec("wc -l $status | grep -Pom1 '^[0-9]+'");
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Searching').'... '.$count]
]);

// start action
} else {
exec("find ".source($source)." -iname ".escapeshellarg($target)." 1>$status 2>$null & echo \$!", $pid);
}
Expand Down Expand Up @@ -648,10 +695,16 @@ while (true) {
$pid = pid_exists($pid);
} else {
if ($action != 15) {
$reply['status'] = _('Done');
$reply['status'] = json_encode([
'action' => $action,
'text' => [_('Done')]
]);
$reply['done'] = 1;
} else {
$reply['status'] = cat($status);
$reply['status'] = json_encode([
'action' => $action,
'results' => cat($status)
]);
$reply['done'] = 2;
}
if ($zfs) {
Expand All @@ -662,7 +715,7 @@ while (true) {
foreach ($datasets as $dataset) if (exec("ls --indicator-style=none /mnt/$dataset|wc -l")==0) exec("zfs destroy $dataset 2>/dev/null");
}
}
if (file_exists($error)) $reply['error'] = str_replace("\n","<br>", trim(file_get_contents($error)));
if (file_exists($error)) $reply['error'] = trim(file_get_contents($error));
delete_file($active, $pid_file, $status, $error);
unset($pid);
$delete_empty_dirs = null;
Expand Down
Loading