# ai-builder/2.2.3/includes/class-ajax-handler.php

AI Builder – Generate pages, blocks, images &amp; translate with AI, version 2.2.3. 736 lines.

- Page: https://pluginprobe.com/plugins/ai-builder/2.2.3/code/includes/class-ajax-handler.php
- Raw: https://pluginprobe.com/plugins/ai-builder/2.2.3/raw/includes/class-ajax-handler.php
- Modified: 2025-11-30T10:16:52+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/ai-builder/2.2.3/code/includes/class-ajax-handler.php#L10-L20`.

```php
<?php

class AIBUI_Ajax_Handler
{
    public function __construct()
    {
        add_action('wp_ajax_aibui_save_token', array($this, 'save_token'));
        add_action('wp_ajax_aibui_set_signup_success', array($this, 'set_signup_success'));
        add_action('wp_ajax_aibui_signout', array($this, 'signout'));
        add_action('wp_ajax_aibui_get_token', array($this, 'get_token'));
        add_action('wp_ajax_aibui_save_post_css', array($this, 'save_post_css'));
        add_action('wp_ajax_aibui_get_post_css', array($this, 'get_post_css'));
        add_action('wp_ajax_aibui_save_page_prompt', array($this, 'save_page_prompt'));
        add_action('wp_ajax_aibui_get_page_prompt', array($this, 'get_page_prompt'));
        add_action('wp_ajax_aibui_save_meta_description', array($this, 'save_meta_description'));
        add_action('wp_ajax_aibui_create_page', array($this, 'create_page'));
        add_action('wp_ajax_nopriv_aibui_submit_contact_form', array($this, 'submit_contact_form'));
        add_action('wp_ajax_aibui_submit_contact_form', array($this, 'submit_contact_form'));
        add_action('wp_mail_failed', array($this, 'capture_mail_error'));

        // Multi-page generations storage endpoints
        add_action('wp_ajax_aibui_save_generation', array($this, 'save_generation'));
        add_action('wp_ajax_aibui_get_generations', array($this, 'get_generations'));
        add_action('wp_ajax_aibui_get_generation', array($this, 'get_generation'));
        add_action('wp_ajax_aibui_mark_generation_applied', array($this, 'mark_generation_applied'));
        
        // Mark pages created via AI
        add_action('wp_ajax_aibui_mark_ai_created', array($this, 'mark_ai_created'));
    }

    public function save_token()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['token'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $token = sanitize_text_field(wp_unslash($_POST['token']));

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        if (empty($token)) {
            wp_send_json_error('Token is required');
        }

        // Sauvegarder le token JWT
        update_option('aibui_jwt_token', $token);

        wp_send_json_success('Token saved successfully');
    }

    public function set_signup_success()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Marquer l'inscription comme réussie
        update_option('aibui_user_successful_signup', true);

        wp_send_json_success('Signup success flag set');
    }

    public function signout()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Supprimer le token JWT
        delete_option('aibui_jwt_token');

        wp_send_json_success('Signed out successfully');
    }

    public function get_token()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Récupérer le token JWT
        $token = get_option('aibui_jwt_token', '');

        if (empty($token)) {
            wp_send_json_error('No token found');
        }

        wp_send_json_success(array('token' => $token));
    }

    public function save_post_css()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['css_content'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $post_id = intval($_POST['post_id']);
        $css_content = wp_unslash($_POST['css_content']);
        $css_type = isset($_POST['css_type']) ? sanitize_text_field(wp_unslash($_POST['css_type'])) : 'page';

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut éditer ce post
        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        if ($css_type === 'page') {
            // Pour les pages, remplacer complètement le CSS de page
            update_post_meta($post_id, 'ai_builder_page_css_content', $css_content);

            // Récupérer le CSS de blocs existant
            $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);

            // Combiner page CSS + block CSS pour le CSS final
            $final_css = $css_content;
            if (!empty($block_css)) {
                $final_css .= "\n" . $block_css;
            }
            update_post_meta($post_id, 'ai_builder_css_content', $final_css);

            error_log("CSS Debug - Saving PAGE CSS. Page length: " . strlen($css_content) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
        } else if ($css_type === 'block') {
            // Pour les blocs, ajouter au CSS existant
            $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
            $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);

            if (empty($page_css)) {
                $page_css = '';
            }
            if (empty($block_css)) {
                $block_css = '';
            }

            // Ajouter le nouveau CSS de bloc
            $block_css .= "\n/* Block CSS - " . date('Y-m-d H:i:s') . " */\n" . $css_content . "\n";

            // Sauvegarder le CSS de bloc
            update_post_meta($post_id, 'ai_builder_block_css_content', $block_css);

            // Combiner page CSS + block CSS pour le CSS final
            $final_css = $page_css;
            if (!empty($block_css)) {
                $final_css .= "\n" . $block_css;
            }
            update_post_meta($post_id, 'ai_builder_css_content', $final_css);

            error_log("CSS Debug - Saving BLOCK CSS. Page length: " . strlen($page_css) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
        }

        wp_send_json_success('CSS saved successfully');
    }


    public function get_post_css()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $post_id = intval($_POST['post_id']);

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut lire ce post
        if (!current_user_can('read_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        // Récupérer les CSS depuis les meta du post
        $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
        $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
        $combined_css = get_post_meta($post_id, 'ai_builder_css_content', true);

        // Log pour debug
        error_log("CSS Debug - Page CSS length: " . strlen($page_css));
        error_log("CSS Debug - Block CSS length: " . strlen($block_css));
        error_log("CSS Debug - Combined CSS length: " . strlen($combined_css));

        wp_send_json_success(array(
            'pageCss' => $page_css,
            'blockCss' => $block_css,
            'combinedCss' => $combined_css
        ));
    }

    public function save_meta_description()
    {
        if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
            wp_send_json_error('Missing required data');
        }

        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        $post_id = intval($_POST['post_id']);
        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        $raw = isset($_POST['meta_desc']) ? wp_unslash($_POST['meta_desc']) : '';
        $san = trim(wp_strip_all_tags($raw));
        if (strlen($san) > 320) {
            $san = mb_substr($san, 0, 320);
        }

        if ($san === '') {
            delete_post_meta($post_id, 'aibui_meta_description');
        } else {
            update_post_meta($post_id, 'aibui_meta_description', $san);
        }

        wp_send_json_success('Meta description saved');
    }

    // Capture wp_mail() errors and store briefly to surface via AJAX
    public function capture_mail_error($wp_error)
    {
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $key = 'aibui_cf_mailerr_' . md5($ip);
        set_transient($key, $wp_error instanceof WP_Error ? $wp_error->get_error_message() : 'Unknown mail error', 120);
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('[AIBUI] wp_mail_failed: ' . (is_object($wp_error) && method_exists($wp_error, 'get_error_message') ? $wp_error->get_error_message() : print_r($wp_error, true)));
        }
    }

    public function submit_contact_form()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aibui_contact_form')) {
            wp_send_json_error('Invalid nonce');
        }

        // Rate limiting per IP: 1 submission per 30 seconds
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $key = 'aibui_cf_rl_' . md5($ip);
        $last = get_transient($key);
        if ($last) {
            wp_send_json_error('Too many requests. Please wait.');
        }
        set_transient($key, time(), 30);

        $recipient = isset($_POST['recipient']) ? sanitize_email(wp_unslash($_POST['recipient'])) : '';
        if (empty($recipient) || !is_email($recipient)) {
            $recipient = sanitize_email(get_option('admin_email'));
        }
        if (empty($recipient) || !is_email($recipient)) {
            wp_send_json_error('No valid recipient configured');
        }

        $subject = sprintf('[%s] Nouveau message de contact', get_bloginfo('name'));

        $fields = [];
        $sender_email = '';
        foreach ($_POST as $key => $value) {
            if (strpos($key, 'field_') === 0) {
                $label_key = 'label_' . $key;
                $type_key = 'type_' . $key;
                $req_key = 'required_' . $key;
                $label = isset($_POST[$label_key]) ? sanitize_text_field(wp_unslash($_POST[$label_key])) : 'Champ';
                $type = isset($_POST[$type_key]) ? sanitize_text_field(wp_unslash($_POST[$type_key])) : 'text';
                $is_required = isset($_POST[$req_key]) && wp_unslash($_POST[$req_key]) === '1';
                $raw = wp_unslash($value);
                switch ($type) {
                    case 'email':
                        $san = sanitize_email($raw);
                        if (!$sender_email && is_email($san)) {
                            $sender_email = $san;
                        }
                        break;
                    case 'number':
                        $san = is_numeric($raw) ? $raw : '';
                        break;
                    case 'date':
                        $san = preg_match('/^\\d{4}-\\d{2}-\\d{2}$/', $raw) ? $raw : '';
                        break;
                    case 'textarea':
                        $san = sanitize_textarea_field($raw);
                        break;
                    default:
                        $san = sanitize_text_field($raw);
                }
                if ($is_required && $san === '') {
                    wp_send_json_error(sprintf('%s est requis', $label ? $label : 'Ce champ'));
                }
                $fields[] = ['label' => $label, 'type' => $type, 'value' => $san];
            }
        }

        if (empty($fields)) {
            wp_send_json_error('No fields provided');
        }

        // Build HTML email content
        $rows = '';
        foreach ($fields as $f) {
            $val = $f['type'] === 'textarea' ? nl2br(esc_html($f['value'])) : esc_html($f['value']);
            $rows .= '<tr><td style="padding:8px 12px;border:1px solid #e5e7eb;font-weight:600;">' . esc_html($f['label']) . '</td><td style="padding:8px 12px;border:1px solid #e5e7eb;">' . $val . '</td></tr>';
        }
        $message = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#111827;">'
            . '<h3 style="margin:0 0 12px;">' . esc_html__('Nouveau message de contact', 'ai-builder') . '</h3>'
            . '<table cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #e5e7eb;width:100%;max-width:720px;">'
            . $rows
            . '</table>'
            . '</div>';

        $headers = [];
        $headers[] = 'Content-Type: text/html; charset=UTF-8';
        $domain = parse_url(home_url(), PHP_URL_HOST);
        $default_from = 'no-reply@' . $domain;
        $user_from = isset($_POST['from_email']) ? sanitize_email(wp_unslash($_POST['from_email'])) : '';
        $from_email = $default_from;
        if ($user_from && is_email($user_from)) {
            // Use as From only if same domain (avoid SPF/DMARC issues)
            $user_domain = substr(strrchr($user_from, '@'), 1);
            if ($user_domain && strtolower($user_domain) === strtolower($domain)) {
                $from_email = $user_from;
            }
        }
        $headers[] = 'From: ' . get_bloginfo('name') . ' <' . $from_email . '>';
        if ($sender_email && is_email($sender_email)) {
            $headers[] = 'Reply-To: ' . $sender_email;
        }

        $sent = wp_mail($recipient, $subject, $message, $headers);
        if (!$sent) {
            $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
            $key_err = 'aibui_cf_mailerr_' . md5($ip);
            $last_err = get_transient($key_err);
            wp_send_json_error($last_err ? $last_err : 'Failed to send');
        }
        wp_send_json_success('Sent');
    }

    public function create_page()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['content_type']) || !isset($_POST['title']) || !isset($_POST['content'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $content_type = sanitize_text_field(wp_unslash($_POST['content_type']));
        $title = sanitize_text_field(wp_unslash($_POST['title']));
        $content = wp_unslash($_POST['content']);
        $css_content = isset($_POST['css_content']) ? wp_unslash($_POST['css_content']) : '';
        $meta_description = isset($_POST['meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['meta_description'])) : '';

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut créer des posts/pages
        if (!current_user_can('publish_posts')) {
            wp_send_json_error('Insufficient permissions');
        }

        // Déséchapper les JSON de commentaires de blocs si l'API a échappé les guillemets
        // Exemple: <!-- wp:cover {\"align\":\"full\"} --> -> <!-- wp:cover {"align":"full"} -->
        $original_content = $content;
        $replacement_count = 0;
        $debug_log = array(); // Stocker les logs pour debug
        
        $content = preg_replace_callback(
            '/<!--\s*wp:([^\s]+)\s+(\{.*?\})\s*-->/',
            function ($matches) use (&$replacement_count, &$debug_log) {
                $block_name = $matches[1];
                $json_str = $matches[2];
                $fixed_json = stripslashes($json_str);
                
                // Log pour debug
                $debug_info = array(
                    'block' => $block_name,
                    'original_json' => substr($json_str, 0, 200),
                    'fixed_json' => substr($fixed_json, 0, 200),
                    'success' => false
                );
                
                // Ne remplacer que si le JSON corrigé est valide
                $decoded = json_decode($fixed_json, true);
                if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
                    $debug_info['error'] = json_last_error_msg();
                    $debug_info['original_json_full'] = $json_str;
                    $debug_log[] = $debug_info;
                    return $matches[0];
                }
                
                $replacement_count++;
                $debug_info['success'] = true;
                $debug_log[] = $debug_info;
                return "<!-- wp:" . $block_name . " " . $fixed_json . " -->";
            },
            $content
        );
        
        // Créer un fichier de log dans le plugin
        $log_file = plugin_dir_path(__FILE__) . '../debug-unescape.log';
        $log_content = "=== DEBUG UNESCAPE - " . date('Y-m-d H:i:s') . " ===\n";
        $log_content .= "Total corrections appliquées: " . $replacement_count . "\n\n";
        $log_content .= "Contenu original (premiers 500 chars):\n" . substr($original_content, 0, 500) . "\n\n";
        $log_content .= "Contenu corrigé (premiers 500 chars):\n" . substr($content, 0, 500) . "\n\n";
        if (strlen($content) > 500) {
            $log_content .= "Contenu corrigé (derniers 500 chars):\n" . substr($content, -500) . "\n\n";
        }
        $log_content .= "\n=== Détails par bloc ===\n";
        foreach ($debug_log as $log) {
            $log_content .= "\nBloc: " . $log['block'] . "\n";
            $log_content .= "Original JSON: " . $log['original_json'] . "\n";
            $log_content .= "Fixed JSON: " . $log['fixed_json'] . "\n";
            if (isset($log['error'])) {
                $log_content .= "ERREUR: " . $log['error'] . "\n";
                $log_content .= "JSON complet: " . $log['original_json_full'] . "\n";
            } else {
                $log_content .= "SUCCÈS\n";
            }
        }
        $log_content .= "\n=== FIN DEBUG ===\n\n";
        file_put_contents($log_file, $log_content, FILE_APPEND);

        // Vérifier que le contenu est au format HTML sérialisé WordPress
        // Le contenu doit commencer par un commentaire de bloc WordPress
        if (empty($content) || strpos(trim($content), '<!-- wp:') !== 0) {
            wp_send_json_error('Invalid content format: Expected WordPress serialized block HTML');
        }

        // Valider le format des blocs avec parse_blocks
        $parsed_blocks = parse_blocks($content);
        if (empty($parsed_blocks) || (count($parsed_blocks) === 1 && empty($parsed_blocks[0]['blockName']))) {
            wp_send_json_error('Invalid block format: Could not parse blocks');
        }

        // Créer le post/page
        $post_data = array(
            'post_title' => $title,
            'post_content' => $content, // Contenu HTML sérialisé directement
            'post_status' => 'publish',
            'post_type' => $content_type === 'post' ? 'post' : 'page',
            'post_author' => get_current_user_id(),
        );

        $post_id = wp_insert_post($post_data);

        if (is_wp_error($post_id)) {
            wp_send_json_error('Failed to create ' . $content_type);
        }

        // Sauvegarder le CSS si présent
        if (!empty($css_content)) {
            update_post_meta($post_id, 'ai_builder_page_css_content', $css_content);
            update_post_meta($post_id, 'ai_builder_css_content', $css_content);
        }

        // Sauvegarder la meta description si présente
        if (!empty($meta_description)) {
            update_post_meta($post_id, 'aibui_meta_description', $meta_description);
        }

        // Récupérer l'URL de la page créée
        $page_url = get_permalink($post_id);
        

        wp_send_json_success(array(
            'page_id' => $post_id,
            'page_url' => $page_url,
            'page_title' => $title
        ));
    }

    // -----------------------------
    // Multi-Page: Generations store
    // -----------------------------
    private function get_generations_option()
    {
        $stored = get_option('aibui_multi_page_generations', array());
        if (!is_array($stored)) {
            $stored = array();
        }
        return $stored;
    }

    private function persist_generations_option($generations)
    {
        if (!is_array($generations)) $generations = array();
        update_option('aibui_multi_page_generations', $generations, false);
    }

    // Save a generation item (status: Pending review)
    public function save_generation()
    {
        if (!isset($_POST['nonce'])) {
            wp_send_json_error('Missing required data');
        }
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Insufficient permissions');
        }

        $payload_raw = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : '';
        $payload = json_decode($payload_raw, true);
        if (!$payload || !is_array($payload)) {
            wp_send_json_error('Invalid payload');
        }

        $id = isset($payload['id']) && is_string($payload['id']) ? $payload['id'] : wp_generate_uuid4();
        $now = current_time('mysql');

        $item = array(
            'id' => $id,
            'title' => sanitize_text_field($payload['title'] ?? ''),
            'metaDesc' => sanitize_textarea_field($payload['metaDesc'] ?? ''),
            'cssContent' => wp_kses_post($payload['cssContent'] ?? ''),
            'blocksJson' => isset($payload['blocksJson']) ? $payload['blocksJson'] : array(),
            'status' => 'Pending review',
            'createdAt' => $now,
            'applied' => false,
            'pageId' => 0,
        );

        $generations = $this->get_generations_option();
        $generations[$id] = $item;
        $this->persist_generations_option($generations);

        wp_send_json_success($item);
    }

    // List generations
    public function get_generations()
    {
        if (!isset($_POST['nonce'])) {
            wp_send_json_error('Missing required data');
        }
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Insufficient permissions');
        }
        $generations = $this->get_generations_option();
        // Return newest first
        $items = array_values($generations);
        usort($items, function($a, $b) {
            return strcmp($b['createdAt'] ?? '', $a['createdAt'] ?? '');
        });
        wp_send_json_success($items);
    }

    // Get one generation by id
    public function get_generation()
    {
        if (!isset($_POST['nonce']) || !isset($_POST['id'])) {
            wp_send_json_error('Missing required data');
        }
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Insufficient permissions');
        }
        $id = sanitize_text_field(wp_unslash($_POST['id']));
        $generations = $this->get_generations_option();
        if (!isset($generations[$id])) {
            wp_send_json_error('Not found');
        }
        wp_send_json_success($generations[$id]);
    }

    // Mark generation as applied (optionally attach pageId and change status)
    public function mark_generation_applied()
    {
        if (!isset($_POST['nonce']) || !isset($_POST['id'])) {
            wp_send_json_error('Missing required data');
        }
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Insufficient permissions');
        }
        $id = sanitize_text_field(wp_unslash($_POST['id']));
        $page_id = isset($_POST['page_id']) ? intval($_POST['page_id']) : 0;

        $generations = $this->get_generations_option();
        if (!isset($generations[$id])) {
            wp_send_json_error('Not found');
        }
        $generations[$id]['applied'] = true;
        if ($page_id > 0) {
            $generations[$id]['pageId'] = $page_id;
        }
        $generations[$id]['status'] = 'Applied';
        $this->persist_generations_option($generations);

        wp_send_json_success($generations[$id]);
    }

    public function save_page_prompt()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['page_prompt'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $post_id = intval($_POST['post_id']);
        $page_prompt = sanitize_textarea_field(wp_unslash($_POST['page_prompt']));

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut modifier ce post
        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        // Sauvegarder le prompt de page
        update_post_meta($post_id, 'ai_builder_page_prompt', $page_prompt);

        wp_send_json_success('Page prompt saved successfully');
    }

    public function get_page_prompt()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $post_id = intval($_POST['post_id']);

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut lire ce post
        if (!current_user_can('read_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        // Récupérer le prompt de page
        $page_prompt = get_post_meta($post_id, 'ai_builder_page_prompt', true);

        wp_send_json_success(array(
            'pagePrompt' => $page_prompt ?: ''
        ));
    }

    /**
     * Marquer une page comme créée via IA
     */
    public function mark_ai_created()
    {
        // Vérifier que les données POST existent
        if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
            wp_send_json_error('Missing required data');
        }

        // Déséchapper et assainir les données
        $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
        $post_id = intval($_POST['post_id']);

        // Vérifier le nonce
        if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
            wp_die('Security check failed');
        }

        // Vérifier que l'utilisateur peut éditer ce post
        if (!current_user_can('edit_post', $post_id)) {
            wp_send_json_error('Insufficient permissions');
        }

        // Marquer la page comme créée via IA
        update_post_meta($post_id, '_aibui_created_by_ai', '1');

        wp_send_json_success('Page marked as AI-created');
    }
}
```
