fix : externaliser tous les scripts inline (CSP script-src 'self')

Tous les <script> inline et event handlers inline bloqués par la CSP sont
déplacés vers des fichiers JS statiques servis par 'self' :
- density-fouc.js  : anti-FOUC densité (chargé en <head>)
- density.js       : widget L/M/S
- trending-home.js : AJAX "Meilleures audiences" (RSS XML)
- admin-stats.js   : groupes AS + pages trending (RSS XML)
- admin.js         : bookAddArticle + bulk-delete (onclick/onchange → listeners)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 21:00:26 +02:00
parent 58a110d5b9
commit 3e856dc476
9 changed files with 199 additions and 157 deletions
+2 -83
View File
@@ -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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
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();