ID, self::USER_META_JOB_TITLE, true ); $expertise = get_user_meta( $user->ID, self::USER_META_EXPERTISE, true ); $website = get_user_meta( $user->ID, self::USER_META_WEBSITE, true ); $same_as = get_user_meta( $user->ID, self::USER_META_SAME_AS, true ); ?>

'Person', 'name' => $user->display_name, 'url' => $author_url ); if ( ! empty( $job_title ) ) { $person['jobTitle'] = $job_title; } if ( ! empty( $expertise ) ) { $person['description'] = $expertise; } if ( ! empty( $same_as_urls ) ) { $person['sameAs'] = $same_as_urls; } return $person; } private function get_author_expert_data( $author_id ) { $person = $this->build_person_schema( $author_id ); return empty( $person ) ? array() : $person; } /* ===== Frontend-Ausgabe ===== */ public function output_person_schema_in_head() { if ( ! is_singular() ) { return; } global $post; if ( empty( $post ) || ! is_object( $post ) ) { return; } $o = $this->get_opts(); if ( ! in_array( $post->post_type, $o['enabled_post_types'], true ) ) { return; } if ( get_post_meta( $post->ID, self::META_NO_EMBED, true ) ) { return; } $person = $this->build_person_schema( $post->post_author ); if ( empty( $person ) ) { return; } $graph = array( '@context' => 'https://schema.org', '@graph' => array( $person, array( '@type' => 'Article', 'url' => get_permalink( $post ), 'headline' => wp_strip_all_tags( get_the_title( $post ) ), 'datePublished' => get_post_time( 'c', true, $post ), 'dateModified' => get_post_modified_time( 'c', true, $post ), 'author' => array( '@type' => 'Person', 'name' => $person['name'], 'url' => $person['url'] ), 'reviewedBy' => array( '@type' => 'Person', 'name' => $person['name'], 'url' => $person['url'], 'jobTitle' => $person['jobTitle'] ?? '' ), 'publisher' => array( '@type' => 'Organization', 'name' => get_bloginfo( 'name' ), 'url' => home_url() ), ), ), ); $flags = 0; if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) { $flags |= JSON_UNESCAPED_UNICODE; } if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { $flags |= JSON_UNESCAPED_SLASHES; } if ( defined( 'JSON_PRETTY_PRINT' ) ) { $flags |= JSON_PRETTY_PRINT; } $json = function_exists( 'wp_json_encode' ) ? wp_json_encode( $graph, $flags ) : json_encode( $graph, $flags ); echo "\n\n"; echo '' . "\n"; } public function output_aije_meta_description() { if ( ! is_singular() ) { return; } global $post; if ( empty( $post ) ) { return; } $seo = $this->get_active_seo_plugin(); if ( $seo !== 'own' ) { return; } $seo_title = get_post_meta( $post->ID, '_aije_seo_title', true ); if ( ! empty( $seo_title ) ) { add_filter( 'pre_get_document_title', function() use ( $seo_title ) { return esc_html( $seo_title ); } ); } $meta = get_post_meta( $post->ID, '_aije_metadesc', true ); if ( ! empty( $meta ) ) { echo '' . "\n"; } $robots = get_post_meta( $post->ID, '_aije_robots', true ); if ( ! empty( $robots ) && $robots !== 'index,follow' ) { echo '' . "\n"; } $canonical = get_post_meta( $post->ID, '_aije_canonical', true ); if ( ! empty( $canonical ) ) { echo '' . "\n"; } } public function output_tracking_head() { $opts = $this->get_opts(); $gtm_id = trim( $opts['gtm_id'] ?? '' ); $ga4_id = trim( $opts['ga4_id'] ?? '' ); $gsc_verify = trim( $opts['gsc_verify'] ?? '' ); if ( $gsc_verify ) { $code = str_replace( 'google-site-verification=', '', $gsc_verify ); echo '' . "\n"; } if ( $gtm_id ) { echo "\n\n\n"; } if ( $ga4_id && ! $gtm_id ) { echo '' . "\n"; echo '' . "\n"; } $meta_pixel = trim( $opts['meta_pixel'] ?? '' ); if ( $meta_pixel ) { echo "\n"; echo '' . "\n"; echo '' . "\n"; echo "\n"; } } /* ===== Auto-Embed (JSON-Blob) ===== */ public function auto_embed() { if ( ! function_exists( 'is_singular' ) || ! is_singular() ) { return; } global $post; if ( empty( $post ) || ! is_object( $post ) ) { return; } $o = $this->get_opts(); if ( ! in_array( $post->post_type, $o['enabled_post_types'], true ) ) { return; } if ( get_post_meta( $post->ID, self::META_NO_EMBED, true ) ) { return; } $data = $this->extract_payload( $post ); $flags = 0; if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) { $flags |= JSON_UNESCAPED_UNICODE; } if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { $flags |= JSON_UNESCAPED_SLASHES; } if ( empty( $o['minify'] ) && defined( 'JSON_PRETTY_PRINT' ) ) { $flags |= JSON_PRETTY_PRINT; } $json = function_exists( 'wp_json_encode' ) ? wp_json_encode( $data, $flags ) : json_encode( $data ); echo "\n\n"; echo '' . "\n"; if ( ! empty( $o['enable_schema'] ) ) { echo '' . "\n"; } echo "\n"; } private function extract_payload( $post ) { $o = $this->get_opts(); $u = get_userdata( $post->post_author ); $acf = array(); $content = apply_filters( 'the_content', $post->post_content ); $text = wp_strip_all_tags( $content ); $lines = preg_split( "/\r\n|\n|\r/", $text ); $paragraphs = array(); foreach ( $lines as $line ) { $line = trim( preg_replace( '/\s+/u', ' ', $line ) ); if ( $line !== '' ) { $paragraphs[] = $line; } } $links = array(); if ( preg_match_all( '/]+href=[\'\"]?([^\'\" >]+)[\'\"]?[^>]*>(.*?)<\/a>/i', $content, $m, PREG_SET_ORDER ) ) { foreach ( $m as $a ) { $href = trim( $a[1] ); $txt = trim( wp_strip_all_tags( $a[2] ) ); if ( $href !== '' ) { $links[] = array( 'text' => $txt, 'href' => $href ); } } } if ( ! empty( $o['acf_include'] ) && function_exists( 'get_fields' ) ) { $acf_fields = get_fields( $post->ID ); if ( is_array( $acf_fields ) && ! empty( $acf_fields ) ) { $acf = $acf_fields; } } $data = array( 'title' => get_the_title( $post ), 'author' => $u ? $u->display_name : null, 'date' => get_post_time( 'Y-m-d', true, $post ), 'paragraphs' => $paragraphs, 'links' => $links, 'meta' => array( 'post_id' => (int) $post->ID, 'site' => get_bloginfo( 'name' ), 'url' => get_permalink( $post ), 'lang' => get_bloginfo( 'language' ) ), ); $expert = $this->get_author_expert_data( $post->post_author ); if ( ! empty( $expert ) ) { $data['author_expert'] = $expert; } if ( ! empty( $acf ) ) { $data['acf'] = $acf; } return $data; } private function schema_article( $post ) { $obj = array( '@context' => 'https://schema.org', '@type' => 'Article', 'url' => get_permalink( $post ), 'headline' => wp_strip_all_tags( get_the_title( $post ) ), 'publisher' => array( '@type' => 'Organization', 'name' => get_bloginfo( 'name' ) ), ); $u = get_userdata( $post->post_author ); if ( $u && $u->display_name ) { $obj['author'] = array( '@type' => 'Person', 'name' => $u->display_name ); } $flags = 0; if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) { $flags |= JSON_UNESCAPED_UNICODE; } if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { $flags |= JSON_UNESCAPED_SLASHES; } return function_exists( 'wp_json_encode' ) ? wp_json_encode( $obj, $flags ) : json_encode( $obj ); } /* ===== llms.txt ===== */ public function register_llms_rewrite() { add_rewrite_rule( '^llms\.txt/?$', 'index.php?aije_llms=1', 'top' ); add_rewrite_tag( '%aije_llms%', '([0-9]+)' ); } public function serve_llms_txt() { if ( ! get_query_var( 'aije_llms' ) ) { return; } if ( ! get_option( 'aije_llms_enabled', 1 ) ) { return; } nocache_headers(); header( 'Content-Type: text/plain; charset=utf-8' ); header( 'X-Robots-Tag: noindex' ); echo $this->generate_llms_content(); exit; } public function append_llms_to_robots( $output, $public ) { if ( get_option( 'aije_llms_block_bots', 0 ) ) { foreach ( array( 'GPTBot', 'CCBot', 'anthropic-ai', 'Claude-Web', 'Bytespider', 'FacebookBot', 'Applebot-Extended' ) as $bot ) { $output .= "\nUser-agent: " . $bot . "\nDisallow: /\n"; } } if ( get_option( 'aije_llms_enabled', 1 ) ) { $output .= "\n# LLMs\nLLMs: " . home_url( '/llms.txt' ) . "\n"; } return $output; } /** * Generiert die llms.txt. * * Strategie: Erst alles aufbauen, dann von hinten Artikel entfernen * bis die Datei unter 2.800 Zeichen liegt. Keine Schätzformeln. */ public function generate_llms_content() { $o = $this->get_opts(); $custom = get_option( 'aije_llms_custom', '' ); $max_posts = max( 1, min( 25, (int) get_option( 'aije_llms_max_posts', 10 ) ) ); $excerpt_len = max( 0, min( 200, (int) get_option( 'aije_llms_excerpt_len', 0 ) ) ); $key_pages = (array) get_option( 'aije_llms_key_pages', array() ); $show_cats = (int) get_option( 'aije_llms_show_cats', 1 ); $link_sitemap= (int) get_option( 'aije_llms_sitemap', 1 ); $limit = 2800; // ── Autor-Daten ──────────────────────────────────────────────── $admins = get_users( array( 'role' => 'administrator', 'number' => 1 ) ); $admin = $admins ? $admins[0] : null; $job = $admin ? get_user_meta( $admin->ID, self::USER_META_JOB_TITLE, true ) : ''; $bio = $admin ? get_user_meta( $admin->ID, self::USER_META_EXPERTISE, true ) : ''; $same_as = $admin ? get_user_meta( $admin->ID, self::USER_META_SAME_AS, true ) : ''; $disp = $admin ? $admin->display_name : get_bloginfo( 'name' ); // ── Block A: Header (immer vollständig) ──────────────────────── $header = ''; $header .= '# ' . $disp . ( get_bloginfo( 'name' ) !== $disp ? ' – ' . get_bloginfo( 'name' ) : '' ) . "\n\n"; $desc_parts = array(); if ( $job ) { $desc_parts[] = $job; } if ( $bio ) { $bio_clean = html_entity_decode( wp_strip_all_tags( $bio ), ENT_QUOTES, 'UTF-8' ); $bio_clean = preg_replace( '/\s+/', ' ', trim( $bio_clean ) ); if ( mb_strlen( $bio_clean ) > 160 ) { $bio_clean = mb_substr( $bio_clean, 0, 157 ); $p = mb_strrpos( $bio_clean, ' ' ); $bio_clean = ( $p ? mb_substr( $bio_clean, 0, $p ) : $bio_clean ) . '...'; } $desc_parts[] = $bio_clean; } elseif ( get_bloginfo( 'description' ) ) { $desc_parts[] = get_bloginfo( 'description' ); } if ( $desc_parts ) { $header .= '> ' . implode( ' – ', $desc_parts ) . "\n\n"; } // ── Block B: Wichtige Seiten (immer vollständig) ─────────────── $pages_block = "## Wichtige Seiten\n\n"; $pages_block .= '- [' . get_bloginfo( 'name' ) . '](' . trailingslashit( home_url() ) . ')'; $tagline = get_bloginfo( 'description' ); if ( $tagline ) { $pages_block .= ': ' . $this->llms_truncate( $tagline, 80 ); } $pages_block .= "\n"; foreach ( $key_pages as $pid ) { $pinned = get_post( (int) $pid ); if ( ! $pinned || $pinned->post_status !== 'publish' ) { continue; } $exc = $pinned->post_excerpt ? $this->llms_truncate( wp_strip_all_tags( $pinned->post_excerpt ), 80 ) : ''; if ( ! $exc && $pinned->post_content ) { $exc = $this->llms_excerpt( $pinned, 80 ); } $pages_block .= '- [' . get_the_title( $pinned ) . '](' . get_permalink( $pinned ) . ')' . ( $exc ? ': ' . $exc : '' ) . "\n"; } if ( $same_as ) { foreach ( preg_split( "/\r\n|\n|\r/", $same_as ) as $profile_url ) { $profile_url = trim( $profile_url ); if ( ! $profile_url || $profile_url[0] === '#' ) { continue; } $host = preg_replace( '/^www\./', '', parse_url( $profile_url, PHP_URL_HOST ) ?? '' ); $label = ucfirst( explode( '.', $host )[0] ?? 'Profil' ); $pages_block .= '- [' . $disp . ' auf ' . $label . '](' . esc_url( $profile_url ) . '): Berufliches Profil' . "\n"; } } $pages_block .= "\n"; // ── Block C: Footer (immer vollständig) ──────────────────────── $footer = "## Nutzungshinweise\n\n"; $footer .= "> Zitate mit Quellenangabe sind erlaubt. Kommerzielle Nutzung ohne Genehmigung ist nicht gestattet.\n"; $footer .= "> Generiert mit [WP Rankify](https://wprankify.de).\n"; if ( $link_sitemap ) { $footer .= "\n## Vollständige Inhalte\n\n"; $sitemap_url = method_exists( $this, 'sitemap_get_url' ) ? $this->sitemap_get_url() : home_url( '/sitemap_index.xml' ); $footer .= '- [XML-Sitemap](' . esc_url( $sitemap_url ) . '): Vollständiger Index aller URLs' . "\n"; } if ( $custom ) { $footer .= "\n" . trim( $custom ) . "\n"; } // ── Block D: Beiträge – KI-Highlight zuerst, dann Auffüllung ──── // Priorität 1: Von Nutzer als KI-Suche markiert (_aije_ki_highlight) // oder als Cornerstone (Yoast) / Pillar (Rank Math) // Davon nur die 5 zuletzt aktualisierten $ki_posts = get_posts( array( 'post_type' => $o['enabled_post_types'], 'post_status' => 'publish', 'posts_per_page' => 5, 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true, 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_aije_ki_highlight', 'value' => '1', 'compare' => '=' ), array( 'key' => '_yoast_wpseo_is_cornerstone', 'value' => '1', 'compare' => '=' ), array( 'key' => 'rank_math_pillar_content', 'value' => 'on', 'compare' => '=' ), ), ) ); // Priorität 2: Neueste Beiträge als Auffüllung (ohne bereits enthaltene) $ki_ids = wp_list_pluck( $ki_posts, 'ID' ); $fill_limit = max( 0, $max_posts - count( $ki_posts ) ); $fill_posts = $fill_limit > 0 ? get_posts( array( 'post_type' => $o['enabled_post_types'], 'post_status' => 'publish', 'posts_per_page' => $fill_limit, 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, 'post__not_in' => $ki_ids ?: array( 0 ), ) ) : array(); $posts = array_merge( $ki_posts, $fill_posts ); // Jede Beitragszeile vorberechnen $post_lines = array(); foreach ( $posts as $p ) { $line = '- [' . get_the_title( $p ) . '](' . get_permalink( $p ) . ')'; if ( $excerpt_len > 0 ) { $exc = $this->llms_excerpt( $p, $excerpt_len ); if ( $exc ) { $line .= ': ' . $exc; } } $post_lines[] = $line; } // Solange Zeilen entfernen bis alles passt while ( ! empty( $post_lines ) ) { $posts_block = "## Aktuelle Inhalte\n\n"; $posts_block .= implode( "\n", $post_lines ) . "\n\n"; $total = mb_strlen( $header . $pages_block . $posts_block . $footer ); if ( $total <= $limit ) { break; } array_pop( $post_lines ); // letzten Eintrag entfernen } // Falls immer noch zu lang (Header+Pages+Footer allein > Limit): ohne Beiträge $posts_block = ''; if ( ! empty( $post_lines ) ) { $posts_block = "## Aktuelle Inhalte\n\n"; $posts_block .= implode( "\n", $post_lines ) . "\n\n"; } return $header . $pages_block . $posts_block . $footer; } /** * Kurzen Excerpt für Link-Descriptions extrahieren. */ private function llms_excerpt( $post, $max = 100 ) { if ( ! empty( $post->post_excerpt ) ) { $text = html_entity_decode( wp_strip_all_tags( $post->post_excerpt ), ENT_QUOTES, 'UTF-8' ); return $this->llms_truncate( preg_replace( '/\s+/', ' ', trim( $text ) ), $max ); } $content = html_entity_decode( wp_strip_all_tags( $post->post_content ), ENT_QUOTES, 'UTF-8' ); $content = preg_replace( '/\s+/', ' ', trim( $content ) ); if ( preg_match( '/^(.{20,' . min( $max, 150 ) . '}[.!?])\s/', $content, $m ) ) { return trim( $m[1] ); } return $this->llms_truncate( $content, $max ); } /** * Text an Wortgrenze kürzen. */ private function llms_truncate( $text, $max ) { $text = trim( $text ); if ( mb_strlen( $text ) <= $max ) { return $text; } $t = mb_substr( $text, 0, $max ); $pos = mb_strrpos( $t, ' ' ); return ( $pos ? mb_substr( $t, 0, $pos ) : $t ) . '...'; } }