403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/clients/client2/web20/web/wp-content/plugins/dgt-verkehrsmonitor/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web20/web/wp-content/plugins/dgt-verkehrsmonitor/includes/render.php
<?php
/**
 * Render-Helfer: Breadcrumb, Pagination, Statistik-Kacheln, Meldungskarten.
 *
 * @package dgt-verkehrsmonitor
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* ─── Breadcrumb ───────────────────────────────────────── */

function dgt_vm_breadcrumb( $crumbs = array() ) {
	echo '<nav class="dgt-breadcrumb" aria-label="Breadcrumb"><ol>';
	echo '<li><a href="' . esc_url( home_url( '/' ) ) . '" title="Startseite drogentest-online.de">Start</a></li>';
	echo '<li><a href="' . esc_url( dgt_vm_url() ) . '" title="Verkehrsmonitor Deutschland – Polizeimeldungen zu Alkohol &amp; Drogen am Steuer">Verkehrsmonitor</a></li>';

	$last = count( $crumbs ) - 1;
	foreach ( $crumbs as $i => $c ) {
		if ( $i === $last ) {
			echo '<li aria-current="page">' . esc_html( $c['label'] ) . '</li>';
		} else {
			$crumb_title = isset( $c['title'] ) ? $c['title'] : $c['label'];
			echo '<li><a href="' . esc_url( $c['url'] ) . '" title="' . esc_attr( $crumb_title ) . '">' . esc_html( $c['label'] ) . '</a></li>';
		}
	}
	echo '</ol></nav>';
}

/* ─── Statistik-Kacheln ────────────────────────────────── */

function dgt_vm_stat_tiles( $stats, $prev = null ) {
	$tiles = array(
		array(
			'label' => 'Alkoholfahrten',
			'value' => isset( $stats->alkohol ) ? (int) $stats->alkohol : 0,
			'prev'  => $prev ? (int) $prev->alkohol : 0,
			'class' => 'vm-tile--alkohol',
		),
		array(
			'label' => 'Drogenfahrten',
			'value' => isset( $stats->drogen ) ? (int) $stats->drogen : 0,
			'prev'  => $prev ? (int) $prev->drogen : 0,
			'class' => 'vm-tile--drogen',
		),
		array(
			'label' => 'Unfälle',
			'value' => isset( $stats->unfaelle ) ? (int) $stats->unfaelle : 0,
			'prev'  => $prev ? (int) $prev->unfaelle : 0,
			'class' => 'vm-tile--unfall',
		),
		array(
			'label' => 'Führerscheine',
			'value' => isset( $stats->fs_entzogen ) ? (int) $stats->fs_entzogen : 0,
			'prev'  => $prev ? (int) $prev->fs_entzogen : 0,
			'class' => 'vm-tile--fs',
		),
	);

	if ( isset( $stats->max_bak ) && $stats->max_bak > 0 ) {
		$tiles[] = array(
			'label' => 'Promille max.',
			'value' => number_format( (float) $stats->max_bak, 2, ',', '' ) . ' ‰',
			'prev'  => 0,
			'class' => 'vm-tile--bak',
			'raw'   => true,
		);
	}

	echo '<div class="vm-tiles">';
	foreach ( $tiles as $t ) {
		$display = ! empty( $t['raw'] ) ? esc_html( $t['value'] ) : esc_html( number_format_i18n( $t['value'] ) );
		printf(
			'<div class="vm-tile %s"><span class="vm-tile-val">%s</span><span class="vm-tile-label">%s</span></div>',
			esc_attr( $t['class'] ),
			$display,
			esc_html( $t['label'] )
		);
	}
	echo '</div>';
}

/* ─── Meldungs-Karte ──────────────────────────────────── */

function dgt_vm_incident_card( $item ) {
	$url = dgt_vm_incident_url( $item );
	$bl  = dgt_vm_bundesland_name( $item->bundesland );
	?>
	<article class="vm-card <?php echo esc_attr( dgt_vm_delikt_class( $item->delikt ) ); ?>">
		<a href="<?php echo esc_url( $url ); ?>" class="vm-card-link" title="<?php echo esc_attr( $item->ai_headline . ' – Polizeimeldung ' . ( $item->stadt ? $item->stadt . ', ' : '' ) . $bl ); ?>">
			<span class="vm-card-badge"><?php echo esc_html( dgt_vm_delikt_label( $item->delikt ) ); ?></span>
			<h3 class="vm-card-title"><?php echo esc_html( $item->ai_headline ); ?></h3>
			<p class="vm-card-summary"><?php echo esc_html( wp_trim_words( $item->ai_summary, 20 ) ); ?></p>
			<span class="vm-card-meta">
				<?php echo esc_html( $item->stadt ? $item->stadt . ' · ' : '' ); ?>
				<?php echo esc_html( $bl ); ?> ·
				<?php echo esc_html( dgt_vm_format_date( $item->source_date ) ); ?>
			</span>
		</a>
	</article>
	<?php
}

/* ─── Meldungs-Liste ──────────────────────────────────── */

function dgt_vm_incident_list( $items ) {
	if ( empty( $items ) ) {
		echo '<p class="vm-empty">Noch keine Meldungen in diesem Bereich.</p>';
		return;
	}
	echo '<div class="vm-cards">';
	foreach ( $items as $item ) {
		dgt_vm_incident_card( $item );
	}
	echo '</div>';
}

function dgt_vm_incident_list_with_highlights( $items, $hl_ids = array() ) {
	if ( empty( $items ) ) {
		echo '<p class="vm-empty">Noch keine Meldungen in diesem Bereich.</p>';
		return;
	}
	echo '<div class="vm-cards">';
	foreach ( $items as $item ) {
		if ( in_array( (int) $item->id, $hl_ids, true ) ) {
			dgt_vm_incident_card_highlight( $item );
		} else {
			dgt_vm_incident_card( $item );
		}
	}
	echo '</div>';
}

