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

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