ID, '_geo_exclude', true ) ) { return $content; } // Check if there is a custom summary already. $custom_summary = get_post_meta( $post->ID, '_geo_custom_summary', true ); if ( ! empty( $custom_summary ) ) { $summary = $custom_summary; } else { $summary = $this->generate_summary( $post ); } if ( empty( $summary ) ) { return $content; } // Build the summary block. $summary_html = $this->build_summary_html( $summary, $post ); return $summary_html . $content; } /** * Generates the summary automatically. * * @since 1.0.0 * @param WP_Post $post Post object. * @return string Generated summary. */ private function generate_summary( $post ) { // First try: Yoast meta description. $yoast = get_post_meta( $post->ID, '_yoast_wpseo_metadesc', true ); if ( ! empty( $yoast ) && strlen( $yoast ) > 50 ) { return $yoast; } // Second try: RankMath meta description. $rankmath = get_post_meta( $post->ID, 'rank_math_description', true ); if ( ! empty( $rankmath ) && strlen( $rankmath ) > 50 ) { return $rankmath; } // Third try: excerpt. if ( ! empty( $post->post_excerpt ) && strlen( $post->post_excerpt ) > 50 ) { return $post->post_excerpt; } // Fallback: extract from content. return $this->extract_summary_from_content( $post->post_content ); } /** * Extracts a summary from the content. * * @since 1.0.0 * @param string $content Post content. * @return string Extracted summary. */ private function extract_summary_from_content( $content ) { // Remove shortcodes and process blocks. $text = strip_shortcodes( $content ); $text = do_blocks( $text ); // Strip HTML. $text = wp_strip_all_tags( $text ); // Clean whitespace. $text = preg_replace( '/\s+/', ' ', $text ); $text = trim( $text ); if ( strlen( $text ) < 50 ) { return ''; } // Take the first 2-3 sentences. $sentences = preg_split( '/(?<=[.!?])\s+/', $text, 4 ); $summary = ''; $max_length = 200; foreach ( $sentences as $sentence ) { if ( strlen( $summary ) + strlen( $sentence ) > $max_length ) { break; } $summary .= $sentence . ' '; } return trim( $summary ); } /** * Builds the summary HTML. * * @since 1.0.0 * @param string $summary Summary text. * @param WP_Post $post Post object. * @return string Summary HTML. */ private function build_summary_html( $summary, $post ) { $reading_time = $this->calculate_reading_time( $post->post_content ); $date = get_the_date( 'F j, Y', $post ); $html = '
'; // Main summary. $html .= '

' . esc_html__( 'In short:', 'aidentity-generative-engine-optimizer' ) . ' '; $html .= esc_html( $summary ); $html .= '

'; // Meta info. $html .= '

'; $html .= '📅 ' . esc_html( $date ) . ''; $html .= '  •  '; /* translators: %d: reading time in minutes */ $html .= '⏱️ ' . sprintf( esc_html__( '%d min read', 'aidentity-generative-engine-optimizer' ), $reading_time ) . ''; $html .= '

'; $html .= '
'; // Add an HTML comment for AI crawlers. $html .= "\n\n"; return $html; } /** * Calculates the reading time. * * @since 1.0.0 * @param string $content Post content. * @return int Reading time in minutes. */ private function calculate_reading_time( $content ) { $text = wp_strip_all_tags( strip_shortcodes( $content ) ); $word_count = str_word_count( $text ); $reading_time = ceil( $word_count / 200 ); // 200 words per minute. return max( 1, $reading_time ); } }