function dgt_vm_incident_card_highlight( $item ) {
	$url    = dgt_vm_incident_url( $item );
	$bl     = dgt_vm_bundesland_name( $item->bundesland );
	$images = json_decode( $item->images ?? '[]', true );
	$thumb  = ( is_array( $images ) && $images ) ? content_url( 'uploads/vm-images/' . $images[0] ) : '';
	?>
	<article class="vm-card vm-card--highlight <?php echo esc_attr( dgt_vm_delikt_class( $item->delikt ) ); ?>">
		<a href="<?php echo esc_url( $url ); ?>" class="vm-card-link vm-card-link--highlight" title="<?php echo esc_attr( $item->ai_headline . ' – Polizeimeldung ' . ( $item->stadt ? $item->stadt . ', ' : '' ) . $bl ); ?>">
			<?php if ( $thumb ) : ?>
				<img class="vm-card-highlight-img" src="<?php echo esc_url( $thumb ); ?>" alt="<?php echo esc_attr( $item->ai_headline ); ?>" loading="lazy">
			<?php endif; ?>
			<div class="vm-card-highlight-body">
				<span class="vm-card-badge"><?php echo esc_html( dgt_vm_delikt_label( $item->delikt ) ); ?></span>
				<h3 class="vm-card-title"><?php echo esc_html( $item->ai_headline ); ?></h3>
				<p class="vm-card-summary"><?php echo esc_html( wp_trim_words( $item->ai_summary, 35 ) ); ?></p>
				<span class="vm-card-meta">
					<?php echo esc_html( $item->stadt ? $item->stadt . ' · ' : '' ); ?>
					<?php echo esc_html( $bl ); ?> ·
					<?php echo esc_html( dgt_vm_format_date( $item->source_date ) ); ?>
				</span>
			</div>
		</a>
	</article>
	<?php
}

/* ─── Meldungs-Zeile (kompakt, für Listen) ─────────────── */

function dgt_vm_incident_row( $item ) {
	$url = dgt_vm_incident_url( $item );
	$bl  = dgt_vm_bundesland_name( $item->bundesland );
	?>
	<a href="<?php echo esc_url( $url ); ?>" class="vm-row <?php echo esc_attr( dgt_vm_delikt_class( $item->delikt ) ); ?>" title="<?php echo esc_attr( $item->ai_headline . ' – ' . ( $item->stadt ? $item->stadt . ', ' : '' ) . $bl ); ?>">
		<span class="vm-row-badge"><?php echo esc_html( dgt_vm_delikt_label( $item->delikt ) ); ?></span>
		<span class="vm-row-title"><?php echo esc_html( $item->ai_headline ); ?></span>
		<span class="vm-row-meta">
			<?php echo esc_html( $item->stadt ? $item->stadt : $bl ); ?> ·
			<?php echo esc_html( dgt_vm_format_date( $item->source_date ) ); ?>
		</span>
	</a>
	<?php
}

/* ─── Pagination ───────────────────────────────────────── */

function dgt_vm_pagination( $result, $base_url ) {
	if ( $result['pages'] <= 1 ) {
		return;
	}

	$paged = $result['paged'];
	$pages = $result['pages'];

	echo '<nav class="vm-pagination" aria-label="Seitennavigation">';

	if ( $paged > 1 ) {
		printf(
			'<a class="vm-page-link" href="%s" title="Vorherige Seite (Seite %d)">&larr; Zurück</a>',
			esc_url( $base_url . 'seite/' . ( $paged - 1 ) . '/' ),
			$paged - 1
		);
	}

	for ( $i = max( 1, $paged - 2 ); $i <= min( $pages, $paged + 2 ); $i++ ) {
		if ( $i === $paged ) {
			printf( '<span class="vm-page-link vm-page-current">%d</span>', $i );
		} else {
			printf(
				'<a class="vm-page-link" href="%s" title="Seite %d von %d">%d</a>',
				esc_url( $base_url . 'seite/' . $i . '/' ),
				$i,
				$pages,
				$i
			);
		}
	}

	if ( $paged < $pages ) {
		printf(
			'<a class="vm-page-link" href="%s" title="Nächste Seite (Seite %d)">Weiter &rarr;</a>',
			esc_url( $base_url . 'seite/' . ( $paged + 1 ) . '/' ),
			$paged + 1
		);
	}

	echo '</nav>';
}

/* ─── Bundesland-Grid ──────────────────────────────────── */

function dgt_vm_bundesland_grid( $stats = array() ) {
	$laender  = dgt_vm_bundeslaender();
	$kuerzel  = dgt_vm_bundesland_kuerzel();

	echo '<div class="vm-bl-grid">';
	foreach ( $laender as $slug => $name ) {
		$count = isset( $stats[ $slug ] ) ? (int) $stats[ $slug ] : 0;
		$kz    = isset( $kuerzel[ $slug ] ) ? $kuerzel[ $slug ] : '';
		printf(
			'<a class="vm-bl-chip" href="%s" title="Polizeimeldungen zu Alkohol &amp; Drogen am Steuer in %s"><span class="vm-bl-kz">%s</span><span class="vm-bl-count">%s</span></a>',
			esc_url( dgt_vm_bundesland_url( $slug ) ),
			esc_attr( $name ),
			esc_html( $kz ),
			esc_html( $count ? number_format_i18n( $count ) : '–' )
		);
	}
	echo '</div>';
}

/* ─── Fakten-Box (Einzelmeldung) ───────────────────────── */

