Upload via GUI (39 Dateien)

This commit is contained in:
2026-08-14 07:59:43 +00:00
parent 05a2e1c7b6
commit 90f388f49c
31 changed files with 16919 additions and 5554 deletions
+345
View File
@@ -0,0 +1,345 @@
<?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.