ID ) : array(); // Fallback to legacy scoring if Ultimate Score not available if ( ! empty( $ultimate_score ) ) { $unified_data = array( 'unified_score' => $ultimate_score['score'], 'color' => $ultimate_score['color'], 'label' => $ultimate_score['label'], 'status' => '', 'content_data' => $ultimate_score, 'content_score' => $ultimate_score['score'], 'content_weight' => 70, 'visibility_score' => 0, 'visibility_weight' => 30, ); } else { $unified_data = AI_Answer_Toolkit_Score::calculate_unified_score( $post->ID ); } // Extract individual components $manual_summary = get_post_meta( $post->ID, '_aitk_summary', true ); $auto_summary = AI_Answer_Toolkit_Summary::generate_auto_summary( $post->ID ); $final_summary = AI_Answer_Toolkit_Summary::get_final_summary( $post->ID ); $summary_type = AI_Answer_Toolkit_Summary::get_summary_type( $post->ID ); $faq_count = AI_Answer_Toolkit_Score::get_faq_count( $post->ID ); // Get stats $content = get_post_field( 'post_content', $post->ID ); $word_count = str_word_count( wp_strip_all_tags( $content ) ); // Get warnings from content data $warnings = self::get_warnings( $post->ID, $unified_data['content_data'] ); // Allow filtering sections $sections = apply_filters( 'ai_answer_toolkit_meta_box_sections', array( 'summary' => true, 'score' => true, 'stats' => true, ), $post->ID ); ?>

'aitk_summary', 'textarea_rows' => 5, 'media_buttons' => false, 'teeny' => true, 'quicktags' => false, 'tinymce' => array( 'toolbar1' => 'bold,italic,link,unlink,undo,redo', 'toolbar2' => '', ), ) ); ?>
0

/100

ID, $ultimate_score ); } else { $hints = AI_Answer_Toolkit_AI_Suggestions::get_improvement_hints( $post->ID, $unified_data['content_data'] ); } $score_value = isset( $unified_data['unified_score'] ) ? (int) $unified_data['unified_score'] : 0; $collapse_non_critical = $score_value >= 80; ?>
post_content ) ); $has_content = $content_word_count > 0 || ! empty( trim( $post->post_title ) ); ?> = 80 ) : ?>