function dgt_vm_facts_box( $item ) {
	$icons = array(
		'delikt'       => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 9v2m0 4h.01M5.07 19H19a2 2 0 001.73-3L13.73 4a2 2 0 00-3.46 0L3.34 16A2 2 0 005.07 19z"/></svg>',
		'faelle'       => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>',
		'bak'          => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 2h8l-2 9H10L8 2z"/><path d="M12 11v11"/><path d="M8 22h8"/></svg>',
		'substanzen'   => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-6 9h9"/></svg>',
		'thc'          => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M7 21h10"/><path d="M12 21V11"/><path d="M12 11C8 11 4 7 4 3h16c0 4-4 8-8 8z"/></svg>',
		'alter'        => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
		'fahrzeug'     => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/><path d="M5 17H3v-6l2-5h9l4 5h3v6h-2"/></svg>',
		'unfall'       => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
		'fs'           => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M12 12a3 3 0 100-6 3 3 0 000 6z"/><path d="M6 20c0-2 2.7-4 6-4s6 2 6 4"/></svg>',
		'ort'          => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>',
		'bundesland'   => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 21h18"/><path d="M5 21V7l8-4v18"/><path d="M19 21V11l-6-4"/><path d="M9 9v.01M9 12v.01M9 15v.01M9 18v.01"/></svg>',
		'polizei'      => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>',
	);

	$cards = array();

	$cards[] = array( 'icon' => $icons['delikt'], 'label' => 'Delikt', 'value' => dgt_vm_delikt_label( $item->delikt ) );

	$anz_alk = isset( $item->anzahl_alkohol ) ? (int) $item->anzahl_alkohol : 1;
	$anz_drg = isset( $item->anzahl_drogen ) ? (int) $item->anzahl_drogen : 1;
	if ( $anz_alk > 1 || $anz_drg > 1 ) {
		$parts = array();
		if ( $item->hat_alkohol && $anz_alk > 1 ) {
			$parts[] = $anz_alk . ' Alkohol';
		}
		if ( $item->hat_drogen && $anz_drg > 1 ) {
			$parts[] = $anz_drg . ' Drogen';
		}
		if ( $parts ) {
			$cards[] = array( 'icon' => $icons['faelle'], 'label' => 'Erfasste Fälle', 'value' => implode( ', ', $parts ) );
		}
	}

	if ( $item->hat_alkohol && $item->bak_wert ) {
		$cards[] = array( 'icon' => $icons['bak'], 'label' => 'Blutalkohol', 'value' => number_format_i18n( $item->bak_wert, 2 ) . ' ‰' );
	}

	if ( $item->hat_drogen ) {
		$subs = json_decode( $item->substanzen, true );
		if ( $subs && is_array( $subs ) ) {
			$labels = array_map( 'dgt_vm_substanz_label', $subs );
			$cards[] = array( 'icon' => $icons['substanzen'], 'label' => 'Substanzen', 'value' => implode( ', ', $labels ) );
		}
		if ( $item->thc_wert ) {
			$cards[] = array( 'icon' => $icons['thc'], 'label' => 'THC-Wert', 'value' => number_format_i18n( $item->thc_wert, 1 ) . ' ng/ml' );
		}
	}

	if ( $item->alter_fahrer && $anz_alk <= 1 && $anz_drg <= 1 ) {
		$cards[] = array( 'icon' => $icons['alter'], 'label' => 'Alter', 'value' => $item->alter_fahrer . ' Jahre' );
	}
	if ( $item->fahrzeugart ) {
		$cards[] = array( 'icon' => $icons['fahrzeug'], 'label' => 'Fahrzeug', 'value' => ucfirst( $item->fahrzeugart ) );
	}
	if ( $item->hat_unfall ) {
		$cards[] = array( 'icon' => $icons['unfall'], 'label' => 'Unfall', 'value' => 'Ja (' . $item->unfall_schwere . ')' );
	}
	if ( $item->fs_sichergestellt ) {
		$cards[] = array( 'icon' => $icons['fs'], 'label' => 'Führerschein', 'value' => 'sichergestellt' );
	}

	echo '<div class="vm-facts">';
	echo '<h3 class="vm-facts-title">Fakten</h3>';
	echo '<div class="vm-facts-grid">';
	foreach ( $cards as $c ) {
		echo '<div class="vm-fact-card">';
		echo '<span class="vm-fact-icon">' . $c['icon'] . '</span>';
		echo '<span class="vm-fact-body"><span class="vm-fact-label">' . esc_html( $c['label'] ) . '</span>';
		echo '<span class="vm-fact-value">' . esc_html( $c['value'] ) . '</span></span>';
		echo '</div>';
	}
	echo '</div>';
	echo '</div>';
}

/* ─── Substanz-Kacheln ─────────────────────────────────── */

function dgt_vm_substance_tiles( $stats ) {
	if ( empty( $stats ) ) {
		return;
	}

	$total = 0;
	foreach ( $stats as $s ) {
		$total += (int) $s->anzahl;
	}

	echo '<div class="vm-sub-tiles">';
	foreach ( $stats as $s ) {
		$pct = $total ? round( $s->anzahl / $total * 100, 1 ) : 0;
		printf(
			'<div class="vm-sub-tile"><span class="vm-sub-name">%s</span><span class="vm-sub-val">%s</span><span class="vm-sub-pct">%s %%</span></div>',
			esc_html( dgt_vm_substanz_label( $s->substanz ) ),
			esc_html( number_format_i18n( $s->anzahl ) ),
			esc_html( number_format_i18n( $pct, 1 ) )
		);
	}
	echo '</div>';
}

/* ─── Balkendiagramm (CSS only) ────────────────────────── */

function dgt_vm_bar_chart( $items, $max = 0 ) {
	if ( empty( $items ) ) {
		return;
	}

	if ( ! $max ) {
		foreach ( $items as $item ) {
			if ( $item['value'] > $max ) {
				$max = $item['value'];
			}
		}
	}

	echo '<div class="vm-bars">';
	foreach ( $items as $item ) {
		$pct = $max ? round( $item['value'] / $max * 100 ) : 0;
		printf(
			'<div class="vm-bar-row"><span class="vm-bar-label">%s</span><div class="vm-bar-track"><div class="vm-bar-fill" style="width:%d%%"></div></div><span class="vm-bar-val">%s</span></div>',
			esc_html( $item['label'] ),
			$pct,
			esc_html( number_format_i18n( $item['value'] ) )
		);
	}
	echo '</div>';
}

/* ─── Single: Suchtberatung-Link ermitteln ────────────── */

function dgt_vm_suchtberatung_link( $stadt, $bundesland ) {
	global $wpdb;

	$c = $wpdb->prefix . 'connections';
	$a = $wpdb->prefix . 'connections_address';

	if ( $stadt ) {
		$results = $wpdb->get_results( $wpdb->prepare(
			"SELECT c.slug, c.organization, COALESCE(c.organization, CONCAT(c.first_name, ' ', c.last_name)) AS name
			 FROM {$a} a JOIN {$c} c ON c.id = a.entry_id
			 WHERE a.city = %s AND c.status = 'approved' AND c.visibility = 'public'
			 ORDER BY c.organization ASC
			 LIMIT 5",
			$stadt
		) );

		if ( $results ) {
			$bl_slug = $bundesland;
			$items = array();
			foreach ( $results as $r ) {
				$items[] = array(
					'name' => trim( $r->name ),
					'url'  => home_url( '/suchtberatung/bundesland/' . $bl_slug . '/' . $r->slug . '/' ),
				);
			}
			return array(
				'type'  => 'stellen',
				'stadt' => $stadt,
				'items' => $items,
				'more'  => home_url( '/suchtberatung/bundesland/' . $bl_slug . '/' . sanitize_title( $stadt ) . '/' ),
			);
		}
	}

	return array(
		'type' => 'bundesland',
		'url'  => home_url( '/suchtberatung/bundesland/' . $bundesland . '/' ),
	);
}

/* ─── Single: Bundesland-Statistiken laden ────────────── */

