PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.2.4
AI Builder – Generate pages, blocks, images & translate with AI v2.2.4
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 2.3.11 All 121 releases
ai-builder / aibui-builder.php

aibui-builder.php in AI Builder – Generate pages, blocks, images & translate with AI 2.2.4, at aibui-builder.php

954 lines 34.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: AI Builder - Generate pages, blocks, text and images with AI
4 * Plugin URI: https://website-ai-builder.com/
5 * Description: This plugin is used to build your website with AI.
6 * Version: 2.2.4
7 * Author: enkic
8 * Author URI: https://enkicorbin.fr/
9 * License: GPLv2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: ai-builder
12 * Requires at least: 5.0
13 * Requires PHP: 7.4
14 */
15
16 if (!defined('ABSPATH'))
17 exit;
18
19 // Définir la version du plugin
20 define('AIBUI_VERSION', '2.2.4');
21
22 // Simple CSS minifier (safe whitespace/comment removal)
23 function aibui_minify_css($css)
24 {
25 if (!is_string($css) || $css === '') return '';
26 // Remove comments
27 $css = preg_replace('#/\*.*?\*/#s', '', $css);
28 // Collapse whitespace
29 $css = preg_replace('/\s+/', ' ', $css);
30 // Remove spaces around symbols
31 $css = preg_replace('/\s*([{};:,>\(\)])\s*/', '$1', $css);
32 // Final trims and unnecessary semicolons
33 $css = str_replace(';}', '}', $css);
34 return trim($css);
35 }
36
37 // Build or fetch a combined CSS file for plugin assets/css/*.css
38 function aibui_get_combined_css_url()
39 {
40 $css_dir = plugin_dir_path(__FILE__) . 'assets/css/';
41 $css_url_base = plugin_dir_url(__FILE__) . 'assets/css/';
42
43 // If directory missing, bail to original behavior
44 if (!is_dir($css_dir)) return '';
45
46 $files = glob($css_dir . '*.css');
47 if (!$files) return '';
48
49 // Compute a hash based on file mtimes and paths to invalidate cache when any source changes
50 $sig_parts = [];
51 foreach ($files as $path) {
52 $sig_parts[] = basename($path) . ':' . filemtime($path);
53 }
54 $signature = md5(implode('|', $sig_parts));
55
56 // Store in uploads to keep plugin dir clean and writable
57 $uploads = wp_upload_dir();
58 if (!empty($uploads['error'])) {
59 return '';
60 }
61 $cache_dir = rtrim($uploads['basedir'], '/').'/ai-builder-cache';
62 $cache_url = rtrim($uploads['baseurl'], '/').'/ai-builder-cache';
63 if (!is_dir($cache_dir)) {
64 wp_mkdir_p($cache_dir);
65 }
66
67 $combined_filename = 'combined-'.$signature.'.css';
68 $combined_path = $cache_dir.'/'.$combined_filename;
69 $combined_url = $cache_url.'/'.$combined_filename;
70
71 if (!file_exists($combined_path)) {
72 $buffer = '';
73 // Keep a stable order: alphabetical by filename
74 sort($files, SORT_STRING);
75 foreach ($files as $path) {
76 // Skip admin-only stylesheet to avoid leaking to frontend bundle
77 if (basename($path) === 'style-admin.css') continue;
78 $content = file_get_contents($path);
79 if ($content === false) continue;
80 $buffer .= "\n/* ".basename($path)." */\n".$content;
81 }
82 $minified = aibui_minify_css($buffer);
83 // Graceful write
84 if (is_writable($cache_dir)) {
85 file_put_contents($combined_path, $minified);
86 } else {
87 return '';
88 }
89 }
90
91 return $combined_url;
92 }
93
94 // Simple JS minifier (very conservative)
95 function aibui_minify_js($js)
96 {
97 if (!is_string($js) || $js === '') return '';
98 // Remove block comments but keep /*! license comments */
99 $js = preg_replace('#/(?!\!)(\*[^*]*\*+(?:[^/*][^*]*\*+)*/)#', '', $js);
100 // Remove line comments
101 $js = preg_replace('#(^|\s)//.*$#m', '$1', $js);
102 // Collapse whitespace
103 $js = preg_replace('/\s+/', ' ', $js);
104 return trim($js);
105 }
106
107 // Build a combined frontend JS bundle from selected plugin scripts
108 function aibui_get_combined_js_url()
109 {
110 $js_list = array(
111 'assets/js/carousel-frontend.js',
112 'assets/js/map-frontend.js',
113 'assets/js/tabs-frontend.js',
114 'assets/js/table-frontend.js',
115 'assets/js/stats-tooltips.js',
116 'assets/js/snackbar-frontend.js',
117 );
118
119 $sig_parts = array();
120 $contents = '';
121 foreach ($js_list as $rel) {
122 $path = plugin_dir_path(__FILE__) . $rel;
123 if (!file_exists($path)) continue;
124 $sig_parts[] = $rel . ':' . filemtime($path);
125 }
126 if (empty($sig_parts)) return '';
127 $signature = md5(implode('|', $sig_parts));
128
129 $uploads = wp_upload_dir();
130 if (!empty($uploads['error'])) {
131 return '';
132 }
133 $cache_dir = rtrim($uploads['basedir'], '/').'/ai-builder-cache';
134 $cache_url = rtrim($uploads['baseurl'], '/').'/ai-builder-cache';
135 if (!is_dir($cache_dir)) {
136 wp_mkdir_p($cache_dir);
137 }
138
139 $combined_filename = 'frontend-'.$signature.'.js';
140 $combined_path = $cache_dir.'/'.$combined_filename;
141 $combined_url = $cache_url.'/'.$combined_filename;
142
143 if (!file_exists($combined_path)) {
144 $buffer = '';
145 foreach ($js_list as $rel) {
146 $path = plugin_dir_path(__FILE__) . $rel;
147 if (!file_exists($path)) continue;
148 $content = file_get_contents($path);
149 if ($content === false) continue;
150 $buffer .= "\n/* ".$rel." */\n".$content."\n";
151 }
152 $minified = aibui_minify_js($buffer);
153 if (is_writable($cache_dir)) {
154 file_put_contents($combined_path, $minified);
155 } else {
156 return '';
157 }
158 }
159
160 return $combined_url;
161 }
162
163 // Charger les menus admin
164 require_once plugin_dir_path(__FILE__) . 'admin/menu.php';
165
166 // Charger le gestionnaire AJAX
167 require_once plugin_dir_path(__FILE__) . 'includes/class-ajax-handler.php';
168
169 // Charger le gestionnaire CSS
170 require_once plugin_dir_path(__FILE__) . 'includes/class-css-handler.php';
171
172 // Charger le gestionnaire de traduction
173 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-handler.php';
174 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
175 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
176 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-switcher.php';
177
178 // Initialiser le gestionnaire de traduction
179 new AIBUI_Translation_Handler();
180 AIBUI_Translation_Settings::init();
181 $translation_manager = new AIBUI_Translation_Manager();
182 $translation_switcher = new AIBUI_Translation_Switcher($translation_manager);
183
184 add_action('admin_enqueue_scripts', function ($hook) {
185 // Charger le CSS admin sur toutes les pages d'administration
186 wp_enqueue_style(
187 'ai-builder-admin-style',
188 plugin_dir_url(__FILE__) . 'assets/css/style-admin.css',
189 [],
190 AIBUI_VERSION
191 );
192
193 // Charger sur l'éditeur de page/article et l'éditeur de modèles (site editor)
194 if ($hook === 'post.php' || $hook === 'post-new.php' || $hook === 'site-editor.php') {
195 wp_enqueue_style(
196 'chat-widget-style',
197 plugin_dir_url(__FILE__) . 'assets/css/chat-widget.css',
198 [],
199 AIBUI_VERSION
200 );
201 wp_enqueue_script(
202 'ai-builder-config',
203 plugin_dir_url(__FILE__) . 'config.js',
204 [],
205 AIBUI_VERSION,
206 true
207 );
208 wp_enqueue_script(
209 'chat-widget',
210 plugin_dir_url(__FILE__) . 'assets/js/chat-widget.js',
211 ['ai-builder-config'],
212 AIBUI_VERSION,
213 true
214 );
215 // Styles tabs pour s'assurer du chargement dans l'éditeur
216 wp_enqueue_style(
217 'ai-builder-tabs-css-admin-editor',
218 plugin_dir_url(__FILE__) . 'assets/css/tabs.css',
219 [],
220 AIBUI_VERSION
221 );
222 // Injection des variables JS pour AJAX et le nonce
223 wp_localize_script(
224 'chat-widget',
225 'aiBuilderVars',
226 array(
227 'ajaxurl' => admin_url('admin-ajax.php'),
228 'nonce' => wp_create_nonce('aibui_nonce'),
229 // Flag to hint we are on the Site Editor (patterns/template parts)
230 'isPatternEditor' => ($hook === 'site-editor.php'),
231 )
232 );
233
234 // Enqueue Multi-Page apply script to support applying generations via URL param
235 wp_enqueue_script(
236 'ai-builder-multi-page-apply',
237 plugin_dir_url(__FILE__) . 'assets/js/multi-page-apply.js',
238 ['ai-builder-config', 'chat-widget', 'wp-blocks', 'wp-data', 'wp-editor', 'wp-block-editor', 'wp-element'],
239 AIBUI_VERSION,
240 true
241 );
242 wp_localize_script(
243 'ai-builder-multi-page-apply',
244 'aiBuilderVars',
245 array(
246 'ajaxurl' => admin_url('admin-ajax.php'),
247 'nonce' => wp_create_nonce('aibui_nonce'),
248 )
249 );
250 }
251
252 // Charger les styles et scripts pour la page account du plugin AI Builder
253 $current_screen = get_current_screen();
254 if ($current_screen && strpos($current_screen->id, 'aibui-assistant') !== false) {
255 wp_enqueue_style(
256 'ai-builder-account-style',
257 plugin_dir_url(__FILE__) . 'assets/css/account.css',
258 [],
259 filemtime(plugin_dir_path(__FILE__) . 'assets/css/account.css')
260 );
261 wp_enqueue_script(
262 'ai-builder-config',
263 plugin_dir_url(__FILE__) . 'config.js',
264 [],
265 AIBUI_VERSION,
266 true
267 );
268 wp_enqueue_script(
269 'ai-builder-account',
270 plugin_dir_url(__FILE__) . 'assets/js/account.js',
271 ['ai-builder-config'],
272 AIBUI_VERSION,
273 true
274 );
275 // Injection des variables JS pour AJAX et le nonce
276 wp_localize_script(
277 'ai-builder-account',
278 'aiBuilderVars',
279 array(
280 'ajaxurl' => admin_url('admin-ajax.php'),
281 'nonce' => wp_create_nonce('aibui_nonce'),
282 'accountUrl' => admin_url('admin.php?page=aibui-account'),
283 'siteDomain' => parse_url(home_url(), PHP_URL_HOST),
284 )
285 );
286 } else if ($current_screen && strpos($current_screen->id, 'aibui-credits') !== false) {
287 wp_enqueue_script(
288 'ai-builder-credits',
289 plugin_dir_url(__FILE__) . 'assets/js/credits.js',
290 ['ai-builder-config'],
291 AIBUI_VERSION,
292 true
293 );
294 // Injection des variables JS pour AJAX et le nonce
295 wp_localize_script(
296 'ai-builder-credits',
297 'aiBuilderVars',
298 array(
299 'ajaxurl' => admin_url('admin-ajax.php'),
300 'nonce' => wp_create_nonce('aibui_nonce'),
301 )
302 );
303 wp_enqueue_style(
304 'ai-builder-credits-additional-style',
305 plugin_dir_url(__FILE__) . 'assets/css/credits-additional.css',
306 [],
307 AIBUI_VERSION
308 );
309 wp_enqueue_style(
310 'ai-builder-credits-style',
311 plugin_dir_url(__FILE__) . 'assets/css/credits.css',
312 [],
313 AIBUI_VERSION
314 );
315 wp_enqueue_script(
316 'ai-builder-config',
317 plugin_dir_url(__FILE__) . 'config.js',
318 [],
319 AIBUI_VERSION,
320 true
321 );
322 } else if ($current_screen && strpos($current_screen->id, 'aibui-settings') !== false) {
323 wp_enqueue_style(
324 'ai-builder-settings-style',
325 plugin_dir_url(__FILE__) . 'assets/css/settings.css',
326 [],
327 AIBUI_VERSION
328 );
329 wp_enqueue_script(
330 'ai-builder-config',
331 plugin_dir_url(__FILE__) . 'config.js',
332 [],
333 AIBUI_VERSION,
334 true
335 );
336 wp_enqueue_script(
337 'ai-builder-settings',
338 plugin_dir_url(__FILE__) . 'assets/js/settings.js',
339 ['ai-builder-config'],
340 AIBUI_VERSION,
341 true
342 );
343 // Injection des variables JS pour AJAX et le nonce
344 wp_localize_script(
345 'ai-builder-settings',
346 'aiBuilderVars',
347 array(
348 'ajaxurl' => admin_url('admin-ajax.php'),
349 'nonce' => wp_create_nonce('aibui_nonce'),
350 )
351 );
352 } else if ($current_screen && strpos($current_screen->id, 'aibui-reset-password') !== false) {
353 wp_enqueue_style(
354 'ai-builder-reset-password-style',
355 plugin_dir_url(__FILE__) . 'assets/css/reset-password.css',
356 [],
357 AIBUI_VERSION
358 );
359 wp_enqueue_script(
360 'ai-builder-config',
361 plugin_dir_url(__FILE__) . 'config.js',
362 [],
363 AIBUI_VERSION,
364 true
365 );
366 wp_enqueue_script(
367 'ai-builder-reset-password',
368 plugin_dir_url(__FILE__) . 'assets/js/reset-password.js',
369 ['ai-builder-config'],
370 AIBUI_VERSION,
371 true
372 );
373 // Injection des variables JS pour AJAX et le nonce
374 wp_localize_script(
375 'ai-builder-reset-password',
376 'aiBuilderVars',
377 array(
378 'ajaxurl' => admin_url('admin-ajax.php'),
379 'nonce' => wp_create_nonce('aibui_nonce'),
380 'accountUrl' => admin_url('admin.php?page=aibui-account'),
381 )
382 );
383 } else if ($current_screen && strpos($current_screen->id, 'aibui-tuto') !== false) {
384 wp_enqueue_style(
385 'ai-builder-tutorial-style',
386 plugin_dir_url(__FILE__) . 'assets/css/tutorial.css',
387 [],
388 AIBUI_VERSION
389 );
390 wp_enqueue_script(
391 'ai-builder-config',
392 plugin_dir_url(__FILE__) . 'config.js',
393 [],
394 AIBUI_VERSION,
395 true
396 );
397 } else if ($current_screen && strpos($current_screen->id, 'aibui-multi-page') !== false) {
398 wp_enqueue_style(
399 'ai-builder-multi-page-style',
400 plugin_dir_url(__FILE__) . 'assets/css/multi-page.css',
401 [],
402 AIBUI_VERSION
403 );
404 wp_enqueue_script(
405 'ai-builder-config',
406 plugin_dir_url(__FILE__) . 'config.js',
407 [],
408 AIBUI_VERSION,
409 true
410 );
411 wp_enqueue_script(
412 'ai-builder-multi-page',
413 plugin_dir_url(__FILE__) . 'assets/js/multi-page.js',
414 ['ai-builder-config'],
415 AIBUI_VERSION,
416 true
417 );
418 // Injection des variables JS pour AJAX et le nonce
419 wp_localize_script(
420 'ai-builder-multi-page',
421 'aiBuilderVars',
422 array(
423 'ajaxurl' => admin_url('admin-ajax.php'),
424 'nonce' => wp_create_nonce('aibui_nonce'),
425 'adminBaseUrl' => admin_url(),
426 )
427 );
428 }
429
430 });
431
432
433 add_action('wp_enqueue_scripts', function () {
434 // Single combined frontend CSS
435 $combined_css = aibui_get_combined_css_url();
436 if ($combined_css) {
437 wp_enqueue_style(
438 'ai-builder-combined',
439 $combined_css,
440 [],
441 null
442 );
443 } else {
444 // Fallback: enqueue at least the essential stylesheet
445 wp_enqueue_style(
446 'aibui-force-alignfull',
447 plugin_dir_url(__FILE__) . 'assets/css/force-align-full.css',
448 [],
449 AIBUI_VERSION
450 );
451 }
452
453 // Charger les styles CSS du bloc AI Image côté frontend
454 wp_enqueue_style(
455 'ai-builder-ai-image-frontend',
456 plugin_dir_url(__FILE__) . 'assets/js/build/index.css',
457 [],
458 filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.css')
459 );
460 // Frontend JS single bundle
461 $combined_js = aibui_get_combined_js_url();
462 if ($combined_js) {
463 wp_enqueue_script(
464 'ai-builder-frontend-bundle',
465 $combined_js,
466 [],
467 null,
468 true
469 );
470 } else {
471 // Fallback: at least enqueue carousel script if bundling failed
472 wp_enqueue_script(
473 'ai-builder-carousel-frontend',
474 plugin_dir_url(__FILE__) . 'assets/js/carousel-frontend.js',
475 [],
476 AIBUI_VERSION,
477 true
478 );
479 }
480
481 // Load fixed background group block CSS for frontend
482 wp_enqueue_style(
483 'ai-builder-fixed-bg-group-frontend',
484 plugin_dir_url(__FILE__) . 'assets/js/src/editor-blocks/fixed-bg-group/style.css',
485 [],
486 AIBUI_VERSION
487 );
488
489 // CSS above is covered by the combined bundle
490
491 // Other JS are included in the combined bundle above
492 // CSS above is covered by the combined bundle
493 // Included in combined bundle
494 // CSS above is covered by the combined bundle
495 // Included in combined bundle
496 });
497
498 // Enqueue contact form script only when the block is present
499 add_action('wp_enqueue_scripts', function () {
500 global $post;
501 $should_load = false;
502
503 // Check if current post has the block
504 if (is_a($post, 'WP_Post') && has_block('ai-builder/aibui-contact-form', $post)) {
505 $should_load = true;
506 }
507
508 // Fallback: check if we're on a page that might have the block
509 if (!$should_load && (is_page() || is_single() || is_home() || is_front_page())) {
510 $should_load = true;
511 }
512
513 if ($should_load) {
514 wp_enqueue_script(
515 'ai-builder-contact-form',
516 plugin_dir_url(__FILE__) . 'assets/js/contact-form.js',
517 [],
518 AIBUI_VERSION,
519 true
520 );
521 }
522 });
523
524 add_action('enqueue_block_editor_assets', function () {
525 // Single combined CSS for editor
526 $combined_css = aibui_get_combined_css_url();
527 if ($combined_css) {
528 wp_enqueue_style(
529 'ai-builder-combined-editor',
530 $combined_css,
531 [],
532 null
533 );
534 } else {
535 wp_enqueue_style(
536 'aibui-force-alignfull-editor',
537 plugin_dir_url(__FILE__) . 'assets/css/force-align-full.css',
538 [],
539 AIBUI_VERSION
540 );
541 }
542 // Charger les styles build des blocs dans l'éditeur
543 wp_enqueue_style(
544 'ai-builder-blocks-editor-build',
545 plugin_dir_url(__FILE__) . 'assets/js/build/index.css',
546 [],
547 filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.css')
548 );
549 // Styles spécifiques d'aperçu du formulaire de contact
550 wp_enqueue_style(
551 'ai-builder-contact-form-editor',
552 plugin_dir_url(__FILE__) . 'assets/css/contact-form-editor.css',
553 [],
554 AIBUI_VERSION
555 );
556 // Covered by combined bundle
557 // Stats tooltips in editor preview
558 wp_enqueue_script(
559 'ai-builder-stats-tooltips-editor',
560 plugin_dir_url(__FILE__) . 'assets/js/stats-tooltips.js',
561 [],
562 AIBUI_VERSION,
563 true
564 );
565 });
566
567 add_action('enqueue_block_editor_assets', function () {
568 wp_enqueue_script(
569 'ai-builder-blocks',
570 plugin_dir_url(__FILE__) . 'assets/js/build/index.js',
571 [
572 'wp-blocks',
573 'wp-block-editor',
574 'wp-element',
575 'wp-components',
576 'wp-i18n',
577 'wp-api-fetch',
578 'wp-hooks'
579 ],
580 filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.js'),
581 true
582 );
583
584 // Injection des variables JS pour AJAX et le nonce (utilisées par les blocs éditeur)
585 wp_localize_script(
586 'ai-builder-blocks',
587 'aiBuilderVars',
588 array(
589 'ajaxurl' => admin_url('admin-ajax.php'),
590 'nonce' => wp_create_nonce('aibui_nonce'),
591 )
592 );
593
594 wp_enqueue_script(
595 'ai-builder-language-switcher-block',
596 plugin_dir_url(__FILE__) . 'assets/js/language-switcher-block.js',
597 array('wp-blocks', 'wp-block-editor', 'wp-element', 'wp-components', 'wp-i18n'),
598 filemtime(plugin_dir_path(__FILE__) . 'assets/js/language-switcher-block.js'),
599 true
600 );
601
602 $translation_settings = AIBUI_Translation_Settings::get_settings();
603 $supported_languages = AIBUI_Translation_Handler::get_supported_languages();
604 $available_langs = isset($translation_settings['available_langs']) && is_array($translation_settings['available_langs'])
605 ? array_values(array_unique($translation_settings['available_langs']))
606 : array();
607 $default_lang = isset($translation_settings['default_lang']) ? $translation_settings['default_lang'] : 'en';
608 if ($default_lang && !in_array($default_lang, $available_langs, true)) {
609 array_unshift($available_langs, $default_lang);
610 }
611 $switcher_defaults = array(
612 'backgroundColor' => !empty($translation_settings['switcher_bg']) ? $translation_settings['switcher_bg'] : '',
613 'textColor' => !empty($translation_settings['switcher_text']) ? $translation_settings['switcher_text'] : '',
614 'minWidth' => !empty($translation_settings['switcher_min_width']) ? (int) $translation_settings['switcher_min_width'] : 90,
615 'minHeight' => !empty($translation_settings['switcher_min_height']) ? (int) $translation_settings['switcher_min_height'] : 30,
616 'borderRadius' => 22,
617 );
618
619 wp_localize_script(
620 'ai-builder-language-switcher-block',
621 'aiBuilderLangSwitch',
622 array(
623 'availableLangs' => $available_langs,
624 'defaultLang' => $default_lang,
625 'labels' => $supported_languages,
626 'switcherEnabled' => !empty($translation_settings['enable_switcher']),
627 'defaults' => $switcher_defaults,
628 )
629 );
630 });
631
632 // Enregistrer le bloc côté PHP
633 add_action('init', function () use ($translation_switcher) {
634 wp_register_style(
635 'ai-builder-language-switcher-style',
636 plugin_dir_url(__FILE__) . 'assets/css/language-switcher.css',
637 array(),
638 AIBUI_VERSION
639 );
640 // Bloc AI Block
641 register_block_type('ai-builder/ai-block', [
642 'editor_script' => 'ai-builder-blocks',
643 'editor_style' => 'ai-builder-blocks-style',
644 'style' => 'ai-builder-blocks-style',
645 'render_callback' => function ($attributes) {
646 // Le bloc AI Block ne s'affiche pas côté frontend car il est remplacé par le contenu généré
647 return '';
648 }
649 ]);
650
651 // Bloc stat-bar (statique, pas de render_callback)
652 register_block_type('ai-builder/stat-bar', [
653 'editor_script' => 'ai-builder-blocks',
654 'editor_style' => 'ai-builder-blocks-style',
655 'style' => 'ai-builder-blocks-style',
656 ]);
657
658 // Bloc aibui-stats (rendu simple côté éditeur, frontend statique)
659 register_block_type('ai-builder/aibui-stats', [
660 'editor_script' => 'ai-builder-blocks',
661 'editor_style' => 'ai-builder-blocks-style',
662 'style' => 'ai-builder-blocks-style',
663 ]);
664
665 // Bloc aibui-carousel
666 register_block_type('ai-builder/aibui-carousel', [
667 'editor_script' => 'ai-builder-blocks',
668 'editor_style' => 'ai-builder-blocks-style',
669 'style' => 'ai-builder-blocks-style',
670 ]);
671
672 // Bloc aibui-yt-video
673 register_block_type('ai-builder/aibui-yt-video', [
674 'editor_script' => 'ai-builder-blocks',
675 'editor_style' => 'ai-builder-blocks-style',
676 'style' => 'ai-builder-blocks-style',
677 ]);
678
679 // Bloc aibui-map
680 register_block_type('ai-builder/aibui-map', [
681 'editor_script' => 'ai-builder-blocks',
682 'editor_style' => 'ai-builder-blocks-style',
683 'style' => 'ai-builder-blocks-style',
684 ]);
685
686 // Bloc aibui-tabs
687 // Bloc aibui-table
688 register_block_type('ai-builder/aibui-table', [
689 'editor_script' => 'ai-builder-blocks',
690 'editor_style' => 'ai-builder-blocks-style',
691 'style' => 'ai-builder-blocks-style',
692 ]);
693 register_block_type('ai-builder/aibui-tabs', [
694 'editor_script' => 'ai-builder-blocks',
695 'editor_style' => 'ai-builder-blocks-style',
696 'style' => 'ai-builder-blocks-style',
697 ]);
698
699 // Bloc aibui-contact-form (rendu via callback PHP pour sécurité)
700 register_block_type('ai-builder/aibui-contact-form', [
701 'editor_script' => 'ai-builder-blocks',
702 'editor_style' => 'ai-builder-blocks-style',
703 'style' => 'ai-builder-blocks-style',
704 'render_callback' => function ($attributes, $content, $block) {
705 $recipient = isset($attributes['recipientEmail']) ? sanitize_email($attributes['recipientEmail']) : '';
706 if (empty($recipient) || !is_email($recipient)) {
707 $recipient = sanitize_email(get_option('admin_email'));
708 }
709 if (empty($recipient) || !is_email($recipient)) {
710 // Pas de destinataire valide disponible, n'arrête pas l'affichage, mais désactive l'envoi
711 $recipient = '';
712 }
713
714 $fields = isset($attributes['fields']) && is_array($attributes['fields']) ? $attributes['fields'] : [];
715 $fields = array_slice($fields, 0, 5);
716
717 $form_title = isset($attributes['formTitle']) ? wp_kses_post($attributes['formTitle']) : '';
718 $description = isset($attributes['description']) ? wp_kses_post($attributes['description']) : '';
719 $button_label = isset($attributes['buttonLabel']) ? sanitize_text_field($attributes['buttonLabel']) : __('Send', 'ai-builder');
720 $success_message = isset($attributes['successMessage']) ? sanitize_text_field($attributes['successMessage']) : __('Thank you, your message has been sent.', 'ai-builder');
721 $from_email_attr = isset($attributes['fromEmail']) ? sanitize_email($attributes['fromEmail']) : '';
722
723 $nonce = wp_create_nonce('aibui_contact_form');
724 $action = esc_url(admin_url('admin-ajax.php'));
725
726 ob_start();
727 ?>
728 <form class="aibui-contact-form aibui-contact-form--frontend" method="post" action="<?php echo $action; ?>"
729 data-endpoint="<?php echo $action; ?>">
730 <?php if ($form_title): ?>
731 <h3 class="aibui-form-title"><?php echo $form_title; ?></h3>
732 <?php endif; ?>
733 <?php if ($description): ?>
734 <p class="aibui-form-description"><?php echo $description; ?></p>
735 <?php endif; ?>
736 <input type="hidden" name="action" value="aibui_submit_contact_form" />
737 <input type="hidden" name="nonce" value="<?php echo esc_attr($nonce); ?>" />
738 <input type="hidden" name="recipient" value="<?php echo esc_attr($recipient); ?>" />
739 <?php if ($from_email_attr): ?>
740 <input type="hidden" name="from_email" value="<?php echo esc_attr($from_email_attr); ?>" />
741 <?php endif; ?>
742 <?php foreach ($fields as $idx => $field):
743 $label = isset($field['label']) ? sanitize_text_field($field['label']) : '';
744 $type = isset($field['type']) ? sanitize_text_field($field['type']) : 'text';
745 $required = !empty($field['required']);
746 $name = 'field_' . $idx;
747 ?>
748 <div class="aibui-field">
749 <label>
750 <?php echo esc_html($label); ?> <?php echo $required ? ' *' : ''; ?>
751 <?php if ($type === 'textarea'): ?>
752 <textarea name="<?php echo esc_attr($name); ?>" <?php echo $required ? 'required' : ''; ?>
753 aria-label="<?php echo esc_attr($label); ?>"></textarea>
754 <?php else: ?>
755 <input
756 type="<?php echo $type === 'email' ? 'email' : ($type === 'number' ? 'number' : ($type === 'date' ? 'date' : 'text')); ?>"
757 name="<?php echo esc_attr($name); ?>" <?php echo $required ? 'required' : ''; ?>
758 aria-label="<?php echo esc_attr($label); ?>" />
759 <?php endif; ?>
760 <input type="hidden" name="label_<?php echo esc_attr($name); ?>" value="<?php echo esc_attr($label); ?>" />
761 <input type="hidden" name="type_<?php echo esc_attr($name); ?>" value="<?php echo esc_attr($type); ?>" />
762 <input type="hidden" name="required_<?php echo esc_attr($name); ?>"
763 value="<?php echo $required ? '1' : '0'; ?>" />
764 </label>
765 </div>
766 <?php endforeach; ?>
767 <div style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">
768 <label>Leave this field empty<input type="text" name="website" tabindex="-1" autocomplete="off" /></label>
769 </div>
770 <div class="aibui-form-message" role="status" aria-live="polite"></div>
771 <input type="hidden" name="success_message" value="<?php echo esc_attr($success_message); ?>" />
772 <button type="submit" class="aibui-button aibui-button--primary"><?php echo esc_html($button_label); ?></button>
773 </form>
774 <?php
775 return ob_get_clean();
776 }
777 ]);
778
779 register_block_type('ai-builder/language-switcher', [
780 'editor_script' => 'ai-builder-language-switcher-block',
781 'editor_style' => 'ai-builder-language-switcher-style',
782 'style' => 'ai-builder-language-switcher-style',
783 'render_callback' => array($translation_switcher, 'render_block'),
784 'attributes' => [
785 'backgroundColor' => ['type' => 'string', 'default' => '#5686c9'],
786 'textColor' => ['type' => 'string', 'default' => '#ffffff'],
787 'minWidth' => ['type' => 'number', 'default' => 90],
788 'minHeight' => ['type' => 'number', 'default' => 30],
789 'borderRadius' => ['type' => 'number', 'default' => 22],
790 ],
791 'supports' => [
792 'align' => ['left', 'center', 'right', 'wide', 'full'],
793 ],
794 ]);
795 });
796
797 add_action('after_setup_theme', function () {
798 add_theme_support('align-wide');
799 });
800
801 function enqueue_ai_builder_scripts() {
802 // Charger config.js en premier (dépendance pour les autres scripts)
803 wp_enqueue_script(
804 'ai-builder-config',
805 plugins_url('config.js', __FILE__),
806 array(),
807 AIBUI_VERSION,
808 false // Charger dans le <head> pour être disponible partout
809 );
810
811 // Autres scripts qui utilisent config.js
812 wp_enqueue_script(
813 'ai-builder-image-ai-controls',
814 plugins_url('assets/js/src/editor-blocks/image-ai-blocks/image-ai-controls.js', __FILE__),
815 array('ai-builder-config'),
816 AIBUI_VERSION,
817 true
818 );
819
820 // Autres scripts qui utilisent config.js
821 wp_enqueue_script(
822 'ai-builder-text-ai-controls',
823 plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__),
824 array('ai-builder-config'),
825 AIBUI_VERSION,
826 true
827 );
828
829 // Autres scripts qui utilisent config.js
830 wp_enqueue_script(
831 'ai-builder-text-ai-controls',
832 plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__),
833 array('ai-builder-config'),
834 AIBUI_VERSION,
835 true
836 );
837
838 // Autres scripts qui utilisent config.js
839 wp_enqueue_script(
840 'ai-builder-ai-block',
841 plugins_url('assets/js/src/editor-blocks/ai-block/ai-block.js', __FILE__),
842 array('ai-builder-config'),
843 AIBUI_VERSION,
844 true
845 );
846 }
847 add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts');
848
849 add_action('wp_enqueue_scripts', function () {
850 // Combined CSS already enqueued above; only enqueue any extra block scss-generated styles if needed
851 // Table, snackbar, cards editor styles are not required on frontend separately when bundled
852
853 // Load snackbar frontend JavaScript
854 wp_enqueue_script(
855 'ai-builder-snackbar-frontend',
856 plugin_dir_url(__FILE__) . 'assets/js/snackbar-frontend.js',
857 [],
858 AIBUI_VERSION,
859 true
860 );
861 });
862
863 add_action('enqueue_block_editor_assets', function () {
864 // Combined CSS already enqueued above; keep editor-specific assets below
865 // Load fixed background group block CSS specifically (already inside combined, but keep safe fallback if combined failed)
866 $fixed_bg_css_url = plugin_dir_url(__FILE__) . 'assets/js/src/editor-blocks/fixed-bg-group/style.css';
867 wp_enqueue_style('ai-builder-fixed-bg-group-editor', $fixed_bg_css_url, [], AIBUI_VERSION);
868
869 // Load WordPress media scripts for image selection
870 wp_enqueue_media();
871 });
872
873 // -------------------------------
874 // Meta description per page/post
875 // -------------------------------
876 add_action('add_meta_boxes', function () {
877 add_meta_box(
878 'aibui_meta_description',
879 __('Meta description', 'ai-builder'),
880 function ($post) {
881 $value = get_post_meta($post->ID, 'aibui_meta_description', true);
882 wp_nonce_field('aibui_meta_description_save', 'aibui_meta_description_nonce');
883 echo '<p>' . esc_html__('Add a meta description for this page/post.', 'ai-builder') . '</p>';
884 echo '<textarea style="width:100%;min-height:90px;" id="aibui_meta_description_field" name="aibui_meta_description" maxlength="320">' . esc_textarea($value) . '</textarea>';
885 echo '<p style="margin-top:6px;color:#555;">' . esc_html__('Tip: 140–160 characters for best SEO results.', 'ai-builder') . '</p>';
886 },
887 array('post', 'page'),
888 'normal',
889 'default'
890 );
891 });
892
893 add_action('save_post', function ($post_id) {
894 if (!isset($_POST['aibui_meta_description_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aibui_meta_description_nonce'])), 'aibui_meta_description_save')) {
895 return;
896 }
897 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
898 if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
899 if (!current_user_can('edit_post', $post_id)) return;
900
901 $raw = isset($_POST['aibui_meta_description']) ? wp_unslash($_POST['aibui_meta_description']) : '';
902 $san = trim(wp_strip_all_tags($raw));
903 if (strlen($san) > 320) {
904 $san = mb_substr($san, 0, 320);
905 }
906 if ($san === '') {
907 delete_post_meta($post_id, 'aibui_meta_description');
908 } else {
909 update_post_meta($post_id, 'aibui_meta_description', $san);
910 }
911 });
912
913 add_action('wp_head', function () {
914 if (is_admin() || !is_singular()) {
915 return;
916 }
917
918 $post_id = get_queried_object_id();
919 if (!$post_id) {
920 return;
921 }
922
923 $possible_keys = array(
924 'aibui_meta_description',
925 '_ai_builder_seo_desc',
926 '_yoast_wpseo_metadesc',
927 '_ai_translation_meta_desc'
928 );
929
930 $desc = '';
931 foreach ($possible_keys as $meta_key) {
932 $value = get_post_meta($post_id, $meta_key, true);
933 if (!empty($value)) {
934 $desc = $value;
935 break;
936 }
937 }
938
939 if (!$desc) {
940 $excerpt = get_post_field('post_excerpt', $post_id);
941 if (!empty($excerpt)) {
942 $desc = $excerpt;
943 }
944 }
945
946 $desc = trim(wp_strip_all_tags((string) $desc));
947 if ($desc !== '') {
948 echo "\n<meta name=\"description\" content=\"" . esc_attr($desc) . "\" />\n";
949 }
950 }, 1);
951
952 // Initialiser le gestionnaire AJAX
953 new AIBUI_Ajax_Handler();
954