__( 'Summary & Answer Optimization', 'ai-answer-toolkit' ), 'question_intent' => __( 'Question & Intent Coverage', 'ai-answer-toolkit' ), 'content_architecture' => __( 'Content Architecture & Formatting', 'ai-answer-toolkit' ), 'schema_structured' => __( 'Structured Data & Schema', 'ai-answer-toolkit' ), 'semantic_nlp' => __( 'Semantic & NLP Optimization', 'ai-answer-toolkit' ), 'technical_seo' => __( 'Technical AEO & SEO Foundations', 'ai-answer-toolkit' ), 'answer_extraction' => __( 'Answer Extraction Readiness', 'ai-answer-toolkit' ), 'ai_llm' => __( 'AI & LLM Optimization Signals', 'ai-answer-toolkit' ), 'eeat_trust' => __( 'E-E-A-T & Trust Signals', 'ai-answer-toolkit' ), 'analytics_future' => __( 'Analytics, Testing & Future Readiness', 'ai-answer-toolkit' ), ); foreach ( $category_map as $key => $label ) { if ( isset( $breakdown[ $key ]['checks'] ) ) { $checks = $breakdown[ $key ]['checks']; $total_checks = count( $checks ); $passed_checks = 0; foreach ( $checks as $check ) { if ( isset( $check['passed'] ) && $check['passed'] ) { $passed_checks++; } } // Calculate completion percentage: (passed / total) * 100 $category_completion[ $label ] = $total_checks > 0 ? round( ( $passed_checks / $total_checks ) * 100 ) : 0; } } } $grouped = array(); foreach ( $hints as $hint ) { $category = isset( $hint['category'] ) ? (string) $hint['category'] : __( 'Overall AEO Quality', 'ai-answer-toolkit' ); if ( ! isset( $grouped[ $category ] ) ) { $grouped[ $category ] = array(); } $grouped[ $category ][] = $hint; } // Ultimate AEO Category Order (Phase 2 - Complete) $category_order = array( // Phase 1 Categories __( 'Summary & Answer Optimization', 'ai-answer-toolkit' ), __( 'Question & Intent Coverage', 'ai-answer-toolkit' ), __( 'Content Architecture & Formatting', 'ai-answer-toolkit' ), __( 'Structured Data & Schema', 'ai-answer-toolkit' ), // Phase 2 Categories __( 'Semantic & NLP Optimization', 'ai-answer-toolkit' ), __( 'Technical AEO & SEO Foundations', 'ai-answer-toolkit' ), __( 'Answer Extraction Readiness', 'ai-answer-toolkit' ), __( 'AI & LLM Optimization Signals', 'ai-answer-toolkit' ), __( 'E-E-A-T & Trust Signals', 'ai-answer-toolkit' ), __( 'Analytics, Testing & Future Readiness', 'ai-answer-toolkit' ), // Legacy categories (fallback) __( 'Summary Optimization', 'ai-answer-toolkit' ), __( 'FAQ & Q&A Structure', 'ai-answer-toolkit' ), __( 'Content Structure', 'ai-answer-toolkit' ), __( 'Service Metadata', 'ai-answer-toolkit' ), __( 'Overall AEO Quality', 'ai-answer-toolkit' ), ); $sorted_grouped = array(); foreach ( $category_order as $cat ) { if ( isset( $grouped[ $cat ] ) ) { $sorted_grouped[ $cat ] = $grouped[ $cat ]; unset( $grouped[ $cat ] ); } } foreach ( $grouped as $cat => $items ) { $sorted_grouped[ $cat ] = $items; } ?>
$items ) : $category_index++; $non_critical_only = true; foreach ( $items as $it ) { $sev = isset( $it['severity'] ) ? $it['severity'] : ( isset( $it['type'] ) ? $it['type'] : 'info' ); if ( $sev === 'critical' ) { $non_critical_only = false; break; } } // Only expand "Summary & Answer Optimization" by default $is_summary_category = ( $category === __( 'Summary & Answer Optimization', 'ai-answer-toolkit' ) ); $collapsed = ! $is_summary_category; // Get category completion percentage for progress indicator $completion_percent = isset( $category_completion[ $category ] ) ? $category_completion[ $category ] : 0; // Determine color based on completion percentage: gradient from red (0%) to green (100%) if ( $completion_percent == 0 ) { $progress_color = '#e0e0e0'; // Grey for 0% (empty circle) } else { // Clean gradient: red → orange → yellow → green // Using HSL color space for vibrant gradient // Red (0°) → Green (120°) in HSL $hue = ( $completion_percent / 100 ) * 120; // 0° to 120° $saturation = 65; // 65% saturation for vibrant colors $lightness = 45; // 45% lightness for good visibility // Convert HSL to RGB $c = ( 1 - abs( 2 * ( $lightness / 100 ) - 1 ) ) * ( $saturation / 100 ); $x = $c * ( 1 - abs( fmod( $hue / 60, 2 ) - 1 ) ); $m = ( $lightness / 100 ) - ( $c / 2 ); if ( $hue < 60 ) { $r = $c; $g = $x; $b = 0; } elseif ( $hue < 120 ) { $r = $x; $g = $c; $b = 0; } else { $r = 0; $g = $c; $b = $x; } $red = (int)( ( $r + $m ) * 255 ); $green = (int)( ( $g + $m ) * 255 ); $blue = (int)( ( $b + $m ) * 255 ); $progress_color = sprintf( '#%02x%02x%02x', $red, $green, $blue ); } ?>

ID ); ?>
post_content ) ); $title = trim( $post->post_title ); $has_content = ! empty( $content ) || ! empty( $title ); // Get content guidance analysis $guidance = AI_Answer_Toolkit_Content_Guidance::analyze_content( $post_id ); ?>

