$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'; $replace = isset($_POST['replace']) ? filter_var(wp_unslash($_POST['replace']), FILTER_VALIDATE_BOOLEAN) : false; // 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); } else if ($css_type === 'block') { $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 = ''; } if ($replace) { // Si c'est une édition manuelle, remplacer complètement le CSS de blocs $block_css = $css_content; } else { // Si c'est une génération IA, ajouter au CSS existant $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); } 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); wp_send_json_success(array( 'pageCss' => $page_css, 'blockCss' => $block_css, 'combinedCss' => $combined_css )); } public function save_post_js() { // Vérifier que les données POST existent if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['js_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']); $js_content = wp_unslash($_POST['js_content']); $js_type = isset($_POST['js_type']) ? sanitize_text_field(wp_unslash($_POST['js_type'])) : 'page'; $replace = isset($_POST['replace']) ? filter_var(wp_unslash($_POST['replace']), FILTER_VALIDATE_BOOLEAN) : false; // 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 ($js_type === 'page') { // Pour les pages, remplacer complètement le JS de page update_post_meta($post_id, 'ai_builder_page_js_content', $js_content); // Récupérer le JS de blocs existant $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true); // Combiner page JS + block JS pour le JS final $final_js = $js_content; if (!empty($block_js)) { $final_js .= "\n" . $block_js; } update_post_meta($post_id, 'ai_builder_js_content', $final_js); } else if ($js_type === 'block') { $page_js = get_post_meta($post_id, 'ai_builder_page_js_content', true); $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true); if (empty($page_js)) { $page_js = ''; } if (empty($block_js)) { $block_js = ''; } if ($replace) { // Si c'est une édition manuelle, remplacer complètement le JS de blocs $block_js = $js_content; } else { // Si c'est une génération IA, ajouter au JS existant $block_js .= "\n/* Block JS - " . date('Y-m-d H:i:s') . " */\n" . $js_content . "\n"; } // Sauvegarder le JS de bloc update_post_meta($post_id, 'ai_builder_block_js_content', $block_js); // Combiner page JS + block JS pour le JS final $final_js = $page_js; if (!empty($block_js)) { $final_js .= "\n" . $block_js; } update_post_meta($post_id, 'ai_builder_js_content', $final_js); } wp_send_json_success('JS saved successfully'); } public function get_post_js() { // 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 JS depuis les meta du post $page_js = get_post_meta($post_id, 'ai_builder_page_js_content', true); $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true); $combined_js = get_post_meta($post_id, 'ai_builder_js_content', true); wp_send_json_success(array( 'pageJS' => $page_js ?: '', 'blockJS' => $block_js ?: '', 'combinedJS' => $combined_js ?: '' )); } 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); } 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 .= '' . esc_html($f['label']) . '' . $val . ''; } $message = '
' . '

' . esc_html__('Nouveau message de contact', 'ai-builder') . '

' . '' . $rows . '
' . '
'; $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: -> $original_content = $content; $replacement_count = 0; $debug_log = array(); // Stocker les logs pour debug $content = preg_replace_callback( '//', 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 ""; }, $content ); // 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), '