function dgt_vm_single_bl_stats( $bundesland ) {
	global $wpdb;
	$t = dgt_vm_table( 'incidents' );

	$now  = current_time( 'Y-m-d' );
	$m1_start = gmdate( 'Y-m-01', strtotime( $now ) );
	$m0_start = gmdate( 'Y-m-01', strtotime( '-1 month', strtotime( $now ) ) );
	$m0_end   = gmdate( 'Y-m-t', strtotime( $m0_start ) );

	$cur = $wpdb->get_row( $wpdb->prepare(
		"SELECT
			SUM(CASE WHEN hat_alkohol = 1 THEN anzahl_alkohol ELSE 0 END) AS alk,
			SUM(CASE WHEN hat_drogen = 1 THEN anzahl_drogen ELSE 0 END) AS drg
		 FROM {$t} WHERE bundesland = %s AND status = 'publiziert' AND source_date >= %s",
		$bundesland, $m1_start
	) );

	$prev = $wpdb->get_row( $wpdb->prepare(
		"SELECT
			SUM(CASE WHEN hat_alkohol = 1 THEN anzahl_alkohol ELSE 0 END) AS alk,
			SUM(CASE WHEN hat_drogen = 1 THEN anzahl_drogen ELSE 0 END) AS drg
		 FROM {$t} WHERE bundesland = %s AND status = 'publiziert' AND source_date BETWEEN %s AND %s",
		$bundesland, $m0_start, $m0_end
	) );

	$alk = (int) ( $cur->alk ?? 0 );
	$drg = (int) ( $cur->drg ?? 0 );
	$p_alk = (int) ( $prev->alk ?? 0 );
	$p_drg = (int) ( $prev->drg ?? 0 );

	$alk_trend = '';
	if ( $p_alk > 0 ) {
		$pct = round( ( $alk - $p_alk ) / $p_alk * 100, 1 );
		$alk_trend = ( $pct >= 0 ? '+' : '' ) . number_format_i18n( $pct, 1 ) . ' %';
	}
	$drg_trend = '';
	if ( $p_drg > 0 ) {
		$pct = round( ( $drg - $p_drg ) / $p_drg * 100, 1 );
		$drg_trend = ( $pct >= 0 ? '+' : '' ) . number_format_i18n( $pct, 1 ) . ' %';
	}

	return array(
		'alkohol'   => $alk,
		'drogen'    => $drg,
		'alk_trend' => $alk_trend,
		'drg_trend' => $drg_trend,
	);
}

function dgt_vm_single_day_stats( $date, $bundesland ) {
	global $wpdb;
	$t = dgt_vm_table( 'incidents' );

	$day = $wpdb->get_row( $wpdb->prepare(
		"SELECT
			SUM(CASE WHEN hat_alkohol = 1 THEN anzahl_alkohol ELSE 0 END) AS alk,
			SUM(CASE WHEN hat_drogen = 1 THEN anzahl_drogen ELSE 0 END) AS drg,
			COUNT(*) AS total
		 FROM {$t} WHERE source_date = %s AND status = 'publiziert'",
		$date
	) );

	$bl = $wpdb->get_row( $wpdb->prepare(
		"SELECT
			SUM(CASE WHEN hat_alkohol = 1 THEN anzahl_alkohol ELSE 0 END) AS alk,
			COUNT(*) AS total
		 FROM {$t} WHERE source_date = %s AND bundesland = %s AND status = 'publiziert'",
		$date, $bundesland
	) );

	return array(
		'day_alk'   => (int) ( $day->alk ?? 0 ),
		'day_total' => (int) ( $day->total ?? 0 ),
		'bl_total'  => (int) ( $bl->total ?? 0 ),
	);
}

/* ─── Single: Kennzahlen-Karten ───────────────────────── */

function dgt_vm_single_stat_cards( $item, $bl_stats, $day_stats ) {
	$bl_name = dgt_vm_bundesland_name( $item->bundesland );
	$bl_kz   = dgt_vm_bundesland_kuerzel();
	$kz      = isset( $bl_kz[ $item->bundesland ] ) ? $bl_kz[ $item->bundesland ] : '';

	$cards = array();

	if ( $item->hat_alkohol && $bl_stats ) {
		$cards[] = array(
			'label' => 'Alkoholdelikte in ' . $kz,
			'value' => number_format_i18n( $bl_stats['alkohol'] ),
		);
		if ( $bl_stats['alk_trend'] ) {
			$cards[] = array(
				'label' => 'Veränderung zum Vormonat',
				'value' => $bl_stats['alk_trend'],
			);
		}
	} elseif ( $item->hat_drogen && $bl_stats ) {
		$cards[] = array(
			'label' => 'Drogendelikte in ' . $kz,
			'value' => number_format_i18n( $bl_stats['drogen'] ),
		);
		if ( $bl_stats['drg_trend'] ) {
			$cards[] = array(
				'label' => 'Veränderung zum Vormonat',
				'value' => $bl_stats['drg_trend'],
			);
		}
	}

	if ( $day_stats['day_alk'] > 0 ) {
		$cards[] = array(
			'label' => 'Erfasste Alkoholfahrten am ' . date_i18n( 'j.n.', strtotime( $item->source_date ) ),
			'value' => number_format_i18n( $day_stats['day_alk'] ),
		);
	}

	if ( $day_stats['day_total'] > 0 && $day_stats['day_alk'] > 0 ) {
		$pct = round( $day_stats['day_alk'] / $day_stats['day_total'] * 100 );
		$cards[] = array(
			'label' => 'Anteil Alkoholdelikte',
			'value' => $pct . ' %',
		);
	}

	if ( empty( $cards ) ) {
		return;
	}

	echo '<div class="vm-s-stat-cards">';
	echo '<h2 class="vm-s-section-title">Statistiken (' . esc_html( $kz ) . ')</h2>';
	echo '<div class="vm-s-stats-grid">';
	foreach ( $cards as $c ) {
		printf(
			'<div class="vm-s-stat-card"><span class="vm-s-stat-val">%s</span><span class="vm-s-stat-label">%s</span></div>',
			esc_html( $c['value'] ),
			esc_html( $c['label'] )
		);
	}
	echo '</div></div>';
}

/* ─── Single: Rechtliche Einordnung ───────────────────── */

