PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.1.7
AI Builder – Generate pages, blocks, images & translate with AI v2.1.7
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 / aibui-builder.php

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

866 lines 31.0 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.1.6
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.1.6');
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 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
595 // Enregistrer le bloc côté PHP
596 add_action('init', function () {
597 // Bloc AI Block
598 register_block_type('ai-builder/ai-block', [
599 'editor_script' => 'ai-builder-blocks',
600 'editor_style' => 'ai-builder-blocks-style',
601 'style' => 'ai-builder-blocks-style',
602 'render_callback' => function ($attributes) {
603 // Le bloc AI Block ne s'affiche pas côté frontend car il est remplacé par le contenu généré
604 return '';
605 }
606 ]);
607
608 // Bloc stat-bar (statique, pas de render_callback)
609 register_block_type('ai-builder/stat-bar', [
610 'editor_script' => 'ai-builder-blocks',
611 'editor_style' => 'ai-builder-blocks-style',
612 'style' => 'ai-builder-blocks-style',
613 ]);
614
615 // Bloc aibui-stats (rendu simple côté éditeur, frontend statique)
616 register_block_type('ai-builder/aibui-stats', [
617 'editor_script' => 'ai-builder-blocks',
618 'editor_style' => 'ai-builder-blocks-style',
619 'style' => 'ai-builder-blocks-style',
620 ]);
621
622 // Bloc aibui-carousel
623 register_block_type('ai-builder/aibui-carousel', [
624 'editor_script' => 'ai-builder-blocks',
625 'editor_style' => 'ai-builder-blocks-style',
626 'style' => 'ai-builder-blocks-style',
627 ]);
628
629 // Bloc aibui-yt-video
630 register_block_type('ai-builder/aibui-yt-video', [
631 'editor_script' => 'ai-builder-blocks',
632 'editor_style' => 'ai-builder-blocks-style',
633 'style' => 'ai-builder-blocks-style',
634 ]);
635
636 // Bloc aibui-map
637 register_block_type('ai-builder/aibui-map', [
638 'editor_script' => 'ai-builder-blocks',
639 'editor_style' => 'ai-builder-blocks-style',
640 'style' => 'ai-builder-blocks-style',
641 ]);
642
643 // Bloc aibui-tabs
644 // Bloc aibui-table
645 register_block_type('ai-builder/aibui-table', [
646 'editor_script' => 'ai-builder-blocks',
647 'editor_style' => 'ai-builder-blocks-style',
648 'style' => 'ai-builder-blocks-style',
649 ]);
650 register_block_type('ai-builder/aibui-tabs', [
651 'editor_script' => 'ai-builder-blocks',
652 'editor_style' => 'ai-builder-blocks-style',
653 'style' => 'ai-builder-blocks-style',
654 ]);
655
656 // Bloc aibui-contact-form (rendu via callback PHP pour sécurité)
657 register_block_type('ai-builder/aibui-contact-form', [
658 'editor_script' => 'ai-builder-blocks',
659 'editor_style' => 'ai-builder-blocks-style',
660 'style' => 'ai-builder-blocks-style',
661 'render_callback' => function ($attributes, $content, $block) {
662 $recipient = isset($attributes['recipientEmail']) ? sanitize_email($attributes['recipientEmail']) : '';
663 if (empty($recipient) || !is_email($recipient)) {
664 $recipient = sanitize_email(get_option('admin_email'));
665 }
666 if (empty($recipient) || !is_email($recipient)) {
667 // Pas de destinataire valide disponible, n'arrête pas l'affichage, mais désactive l'envoi
668 $recipient = '';
669 }
670
671 $fields = isset($attributes['fields']) && is_array($attributes['fields']) ? $attributes['fields'] : [];
672 $fields = array_slice($fields, 0, 5);
673
674 $form_title = isset($attributes['formTitle']) ? wp_kses_post($attributes['formTitle']) : '';
675 $description = isset($attributes['description']) ? wp_kses_post($attributes['description']) : '';
676 $button_label = isset($attributes['buttonLabel']) ? sanitize_text_field($attributes['buttonLabel']) : __('Send', 'ai-builder');
677 $success_message = isset($attributes['successMessage']) ? sanitize_text_field($attributes['successMessage']) : __('Thank you, your message has been sent.', 'ai-builder');
678 $from_email_attr = isset($attributes['fromEmail']) ? sanitize_email($attributes['fromEmail']) : '';
679
680 $nonce = wp_create_nonce('aibui_contact_form');
681 $action = esc_url(admin_url('admin-ajax.php'));
682
683 ob_start();
684 ?>
685 <form class="aibui-contact-form aibui-contact-form--frontend" method="post" action="<?php echo $action; ?>"
686 data-endpoint="<?php echo $action; ?>">
687 <?php if ($form_title): ?>
688 <h3 class="aibui-form-title"><?php echo $form_title; ?></h3>
689 <?php endif; ?>
690 <?php if ($description): ?>
691 <p class="aibui-form-description"><?php echo $description; ?></p>
692 <?php endif; ?>
693 <input type="hidden" name="action" value="aibui_submit_contact_form" />
694 <input type="hidden" name="nonce" value="<?php echo esc_attr($nonce); ?>" />
695 <input type="hidden" name="recipient" value="<?php echo esc_attr($recipient); ?>" />
696 <?php if ($from_email_attr): ?>
697 <input type="hidden" name="from_email" value="<?php echo esc_attr($from_email_attr); ?>" />
698 <?php endif; ?>
699 <?php foreach ($fields as $idx => $field):
700 $label = isset($field['label']) ? sanitize_text_field($field['label']) : '';
701 $type = isset($field['type']) ? sanitize_text_field($field['type']) : 'text';
702 $required = !empty($field['required']);
703 $name = 'field_' . $idx;
704 ?>
705 <div class="aibui-field">
706 <label>
707 <?php echo esc_html($label); ?> <?php echo $required ? ' *' : ''; ?>
708 <?php if ($type === 'textarea'): ?>
709 <textarea name="<?php echo esc_attr($name); ?>" <?php echo $required ? 'required' : ''; ?>
710 aria-label="<?php echo esc_attr($label); ?>"></textarea>
711 <?php else: ?>
712 <input
713 type="<?php echo $type === 'email' ? 'email' : ($type === 'number' ? 'number' : ($type === 'date' ? 'date' : 'text')); ?>"
714 name="<?php echo esc_attr($name); ?>" <?php echo $required ? 'required' : ''; ?>
715 aria-label="<?php echo esc_attr($label); ?>" />
716 <?php endif; ?>
717 <input type="hidden" name="label_<?php echo esc_attr($name); ?>" value="<?php echo esc_attr($label); ?>" />
718 <input type="hidden" name="type_<?php echo esc_attr($name); ?>" value="<?php echo esc_attr($type); ?>" />
719 <input type="hidden" name="required_<?php echo esc_attr($name); ?>"
720 value="<?php echo $required ? '1' : '0'; ?>" />
721 </label>
722 </div>
723 <?php endforeach; ?>
724 <div style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">
725 <label>Leave this field empty<input type="text" name="website" tabindex="-1" autocomplete="off" /></label>
726 </div>
727 <div class="aibui-form-message" role="status" aria-live="polite"></div>
728 <input type="hidden" name="success_message" value="<?php echo esc_attr($success_message); ?>" />
729 <button type="submit" class="aibui-button aibui-button--primary"><?php echo esc_html($button_label); ?></button>
730 </form>
731 <?php
732 return ob_get_clean();
733 }
734 ]);
735 });
736
737 add_action('after_setup_theme', function () {
738 add_theme_support('align-wide');
739 });
740
741 function enqueue_ai_builder_scripts() {
742 // Charger config.js en premier (dépendance pour les autres scripts)
743 wp_enqueue_script(
744 'ai-builder-config',
745 plugins_url('config.js', __FILE__),
746 array(),
747 AIBUI_VERSION,
748 false // Charger dans le <head> pour être disponible partout
749 );
750
751 // Autres scripts qui utilisent config.js
752 wp_enqueue_script(
753 'ai-builder-image-ai-controls',
754 plugins_url('assets/js/src/editor-blocks/image-ai-blocks/image-ai-controls.js', __FILE__),
755 array('ai-builder-config'),
756 AIBUI_VERSION,
757 true
758 );
759
760 // Autres scripts qui utilisent config.js
761 wp_enqueue_script(
762 'ai-builder-text-ai-controls',
763 plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__),
764 array('ai-builder-config'),
765 AIBUI_VERSION,
766 true
767 );
768
769 // Autres scripts qui utilisent config.js
770 wp_enqueue_script(
771 'ai-builder-text-ai-controls',
772 plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__),
773 array('ai-builder-config'),
774 AIBUI_VERSION,
775 true
776 );
777
778 // Autres scripts qui utilisent config.js
779 wp_enqueue_script(
780 'ai-builder-ai-block',
781 plugins_url('assets/js/src/editor-blocks/ai-block/ai-block.js', __FILE__),
782 array('ai-builder-config'),
783 AIBUI_VERSION,
784 true
785 );
786 }
787 add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts');
788
789 add_action('wp_enqueue_scripts', function () {
790 // Combined CSS already enqueued above; only enqueue any extra block scss-generated styles if needed
791 // Table, snackbar, cards editor styles are not required on frontend separately when bundled
792
793 // Load snackbar frontend JavaScript
794 wp_enqueue_script(
795 'ai-builder-snackbar-frontend',
796 plugin_dir_url(__FILE__) . 'assets/js/snackbar-frontend.js',
797 [],
798 AIBUI_VERSION,
799 true
800 );
801 });
802
803 add_action('enqueue_block_editor_assets', function () {
804 // Combined CSS already enqueued above; keep editor-specific assets below
805 // Load fixed background group block CSS specifically (already inside combined, but keep safe fallback if combined failed)
806 $fixed_bg_css_url = plugin_dir_url(__FILE__) . 'assets/js/src/editor-blocks/fixed-bg-group/style.css';
807 wp_enqueue_style('ai-builder-fixed-bg-group-editor', $fixed_bg_css_url, [], AIBUI_VERSION);
808
809 // Load WordPress media scripts for image selection
810 wp_enqueue_media();
811 });
812
813 // -------------------------------
814 // Meta description per page/post
815 // -------------------------------
816 add_action('add_meta_boxes', function () {
817 add_meta_box(
818 'aibui_meta_description',
819 __('Meta description', 'ai-builder'),
820 function ($post) {
821 $value = get_post_meta($post->ID, 'aibui_meta_description', true);
822 wp_nonce_field('aibui_meta_description_save', 'aibui_meta_description_nonce');
823 echo '<p>' . esc_html__('Add a meta description for this page/post.', 'ai-builder') . '</p>';
824 echo '<textarea style="width:100%;min-height:90px;" id="aibui_meta_description_field" name="aibui_meta_description" maxlength="320">' . esc_textarea($value) . '</textarea>';
825 echo '<p style="margin-top:6px;color:#555;">' . esc_html__('Tip: 140–160 characters for best SEO results.', 'ai-builder') . '</p>';
826 },
827 array('post', 'page'),
828 'normal',
829 'default'
830 );
831 });
832
833 add_action('save_post', function ($post_id) {
834 if (!isset($_POST['aibui_meta_description_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aibui_meta_description_nonce'])), 'aibui_meta_description_save')) {
835 return;
836 }
837 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
838 if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
839 if (!current_user_can('edit_post', $post_id)) return;
840
841 $raw = isset($_POST['aibui_meta_description']) ? wp_unslash($_POST['aibui_meta_description']) : '';
842 $san = trim(wp_strip_all_tags($raw));
843 if (strlen($san) > 320) {
844 $san = mb_substr($san, 0, 320);
845 }
846 if ($san === '') {
847 delete_post_meta($post_id, 'aibui_meta_description');
848 } else {
849 update_post_meta($post_id, 'aibui_meta_description', $san);
850 }
851 });
852
853 add_action('wp_head', function () {
854 if (is_admin()) return;
855 if (!is_singular()) return;
856 $post_id = get_queried_object_id();
857 if (!$post_id) return;
858 $desc = get_post_meta($post_id, 'aibui_meta_description', true);
859 if ($desc) {
860 echo "\n<meta name=\"description\" content=\"" . esc_attr($desc) . "\" />\n";
861 }
862 }, 1);
863
864 // Initialiser le gestionnaire AJAX
865 new AIBUI_Ajax_Handler();
866