__('Monat', 'wp-multi'), 'year' => __('Jahr', 'wp-multi'), 'timeline' => __('Autoren-Zeitlinie', 'wp-multi'), ]; } /** * Beitragstyp, der gezählt wird. */ function wp_multi_get_stats_post_type() { return apply_filters('wp_multi_stats_post_type', 'post'); } /** * Liefert alle Jahre mit veröffentlichten Beiträgen samt Anzahl. * * @return array Zeilen mit year und total, aufsteigend nach Jahr. */ function wp_multi_get_publication_years() { static $years = null; if ($years !== null) { return $years; } global $wpdb; $years = $wpdb->get_results($wpdb->prepare(" SELECT YEAR(post_date) AS year, COUNT(*) AS total FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = %s GROUP BY YEAR(post_date) ORDER BY year ASC ", wp_multi_get_stats_post_type())); return $years; } /** * Callback für den Menüpunkt "Veröffentlichungen". */ function wp_multi_publication_stats_page() { $tabs = wp_multi_get_publication_stats_tabs(); $current_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'month'; if (!isset($tabs[$current_tab])) { $current_tab = 'month'; } $base_url = add_query_arg(['page' => 'wp-multi-publications'], admin_url('admin.php')); wpmt_admin_page_open( __('Veröffentlichungen', 'wp-multi'), __('Wie viele Geschichten pro Monat und pro Jahr veröffentlicht wurden', 'wp-multi') ); ?> mit prozentualer Breite reicht, * lädt nichts nach und funktioniert auch ohne JavaScript. * * @param string $label Beschriftung links. * @param int $value Wert. * @param int $max Größter Wert der Reihe (für die Balkenbreite). */ function wp_multi_render_stat_bar($label, $value, $max) { $percent = $max > 0 ? round(($value / $max) * 100) : 0; ?>
' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '

'; wpmt_admin_card_close(); return; } $available_years = []; foreach ($years as $row) { $available_years[] = (int) $row->year; } // Jahr aus der URL, sonst das jüngste Jahr mit Beiträgen $selected_year = isset($_GET['stats_year']) ? (int) $_GET['stats_year'] : 0; if (!in_array($selected_year, $available_years, true)) { $selected_year = end($available_years); } $months = $wpdb->get_results($wpdb->prepare(" SELECT MONTH(post_date) AS month, COUNT(*) AS total FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = %s AND YEAR(post_date) = %d GROUP BY MONTH(post_date) ORDER BY month ASC ", wp_multi_get_stats_post_type(), $selected_year)); $month_totals = array_fill(1, 12, 0); $max_month_total = 0; $year_total = 0; foreach ((array) $months as $row) { $month_totals[(int) $row->month] = (int) $row->total; $year_total += (int) $row->total; if ((int) $row->total > $max_month_total) { $max_month_total = (int) $row->total; } } $month_url = add_query_arg('tab', 'month', $base_url); wpmt_admin_card_open(__('Veröffentlichungen pro Monat', 'wp-multi')); ?>

get_month_abbrev($wp_locale->get_month($month)), $month_totals[$month], $max_month_total); ?>
get_month($month)); ?> 0
' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '

'; wpmt_admin_card_close(); return; } $max_total = 0; $grand_total = 0; $totals_by_year = []; foreach ($years as $row) { $totals_by_year[(int) $row->year] = (int) $row->total; $grand_total += (int) $row->total; if ((int) $row->total > $max_total) { $max_total = (int) $row->total; } } $year_keys = array_keys($totals_by_year); $month_url = add_query_arg('tab', 'month', $base_url); ?>