function dgt_vm_link_law_refs( $html ) {
	$laws = array(
		array(
			'pattern' => '/(?:§\s*316\s*StGB|StGB\s*§\s*316)/',
			'url'     => 'https://www.gesetze-im-internet.de/stgb/__316.html',
			'label'   => '§ 316 StGB',
			'title'   => '§ 316 StGB – Trunkenheit im Verkehr',
		),
		array(
			'pattern' => '/(?:§\s*315c\s*StGB|StGB\s*§\s*315c)/',
			'url'     => 'https://www.gesetze-im-internet.de/stgb/__315c.html',
			'label'   => '§ 315c StGB',
			'title'   => '§ 315c StGB – Gefährdung des Straßenverkehrs',
		),
		array(
			'pattern' => '/(?:§\s*142\s*StGB|StGB\s*§\s*142)/',
			'url'     => 'https://www.gesetze-im-internet.de/stgb/__142.html',
			'label'   => '§ 142 StGB',
			'title'   => '§ 142 StGB – Unerlaubtes Entfernen vom Unfallort',
		),
		array(
			'pattern' => '/(?:§\s*113\s*StGB|StGB\s*§\s*113)/',
			'url'     => 'https://www.gesetze-im-internet.de/stgb/__113.html',
			'label'   => '§ 113 StGB',
			'title'   => '§ 113 StGB – Widerstand gegen Vollstreckungsbeamte',
		),
		array(
			'pattern' => '/(?:§\s*24a\s*StVG|StVG\s*§\s*24a)/',
			'url'     => 'https://www.gesetze-im-internet.de/stvg/__24a.html',
			'label'   => '§ 24a StVG',
			'title'   => '§ 24a StVG – 0,5-Promille-Grenze und Drogennachweis',
		),
		array(
			'pattern' => '/(?:§\s*21\s*StVG|StVG\s*§\s*21)/',
			'url'     => 'https://www.gesetze-im-internet.de/stvg/__21.html',
			'label'   => '§ 21 StVG',
			'title'   => '§ 21 StVG – Fahren ohne Fahrerlaubnis',
		),
		array(
			'pattern' => '/(?:§\s*29\s*BtMG|BtMG\s*§\s*29)/',
			'url'     => 'https://www.gesetze-im-internet.de/btmg_1981/__29.html',
			'label'   => '§ 29 BtMG',
			'title'   => '§ 29 BtMG – Straftaten nach dem Betäubungsmittelgesetz',
		),
	);
	foreach ( $laws as $law ) {
		$anchor = '<a href="' . esc_url( $law['url'] ) . '" target="_blank" rel="noopener noreferrer" title="' . esc_attr( $law['title'] ) . '" class="vm-law-ref">' . esc_html( $law['label'] ) . '</a>';
		$pat    = rtrim( $law['pattern'], '/' ) . '(?![^<]*<\/a>)/';
		$html   = preg_replace( $pat, $anchor, $html );
	}
	return $html;
}

function dgt_vm_legal_notice( $item ) {
	$notices = array();

	if ( $item->ai_rechtsinfo ) {
		$text = $item->ai_rechtsinfo;
		$text = preg_replace( '/\*\*/', '', $text );
		$notices[] = $text;
	} else {
		if ( $item->hat_alkohol ) {
			if ( $item->bak_wert && $item->bak_wert >= 1.1 ) {
				$notices[] = 'Ab 1,1 Promille wird in Deutschland von absoluter Fahruntüchtigkeit ausgegangen. Es handelt sich um eine Straftat nach § 316 StGB (Trunkenheit im Verkehr), die mit Freiheitsstrafe bis zu einem Jahr oder Geldstrafe geahndet wird.';
			} elseif ( $item->bak_wert && $item->bak_wert >= 0.5 ) {
				$notices[] = 'Ab 0,5 Promille liegt eine Ordnungswidrigkeit nach § 24a StVG vor. Es drohen mindestens 500 Euro Bußgeld, 2 Punkte in Flensburg und 1 Monat Fahrverbot.';
			} else {
				$notices[] = 'Fahrten unter Alkoholeinfluss sind ordnungswidrig und bei erheblicher Beeinträchtigung strafbar. Der Atemalkoholtest dient als Nachweis für ein Verfahren.';
			}
		}

		if ( $item->hat_drogen ) {
			$subs = json_decode( $item->substanzen, true );
			if ( $subs && in_array( 'cannabis', $subs, true ) ) {
				$notices[] = 'Seit April 2024 gilt in Deutschland ein THC-Grenzwert von 3,5 ng/ml im Blutserum. Eine Überschreitung stellt eine Ordnungswidrigkeit nach § 24a StVG dar.';
			} elseif ( $subs && in_array( 'kokain', $subs, true ) ) {
				$notices[] = 'Der Nachweis von Kokain im Straßenverkehr erfüllt den Tatbestand einer Ordnungswidrigkeit nach § 24a StVG. Zusätzlich kann der Besitz nach § 29 BtMG strafrechtlich verfolgt werden.';
			} else {
				$notices[] = 'Das Führen eines Fahrzeugs unter dem Einfluss berauschender Mittel stellt eine Ordnungswidrigkeit nach § 24a StVG dar und kann mit Bußgeld, Punkten und Fahrverbot geahndet werden.';
			}
		}
	}

	if ( empty( $notices ) ) {
		return;
	}

	echo '<div class="vm-s-legal">';
	echo '<div class="vm-s-legal-icon">';
	echo '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>';
	echo '</div>';
	echo '<div class="vm-s-legal-body">';
	echo '<strong class="vm-s-legal-title">Rechtliche Einordnung</strong>';
	foreach ( $notices as $n ) {
		echo '<p>' . dgt_vm_link_law_refs( esc_html( $n ) ) . '</p>';
	}
	echo '</div>';
	echo '</div>';
}

/* ─── Produktempfehlung (CTA-Box) ──────────────────────── */

function dgt_vm_product_cta( $context = 'allgemein' ) {
	$links = array(
		'alkohol'   => array(
			'text'  => 'Mit einem Alkoholtester vor der Fahrt prüfen',
			'label' => 'Alkoholtester ansehen',
			'url'   => home_url( '/produkt/' ),
			'title' => 'Alkoholtester kaufen – Atemalkohol vor der Fahrt prüfen',
		),
		'cannabis'  => array(
			'text'  => 'Mit einem THC-Schnelltest vor der Fahrt prüfen, ob noch Rückstände nachweisbar sind',
			'label' => 'THC-Drogentest ansehen',
			'url'   => home_url( '/produkt/' ),
			'title' => 'THC-Drogentest kaufen – Cannabis-Schnelltest für zuhause',
		),
		'allgemein' => array(
			'text'  => 'Mit einem Schnelltest vor Fahrtantritt prüfen, ob noch Rückstände nachweisbar sind',
			'label' => 'Drogentests ansehen',
			'url'   => home_url( '/produkt/' ),
			'title' => 'Drogentests kaufen – Schnelltests für verschiedene Substanzen',
		),
	);

	$cta = isset( $links[ $context ] ) ? $links[ $context ] : $links['allgemein'];
	?>
	<div class="vm-cta">
		<p class="vm-cta-text"><?php echo esc_html( $cta['text'] ); ?></p>
		<a class="btn dgt-btn dgt-btn-primary" href="<?php echo esc_url( $cta['url'] ); ?>" title="<?php echo esc_attr( $cta['title'] ); ?>">
			<?php echo esc_html( $cta['label'] ); ?>
			<svg class="dgt-arrow" width="18" height="14" viewBox="0 0 18 14" aria-hidden="true"><path d="M1 7h15m0 0-5.5-5.5M16 7l-5.5 5.5" stroke="currentColor" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
		</a>
	</div>
	<?php
}

