PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.3.11
AI Builder – Generate pages, blocks, images & translate with AI v2.3.11
2.7.10 2.7.9 2.7.8 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.10 All 122 releases
ai-builder / includes / class-ajax-handler.php

class-ajax-handler.php in AI Builder – Generate pages, blocks, images & translate with AI 2.3.11, at includes/class-ajax-handler.php

721 lines 27.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class AIBUI_Ajax_Handler
4 {
5 public function __construct()
6 {
7 add_action('wp_ajax_aibui_save_token', array($this, 'save_token'));
8 add_action('wp_ajax_aibui_set_signup_success', array($this, 'set_signup_success'));
9 add_action('wp_ajax_aibui_signout', array($this, 'signout'));
10 add_action('wp_ajax_aibui_get_token', array($this, 'get_token'));
11 add_action('wp_ajax_aibui_save_post_css', array($this, 'save_post_css'));
12 add_action('wp_ajax_aibui_get_post_css', array($this, 'get_post_css'));
13 add_action('wp_ajax_aibui_save_page_prompt', array($this, 'save_page_prompt'));
14 add_action('wp_ajax_aibui_get_page_prompt', array($this, 'get_page_prompt'));
15 add_action('wp_ajax_aibui_save_meta_description', array($this, 'save_meta_description'));
16 add_action('wp_ajax_aibui_create_page', array($this, 'create_page'));
17 add_action('wp_ajax_nopriv_aibui_submit_contact_form', array($this, 'submit_contact_form'));
18 add_action('wp_ajax_aibui_submit_contact_form', array($this, 'submit_contact_form'));
19 add_action('wp_mail_failed', array($this, 'capture_mail_error'));
20
21 // Multi-page generations storage endpoints
22 add_action('wp_ajax_aibui_save_generation', array($this, 'save_generation'));
23 add_action('wp_ajax_aibui_get_generations', array($this, 'get_generations'));
24 add_action('wp_ajax_aibui_get_generation', array($this, 'get_generation'));
25 add_action('wp_ajax_aibui_mark_generation_applied', array($this, 'mark_generation_applied'));
26
27 // Mark pages created via AI
28 add_action('wp_ajax_aibui_mark_ai_created', array($this, 'mark_ai_created'));
29 }
30
31 public function save_token()
32 {
33 // Vérifier que les données POST existent
34 if (!isset($_POST['nonce']) || !isset($_POST['token'])) {
35 wp_send_json_error('Missing required data');
36 }
37
38 // Déséchapper et assainir les données
39 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
40 $token = sanitize_text_field(wp_unslash($_POST['token']));
41
42 // Vérifier le nonce
43 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
44 wp_die('Security check failed');
45 }
46
47 if (empty($token)) {
48 wp_send_json_error('Token is required');
49 }
50
51 // Sauvegarder le token JWT
52 update_option('aibui_jwt_token', $token);
53
54 wp_send_json_success('Token saved successfully');
55 }
56
57 public function set_signup_success()
58 {
59 // Vérifier que les données POST existent
60 if (!isset($_POST['nonce'])) {
61 wp_send_json_error('Missing required data');
62 }
63
64 // Déséchapper et assainir les données
65 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
66
67 // Vérifier le nonce
68 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
69 wp_die('Security check failed');
70 }
71
72 // Marquer l'inscription comme réussie
73 update_option('aibui_user_successful_signup', true);
74
75 wp_send_json_success('Signup success flag set');
76 }
77
78 public function signout()
79 {
80 // Vérifier que les données POST existent
81 if (!isset($_POST['nonce'])) {
82 wp_send_json_error('Missing required data');
83 }
84
85 // Déséchapper et assainir les données
86 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
87
88 // Vérifier le nonce
89 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
90 wp_die('Security check failed');
91 }
92
93 // Supprimer le token JWT
94 delete_option('aibui_jwt_token');
95
96 wp_send_json_success('Signed out successfully');
97 }
98
99 public function get_token()
100 {
101 // Increase timeout for this request (in case DB is slow)
102 // Default is usually 30s, but some hosts have shorter limits
103 @set_time_limit(60); // Allow up to 60 seconds
104
105 // Vérifier que les données POST existent
106 if (!isset($_POST['nonce'])) {
107 wp_send_json_error('Missing required data');
108 }
109
110 // Déséchapper et assainir les données
111 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
112
113 // Vérifier le nonce
114 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
115 wp_die('Security check failed');
116 }
117
118 // Récupérer le token JWT
119 $token = get_option('aibui_jwt_token', '');
120
121 if (empty($token)) {
122 wp_send_json_error('No token found');
123 }
124
125 wp_send_json_success(array('token' => $token));
126 }
127
128 public function save_post_css()
129 {
130 // Vérifier que les données POST existent
131 if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['css_content'])) {
132 wp_send_json_error('Missing required data');
133 }
134
135 // Déséchapper et assainir les données
136 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
137 $post_id = intval($_POST['post_id']);
138 $css_content = wp_unslash($_POST['css_content']);
139 $css_type = isset($_POST['css_type']) ? sanitize_text_field(wp_unslash($_POST['css_type'])) : 'page';
140
141 // Vérifier le nonce
142 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
143 wp_die('Security check failed');
144 }
145
146 // Vérifier que l'utilisateur peut éditer ce post
147 if (!current_user_can('edit_post', $post_id)) {
148 wp_send_json_error('Insufficient permissions');
149 }
150
151 if ($css_type === 'page') {
152 // Pour les pages, remplacer complètement le CSS de page
153 update_post_meta($post_id, 'ai_builder_page_css_content', $css_content);
154
155 // Récupérer le CSS de blocs existant
156 $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
157
158 // Combiner page CSS + block CSS pour le CSS final
159 $final_css = $css_content;
160 if (!empty($block_css)) {
161 $final_css .= "\n" . $block_css;
162 }
163 update_post_meta($post_id, 'ai_builder_css_content', $final_css);
164
165 error_log("CSS Debug - Saving PAGE CSS. Page length: " . strlen($css_content) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
166 } else if ($css_type === 'block') {
167 // Pour les blocs, ajouter au CSS existant
168 $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
169 $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
170
171 if (empty($page_css)) {
172 $page_css = '';
173 }
174 if (empty($block_css)) {
175 $block_css = '';
176 }
177
178 // Ajouter le nouveau CSS de bloc
179 $block_css .= "\n/* Block CSS - " . date('Y-m-d H:i:s') . " */\n" . $css_content . "\n";
180
181 // Sauvegarder le CSS de bloc
182 update_post_meta($post_id, 'ai_builder_block_css_content', $block_css);
183
184 // Combiner page CSS + block CSS pour le CSS final
185 $final_css = $page_css;
186 if (!empty($block_css)) {
187 $final_css .= "\n" . $block_css;
188 }
189 update_post_meta($post_id, 'ai_builder_css_content', $final_css);
190
191 error_log("CSS Debug - Saving BLOCK CSS. Page length: " . strlen($page_css) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
192 }
193
194 wp_send_json_success('CSS saved successfully');
195 }
196
197
198 public function get_post_css()
199 {
200 // Vérifier que les données POST existent
201 if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
202 wp_send_json_error('Missing required data');
203 }
204
205 // Déséchapper et assainir les données
206 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
207 $post_id = intval($_POST['post_id']);
208
209 // Vérifier le nonce
210 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
211 wp_die('Security check failed');
212 }
213
214 // Vérifier que l'utilisateur peut lire ce post
215 if (!current_user_can('read_post', $post_id)) {
216 wp_send_json_error('Insufficient permissions');
217 }
218
219 // Récupérer les CSS depuis les meta du post
220 $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
221 $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
222 $combined_css = get_post_meta($post_id, 'ai_builder_css_content', true);
223
224 // Log pour debug
225 error_log("CSS Debug - Page CSS length: " . strlen($page_css));
226 error_log("CSS Debug - Block CSS length: " . strlen($block_css));
227 error_log("CSS Debug - Combined CSS length: " . strlen($combined_css));
228
229 wp_send_json_success(array(
230 'pageCss' => $page_css,
231 'blockCss' => $block_css,
232 'combinedCss' => $combined_css
233 ));
234 }
235
236 public function save_meta_description()
237 {
238 if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
239 wp_send_json_error('Missing required data');
240 }
241
242 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
243 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
244 wp_die('Security check failed');
245 }
246
247 $post_id = intval($_POST['post_id']);
248 if (!current_user_can('edit_post', $post_id)) {
249 wp_send_json_error('Insufficient permissions');
250 }
251
252 $raw = isset($_POST['meta_desc']) ? wp_unslash($_POST['meta_desc']) : '';
253 $san = trim(wp_strip_all_tags($raw));
254 if (strlen($san) > 320) {
255 $san = mb_substr($san, 0, 320);
256 }
257
258 if ($san === '') {
259 delete_post_meta($post_id, 'aibui_meta_description');
260 } else {
261 update_post_meta($post_id, 'aibui_meta_description', $san);
262 }
263
264 wp_send_json_success('Meta description saved');
265 }
266
267 // Capture wp_mail() errors and store briefly to surface via AJAX
268 public function capture_mail_error($wp_error)
269 {
270 $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
271 $key = 'aibui_cf_mailerr_' . md5($ip);
272 set_transient($key, $wp_error instanceof WP_Error ? $wp_error->get_error_message() : 'Unknown mail error', 120);
273 if (defined('WP_DEBUG') && WP_DEBUG) {
274 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)));
275 }
276 }
277
278 public function submit_contact_form()
279 {
280 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'aibui_contact_form')) {
281 wp_send_json_error('Invalid nonce');
282 }
283
284 // Rate limiting per IP: 1 submission per 30 seconds
285 $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
286 $key = 'aibui_cf_rl_' . md5($ip);
287 $last = get_transient($key);
288 if ($last) {
289 wp_send_json_error('Too many requests. Please wait.');
290 }
291 set_transient($key, time(), 30);
292
293 $recipient = isset($_POST['recipient']) ? sanitize_email(wp_unslash($_POST['recipient'])) : '';
294 if (empty($recipient) || !is_email($recipient)) {
295 $recipient = sanitize_email(get_option('admin_email'));
296 }
297 if (empty($recipient) || !is_email($recipient)) {
298 wp_send_json_error('No valid recipient configured');
299 }
300
301 $subject = sprintf('[%s] Nouveau message de contact', get_bloginfo('name'));
302
303 $fields = [];
304 $sender_email = '';
305 foreach ($_POST as $key => $value) {
306 if (strpos($key, 'field_') === 0) {
307 $label_key = 'label_' . $key;
308 $type_key = 'type_' . $key;
309 $req_key = 'required_' . $key;
310 $label = isset($_POST[$label_key]) ? sanitize_text_field(wp_unslash($_POST[$label_key])) : 'Champ';
311 $type = isset($_POST[$type_key]) ? sanitize_text_field(wp_unslash($_POST[$type_key])) : 'text';
312 $is_required = isset($_POST[$req_key]) && wp_unslash($_POST[$req_key]) === '1';
313 $raw = wp_unslash($value);
314 switch ($type) {
315 case 'email':
316 $san = sanitize_email($raw);
317 if (!$sender_email && is_email($san)) {
318 $sender_email = $san;
319 }
320 break;
321 case 'number':
322 $san = is_numeric($raw) ? $raw : '';
323 break;
324 case 'date':
325 $san = preg_match('/^\\d{4}-\\d{2}-\\d{2}$/', $raw) ? $raw : '';
326 break;
327 case 'textarea':
328 $san = sanitize_textarea_field($raw);
329 break;
330 default:
331 $san = sanitize_text_field($raw);
332 }
333 if ($is_required && $san === '') {
334 wp_send_json_error(sprintf('%s est requis', $label ? $label : 'Ce champ'));
335 }
336 $fields[] = ['label' => $label, 'type' => $type, 'value' => $san];
337 }
338 }
339
340 if (empty($fields)) {
341 wp_send_json_error('No fields provided');
342 }
343
344 // Build HTML email content
345 $rows = '';
346 foreach ($fields as $f) {
347 $val = $f['type'] === 'textarea' ? nl2br(esc_html($f['value'])) : esc_html($f['value']);
348 $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>';
349 }
350 $message = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#111827;">'
351 . '<h3 style="margin:0 0 12px;">' . esc_html__('Nouveau message de contact', 'ai-builder') . '</h3>'
352 . '<table cellpadding="0" cellspacing="0" style="border-collapse:collapse;border:1px solid #e5e7eb;width:100%;max-width:720px;">'
353 . $rows
354 . '</table>'
355 . '</div>';
356
357 $headers = [];
358 $headers[] = 'Content-Type: text/html; charset=UTF-8';
359 $domain = parse_url(home_url(), PHP_URL_HOST);
360 $default_from = 'no-reply@' . $domain;
361 $user_from = isset($_POST['from_email']) ? sanitize_email(wp_unslash($_POST['from_email'])) : '';
362 $from_email = $default_from;
363 if ($user_from && is_email($user_from)) {
364 // Use as From only if same domain (avoid SPF/DMARC issues)
365 $user_domain = substr(strrchr($user_from, '@'), 1);
366 if ($user_domain && strtolower($user_domain) === strtolower($domain)) {
367 $from_email = $user_from;
368 }
369 }
370 $headers[] = 'From: ' . get_bloginfo('name') . ' <' . $from_email . '>';
371 if ($sender_email && is_email($sender_email)) {
372 $headers[] = 'Reply-To: ' . $sender_email;
373 }
374
375 $sent = wp_mail($recipient, $subject, $message, $headers);
376 if (!$sent) {
377 $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
378 $key_err = 'aibui_cf_mailerr_' . md5($ip);
379 $last_err = get_transient($key_err);
380 wp_send_json_error($last_err ? $last_err : 'Failed to send');
381 }
382 wp_send_json_success('Sent');
383 }
384
385 public function create_page()
386 {
387 // Vérifier que les données POST existent
388 if (!isset($_POST['nonce']) || !isset($_POST['content_type']) || !isset($_POST['title']) || !isset($_POST['content'])) {
389 wp_send_json_error('Missing required data');
390 }
391
392 // Déséchapper et assainir les données
393 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
394 $content_type = sanitize_text_field(wp_unslash($_POST['content_type']));
395 $title = sanitize_text_field(wp_unslash($_POST['title']));
396 $content = wp_unslash($_POST['content']);
397 $css_content = isset($_POST['css_content']) ? wp_unslash($_POST['css_content']) : '';
398 $meta_description = isset($_POST['meta_description']) ? sanitize_textarea_field(wp_unslash($_POST['meta_description'])) : '';
399
400 // Vérifier le nonce
401 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
402 wp_die('Security check failed');
403 }
404
405 // Vérifier que l'utilisateur peut créer des posts/pages
406 if (!current_user_can('publish_posts')) {
407 wp_send_json_error('Insufficient permissions');
408 }
409
410 // Déséchapper les JSON de commentaires de blocs si l'API a échappé les guillemets
411 // Exemple: <!-- wp:cover {\"align\":\"full\"} --> -> <!-- wp:cover {"align":"full"} -->
412 $original_content = $content;
413 $replacement_count = 0;
414 $debug_log = array(); // Stocker les logs pour debug
415
416 $content = preg_replace_callback(
417 '/<!--\s*wp:([^\s]+)\s+(\{.*?\})\s*-->/',
418 function ($matches) use (&$replacement_count, &$debug_log) {
419 $block_name = $matches[1];
420 $json_str = $matches[2];
421 $fixed_json = stripslashes($json_str);
422
423 // Log pour debug
424 $debug_info = array(
425 'block' => $block_name,
426 'original_json' => substr($json_str, 0, 200),
427 'fixed_json' => substr($fixed_json, 0, 200),
428 'success' => false
429 );
430
431 // Ne remplacer que si le JSON corrigé est valide
432 $decoded = json_decode($fixed_json, true);
433 if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
434 $debug_info['error'] = json_last_error_msg();
435 $debug_info['original_json_full'] = $json_str;
436 $debug_log[] = $debug_info;
437 return $matches[0];
438 }
439
440 $replacement_count++;
441 $debug_info['success'] = true;
442 $debug_log[] = $debug_info;
443 return "<!-- wp:" . $block_name . " " . $fixed_json . " -->";
444 },
445 $content
446 );
447
448 // Créer un fichier de log dans le plugin
449 $log_file = plugin_dir_path(__FILE__) . '../debug-unescape.log';
450 $log_content = "=== DEBUG UNESCAPE - " . date('Y-m-d H:i:s') . " ===\n";
451 $log_content .= "Total corrections appliquées: " . $replacement_count . "\n\n";
452 $log_content .= "Contenu original (premiers 500 chars):\n" . substr($original_content, 0, 500) . "\n\n";
453 $log_content .= "Contenu corrigé (premiers 500 chars):\n" . substr($content, 0, 500) . "\n\n";
454 if (strlen($content) > 500) {
455 $log_content .= "Contenu corrigé (derniers 500 chars):\n" . substr($content, -500) . "\n\n";
456 }
457 $log_content .= "\n=== Détails par bloc ===\n";
458 foreach ($debug_log as $log) {
459 $log_content .= "\nBloc: " . $log['block'] . "\n";
460 $log_content .= "Original JSON: " . $log['original_json'] . "\n";
461 $log_content .= "Fixed JSON: " . $log['fixed_json'] . "\n";
462 if (isset($log['error'])) {
463 $log_content .= "ERREUR: " . $log['error'] . "\n";
464 $log_content .= "JSON complet: " . $log['original_json_full'] . "\n";
465 } else {
466 $log_content .= "SUCCÈS\n";
467 }
468 }
469 $log_content .= "\n=== FIN DEBUG ===\n\n";
470 file_put_contents($log_file, $log_content, FILE_APPEND);
471
472 // Vérifier que le contenu est au format HTML sérialisé WordPress
473 // Le contenu doit commencer par un commentaire de bloc WordPress
474 if (empty($content) || strpos(trim($content), '<!-- wp:') !== 0) {
475 wp_send_json_error('Invalid content format: Expected WordPress serialized block HTML');
476 }
477
478 // Valider le format des blocs avec parse_blocks
479 $parsed_blocks = parse_blocks($content);
480 if (empty($parsed_blocks) || (count($parsed_blocks) === 1 && empty($parsed_blocks[0]['blockName']))) {
481 wp_send_json_error('Invalid block format: Could not parse blocks');
482 }
483
484 // Créer le post/page
485 $post_data = array(
486 'post_title' => $title,
487 'post_content' => $content, // Contenu HTML sérialisé directement
488 'post_status' => 'publish',
489 'post_type' => $content_type === 'post' ? 'post' : 'page',
490 'post_author' => get_current_user_id(),
491 );
492
493 $post_id = wp_insert_post($post_data);
494
495 if (is_wp_error($post_id)) {
496 wp_send_json_error('Failed to create ' . $content_type);
497 }
498
499 // Sauvegarder le CSS si présent
500 if (!empty($css_content)) {
501 update_post_meta($post_id, 'ai_builder_page_css_content', $css_content);
502 update_post_meta($post_id, 'ai_builder_css_content', $css_content);
503 }
504
505 // Sauvegarder la meta description si présente
506 if (!empty($meta_description)) {
507 update_post_meta($post_id, 'aibui_meta_description', $meta_description);
508 }
509
510 // Récupérer l'URL de la page créée
511 $page_url = get_permalink($post_id);
512
513
514 wp_send_json_success(array(
515 'page_id' => $post_id,
516 'page_url' => $page_url,
517 'page_title' => $title
518 ));
519 }
520
521 // -----------------------------
522 // Multi-Page: Generations store (using JSON files)
523 // -----------------------------
524 private function get_storage()
525 {
526 static $storage = null;
527 if ($storage === null) {
528 require_once plugin_dir_path(__FILE__) . 'class-generations-storage.php';
529 $storage = new AIBUI_Generations_Storage();
530 }
531 return $storage;
532 }
533
534 // Save a generation item (status: Pending review)
535 public function save_generation()
536 {
537 if (!isset($_POST['nonce'])) {
538 wp_send_json_error('Missing required data');
539 }
540 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
541 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
542 wp_die('Security check failed');
543 }
544 if (!current_user_can('edit_posts')) {
545 wp_send_json_error('Insufficient permissions');
546 }
547
548 $payload_raw = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : '';
549 $payload = json_decode($payload_raw, true);
550 if (!$payload || !is_array($payload)) {
551 wp_send_json_error('Invalid payload');
552 }
553
554 $storage = $this->get_storage();
555 $result = $storage->save($payload);
556
557 if (is_wp_error($result)) {
558 wp_send_json_error($result->get_error_message());
559 }
560
561 wp_send_json_success($result);
562 }
563
564 // List generations
565 public function get_generations()
566 {
567 if (!isset($_POST['nonce'])) {
568 wp_send_json_error('Missing required data');
569 }
570 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
571 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
572 wp_die('Security check failed');
573 }
574 if (!current_user_can('edit_posts')) {
575 wp_send_json_error('Insufficient permissions');
576 }
577
578 $storage = $this->get_storage();
579 $items = $storage->get_all();
580
581 wp_send_json_success($items);
582 }
583
584 // Get one generation by id
585 public function get_generation()
586 {
587 if (!isset($_POST['nonce']) || !isset($_POST['id'])) {
588 wp_send_json_error('Missing required data');
589 }
590 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
591 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
592 wp_die('Security check failed');
593 }
594 if (!current_user_can('edit_posts')) {
595 wp_send_json_error('Insufficient permissions');
596 }
597 $id = sanitize_text_field(wp_unslash($_POST['id']));
598
599 $storage = $this->get_storage();
600 $result = $storage->get($id);
601
602 if (is_wp_error($result)) {
603 wp_send_json_error($result->get_error_message());
604 }
605
606 wp_send_json_success($result);
607 }
608
609 // Mark generation as applied (optionally attach pageId and change status)
610 public function mark_generation_applied()
611 {
612 if (!isset($_POST['nonce']) || !isset($_POST['id'])) {
613 wp_send_json_error('Missing required data');
614 }
615 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
616 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
617 wp_die('Security check failed');
618 }
619 if (!current_user_can('edit_posts')) {
620 wp_send_json_error('Insufficient permissions');
621 }
622 $id = sanitize_text_field(wp_unslash($_POST['id']));
623 $page_id = isset($_POST['page_id']) ? intval($_POST['page_id']) : 0;
624
625 $storage = $this->get_storage();
626 $result = $storage->mark_applied($id, $page_id);
627
628 if (is_wp_error($result)) {
629 wp_send_json_error($result->get_error_message());
630 }
631
632 wp_send_json_success($result);
633 }
634
635 public function save_page_prompt()
636 {
637 // Vérifier que les données POST existent
638 if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['page_prompt'])) {
639 wp_send_json_error('Missing required data');
640 }
641
642 // Déséchapper et assainir les données
643 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
644 $post_id = intval($_POST['post_id']);
645 $page_prompt = sanitize_textarea_field(wp_unslash($_POST['page_prompt']));
646
647 // Vérifier le nonce
648 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
649 wp_die('Security check failed');
650 }
651
652 // Vérifier que l'utilisateur peut modifier ce post
653 if (!current_user_can('edit_post', $post_id)) {
654 wp_send_json_error('Insufficient permissions');
655 }
656
657 // Sauvegarder le prompt de page
658 update_post_meta($post_id, 'ai_builder_page_prompt', $page_prompt);
659
660 wp_send_json_success('Page prompt saved successfully');
661 }
662
663 public function get_page_prompt()
664 {
665 // Vérifier que les données POST existent
666 if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
667 wp_send_json_error('Missing required data');
668 }
669
670 // Déséchapper et assainir les données
671 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
672 $post_id = intval($_POST['post_id']);
673
674 // Vérifier le nonce
675 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
676 wp_die('Security check failed');
677 }
678
679 // Vérifier que l'utilisateur peut lire ce post
680 if (!current_user_can('read_post', $post_id)) {
681 wp_send_json_error('Insufficient permissions');
682 }
683
684 // Récupérer le prompt de page
685 $page_prompt = get_post_meta($post_id, 'ai_builder_page_prompt', true);
686
687 wp_send_json_success(array(
688 'pagePrompt' => $page_prompt ?: ''
689 ));
690 }
691
692 /**
693 * Marquer une page comme créée via IA
694 */
695 public function mark_ai_created()
696 {
697 // Vérifier que les données POST existent
698 if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
699 wp_send_json_error('Missing required data');
700 }
701
702 // Déséchapper et assainir les données
703 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
704 $post_id = intval($_POST['post_id']);
705
706 // Vérifier le nonce
707 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
708 wp_die('Security check failed');
709 }
710
711 // Vérifier que l'utilisateur peut éditer ce post
712 if (!current_user_can('edit_post', $post_id)) {
713 wp_send_json_error('Insufficient permissions');
714 }
715
716 // Marquer la page comme créée via IA
717 update_post_meta($post_id, '_aibui_created_by_ai', '1');
718
719 wp_send_json_success('Page marked as AI-created');
720 }
721 }