Files
wp-multi/includes/analytics.php
T
2026-08-14 07:59:43 +00:00

346 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* WP Multi Modul: Benutzer-Analytics
*
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
*/
if (!defined('ABSPATH')) { exit; }
/*
* Benutzer-Analytics mit verbesserten Sicherheits-, Performance- und Benutzerfreundlichkeits-Features
*/
// global ist nötig, weil die Plugin-Datei bei der Aktivierung innerhalb einer
// Funktion eingebunden wird und $wpdb dort sonst nicht verfügbar ist.
global $wpdb;
define('WP_MULTI_ANALYTICS_TABLE', $wpdb->prefix . 'wp_multi_user_analytics');
/**
* Erstellt die Datenbanktabelle für Benutzer-Analytics.
*/
function wp_multi_create_analytics_table() {
global $wpdb;
$table_name = WP_MULTI_ANALYTICS_TABLE;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
action varchar(255) NOT NULL,
post_id bigint(20) DEFAULT NULL,
timestamp datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX idx_timestamp (timestamp),
INDEX idx_action (action)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
register_activation_hook(WP_MULTI_FILE, 'wp_multi_create_analytics_table');
/**
* Verfolgt Benutzerinteraktionen (Kommentare und Beitragsaufrufe).
*
* @param int $user_id Benutzer-ID.
* @param string $action Aktion (z. B. 'view', 'comment').
* @param int $post_id Beitrag-ID (optional).
* @return bool Erfolg der Operation.
*/
function wp_multi_track_user_activity($user_id, $action, $post_id = null) {
global $wpdb;
$table_name = WP_MULTI_ANALYTICS_TABLE;
$user_id = absint($user_id);
$action = sanitize_text_field($action);
$post_id = $post_id ? absint($post_id) : null;
if ($action === 'view' && is_single()) {
$post_id = get_the_ID();
}
if (!$user_id || !$action) {
return false;
}
return $wpdb->insert(
$table_name,
array(
'user_id' => $user_id,
'action' => $action,
'post_id' => $post_id,
),
array('%d', '%s', '%d')
);
}
/**
* Verfolgt Kommentar-Aktivitäten.
*
* @param int $comment_id Kommentar-ID.
*/
function wp_multi_comment_activity($comment_id) {
$comment = get_comment($comment_id);
$user_id = absint($comment->user_id);
if ($user_id) {
wp_multi_track_user_activity($user_id, 'comment', $comment->comment_post_ID);
}
}
add_action('comment_post', 'wp_multi_comment_activity');
/**
* Verfolgt Beitragsaufrufe.
*/
function wp_multi_post_view_activity() {
if (is_single() && is_user_logged_in()) {
$user_id = get_current_user_id();
$post_id = get_the_ID();
wp_multi_track_user_activity($user_id, 'view', $post_id);
}
}
add_action('wp_head', 'wp_multi_post_view_activity');
/**
* Ruft rohe Analytics-Daten aus der Datenbank ab.
*
* @param string $date_query Datum für die Abfrage.
* @return array Rohe Analytics-Daten.
*/
function wp_multi_fetch_raw_analytics($start_datetime = null, $end_datetime = null) {
global $wpdb;
if (!$start_datetime) {
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days'));
}
$where = 'WHERE timestamp >= %s';
$params = [$start_datetime];
if ($end_datetime) {
$where .= ' AND timestamp <= %s';
$params[] = $end_datetime;
}
$sql = "SELECT DATE(timestamp) AS date, action, post_id, COUNT(*) AS count, user_id, timestamp
FROM " . WP_MULTI_ANALYTICS_TABLE . "
$where
GROUP BY date, action, post_id, user_id, timestamp
ORDER BY date ASC";
return $wpdb->get_results($wpdb->prepare($sql, $params));
}
/**
* Verarbeitet rohe Analytics-Daten für Diagramm und Tabelle.
*
* @param array $results Rohe Analytics-Daten.
* @return array Verarbeitete Daten.
*/
function wp_multi_process_analytics_data($results) {
$dates = [];
$comment_counts = [];
$view_counts = [];
foreach ($results as $result) {
$date = $result->date;
if (!in_array($date, $dates)) {
$dates[] = $date;
}
if ($result->action === 'comment') {
$comment_counts[$date] = ($comment_counts[$date] ?? 0) + $result->count;
} elseif ($result->action === 'view') {
$view_counts[$date] = ($view_counts[$date] ?? 0) + $result->count;
}
}
$all_dates = [];
$datasets = ['comments' => [], 'views' => []];
for ($i = 6; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-$i day"));
$all_dates[] = $date;
$datasets['comments'][] = $comment_counts[$date] ?? 0;
$datasets['views'][] = $view_counts[$date] ?? 0;
}
return [
'dates' => array_reverse($all_dates),
'datasets' => [
[
'label' => __('Kommentare', 'wp-multi'),
'data' => array_reverse($datasets['comments']),
'borderColor' => 'rgba(75, 192, 192, 1)',
'borderWidth' => 1,
'fill' => false,
],
[
'label' => __('Beitragsaufrufe', 'wp-multi'),
'data' => array_reverse($datasets['views']),
'borderColor' => 'rgba(153, 102, 255, 1)',
'borderWidth' => 1,
'fill' => false,
]
],
'data' => $results
];
}
/**
* Ruft Analytics-Daten mit Caching ab.
*
* @param string $date_query Datum für die Abfrage.
* @return array Analytics-Daten.
*/
function wp_multi_get_analytics_data($start_datetime = null, $end_datetime = null) {
$cache_key = 'wp_multi_analytics_data_' . md5($start_datetime . '|' . $end_datetime);
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
$results = wp_multi_fetch_raw_analytics($start_datetime, $end_datetime);
$data = wp_multi_process_analytics_data($results);
set_transient($cache_key, $data, HOUR_IN_SECONDS);
return $data;
}
/**
* Zeigt die Benutzer-Analytics-Seite im Admin-Bereich an.
*/
function wp_multi_display_user_analytics() {
global $wpdb;
if (!$wpdb->get_var("SHOW TABLES LIKE '" . WP_MULTI_ANALYTICS_TABLE . "'")) {
echo '<div class="error"><p>' . esc_html__('Die Analytics-Tabelle existiert nicht. Bitte aktiviere das Plugin erneut.', 'wp-multi') . '</p></div>';
return;
}
$time_range = isset($_GET['time_range']) ? sanitize_text_field($_GET['time_range']) : '7days';
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days'));
$end_datetime = null;
if ($time_range === '30days') {
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-30 days'));
} elseif ($time_range === 'custom' && isset($_GET['start_date'], $_GET['end_date'])) {
$custom_start = sanitize_text_field(wp_unslash($_GET['start_date']));
$custom_end = sanitize_text_field(wp_unslash($_GET['end_date']));
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_start) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_end)) {
$start_datetime = $custom_start . ' 00:00:00';
$end_datetime = $custom_end . ' 23:59:59';
}
}
$results = wp_multi_get_analytics_data($start_datetime, $end_datetime);
wpmt_admin_page_open(
__('Benutzer Analytics', 'wp-multi'),
__('Beitragsaufrufe und Kommentar-Aktivität deiner Besucher', 'wp-multi')
);
wpmt_admin_card_open(__('Zeitraum', 'wp-multi'));
?>
<form method="get">
<input type="hidden" name="page" value="wp_multi_analytics">
<select name="time_range" onchange="this.form.submit()" style="max-width:240px;">
<option value="7days" <?php selected($time_range, '7days'); ?>><?php esc_html_e('Letzte 7 Tage', 'wp-multi'); ?></option>
<option value="30days" <?php selected($time_range, '30days'); ?>><?php esc_html_e('Letzte 30 Tage', 'wp-multi'); ?></option>
<option value="custom" <?php selected($time_range, 'custom'); ?>><?php esc_html_e('Benutzerdefiniert', 'wp-multi'); ?></option>
</select>
<?php if ($time_range === 'custom') : ?>
<input type="date" name="start_date" value="<?php echo esc_attr($_GET['start_date'] ?? ''); ?>" style="max-width:180px;">
<input type="date" name="end_date" value="<?php echo esc_attr($_GET['end_date'] ?? ''); ?>" style="max-width:180px;">
<?php endif; ?>
<button type="submit" class="wpm-btn wpm-btn--primary"><?php esc_html_e('Filtern', 'wp-multi'); ?></button>
</form>
<?php
wpmt_admin_card_close();
if (empty($results['data'])) {
wpmt_admin_notice('info', __('Keine Daten für den gewählten Zeitraum verfügbar.', 'wp-multi'));
} else {
wpmt_admin_card_open(__('Aktivitätsverlauf', 'wp-multi'));
?>
<canvas id="userActivityChart" style="height: 300px; width: 100%;"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
if (typeof Chart !== 'undefined') {
document.addEventListener('DOMContentLoaded', function () {
var ctx = document.getElementById('userActivityChart').getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo wp_json_encode($results['dates']); ?>,
datasets: <?php echo wp_json_encode($results['datasets']); ?>,
},
options: {
scales: {
x: { title: { display: true, text: '<?php echo esc_js(__('Datum', 'wp-multi')); ?>' } },
y: { title: { display: true, text: '<?php echo esc_js(__('Anzahl', 'wp-multi')); ?>' }, beginAtZero: true }
}
}
});
});
} else {
console.error('Chart.js konnte nicht geladen werden.');
}
</script>
<?php
wpmt_admin_card_close();
wpmt_admin_card_open(__('Einzelne Aktivitäten', 'wp-multi'));
?>
<div class="wpm-table-wrap">
<table class="wpm-table">
<thead>
<tr>
<th><?php esc_html_e('Benutzer ID', 'wp-multi'); ?></th>
<th><?php esc_html_e('Aktion', 'wp-multi'); ?></th>
<th><?php esc_html_e('Beitrag Titel', 'wp-multi'); ?></th>
<th><?php esc_html_e('Beitrag ID', 'wp-multi'); ?></th>
<th><?php esc_html_e('Zeitstempel', 'wp-multi'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($results['data'] as $row) : ?>
<tr>
<td><?php echo esc_html($row->user_id); ?></td>
<td><span class="wpm-badge <?php echo $row->action === 'comment' ? 'wpm-badge--success' : ''; ?>"><?php echo esc_html($row->action); ?></span></td>
<td>
<?php
if ($row->post_id) {
$post_title = get_the_title($row->post_id);
echo esc_html($post_title ?: __('Kein Titel verfügbar', 'wp-multi'));
} else {
echo esc_html__('Kein Beitrag', 'wp-multi');
}
?>
</td>
<td><?php echo esc_html($row->post_id ?: '-'); ?></td>
<td><?php echo esc_html($row->timestamp); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
wpmt_admin_card_close();
}
wpmt_admin_page_close();
}
// Der Analytics-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.