Files
wp-multi/includes/publication-stats.php
T
2026-08-23 15:23:16 +00:00

697 lines
27 KiB
PHP
Raw 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: Veröffentlichungs-Statistik
*
* Zeigt, wie viele Geschichten pro Monat und pro Jahr veröffentlicht wurden.
* Eigener Menüpunkt, unabhängig von der Gast-Autoren-Übersicht.
*/
if (!defined('ABSPATH')) { exit; }
/**
* Liefert die Reiter der Statistik-Seite.
*/
function wp_multi_get_publication_stats_tabs() {
return [
'month' => __('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')
);
?>
<h2 class="nav-tab-wrapper wpm-tabs">
<?php foreach ($tabs as $tab_key => $tab_label) : ?>
<a href="<?php echo esc_url(add_query_arg('tab', $tab_key, $base_url)); ?>"
class="nav-tab<?php echo ($current_tab === $tab_key) ? ' nav-tab-active' : ''; ?>">
<?php echo esc_html($tab_label); ?>
</a>
<?php endforeach; ?>
</h2>
<?php
if ($current_tab === 'year') {
wp_multi_render_publication_years($base_url);
} elseif ($current_tab === 'timeline') {
wp_multi_render_author_timeline($base_url);
} else {
wp_multi_render_publication_months($base_url);
}
wpmt_admin_page_close();
}
/**
* Gibt eine Balkenzeile für die Statistik aus.
*
* Bewusst ohne Diagramm-Bibliothek: ein <div> 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;
?>
<div class="wpm-stat-row" style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">
<span class="wpm-stat-label" style="flex:0 0 6em;"><?php echo esc_html($label); ?></span>
<span class="wpm-stat-track" style="flex:1 1 auto;background:rgba(125,125,125,.2);border-radius:3px;overflow:hidden;">
<span class="wpm-stat-fill" style="display:block;height:14px;width:<?php echo (int) $percent; ?>%;min-width:<?php echo $value > 0 ? '2px' : '0'; ?>;background:#2271b1;"></span>
</span>
<span class="wpm-stat-value" style="flex:0 0 5em;text-align:right;"><?php echo esc_html(number_format_i18n($value)); ?></span>
</div>
<?php
}
/**
* Reiter "Monat": Veröffentlichungen je Monat eines Jahres.
*
* @param string $base_url Basis-URL der Seite.
*/
function wp_multi_render_publication_months($base_url) {
global $wpdb, $wp_locale;
$years = wp_multi_get_publication_years();
if (!$years) {
wpmt_admin_card_open(__('Veröffentlichungen pro Monat', 'wp-multi'));
echo '<p class="wpm-muted">' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '</p>';
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'));
?>
<p class="wpm-muted">
<?php _e('Jahr:', 'wp-multi'); ?>
<?php foreach (array_reverse($available_years) as $year) : ?>
<a href="<?php echo esc_url(add_query_arg('stats_year', $year, $month_url)); ?>" class="wpm-filter-link<?php echo ($year === $selected_year) ? ' wpm-filter-active' : ''; ?>">
<?php if ($year === $selected_year) : ?><strong><?php endif; ?>
<?php echo esc_html($year); ?>
<?php if ($year === $selected_year) : ?></strong><?php endif; ?>
</a>
<?php endforeach; ?>
</p>
<p class="wpm-muted">
<?php
printf(
/* translators: 1: Jahr, 2: Anzahl der Geschichten */
esc_html__('%1$d: %2$s Geschichten', 'wp-multi'),
(int) $selected_year,
esc_html(number_format_i18n($year_total))
);
?>
</p>
<div class="wpm-stat-chart" style="margin:16px 0;">
<?php for ($month = 1; $month <= 12; $month++) : ?>
<?php wp_multi_render_stat_bar($wp_locale->get_month_abbrev($wp_locale->get_month($month)), $month_totals[$month], $max_month_total); ?>
<?php endfor; ?>
</div>
<div class="wpm-table-wrap">
<table class="wpm-table">
<thead>
<tr>
<th><?php _e('Monat', 'wp-multi'); ?></th>
<th><?php _e('Geschichten', 'wp-multi'); ?></th>
</tr>
</thead>
<tbody>
<?php for ($month = 1; $month <= 12; $month++) : ?>
<tr>
<td><?php echo esc_html($wp_locale->get_month($month)); ?></td>
<td>
<?php if ($month_totals[$month]) : ?>
<?php echo esc_html(number_format_i18n($month_totals[$month])); ?>
<?php else : ?>
<span class="wpm-muted">0</span>
<?php endif; ?>
</td>
</tr>
<?php endfor; ?>
</tbody>
</table>
</div>
<?php
wpmt_admin_card_close();
}
/**
* Reiter "Jahr": Veröffentlichungen je Jahr.
*
* @param string $base_url Basis-URL der Seite.
*/
function wp_multi_render_publication_years($base_url) {
$years = wp_multi_get_publication_years();
wpmt_admin_card_open(__('Veröffentlichungen pro Jahr', 'wp-multi'));
if (!$years) {
echo '<p class="wpm-muted">' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '</p>';
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);
?>
<p class="wpm-muted">
<?php
printf(
/* translators: 1: Gesamtzahl, 2: erstes Jahr, 3: letztes Jahr */
esc_html__('%1$s Geschichten von %2$d bis %3$d.', 'wp-multi'),
esc_html(number_format_i18n($grand_total)),
(int) reset($year_keys),
(int) end($year_keys)
);
?>
</p>
<div class="wpm-stat-chart" style="margin:16px 0;">
<?php foreach ($totals_by_year as $year => $total) : ?>
<?php wp_multi_render_stat_bar($year, $total, $max_total); ?>
<?php endforeach; ?>
</div>
<div class="wpm-table-wrap">
<table class="wpm-table">
<thead>
<tr>
<th><?php _e('Jahr', 'wp-multi'); ?></th>
<th><?php _e('Geschichten', 'wp-multi'); ?></th>
<th><?php _e('Veränderung', 'wp-multi'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach (array_reverse($totals_by_year, true) as $year => $total) : ?>
<tr>
<td>
<a href="<?php echo esc_url(add_query_arg('stats_year', (int) $year, $month_url)); ?>" title="<?php esc_attr_e('Monate dieses Jahres anzeigen', 'wp-multi'); ?>">
<?php echo esc_html($year); ?>
</a>
</td>
<td><?php echo esc_html(number_format_i18n($total)); ?></td>
<td>
<?php
// Vergleich mit dem Vorjahr, sofern es Beiträge hatte
if (!isset($totals_by_year[$year - 1])) {
echo '<span class="wpm-muted">—</span>';
} else {
$diff = $total - $totals_by_year[$year - 1];
$sign = $diff > 0 ? '+' : '';
echo '<span class="wpm-muted">' . esc_html($sign . number_format_i18n($diff)) . '</span>';
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
wpmt_admin_card_close();
}
/**
* Liefert die Beschriftungen der Autorengruppen.
*/
function wp_multi_get_timeline_groups() {
return [
'lost' => '🔥 ' . __('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 '<p class="wpm-muted">' . esc_html__('Noch keine Daten für eine Zeitlinie vorhanden.', 'wp-multi') . '</p>';
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'));
?>
<p class="wpm-muted">
<?php _e('Gruppe:', 'wp-multi'); ?>
<a href="<?php echo esc_url(remove_query_arg('group', $timeline_url)); ?>" class="wpm-filter-link<?php echo ($group_filter === '') ? ' wpm-filter-active' : ''; ?>">
<?php if ($group_filter === '') : ?><strong><?php endif; ?>
<?php printf(esc_html__('Alle (%d)', 'wp-multi'), count($timeline)); ?>
<?php if ($group_filter === '') : ?></strong><?php endif; ?>
</a>
<?php foreach ($groups as $key => $label) : ?>
|
<a href="<?php echo esc_url(add_query_arg('group', $key, $timeline_url)); ?>" class="wpm-filter-link<?php echo ($group_filter === $key) ? ' wpm-filter-active' : ''; ?>">
<?php if ($group_filter === $key) : ?><strong><?php endif; ?>
<?php echo esc_html($label); ?> (<?php echo (int) $group_counts[$key]; ?>)
<?php if ($group_filter === $key) : ?></strong><?php endif; ?>
</a>
<?php endforeach; ?>
</p>
<div class="wpm-table-wrap">
<table class="wpm-table">
<thead>
<tr>
<th><?php _e('Autor', 'wp-multi'); ?></th>
<?php foreach ($column_years as $year) : ?>
<th><?php echo esc_html($year); ?></th>
<?php endforeach; ?>
<th><?php _e('Gesamt', 'wp-multi'); ?></th>
<th><?php _e('Gruppe', 'wp-multi'); ?></th>
</tr>
</thead>
<tbody>
<?php if ($authors) : ?>
<?php foreach ($authors as $author) : ?>
<tr>
<td>
<?php if (function_exists('wp_multi_get_guest_author_detail_url')) : ?>
<a href="<?php echo esc_url(wp_multi_get_guest_author_detail_url($author_base_url, $author['name'])); ?>"><?php echo esc_html($author['name']); ?></a>
<?php else : ?>
<?php echo esc_html($author['name']); ?>
<?php endif; ?>
</td>
<?php foreach ($column_years as $year) : ?>
<?php $count = isset($author['per_year'][$year]) ? (int) $author['per_year'][$year] : 0; ?>
<td>
<?php if ($count) : ?>
<?php echo esc_html(number_format_i18n($count)); ?>
<?php else : ?>
<span class="wpm-muted">·</span>
<?php endif; ?>
</td>
<?php endforeach; ?>
<td><strong><?php echo esc_html(number_format_i18n($author['total'])); ?></strong></td>
<td><span class="wpm-muted"><?php echo esc_html($groups[$author['group']]); ?></span></td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr><td colspan="<?php echo (int) (count($column_years) + 3); ?>"><?php _e('Keine Autoren in dieser Gruppe.', 'wp-multi'); ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<p class="wpm-muted">
<?php
printf(
/* translators: 1: Anzahl Jahre, 2: Mindestanzahl für Vielschreiber */
esc_html__('Spalten zeigen die letzten %1$d Jahre. Als "verlorener Vielschreiber" gilt, wer früher mindestens %2$d Geschichten in einem Jahr hatte und seither so gut wie nichts mehr veröffentlicht.', 'wp-multi'),
count($column_years),
(int) apply_filters('wp_multi_timeline_prolific_min', 5)
);
?>
</p>
<?php
wpmt_admin_card_close();
wp_multi_render_year_comparison($base_url, $available_years, $timeline, $author_base_url);
}
/**
* Vergleicht zwei Jahre und zeigt, bei welchen Autoren die Differenz entsteht.
*
* @param string $base_url Basis-URL der Seite.
* @param array $available_years Alle Jahre mit Beiträgen.
* @param array $timeline Zuordnung "Autor => [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'));
?>
<p class="wpm-muted">
<?php _e('Von:', 'wp-multi'); ?>
<?php foreach (array_reverse($available_years) as $year) : ?>
<a href="<?php echo esc_url(add_query_arg(['cmp_from' => $year, 'cmp_to' => $to], $compare_url)); ?>" class="wpm-filter-link<?php echo ($year === $from) ? ' wpm-filter-active' : ''; ?>">
<?php if ($year === $from) : ?><strong><?php endif; ?><?php echo esc_html($year); ?><?php if ($year === $from) : ?></strong><?php endif; ?>
</a>
<?php endforeach; ?>
</p>
<p class="wpm-muted">
<?php _e('Bis:', 'wp-multi'); ?>
<?php foreach (array_reverse($available_years) as $year) : ?>
<a href="<?php echo esc_url(add_query_arg(['cmp_from' => $from, 'cmp_to' => $year], $compare_url)); ?>" class="wpm-filter-link<?php echo ($year === $to) ? ' wpm-filter-active' : ''; ?>">
<?php if ($year === $to) : ?><strong><?php endif; ?><?php echo esc_html($year); ?><?php if ($year === $to) : ?></strong><?php endif; ?>
</a>
<?php endforeach; ?>
</p>
<p>
<?php
printf(
/* translators: 1: erstes Jahr, 2: Anzahl, 3: zweites Jahr, 4: Anzahl, 5: Differenz */
esc_html__('%1$d: %2$s Geschichten %3$d: %4$s Geschichten Differenz: %5$s', 'wp-multi'),
(int) $from,
esc_html(number_format_i18n($sum_from)),
(int) $to,
esc_html(number_format_i18n($sum_to)),
esc_html(($total_diff > 0 ? '+' : '') . number_format_i18n($total_diff))
);
?>
</p>
<div class="wpm-table-wrap">
<table class="wpm-table">
<thead>
<tr>
<th><?php _e('Autor', 'wp-multi'); ?></th>
<th><?php echo esc_html($from); ?></th>
<th><?php echo esc_html($to); ?></th>
<th><?php _e('Differenz', 'wp-multi'); ?></th>
</tr>
</thead>
<tbody>
<?php if ($rows) : ?>
<?php foreach ($rows as $row) : ?>
<tr>
<td>
<?php if (function_exists('wp_multi_get_guest_author_detail_url')) : ?>
<a href="<?php echo esc_url(wp_multi_get_guest_author_detail_url($author_base_url, $row['name'])); ?>"><?php echo esc_html($row['name']); ?></a>
<?php else : ?>
<?php echo esc_html($row['name']); ?>
<?php endif; ?>
</td>
<td><?php echo esc_html(number_format_i18n($row['from'])); ?></td>
<td><?php echo esc_html(number_format_i18n($row['to'])); ?></td>
<td>
<?php if ($row['diff'] < 0) : ?>
<span class="wpm-badge wpm-badge--danger"><?php echo esc_html(number_format_i18n($row['diff'])); ?></span>
<?php else : ?>
<span class="wpm-badge wpm-badge--success">+<?php echo esc_html(number_format_i18n($row['diff'])); ?></span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr><td colspan="4"><?php _e('Kein Unterschied zwischen diesen Jahren.', 'wp-multi'); ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<p class="wpm-muted"><?php _e('Gezählt werden nur Geschichten mit hinterlegtem Gast-Autor. Beiträge ohne dieses Feld tauchen in der Jahresstatistik auf, hier aber nicht.', 'wp-multi'); ?></p>
<?php
wpmt_admin_card_close();
}