| 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 : |
<?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 & 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)">← 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 →</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 & 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 – 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 – 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 & Nachweisinfos im Ü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 );
}