📄 Source: selector.php
<?php
$dir = isset($_GET['dir']) ? $_GET['dir'] : '';
$full_path = __DIR__ . '/' . $dir;
// Security: prevent directory traversal
if (strpos($full_path, __DIR__) !== 0 || !is_dir($full_path)) {
die('Invalid directory');
}
// Allowed file types
$allowed_extensions = ['php', 'html', 'htm', 'pdf', 'txt', 'md', 'json', 'xml', 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'css', 'js'];
// Get theme
$theme = isset($_COOKIE['dashboard_theme']) ? $_COOKIE['dashboard_theme'] : 'light';
if (isset($_GET['theme']) && in_array($_GET['theme'], ['light', 'dark'])) {
$theme = $_GET['theme'];
setcookie('dashboard_theme', $theme, time() + (365 * 24 * 60 * 60), '/');
$redirect_url = 'selector.php?dir=' . urlencode($dir);
header("Location: " . $redirect_url);
exit;
}
// Handle AJAX refresh request
if (isset($_GET['ajax'])) {
header('Content-Type: application/json');
$items = scandir($full_path);
$directories = [];
$web_files = [];
foreach ($items as $item) {
if ($item == '.' || $item == '..') continue;
$item_path = $full_path . '/' . $item;
if (is_dir($item_path)) {
$directories[] = $item;
} elseif (in_array(pathinfo($item, PATHINFO_EXTENSION), $allowed_extensions)) {
$web_files[] = $item;
}
}
sort($directories);
sort($web_files);
echo json_encode([
'directories' => $directories,
'web_files' => $web_files,
'current_dir' => $dir
]);
exit;
}
// Scan directory
$items = scandir($full_path);
$directories = [];
$web_files = [];
foreach ($items as $item) {
if ($item == '.' || $item == '..') continue;
$item_path = $full_path . '/' . $item;
if (is_dir($item_path)) {
$directories[] = $item;
} elseif (in_array(pathinfo($item, PATHINFO_EXTENSION), $allowed_extensions)) {
$web_files[] = $item;
}
}
sort($directories);
sort($web_files);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Browser - <?php echo htmlspecialchars($dir); ?></title>
<link rel="stylesheet" href="style.css">
<style>
.file-browser {
background: var(--card-bg);
border-radius: 15px;
padding: 30px;
margin-top: 20px;
}
.breadcrumb {
margin-bottom: 20px;
padding: 15px;
background: var(--secondary-bg);
border-radius: 8px;
}
.breadcrumb a {
color: var(--accent-color);
text-decoration: none;
}
.file-item-actions {
display: flex;
gap: 10px;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
.quick-launch {
background: var(--secondary-bg);
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
}
h2 {
color: var(--accent-color);
margin-bottom: 15px;
}
.refresh-btn {
background: var(--secondary-bg);
border: 1px solid var(--border-color);
padding: 6px 12px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
color: var(--text-color);
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 5px;
}
.refresh-btn:hover {
background: var(--accent-color);
color: white;
}
.refresh-btn.spinning {
animation: spin 0.5s ease-in-out;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
background: #4caf50;
color: white;
padding: 12px 20px;
border-radius: 8px;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.toast.error {
background: #f44336;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
</style>
</head>
<body class="<?php echo $theme; ?>">
<div class="container">
<div class="header">
<div class="header-top">
<div>
<h1>📂 File Browser</h1>
<div class="subtitle"><?php echo htmlspecialchars($dir); ?></div>
</div>
<div style="display: flex; gap: 10px;">
<button class="refresh-btn" onclick="refreshFolder()" id="refreshBtn">
🔄 Refresh
</button>
<a href="?theme=<?php echo $theme === 'dark' ? 'light' : 'dark'; ?>&dir=<?php echo urlencode($dir); ?>" class="theme-toggle" style="text-decoration: none; display: inline-block; line-height: 34px;">
<?php echo $theme === 'dark' ? '☀️ Light Mode' : '🌙 Dark Mode'; ?>
</a>
</div>
</div>
</div>
<div class="file-browser" id="fileBrowser">
<div class="breadcrumb">
<a href="index.php">🏠 Dashboard</a> /
<?php
$parts = explode('/', $dir);
$path = '';
foreach ($parts as $part) {
if ($part == '') continue;
$path .= $part;
echo '<a href="selector.php?dir=' . urlencode($path) . '">' . htmlspecialchars($part) . '</a> / ';
$path .= '/';
}
?>
</div>
<?php if (!empty($web_files)): ?>
<div class="quick-launch">
<span>🎯 Quick Launch:</span>
<select id="quickLaunch" onchange="if(this.value) window.location.href='<?php echo htmlspecialchars($dir); ?>/'+this.value">
<option value="">-- Select file to open --</option>
<?php foreach ($web_files as $file): ?>
<option value="<?php echo htmlspecialchars($file); ?>">
<?php
$ext = pathinfo($file, PATHINFO_EXTENSION);
$icon = '📄';
if ($ext == 'php') $icon = '🐘';
elseif ($ext == 'pdf') $icon = '📑';
elseif (in_array($ext, ['jpg','jpeg','png','gif'])) $icon = '🖼️';
elseif ($ext == 'html' || $ext == 'htm') $icon = '🌐';
echo $icon . ' ' . htmlspecialchars($file);
?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<h2>📁 Directories</h2>
<div class="file-list" id="directoriesList">
<div class="file-item">
<span class="file-name">📁 ..</span>
<?php if ($dir == ''): ?>
<a href="index.php" class="btn btn-sm">⬆️ Go to Dashboard</a>
<?php else:
$parent = dirname($dir);
if ($parent == '.') $parent = '';
?>
<a href="selector.php?dir=<?php echo urlencode($parent); ?>" class="btn btn-sm">⬆️ Parent Directory</a>
<?php endif; ?>
</div>
<?php foreach ($directories as $subdir): ?>
<div class="file-item">
<span class="file-name">📁 <?php echo htmlspecialchars($subdir); ?>/</span>
<a href="selector.php?dir=<?php echo urlencode($dir . '/' . $subdir); ?>" class="btn btn-sm">🔓 Open</a>
</div>
<?php endforeach; ?>
</div>
<?php if (!empty($web_files)): ?>
<h2 style="margin-top: 30px;">📄 Files</h2>
<div class="file-list" id="webFilesList">
<?php foreach ($web_files as $file):
$ext = pathinfo($file, PATHINFO_EXTENSION);
$icon = '📄';
if ($ext == 'php') $icon = '🐘';
elseif ($ext == 'pdf') $icon = '📑';
elseif (in_array($ext, ['jpg','jpeg','png','gif'])) $icon = '🖼️';
elseif ($ext == 'html' || $ext == 'htm') $icon = '🌐';
elseif ($ext == 'txt') $icon = '📝';
elseif ($ext == 'css') $icon = '🎨';
elseif ($ext == 'js') $icon = '📜';
?>
<div class="file-item file-item-actions">
<span class="file-name">
<?php echo $icon; ?>
<?php echo htmlspecialchars($file); ?>
</span>
<div>
<a href="<?php echo htmlspecialchars($dir . '/' . $file); ?>" class="btn btn-sm" target="_blank">▶ Open</a>
<a href="?view=<?php echo urlencode($file); ?>&dir=<?php echo urlencode($dir); ?>" class="btn btn-sm btn-outline">📄 View Source</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php
// Show source code if requested
if (isset($_GET['view'])) {
$view_file = $_GET['view'];
$view_path = $full_path . '/' . $view_file;
if (file_exists($view_path)) {
echo '<div class="container" style="margin-top: 20px;">
<div class="file-browser">
<h2>📄 Source: ' . htmlspecialchars($view_file) . '</h2>
<pre style="background: var(--secondary-bg); padding: 15px; border-radius: 8px; overflow-x: auto; font-family: monospace; font-size: 13px;"><code>' . htmlspecialchars(file_get_contents($view_path)) . '</code></pre>
<a href="selector.php?dir=' . urlencode($dir) . '" class="btn">← Back</a>
</div>
</div>';
}
}
?>
<script>
async function refreshFolder() {
const refreshBtn = document.getElementById('refreshBtn');
const currentDir = '<?php echo addslashes($dir); ?>';
refreshBtn.classList.add('spinning');
try {
const response = await fetch('?ajax=1&dir=' + encodeURIComponent(currentDir));
const data = await response.json();
const directoriesList = document.getElementById('directoriesList');
const parentItem = directoriesList.children[0];
directoriesList.innerHTML = '';
directoriesList.appendChild(parentItem);
data.directories.forEach(subdir => {
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<span class="file-name">📁 ${escapeHtml(subdir)}/</span>
<a href="selector.php?dir=${encodeURIComponent(currentDir + '/' + subdir)}" class="btn btn-sm">🔓 Open</a>
`;
directoriesList.appendChild(item);
});
const webFilesList = document.getElementById('webFilesList');
if (webFilesList) {
webFilesList.innerHTML = '';
data.web_files.forEach(file => {
const ext = file.split('.').pop();
let icon = '📄';
if (ext == 'php') icon = '🐘';
else if (ext == 'pdf') icon = '📑';
else if (['jpg','jpeg','png','gif'].includes(ext)) icon = '🖼️';
else if (ext == 'html' || ext == 'htm') icon = '🌐';
else if (ext == 'txt') icon = '📝';
const item = document.createElement('div');
item.className = 'file-item file-item-actions';
item.innerHTML = `
<span class="file-name">${icon} ${escapeHtml(file)}</span>
<div>
<a href="${encodeURIComponent(currentDir + '/' + file)}" class="btn btn-sm" target="_blank">▶ Open</a>
<a href="?view=${encodeURIComponent(file)}&dir=${encodeURIComponent(currentDir)}" class="btn btn-sm btn-outline">📄 View Source</a>
</div>
`;
webFilesList.appendChild(item);
});
}
showToast('✅ Folder refreshed!');
} catch (error) {
console.error('Refresh failed:', error);
showToast('❌ Refresh failed', true);
} finally {
setTimeout(() => refreshBtn.classList.remove('spinning'), 500);
}
}
function showToast(message, isError = false) {
const toast = document.createElement('div');
toast.className = 'toast';
if (isError) toast.classList.add('error');
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
const quickLaunch = document.getElementById('quickLaunch');
if (quickLaunch) {
quickLaunch.addEventListener('change', function() {
if (this.value) {
window.location.href = '<?php echo htmlspecialchars($dir); ?>/' + this.value;
}
});
}
</script>
</body>
</html>
← Back