PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.1
AI Builder – Generate pages, blocks, images & translate with AI v2.7.1
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.7.1, at includes/class-ajax-handler.php

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