Files
wevads-platform/scripts/sidebar-admin2.php
2026-02-26 04:53:11 +01:00

341 lines
14 KiB
PHP
Executable File

<?php
// Scan all server files
$excludeDirs = ['plugins','css','js','images','uploads','chrome','tmp','storage','assets','styles','videos','media','data','config','memes','artifacts','scripts','fonts','webfonts'];
$excludeFiles = ['index.php','.htaccess','click.php','lead.php','open.php','unsub.php'];
function scanAll($dir, $base = '', $exDirs = [], $exFiles = []) {
$files = [];
if (!is_dir($dir)) return $files;
foreach (scandir($dir) as $f) {
if ($f[0] === '.') continue;
$path = $dir . '/' . $f;
$rel = $base ? $base . '/' . $f : $f;
if (is_dir($path)) {
if (!in_array($f, $exDirs)) {
$files = array_merge($files, scanAll($path, $rel, $exDirs, $exFiles));
}
} else {
$ext = pathinfo($f, PATHINFO_EXTENSION);
if (in_array($ext, ['php','html']) && !in_array($f, $exFiles)) {
$files[] = ['name' => $f, 'path' => '/' . $rel];
}
}
}
return $files;
}
$allFiles = scanAll('/opt/wevads/public', '', $excludeDirs, $excludeFiles);
usort($allFiles, fn($a,$b) => strcmp($a['name'], $b['name']));
// Parse actual menu.html to get real structure
function parseMenu($file) {
$html = file_get_contents($file);
$menu = [];
// Remove comments
$html = preg_replace('/<!--.*?-->/s', '', $html);
// Split by nav-toggle to get each parent section
$parts = preg_split('/(<a[^>]*nav-toggle[^>]*>)/', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; $i < count($parts); $i += 2) {
if (!isset($parts[$i+1])) continue;
$section = $parts[$i] . $parts[$i+1];
// Get parent title
if (preg_match('/<span class="title"[^>]*>([^<]+)/', $section, $pm)) {
$parentTitle = trim($pm[1]);
// Get sub-menu section
$children = [];
if (preg_match('/<ul class="sub-menu">(.*?)(<\/ul>|<li class="nav-item)/s', $section, $sm)) {
$subMenu = $sm[1];
// Find all children links
preg_match_all('/href="([^"]+)"[^>]*>.*?<span class="title">([^<]+)/s', $subMenu, $cm, PREG_SET_ORDER);
foreach ($cm as $c) {
$url = trim($c[1]);
// Clean template syntax
$url = preg_replace('/\{echo $app\[.base_url.\]\}/', '', $url);
if (empty($url)) $url = '#';
$children[] = ['title' => trim($c[2]), 'url' => $url];
}
}
if (!empty($parentTitle)) {
$menu[] = ['title' => $parentTitle, 'children' => $children];
}
}
}
return $menu;
}
$menuStructure = parseMenu('/opt/wevads/app/views/includes/menu.html');
$menuJson = json_encode($menuStructure, JSON_UNESCAPED_UNICODE);
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Sidebar Admin - <?= count($allFiles) ?> fichiers | <?= count($menuStructure) ?> menus</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:#030308;color:#fff;min-height:100vh}
.header{display:flex;justify-content:space-between;align-items:center;padding:12px 20px;background:linear-gradient(135deg,rgba(0,255,255,0.08),rgba(191,0,255,0.08));border-bottom:1px solid rgba(0,255,255,0.2)}
.header h1{font-size:1.2em;color:#00ffff}
.stats{display:flex;gap:15px}
.stat{text-align:center}
.stat b{font-size:1.3em;color:#00ff88}
.stat small{display:block;font-size:0.65em;color:#667}
.btn{padding:8px 14px;border:none;border-radius:6px;cursor:pointer;font-weight:600;font-size:0.8em;display:inline-flex;align-items:center;gap:5px;text-decoration:none}
.btn-g{background:#00ff88;color:#000}
.btn-sec{background:transparent;border:1px solid rgba(0,255,255,0.3);color:#fff}
.main{display:grid;grid-template-columns:1fr 50px 1fr;height:calc(100vh - 55px)}
.panel{background:#0a0a15;display:flex;flex-direction:column;overflow:hidden}
.panel-head{padding:10px 12px;border-bottom:1px solid rgba(0,255,255,0.2);background:rgba(0,0,0,0.3);display:flex;justify-content:space-between;align-items:center}
.panel-head h2{font-size:0.9em;color:#00ffff}
.panel-body{flex:1;overflow-y:auto;padding:8px}
.search{width:100%;padding:8px 10px;background:rgba(0,0,0,0.5);border:1px solid rgba(0,255,255,0.2);border-radius:5px;color:#fff;margin-bottom:8px;font-size:0.8em}
.search:focus{outline:none;border-color:#00ffff}
.center{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:#050508;border-left:1px solid rgba(0,255,255,0.2);border-right:1px solid rgba(0,255,255,0.2)}
.tbtn{width:38px;height:38px;border:2px solid #00ffff;border-radius:50%;background:rgba(0,255,255,0.1);color:#00ffff;cursor:pointer;font-size:1em}
.tbtn:hover{background:#00ffff;color:#000}
.item{display:flex;align-items:center;padding:6px 8px;margin-bottom:3px;background:rgba(0,0,0,0.3);border:1px solid transparent;border-radius:5px;cursor:pointer;font-size:0.8em}
.item:hover{background:rgba(0,255,255,0.05);border-color:rgba(0,255,255,0.2)}
.item.selected{background:rgba(0,255,255,0.1);border-color:#00ffff}
.item-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.item-path{font-size:0.7em;color:#667;margin-left:8px}
.parent{background:rgba(0,255,255,0.08);border:1px solid rgba(0,255,255,0.25);font-weight:600;margin-top:8px}
.parent:first-child{margin-top:0}
.children{margin-left:15px;padding-left:8px;border-left:2px solid rgba(0,255,255,0.2);margin-bottom:6px}
.child{font-size:0.75em;padding:5px 8px}
.mbtn{width:22px;height:22px;border:none;border-radius:3px;background:rgba(0,255,255,0.1);color:#00ffff;cursor:pointer;margin-left:3px;font-size:0.65em}
.mbtn:hover{background:#00ffff;color:#000}
.mbtn.del{background:rgba(255,50,100,0.1);color:#ff3366}
.mbtn.del:hover{background:#ff3366;color:#fff}
.collapse-btn{background:none;border:none;color:#00ffff;cursor:pointer;margin-right:5px;font-size:0.8em}
::-webkit-scrollbar{width:5px}
::-webkit-scrollbar-track{background:#050508}
::-webkit-scrollbar-thumb{background:rgba(0,255,255,0.3);border-radius:3px}
</style>
</head>
<body>
<header class="header">
<h1>🎛️ Sidebar Admin</h1>
<div class="stats">
<div class="stat"><b><?= count($allFiles) ?></b><small>Fichiers</small></div>
<div class="stat"><b><?= count($menuStructure) ?></b><small>Menus</small></div>
</div>
<div style="display:flex;gap:6px">
<button class="btn btn-sec" onclick="location.reload()"><i class="fas fa-sync"></i></button>
<a href="/standalone-index.php" class="btn btn-sec"><i class="fas fa-list"></i> Index</a>
<button class="btn btn-sec" onclick="expandAll()"><i class="fas fa-expand"></i></button>
<button class="btn btn-sec" onclick="collapseAll()"><i class="fas fa-compress"></i></button>
<button class="btn btn-g" onclick="saveMenu()"><i class="fas fa-save"></i> Save</button>
</div>
</header>
<div class="main">
<div class="panel">
<div class="panel-head"><h2><i class="fas fa-folder-open"></i> Fichiers (<?= count($allFiles) ?>)</h2></div>
<div class="panel-body">
<input type="text" class="search" id="sF" placeholder="🔍 Rechercher fichier..." onkeyup="filterF()">
<div id="fList">
<?php foreach ($allFiles as $i => $f): ?>
<div class="item" data-i="<?= $i ?>" data-n="<?= htmlspecialchars(strtolower($f['name'])) ?>" data-p="<?= htmlspecialchars($f['path']) ?>" onclick="selF(this)">
<span class="item-name"><?= htmlspecialchars($f['name']) ?></span>
<span class="item-path"><?= htmlspecialchars($f['path']) ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="center">
<button class="tbtn" onclick="addToMenu()" title="Ajouter →"><i class="fas fa-chevron-right"></i></button>
<button class="tbtn" onclick="remFromMenu()" title="← Retirer"><i class="fas fa-chevron-left"></i></button>
<div style="font-size:0.6em;color:#667;text-align:center;margin-top:8px">Select<br>then click</div>
</div>
<div class="panel">
<div class="panel-head">
<h2><i class="fas fa-sitemap"></i> Menu (<?= count($menuStructure) ?>)</h2>
<button class="btn btn-sec" style="padding:4px 8px;font-size:0.7em" onclick="addParent()"><i class="fas fa-plus"></i></button>
</div>
<div class="panel-body">
<input type="text" class="search" id="sM" placeholder="🔍 Rechercher menu..." onkeyup="filterM()">
<div id="mList"></div>
</div>
</div>
</div>
<script>
let M = <?= $menuJson ?>;
let selFile = null;
let selMenu = null;
let collapsed = {};
function render() {
let h = '';
M.forEach((p, pi) => {
const isCollapsed = collapsed[pi];
h += `<div class="item parent" onclick="selParent(${pi})" id="p${pi}">
<button class="collapse-btn" onclick="event.stopPropagation();toggle(${pi})"><i class="fas fa-${isCollapsed ? 'plus' : 'minus'}"></i></button>
<span class="item-name">${p.title}</span>
<span class="item-path">${p.children ? p.children.length : 0} items</span>
<button class="mbtn" onclick="event.stopPropagation();addToP(${pi})" title="Add here"><i class="fas fa-plus"></i></button>
<button class="mbtn" onclick="event.stopPropagation();editP(${pi})"><i class="fas fa-edit"></i></button>
<button class="mbtn del" onclick="event.stopPropagation();delP(${pi})"><i class="fas fa-trash"></i></button>
</div>`;
if (p.children && p.children.length > 0 && !isCollapsed) {
h += '<div class="children">';
p.children.forEach((c, ci) => {
h += `<div class="item child" onclick="selChild(${pi},${ci})" id="c${pi}_${ci}">
<span class="item-name">${c.title}</span>
<span class="item-path">${c.url}</span>
<button class="mbtn" onclick="event.stopPropagation();editC(${pi},${ci})"><i class="fas fa-edit"></i></button>
<button class="mbtn del" onclick="event.stopPropagation();delC(${pi},${ci})"><i class="fas fa-trash"></i></button>
</div>`;
});
h += '</div>';
}
});
document.getElementById('mList').innerHTML = h;
}
function toggle(pi) { collapsed[pi] = !collapsed[pi]; render(); }
function expandAll() { collapsed = {}; render(); }
function collapseAll() { M.forEach((p, pi) => collapsed[pi] = true); render(); }
function filterF() {
const q = document.getElementById('sF').value.toLowerCase();
document.querySelectorAll('#fList .item').forEach(e => {
const n = e.dataset.n || '';
const p = e.dataset.p || '';
e.style.display = (n.includes(q) || p.includes(q)) ? 'flex' : 'none';
});
}
function filterM() {
const q = document.getElementById('sM').value.toLowerCase();
document.querySelectorAll('#mList .item').forEach(e => {
const t = e.querySelector('.item-name').textContent.toLowerCase();
e.style.display = t.includes(q) ? 'flex' : 'none';
});
}
function selF(el) {
document.querySelectorAll('.item').forEach(e => e.classList.remove('selected'));
el.classList.add('selected');
selFile = { name: el.querySelector('.item-name').textContent, path: el.dataset.p };
selMenu = null;
}
function selParent(pi) {
document.querySelectorAll('.item').forEach(e => e.classList.remove('selected'));
document.getElementById('p' + pi).classList.add('selected');
selMenu = { type: 'parent', pi: pi };
selFile = null;
}
function selChild(pi, ci) {
document.querySelectorAll('.item').forEach(e => e.classList.remove('selected'));
document.getElementById('c' + pi + '_' + ci).classList.add('selected');
selMenu = { type: 'child', pi: pi, ci: ci };
selFile = null;
}
function addToMenu() {
if (!selFile) { alert('Select a file first!'); return; }
if (!selMenu || selMenu.type !== 'parent') { alert('Select a parent menu!'); return; }
if (!M[selMenu.pi].children) M[selMenu.pi].children = [];
M[selMenu.pi].children.push({ title: selFile.name, url: selFile.path });
render();
alert('Added: ' + selFile.name);
selFile = null;
document.querySelectorAll('.item').forEach(e => e.classList.remove('selected'));
}
function addToP(pi) {
if (!selFile) { alert('Select a file first!'); return; }
if (!M[pi].children) M[pi].children = [];
M[pi].children.push({ title: selFile.name, url: selFile.path });
render();
alert('Added to ' + M[pi].title);
selFile = null;
document.querySelectorAll('.item').forEach(e => e.classList.remove('selected'));
}
function remFromMenu() {
if (!selMenu) { alert('Select a menu item!'); return; }
if (selMenu.type === 'parent') {
if (confirm('Delete "' + M[selMenu.pi].title + '" and all children?')) {
M.splice(selMenu.pi, 1);
render();
}
} else {
M[selMenu.pi].children.splice(selMenu.ci, 1);
render();
}
selMenu = null;
}
function addParent() {
const t = prompt('Parent menu name:');
if (t) { M.push({ title: t, children: [] }); render(); }
}
function editP(pi) {
const t = prompt('Title:', M[pi].title);
if (t) { M[pi].title = t; render(); }
}
function delP(pi) {
if (confirm('Delete "' + M[pi].title + '"?')) { M.splice(pi, 1); render(); }
}
function editC(pi, ci) {
const c = M[pi].children[ci];
const t = prompt('Title:', c.title);
if (t) {
c.title = t;
const u = prompt('URL:', c.url);
if (u) c.url = u;
render();
}
}
function delC(pi, ci) {
M[pi].children.splice(ci, 1);
render();
}
function saveMenu() {
let html = '';
M.forEach(p => {
const icon = p.title.includes('IA') ? 'fa-brain' : p.title.includes('Cloud') ? 'fa-cloud' : p.title.includes('Domain') ? 'fa-globe' : p.title.includes('Send') ? 'fa-envelope' : 'fa-folder';
html += '<li class="nav-item"><a href="javascript:;" class="nav-link nav-toggle"><i class="fa ' + icon + '"></i><span class="title">' + p.title + '</span><span class="arrow"></span></a><ul class="sub-menu">';
if (p.children) {
p.children.forEach(c => {
html += '<li><a href="' + c.url + '" class="nav-link"><span class="title">' + c.title + '</span></a></li>';
});
}
html += '</ul></li>\n';
});
console.log('=== MENU HTML ===\n' + html);
try {
navigator.clipboard.writeText(html);
alert('✅ Copied to clipboard!\n\nPaste in:\n/opt/wevads/app/views/includes/menu.html\n\nThen: systemctl restart apache2');
} catch (e) {
alert('See console (F12) for HTML');
}
}
render();
</script>
</body>
</html>