Skip to content

Commit

Permalink
File Manager updates
Browse files Browse the repository at this point in the history
- Show thumbnails for image files with user-selectable thumbnail size
- Fix bug in button enabling/disabling
- Allow viewing image files from subdirectories.  FPP will only work
  with the main images directory, but plugins can use subdirs if
  desired.
  • Loading branch information
cpinkham committed Nov 9, 2023
1 parent e04452d commit 33856d7
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 52 deletions.
131 changes: 108 additions & 23 deletions www/api/controllers/files.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,41 +75,54 @@ function files_rename()

/////////////////////////////////////////////////////////////////////////////
// GET /api/files/:DirName
function GetFiles()
{
function GetFilesHelper($dirName, $prefix = '') {
$files = array();

$dirName = params("DirName");
check($dirName, "dirName", __FUNCTION__);
$dirName = GetDirSetting($dirName);
if ($dirName == "") {
return json(array("status" => "Invalid Directory"));
}
if ($prefix != '')
$prefix .= '/';

// if ?nameOnly=1 was passed, then just array of names
if (isset($_GET['nameOnly']) && ($_GET['nameOnly'] == '1')) {
$rc = array();
foreach (scandir($dirName) as $fileName) {
if ($fileName != '.' && $fileName != '..') {
if (!preg_match("//u", $fileName)) {
$fileName = iconv("ISO-8859-1", 'UTF-8//TRANSLIT', $fileName);
if (is_dir($dirName . '/' . $fileName)) {
$rc = array_merge($rc, GetFilesHelper($dirName . '/' . $fileName, $prefix . $fileName));
} else {
if (!preg_match("//u", $fileName)) {
$fileName = iconv("ISO-8859-1", 'UTF-8//TRANSLIT', $fileName);
}
array_push($rc, $prefix . $fileName);
}
array_push($rc, $fileName);
}
}
if (strtolower(params("DirName")) == "logs") {
array_push($rc, "/var/log/messages");
array_push($rc, "/var/log/syslog");
}
return json($rc);
return $rc;
}

foreach (scandir($dirName) as $fileName) {
if ($fileName != '.' && $fileName != '..') {
if (!preg_match("//u", $fileName)) {
$fileName = iconv("ISO-8859-1", 'UTF-8//TRANSLIT', $fileName);
if (is_dir($dirName . '/' . $fileName)) {
$entries = GetFilesHelper($dirName . '/' . $fileName, $prefix . $fileName);
if (!count($entries)) {
$current = array();
$current["name"] = $prefix . $fileName;
$current["mtime"] = date('m/d/y h:i A', filemtime($dirName . '/' . $fileName));
$current["sizeBytes"] = 0;
$current["sizeHuman"] = 'Directory';

$entries = array($current);
}
$files = array_merge($files, $entries);
} else {
if (!preg_match("//u", $fileName)) {
$fileName = iconv("ISO-8859-1", 'UTF-8//TRANSLIT', $fileName);
}
GetFileInfo($files, $dirName, $fileName, $prefix);
}
GetFileInfo($files, $dirName, $fileName);
}
}

Expand All @@ -124,7 +137,23 @@ function GetFiles()

}

return json(array("status" => "ok", "files" => $files));
return $files;
}

function GetFiles()
{
$dirName = params("DirName");
check($dirName, "dirName", __FUNCTION__);
$dirName = GetDirSetting($dirName);
if ($dirName == "") {
return json(array("status" => "Invalid Directory"));
}

if (isset($_GET['nameOnly']) && ($_GET['nameOnly'] == '1')) {
return json(GetFilesHelper($dirName));
} else {
return json(array("status" => "ok", "files" => GetFilesHelper($dirName)));
}
}

function GetFile()
Expand Down Expand Up @@ -166,13 +195,13 @@ function GetFileImpl($dir, $filename, $lines, $play, $attach)
return;
}

if ($isLog && (substr($filename, 0, 9) == "_var_log_")) {
if ($isLog && (substr($filename, 0, 8) == "var/log/")) {
$dir = "/var/log";
$filename = substr($filename, 9);
$filename = substr($filename, 8);
}

if (!file_exists($dir . '/' . $filename)){
echo "File does not exist.";
echo "File $dir/$filename does not exist.";
return;
}

Expand Down Expand Up @@ -514,11 +543,13 @@ function DeleteFile()
$status = "File not found";
$dirName = params("DirName");
$dir = GetDirSetting($dirName);
$fileName = findFile($dir, params("Name"));
$fileName = findFile($dir, params(0));

$fullPath = "$dir/$fileName";

if ($dir == "") {
if (preg_match('/\/\.\.\//', $fileName)) {
$status = 'Cannot access parent directory';
} else if ($dir == "") {
$status = "Invalid Directory";
} else if (!file_exists($fullPath)) {
$status = "File Not Found";
Expand Down Expand Up @@ -698,15 +729,15 @@ function PostFile()
return json(array("status" => $status, "file" => $fileName, "dir" => $dirName, "written" => $size, "size" => $totalSize, "offset" => $offset));
}

function GetFileInfo(&$list, $dirName, $fileName)
function GetFileInfo(&$list, $dirName, $fileName, $prefix = '')
{
$fileFullName = $dirName . '/' . $fileName;
$filesize = real_filesize($fileFullName);
if (intval($filesize) < PHP_INT_MAX) {
$filesize = intval($filesize);
}
$current = array();
$current["name"] = $fileName;
$current["name"] = $prefix . $fileName;
$current["mtime"] = date('m/d/y h:i A', filemtime($fileFullName));
$current["sizeBytes"] = $filesize;
$current["sizeHuman"] = human_filesize($fileFullName);
Expand Down Expand Up @@ -738,3 +769,57 @@ function GetFileInfo(&$list, $dirName, $fileName)
}
array_push($list, $current);
}

function CreateDir()
{
$status = "Unable to create directory";
$dirName = params("DirName");
$subDir = params("SubDir");
$sanitized = sanitizeFilename($subDir);
$dir = GetDirSetting($dirName);
$fullPath = "$dir/$subDir";

if ($sanitized != $subDir) {
$status = "Invalid subdirectory name";
} else if ($dir == "") {
$status = "Invalid Directory";
} else if (file_exists($fullPath)) {
$status = "Subdirectory already exists";
} else {
if (mkdir($fullPath)) {
chmod($fullPath, 0755);
$status = "OK";
} else {
$status = "Unable to delete file";
}
}

return json(array("status" => $status, "subdir" => $subDir, "dir" => $dirName));
}

function DeleteDir()
{
$status = "SubDir not found";
$dirName = params("DirName");
$subDir = params("SubDir");
$sanitized = sanitizeFilename($subDir);
$dir = GetDirSetting($dirName);
$fullPath = "$dir/$subDir";

if ($sanitized != $subDir) {
$status = "Invalid subdirectory name";
} else if ($dir == "") {
$status = "Invalid Directory";
} else if (!file_exists($fullPath)) {
$status = "Subdirectory '$subDir' does not exist";
} else {
if (rmdir($fullPath)) {
$status = "OK";
} else {
$status = "Unable to delete subdirectory, does it contain any files?";
}
}

return json(array("status" => $status, "subdir" => $subDir, "dir" => $dirName));
}

5 changes: 4 additions & 1 deletion www/api/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
dispatch_post('/configfile/**', 'UploadConfigFile');
dispatch_delete('/configfile/**', 'DeleteConfigFile');

dispatch_post('/dir/:DirName/:SubDir', 'CreateDir');
dispatch_delete('/dir/:DirName/:SubDir', 'DeleteDir');

dispatch_get('/effects', 'effects_list');
dispatch_get('/effects/ALL', 'effects_list_ALL');

Expand All @@ -59,7 +62,7 @@
dispatch_post('/file/:DirName/copy/:source/:dest', 'files_copy');
dispatch_post('/file/:DirName/rename/:source/:dest', 'files_rename');
dispatch_get('/file/:DirName/**', 'GetFile');
dispatch_delete('/file/:DirName/:Name', 'DeleteFile');
dispatch_delete('/file/:DirName/**', 'DeleteFile');
dispatch_post('/file/:DirName', 'PatchFile');
dispatch_patch('/file/:DirName', 'PatchFile');
dispatch_post('/file/:DirName/:Name', 'PostFile');
Expand Down
80 changes: 54 additions & 26 deletions www/js/fpp.js
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,8 @@ function AddTableRowFromTemplate(table) {
function HandleTableRowMouseClick(event, row) {
if ((event.target.nodeName == 'INPUT') ||
(event.target.nodeName == 'TEXTAREA') ||
(event.target.nodeName == 'SELECT'))
(event.target.nodeName == 'SELECT') ||
(row.hasClass('unselectableRow')))
return;

event.preventDefault(); // prevent mouse move from highlighting text
Expand Down Expand Up @@ -3281,7 +3282,21 @@ function GetFiles(dir) {
detail = f.playtimeSeconds;
}

var tableRow = "<tr class='fileDetails' id='fileDetail_" + i + "'><td class ='fileName'>" + f.name.replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</td><td class='fileExtraInfo'>" + detail + "</td><td class ='fileTime'>" + f.mtime + "</td></tr>";
var thumbSize = 0;
if ((settings.hasOwnProperty('fileManagerThumbnailSize')) && (settings['fileManagerThumbnailSize'] > 0))
thumbSize = settings['fileManagerThumbnailSize'];

var tableRow = '';
if ((dir == 'Images') && (thumbSize > 0)) {
if (parseInt(f.sizeBytes) > 0) {
tableRow = "<tr class='fileDetails' id='fileDetail_" + i + "'><td class ='fileName'>" + f.name.replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</td><td class='fileExtraInfo'>" + detail + "</td><td class ='fileTime'>" + f.mtime + "</td><td><img style='display: block; max-width: " + thumbSize + "px; max-height: " + thumbSize + "px; width: auto; height: auto;' src='api/file/" + dir + "/" + f.name + "' onClick=\"ViewImage('" + f.name + "');\" /></td></tr>";
} else {
tableRow = "<tr class='fileDetails unselectableRow' id='fileDetail_" + i + "'><td class ='fileName'>" + f.name.replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</td><td class='fileExtraInfo'>" + detail + "</td><td class ='fileTime'>" + f.mtime + "</td><td>Empty Subdir</td></tr>";
}
} else {
tableRow = "<tr class='fileDetails' id='fileDetail_" + i + "'><td class ='fileName'>" + f.name.replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</td><td class='fileExtraInfo'>" + detail + "</td><td class ='fileTime'>" + f.mtime + "</td></tr>";
}

$('#tbl' + dir).append(tableRow);
++i;
});
Expand Down Expand Up @@ -4961,15 +4976,15 @@ function RenameFile(dir, file) {
}

function DownloadFile(dir, file) {
location.href = "api/file/" + dir + "/" + encodeURIComponent(file.replace(/\//g, '_'));
location.href = "api/file/" + dir + "/" + encodeURIComponent(file).replace('%2F','/');
}

function DownloadFiles(dir, files) {
if (files.length == 1) {
DownloadFile(dir, files[0]);
} else {
for (var i = 0; i < files.length; i++) {
window.open("api/file/" + dir + "/" + encodeURIComponent(files[i]));
window.open("api/file/" + dir + "/" + encodeURIComponent(files[i]).replace('%2F','/'));
}
}
}
Expand All @@ -4979,20 +4994,20 @@ function DownloadZip(dir) {
}

function ViewImage(file) {
var url = "api/file/Images/" + encodeURIComponent(file);
window.open(url, '_blank');
var url = "api/file/Images/" + encodeURIComponent(file).replace('%2F','/');
ViewFileImpl(url, file, "<center><a href='" + url + "' target='_blank'><img src='" + url + "' style='display: block; max-width: 700px; max-height: 500px; width: auto; height: auto;'></a><br>Click image to display full size.</center>");
}

function ViewFile(dir, file) {
var url = "api/file/" + dir + "/" + encodeURIComponent(file.replace(/\//g, '_'));
var url = "api/file/" + dir + "/" + encodeURIComponent(file).replace('%2F','/');
ViewFileImpl(url, file);
}
function TailFile(dir, file, lines) {
var url = "api/file/" + dir + "/" + encodeURIComponent(file.replace(/\//g, '_')) + "?tail=" + lines;
var url = "api/file/" + dir + "/" + encodeURIComponent(file).replace('%2F','/') + "?tail=" + lines;
//console.log(url);
ViewFileImpl(url, file);
}
function ViewFileImpl(url, file) {
function ViewFileImpl(url, file, html = '') {
var options = {
id: "fileViewerDialog",
title: "File Viewer: " + file,
Expand All @@ -5009,21 +5024,20 @@ function ViewFileImpl(url, file) {
}
};
DoModalDialog(options);
$.get(url, function (text) {
var ext = file.split('.').pop();
if (ext != "html")
$('#fileViewerText').html("<pre>" + text.replace(/</g, '&lt;').replace(/>/g, '&gt;') + "</pre>");
});
if (html == '') {
$.get(url, function (text) {
var ext = file.split('.').pop();
if (ext != "html")
$('#fileViewerText').html("<pre>" + text.replace(/</g, '&lt;').replace(/>/g, '&gt;') + "</pre>");
});
} else {
$('#fileViewerText').html(html);
}
}

function DeleteFile(dir, row, file, silent = false) {
if (file.indexOf("/") > -1) {
alert("You can not delete this file.");
return;
}

$.ajax({
url: "api/file/" + dir + "/" + encodeURIComponent(file),
url: "api/file/" + dir + "/" + encodeURIComponent(file).replace('%2F','/'),
type: 'DELETE'
}).done(function (data) {
if (data.status == "OK") {
Expand All @@ -5040,16 +5054,30 @@ function DeleteFile(dir, row, file, silent = false) {

function SetupSelectableTableRow(info) {
$('#' + info.tableName + ' > tbody').on('mousedown', 'tr', function (event, ui) {
$('#' + info.tableName + ' > tbody > tr').removeClass('fppTableSelectedEntry');
$(this).addClass('fppTableSelectedEntry');
var items = $('#' + info.tableName + ' > tbody > tr');
info.selected = items.index(this);
var enabledButtonState;
var disabledButtonState;

if ($(this).hasClass('fppTableSelectedEntry')) {
$(this).removeClass('fppTableSelectedEntry');

info.selected = -1;
enabledButtonState = 'disable';
disabledButtonState = 'enable';
} else {
$('#' + info.tableName + ' > tbody > tr').removeClass('fppTableSelectedEntry');
$(this).addClass('fppTableSelectedEntry');

var items = $('#' + info.tableName + ' > tbody > tr');
info.selected = items.index(this);
enabledButtonState = 'enable';
disabledButtonState = 'disable';
}

for (var i = 0; i < info.enableButtons.length; i++) {
SetButtonState('#' + info.enableButtons[i], "enable");
SetButtonState('#' + info.enableButtons[i], enabledButtonState);
}
for (var i = 0; i < info.disableButtons.length; i++) {
SetButtonState('#' + info.disableButtons[i], "disable");
SetButtonState('#' + info.disableButtons[i], disabledButtonState);
}
});

Expand Down
Loading

0 comments on commit 33856d7

Please sign in to comment.