Files
2026-10-18 15:55:34 +02:00

312 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../../includes/auth.php';
requireLogin('../../index.php');
require_once __DIR__ . '/../../config/config.php';
require_once __DIR__ . '/../../includes/workspace.php';
$sftp = initializeSFTP();
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
if (!isRemotePathWithin($currentPath, $defaultPath)) {
$currentPath = $defaultPath;
}
if (!$sftp->chdir($currentPath)) {
die("Failed to navigate to the selected folder: $currentPath");
}
$items = $sftp->nlist();
if ($items === false) {
http_response_code(500);
exit('Failed to list directory contents.');
}
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioExtensions = ['mp3', 'wav', 'm4a', 'flac', 'aac'];
$editableExtensions = ['txt', 'html', 'css', 'js', 'php', 'xml', 'json', 'md', 'csv', 'log', 'ini', 'conf', 'sh', 'bat', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp'];
$archiveExtensions = ['zip', 'rar', 'tar', 'gz', '7z', 'bz2', 'xz', 'tar.gz', 'tar.bz2', 'tar.xz'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Files · USB RAID</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="<?= workspaceAsset('../../', 'css/modern.css') ?>">
<link rel="stylesheet" href="<?= workspaceAsset('../../', 'css/workspace.css') ?>">
<link rel="stylesheet" href="<?= workspaceAsset('../../', 'content/ftp/css/index.css') ?>">
<script src="<?= workspaceAsset('../../', 'js/theme.js') ?>"></script>
<script src="<?= workspaceAsset('../../', 'js/ui.js') ?>" defer></script>
</head>
<?php renderWorkspaceStart([
'root' => '../../',
'active' => 'files',
'section' => 'Files',
'eyebrow' => 'Your remote storage',
'title' => 'All files',
'description' => 'Everything in its place. Browse, manage and transfer your files.',
'connected' => true,
]); ?>
<nav class="pathbar" aria-label="Directory path"><a href="index.php" title="Home directory">⌂ <span>Home</span></a><?php
$relativePath = trim(substr($currentPath, strlen(rtrim($defaultPath, '/'))), '/');
$crumbPath = rtrim($defaultPath, '/');
foreach (array_filter(explode('/', $relativePath), 'strlen') as $segment):
$crumbPath .= '/' . $segment;
?><span class="path-separator" aria-hidden="true">/</span><a href="?path=<?= urlencode($crumbPath) ?>"><?= htmlspecialchars($segment) ?></a><?php endforeach; ?><span class="path-label" title="<?= htmlspecialchars($currentPath) ?>"><?= htmlspecialchars($currentPath) ?></span></nav>
<section class="file-panel" aria-label="File browser">
<div class="browser-toolbar"><label class="search-field"><span aria-hidden="true">⌕</span><input id="fileSearch" type="search" placeholder="Search this folder…" aria-label="Search this folder"><kbd>/</kbd></label><select id="typeFilter" class="form-select" aria-label="Filter by type"><option value="all">All types</option><option value="folder">Folders</option><option value="file">Files</option></select></div>
<div class="file-content">
<!-- Action Buttons -->
<div class="action-buttons">
<?php if (hasPermission('upPer')) { ?>
<button type="button" class="btn" data-dialog="createFileModal">+ New file</button>
<button type="button" class="btn btn-primary" onclick="document.getElementById('fileInput').click()">↑ Upload files</button>
<button type="button" class="btn" data-dialog="createDirModal">+ New folder</button>
<button type="button" class="btn btn-info" onclick="document.getElementById('dirInput').click()">Upload folder</button>
<input type="file" id="fileInput" multiple style="display: none;" onchange="handleFileSelect(event)">
<input type="file" id="dirInput" webkitdirectory directory multiple style="display: none;" onchange="handleFileSelect(event)">
<?php } ?>
</div>
<!-- Modal Forms -->
<!-- Create Directory Dialog -->
<dialog class="modal" id="createDirModal" tabindex="-1" aria-labelledby="createDirModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createDirModalLabel">Create New Directory</h5>
<button type="button" class="btn-close" data-dialog-close aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="dirName" class="form-label">Directory Name</label>
<input type="text" class="form-control" id="dirName" autofocus placeholder="Enter directory name">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dialog-close>Cancel</button>
<button type="button" class="btn btn-primary" onclick="createDirectory()">Create</button>
</div>
</div>
</div>
</dialog>
<!-- Create File Dialog -->
<dialog class="modal" id="createFileModal" tabindex="-1" aria-labelledby="createFileModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createFileModalLabel">Create New File</h5>
<button type="button" class="btn-close" data-dialog-close aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="fileName" class="form-label">File Name (with extension)</label>
<input type="text" class="form-control" id="fileName" autofocus placeholder="example.txt">
<div class="form-text">Supported extensions: .txt, .html, .css, .js, .php, etc.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dialog-close>Cancel</button>
<button type="button" class="btn btn-primary" onclick="createFile()">Create</button>
</div>
</div>
</div>
</dialog>
<!-- Rename Confirmation Dialog -->
<dialog class="modal" id="renameModal" tabindex="-1" aria-labelledby="renameModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="renameModalLabel">Rename Item</h5>
<button type="button" class="btn-close" data-dialog-close aria-label="Close"></button>
</div>
<form method="POST" action="rename.php">
<div class="modal-body">
<div class="mb-3">
<label for="originalName" class="form-label">Current Name:</label>
<input type="text" class="form-control" id="originalName" disabled>
<input type="hidden" id="fullPath" name="path">
<input type="hidden" id="isDirectory" name="isDirectory">
</div>
<div class="mb-3">
<label for="newName" class="form-label">New Name:</label>
<input type="text" class="form-control" id="newName" autofocus name="newName" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dialog-close>Cancel</button>
<button type="submit" class="btn btn-primary">Rename</button>
</div>
</form>
</div>
</div>
</dialog>
<!-- End of Modal Forms -->
<?php if (hasPermission('upPer')) { ?>
<div class="dropzone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)">
<span class="upload-symbol" aria-hidden="true">↑</span><span><strong>Drop files or folders here</strong><small>Or use the upload buttons to browse your device</small></span>
</div>
<?php } ?>
<div class="file-table-scroll"><table class="file-table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Size</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<?php
$directories = [];
$files = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
if ($sftp->is_dir($item)) {
$directories[] = $item;
} else {
$files[] = $item;
}
}
// Sort directories and files alphabetically
sort($directories, SORT_STRING | SORT_FLAG_CASE);
sort($files, SORT_STRING | SORT_FLAG_CASE);
if ($currentPath !== $defaultPath) : ?>
<tr>
<td colspan="3"><a class="text-danger" href="?path=<?= urlencode(remoteDirname($currentPath)) ?>"><b>↑ Parent folder</b></a></td>
</tr>
<?php endif;
foreach ($directories as $directory) : ?>
<tr data-file-name="<?= htmlspecialchars($directory) ?>" data-kind="folder">
<td><span class="file-icon folder-icon" aria-hidden="true">▰</span>
<a href="?path=<?= urlencode(rtrim($currentPath, '/') . '/' . $directory) ?>"><?= htmlspecialchars($directory) ?></a></td><td class="size-cell">—</td>
<td>
<?php if (hasPermission('delPer')) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (hasPermission('downPer')) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
</form>
<?php endif; ?>
<?php if (hasPermission('upPer')) : ?>
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($directory)) ?>', '<?= htmlspecialchars(addslashes(rtrim($currentPath, '/') . '/' . $directory)) ?>', true)">Rename</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach;
foreach ($files as $file) : ?>
<?php
$fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
$isPdf = $fileExtension === 'pdf';
$isImage = in_array($fileExtension, $imageExtensions);
$isVideo = in_array($fileExtension, $videoExtensions);
$isAudio = in_array($fileExtension, $audioExtensions);
$isMedia = $isImage || $isVideo || $isAudio;
$isEditable = in_array($fileExtension, $editableExtensions);
$isArchive = in_array($fileExtension, $archiveExtensions);
if (!$isArchive && strpos($file, '.tar.') !== false) {
$isArchive = true;
}
?>
<tr data-file-name="<?= htmlspecialchars($file) ?>" data-kind="file">
<td><span class="file-icon" aria-hidden="true">▤</span>
<?php if ($isMedia): ?>
<a class="text-info-emphasis" href="view.php?file=<?= urlencode(rtrim($currentPath, '/') . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<?php if ($isImage): ?>
<span class="badge bg-success rounded-pill">Image</span>
<?php elseif ($isVideo): ?>
<span class="badge bg-primary rounded-pill">Video</span>
<?php elseif ($isAudio): ?>
<span class="badge bg-info rounded-pill">Audio</span>
<?php endif; ?>
<?php elseif ($isEditable): ?>
<a class="text-warning-emphasis" href="open.php?file=<?= urlencode(rtrim($currentPath, '/') . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php elseif ($isPdf): ?>
<a class="text-info-emphasis" href="pdf.php?file=<?= urlencode(rtrim($currentPath, '/') . '/' . $file) ?>&type=pdf">
<?= htmlspecialchars($file) ?>
</a>
<span class="badge bg-danger rounded-pill">PDF</span>
<?php else: ?>
<?= htmlspecialchars($file) ?>
<span class="badge bg-light text-dark rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php endif; ?>
</td>
<td>
<span class="text"><?= formatBytes($sftp->stat(rtrim($currentPath, '/') . '/' . $file)['size']) ?></span>
</td>
<td>
<?php if (hasPermission('delPer')) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (hasPermission('downPer')) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
</form>
<?php endif; ?>
<?php if (hasPermission('upPer')) : ?>
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($file)) ?>', '<?= htmlspecialchars(addslashes(rtrim($currentPath, '/') . '/' . $file)) ?>')">Rename</button>
<?php endif; ?>
<?php if ($isArchive && hasPermission('upPer')) : ?>
<form method="POST" action="extract.php" style="display:inline;">
<input type="hidden" name="archive" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
<button type="submit" class="btn btn-info mt-1 mb-1">Extract</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table></div>
<div id="emptySearch" class="empty-state" hidden><span aria-hidden="true">⌕</span><h3>No files to show</h3><p>Try another search or upload files to this folder.</p></div>
<div class="browser-status"><span id="fileCount" role="status"></span><span>Folders first · A–Z</span></div>
</div>
</section>
<?php renderWorkspaceEnd(); ?>
<script>
// Pass PHP variables to JavaScript
const currentPath = <?= json_encode($currentPath, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
</script>
<script src="<?= workspaceAsset('../../', 'content/ftp/js/index.js') ?>"></script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>