<?php
/**
* File: view_structure.php
* Fungsi: Melihat struktur file dan folder secara dinamis
* File ini akan mendeteksi lokasinya sendiri dan menampilkan struktur dari direktori tersebut
*/
// Mendapatkan direktori dimana file ini berada
$currentDir = __DIR__;
// Deteksi path relatif dari root website
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
$relativePath = str_replace($documentRoot, '', $currentDir);
$relativePath = str_replace('\\', '/', $relativePath);
// Parameter untuk kedalaman scan (default: semua level)
$maxDepth = isset($_GET['depth']) ? intval($_GET['depth']) : -1; // -1 = unlimited
$showHidden = isset($_GET['hidden']) ? true : false;
$searchTerm = isset($_GET['search']) ? trim($_GET['search']) : '';
// Tentukan direktori yang akan di-scan (bisa dari parameter atau default)
$scanDir = isset($_GET['dir']) ? $_GET['dir'] : $currentDir;
// Validasi direktori
if (!is_dir($scanDir)) {
die("Direktori tidak ditemukan: " . htmlspecialchars($scanDir));
}
// Security: pastikan direktori masih di dalam document root
$realScanDir = realpath($scanDir);
$realDocumentRoot = realpath($documentRoot);
if (strpos($realScanDir, $realDocumentRoot) !== 0) {
die("Akses ke direktori di luar document root tidak diizinkan.");
}
/**
* Fungsi untuk mendapatkan struktur direktori secara rekursif
*
* @param string $dir Path direktori
* @param int $level Level indentasi
* @param int $maxDepth Maksimum kedalaman
* @param bool $showHidden Tampilkan file hidden
* @param string $searchTerm Filter pencarian
* @return array Array struktur direktori
*/
function getDirectoryStructure($dir, $level = 0, $maxDepth = -1, $showHidden = false, $searchTerm = '') {
$structure = [];
// Cek kedalaman
if ($maxDepth !== -1 && $level >= $maxDepth) {
return $structure;
}
// Baca semua file dan folder dalam direktori
$items = scandir($dir);
if ($items === false) {
return $structure;
}
// Filter hidden files
if (!$showHidden) {
$items = array_filter($items, function($item) {
return $item[0] !== '.';
});
}
// Filter . dan ..
$items = array_filter($items, function($item) {
return $item !== '.' && $item !== '..';
});
// Filter berdasarkan pencarian
if (!empty($searchTerm)) {
$items = array_filter($items, function($item) use ($searchTerm) {
return stripos($item, $searchTerm) !== false;
});
}
// Sortir items (folder dulu, kemudian file)
$folders = [];
$files = [];
foreach ($items as $item) {
$fullPath = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($fullPath)) {
$folders[] = $item;
} else {
$files[] = $item;
}
}
sort($folders, SORT_STRING | SORT_FLAG_CASE);
sort($files, SORT_STRING | SORT_FLAG_CASE);
$items = array_merge($folders, $files);
// Proses setiap item
foreach ($items as $item) {
$fullPath = $dir . DIRECTORY_SEPARATOR . $item;
// Dapatkan timestamp modifikasi
$modifiedTime = filemtime($fullPath);
if (is_dir($fullPath)) {
// Ini adalah folder
$children = getDirectoryStructure($fullPath, $level + 1, $maxDepth, $showHidden, $searchTerm);
$structure[] = [
'type' => 'folder',
'name' => $item,
'path' => $fullPath,
'level' => $level,
'modified' => date('Y-m-d H:i:s', $modifiedTime),
'children' => $children,
'childCount' => count($children)
];
} else {
// Ini adalah file
$fileSize = filesize($fullPath);
$extension = pathinfo($item, PATHINFO_EXTENSION);
$structure[] = [
'type' => 'file',
'name' => $item,
'path' => $fullPath,
'level' => $level,
'size' => formatFileSize($fileSize),
'sizeBytes' => $fileSize,
'extension' => $extension,
'modified' => date('Y-m-d H:i:s', $modifiedTime),
'isImage' => in_array(strtolower($extension), ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']),
'isCode' => in_array(strtolower($extension), ['php', 'html', 'css', 'js', 'json', 'xml', 'sql'])
];
}
}
return $structure;
}
/**
* Fungsi untuk format ukuran file
*/
function formatFileSize($bytes) {
if ($bytes === 0) return '0 B';
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = floor(log($bytes, 1024));
$size = round($bytes / pow(1024, $i), 2);
return $size . ' ' . $units[$i];
}
/**
* Fungsi untuk mendapatkan warna berdasarkan ekstensi file
*/
function getFileColor($extension) {
$colors = [
'php' => '#4F5D95',
'html' => '#E34C26',
'htm' => '#E34C26',
'css' => '#563D7C',
'js' => '#F1E05A',
'json' => '#000000',
'txt' => '#333333',
'md' => '#083FA1',
'sql' => '#003B6F',
'xml' => '#006400',
'png' => '#A8B9C0',
'jpg' => '#A8B9C0',
'jpeg' => '#A8B9C0',
'gif' => '#A8B9C0',
'svg' => '#FF9900',
'ico' => '#FF6B6B',
'pdf' => '#B22222',
'zip' => '#D4A843',
'rar' => '#D4A843',
'tar' => '#D4A843',
'gz' => '#D4A843',
'exe' => '#8B0000',
'bat' => '#8B0000',
'sh' => '#8B0000',
'log' => '#808080',
'env' => '#2E8B57',
'yml' => '#2E8B57',
'yaml' => '#2E8B57'
];
return isset($colors[strtolower($extension)]) ? $colors[strtolower($extension)] : '#000000';
}
/**
* Fungsi untuk mendapatkan icon berdasarkan tipe file
*/
function getFileIcon($item) {
if ($item['type'] === 'folder') {
return '📁';
}
$ext = strtolower($item['extension']);
$icons = [
'php' => '🐘',
'html' => '🌐',
'htm' => '🌐',
'css' => '🎨',
'js' => '⚡',
'json' => '📋',
'txt' => '📄',
'md' => '📝',
'sql' => '🗄️',
'xml' => '📰',
'png' => '🖼️',
'jpg' => '🖼️',
'jpeg' => '🖼️',
'gif' => '🖼️',
'svg' => '🖼️',
'ico' => '🖼️',
'pdf' => '📕',
'zip' => '📦',
'rar' => '📦',
'exe' => '⚙️',
'bat' => '⚙️',
'sh' => '⚙️'
];
return isset($icons[$ext]) ? $icons[$ext] : '📄';
}
/**
* Fungsi untuk menampilkan struktur dalam bentuk tree dengan HTML
*/
function displayTree($structure, $prefix = '', $isLast = true, $level = 0) {
$lastIndex = count($structure) - 1;
$currentLevel = $level;
foreach ($structure as $index => $item) {
$isLastItem = ($index === $lastIndex);
$branch = $isLastItem ? '└── ' : '├── ';
$childPrefix = $isLastItem ? ' ' : '│ ';
// Tambahkan prefix dengan indentasi
$indent = '';
for ($i = 0; $i < $level; $i++) {
$indent .= '│ ';
}
// Tampilkan item
if ($item['type'] === 'folder') {
$icon = '📁';
echo '<div class="tree-item">';
echo $indent . $branch . '<span class="folder" onclick="toggleFolder(this)" data-path="' . htmlspecialchars($item['path']) . '">';
echo $icon . ' <strong>' . htmlspecialchars($item['name']) . '</strong>';
if ($item['childCount'] > 0) {
echo ' <span class="badge">' . $item['childCount'] . ' items</span>';
}
echo ' <span class="modified">🕐 ' . $item['modified'] . '</span>';
echo '</span>';
echo '</div>';
// Proses child folder dengan level yang lebih tinggi
if (!empty($item['children'])) {
echo '<div class="children" style="display: block;">';
displayTree($item['children'], '', $isLastItem, $level + 1);
echo '</div>';
}
} else {
$icon = getFileIcon($item);
$color = getFileColor($item['extension']);
echo '<div class="tree-item file-item">';
echo $indent . $branch . '<span style="color: ' . $color . ';">';
echo $icon . ' ' . htmlspecialchars($item['name']);
echo ' <span class="file-size">(' . $item['size'] . ')</span>';
echo ' <span class="modified">🕐 ' . $item['modified'] . '</span>';
echo '</span>';
echo '</div>';
}
}
}
/**
* Fungsi untuk menampilkan statistik
*/
function displayStatistics($structure, $path) {
$totalFiles = 0;
$totalFolders = 0;
$totalSize = 0;
$fileTypes = [];
$largeFiles = [];
function countItems($items, &$totalFiles, &$totalFolders, &$totalSize, &$fileTypes, &$largeFiles) {
foreach ($items as $item) {
if ($item['type'] === 'folder') {
$totalFolders++;
if (!empty($item['children'])) {
countItems($item['children'], $totalFiles, $totalFolders, $totalSize, $fileTypes, $largeFiles);
}
} else {
$totalFiles++;
$totalSize += $item['sizeBytes'];
$ext = $item['extension'];
if (!isset($fileTypes[$ext])) {
$fileTypes[$ext] = 0;
}
$fileTypes[$ext]++;
// File besar (> 1MB)
if ($item['sizeBytes'] > 1024 * 1024) {
$largeFiles[] = $item;
}
}
}
}
countItems($structure, $totalFiles, $totalFolders, $totalSize, $fileTypes, $largeFiles);
echo '<div class="stats">';
echo '<h3>📊 Statistik</h3>';
echo '<div class="stats-grid">';
echo '<div class="stat-item"><span class="stat-label">📁 Total Folder</span><span class="stat-value">' . number_format($totalFolders) . '</span></div>';
echo '<div class="stat-item"><span class="stat-label">📄 Total File</span><span class="stat-value">' . number_format($totalFiles) . '</span></div>';
echo '<div class="stat-item"><span class="stat-label">💾 Total Ukuran</span><span class="stat-value">' . formatFileSize($totalSize) . '</span></div>';
echo '<div class="stat-item"><span class="stat-label">📂 Direktori</span><span class="stat-value" style="font-size: 0.8em;">' . htmlspecialchars($path) . '</span></div>';
echo '</div>';
// Tipe file terbanyak
if (!empty($fileTypes)) {
arsort($fileTypes);
echo '<div class="file-types">';
echo '<h4>🔝 Top Ekstensi File</h4>';
echo '<ul>';
$count = 0;
foreach ($fileTypes as $ext => $countType) {
if ($count++ >= 10) break;
$extDisplay = empty($ext) ? 'tanpa ekstensi' : '.' . $ext;
$color = getFileColor($ext);
echo '<li><span style="color: ' . $color . ';">' . $extDisplay . '</span>: ' . $countType . ' file</li>';
}
echo '</ul>';
echo '</div>';
}
// File besar
if (!empty($largeFiles)) {
echo '<div class="large-files">';
echo '<h4>📦 File Besar (>1MB)</h4>';
echo '<ul>';
usort($largeFiles, function($a, $b) {
return $b['sizeBytes'] - $a['sizeBytes'];
});
foreach (array_slice($largeFiles, 0, 10) as $file) {
echo '<li>' . htmlspecialchars($file['name']) . ' (' . $file['size'] . ')</li>';
}
echo '</ul>';
echo '</div>';
}
echo '</div>';
}
// Proses utama
$structure = getDirectoryStructure($scanDir, 0, $maxDepth, $showHidden, $searchTerm);
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Explorer - <?php echo basename($scanDir); ?></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f0f2f5;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #e8ecf1;
}
h1 {
color: #1a2332;
font-size: 24px;
}
.path-info {
background: #f8f9fa;
padding: 12px 18px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 14px;
word-break: break-all;
border-left: 4px solid #4a90d9;
}
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin: 15px 0;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.controls input, .controls select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.controls button {
padding: 8px 16px;
background: #4a90d9;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.controls button:hover {
background: #357abd;
}
.controls label {
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
}
.tree-container {
background: #fafbfc;
border: 1px solid #e8ecf1;
border-radius: 8px;
padding: 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.8;
overflow-x: auto;
max-height: 600px;
overflow-y: auto;
}
.tree-item {
white-space: nowrap;
}
.folder {
cursor: pointer;
color: #1a2332;
font-weight: 500;
}
.folder:hover {
background: #e8ecf1;
border-radius: 3px;
padding: 0 4px;
}
.children {
padding-left: 20px;
}
.badge {
background: #4a90d9;
color: white;
font-size: 10px;
padding: 2px 8px;
border-radius: 10px;
margin-left: 5px;
}
.file-size {
color: #888;
font-size: 0.85em;
}
.modified {
color: #999;
font-size: 0.8em;
margin-left: 10px;
}
.stats {
margin-top: 25px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 15px 0;
}
.stat-item {
background: white;
padding: 12px;
border-radius: 6px;
border: 1px solid #e8ecf1;
}
.stat-label {
display: block;
font-size: 12px;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.stat-value {
display: block;
font-size: 18px;
font-weight: 600;
color: #1a2332;
margin-top: 5px;
}
.file-types, .large-files {
margin-top: 15px;
}
.file-types ul, .large-files ul {
list-style: none;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 5px;
}
.file-types li, .large-files li {
padding: 5px 10px;
background: white;
border-radius: 4px;
border: 1px solid #e8ecf1;
font-size: 13px;
}
.breadcrumb {
padding: 10px 0;
color: #666;
font-size: 14px;
}
.breadcrumb a {
color: #4a90d9;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
.footer-actions {
margin-top: 20px;
display: flex;
gap: 10px;
justify-content: center;
}
.footer-actions a {
padding: 10px 20px;
background: #4a90d9;
color: white;
border-radius: 5px;
text-decoration: none;
}
.footer-actions a:hover {
background: #357abd;
}
@media (max-width: 768px) {
.container {
padding: 15px;
}
.controls {
flex-direction: column;
}
.header {
flex-direction: column;
align-items: stretch;
}
.tree-container {
font-size: 12px;
max-height: 400px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📂 File Explorer</h1>
<div class="path-info">
📍 <?php echo htmlspecialchars($scanDir); ?>
</div>
</div>
<div class="breadcrumb">
<?php
$parts = explode('/', str_replace('\\', '/', $scanDir));
$currentPath = '';
foreach ($parts as $index => $part) {
$currentPath .= $part . '/';
if ($index == count($parts) - 1) {
echo '<strong>' . htmlspecialchars($part) . '</strong>';
} else {
echo '<a href="?dir=' . urlencode(rtrim($currentPath, '/')) . '">' . htmlspecialchars($part) . '</a> / ';
}
}
?>
</div>
<form method="GET" class="controls">
<input type="text" name="search" placeholder="🔍 Cari file/folder..." value="<?php echo htmlspecialchars($searchTerm); ?>">
<select name="depth">
<option value="-1" <?php echo $maxDepth == -1 ? 'selected' : ''; ?>>Semua Level</option>
<option value="0" <?php echo $maxDepth == 0 ? 'selected' : ''; ?>>Level 0</option>
<option value="1" <?php echo $maxDepth == 1 ? 'selected' : ''; ?>>Level 1</option>
<option value="2" <?php echo $maxDepth == 2 ? 'selected' : ''; ?>>Level 2</option>
<option value="3" <?php echo $maxDepth == 3 ? 'selected' : ''; ?>>Level 3</option>
<option value="4" <?php echo $maxDepth == 4 ? 'selected' : ''; ?>>Level 4</option>
<option value="5" <?php echo $maxDepth == 5 ? 'selected' : ''; ?>>Level 5</option>
</select>
<label>
<input type="checkbox" name="hidden" value="1" <?php echo $showHidden ? 'checked' : ''; ?>>
Tampilkan Hidden
</label>
<button type="submit">🔍 Tampilkan</button>
<button type="button" onclick="window.location.href=''">🔄 Reset</button>
</form>
<?php if (empty($structure)): ?>
<div style="padding: 20px; text-align: center; color: #999;">
<?php if (!empty($searchTerm)): ?>
Tidak ada hasil untuk pencarian "<?php echo htmlspecialchars($searchTerm); ?>"
<?php else: ?>
Folder kosong atau tidak ada item yang ditemukan.
<?php endif; ?>
</div>
<?php else: ?>
<div class="tree-container" id="treeContainer">
<?php displayTree($structure); ?>
</div>
<?php displayStatistics($structure, $scanDir); ?>
<?php endif; ?>
<div class="footer-actions">
<a href="?">🔄 Refresh</a>
<a href="#" onclick="window.print(); return false;">🖨️ Print</a>
<a href="#" onclick="exportTree(); return false;">📥 Export Tree</a>
</div>
</div>
<script>
// Fungsi toggle folder
function toggleFolder(element) {
const children = element.closest('.tree-item').nextElementSibling;
if (children && children.classList.contains('children')) {
if (children.style.display === 'none') {
children.style.display = 'block';
element.innerHTML = element.innerHTML.replace('📂', '📁');
} else {
children.style.display = 'none';
element.innerHTML = element.innerHTML.replace('📁', '📂');
}
}
}
// Fungsi export tree
function exportTree() {
const treeContainer = document.getElementById('treeContainer');
const text = treeContainer.innerText;
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'tree_structure.txt';
a.click();
URL.revokeObjectURL(url);
}
// Auto expand folder yang memiliki child
document.addEventListener('DOMContentLoaded', function() {
// Semua folder yang memiliki child akan otomatis terbuka
document.querySelectorAll('.folder').forEach(function(element) {
const children = element.closest('.tree-item').nextElementSibling;
if (children && children.classList.contains('children')) {
const badge = element.querySelector('.badge');
if (badge && parseInt(badge.textContent) > 0) {
// Biarkan terbuka secara default
children.style.display = 'block';
}
}
});
});
</script>
</body>
</html>
Link Insturmen dan Penilaian Akreditasi Perpustakaan
link instrumen akreditasi perpustakaan : https://drive.google.com/drive/folders/1jU1Ixil-JEJ66_hEFfXYhioiHkqloTya link
21
Aug