PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.2
AI Builder – Generate pages, blocks, images & translate with AI v2.7.2
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
← All changes | includes/class-ajax-handler.php +235 -89 2.2.42.7.2 View file →
@@ -9,8 +9,10 @@
9 9 add_action('wp_ajax_aibui_signout', array($this, 'signout'));
10 10 add_action('wp_ajax_aibui_get_token', array($this, 'get_token'));
11 11 add_action('wp_ajax_aibui_save_post_css', array($this, 'save_post_css'));
12 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'));
13 15 add_action('wp_ajax_aibui_save_page_prompt', array($this, 'save_page_prompt'));
14 16 add_action('wp_ajax_aibui_get_page_prompt', array($this, 'get_page_prompt'));
15 17 add_action('wp_ajax_aibui_save_meta_description', array($this, 'save_meta_description'));
16 18 add_action('wp_ajax_aibui_create_page', array($this, 'create_page'));
@@ -25,8 +27,9 @@
25 27 add_action('wp_ajax_aibui_mark_generation_applied', array($this, 'mark_generation_applied'));
26 28
27 29 // Mark pages created via AI
28 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'));
29 32 }
30 33
31 34 public function save_token()
32 35 {
@@ -129,11 +132,12 @@
129 132 }
130 133
131 134 // Déséchapper et assainir les données
132 135 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
133 - $post_id = intval($_POST['post_id']);
136 + $raw_post_id = wp_unslash($_POST['post_id']);
134 137 $css_content = wp_unslash($_POST['css_content']);
135 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;
136 140
137 141 // Vérifier le nonce
138 142 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
139 143 wp_die('Security check failed');
@@ -138,8 +142,23 @@
138 142 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
139 143 wp_die('Security check failed');
140 144 }
141 145
146 + // --- Template / template part (Site Editor) ---
147 + // ex: "twentytwentyfive//404" ou "twentytwentyfive//header"
148 + if (class_exists('AIBUI_Template_Assets') && AIBUI_Template_Assets::is_template_id($raw_post_id)) {
149 + if (!AIBUI_Template_Assets::current_user_can_edit()) {
150 + wp_send_json_error('Insufficient permissions');
151 + }
152 + $ok = AIBUI_Template_Assets::instance()->save_css($raw_post_id, $css_content, $css_type, $replace);
153 + if ($ok) {
154 + wp_send_json_success('CSS saved successfully (template)');
155 + }
156 + wp_send_json_error('Unable to save template CSS');
157 + }
158 +
159 + $post_id = intval($raw_post_id);
160 +
142 161 // Vérifier que l'utilisateur peut éditer ce post
143 162 if (!current_user_can('edit_post', $post_id)) {
144 163 wp_send_json_error('Insufficient permissions');
145 164 }
@@ -157,11 +176,9 @@
157 176 $final_css .= "\n" . $block_css;
158 177 }
159 178 update_post_meta($post_id, 'ai_builder_css_content', $final_css);
160 179
161 - error_log("CSS Debug - Saving PAGE CSS. Page length: " . strlen($css_content) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
162 180 } else if ($css_type === 'block') {
163 - // Pour les blocs, ajouter au CSS existant
164 181 $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
165 182 $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
166 183
167 184 if (empty($page_css)) {
@@ -170,10 +187,15 @@
170 187 if (empty($block_css)) {
171 188 $block_css = '';
172 189 }
173 190
174 - // Ajouter le nouveau CSS de bloc
175 - $block_css .= "\n/* Block CSS - " . date('Y-m-d H:i:s') . " */\n" . $css_content . "\n";
191 + if ($replace) {
192 + // Si c'est une édition manuelle, remplacer complètement le CSS de blocs
193 + $block_css = $css_content;
194 + } else {
195 + // Si c'est une génération IA, ajouter au CSS existant
196 + $block_css .= "\n/* Block CSS - " . date('Y-m-d H:i:s') . " */\n" . $css_content . "\n";
197 + }
176 198
177 199 // Sauvegarder le CSS de bloc
178 200 update_post_meta($post_id, 'ai_builder_block_css_content', $block_css);
179 201
@@ -183,9 +205,8 @@
183 205 $final_css .= "\n" . $block_css;
184 206 }
185 207 update_post_meta($post_id, 'ai_builder_css_content', $final_css);
186 208
187 - error_log("CSS Debug - Saving BLOCK CSS. Page length: " . strlen($page_css) . ", Block length: " . strlen($block_css) . ", Final length: " . strlen($final_css));
188 209 }
189 210
190 211 wp_send_json_success('CSS saved successfully');
191 212 }
@@ -199,9 +220,9 @@
199 220 }
200 221
201 222 // Déséchapper et assainir les données
202 223 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
203 - $post_id = intval($_POST['post_id']);
224 + $raw_post_id = wp_unslash($_POST['post_id']);
204 225
205 226 // Vérifier le nonce
206 227 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
207 228 wp_die('Security check failed');
@@ -206,8 +227,23 @@
206 227 if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
207 228 wp_die('Security check failed');
208 229 }
209 230
231 + // --- Template / template part (Site Editor) ---
232 + if (class_exists('AIBUI_Template_Assets') && AIBUI_Template_Assets::is_template_id($raw_post_id)) {
233 + if (!AIBUI_Template_Assets::current_user_can_edit()) {
234 + wp_send_json_error('Insufficient permissions');
235 + }
236 + $entry = AIBUI_Template_Assets::instance()->get_entry($raw_post_id);
237 + wp_send_json_success(array(
238 + 'pageCss' => isset($entry['page_css']) ? (string) $entry['page_css'] : '',
239 + 'blockCss' => isset($entry['block_css']) ? (string) $entry['block_css'] : '',
240 + 'combinedCss' => isset($entry['css']) ? (string) $entry['css'] : '',
241 + ));
242 + }
243 +
244 + $post_id = intval($raw_post_id);
245 +
210 246 // Vérifier que l'utilisateur peut lire ce post
211 247 if (!current_user_can('read_post', $post_id)) {
212 248 wp_send_json_error('Insufficient permissions');
213 249 }
@@ -216,13 +252,8 @@
216 252 $page_css = get_post_meta($post_id, 'ai_builder_page_css_content', true);
217 253 $block_css = get_post_meta($post_id, 'ai_builder_block_css_content', true);
218 254 $combined_css = get_post_meta($post_id, 'ai_builder_css_content', true);
219 255
220 - // Log pour debug
221 - error_log("CSS Debug - Page CSS length: " . strlen($page_css));
222 - error_log("CSS Debug - Block CSS length: " . strlen($block_css));
223 - error_log("CSS Debug - Combined CSS length: " . strlen($combined_css));
224 -
225 256 wp_send_json_success(array(
226 257 'pageCss' => $page_css,
227 258 'blockCss' => $block_css,
228 259 'combinedCss' => $combined_css
@@ -228,8 +259,142 @@
228 259 'combinedCss' => $combined_css
229 260 ));
230 261 }
231 262
263 + public function save_post_js()
264 + {
265 + // Vérifier que les données POST existent
266 + if (!isset($_POST['nonce']) || !isset($_POST['post_id']) || !isset($_POST['js_content'])) {
267 + wp_send_json_error('Missing required data');
268 + }
269 +
270 + // Déséchapper et assainir les données
271 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
272 + $raw_post_id = wp_unslash($_POST['post_id']);
273 + $js_content = wp_unslash($_POST['js_content']);
274 + $js_type = isset($_POST['js_type']) ? sanitize_text_field(wp_unslash($_POST['js_type'])) : 'page';
275 + $replace = isset($_POST['replace']) ? filter_var(wp_unslash($_POST['replace']), FILTER_VALIDATE_BOOLEAN) : false;
276 +
277 + // Vérifier le nonce
278 + if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
279 + wp_die('Security check failed');
280 + }
281 +
282 + // --- Template / template part (Site Editor) ---
283 + if (class_exists('AIBUI_Template_Assets') && AIBUI_Template_Assets::is_template_id($raw_post_id)) {
284 + if (!AIBUI_Template_Assets::current_user_can_edit()) {
285 + wp_send_json_error('Insufficient permissions');
286 + }
287 + $ok = AIBUI_Template_Assets::instance()->save_js($raw_post_id, $js_content, $js_type, $replace);
288 + if ($ok) {
289 + wp_send_json_success('JS saved successfully (template)');
290 + }
291 + wp_send_json_error('Unable to save template JS');
292 + }
293 +
294 + $post_id = intval($raw_post_id);
295 +
296 + // Vérifier que l'utilisateur peut éditer ce post
297 + if (!current_user_can('edit_post', $post_id)) {
298 + wp_send_json_error('Insufficient permissions');
299 + }
300 +
301 + if ($js_type === 'page') {
302 + // Pour les pages, remplacer complètement le JS de page
303 + update_post_meta($post_id, 'ai_builder_page_js_content', $js_content);
304 +
305 + // Récupérer le JS de blocs existant
306 + $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true);
307 +
308 + // Combiner page JS + block JS pour le JS final
309 + $final_js = $js_content;
310 + if (!empty($block_js)) {
311 + $final_js .= "\n" . $block_js;
312 + }
313 + update_post_meta($post_id, 'ai_builder_js_content', $final_js);
314 +
315 + } else if ($js_type === 'block') {
316 + $page_js = get_post_meta($post_id, 'ai_builder_page_js_content', true);
317 + $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true);
318 +
319 + if (empty($page_js)) {
320 + $page_js = '';
321 + }
322 + if (empty($block_js)) {
323 + $block_js = '';
324 + }
325 +
326 + if ($replace) {
327 + // Si c'est une édition manuelle, remplacer complètement le JS de blocs
328 + $block_js = $js_content;
329 + } else {
330 + // Si c'est une génération IA, ajouter au JS existant
331 + $block_js .= "\n/* Block JS - " . date('Y-m-d H:i:s') . " */\n" . $js_content . "\n";
332 + }
333 +
334 + // Sauvegarder le JS de bloc
335 + update_post_meta($post_id, 'ai_builder_block_js_content', $block_js);
336 +
337 + // Combiner page JS + block JS pour le JS final
338 + $final_js = $page_js;
339 + if (!empty($block_js)) {
340 + $final_js .= "\n" . $block_js;
341 + }
342 + update_post_meta($post_id, 'ai_builder_js_content', $final_js);
343 +
344 + }
345 +
346 + wp_send_json_success('JS saved successfully');
347 + }
348 +
349 + public function get_post_js()
350 + {
351 + // Vérifier que les données POST existent
352 + if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
353 + wp_send_json_error('Missing required data');
354 + }
355 +
356 + // Déséchapper et assainir les données
357 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
358 + $raw_post_id = wp_unslash($_POST['post_id']);
359 +
360 + // Vérifier le nonce
361 + if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
362 + wp_die('Security check failed');
363 + }
364 +
365 + // --- Template / template part (Site Editor) ---
366 + if (class_exists('AIBUI_Template_Assets') && AIBUI_Template_Assets::is_template_id($raw_post_id)) {
367 + if (!AIBUI_Template_Assets::current_user_can_edit()) {
368 + wp_send_json_error('Insufficient permissions');
369 + }
370 + $entry = AIBUI_Template_Assets::instance()->get_entry($raw_post_id);
371 + wp_send_json_success(array(
372 + 'pageJS' => isset($entry['page_js']) ? (string) $entry['page_js'] : '',
373 + 'blockJS' => isset($entry['block_js']) ? (string) $entry['block_js'] : '',
374 + 'combinedJS' => isset($entry['js']) ? (string) $entry['js'] : '',
375 + ));
376 + }
377 +
378 + $post_id = intval($raw_post_id);
379 +
380 + // Vérifier que l'utilisateur peut lire ce post
381 + if (!current_user_can('read_post', $post_id)) {
382 + wp_send_json_error('Insufficient permissions');
383 + }
384 +
385 + // Récupérer les JS depuis les meta du post
386 + $page_js = get_post_meta($post_id, 'ai_builder_page_js_content', true);
387 + $block_js = get_post_meta($post_id, 'ai_builder_block_js_content', true);
388 + $combined_js = get_post_meta($post_id, 'ai_builder_js_content', true);
389 +
390 + wp_send_json_success(array(
391 + 'pageJS' => $page_js ?: '',
392 + 'blockJS' => $block_js ?: '',
393 + 'combinedJS' => $combined_js ?: ''
394 + ));
395 + }
396 +
232 397 public function save_meta_description()
233 398 {
234 399 if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
235 400 wp_send_json_error('Missing required data');
@@ -265,11 +430,9 @@
265 430 {
266 431 $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
267 432 $key = 'aibui_cf_mailerr_' . md5($ip);
268 433 set_transient($key, $wp_error instanceof WP_Error ? $wp_error->get_error_message() : 'Unknown mail error', 120);
269 - if (defined('WP_DEBUG') && WP_DEBUG) {
270 - 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)));
271 - }
434 +
272 435 }
273 436
274 437 public function submit_contact_form()
275 438 {
@@ -440,32 +603,8 @@
440 603 },
441 604 $content
442 605 );
443 606
444 - // Créer un fichier de log dans le plugin
445 - $log_file = plugin_dir_path(__FILE__) . '../debug-unescape.log';
446 - $log_content = "=== DEBUG UNESCAPE - " . date('Y-m-d H:i:s') . " ===\n";
447 - $log_content .= "Total corrections appliquées: " . $replacement_count . "\n\n";
448 - $log_content .= "Contenu original (premiers 500 chars):\n" . substr($original_content, 0, 500) . "\n\n";
449 - $log_content .= "Contenu corrigé (premiers 500 chars):\n" . substr($content, 0, 500) . "\n\n";
450 - if (strlen($content) > 500) {
451 - $log_content .= "Contenu corrigé (derniers 500 chars):\n" . substr($content, -500) . "\n\n";
452 - }
453 - $log_content .= "\n=== Détails par bloc ===\n";
454 - foreach ($debug_log as $log) {
455 - $log_content .= "\nBloc: " . $log['block'] . "\n";
456 - $log_content .= "Original JSON: " . $log['original_json'] . "\n";
457 - $log_content .= "Fixed JSON: " . $log['fixed_json'] . "\n";
458 - if (isset($log['error'])) {
459 - $log_content .= "ERREUR: " . $log['error'] . "\n";
460 - $log_content .= "JSON complet: " . $log['original_json_full'] . "\n";
461 - } else {
462 - $log_content .= "SUCCÈS\n";
463 - }
464 - }
465 - $log_content .= "\n=== FIN DEBUG ===\n\n";
466 - file_put_contents($log_file, $log_content, FILE_APPEND);
467 -
468 607 // Vérifier que le contenu est au format HTML sérialisé WordPress
469 608 // Le contenu doit commencer par un commentaire de bloc WordPress
470 609 if (empty($content) || strpos(trim($content), '<!-- wp:') !== 0) {
471 610 wp_send_json_error('Invalid content format: Expected WordPress serialized block HTML');
@@ -514,25 +653,20 @@
514 653 ));
515 654 }
516 655
517 656 // -----------------------------
518 - // Multi-Page: Generations store
657 + // Multi-Page: Generations store (using JSON files)
519 658 // -----------------------------
520 - private function get_generations_option()
659 + private function get_storage()
521 660 {
522 - $stored = get_option('aibui_multi_page_generations', array());
523 - if (!is_array($stored)) {
524 - $stored = array();
661 + static $storage = null;
662 + if ($storage === null) {
663 + require_once plugin_dir_path(__FILE__) . 'class-generations-storage.php';
664 + $storage = new AIBUI_Generations_Storage();
525 665 }
526 - return $stored;
666 + return $storage;
527 667 }
528 668
529 - private function persist_generations_option($generations)
530 - {
531 - if (!is_array($generations)) $generations = array();
532 - update_option('aibui_multi_page_generations', $generations, false);
533 - }
534 -
535 669 // Save a generation item (status: Pending review)
536 670 public function save_generation()
537 671 {
538 672 if (!isset($_POST['nonce'])) {
@@ -551,28 +685,16 @@
551 685 if (!$payload || !is_array($payload)) {
552 686 wp_send_json_error('Invalid payload');
553 687 }
554 688
555 - $id = isset($payload['id']) && is_string($payload['id']) ? $payload['id'] : wp_generate_uuid4();
556 - $now = current_time('mysql');
689 + $storage = $this->get_storage();
690 + $result = $storage->save($payload);
557 691
558 - $item = array(
559 - 'id' => $id,
560 - 'title' => sanitize_text_field($payload['title'] ?? ''),
561 - 'metaDesc' => sanitize_textarea_field($payload['metaDesc'] ?? ''),
562 - 'cssContent' => wp_kses_post($payload['cssContent'] ?? ''),
563 - 'blocksJson' => isset($payload['blocksJson']) ? $payload['blocksJson'] : array(),
564 - 'status' => 'Pending review',
565 - 'createdAt' => $now,
566 - 'applied' => false,
567 - 'pageId' => 0,
568 - );
692 + if (is_wp_error($result)) {
693 + wp_send_json_error($result->get_error_message());
694 + }
569 695
570 - $generations = $this->get_generations_option();
571 - $generations[$id] = $item;
572 - $this->persist_generations_option($generations);
573 -
574 - wp_send_json_success($item);
696 + wp_send_json_success($result);
575 697 }
576 698
577 699 // List generations
578 700 public function get_generations()
@@ -586,14 +708,12 @@
586 708 }
587 709 if (!current_user_can('edit_posts')) {
588 710 wp_send_json_error('Insufficient permissions');
589 711 }
590 - $generations = $this->get_generations_option();
591 - // Return newest first
592 - $items = array_values($generations);
593 - usort($items, function($a, $b) {
594 - return strcmp($b['createdAt'] ?? '', $a['createdAt'] ?? '');
595 - });
712 +
713 + $storage = $this->get_storage();
714 + $items = $storage->get_all();
715 +
596 716 wp_send_json_success($items);
597 717 }
598 718
599 719 // Get one generation by id
@@ -609,13 +729,17 @@
609 729 if (!current_user_can('edit_posts')) {
610 730 wp_send_json_error('Insufficient permissions');
611 731 }
612 732 $id = sanitize_text_field(wp_unslash($_POST['id']));
613 - $generations = $this->get_generations_option();
614 - if (!isset($generations[$id])) {
615 - wp_send_json_error('Not found');
733 +
734 + $storage = $this->get_storage();
735 + $result = $storage->get($id);
736 +
737 + if (is_wp_error($result)) {
738 + wp_send_json_error($result->get_error_message());
616 739 }
617 - wp_send_json_success($generations[$id]);
740 +
741 + wp_send_json_success($result);
618 742 }
619 743
620 744 // Mark generation as applied (optionally attach pageId and change status)
621 745 public function mark_generation_applied()
@@ -632,20 +756,16 @@
632 756 }
633 757 $id = sanitize_text_field(wp_unslash($_POST['id']));
634 758 $page_id = isset($_POST['page_id']) ? intval($_POST['page_id']) : 0;
635 759
636 - $generations = $this->get_generations_option();
637 - if (!isset($generations[$id])) {
638 - wp_send_json_error('Not found');
760 + $storage = $this->get_storage();
761 + $result = $storage->mark_applied($id, $page_id);
762 +
763 + if (is_wp_error($result)) {
764 + wp_send_json_error($result->get_error_message());
639 765 }
640 - $generations[$id]['applied'] = true;
641 - if ($page_id > 0) {
642 - $generations[$id]['pageId'] = $page_id;
643 - }
644 - $generations[$id]['status'] = 'Applied';
645 - $this->persist_generations_option($generations);
646 766
647 - wp_send_json_success($generations[$id]);
767 + wp_send_json_success($result);
648 768 }
649 769
650 770 public function save_page_prompt()
651 771 {
@@ -731,6 +851,32 @@
731 851 // Marquer la page comme créée via IA
732 852 update_post_meta($post_id, '_aibui_created_by_ai', '1');
733 853
734 854 wp_send_json_success('Page marked as AI-created');
855 + }
856 +
857 + /**
858 + * Get whether a post was created via AI Builder.
859 + */
860 + public function get_ai_created_status()
861 + {
862 + if (!isset($_POST['nonce']) || !isset($_POST['post_id'])) {
863 + wp_send_json_error('Missing required data');
864 + }
865 +
866 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
867 + $post_id = intval($_POST['post_id']);
868 +
869 + if (!wp_verify_nonce($nonce, 'aibui_nonce')) {
870 + wp_die('Security check failed');
871 + }
872 +
873 + if (!current_user_can('read_post', $post_id)) {
874 + wp_send_json_error('Insufficient permissions');
875 + }
876 +
877 + $flag = get_post_meta($post_id, '_aibui_created_by_ai', true);
878 + wp_send_json_success(array(
879 + 'isAICreated' => ($flag === '1' || $flag === 1 || $flag === true),
880 + ));
735 881 }
736 882 }