$total) : ?>
$total) : ?>
—'; } else { $diff = $total - $totals_by_year[$year - 1]; $sign = $diff > 0 ? '+' : ''; echo '' . esc_html($sign . number_format_i18n($diff)) . ''; } ?>
'🔥 ' . __('Verlorene Vielschreiber', 'wp-multi'), 'active' => '🟢 ' . __('Aktive Autoren', 'wp-multi'), 'new' => '🆕 ' . __('Neue Autoren', 'wp-multi'), 'quiet' => '🟡 ' . __('Stille Autoren', 'wp-multi'), 'one_off' => '💤 ' . __('Einmalige Autoren', 'wp-multi'), ]; } /** * Ordnet einen Autor anhand seiner Jahreswerte einer Gruppe zu. * * 🆕 Neue Autoren – erste Geschichte im aktuellen oder letzten Jahr * 🔥 Verlorene Vielschreiber – früher ein starkes Jahr, seitdem so gut wie nichts * 🟢 Aktive Autoren – im aktuellen oder letzten Jahr veröffentlicht * 💤 Einmalige Autoren – höchstens zwei Geschichten und lange nichts mehr * 🟡 Stille Autoren – alles dazwischen: mehr als zwei Geschichten, aber still * * @param array $per_year Zuordnung "Jahr => Anzahl". * @param int $current_year Aktuelles Jahr. * @return string Gruppenschlüssel. */ function wp_multi_get_author_timeline_group($per_year, $current_year) { // Die letzten beiden Jahre gelten als "jetzt" $recent_span = (int) apply_filters('wp_multi_timeline_recent_years', 2); $recent_from = $current_year - ($recent_span - 1); $total = 0; $recent_total = 0; $earlier_peak = 0; $first_year = null; foreach ($per_year as $year => $count) { $year = (int) $year; $count = (int) $count; $total += $count; if ($first_year === null || $year < $first_year) { $first_year = $year; } if ($year >= $recent_from) { $recent_total += $count; } elseif ($count > $earlier_peak) { $earlier_peak = $count; } } // Wer erst im "Jetzt"-Fenster angefangen hat, ist neu if ($first_year !== null && $first_year >= $recent_from) { return 'new'; } $prolific_min = (int) apply_filters('wp_multi_timeline_prolific_min', 5); $recent_max = (int) apply_filters('wp_multi_timeline_recent_max', 1); if ($earlier_peak >= $prolific_min && $recent_total <= $recent_max) { return 'lost'; } if ($recent_total > 0) { return 'active'; } if ($total <= 2) { return 'one_off'; } return 'quiet'; } /** * Liest die Veröffentlichungen je Autor und Jahr. * * @return array Zuordnung "Autor => [Jahr => Anzahl]". */ function wp_multi_get_author_timeline_data() { static $data = null; if ($data !== null) { return $data; } global $wpdb; $rows = $wpdb->get_results($wpdb->prepare(" SELECT pm.meta_value AS guest_author, YEAR(p.post_date) AS year, COUNT(*) AS total FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_guest_author' AND pm.meta_value != '' WHERE p.post_status = 'publish' AND p.post_type = %s GROUP BY pm.meta_value, YEAR(p.post_date) ", wp_multi_get_stats_post_type())); $data = []; foreach ((array) $rows as $row) { $data[$row->guest_author][(int) $row->year] = (int) $row->total; } return $data; } /** * Reiter "Autoren-Zeitlinie": wer wann geschrieben hat und wer abgewandert ist. * * @param string $base_url Basis-URL der Seite. */ function wp_multi_render_author_timeline($base_url) { $years = wp_multi_get_publication_years(); $timeline = wp_multi_get_author_timeline_data(); if (!$years || !$timeline) { wpmt_admin_card_open(__('Autoren-Zeitlinie', 'wp-multi')); echo '

' . esc_html__('Noch keine Daten für eine Zeitlinie vorhanden.', 'wp-multi') . '

'; wpmt_admin_card_close(); return; } $available_years = []; foreach ($years as $row) { $available_years[] = (int) $row->year; } $current_year = (int) end($available_years); // Nur die letzten Jahre als Spalten, sonst wird die Tabelle unlesbar $columns = (int) apply_filters('wp_multi_timeline_columns', 6); $column_years = array_slice($available_years, -$columns); $groups = wp_multi_get_timeline_groups(); $group_filter = isset($_GET['group']) ? sanitize_key($_GET['group']) : ''; if (!isset($groups[$group_filter])) { $group_filter = ''; } // Autoren aufbereiten: Gruppe, Gesamtzahl und Jahreswerte $authors = []; $group_counts = array_fill_keys(array_keys($groups), 0); foreach ($timeline as $name => $per_year) { $group = wp_multi_get_author_timeline_group($per_year, $current_year); $group_counts[$group]++; $total = 0; foreach ($per_year as $count) { $total += (int) $count; } if ($group_filter !== '' && $group !== $group_filter) { continue; } $authors[] = [ 'name' => $name, 'group' => $group, 'total' => $total, 'per_year' => $per_year, ]; } // Nach Gesamtzahl sortieren – die produktivsten zuerst usort($authors, function ($a, $b) { if ($a['total'] === $b['total']) { return strcasecmp($a['name'], $b['name']); } return $b['total'] - $a['total']; }); $timeline_url = add_query_arg('tab', 'timeline', $base_url); $author_base_url = add_query_arg(['page' => 'guest_author_overview'], admin_url('admin.php')); wpmt_admin_card_open(__('Autoren-Zeitlinie', 'wp-multi')); ?>

$label) : ?> | ()

·

[Jahr => Anzahl]". * @param string $author_base_url Basis-URL der Autoren-Detailansicht. */ function wp_multi_render_year_comparison($base_url, $available_years, $timeline, $author_base_url) { if (count($available_years) < 2) { return; } // Standard: die beiden letzten abgeschlossenen Jahre $default_to = $available_years[count($available_years) - 2]; $default_from = $available_years[count($available_years) - 3] ?? $available_years[0]; $from = isset($_GET['cmp_from']) ? (int) $_GET['cmp_from'] : $default_from; $to = isset($_GET['cmp_to']) ? (int) $_GET['cmp_to'] : $default_to; if (!in_array($from, $available_years, true)) { $from = $default_from; } if (!in_array($to, $available_years, true)) { $to = $default_to; } $rows = []; $sum_from = 0; $sum_to = 0; foreach ($timeline as $name => $per_year) { $count_from = isset($per_year[$from]) ? (int) $per_year[$from] : 0; $count_to = isset($per_year[$to]) ? (int) $per_year[$to] : 0; $sum_from += $count_from; $sum_to += $count_to; if ($count_from === $count_to) { continue; } $rows[] = [ 'name' => $name, 'from' => $count_from, 'to' => $count_to, 'diff' => $count_to - $count_from, ]; } // Größte Rückgänge zuerst usort($rows, function ($a, $b) { if ($a['diff'] === $b['diff']) { return strcasecmp($a['name'], $b['name']); } return $a['diff'] - $b['diff']; }); $compare_url = add_query_arg('tab', 'timeline', $base_url); $total_diff = $sum_to - $sum_from; wpmt_admin_card_open(__('Jahresvergleich', 'wp-multi')); ?>

0 ? '+' : '') . number_format_i18n($total_diff)) ); ?>

+