function dgt_vm_fb_teaser_compact() {
	?>
	<div class="vm-s-sb-card vm-fb-compact">
		<div class="vm-fb-compact-head">
			<svg width="22" height="22" viewBox="0 0 24 24" fill="#4599f7" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
			<h3 class="vm-s-sb-title">Diskutiere mit!</h3>
		</div>
		<p class="vm-fb-compact-text">Kuriose Fälle, Tages-Highlights und offene Diskussionen &ndash; werde Teil der Community.</p>
		<a class="vm-fb-compact-btn" href="https://www.facebook.com/profile.php?id=61590466511052" target="_blank" rel="noopener">
			<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
			Auf Facebook folgen
		</a>
	</div>
	<?php
}

function dgt_vm_fb_teaser() {
	?>
	<div class="vm-fb-teaser">
		<div class="vm-fb-teaser-bubble">
			<div class="vm-fb-teaser-body">
				<h3 class="vm-fb-teaser-title">
					<svg width="20" height="20" viewBox="0 0 24 24" fill="#4599f7" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
					Verkehrsmonitor auf Facebook
				</h3>
				<p class="vm-fb-teaser-text">Tägliche Zusammenfassungen, die kuriosesten Polizeieinsätze und spannende Diskussionen &ndash; auf unserer Facebook-Seite bekommst du die Highlights des Tages und kannst dich mit der Community zu den einzelnen Fällen austauschen.</p>
			</div>
			<a class="vm-fb-teaser-btn" href="https://www.facebook.com/profile.php?id=61590466511052" target="_blank" rel="noopener">
				<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
				Jetzt folgen
			</a>
		</div>
	</div>
	<?php
}


/**
 * Inline-Verlinkung im Pressetext: erstes Vorkommen einer festgestellten Substanz
 * (Kokain, Amphetamin, Cannabis/THC, MDMA/Ecstasy, Heroin/Opiate, Methamphetamin)
 * wird auf die passende Drogentest-Kategorie (/substanz/<slug>/) verlinkt.
 * Zusätzlich: erstes "nachweisbar/Nachweis"-Wort -> Nachweiszeiten-Ratgeber.
 *
 * SEO-konform: kontextrelevant, sprechende Anchor-Texte mit title-Attribut,
 * je Substanz max. 1 Link. Berührt keine bestehenden <a>-Tags und keine
 * Überschriften.
 *
 * @param string $html  Bereits durch wpautop/wp_kses_post verarbeiteter Summary-HTML.
 * @param array  $subs  Erkannte Substanz-Keys der Meldung.
 * @return string
 */
function dgt_vm_link_substances( $html, $subs ) {
	$html = (string) $html;
	if ( '' === $html || empty( $subs ) || ! is_array( $subs ) ) {
		return $html;
	}

	// Substanz-Key -> dgt_substanz-Term-Slug.
	$term_map = array(
		'cannabis'       => 'thc-cannabis',
		'thc'            => 'thc-cannabis',
		'kokain'         => 'kokain',
		'amphetamin'     => 'amphetamin',
		'methamphetamin' => 'methamphetamin',
		'heroin'         => 'opiate-morphin',
		'opiate'         => 'opiate-morphin',
		'mdma'           => 'mdma-ecstasy',
		'ecstasy'        => 'mdma-ecstasy',
		'medikamente'    => 'benzodiazepine',
	);

	// Substanz-Key -> Regex-Patterns für Synonyme im Pressetext.
	$word_patterns = array(
		'cannabis'       => array( 'Cannabis', 'Marihuana', 'Marijuana', 'Haschisch', 'THC' ),
		'thc'            => array( 'THC', 'Cannabis', 'Marihuana', 'Marijuana', 'Haschisch' ),
		'kokain'         => array( 'Kokain', 'Koks' ),
		'amphetamin'     => array( 'Amphetamin', 'Amphetamine', 'Speed' ),
		'methamphetamin' => array( 'Methamphetamin', 'Crystal Meth', 'Crystal' ),
		'heroin'         => array( 'Heroin' ),
		'opiate'         => array( 'Opiate', 'Opiat', 'Morphin' ),
		'mdma'           => array( 'MDMA' ),
		'ecstasy'        => array( 'Ecstasy' ),
		'medikamente'    => array( 'Benzodiazepine', 'Benzos' ),
	);

	// Targets sammeln (dedupliziert pro Term-Slug).
	$targets = array();
	foreach ( (array) $subs as $s ) {
		$s = sanitize_key( $s );
		if ( empty( $term_map[ $s ] ) || isset( $targets[ $term_map[ $s ] ] ) || empty( $word_patterns[ $s ] ) ) {
			continue;
		}
		$url = get_term_link( $term_map[ $s ], 'dgt_substanz' );
		if ( is_wp_error( $url ) ) {
			continue;
		}
		$targets[ $term_map[ $s ] ] = array(
			'url'      => (string) $url,
			'label'    => dgt_vm_substanz_label( $s ),
			'patterns' => $word_patterns[ $s ],
		);
	}

	if ( empty( $targets ) ) {
		return $html;
	}

	// Sicherheitsgurt: nichts in <a>, in Überschriften oder in HTML-Attributen ersetzen.
	$html = dgt_vm_link_substances_replace( $html, $targets );

	// Länder mit eigener Drogenpolitik-Unterseite (CPT „drogenpolitik")
	// im Pressetext einmalig verlinken. Erkennung über die Post-Titel.
	$country_targets = dgt_vm_country_link_targets();
	if ( ! empty( $country_targets ) ) {
		$html = dgt_vm_link_substances_replace( $html, $country_targets );
	}

	// Nachweiszeiten-Ratgeber – erstes "nachweisbar/Nachweis"-Wort verlinken.
	$nz = get_page_by_path( 'nachweiszeiten-drogen-thc-kokain-amphetamin', OBJECT, 'post' );
	if ( $nz instanceof WP_Post && 'publish' === $nz->post_status ) {
		$nz_url   = get_permalink( $nz );
		$nz_title = 'Nachweiszeiten von Drogen: THC, Kokain, Amphetamin und mehr';
		$html     = dgt_vm_link_substances_replace(
			$html,
			array(
				'__nachweis' => array(
					'url'      => $nz_url,
					'label'    => 'Nachweiszeiten',
					'title'    => $nz_title,
					'patterns' => array( 'Nachweiszeiten', 'nachweisbar', 'Nachweis' ),
				),
			)
		);
	}

	return $html;
}

