Edit Code · SiloDrive
Back to CMS
Open Page
Source Code Viewer
13925 bytes loaded
<?php declare(strict_types=1); $sets = [ 'icons' => [ 'label' => 'Silo UI Icons', 'path' => '/var/www/html/silocloud/assets/icons', 'web' => '/silocloud/assets/icons', 'note' => 'Canonical application icon library for Drive, viewers, shell controls, file cards, and toolbars.' ], 'images' => [ 'label' => 'Silo Images', 'path' => '/var/www/html/silocloud/assets/images', 'web' => '/silocloud/assets/images', 'note' => 'Logo, image, hero, preview, branding, and visual assets.' ], 'incoming' => [ 'label' => 'Shared Incoming Uploads', 'path' => '/var/www/html/silocloud/shared-assets/incoming', 'web' => '/silocloud/shared-assets/incoming', 'note' => 'Recently uploaded ZIPs and loose files before promotion.' ], 'latest_import' => [ 'label' => 'Latest Import Work Folder', 'path' => '/var/www/html/silocloud/shared-assets/temp/latest-import', 'web' => '/silocloud/shared-assets/temp/latest-import', 'note' => 'Temporary extracted icon pack inspection folder.' ], ]; function h($v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); } function human_size(string $file): string { if (!is_file($file)) return '—'; $size = filesize($file); if ($size === false) return '—'; $units = ['B','KB','MB','GB']; $i = 0; while ($size >= 1024 && $i < count($units)-1) { $size /= 1024; $i++; } return round($size, $i === 0 ? 0 : 1) . ' ' . $units[$i]; } function asset_type(string $file): string { $name = strtolower(basename($file)); $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); if (str_contains($name, 'folder')) return 'folder'; if (str_contains($name, 'file-pdf') || $ext === 'pdf') return 'pdf'; if (str_contains($name, 'file-word')) return 'document'; if (str_contains($name, 'file-video') || str_contains($name, 'video')) return 'video'; if (str_contains($name, 'image') || str_contains($name, 'camera')) return 'image'; if (str_contains($name, 'code') || str_contains($name, 'file-code')) return 'code'; if (str_contains($name, 'upload') || str_contains($name, 'download') || str_contains($name, 'cloud')) return 'cloud'; if (str_contains($name, 'play') || str_contains($name, 'pause') || str_contains($name, 'stop') || str_contains($name, 'volume')) return 'media'; if (str_contains($name, 'user') || str_contains($name, 'address') || str_contains($name, 'contact')) return 'identity'; if (str_contains($name, 'comment') || str_contains($name, 'envelope')) return 'communication'; if (str_contains($name, 'database') || str_contains($name, 'server') || str_contains($name, 'hdd')) return 'infrastructure'; if (str_contains($name, 'shield') || str_contains($name, 'lock') || str_contains($name, 'anchor') || str_contains($name, 'chain')) return 'security'; if (str_contains($name, 'window') || str_contains($name, 'expand') || str_contains($name, 'ellipsis')) return 'window'; if (str_contains($name, 'silo') || str_contains($name, 'logo')) return 'brand'; return $ext ?: 'asset'; } function collect_assets(array $set): array { $dir = $set['path']; if (!is_dir($dir)) return []; $files = glob($dir . '/*.{svg,png,jpg,jpeg,webp,ico,zip}', GLOB_BRACE) ?: []; usort($files, function($a, $b) { return filemtime($b) <=> filemtime($a); }); $out = []; foreach ($files as $file) { $base = basename($file); $out[] = [ 'name' => $base, 'path' => $file, 'web' => $set['web'] . '/' . rawurlencode($base), 'ext' => strtolower(pathinfo($file, PATHINFO_EXTENSION)), 'size' => human_size($file), 'mtime' => date('Y-m-d H:i', filemtime($file) ?: time()), 'type' => asset_type($file), ]; } return $out; } $allCounts = []; foreach ($sets as $key => $set) { $allCounts[$key] = count(collect_assets($set)); } ?> <!doctype html> <html lang="en"> <head><link rel="icon" type="image/png" href="/silocloud/assets/images/cloud.png"> <meta charset="utf-8"> <title>SiloCloud Drive Asset Inventory</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> :root{ --primary:#1267e8; --bg:#f7f9fd; --surface:#fff; --panel:#fbfcff; --line:#e4e9f2; --text:#101828; --muted:#667085; --green:#16a34a; --red:#d92d20; --shadow:0 12px 28px rgba(15,23,42,.07); } *{box-sizing:border-box} body{ margin:0; background:var(--bg); color:var(--text); font-family:Arial,Helvetica,sans-serif; } .header{ position:sticky; top:0; z-index:5; background:rgba(255,255,255,.94); backdrop-filter:blur(10px); border-bottom:1px solid var(--line); padding:18px 26px; } .header-row{ display:flex; align-items:center; gap:18px; } .logo{ width:42px; height:42px; border-radius:12px; background:linear-gradient(135deg,#1267e8,#8ec5ff); } h1{ margin:0; font-size:26px; letter-spacing:-.7px; } .sub{ margin:4px 0 0; color:var(--muted); font-size:13px; } .searchbar{ margin-left:auto; display:flex; gap:10px; align-items:center; } .searchbar input,.searchbar select{ height:40px; border:1px solid var(--line); border-radius:8px; padding:0 12px; font-size:13px; background:white; } .searchbar input{width:320px} .wrap{padding:22px 26px 50px} .stats{ display:grid; grid-template-columns:repeat(4,minmax(170px,1fr)); gap:14px; margin-bottom:22px; } .stat{ background:var(--surface); border:1px solid var(--line); border-radius:10px; padding:16px; box-shadow:var(--shadow); } .stat strong{ display:block; font-size:24px; margin-bottom:4px; } .stat span{ color:var(--muted); font-size:12px; } .section{ background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow); margin-bottom:22px; overflow:hidden; } .section-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:18px; padding:18px 20px; border-bottom:1px solid var(--line); } .section h2{ margin:0; font-size:18px; } .section p{ margin:5px 0 0; color:var(--muted); font-size:13px; } .path{ font-family:Consolas,monospace; font-size:12px; color:#344054; background:#f3f6fb; border:1px solid var(--line); border-radius:8px; padding:8px 10px; white-space:nowrap; } .grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:14px; padding:18px; } .card{ border:1px solid var(--line); background:var(--panel); border-radius:10px; padding:12px; min-height:178px; display:flex; flex-direction:column; } .preview{ height:62px; display:grid; place-items:center; margin-bottom:10px; background:white; border:1px solid var(--line); border-radius:8px; } .preview img{ max-width:76px; max-height:42px; object-fit:contain; } .zipbox{ color:var(--red); font-weight:900; font-size:22px; } .name{ font-weight:800; font-size:12px; line-height:1.35; word-break:break-word; min-height:34px; } .meta{ display:flex; gap:6px; flex-wrap:wrap; margin:8px 0; } .badge{ font-size:10px; padding:3px 7px; border-radius:99px; background:#eef2f7; color:#475467; font-weight:800; } .badge.type{ background:#eaf2ff; color:#1267e8; } .url{ margin-top:auto; font-size:11px; color:var(--muted); background:#fff; border:1px solid var(--line); border-radius:7px; padding:7px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-family:Consolas,monospace; } .actions{ display:flex; gap:7px; margin-top:8px; } .actions a,.actions button{ flex:1; height:30px; border:1px solid var(--line); background:#fff; color:var(--text); border-radius:7px; font-size:11px; font-weight:800; text-decoration:none; display:grid; place-items:center; cursor:pointer; } .actions button{ font-family:inherit; } .hidden{display:none!important} .empty{ padding:22px; color:var(--muted); } @media(max-width:900px){ .header-row{display:block} .searchbar{margin:14px 0 0} .searchbar input{width:100%} .stats{grid-template-columns:1fr 1fr} } </style> <style> .cms-return-bar{ position:sticky; top:0; z-index:9999; height:54px; background:#ffffff; border-bottom:1px solid #e3e8f0; display:flex; align-items:center; justify-content:space-between; padding:0 18px; font-family:Arial,Helvetica,sans-serif; box-shadow:0 6px 18px rgba(15,23,42,.05); } .cms-return-bar strong{ color:#101828; font-size:15px; } .cms-return-actions{ display:flex; gap:10px; align-items:center; } .cms-return-btn{ height:34px; display:inline-flex; align-items:center; justify-content:center; padding:0 12px; border-radius:9px; background:#2563eb; color:#fff !important; text-decoration:none; font-size:13px; font-weight:800; } .cms-return-btn.secondary{ background:#eef2f7; color:#111827 !important; } </style> </head> <body> <div class="cms-return-bar"> <strong>SiloDrive Asset Inventory</strong> <div class="cms-return-actions"> <a class="cms-return-btn secondary" href="/silocloud/apps/portal/">Workspace Portal</a> <a class="cms-return-btn" href="/silocloud/apps/cms/">Return to App Registry</a> </div> </div> <header class="header"> <div class="header-row"> <div class="logo"></div> <div> <h1>SiloCloud Drive Asset Inventory</h1> <div class="sub">Icons, images, incoming uploads, and latest extracted UI packs for Drive and viewer microservices.</div> </div> <div class="searchbar"> <input id="search" placeholder="Search icons, file names, actions, file types..."> <select id="typeFilter"> <option value="">All types</option> <option value="folder">Folder</option> <option value="pdf">PDF</option> <option value="document">Document</option> <option value="video">Video</option> <option value="image">Image</option> <option value="code">Code</option> <option value="cloud">Cloud</option> <option value="media">Media</option> <option value="identity">Identity</option> <option value="communication">Communication</option> <option value="infrastructure">Infrastructure</option> <option value="security">Security</option> <option value="window">Window</option> <option value="brand">Brand</option> </select> </div> </div> </header> <main class="wrap"> <section class="stats"> <?php foreach ($sets as $key => $set): ?> <div class="stat"> <strong><?=h($allCounts[$key])?></strong> <span><?=h($set['label'])?></span> </div> <?php endforeach; ?> </section> <?php foreach ($sets as $key => $set): ?> <?php $assets = collect_assets($set); ?> <section class="section" data-section="<?=h($key)?>"> <div class="section-head"> <div> <h2><?=h($set['label'])?> <span style="color:var(--muted);font-size:13px">(<?=count($assets)?>)</span></h2> <p><?=h($set['note'])?></p> </div> <div class="path"><?=h($set['path'])?></div> </div> <?php if (!$assets): ?> <div class="empty">No assets found in this directory.</div> <?php else: ?> <div class="grid"> <?php foreach ($assets as $asset): ?> <?php $isVisual = in_array($asset['ext'], ['svg','png','jpg','jpeg','webp','ico'], true); ?> <article class="card asset-card" data-name="<?=h(strtolower($asset['name']))?>" data-type="<?=h($asset['type'])?>" data-ext="<?=h($asset['ext'])?>"> <div class="preview"> <?php if ($asset['ext'] === 'zip'): ?> <div class="zipbox">ZIP</div> <?php elseif ($isVisual): ?> <img src="<?=h($asset['web'])?>" alt=""> <?php else: ?> <div class="zipbox"><?=h(strtoupper($asset['ext']))?></div> <?php endif; ?> </div> <div class="name"><?=h($asset['name'])?></div> <div class="meta"> <span class="badge type"><?=h($asset['type'])?></span> <span class="badge"><?=h(strtoupper($asset['ext']))?></span> <span class="badge"><?=h($asset['size'])?></span> </div> <div class="url" title="<?=h($asset['web'])?>"><?=h($asset['web'])?></div> <div class="actions"> <a href="<?=h($asset['web'])?>" target="_blank">Open</a> <button type="button" onclick="copyAssetUrl('<?=h($asset['web'])?>')">Copy URL</button> </div> </article> <?php endforeach; ?> </div> <?php endif; ?> </section> <?php endforeach; ?> </main> <script> const search = document.getElementById('search'); const typeFilter = document.getElementById('typeFilter'); function applyFilters(){ const q = (search.value || '').toLowerCase().trim(); const type = typeFilter.value; document.querySelectorAll('.asset-card').forEach(card => { const name = card.dataset.name || ''; const cardType = card.dataset.type || ''; const ext = card.dataset.ext || ''; const matchText = !q || name.includes(q) || cardType.includes(q) || ext.includes(q); const matchType = !type || cardType === type; card.classList.toggle('hidden', !(matchText && matchType)); }); } function copyAssetUrl(url){ navigator.clipboard.writeText(url).then(() => { alert('Copied: ' + url); }).catch(() => { prompt('Copy this URL:', url); }); } search.addEventListener('input', applyFilters); typeFilter.addEventListener('change', applyFilters); </script> </body> </html>