v1.6.8 — scripts CSP-conformes, densité L/M/S, RSS XML #76
@@ -0,0 +1,68 @@
|
||||
/* Admin stats : groupes AS + chargement pages via flux RSS XML /trending?period=14d */
|
||||
|
||||
// ── Groupes de réseaux ────────────────────────────────────────────────────────
|
||||
(function () {
|
||||
var addBtn = document.getElementById('as-group-add');
|
||||
if (!addBtn) { return; }
|
||||
|
||||
addBtn.addEventListener('click', function () {
|
||||
var tpl = document.getElementById('as-group-tpl').content.cloneNode(true);
|
||||
document.getElementById('as-groups-list').appendChild(tpl);
|
||||
});
|
||||
|
||||
document.getElementById('as-groups-list').addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('as-group-delete')) {
|
||||
e.target.closest('.as-group-row').remove();
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
// ── Pages les plus visitées (RSS XML) ────────────────────────────────────────
|
||||
(function () {
|
||||
var container = document.getElementById('stats-pages-container');
|
||||
var badge = document.getElementById('stats-pages-count');
|
||||
if (!container) { return; }
|
||||
|
||||
function esc(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
fetch('/trending?period=14d')
|
||||
.then(function (r) { return r.ok ? r.text() : Promise.reject(); })
|
||||
.then(function (xml) {
|
||||
var doc = new DOMParser().parseFromString(xml, 'application/xml');
|
||||
var items = Array.from(doc.querySelectorAll('item'));
|
||||
if (!items.length) {
|
||||
container.innerHTML = '<p class="text-muted p-3 mb-0">Aucune donnée.</p>';
|
||||
return;
|
||||
}
|
||||
var rows = items.map(function (item) {
|
||||
var raw = (item.querySelector('title') || { textContent: '' }).textContent;
|
||||
var link = ((item.querySelector('link') || {}).textContent || '').trim();
|
||||
var m = raw.match(/\((\d+)\s+visiteurs?\)$/);
|
||||
var vis = m ? parseInt(m[1], 10) : 0;
|
||||
var title = raw.replace(/\s*\(\d+\s+visiteurs?\)$/, '');
|
||||
var slug = decodeURIComponent(link.replace(/.*\/post\//, ''));
|
||||
return { title: title, link: link, slug: slug, vis: vis };
|
||||
});
|
||||
var maxV = Math.max.apply(null, rows.map(function (r) { return r.vis; })) || 1;
|
||||
var html = '<div class="table-responsive"><table class="table table-sm table-hover mb-0 small"><tbody>';
|
||||
rows.forEach(function (row, i) {
|
||||
var pct = Math.round(row.vis / maxV * 100);
|
||||
var vis = row.vis.toLocaleString('fr-FR');
|
||||
html += '<tr>'
|
||||
+ '<td class="text-muted ps-3" style="width:2rem">' + (i + 1) + '</td>'
|
||||
+ '<td><a href="' + esc(row.link) + '" target="_blank" class="text-decoration-none text-truncate d-block" style="max-width:260px" title="' + esc(row.slug) + '">'
|
||||
+ esc(row.title || row.slug) + '</a>'
|
||||
+ '<div class="progress mt-1" style="height:3px"><div class="progress-bar" style="width:' + pct + '%"></div></div></td>'
|
||||
+ '<td class="text-end fw-semibold pe-3">' + vis + ' <span class="text-muted fw-normal">vis.</span></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
html += '</tbody></table></div>';
|
||||
if (badge) { badge.textContent = rows.length + ' URLs'; }
|
||||
container.innerHTML = html;
|
||||
})
|
||||
.catch(function () {
|
||||
container.innerHTML = '<p class="text-muted p-3 mb-0">Impossible de charger le flux.</p>';
|
||||
});
|
||||
}());
|
||||
@@ -45,4 +45,28 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Suppression groupée avec confirmation (remplace onclick inline)
|
||||
var bulkDeleteBtn = document.getElementById('bulk-delete-btn');
|
||||
if (bulkDeleteBtn) {
|
||||
bulkDeleteBtn.addEventListener('click', function (e) {
|
||||
var checked = document.querySelectorAll('.bulk-check:checked').length;
|
||||
if (checked === 0) { e.preventDefault(); return; }
|
||||
var msg = bulkDeleteBtn.getAttribute('data-confirm-bulk') || 'Confirmer ?';
|
||||
if (!window.confirm(msg)) { e.preventDefault(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Ajout d'un article à un livre (remplace onchange="bookAddArticle(this)")
|
||||
var bookArticleSel = document.getElementById('book-article-select');
|
||||
if (bookArticleSel) {
|
||||
bookArticleSel.addEventListener('change', function () {
|
||||
var slug = bookArticleSel.value;
|
||||
if (!slug) { return; }
|
||||
var ta = document.getElementById('book-articles-ta');
|
||||
var lines = ta.value.split('\n').map(function (s) { return s.trim(); }).filter(Boolean);
|
||||
if (lines.indexOf(slug) === -1) { lines.push(slug); ta.value = lines.join('\n'); }
|
||||
bookArticleSel.value = '';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* Anti-FOUC densité — chargé tôt dans <head> pour appliquer max-width avant rendu de <main> */
|
||||
(function () {
|
||||
var d = localStorage.getItem('folio_density');
|
||||
if (d && d !== 'l') {
|
||||
var mw = d === 'm' ? '980px' : '660px';
|
||||
var s = document.createElement('style');
|
||||
s.id = 'density-fouc';
|
||||
s.textContent = 'main[role="main"]{max-width:' + mw + '!important;margin-left:auto!important;margin-right:auto!important}';
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
}());
|
||||
@@ -0,0 +1,38 @@
|
||||
/* Sélecteur de densité L/M/S — persisté dans localStorage */
|
||||
(function () {
|
||||
var KEY = 'folio_density';
|
||||
var cur = localStorage.getItem(KEY) || 'l';
|
||||
|
||||
function applyDensity(d) {
|
||||
var fouc = document.getElementById('density-fouc');
|
||||
if (d !== 'l') {
|
||||
var mw = d === 'm' ? '980px' : '660px';
|
||||
if (!fouc) {
|
||||
fouc = document.createElement('style');
|
||||
fouc.id = 'density-fouc';
|
||||
document.head.appendChild(fouc);
|
||||
}
|
||||
fouc.textContent = 'main[role="main"]{max-width:' + mw + '!important;margin-left:auto!important;margin-right:auto!important}';
|
||||
} else {
|
||||
if (fouc) { fouc.parentNode.removeChild(fouc); }
|
||||
}
|
||||
document.querySelectorAll('.density-btn').forEach(function (btn) {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-d') === d);
|
||||
});
|
||||
}
|
||||
|
||||
applyDensity(cur);
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var el = e.target;
|
||||
while (el && el !== document) {
|
||||
if (el.classList && el.classList.contains('density-btn')) {
|
||||
cur = el.getAttribute('data-d') || 'l';
|
||||
try { localStorage.setItem(KEY, cur); } catch (ignore) {}
|
||||
applyDensity(cur);
|
||||
return;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
});
|
||||
}());
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Chargement AJAX de la section "Meilleures audiences" via le flux RSS XML /trending?period=1h */
|
||||
(function () {
|
||||
var grid = document.getElementById('home-audiences-grid');
|
||||
if (!grid) { return; }
|
||||
|
||||
var gradients = [
|
||||
'linear-gradient(135deg,#667eea 0%,#764ba2 100%)',
|
||||
'linear-gradient(135deg,#f093fb 0%,#f5576c 100%)',
|
||||
'linear-gradient(135deg,#4facfe 0%,#00f2fe 100%)',
|
||||
'linear-gradient(135deg,#43e97b 0%,#38f9d7 100%)',
|
||||
'linear-gradient(135deg,#fa709a 0%,#fee140 100%)',
|
||||
'linear-gradient(135deg,#a18cd1 0%,#fbc2eb 100%)'
|
||||
];
|
||||
|
||||
function esc(s) {
|
||||
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
fetch('/trending?period=1h')
|
||||
.then(function (r) { return r.ok ? r.text() : Promise.reject(); })
|
||||
.then(function (xml) {
|
||||
var doc = new DOMParser().parseFromString(xml, 'application/xml');
|
||||
var items = Array.from(doc.querySelectorAll('item')).slice(0, 6);
|
||||
if (!items.length) { return; }
|
||||
|
||||
grid.innerHTML = items.map(function (item, i) {
|
||||
var raw = (item.querySelector('title') || { textContent: '' }).textContent;
|
||||
var title = raw.replace(/\s*\(\d+\s+visiteurs?\)$/, '');
|
||||
var link = ((item.querySelector('link') || {}).textContent || '#').trim();
|
||||
var pd = (item.querySelector('pubDate') || { textContent: '' }).textContent;
|
||||
var date = '';
|
||||
try { if (pd) { date = new Date(pd).toLocaleDateString('fr-FR'); } } catch (err) {}
|
||||
var grad = gradients[i % gradients.length];
|
||||
|
||||
return '<article class="card">'
|
||||
+ '<div class="card-cover" style="background:' + grad + '"></div>'
|
||||
+ '<div class="card-body d-flex flex-column">'
|
||||
+ '<h2 class="card-title"><a href="' + esc(link) + '">' + esc(title) + '</a></h2>'
|
||||
+ '<div class="post-entry-meta mt-auto">'
|
||||
+ (date ? '<span>' + esc(date) + '</span>' : '')
|
||||
+ '<a href="' + esc(link) + '" class="post-entry-read">→ lire</a>'
|
||||
+ '</div></div>'
|
||||
+ '<a href="' + esc(link) + '" class="stretched-link"></a>'
|
||||
+ '</article>';
|
||||
}).join('');
|
||||
|
||||
var section = document.getElementById('home-audiences-section');
|
||||
if (section) { section.hidden = false; }
|
||||
})
|
||||
.catch(function () {});
|
||||
}());
|
||||
+3
-16
@@ -264,8 +264,8 @@ function adminStatusBadge(array $a, int $now): string
|
||||
<input class="form-check-input" type="checkbox" id="check-all">
|
||||
<label class="form-check-label small text-muted" for="check-all">Tout sélectionner</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger btn-sm"
|
||||
onclick="return document.querySelectorAll('.bulk-check:checked').length > 0 && confirm('Supprimer les articles sélectionnés ? Cette action est irréversible.')">
|
||||
<button type="submit" id="bulk-delete-btn" class="btn btn-danger btn-sm"
|
||||
data-confirm-bulk="Supprimer les articles sélectionnés ? Cette action est irréversible.">
|
||||
Supprimer la sélection
|
||||
</button>
|
||||
</div>
|
||||
@@ -1273,7 +1273,7 @@ foreach (COLOR_PALETTE_16 as $_i => $_rgb):
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-medium">Ajouter une page existante</label>
|
||||
<select class="form-select" onchange="bookAddArticle(this)">
|
||||
<select class="form-select" id="book-article-select">
|
||||
<option value="">— Choisir un article —</option>
|
||||
<?php
|
||||
$alreadyIn = $eb['articles'] ?? [];
|
||||
@@ -1304,19 +1304,6 @@ foreach (COLOR_PALETTE_16 as $_i => $_rgb):
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm">🗑 Supprimer ce livre</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function bookAddArticle(sel) {
|
||||
var slug = sel.value;
|
||||
if (!slug) return;
|
||||
var ta = document.getElementById('book-articles-ta');
|
||||
var lines = ta.value.split('\n').map(function(s) { return s.trim(); }).filter(Boolean);
|
||||
if (lines.indexOf(slug) === -1) {
|
||||
lines.push(slug);
|
||||
ta.value = lines.join('\n');
|
||||
}
|
||||
sel.value = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php elseif (isset($_GET['new'])): ?>
|
||||
<h5>Nouveau livre</h5>
|
||||
|
||||
@@ -195,60 +195,4 @@ $_activeGroup = trim($_GET['group'] ?? '');
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
document.getElementById('as-group-add').addEventListener('click', () => {
|
||||
const tpl = document.getElementById('as-group-tpl').content.cloneNode(true);
|
||||
document.getElementById('as-groups-list').appendChild(tpl);
|
||||
});
|
||||
document.getElementById('as-groups-list').addEventListener('click', e => {
|
||||
if (e.target.classList.contains('as-group-delete')) {
|
||||
e.target.closest('.as-group-row').remove();
|
||||
}
|
||||
});
|
||||
|
||||
// ── Chargement des pages via le flux RSS XML ──────────────────────────────────
|
||||
(function(){
|
||||
var container = document.getElementById('stats-pages-container');
|
||||
var badge = document.getElementById('stats-pages-count');
|
||||
if (!container) return;
|
||||
function esc(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
fetch('/trending?period=14d')
|
||||
.then(function(r){ return r.ok ? r.text() : Promise.reject(); })
|
||||
.then(function(xml){
|
||||
var doc = new DOMParser().parseFromString(xml, 'application/xml');
|
||||
var items = Array.from(doc.querySelectorAll('item'));
|
||||
if (!items.length) {
|
||||
container.innerHTML = '<p class="text-muted p-3 mb-0">Aucune donnée.</p>';
|
||||
return;
|
||||
}
|
||||
var rows = items.map(function(item){
|
||||
var raw = (item.querySelector('title') || {textContent:''}).textContent;
|
||||
var link = ((item.querySelector('link') || {}).textContent || '').trim();
|
||||
var m = raw.match(/\((\d+)\s+visiteurs?\)$/);
|
||||
var vis = m ? parseInt(m[1], 10) : 0;
|
||||
var title = raw.replace(/\s*\(\d+\s+visiteurs?\)$/, '');
|
||||
var slug = decodeURIComponent(link.replace(/.*\/post\//, ''));
|
||||
return {title: title, link: link, slug: slug, vis: vis};
|
||||
});
|
||||
var maxV = Math.max.apply(null, rows.map(function(r){ return r.vis; })) || 1;
|
||||
var html = '<div class="table-responsive"><table class="table table-sm table-hover mb-0 small"><tbody>';
|
||||
rows.forEach(function(row, i){
|
||||
var pct = Math.round(row.vis / maxV * 100);
|
||||
var vis = row.vis.toLocaleString('fr-FR');
|
||||
html += '<tr>'
|
||||
+ '<td class="text-muted ps-3" style="width:2rem">' + (i+1) + '</td>'
|
||||
+ '<td><a href="' + esc(row.link) + '" target="_blank" class="text-decoration-none text-truncate d-block" style="max-width:260px" title="' + esc(row.slug) + '">'
|
||||
+ esc(row.title || row.slug) + '</a>'
|
||||
+ '<div class="progress mt-1" style="height:3px"><div class="progress-bar" style="width:' + pct + '%"></div></div></td>'
|
||||
+ '<td class="text-end fw-semibold pe-3">' + vis + ' <span class="text-muted fw-normal">vis.</span></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
html += '</tbody></table></div>';
|
||||
if (badge) badge.textContent = rows.length + ' URLs';
|
||||
container.innerHTML = html;
|
||||
})
|
||||
.catch(function(){
|
||||
container.innerHTML = '<p class="text-muted p-3 mb-0">Impossible de charger le flux.</p>';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="/assets/js/admin-stats.js" defer></script>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</head>
|
||||
|
||||
<body<?php if (!empty($bodyClass ?? '')): ?> class="<?= htmlspecialchars($bodyClass) ?>"<?php endif; ?>>
|
||||
<script>(function(){var d=localStorage.getItem('folio_density');if(d&&d!=='l'){var s=document.createElement('style');s.id='density-fouc';s.textContent='main[role="main"]{max-width:'+(d==='m'?'980px':'660px')+'!important;margin-left:auto!important;margin-right:auto!important}';document.head.appendChild(s);}})();</script>
|
||||
<script src="/assets/js/density-fouc.js"></script>
|
||||
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark mb-0" role="navigation" aria-label="Navigation principale">
|
||||
|
||||
+2
-83
@@ -160,50 +160,7 @@ function _renderCard(array $post, array $privateCats, array $allCats, \Parsedown
|
||||
</h2>
|
||||
<div class="post-grid" id="home-audiences-grid"></div>
|
||||
</section>
|
||||
<script>
|
||||
(function(){
|
||||
var _g=[
|
||||
'linear-gradient(135deg,#667eea 0%,#764ba2 100%)',
|
||||
'linear-gradient(135deg,#f093fb 0%,#f5576c 100%)',
|
||||
'linear-gradient(135deg,#4facfe 0%,#00f2fe 100%)',
|
||||
'linear-gradient(135deg,#43e97b 0%,#38f9d7 100%)',
|
||||
'linear-gradient(135deg,#fa709a 0%,#fee140 100%)',
|
||||
'linear-gradient(135deg,#a18cd1 0%,#fbc2eb 100%)'
|
||||
];
|
||||
function _e(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
||||
fetch('/trending?period=1h')
|
||||
.then(function(r){return r.ok?r.text():Promise.reject();})
|
||||
.then(function(xml){
|
||||
var doc=new DOMParser().parseFromString(xml,'application/xml');
|
||||
var items=Array.from(doc.querySelectorAll('item')).slice(0,6);
|
||||
if(!items.length)return;
|
||||
var grid=document.getElementById('home-audiences-grid');
|
||||
if(!grid)return;
|
||||
grid.innerHTML=items.map(function(item,i){
|
||||
var raw=(item.querySelector('title')||{textContent:''}).textContent;
|
||||
var title=raw.replace(/\s*\(\d+\s+visiteurs?\)$/,'');
|
||||
var link=((item.querySelector('link')||{}).textContent||'#').trim();
|
||||
var pd=(item.querySelector('pubDate')||{textContent:''}).textContent;
|
||||
var date='';
|
||||
try{if(pd)date=new Date(pd).toLocaleDateString('fr-FR');}catch(e){}
|
||||
var grad=_g[i%_g.length];
|
||||
return '<article class="card">'
|
||||
+'<div class="card-cover" style="background:'+grad+'"></div>'
|
||||
+'<div class="card-body d-flex flex-column">'
|
||||
+'<h2 class="card-title"><a href="'+_e(link)+'">'+_e(title)+'</a></h2>'
|
||||
+'<div class="post-entry-meta mt-auto">'
|
||||
+(date?'<span>'+_e(date)+'</span>':'')
|
||||
+'<a href="'+_e(link)+'" class="post-entry-read">→ lire</a>'
|
||||
+'</div></div>'
|
||||
+'<a href="'+_e(link)+'" class="stretched-link"></a>'
|
||||
+'</article>';
|
||||
}).join('');
|
||||
var s=document.getElementById('home-audiences-section');
|
||||
if(s)s.hidden=false;
|
||||
})
|
||||
.catch(function(){});
|
||||
})();
|
||||
</script>
|
||||
<script src="/assets/js/trending-home.js"></script>
|
||||
|
||||
<?php /* ─── Récemment mis à jour ──────────────────────────────────────── */ ?>
|
||||
<?php if (!empty($recentlyUpdated)): ?>
|
||||
@@ -344,45 +301,7 @@ if (!empty($_tagCats)):
|
||||
<button type="button" class="density-btn" data-d="s" title="Compact">S</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var KEY = 'folio_density';
|
||||
var cur = localStorage.getItem(KEY) || 'l';
|
||||
|
||||
function applyDensity(d) {
|
||||
var fouc = document.getElementById('density-fouc');
|
||||
if (d !== 'l') {
|
||||
var mw = d === 'm' ? '980px' : '660px';
|
||||
if (!fouc) {
|
||||
fouc = document.createElement('style');
|
||||
fouc.id = 'density-fouc';
|
||||
document.head.appendChild(fouc);
|
||||
}
|
||||
fouc.textContent = 'main[role="main"]{max-width:' + mw + '!important;margin-left:auto!important;margin-right:auto!important}';
|
||||
} else {
|
||||
if (fouc) { fouc.parentNode.removeChild(fouc); }
|
||||
}
|
||||
document.querySelectorAll('.density-btn').forEach(function(btn){
|
||||
btn.classList.toggle('active', btn.getAttribute('data-d') === d);
|
||||
});
|
||||
}
|
||||
|
||||
applyDensity(cur);
|
||||
|
||||
document.addEventListener('click', function(e){
|
||||
var el = e.target;
|
||||
while (el && el !== document) {
|
||||
if (el.classList && el.classList.contains('density-btn')) {
|
||||
cur = el.getAttribute('data-d') || 'l';
|
||||
try { localStorage.setItem(KEY, cur); } catch(ignore) {}
|
||||
applyDensity(cur);
|
||||
return;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="/assets/js/density.js"></script>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
|
||||
Reference in New Issue
Block a user