/**
 * Sammelt die Länder, zu denen es eine Drogenpolitik-Unterseite (CPT
 * `drogenpolitik`) gibt – jeweils mit Land-Namen + Synonymen als Such-Pattern.
 *
 * Cache pro Request (statisch), damit die DB-Abfrage nicht pro Aufruf neu läuft.
 *
 * @return array
 */
function dgt_vm_country_link_targets() {
	static $cache = null;
	if ( null !== $cache ) {
		return $cache;
	}

	$posts = get_posts(
		array(
			'post_type'      => 'drogenpolitik',
			'post_status'    => 'publish',
			'posts_per_page' => -1,
			'orderby'        => 'title',
			'order'          => 'ASC',
			'no_found_rows'  => true,
		)
	);

	// Zusätzliche/abweichende Schreibweisen, die im Pressetext vorkommen können.
	$aliases = array(
		'oesterreich'     => array( 'Österreich' ),
		'tschechien'      => array( 'Tschechien', 'Tschechische Republik' ),
		'niederlande'     => array( 'Niederlande', 'Niederlanden', 'Holland' ),
		'daenemark'       => array( 'Dänemark' ),
		'tuerkei'         => array( 'Türkei' ),
		'suedafrika'      => array( 'Südafrika' ),
		'suedkorea'       => array( 'Südkorea', 'Süd-Korea' ),
		'grossbritannien' => array( 'Großbritannien', 'Vereinigtes Königreich', 'UK', 'Großbritannien und Nordirland' ),
		'aegypten'        => array( 'Ägypten' ),
		'saudi-arabien'   => array( 'Saudi-Arabien', 'Saudiarabien' ),
		'costa-rica'      => array( 'Costa Rica' ),
		'usa'             => array( 'USA', 'Vereinigte Staaten', 'Vereinigte Staaten von Amerika' ),
	);

	$targets = array();
	foreach ( (array) $posts as $p ) {
		$slug  = $p->post_name;
		$title = trim( wp_strip_all_tags( $p->post_title ) );
		// "Indonesien bei Alkoholkonsum" -> Land "Indonesien" (vor erstem Doppelpunkt/„bei").
		$land = preg_split( '/\s*(?::| bei | und )\s*/u', $title, 2 );
		$land = trim( $land[0] );
		if ( '' === $land ) {
			continue;
		}

		// Wenn schon ein Target mit gleicher Namens-Basis existiert (z. B. zwei
		// Indonesien-Beiträge), Sub-Seiten nicht überschreiben.
		$key = sanitize_title( $land );
		if ( isset( $targets[ $key ] ) ) {
			continue;
		}

		$patterns = array( $land );
		// Automatische Beugungen (Adjektive, Dativ) für den Volltext-Match.
		$patterns = array_merge( $patterns, dgt_vm_country_word_forms( $land ) );
		if ( isset( $aliases[ $slug ] ) ) {
			$patterns = array_merge( $aliases[ $slug ], $patterns );
		}
		$patterns = array_values( array_unique( array_filter( $patterns ) ) );

		$targets[ $key ] = array(
			'url'      => get_permalink( $p ),
			'label'    => $land,
			'title'    => 'Drogenpolitik in ' . dgt_vm_country_dativ( $land ) . ': Strafen, Gesetze & Konsumregeln im Überblick',
			'patterns' => $patterns,
		);
	}

	$cache = $targets;
	return $cache;
}

/**
 * Liefert die grammatikalisch passende Dativ-Form eines Ländernamens
 * für Wendungen wie „Drogenpolitik in {…}".
 *   Niederlande -> den Niederlanden
 *   Ukraine     -> der Ukraine
 *   Tschechien  -> Tschechien
 *
 * @param string $land
 * @return string
 */
function dgt_vm_country_dativ( $land ) {
	$map = array(
		'Niederlande'           => 'den Niederlanden',
		'USA'                   => 'den USA',
		'Vereinigte Staaten'    => 'den Vereinigten Staaten',
		'Philippinen'           => 'den Philippinen',
		'Vereinigtes Königreich' => 'dem Vereinigten Königreich',
		'Ukraine'               => 'der Ukraine',
		'Türkei'                => 'der Türkei',
		'Schweiz'               => 'der Schweiz',
		'Slowakei'              => 'der Slowakei',
		'Mongolei'              => 'der Mongolei',
		'Dominikanische Republik' => 'der Dominikanischen Republik',
		'Tschechische Republik' => 'der Tschechischen Republik',
		'Iran'                  => 'dem Iran',
		'Irak'                  => 'dem Irak',
		'Libanon'               => 'dem Libanon',
		'Jemen'                 => 'dem Jemen',
		'Sudan'                 => 'dem Sudan',
		'Tschad'                => 'dem Tschad',
		'Kongo'                 => 'dem Kongo',
		'Senegal'               => 'dem Senegal',
		'Niger'                 => 'dem Niger',
	);
	return isset( $map[ $land ] ) ? $map[ $land ] : $land;
}

/**
 * Erzeugt deutsche Wortformen zu einem Ländernamen für die Inline-Verlinkung:
 *   Polen      -> polnisch, polnische, polnischer, polnischen, polnisches, Polens, Polen
 *   Ukraine    -> ukrainisch, ukrainische, ukrainischer, ukrainischen, ukrainisches, Ukraine
 *   Tschechien -> tschechisch, tschechische, ...
 *   Frankreich -> französisch, französische, ...
 * Bekannte Sonderfälle (französisch, spanisch, dänisch usw.) sind hinterlegt;
 * sonst wird "{stamm}isch" gebildet.
 *
 * @param string $land
 * @return array
 */
