PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.2
AI Builder – Generate pages, blocks, images & translate with AI v2.7.2
2.7.10 2.7.9 2.7.8 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.10 All 122 releases
← All changes | aibui-builder.php +603 -4 2.2.42.7.2 View file →
@@ -2,9 +2,9 @@
2 2 /**
3 3 * Plugin Name: AI Builder - Generate pages, blocks, text and images with AI
4 4 * Plugin URI: https://website-ai-builder.com/
5 5 * Description: This plugin is used to build your website with AI.
6 - * Version: 2.2.4
6 + * Version: 2.7.2
7 7 * Author: enkic
8 8 * Author URI: https://enkicorbin.fr/
9 9 * License: GPLv2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -16,10 +16,93 @@
16 16 if (!defined('ABSPATH'))
17 17 exit;
18 18
19 19 // Définir la version du plugin
20 -define('AIBUI_VERSION', '2.2.4');
20 +define('AIBUI_VERSION', '2.7.2');
21 21
22 +/**
23 + * Redirect to AI Builder dashboard after plugin activation.
24 + */
25 +function aibui_activation_redirect() {
26 + // Set a transient to trigger redirect on next admin page load
27 + set_transient('aibui_activation_redirect', true, 30);
28 + aibui_get_installation_id();
29 +}
30 +register_activation_hook(__FILE__, 'aibui_activation_redirect');
31 +
32 +/**
33 + * Perform the redirect after plugin activation.
34 + */
35 +function aibui_redirect_after_activation() {
36 + // Check if we should redirect
37 + if (!get_transient('aibui_activation_redirect')) {
38 + return;
39 + }
40 +
41 + // Delete the transient so we don't redirect again
42 + delete_transient('aibui_activation_redirect');
43 +
44 + // Only redirect if user can manage options and it's not a bulk activation
45 + if (!current_user_can('manage_options') || isset($_GET['activate-multi'])) {
46 + return;
47 + }
48 +
49 + // Redirect to AI Builder dashboard
50 + wp_safe_redirect(admin_url('admin.php?page=aibui-assistant'));
51 + exit;
52 +}
53 +add_action('admin_init', 'aibui_redirect_after_activation');
54 +
55 +/**
56 + * Option key: one UUID per WordPress installation (distinct from hostname; avoids localhost collisions).
57 + */
58 +define('AIBUI_INSTALLATION_ID_OPTION', 'aibui_installation_id');
59 +
60 +/**
61 + * UUID v4; uses core helper on WP 6.3+, else random_bytes fallback.
62 + *
63 + * @return string
64 + */
65 +function aibui_generate_uuid4()
66 +{
67 + if (function_exists('wp_generate_uuid4')) {
68 + return wp_generate_uuid4();
69 + }
70 + try {
71 + $data = random_bytes(16);
72 + } catch (Exception $e) {
73 + return sprintf(
74 + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
75 + wp_rand(0, 0xffff),
76 + wp_rand(0, 0xffff),
77 + wp_rand(0, 0xffff),
78 + wp_rand(0, 0x0fff) | 0x4000,
79 + wp_rand(0, 0x3fff) | 0x8000,
80 + wp_rand(0, 0xffff),
81 + wp_rand(0, 0xffff),
82 + wp_rand(0, 0xffff)
83 + );
84 + }
85 + $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
86 + $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
87 + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
88 +}
89 +
90 +/**
91 + * Persistent installation id for this DB; created on first read.
92 + *
93 + * @return string
94 + */
95 +function aibui_get_installation_id()
96 +{
97 + $id = get_option(AIBUI_INSTALLATION_ID_OPTION, '');
98 + if (!is_string($id) || $id === '') {
99 + $id = aibui_generate_uuid4();
100 + update_option(AIBUI_INSTALLATION_ID_OPTION, $id, false);
101 + }
102 + return $id;
103 +}
104 +
22 105 // Simple CSS minifier (safe whitespace/comment removal)
23 106 function aibui_minify_css($css)
24 107 {
25 108 if (!is_string($css) || $css === '') return '';
@@ -159,11 +242,297 @@
159 242
160 243 return $combined_url;
161 244 }
162 245
246 +/**
247 + * Check if WooCommerce is installed and active.
248 + *
249 + * @return bool True if WooCommerce is active, false otherwise.
250 + */
251 +function aibui_is_woocommerce_installed()
252 +{
253 + // Check if WooCommerce class exists (plugin loaded)
254 + if (class_exists('WooCommerce')) {
255 + return true;
256 + }
257 +
258 + // Alternative check: see if the plugin is active
259 + if (function_exists('is_plugin_active')) {
260 + return is_plugin_active('woocommerce/woocommerce.php');
261 + }
262 +
263 + // Fallback: check active plugins option
264 + $active_plugins = get_option('active_plugins', array());
265 + return in_array('woocommerce/woocommerce.php', $active_plugins, true);
266 +}
267 +
268 +/**
269 + * Get the name of the currently active WordPress theme.
270 + *
271 + * @return string Theme name or empty string if not available.
272 + */
273 +function aibui_get_active_theme_name()
274 +{
275 + if (function_exists('wp_get_theme')) {
276 + $theme = wp_get_theme();
277 + return $theme->get('Name');
278 + }
279 + return '';
280 +}
281 +
282 +/**
283 + * Get up to 15 active plugin names for AI context.
284 + *
285 + * @return array<int, string>
286 + */
287 +function aibui_get_active_plugins_context()
288 +{
289 + // Ensure WordPress plugin helpers are available.
290 + if (!function_exists('get_plugins')) {
291 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
292 + }
293 +
294 + $installed_plugins = function_exists('get_plugins') ? get_plugins() : array();
295 + $active_plugins = (array) get_option('active_plugins', array());
296 + $active_plugins = array_slice($active_plugins, 0, 15);
297 +
298 + $plugin_names = array();
299 + foreach ($active_plugins as $plugin_file) {
300 + $plugin_file = (string) $plugin_file;
301 + if (isset($installed_plugins[$plugin_file]['Name']) && $installed_plugins[$plugin_file]['Name'] !== '') {
302 + $plugin_names[] = sanitize_text_field($installed_plugins[$plugin_file]['Name']);
303 + } else {
304 + $plugin_names[] = sanitize_text_field($plugin_file);
305 + }
306 + }
307 +
308 + return array_values(array_slice($plugin_names, 0, 15));
309 +}
310 +
311 +/**
312 + * Get current WordPress version.
313 + *
314 + * @return string
315 + */
316 +function aibui_get_wordpress_version()
317 +{
318 + return (string) get_bloginfo('version');
319 +}
320 +
321 +
322 +/**
323 + * Check if global AI personalization settings are still empty.
324 + *
325 + * We query the remote settings API (same as the Settings page) and consider the
326 + * configuration "empty" when all settings (primaryColor, secondaryColor, siteName,
327 + * siteDescription, designStyle, blockShapes, copywritingTone) are missing/empty.
328 + */
329 +function aibui_are_personalization_settings_empty()
330 +{
331 + // Require an authenticated session with the cloud API.
332 + $jwt_token = get_option('aibui_jwt_token', '');
333 + if (empty($jwt_token)) {
334 + return false;
335 + }
336 +
337 + $api_url = 'https://api.wordpress-ai-builder.com/api/settings';
338 + $response = wp_remote_get($api_url, array(
339 + 'timeout' => 15,
340 + 'headers' => array(
341 + 'Authorization' => 'Bearer ' . $jwt_token,
342 + 'Content-Type' => 'application/json',
343 + ),
344 + ));
345 +
346 + if (is_wp_error($response)) {
347 + return false;
348 + }
349 +
350 + $code = wp_remote_retrieve_response_code($response);
351 + if ($code !== 200) {
352 + return false;
353 + }
354 +
355 + $body = wp_remote_retrieve_body($response);
356 + $data = json_decode($body, true);
357 + if (!is_array($data)) {
358 + return false;
359 + }
360 +
361 + $primaryColor = isset($data['primaryColor']) ? trim((string) $data['primaryColor']) : '';
362 + $secondaryColor = isset($data['secondaryColor']) ? trim((string) $data['secondaryColor']) : '';
363 + $siteName = isset($data['siteName']) ? trim((string) $data['siteName']) : '';
364 + $siteDescription = isset($data['siteDescription']) ? trim((string) $data['siteDescription']) : '';
365 + $designStyle = isset($data['designStyle']) ? trim((string) $data['designStyle']) : '';
366 + $blockShapes = isset($data['blockShapes']) ? trim((string) $data['blockShapes']) : '';
367 + $copywritingTone = isset($data['copywritingTone']) ? trim((string) $data['copywritingTone']) : '';
368 +
369 + return ($primaryColor === '' && $secondaryColor === '' && $siteName === '' && $siteDescription === '' && $designStyle === '' && $blockShapes === '' && $copywritingTone === '');
370 +}
371 +
372 +/**
373 + * Render onboarding content (as in-page blocks, not admin notices).
374 + *
375 + * This is intentionally NOT hooked into admin_notices because we only want it
376 + * to appear inside specific AI Builder pages (Account, Credits, Tutorial).
377 + */
378 +function aibui_render_onboarding_blocks()
379 +{
380 + if (!is_admin()) {
381 + return;
382 + }
383 +
384 + $show_personalization = current_user_can('manage_options') && aibui_are_personalization_settings_empty();
385 + $show_first_gen = current_user_can('edit_posts') && aibui_user_has_not_generated_content();
386 +
387 + if (!$show_personalization && !$show_first_gen) {
388 + return;
389 + }
390 +
391 + $settings_url = admin_url('admin.php?page=aibui-settings');
392 + $new_page_url = admin_url('post-new.php?post_type=page');
393 + ?>
394 + <div class="aibui-onboarding-blocks" role="region" aria-label="<?php echo esc_attr__('AI Builder onboarding', 'ai-builder'); ?>">
395 + <?php if ($show_personalization): ?>
396 + <div class="aibui-onboarding-card aibui-onboarding-card--info">
397 + <div class="aibui-onboarding-card__body">
398 + <div class="aibui-onboarding-card__title">
399 + <?php echo esc_html__('Make AI Builder more personal for your site.', 'ai-builder'); ?>
400 + </div>
401 + <div class="aibui-onboarding-card__text">
402 + <?php echo wp_kses_post(sprintf(
403 + /* translators: %s: AI Builder settings URL */
404 + __('Configure your site preferences in <a href="%s">AI Builder Settings</a> for better, more personalized results.', 'ai-builder'),
405 + esc_url($settings_url)
406 + )); ?>
407 + </div>
408 + </div>
409 + </div>
410 + <?php endif; ?>
411 +
412 + <?php if ($show_first_gen): ?>
413 + <div class="aibui-onboarding-card aibui-onboarding-card--success">
414 + <div class="aibui-onboarding-card__body">
415 + <div class="aibui-onboarding-card__title">
416 + <?php echo esc_html__('Ready to create your first AI-powered content?', 'ai-builder'); ?>
417 + </div>
418 + <div class="aibui-onboarding-card__text">
419 + <?php echo wp_kses_post(__('To generate your first AI content, go to any page or post and use the AI chat widget in the bottom-left corner of the editor. Simply describe what you want, and AI Builder will create it for you!', 'ai-builder')); ?>
420 + </div>
421 + <div class="aibui-onboarding-card__actions">
422 + <a class="aibui-onboarding-card__cta" href="<?php echo esc_url($new_page_url); ?>">
423 + <span aria-hidden="true">✨</span>
424 + <?php echo esc_html__('Create your first Gutenberg page with AI', 'ai-builder'); ?>
425 + </a>
426 + </div>
427 + </div>
428 + </div>
429 + <?php endif; ?>
430 + </div>
431 + <?php
432 +}
433 +
434 +/**
435 + * Check if the user has never generated AI content.
436 + *
437 + * We query the remote user profile API and check if hasGeneratedAIContent is false.
438 + */
439 +function aibui_user_has_not_generated_content()
440 +{
441 + // Require an authenticated session with the cloud API.
442 + $jwt_token = get_option('aibui_jwt_token', '');
443 + if (empty($jwt_token)) {
444 + return false;
445 + }
446 +
447 + // Use transient to cache the result to avoid excessive API calls
448 + $cache_key = 'aibui_has_generated_content_' . md5($jwt_token);
449 + $cached = get_transient($cache_key);
450 + if ($cached !== false) {
451 + return $cached === 'no';
452 + }
453 +
454 + $api_url = 'https://api.wordpress-ai-builder.com/api/user/profile';
455 + $response = wp_remote_get($api_url, array(
456 + 'timeout' => 10,
457 + 'headers' => array(
458 + 'Authorization' => 'Bearer ' . $jwt_token,
459 + 'Content-Type' => 'application/json',
460 + ),
461 + ));
462 +
463 + if (is_wp_error($response)) {
464 + return false;
465 + }
466 +
467 + $code = wp_remote_retrieve_response_code($response);
468 + if ($code !== 200) {
469 + return false;
470 + }
471 +
472 + $body = wp_remote_retrieve_body($response);
473 + $data = json_decode($body, true);
474 +
475 + if (!is_array($data)) {
476 + return false;
477 + }
478 +
479 + $user = isset($data['user']) ? $data['user'] : null;
480 +
481 + if ($user === null) {
482 + return false;
483 + }
484 +
485 + // Get hasGeneratedAIContent from user object (not from root data)
486 + $hasGeneratedAIContent = isset($user['hasGeneratedAIContent']) ? (bool) $user['hasGeneratedAIContent'] : true;
487 +
488 +
489 + // Cache the result for 10 seconds
490 + set_transient($cache_key, $hasGeneratedAIContent ? 'yes' : 'no', 10);
491 +
492 + return !$hasGeneratedAIContent;
493 +}
494 +
495 +// NOTE: the first-generation onboarding is now rendered via aibui_render_onboarding_blocks()
496 +// inside Account/Credits/Tutorial page templates (not as a global admin notice).
497 +
163 498 // Charger les menus admin
164 499 require_once plugin_dir_path(__FILE__) . 'admin/menu.php';
165 500
501 +// DEBUG heartbeats (à retirer) pour détecter un éventuel fatal silencieux
502 +// lors de l'inclusion du service des templates.
503 +@file_put_contents(
504 + plugin_dir_path(__FILE__) . 'aibui-debug.log',
505 + '[' . date('Y-m-d H:i:s') . '] aibui-builder.php REACHED line ' . __LINE__ . "\n",
506 + FILE_APPEND
507 +);
508 +
509 +add_action('wp_head', function () {
510 + echo "\n<!-- ai-builder heartbeat :: BEFORE_TEMPLATE_ASSETS_REQUIRE -->\n";
511 +}, 1);
512 +
513 +// Charger le service de gestion du CSS/JS des templates et template parts.
514 +// IMPORTANT : doit être chargé AVANT class-ajax-handler.php car celui-ci y fait
515 +// appel pour router les sauvegardes provenant du Site Editor.
516 +$aibui_template_assets_file = plugin_dir_path(__FILE__) . 'includes/class-template-assets.php';
517 +if (function_exists('opcache_invalidate')) {
518 + @opcache_invalidate($aibui_template_assets_file, true);
519 +}
520 +@clearstatcache(true, $aibui_template_assets_file);
521 +require_once $aibui_template_assets_file;
522 +
523 +@file_put_contents(
524 + plugin_dir_path(__FILE__) . 'aibui-debug.log',
525 + '[' . date('Y-m-d H:i:s') . '] after require class_exists=' . (class_exists('AIBUI_Template_Assets') ? '1' : '0') . "\n",
526 + FILE_APPEND
527 +);
528 +
529 +add_action('wp_head', function () {
530 + $class_exists = class_exists('AIBUI_Template_Assets') ? 1 : 0;
531 + $file_exists = file_exists(plugin_dir_path(__FILE__) . 'includes/class-template-assets.php') ? 1 : 0;
532 + echo "\n<!-- ai-builder heartbeat :: AFTER_TEMPLATE_ASSETS_REQUIRE class_exists=" . $class_exists . " file_exists=" . $file_exists . " -->\n";
533 +}, 1);
534 +
166 535 // Charger le gestionnaire AJAX
167 536 require_once plugin_dir_path(__FILE__) . 'includes/class-ajax-handler.php';
168 537
169 538 // Charger le gestionnaire CSS
@@ -168,8 +537,11 @@
168 537
169 538 // Charger le gestionnaire CSS
170 539 require_once plugin_dir_path(__FILE__) . 'includes/class-css-handler.php';
171 540
541 +// Charger le gestionnaire JS
542 +require_once plugin_dir_path(__FILE__) . 'includes/class-js-handler.php';
543 +
172 544 // Charger le gestionnaire de traduction
173 545 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-handler.php';
174 546 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
175 547 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
@@ -174,8 +546,14 @@
174 546 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
175 547 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
176 548 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-switcher.php';
177 549
550 +// Charger les services de l'Agent Chat
551 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-discovery-service.php';
552 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-security-service.php';
553 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-execution-service.php';
554 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-chat-handler.php';
555 +
178 556 // Initialiser le gestionnaire de traduction
179 557 new AIBUI_Translation_Handler();
180 558 AIBUI_Translation_Settings::init();
181 559 $translation_manager = new AIBUI_Translation_Manager();
@@ -180,8 +558,11 @@
180 558 AIBUI_Translation_Settings::init();
181 559 $translation_manager = new AIBUI_Translation_Manager();
182 560 $translation_switcher = new AIBUI_Translation_Switcher($translation_manager);
183 561
562 +// Initialiser le gestionnaire de l'Agent Chat
563 +new AIBUI_Agent_Chat_Handler();
564 +
184 565 add_action('admin_enqueue_scripts', function ($hook) {
185 566 // Charger le CSS admin sur toutes les pages d'administration
186 567 wp_enqueue_style(
187 568 'ai-builder-admin-style',
@@ -225,10 +606,21 @@
225 606 'aiBuilderVars',
226 607 array(
227 608 'ajaxurl' => admin_url('admin-ajax.php'),
228 609 'nonce' => wp_create_nonce('aibui_nonce'),
610 + 'adminBaseUrl' => admin_url(),
611 + // Base URL for plugin assets (e.g. chat style preview images)
612 + 'pluginUrl' => plugin_dir_url(__FILE__),
229 613 // Flag to hint we are on the Site Editor (patterns/template parts)
230 614 'isPatternEditor' => ($hook === 'site-editor.php'),
615 + // WooCommerce detection
616 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
617 + // Active theme name
618 + 'activeThemeName' => aibui_get_active_theme_name(),
619 + // Active plugins context (max 15)
620 + 'sitePlugins' => aibui_get_active_plugins_context(),
621 + // Current WordPress version
622 + 'wordpressVersion' => aibui_get_wordpress_version(),
231 623 )
232 624 );
233 625
234 626 // Enqueue Multi-Page apply script to support applying generations via URL param
@@ -244,14 +636,23 @@
244 636 'aiBuilderVars',
245 637 array(
246 638 'ajaxurl' => admin_url('admin-ajax.php'),
247 639 'nonce' => wp_create_nonce('aibui_nonce'),
640 + // Keep editor context keys to avoid overriding data needed by chat-widget.
641 + 'isPatternEditor' => ($hook === 'site-editor.php'),
642 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
643 + // Active theme name
644 + 'activeThemeName' => aibui_get_active_theme_name(),
645 + // Active plugins context (max 15)
646 + 'sitePlugins' => aibui_get_active_plugins_context(),
647 + // Current WordPress version
648 + 'wordpressVersion' => aibui_get_wordpress_version(),
248 649 )
249 650 );
250 651 }
251 652
653 + $current_screen = get_current_screen();
252 654 // Charger les styles et scripts pour la page account du plugin AI Builder
253 - $current_screen = get_current_screen();
254 655 if ($current_screen && strpos($current_screen->id, 'aibui-assistant') !== false) {
255 656 wp_enqueue_style(
256 657 'ai-builder-account-style',
257 658 plugin_dir_url(__FILE__) . 'assets/css/account.css',
@@ -280,8 +681,9 @@
280 681 'ajaxurl' => admin_url('admin-ajax.php'),
281 682 'nonce' => wp_create_nonce('aibui_nonce'),
282 683 'accountUrl' => admin_url('admin.php?page=aibui-account'),
283 684 'siteDomain' => parse_url(home_url(), PHP_URL_HOST),
685 + 'installationId' => aibui_get_installation_id(),
284 686 )
285 687 );
286 688 } else if ($current_screen && strpos($current_screen->id, 'aibui-credits') !== false) {
287 689 wp_enqueue_script(
@@ -422,15 +824,98 @@
422 824 array(
423 825 'ajaxurl' => admin_url('admin-ajax.php'),
424 826 'nonce' => wp_create_nonce('aibui_nonce'),
425 827 'adminBaseUrl' => admin_url(),
828 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
829 + // Active theme name
830 + 'activeThemeName' => aibui_get_active_theme_name(),
831 + // Active plugins context (max 15)
832 + 'sitePlugins' => aibui_get_active_plugins_context(),
833 + // Current WordPress version
834 + 'wordpressVersion' => aibui_get_wordpress_version(),
426 835 )
427 836 );
837 + } else if ($current_screen && strpos($current_screen->id, 'aibui-agent-chat') !== false) {
838 + // Agent Chat page scripts and styles
839 + wp_enqueue_script(
840 + 'ai-builder-config',
841 + plugin_dir_url(__FILE__) . 'config.js',
842 + [],
843 + AIBUI_VERSION,
844 + true
845 + );
846 + wp_enqueue_script(
847 + 'ai-builder-agent-chat',
848 + plugin_dir_url(__FILE__) . 'assets/js/agent-chat.js',
849 + ['ai-builder-config'],
850 + AIBUI_VERSION,
851 + true
852 + );
853 + // Localize with nonce - IMPORTANT: use a different nonce for agent actions
854 + wp_localize_script(
855 + 'ai-builder-agent-chat',
856 + 'aibuiAgentVars',
857 + array(
858 + 'ajaxurl' => admin_url('admin-ajax.php'),
859 + 'nonce' => wp_create_nonce('aibui_agent_nonce'),
860 + 'restBase' => esc_url_raw(rest_url()),
861 + 'wpRestDocsBase' => 'https://developer.wordpress.org/rest-api/reference/',
862 + )
863 + );
864 + // Also expose standard AJAX nonce for shared endpoints like aibui_get_token
865 + wp_localize_script(
866 + 'ai-builder-agent-chat',
867 + 'aiBuilderVars',
868 + array(
869 + 'ajaxurl' => admin_url('admin-ajax.php'),
870 + 'nonce' => wp_create_nonce('aibui_nonce'),
871 + )
872 + );
428 873 }
429 874
875 + // Bandeau de review : uniquement sur les pages admin du plugin
876 + if ($current_screen) {
877 + $screen_id = $current_screen->id;
878 + $is_plugin_screen =
879 + strpos($screen_id, 'aibui-assistant') !== false ||
880 + strpos($screen_id, 'aibui-credits') !== false ||
881 + strpos($screen_id, 'aibui-tuto') !== false ||
882 + strpos($screen_id, 'aibui-multi-page') !== false ||
883 + strpos($screen_id, 'aibui-agent-chat') !== false ||
884 + strpos($screen_id, 'aibui-translation-settings') !== false ||
885 + strpos($screen_id, 'aibui-headers-footers') !== false ||
886 + strpos($screen_id, 'aibui-settings') !== false;
887 +
888 + if ($is_plugin_screen) {
889 + // S'assurer que config.js est chargé
890 + wp_enqueue_script(
891 + 'ai-builder-config',
892 + plugin_dir_url(__FILE__) . 'config.js',
893 + [],
894 + AIBUI_VERSION,
895 + true
896 + );
897 + wp_enqueue_script(
898 + 'ai-builder-review-banner',
899 + plugin_dir_url(__FILE__) . 'assets/js/review-banner.js',
900 + ['ai-builder-config'],
901 + AIBUI_VERSION,
902 + true
903 + );
904 + wp_localize_script(
905 + 'ai-builder-review-banner',
906 + 'aiBuilderReviewVars',
907 + array(
908 + 'ajaxurl' => admin_url('admin-ajax.php'),
909 + 'nonce' => wp_create_nonce('aibui_nonce'),
910 + 'reviewUrl' => 'https://wordpress.org/support/plugin/ai-builder/reviews/#new-post',
911 + )
912 + );
913 + }
914 + }
915 +
430 916 });
431 917
432 -
433 918 add_action('wp_enqueue_scripts', function () {
434 919 // Single combined frontend CSS
435 920 $combined_css = aibui_get_combined_css_url();
436 921 if ($combined_css) {
@@ -598,8 +1083,26 @@
598 1083 filemtime(plugin_dir_path(__FILE__) . 'assets/js/language-switcher-block.js'),
599 1084 true
600 1085 );
601 1086
1087 + // Unregister legacy/unused AI Builder blocks from the inserter
1088 + wp_enqueue_script(
1089 + 'ai-builder-unregister-ai-blocks',
1090 + plugin_dir_url(__FILE__) . 'assets/js/unregister-ai-blocks.js',
1091 + array('ai-builder-blocks', 'wp-blocks'),
1092 + AIBUI_VERSION,
1093 + true
1094 + );
1095 +
1096 + // CSS Class Inspector: button in block sidebar to jump to CSS
1097 + wp_enqueue_script(
1098 + 'ai-builder-css-class-inspector',
1099 + plugin_dir_url(__FILE__) . 'assets/js/css-class-inspector.js',
1100 + array('wp-hooks', 'wp-compose', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'),
1101 + AIBUI_VERSION,
1102 + true
1103 + );
1104 +
602 1105 $translation_settings = AIBUI_Translation_Settings::get_settings();
603 1106 $supported_languages = AIBUI_Translation_Handler::get_supported_languages();
604 1107 $available_langs = isset($translation_settings['available_langs']) && is_array($translation_settings['available_langs'])
605 1108 ? array_values(array_unique($translation_settings['available_langs']))
@@ -842,8 +1345,19 @@
842 1345 array('ai-builder-config'),
843 1346 AIBUI_VERSION,
844 1347 true
845 1348 );
1349 +
1350 + // Localize WooCommerce detection for block editor scripts
1351 + wp_localize_script(
1352 + 'ai-builder-config',
1353 + 'aiBuilderEditorVars',
1354 + array(
1355 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
1356 + // Active theme name
1357 + 'activeThemeName' => aibui_get_active_theme_name(),
1358 + )
1359 + );
846 1360 }
847 1361 add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts');
848 1362
849 1363 add_action('wp_enqueue_scripts', function () {
@@ -950,4 +1464,89 @@
950 1464 }, 1);
951 1465
952 1466 // Initialiser le gestionnaire AJAX
953 1467 new AIBUI_Ajax_Handler();
1468 +
1469 +// AJAX: create a header or footer template part for the active block theme
1470 +add_action('wp_ajax_aibui_create_template_part', function () {
1471 + check_ajax_referer('aibui_nonce', 'nonce');
1472 +
1473 + if (!current_user_can('edit_theme_options')) {
1474 + wp_send_json_error('Permission denied');
1475 + }
1476 +
1477 + $area = sanitize_text_field($_POST['area'] ?? '');
1478 + if (!in_array($area, array('header', 'footer'), true)) {
1479 + wp_send_json_error('Invalid area');
1480 + }
1481 +
1482 + $theme_slug = get_stylesheet();
1483 + $title = $area === 'header' ? 'Header' : 'Footer';
1484 +
1485 + $post_id = wp_insert_post(array(
1486 + 'post_title' => $title,
1487 + 'post_name' => $area,
1488 + 'post_content' => '',
1489 + 'post_status' => 'publish',
1490 + 'post_type' => 'wp_template_part',
1491 + ));
1492 +
1493 + if (is_wp_error($post_id)) {
1494 + wp_send_json_error($post_id->get_error_message());
1495 + }
1496 +
1497 + wp_set_object_terms($post_id, $area, 'wp_template_part_area');
1498 + wp_set_object_terms($post_id, $theme_slug, 'wp_theme');
1499 +
1500 + $edit_url = admin_url(
1501 + 'site-editor.php?postType=wp_template_part&postId='
1502 + . urlencode($theme_slug . '//' . $area)
1503 + . '&canvas=edit'
1504 + );
1505 +
1506 + wp_send_json_success(array('edit_url' => $edit_url));
1507 +});
1508 +
1509 +
1510 +// -------------------------------
1511 +// Multi-Page Generator: Cleanup cron and migration
1512 +// -------------------------------
1513 +function aibui_cleanup_old_generations() {
1514 + require_once plugin_dir_path(__FILE__) . 'includes/class-generations-storage.php';
1515 + $storage = new AIBUI_Generations_Storage();
1516 + $deleted_count = $storage->cleanup_old(30); // Delete applied generations older than 30 days
1517 +
1518 +
1519 +}
1520 +add_action('aibui_daily_cleanup', 'aibui_cleanup_old_generations');
1521 +
1522 +// Schedule daily cleanup if not already scheduled
1523 +if (!wp_next_scheduled('aibui_daily_cleanup')) {
1524 + wp_schedule_event(time(), 'daily', 'aibui_daily_cleanup');
1525 +}
1526 +
1527 +// Migrate old wp_options data to files (one-time migration on activation/update)
1528 +function aibui_migrate_generations_to_files() {
1529 + // Check if migration already done
1530 + if (get_option('aibui_generations_migrated_to_files', false)) {
1531 + return;
1532 + }
1533 +
1534 + require_once plugin_dir_path(__FILE__) . 'includes/class-generations-storage.php';
1535 + $storage = new AIBUI_Generations_Storage();
1536 + $migrated_count = $storage->migrate_from_options();
1537 +
1538 + if ($migrated_count > 0) {
1539 + // Mark migration as done
1540 + update_option('aibui_generations_migrated_to_files', true, false);
1541 +
1542 +
1543 + }
1544 +}
1545 +// Run migration on admin init (only once)
1546 +add_action('admin_init', function() {
1547 + static $migration_done = false;
1548 + if (!$migration_done && current_user_can('manage_options')) {
1549 + aibui_migrate_generations_to_files();
1550 + $migration_done = true;
1551 + }
1552 +}, 5);