fix: visitors.json clés perdues, bouton AS inaccessible, graphique visiteurs
- Fix array_merge → + pour préserver clés 7/14/30 dans visitors.json - Bouton ✕ exclusion AS sorti du div 9rem + stopPropagation - Handler délégué unique (removeEventListener avant de rajouter) - Graphique trend : visiteurs uniques/jour depuis ip_data (top 200) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,18 @@ Format : [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) — versionnag
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.6.35] - 2026-05-19
|
||||||
|
|
||||||
|
### Corrigé
|
||||||
|
- `visitors.json` : utilisation de `+` au lieu de `array_merge` pour préserver les clés entières 7/14/30 (array_merge les renumérote en 0/1/2)
|
||||||
|
- Admin stats / Visiteurs par pays : bouton ✕ déplacé hors du div 9rem (il était écrasé par le nom de l'AS) ; `e.stopPropagation()` ajouté pour ne pas déclencher l'accordéon
|
||||||
|
- Admin stats / Visiteurs par pays : listener délégué stocké et retiré avant réajout (évite l'accumulation de handlers après chaque `renderCountry()`)
|
||||||
|
|
||||||
|
### Modifié
|
||||||
|
- Graphique "Trafic total" → "Visiteurs uniques / jour" calculé depuis les IPs du top 200 (approximation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.6.34] - 2026-05-19
|
## [1.6.34] - 2026-05-19
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ var _csrf = (typeof FOLIO_CSRF !== 'undefined') ? FOLIO_CSRF : '';
|
|||||||
var ipData = (typeof FOLIO_IP_DATA !== 'undefined') ? FOLIO_IP_DATA : {};
|
var ipData = (typeof FOLIO_IP_DATA !== 'undefined') ? FOLIO_IP_DATA : {};
|
||||||
if (!el || !asList.length) { return; }
|
if (!el || !asList.length) { return; }
|
||||||
|
|
||||||
|
var _countryClickHandler = null;
|
||||||
var dispNames = null;
|
var dispNames = null;
|
||||||
try { dispNames = new Intl.DisplayNames(['fr'], { type: 'region' }); } catch (e) {}
|
try { dispNames = new Intl.DisplayNames(['fr'], { type: 'region' }); } catch (e) {}
|
||||||
function countryName(code) {
|
function countryName(code) {
|
||||||
@@ -277,17 +278,17 @@ var _csrf = (typeof FOLIO_CSRF !== 'undefined') ? FOLIO_CSRF : '';
|
|||||||
var toggleAttrs = hasIps ? ' data-bs-toggle="collapse" data-bs-target="#' + asId + '" role="button"' : '';
|
var toggleAttrs = hasIps ? ' data-bs-toggle="collapse" data-bs-target="#' + asId + '" role="button"' : '';
|
||||||
var chevron = hasIps ? '<span class="text-muted ms-1" style="font-size:.65rem">▾</span>' : '';
|
var chevron = hasIps ? '<span class="text-muted ms-1" style="font-size:.65rem">▾</span>' : '';
|
||||||
var excludeBtn = n.asn
|
var excludeBtn = n.asn
|
||||||
? '<button class="btn btn-sm py-0 px-1 ms-2 text-muted border-0 exclude-as-btn" style="font-size:.65rem" title="Exclure cet AS des stats" data-asn="' + esc(n.asn) + '" data-name="' + esc(n.name || '') + '">✕</button>'
|
? '<button class="btn btn-sm py-0 px-1 text-muted border-0 exclude-as-btn" style="font-size:.65rem;flex-shrink:0" title="Exclure cet AS des stats" data-asn="' + esc(n.asn) + '" data-name="' + esc(n.name || '') + '">✕</button>'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
return '<div>'
|
return '<div>'
|
||||||
+ '<div class="d-flex align-items-center gap-2 py-1"' + toggleAttrs + '>'
|
+ '<div class="d-flex align-items-center gap-1 py-1"' + toggleAttrs + '>'
|
||||||
+ '<div class="small d-flex align-items-center" style="width:9rem;flex-shrink:0">'
|
+ '<div class="small d-flex align-items-center" style="min-width:0;flex:1 1 9rem;overflow:hidden">'
|
||||||
+ esc(n.name || '?')
|
+ '<span class="text-truncate">' + esc(n.name || '?') + '</span>'
|
||||||
+ (n.asn ? ' <span class="text-muted">AS' + esc(n.asn) + '</span>' : '')
|
+ (n.asn ? ' <span class="text-muted text-nowrap">AS' + esc(n.asn) + '</span>' : '')
|
||||||
+ chevron
|
+ chevron
|
||||||
+ excludeBtn
|
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
|
+ excludeBtn
|
||||||
+ '<div class="flex-grow-1"><div class="progress" style="height:4px">'
|
+ '<div class="flex-grow-1"><div class="progress" style="height:4px">'
|
||||||
+ '<div class="progress-bar bg-info" style="width:' + npct + '%"></div>'
|
+ '<div class="progress-bar bg-info" style="width:' + npct + '%"></div>'
|
||||||
+ '</div></div>'
|
+ '</div></div>'
|
||||||
@@ -336,13 +337,15 @@ var _csrf = (typeof FOLIO_CSRF !== 'undefined') ? FOLIO_CSRF : '';
|
|||||||
|
|
||||||
el.innerHTML = html;
|
el.innerHTML = html;
|
||||||
|
|
||||||
// Délégation : boutons exclure / inclure
|
// Délégation : boutons exclure / inclure (handler unique pour éviter les doublons)
|
||||||
el.addEventListener('click', function (e) {
|
if (_countryClickHandler) { el.removeEventListener('click', _countryClickHandler); }
|
||||||
|
_countryClickHandler = function (e) {
|
||||||
var btn = e.target.closest('.exclude-as-btn');
|
var btn = e.target.closest('.exclude-as-btn');
|
||||||
if (btn) { excludeAs(btn.getAttribute('data-asn'), btn.getAttribute('data-name')); return; }
|
if (btn) { e.stopPropagation(); excludeAs(btn.getAttribute('data-asn'), btn.getAttribute('data-name')); return; }
|
||||||
btn = e.target.closest('.include-as-btn');
|
btn = e.target.closest('.include-as-btn');
|
||||||
if (btn) { includeAs(btn.getAttribute('data-asn')); }
|
if (btn) { e.stopPropagation(); includeAs(btn.getAttribute('data-asn')); }
|
||||||
}, { once: true });
|
};
|
||||||
|
el.addEventListener('click', _countryClickHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCountry();
|
renderCountry();
|
||||||
@@ -543,14 +546,14 @@ var _csrf = (typeof FOLIO_CSRF !== 'undefined') ? FOLIO_CSRF : '';
|
|||||||
var dots = pts.map(function (p) {
|
var dots = pts.map(function (p) {
|
||||||
return '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="14"'
|
return '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="14"'
|
||||||
+ ' fill="transparent" cursor="default">'
|
+ ' fill="transparent" cursor="default">'
|
||||||
+ '<title>' + esc(p.l) + ' : ' + p.v + ' vis.</title>'
|
+ '<title>' + esc(p.l) + ' : ' + p.v + ' visiteur(s)</title>'
|
||||||
+ '</circle>'
|
+ '</circle>'
|
||||||
+ '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="3"'
|
+ '<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="3"'
|
||||||
+ ' fill="var(--bs-primary,#0d6efd)" stroke="#fff" stroke-width="1.5" pointer-events="none"/>';
|
+ ' fill="var(--bs-primary,#0d6efd)" stroke="#fff" stroke-width="1.5" pointer-events="none"/>';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
trendEl.innerHTML =
|
trendEl.innerHTML =
|
||||||
'<p class="small text-muted mb-2 fw-semibold">Trafic total — 30 derniers jours</p>'
|
'<p class="small text-muted mb-2 fw-semibold">Visiteurs uniques / jour — 30 derniers jours <span class="fw-normal opacity-50">(top 200 IPs)</span></p>'
|
||||||
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + VW + ' ' + VH + '"'
|
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + VW + ' ' + VH + '"'
|
||||||
+ ' style="width:100%;height:480px;display:block;overflow:visible">'
|
+ ' style="width:100%;height:480px;display:block;overflow:visible">'
|
||||||
+ '<defs>'
|
+ '<defs>'
|
||||||
@@ -690,11 +693,13 @@ var _csrf = (typeof FOLIO_CSRF !== 'undefined') ? FOLIO_CSRF : '';
|
|||||||
return { title: title, link: link, slug: slug, vis: vis, daily: daily };
|
return { title: title, link: link, slug: slug, vis: vis, daily: daily };
|
||||||
});
|
});
|
||||||
var nDays = Object.values(pagesByDay)[0] ? Object.values(pagesByDay)[0].length : 30;
|
var nDays = Object.values(pagesByDay)[0] ? Object.values(pagesByDay)[0].length : 30;
|
||||||
var totals = new Array(nDays).fill(0);
|
// Visiteurs uniques par jour — compté sur les IPs du top 200 (approximation)
|
||||||
Object.values(pagesByDay).forEach(function (arr) {
|
var dailyVisitors = new Array(nDays).fill(0);
|
||||||
arr.forEach(function (v, i) { if (i < nDays) { totals[i] += v; } });
|
Object.keys(ipData).forEach(function (ip) {
|
||||||
|
var daily = ipData[ip].daily || [];
|
||||||
|
daily.forEach(function (v, i) { if (i < nDays && v > 0) { dailyVisitors[i]++; } });
|
||||||
});
|
});
|
||||||
trendChart(totals);
|
trendChart(dailyVisitors);
|
||||||
multiLineChart(pagesByDay, rows);
|
multiLineChart(pagesByDay, rows);
|
||||||
|
|
||||||
var html = '<div class="table-responsive"><table class="table table-sm table-hover mb-0 small w-100"><tbody>';
|
var html = '<div class="table-responsive"><table class="table table-sm table-hover mb-0 small w-100"><tbody>';
|
||||||
|
|||||||
+1
-1
@@ -2823,7 +2823,7 @@ switch ($action) {
|
|||||||
if ($_artUuid !== null && preg_match('/^[0-9a-f\-]{36}$/i', $_artUuid)) {
|
if ($_artUuid !== null && preg_match('/^[0-9a-f\-]{36}$/i', $_artUuid)) {
|
||||||
@file_put_contents(
|
@file_put_contents(
|
||||||
DATA_PATH . '/' . $_artUuid . '/visitors.json',
|
DATA_PATH . '/' . $_artUuid . '/visitors.json',
|
||||||
json_encode(array_merge($_artCounts, ['updated' => time()]), JSON_UNESCAPED_UNICODE)
|
json_encode($_artCounts + ['updated' => time()], JSON_UNESCAPED_UNICODE)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.6.34
|
1.6.35
|
||||||
|
|||||||
Reference in New Issue
Block a user