function dgt_vm_country_word_forms( $land ) {
	if ( '' === $land ) {
		return array();
	}

	// Adjektivstamm-Sonderfälle (Kleinbuchstabe ohne -isch-Endung).
	$stems = array(
		'Polen'                 => 'poln',
		'Ukraine'               => 'ukrain',
		'Tschechien'            => 'tschech',
		'Tschechische Republik' => 'tschech',
		'Niederlande'           => 'niederländ',
		'Holland'               => 'holländ',
		'Dänemark'              => 'dän',
		'Frankreich'            => 'französ',
		'Belgien'               => 'belg',
		'Griechenland'          => 'griech',
		'Italien'               => 'italien',
		'Spanien'               => 'span',
		'Portugal'              => 'portugies',
		'Schweden'              => 'schwed',
		'Norwegen'              => 'norweg',
		'Finnland'              => 'finn',
		'Schweiz'               => 'schweizer',
		'Österreich'            => 'österreich',
		'Türkei'                => 'türk',
		'Russland'              => 'russ',
		'Rumänien'              => 'rumän',
		'Bulgarien'             => 'bulgar',
		'Ungarn'                => 'ungar',
		'Kroatien'              => 'kroat',
		'Serbien'               => 'serb',
		'Slowakei'              => 'slowak',
		'Slowenien'             => 'slowen',
		'Großbritannien'        => 'britisch',
		'Vereinigtes Königreich' => 'britisch',
		'Irland'                => 'ir',
		'USA'                   => 'amerikan',
		'Vereinigte Staaten'    => 'amerikan',
		'Kanada'                => 'kanad',
		'Mexiko'                => 'mexikan',
		'Brasilien'             => 'brasilian',
		'Argentinien'           => 'argentin',
		'Chile'                 => 'chilen',
		'Kolumbien'             => 'kolumbian',
		'Peru'                  => 'peruan',
		'Venezuela'             => 'venezolan',
		'Uruguay'               => 'uruguay',
		'Paraguay'              => 'paraguay',
		'Bolivien'              => 'bolivian',
		'Ecuador'               => 'ecuadorian',
		'Kuba'                  => 'kuban',
		'Costa Rica'            => 'costa-rican',
		'Jamaika'               => 'jamaikan',
		'Australien'            => 'australisch',
		'Neuseeland'            => 'neuseeländ',
		'Südafrika'             => 'südafrikan',
		'Nigeria'               => 'nigerian',
		'Kenia'                 => 'kenian',
		'Ghana'                 => 'ghana',
		'Marokko'               => 'marokkan',
		'Ägypten'               => 'ägypt',
		'China'                 => 'chines',
		'Japan'                 => 'japan',
		'Südkorea'              => 'südkorean',
		'Indien'                => 'ind',
		'Pakistan'              => 'pakistan',
		'Bangladesch'           => 'bangladesch',
		'Indonesien'            => 'indones',
		'Malaysia'              => 'malays',
		'Philippinen'           => 'philippin',
		'Thailand'              => 'thailänd',
		'Vietnam'               => 'vietnames',
		'Singapur'              => 'singapur',
		'Kambodscha'            => 'kambodschan',
		'Laos'                  => 'lao',
		'Myanmar'               => 'myanmar',
		'Nepal'                 => 'nepal',
		'Iran'                  => 'iran',
		'Irak'                  => 'irak',
		'Israel'                => 'israel',
		'Libanon'               => 'libanes',
		'Saudi-Arabien'         => 'saudi-arab',
		'Katar'                 => 'katar',
		'Georgien'              => 'georg',
		'Luxemburg'             => 'luxemburg',
	);

	// Spezifische Dativ-Formen, die nicht der Adjektiv-Regel folgen.
	$dative = array(
		'Niederlande'           => array( 'Niederlanden' ),
		'USA'                   => array( 'den USA' ),
		'Vereinigte Staaten'    => array( 'den Vereinigten Staaten' ),
		'Philippinen'           => array( 'Philippinen' ),
		'Vereinigtes Königreich' => array( 'Vereinigten Königreich' ),
	);

	$forms = array();
	$stem  = isset( $stems[ $land ] ) ? $stems[ $land ] : null;
	if ( null === $stem ) {
		// Generischer Stamm: Land klein, ohne Endungen wie -ien/-en/-land/-istan.
		$base = mb_strtolower( $land, 'UTF-8' );
		$base = preg_replace( '/(ien|en|land|istan|ei)$/u', '', $base );
		if ( '' === $base ) {
			$base = mb_strtolower( $land, 'UTF-8' );
		}
		$stem = $base;
	}

	if ( '' !== $stem ) {
		// Stamm endet schon auf "isch" (z. B. britisch)? Dann nicht doppeln.
		$adj = ( preg_match( '/isch$/u', $stem ) ) ? $stem : $stem . 'isch';
		foreach ( array( '', 'e', 'er', 'en', 'es' ) as $suf ) {
			$forms[] = $adj . $suf;
		}
	}

	if ( isset( $dative[ $land ] ) ) {
		$forms = array_merge( $forms, $dative[ $land ] );
	}

	return array_values( array_unique( array_filter( $forms ) ) );
}

/**
 * Interner Replacer: ersetzt im HTML außerhalb von <a>, Überschriften und
 * Tag-Attributen das erste Vorkommen je Target durch einen <a>-Link.
 *
 * @param string $html
 * @param array  $targets  [ key => [ url, label, patterns[], title? ] ]
 * @return string
 */
function dgt_vm_link_substances_replace( $html, $targets ) {
	if ( empty( $targets ) ) {
		return $html;
	}
	$parts = preg_split( '/(<[^>]+>)/', $html, -1, PREG_SPLIT_DELIM_CAPTURE );
	if ( ! is_array( $parts ) ) {
		return $html;
	}

	$in_a       = 0;
	$in_heading = 0;
	$done       = array();

	foreach ( $parts as $idx => $part ) {
		if ( '' === $part ) {
			continue;
		}
		if ( '<' === $part[0] ) {
			if ( preg_match( '/^<\s*a[\s>]/i', $part ) ) {
				$in_a++;
			} elseif ( preg_match( '/^<\s*\/\s*a\s*>/i', $part ) ) {
				$in_a = max( 0, $in_a - 1 );
			} elseif ( preg_match( '/^<\s*h[1-6][\s>]/i', $part ) ) {
				$in_heading++;
			} elseif ( preg_match( '/^<\s*\/\s*h[1-6]\s*>/i', $part ) ) {
				$in_heading = max( 0, $in_heading - 1 );
			}
			continue;
		}
		if ( $in_a > 0 || $in_heading > 0 ) {
			continue;
		}
		foreach ( $targets as $key => $t ) {
			if ( ! empty( $done[ $key ] ) ) {
				continue;
			}
			foreach ( $t['patterns'] as $word ) {
				$pattern = '/(?<![\p{L}\p{N}])(' . preg_quote( $word, '/' ) . ')(?![\p{L}\p{N}])/u';
				if ( preg_match( $pattern, $part, $m, PREG_OFFSET_CAPTURE ) ) {
					$off     = $m[1][1];
					$match   = $m[1][0];
					$title   = isset( $t['title'] ) ? $t['title'] : ( $t['label'] . '-Drogentests &amp; Nachweisinfos im &Uuml;berblick' );
					$anchor  = '<a href="' . esc_url( $t['url'] ) . '" title="' . esc_attr( html_entity_decode( $title ) ) . '">' . $match . '</a>';
					$part    = substr( $part, 0, $off ) . $anchor . substr( $part, $off + strlen( $match ) );
					$parts[ $idx ] = $part;
					$done[ $key ]  = true;
					continue 2; // dieses Target erledigt, im selben Knoten nach weiteren Targets suchen
				}
			}
		}
	}

	return implode( '', $parts );
}

Youez - 2016 - github.com/yon3zu
LinuXploit