Skip to content

Commit 612294f

Browse files
committed
* **Statistik**: Neue Auswertung der Consent-Logs im Backend (Tägliche Consents, Top-Services)
1 parent 9fdd11a commit 612294f

File tree

7 files changed

+193
-2
lines changed

7 files changed

+193
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* **Neue Public API**: Einführung der Klasse `FriendsOfRedaxo\ConsentManager\ConsentManager` für den einfachen Zugriff auf gecachte Daten (Cookies, Gruppen, Texte, Domains)
88
* **Performance**: Interne Klassen (`Frontend`, `InlineConsent`, `GoogleConsentMode`) nutzen nun den Cache statt direkter SQL-Abfragen
99
* **Code-Qualität**: Refactoring der `InlineConsent` Klasse zur Vermeidung von Code-Duplizierung bei der Video-ID-Erkennung
10+
* **Statistik**: Neue Auswertung der Consent-Logs im Backend (Tägliche Consents, Top-Services)
1011
* **API Dokumentation**: Neue Dokumentation der öffentlichen API in der README.md
1112

1213
### 🐛 Bugfixes

boot.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
use FriendsOfRedaxo\ConsentManager\Api\ConsentManager;
4+
use FriendsOfRedaxo\ConsentManager\Api\ConsentStatsApi;
45
use FriendsOfRedaxo\ConsentManager\Cache;
56
use FriendsOfRedaxo\ConsentManager\CLang;
67
use FriendsOfRedaxo\ConsentManager\Cronjob\LogDelete;
@@ -216,6 +217,7 @@
216217
}
217218

218219
rex_api_function::register('consent_manager', ConsentManager::class);
220+
rex_api_function::register('consent_manager_stats', ConsentStatsApi::class);
219221

