/** * Plugin Name: SH Google Ads Longtail Export * Description: Erstellt Google-Ads-Keyword-CSV aus eigenen Product-Tag-, Produkt-View- und Awin-Klick-Statistiken. * Version: 2.1.0 */ if (!defined('ABSPATH')) { exit; } if (!is_admin()) { return; } /* --------------------------------------------------- * ADMIN MENÜ * --------------------------------------------------- */ add_action('admin_menu', function () { add_submenu_page( 'sh-eigene-statistiken', 'Google Ads Longtails', 'Google Ads Longtails', 'manage_options', 'sh-google-ads-longtails', 'sh_gads_page' ); }, 999); /* --------------------------------------------------- * TABLE EXISTS * --------------------------------------------------- */ function sh_gads_table_exists($table): bool { global $wpdb; return (bool)$wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $table ) ); } /* --------------------------------------------------- * UTF-8 CLEAN * --------------------------------------------------- */ function sh_gads_clean_keyword($text): string { $text = html_entity_decode( (string)$text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); /* * WICHTIG: * Keine Umlaut-Umwandlung mehr. * * Also: * ä bleibt ä * ö bleibt ö * ü bleibt ü * ß bleibt ß * * Google Ads kann UTF-8 problemlos. */ $text = trim($text); /* * lowercase mit UTF-8 */ $text = mb_strtolower( $text, 'UTF-8' ); /* * Sonderzeichen entfernen * Umlaute bleiben erhalten */ $text = preg_replace( '/[^\p{L}\p{N}\s\-]/u', ' ', $text ); /* * doppelte Leerzeichen entfernen */ $text = preg_replace( '/\s+/u', ' ', $text ); return trim($text); } /* --------------------------------------------------- * KEYWORD SCORE * --------------------------------------------------- */ function sh_gads_keyword_score($row): float { $views = (int)($row->views ?? 0); $clicks = (int)($row->clicks ?? 0); $products = (int)($row->products ?? 0); return ($clicks * 20) + ($views * 2) + $products; } /* --------------------------------------------------- * KEYWORDS HOLEN * --------------------------------------------------- */ function sh_gads_get_keywords($limit = 300): array { global $wpdb; $tag_index = $wpdb->prefix . 'sh_product_tag_visit_index'; if ( !sh_gads_table_exists( $tag_index ) ) { return []; } $rows = $wpdb->get_results(" SELECT t.term_id, t.name, t.slug, i.views, i.clicks, i.products FROM {$tag_index} i INNER JOIN {$wpdb->terms} t ON t.term_id = i.term_id WHERE i.views > 0 OR i.clicks > 0 ORDER BY i.clicks DESC, i.views DESC, i.products DESC LIMIT " . (int)$limit ); $keywords = []; foreach ($rows as $row) { $kw = sh_gads_clean_keyword( $row->name ); if ($kw === '') { continue; } if ( mb_strlen( $kw, 'UTF-8' ) < 3 ) { continue; } $term_link = get_term_link( (int)$row->term_id, 'product_tag' ); if ( is_wp_error( $term_link ) ) { continue; } $keywords[$kw] = [ 'keyword' => $kw, 'ad_group' => mb_substr( $row->name, 0, 80, 'UTF-8' ), 'url' => $term_link, 'views' => (int)$row->views, 'clicks' => (int)$row->clicks, 'products' => (int)$row->products, 'score' => sh_gads_keyword_score( $row ), ]; } uasort($keywords, function ($a, $b) { return $b['score'] <=> $a['score']; }); return array_values($keywords); } /* --------------------------------------------------- * CSV ESCAPE * --------------------------------------------------- */ function sh_gads_csv_escape($value): string { $value = (string)$value; $value = str_replace( '"', '""', $value ); return '"' . $value . '"'; } /* --------------------------------------------------- * CSV DOWNLOAD * --------------------------------------------------- */ function sh_gads_download_csv( $match_type = 'both', $limit = 300 ) { $keywords = sh_gads_get_keywords($limit); $filename = 'google-ads-longtails-' . date('Y-m-d') . '.csv'; /* * OUTPUT BUFFER LEEREN */ if (ob_get_length()) { ob_clean(); } /* * UTF-8 CSV */ header( 'Content-Type: text/csv; charset=UTF-8' ); header( 'Content-Disposition: attachment; filename="' . $filename . '"' ); /* * UTF-8 BOM * Wichtig für Excel + Umlaute */ echo "\xEF\xBB\xBF"; echo implode(',', [ 'Campaign', 'Ad group', 'Keyword', 'Match type', 'Final URL', 'Views', 'Affiliate Clicks', 'Products' ]) . "\n"; foreach ($keywords as $item) { $rows = []; /* * PHRASE */ if ( $match_type === 'phrase' || $match_type === 'both' ) { $rows[] = [ 'match' => 'Phrase', 'kw' => '"' . $item['keyword'] . '"', ]; } /* * EXACT */ if ( $match_type === 'exact' || $match_type === 'both' ) { $rows[] = [ 'match' => 'Exact', 'kw' => '[' . $item['keyword'] . ']', ]; } foreach ($rows as $r) { echo implode(',', [ sh_gads_csv_escape( 'Gartenhaus Longtail' ), sh_gads_csv_escape( $item['ad_group'] ), sh_gads_csv_escape( $r['kw'] ), sh_gads_csv_escape( $r['match'] ), sh_gads_csv_escape( $item['url'] ), (int)$item['views'], (int)$item['clicks'], (int)$item['products'], ]) . "\n"; } } exit; } /* --------------------------------------------------- * NEGATIVE KEYWORDS CSV * --------------------------------------------------- */ function sh_gads_download_negative_csv() { $negative = [ 'kostenlos', 'gratis', 'bauplan', 'bauanleitung', 'anleitung', 'pdf', 'zeichnung', 'selber bauen', 'gebraucht', 'ebay', 'amazon', 'obi', 'hornbach', 'bauhaus', 'toom', 'kleinanzeigen', 'forum', 'bilder', 'foto', 'ideen', 'minecraft', 'spielzeug', ]; if (ob_get_length()) { ob_clean(); } header( 'Content-Type: text/csv; charset=UTF-8' ); header( 'Content-Disposition: attachment; filename="google-ads-negative-keywords-' . date('Y-m-d') . '.csv"' ); /* * UTF-8 BOM */ echo "\xEF\xBB\xBF"; echo "Campaign,Negative keyword,Match type\n"; foreach ($negative as $kw) { echo implode(',', [ sh_gads_csv_escape( 'Gartenhaus Longtail' ), sh_gads_csv_escape($kw), sh_gads_csv_escape('Phrase'), ]) . "\n"; } exit; } /* --------------------------------------------------- * EXPORT ROUTEN * --------------------------------------------------- */ add_action('admin_init', function () { if ( !current_user_can( 'manage_options' ) ) { return; } if (isset($_GET['sh_gads_export'])) { $match = sanitize_text_field( $_GET['match'] ?? 'both' ); $limit = max( 10, min( 2000, (int)( $_GET['limit'] ?? 300 ) ) ); sh_gads_download_csv( $match, $limit ); } if ( isset( $_GET['sh_gads_negative_export'] ) ) { sh_gads_download_negative_csv(); } }); /* --------------------------------------------------- * ADMIN SEITE * --------------------------------------------------- */ function sh_gads_page() { $keywords = sh_gads_get_keywords(100); echo '
Erstellt automatisch Google-Ads-Keywords aus deinen echten Produkt- und Product-Tag-Daten.
'; echo ''; echo ' CSV Phrase + Exact '; echo ' Nur Phrase '; echo ' Nur Exact '; echo ' Negative Keywords '; echo '
'; echo '| # | '; echo 'Keyword | '; echo 'Phrase | '; echo 'Exact | '; echo 'Views | '; echo 'Klicks | '; echo 'Produkte | '; echo 'URL | '; echo '
|---|---|---|---|---|---|---|---|
| Keine Daten gefunden. | '; echo '|||||||
| ' . (int)$i . ' | '; echo '' . esc_html( $item['keyword'] ) . ' | '; echo '
"'
. esc_html(
$item['keyword']
)
. '"
| ';
echo '
['
. esc_html(
$item['keyword']
)
. ']
| ';
echo '' . (int)$item['views'] . ' | '; echo '' . (int)$item['clicks'] . ' | '; echo '' . (int)$item['products'] . ' | '; echo ''; echo ' öffnen '; echo ' | '; echo '