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

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