220222
// CKE5 oEmbed Parser automatisch im Frontend registrieren - nur wenn CKE5 verfügbar ist
221223
if (rex::isFrontend() && rex_addon::exists('cke5') && rex_addon::get('cke5')->isAvailable()) {

lang/de_de.lang

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,14 @@ consent_manager_button_inline_allow_all_text_info = Inline Placeholder: Button "
414414
# Theme Editor
415415
consent_manager_theme_editor_saved = Theme "{0}" wurde erfolgreich erstellt und gespeichert!
416416
consent_manager_theme_editor_project_addon_required = Der Theme-Editor benötigt das installierte "project" AddOn, um eigene Themes zu speichern. Bitte installiere das project AddOn über den Installer.
417+
418+
consent_manager_stats = Statistik
419+
consent_manager_stats_title = Consent Statistik
420+
consent_manager_stats_error = Fehler beim Laden der Statistik.
421+
consent_manager_stats_total = Gesamt Consents (30 Tage):
422+
consent_manager_stats_top_cookies = Top Cookies/Services
423+
consent_manager_stats_service = Service
424+
consent_manager_stats_count = Anzahl
425+
consent_manager_stats_history = Verlauf (Tage)
426+
consent_manager_stats_date = Datum
427+

lang/en_gb.lang

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,14 @@ consent_manager_button_inline_allow_all_text_info = Inline Placeholder: Button "
413413

414414
# Theme Editor
415415
consent_manager_theme_editor_saved = Theme "{0}" has been successfully created and saved!
416-
consent_manager_theme_editor_project_addon_required = The theme editor requires the installed "project" addon to save custom themes. Please install the project addon via the installer.
416+
consent_manager_theme_editor_project_addon_required = The theme editor requires the installed "project" addon to save custom themes. Please install the project addon via the installer.
417+
418+
consent_manager_stats = Statistics
419+
consent_manager_stats_title = Consent Statistics
420+
consent_manager_stats_error = Error loading statistics.
421+
consent_manager_stats_total = Total Consents (30 days):
422+
consent_manager_stats_top_cookies = Top Cookies/Services
423+
consent_manager_stats_service = Service
424+
consent_manager_stats_count = Count
425+
consent_manager_stats_history = History (Days)
426+
consent_manager_stats_date = Date

lib/Api/ConsentStatsApi.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace FriendsOfRedaxo\ConsentManager\Api;
4+
5+
use FriendsOfRedaxo\ConsentManager\ConsentStats;
6+
use rex_api_function;
7+
use rex_response;
8+
9+
class ConsentStatsApi extends rex_api_function
10+
{
11+
protected $published = false; // Only for backend users
12+
13+
public function execute(): void
14+
{
15+
$days = rex_request('days', 'int', 30);
16+
$stats = ConsentStats::getStats($days);
17+
18+
rex_response::cleanOutputBuffers();
19+
rex_response::sendJson($stats);
20+
exit;
21+
}
22+
}

lib/ConsentStats.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace FriendsOfRedaxo\ConsentManager;
4+
5+
use rex;
6+
use rex_sql;
7+
8+
class ConsentStats
9+
{
10+
/**
11+
* Returns statistics about consents.
12+
*
13+
* @param int $days Number of days to look back
14+
* @return array<string, mixed>
15+
*/
16+
public static function getStats(int $days = 30): array
17+
{
18+
$sql = rex_sql::factory();
19+
20+
// 1. Total consents per day
21+
$query = 'SELECT DATE(createdate) as date, COUNT(*) as count
22+
FROM ' . rex::getTable('consent_manager_consent_log') . '
23+
WHERE createdate >= DATE_SUB(NOW(), INTERVAL ? DAY)
24+
GROUP BY DATE(createdate)
25+
ORDER BY date ASC';
26+
27+
$dailyStats = $sql->getArray($query, [$days]);
28+
29+
// 2. Consent distribution (parsing JSON is heavy in SQL, so we do it in PHP for now or use simple LIKE if possible, but JSON is better)
30+
// Since we don't know if the DB supports JSON functions (MySQL 5.7+), we fetch and process in PHP for compatibility,
31+
// assuming the log isn't massive or we limit the query.
32+
// For better performance on large datasets, we should use a dedicated stats table or summary table, but for now:
33+
34+
$query = 'SELECT consents FROM ' . rex::getTable('consent_manager_consent_log') . '
35+
WHERE createdate >= DATE_SUB(NOW(), INTERVAL ? DAY)';
36+
37+
$logs = $sql->getArray($query, [$days]);
38+
39+
$cookieStats = [];
40+
$totalConsents = count($logs);
41+
42+
foreach ($logs as $log) {
43+
$consents = json_decode($log['consents'], true);
44+
if (is_array($consents)) {
45+
foreach ($consents as $uid) {
46+
if (!isset($cookieStats[$uid])) {
47+
$cookieStats[$uid] = 0;
48+
}
49+
$cookieStats[$uid]++;
50+
}
51+
}
52+
}
53+
54+
// Sort by count desc
55+
arsort($cookieStats);
56+
57+
return [
58+
'daily' => $dailyStats,
59+
'cookies' => $cookieStats,
60+
'total' => $totalConsents,
61+
'period_days' => $days
62+
];
63+
}
64+
}

pages/log.php

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,89 @@
7474
$fragmentsearch->setVar('value', $searchvalue);
7575
$cmsearch = $fragmentsearch->parse('core/form/search.php');
7676

77+
$statsBtn = '<button class="btn btn-info" id="btn-consent-stats" style="margin-right: 10px;"><i class="rex-icon fa-bar-chart"></i> ' . rex_i18n::msg('consent_manager_stats') . '</button>';
78+
7779
$fragment = new rex_fragment();
7880
$fragment->setVar('title', rex_i18n::msg('consent_manager_thead_title'));
79-
$fragment->setVar('options', $cmsearch, false);
81+
$fragment->setVar('options', '<div style="display:flex; justify-content:flex-end; align-items:center;">' . $statsBtn . $cmsearch . '</div>', false);
8082
$fragment->setVar('content', $list->get(), false);
8183
echo $fragment->parse('core/page/section.php');
84+
85+
?>
86+
<!-- Modal -->
87+
<div class="modal fade" id="consent-stats-modal" tabindex="-1" role="dialog">
88+
<div class="modal-dialog modal-lg" role="document">
89+
<div class="modal-content">
90+
<div class="modal-header">
91+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
92+
<h4 class="modal-title"><?php echo rex_i18n::msg('consent_manager_stats_title'); ?></h4>
93+
</div>
94+
<div class="modal-body" id="consent-stats-body">
95+
<div class="text-center"><i class="rex-icon fa-spinner fa-spin fa-3x"></i></div>
96+
</div>
97+
</div>
98+
</div>
99+
</div>
100+
101+
<script nonce="<?= rex_response::getNonce() ?>">
102+
$(document).on('click', '#btn-consent-stats', function() {
103+
$('#consent-stats-modal').modal('show');
104+
loadConsentStats();
105+
});
106+
107+
function loadConsentStats() {
108+
$('#consent-stats-body').html('<div class="text-center"><i class="rex-icon fa-spinner fa-spin fa-3x"></i></div>');
109+
$.ajax({
110+
url: 'index.php?rex-api-call=consent_manager_stats&days=30',
111+
dataType: 'json',
112+
success: function(data) {
113+
renderStats(data);
114+
},
115+
error: function() {
116+
$('#consent-stats-body').html('<div class="alert alert-danger"><?php echo rex_i18n::msg('consent_manager_stats_error'); ?></div>');
117+
}
118+
});
119+
}
120+
121+
function renderStats(data) {
122+
var html = '';
123+
124+
// Summary
125+
html += '<div class="row"><div class="col-md-12"><h4><?php echo rex_i18n::msg('consent_manager_stats_total'); ?> ' + data.total + '</h4></div></div>';
126+
html += '<hr>';
127+
128+
// Cookies
129+
html += '<div class="row"><div class="col-md-6">';
130+
html += '<h5><?php echo rex_i18n::msg('consent_manager_stats_top_cookies'); ?></h5>';
131+
html += '<table class="table table-striped table-condensed">';
132+
html += '<thead><tr><th><?php echo rex_i18n::msg('consent_manager_stats_service'); ?></th><th><?php echo rex_i18n::msg('consent_manager_stats_count'); ?></th><th>%</th></tr></thead><tbody>';
133+
134+
$.each(data.cookies, function(uid, count) {
135+
var percent = data.total > 0 ? Math.round((count / data.total) * 100) : 0;
136+
html += '<tr>';
137+
html += '<td>' + uid + '</td>';
138+
html += '<td>' + count + '</td>';
139+
html += '<td><div class="progress" style="margin-bottom:0"><div class="progress-bar progress-bar-info" role="progressbar" style="width: ' + percent + '%;">' + percent + '%</div></div></td>';
140+
html += '</tr>';
141+
});
142+
html += '</tbody></table>';
143+
html += '</div>';
144+
145+
// Daily
146+
html += '<div class="col-md-6">';
147+
html += '<h5><?php echo rex_i18n::msg('consent_manager_stats_history'); ?></h5>';
148+
html += '<table class="table table-striped table-condensed">';
149+
html += '<thead><tr><th><?php echo rex_i18n::msg('consent_manager_stats_date'); ?></th><th><?php echo rex_i18n::msg('consent_manager_stats_count'); ?></th></tr></thead><tbody>';
150+
$.each(data.daily, function(i, day) {
151+
html += '<tr>';
152+
html += '<td>' + day.date + '</td>';
153+
html += '<td>' + day.count + '</td>';
154+
html += '</tr>';
155+
});
156+
html += '</tbody></table>';
157+
html += '</div></div>';
158+
159+
$('#consent-stats-body').html(html);
160+
}
161+
</script>
162+
<?php

0 commit comments

Comments
 (0)