(missing)'; } } ?>
/100

$check_result ) : ?>

'dashicons-yes-alt', 'warning' => 'dashicons-warning', 'fail' => 'dashicons-dismiss', ); return isset( $icons[ $status ] ) ? $icons[ $status ] : 'dashicons-info'; } /** * Get visibility check label. * * @param string $check_name Check name. * @return string Check label. */ private static function get_visibility_check_label( $check_name ) { $labels = array( 'summary' => __( 'Summary Visibility', 'ai-answer-toolkit' ), 'article' => __( 'Article Schema', 'ai-answer-toolkit' ), 'faq' => __( 'FAQ Schema', 'ai-answer-toolkit' ), 'placement' => __( 'Summary Placement', 'ai-answer-toolkit' ), ); return isset( $labels[ $check_name ] ) ? $labels[ $check_name ] : ucfirst( $check_name ); } /** * Get warnings for the post. * * @param int $post_id Post ID. * @param array $score_data Score data. * @return array Warnings. */ private static function get_warnings( $post_id, $score_data ) { $warnings = array(); // Check for issues in score data if ( ! empty( $score_data['issues'] ) ) { foreach ( $score_data['issues'] as $issue ) { $warnings[] = array( 'type' => 'warning', 'message' => $issue, ); } } // Critical warning is now displayed directly after score status, not in warnings list // Allow filtering return apply_filters( 'ai_answer_toolkit_meta_box_warnings', $warnings, $post_id ); } /** * Save meta box data. * * @param int $post_id Post ID. * @param WP_Post $post Post object. */ public static function save_meta_box( $post_id, $post ) { // Check nonce if ( ! isset( $_POST['AI_Answer_Toolkit_meta_box_nonce'] ) ) { return; } if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['AI_Answer_Toolkit_meta_box_nonce'] ) ), 'AI_Answer_Toolkit_meta_box' ) ) { return; } // Check autosave if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Check permissions $post_type = get_post_type_object( $post->post_type ); if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) ) { return; } // Save manual summary if ( isset( $_POST['aitk_summary'] ) ) { $summary = wp_kses_post( wp_unslash( $_POST['aitk_summary'] ) ); update_post_meta( $post_id, '_aitk_summary', $summary ); // Fire action hook do_action( 'ai_answer_toolkit_meta_box_summary_saved', $post_id, $summary ); } } /** * Handle AJAX request to refresh content guidance. */ public static function ajax_refresh_content_guidance() { // Verify nonce check_ajax_referer( 'aitk_ai_suggestions', 'nonce' ); // Get post ID and content $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; $content = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : ''; if ( ! $post_id ) { wp_send_json_error( array( 'message' => 'Invalid post ID' ) ); } // Get post object // Create temporary post object with updated content $post = get_post( $post_id ); if ( ! $post ) { wp_send_json_error( array( 'message' => 'Post not found' ) ); } // Temporarily update post content for analysis $original_content = $post->post_content; $post->post_content = $content; // Update global post temporarily $GLOBALS['post'] = $post; // Get fresh guidance analysis $guidance = AI_Answer_Toolkit_Content_Guidance::analyze_content( $post_id ); // Restore original content $post->post_content = $original_content; $GLOBALS['post'] = $post; // Generate HTML ob_start(); ?>

section (missing)
$html ) ); } /** * Handle unified AJAX request to recalculate all AEO components. * * Returns: score, tips, visibility, and guidance in a single response. * Phase 1: Quick Win - Batch all updates into one request. */ public static function ajax_recalculate_all() { // Verify nonce check_ajax_referer( 'aitk_ai_suggestions', 'nonce' ); // Get post ID and content $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; $content = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : ''; $title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : ''; $summary = isset( $_POST['summary'] ) ? wp_kses_post( wp_unslash( $_POST['summary'] ) ) : ''; if ( ! $post_id ) { wp_send_json_error( array( 'message' => 'Invalid post ID' ) ); } // Verify user can edit this post if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_send_json_error( array( 'message' => 'Permission denied' ) ); } // Get post object $post = get_post( $post_id ); if ( ! $post ) { wp_send_json_error( array( 'message' => 'Post not found' ) ); } // Temporarily update post data for analysis $original_content = $post->post_content; $original_title = $post->post_title; $original_summary = get_post_meta( $post_id, '_aitk_summary', true ); $post->post_content = $content; if ( ! empty( $title ) ) { $post->post_title = $title; } if ( ! empty( $summary ) ) { update_post_meta( $post_id, '_aitk_summary', $summary ); } $GLOBALS['post'] = $post; // Calculate all components $response = array(); // 1. Overall AEO Score $score_data = AI_Answer_Toolkit_Score::calculate_score( $post_id ); $response['score'] = array( 'unified_score' => $score_data['score'], 'label' => $score_data['label'], 'color' => $score_data['color'], 'status' => $score_data['status'], 'breakdown' => $score_data['breakdown'], ); // 2. Improvement Tips $tips = AI_Answer_Toolkit_AI_Suggestions::get_improvement_hints( $post_id ); $response['tips'] = self::render_improvement_tips_html( $tips ); // 3. Visibility Checks $visibility_data = AI_Answer_Toolkit_Visibility::analyze_visibility( $post_id ); $response['visibility'] = array( 'score' => $visibility_data['score'], 'checks' => $visibility_data['checks'], 'warnings' => $visibility_data['warnings'], 'html' => self::render_visibility_checks_html( $visibility_data ), ); // 4. Content Guidance $guidance = AI_Answer_Toolkit_Content_Guidance::analyze_content( $post_id ); $response['guidance'] = array( 'html' => self::render_content_guidance_html( $guidance ), ); // 5. Content Insights Stats $word_count = str_word_count( wp_strip_all_tags( $post->post_content ) ); $faq_count = self::count_faq_blocks( $post->post_content ); $final_summary = get_post_meta( $post_id, '_aitk_summary', true ); $response['stats'] = array( 'html' => self::render_stats_html( $post_id, $word_count, $faq_count, $final_summary ), ); // Restore original post data $post->post_content = $original_content; $post->post_title = $original_title; if ( ! empty( $summary ) ) { update_post_meta( $post_id, '_aitk_summary', $original_summary ); } $GLOBALS['post'] = $post; wp_send_json_success( $response ); } /** * Render improvement tips HTML for AJAX response. * * @param array $tips Improvement tips array. * @return string HTML output. */ private static function render_improvement_tips_html( $tips ) { if ( empty( $tips ) ) { return '

' . esc_html__( 'Great job! No improvement tips at this time.', 'ai-answer-toolkit' ) . '

'; } // Group tips by category $grouped = array(); foreach ( $tips as $tip ) { $cat = isset( $tip['category'] ) ? $tip['category'] : 'general'; if ( ! isset( $grouped[ $cat ] ) ) { $grouped[ $cat ] = array(); } $grouped[ $cat ][] = $tip; } ob_start(); foreach ( $grouped as $category => $category_tips ) { $cat_label = ucwords( str_replace( '_', ' ', $category ) ); $is_collapsed = ( $category !== 'critical' && $category !== 'content_quality' ) ? '1' : '0'; ?>
$check_result ) : ?>

(missing)'; } } ?>
admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aitk_ai_suggestions' ), ) ); } /** * Render stats HTML for AJAX response. * * @param int $post_id Post ID. * @param int $word_count Word count. * @param int $faq_count FAQ count. * @param string $final_summary Summary text. * @return string HTML output. */ private static function render_stats_html( $post_id, $word_count, $faq_count, $final_summary ) { $post = get_post( $post_id ); ob_start(); ?>
post_type === 'service' ) : ?>
✓ ✗
/i'; preg_match_all( $faq_pattern, $content, $matches ); return count( $matches[0] ); } } AI_Answer_Toolkit_Unified_Meta_Box::init();