Upload via GUI (30 Dateien)

This commit is contained in:
2026-08-23 13:35:10 +00:00
parent 90f388f49c
commit 2b162e581f
2 changed files with 1973 additions and 144 deletions
+136 -29
View File
@@ -1,13 +1,13 @@
<?php <?php
/** /**
* WP Multi Modul: Benutzer-Analytics * WP Multi Modul: Benutzer-Analytics
* *
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in * Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist). * wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
*/ */
if (!defined('ABSPATH')) { exit; } if (!defined('ABSPATH')) { exit; }
/* /*
* Benutzer-Analytics mit verbesserten Sicherheits-, Performance- und Benutzerfreundlichkeits-Features * Benutzer-Analytics mit verbesserten Sicherheits-, Performance- und Benutzerfreundlichkeits-Features
*/ */
@@ -62,7 +62,8 @@ function wp_multi_track_user_activity($user_id, $action, $post_id = null) {
$post_id = get_the_ID(); $post_id = get_the_ID();
} }
if (!$user_id || !$action) { // user_id 0 ist gewollt: Die Seite bietet keinen Login, Aufrufe sind anonym.
if (!$action) {
return false; return false;
} }
@@ -92,26 +93,93 @@ function wp_multi_comment_activity($comment_id) {
add_action('comment_post', 'wp_multi_comment_activity'); add_action('comment_post', 'wp_multi_comment_activity');
/** /**
* Verfolgt Beitragsaufrufe. * Erkennt Bots und Crawler anhand des User-Agents.
*
* @return bool
*/
function wp_multi_is_bot_request() {
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
// Ohne User-Agent ist es so gut wie nie ein echter Besucher
if ($user_agent === '') {
return true;
}
$signatures = apply_filters('wp_multi_bot_signatures', [
'bot', 'crawl', 'spider', 'slurp', 'archiver', 'preview', 'monitor', 'validator',
'facebookexternalhit', 'headless', 'phantomjs', 'python', 'curl', 'wget', 'go-http',
'java/', 'okhttp', 'axios', 'scrapy', 'lighthouse', 'pingdom', 'uptime',
]);
foreach ($signatures as $signature) {
if (strpos($user_agent, $signature) !== false) {
return true;
}
}
return false;
}
/**
* Prüft, ob dieser Besucher den Beitrag gerade erst aufgerufen hat.
*
* Ohne Login gibt es keine Benutzer-ID, deshalb dient ein kurzlebiger Hash aus
* IP und User-Agent als Erkennungsmerkmal. Gespeichert wird nur der Hash, nie
* die IP selbst ein Reload zählt damit nicht mehrfach.
*
* @param int $post_id Beitrag-ID.
* @return bool True, wenn der Aufruf bereits gezählt wurde.
*/
function wp_multi_view_already_counted($post_id) {
$ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
$key = 'wp_multi_view_' . md5($post_id . '|' . $ip . '|' . $user_agent . '|' . wp_salt());
if (get_transient($key)) {
return true;
}
// Sperrfrist je Besucher und Beitrag
$ttl = (int) apply_filters('wp_multi_view_throttle', 30 * MINUTE_IN_SECONDS);
set_transient($key, 1, $ttl);
return false;
}
/**
* Verfolgt Beitragsaufrufe auch von nicht eingeloggten Besuchern.
*/ */
function wp_multi_post_view_activity() { function wp_multi_post_view_activity() {
if (is_single() && is_user_logged_in()) { if (is_admin() || !is_single() || is_feed()) {
$user_id = get_current_user_id(); return;
$post_id = get_the_ID();
wp_multi_track_user_activity($user_id, 'view', $post_id);
} }
// Eigene Aufrufe der Redaktion verfälschen die Zahlen
if (is_user_logged_in() && current_user_can('edit_posts') && !apply_filters('wp_multi_track_editor_views', false)) {
return;
}
if (wp_multi_is_bot_request()) {
return;
}
$post_id = get_the_ID();
if (!$post_id || wp_multi_view_already_counted($post_id)) {
return;
}
// Ohne Login ist die Benutzer-ID 0 der Aufruf zählt trotzdem
wp_multi_track_user_activity(get_current_user_id(), 'view', $post_id);
} }
add_action('wp_head', 'wp_multi_post_view_activity'); add_action('wp_head', 'wp_multi_post_view_activity');
/** /**
* Ruft rohe Analytics-Daten aus der Datenbank ab. * Baut die WHERE-Bedingung für den gewählten Zeitraum.
* *
* @param string $date_query Datum für die Abfrage. * @return array [WHERE-Fragment, Parameter]
* @return array Rohe Analytics-Daten.
*/ */
function wp_multi_fetch_raw_analytics($start_datetime = null, $end_datetime = null) { function wp_multi_analytics_date_where($start_datetime = null, $end_datetime = null) {
global $wpdb;
if (!$start_datetime) { if (!$start_datetime) {
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days')); $start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days'));
} }
@@ -124,22 +192,60 @@ function wp_multi_fetch_raw_analytics($start_datetime = null, $end_datetime = nu
$params[] = $end_datetime; $params[] = $end_datetime;
} }
$sql = "SELECT DATE(timestamp) AS date, action, post_id, COUNT(*) AS count, user_id, timestamp return [$where, $params];
}
/**
* Ruft die Tagessummen je Aktion ab Grundlage für das Diagramm.
*
* Aggregiert in der Datenbank statt in PHP: Seit auch anonyme Aufrufe gezählt
* werden, kämen hier sonst zehntausende Einzelzeilen an.
*
* @return array Zeilen mit date, action und count.
*/
function wp_multi_fetch_analytics_totals($start_datetime = null, $end_datetime = null) {
global $wpdb;
list($where, $params) = wp_multi_analytics_date_where($start_datetime, $end_datetime);
$sql = "SELECT DATE(timestamp) AS date, action, COUNT(*) AS count
FROM " . WP_MULTI_ANALYTICS_TABLE . " FROM " . WP_MULTI_ANALYTICS_TABLE . "
$where $where
GROUP BY date, action, post_id, user_id, timestamp GROUP BY date, action
ORDER BY date ASC"; ORDER BY date ASC";
return $wpdb->get_results($wpdb->prepare($sql, $params)); return $wpdb->get_results($wpdb->prepare($sql, $params));
} }
/**
* Ruft die jüngsten Einzelereignisse ab Grundlage für die Detailtabelle.
*
* @return array Rohe Analytics-Daten (begrenzt).
*/
function wp_multi_fetch_raw_analytics($start_datetime = null, $end_datetime = null) {
global $wpdb;
list($where, $params) = wp_multi_analytics_date_where($start_datetime, $end_datetime);
// Ohne Limit würde die Detailtabelle bei anonymem Tracking die Seite sprengen
$params[] = (int) apply_filters('wp_multi_analytics_detail_limit', 200);
$sql = "SELECT DATE(timestamp) AS date, action, post_id, 1 AS count, user_id, timestamp
FROM " . WP_MULTI_ANALYTICS_TABLE . "
$where
ORDER BY timestamp DESC
LIMIT %d";
return $wpdb->get_results($wpdb->prepare($sql, $params));
}
/** /**
* Verarbeitet rohe Analytics-Daten für Diagramm und Tabelle. * Verarbeitet rohe Analytics-Daten für Diagramm und Tabelle.
* *
* @param array $results Rohe Analytics-Daten. * @param array $results Rohe Analytics-Daten.
* @return array Verarbeitete Daten. * @return array Verarbeitete Daten.
*/ */
function wp_multi_process_analytics_data($results) { function wp_multi_process_analytics_data($results, $recent = null) {
$dates = []; $dates = [];
$comment_counts = []; $comment_counts = [];
$view_counts = []; $view_counts = [];
@@ -184,7 +290,7 @@ function wp_multi_process_analytics_data($results) {
'fill' => false, 'fill' => false,
] ]
], ],
'data' => $results 'data' => ($recent === null) ? $results : $recent
]; ];
} }
@@ -202,8 +308,9 @@ function wp_multi_get_analytics_data($start_datetime = null, $end_datetime = nul
return $cached_data; return $cached_data;
} }
$results = wp_multi_fetch_raw_analytics($start_datetime, $end_datetime); $totals = wp_multi_fetch_analytics_totals($start_datetime, $end_datetime);
$data = wp_multi_process_analytics_data($results); $recent = wp_multi_fetch_raw_analytics($start_datetime, $end_datetime);
$data = wp_multi_process_analytics_data($totals, $recent);
set_transient($cache_key, $data, HOUR_IN_SECONDS); set_transient($cache_key, $data, HOUR_IN_SECONDS);
return $data; return $data;
@@ -308,7 +415,7 @@ function wp_multi_display_user_analytics() {
<tbody> <tbody>
<?php foreach ($results['data'] as $row) : ?> <?php foreach ($results['data'] as $row) : ?>
<tr> <tr>
<td><?php echo esc_html($row->user_id); ?></td> <td><?php echo $row->user_id ? esc_html($row->user_id) : esc_html__('Gast', 'wp-multi'); ?></td>
<td><span class="wpm-badge <?php echo $row->action === 'comment' ? 'wpm-badge--success' : ''; ?>"><?php echo esc_html($row->action); ?></span></td> <td><span class="wpm-badge <?php echo $row->action === 'comment' ? 'wpm-badge--success' : ''; ?>"><?php echo esc_html($row->action); ?></span></td>
<td> <td>
<?php <?php
+1837 -115
View File
File diff suppressed because it is too large Load Diff