PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.10
AI Builder – Generate pages, blocks, images & translate with AI v2.7.10
2.8.0 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 All 123 releases
← All changes | aibui-builder.php +533 -25 2.3.02.7.10 View file →
@@ -1,10 +1,10 @@
1 1 <?php
2 2 /**
3 - * Plugin Name: AI Builder - Generate pages, blocks, text and images with AI
3 + * Plugin Name: AI Builder
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.3.0
6 + * Version: 2.7.10
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.3.0');
20 +define('AIBUI_VERSION', '2.7.10');
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 '';
@@ -160,8 +243,84 @@
160 243 return $combined_url;
161 244 }
162 245
163 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 +/**
164 323 * Check if global AI personalization settings are still empty.
165 324 *
166 325 * We query the remote settings API (same as the Settings page) and consider the
167 326 * configuration "empty" when all settings (primaryColor, secondaryColor, siteName,
@@ -210,46 +369,199 @@
210 369 return ($primaryColor === '' && $secondaryColor === '' && $siteName === '' && $siteDescription === '' && $designStyle === '' && $blockShapes === '' && $copywritingTone === '');
211 370 }
212 371
213 372 /**
214 - * Display a subtle onboarding banner on key AI Builder pages when
215 - * personalization settings are still empty.
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).
216 377 */
217 -function aibui_personalization_notice()
378 +function aibui_render_onboarding_blocks()
218 379 {
219 - if (!is_admin() || !current_user_can('manage_options')) {
380 + if (!is_admin()) {
220 381 return;
221 382 }
222 383
223 - $page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
224 - $target_pages = array(
225 - 'aibui-assistant', // Account (main dashboard)
226 - 'aibui-credits', // Credits
227 - 'aibui-tuto', // Tutorial
228 - 'aibui-multi-page', // Multi Page Generator
229 - 'aibui-translation-settings', // Translations
230 - );
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();
231 386
232 - if (!in_array($page, $target_pages, true)) {
387 + if (!$show_personalization && !$show_first_gen) {
233 388 return;
234 389 }
235 390
236 - if (!aibui_are_personalization_settings_empty()) {
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 + * Pagora AI promo banner.
436 + *
437 + * Shown at the top of every AI Builder admin page (admin.php?page=aibui-*).
438 + * Rendered on admin_notices but WITHOUT the "notice" class so WordPress does
439 + * not relocate it, and dismissible per user (stored in user meta).
440 + */
441 +function aibui_is_plugin_admin_page()
442 +{
443 + if (!is_admin()) {
444 + return false;
445 + }
446 + $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
447 + return $page !== '' && strpos($page, 'aibui-') === 0;
448 +}
449 +
450 +function aibui_render_pagora_promo()
451 +{
452 + if (!aibui_is_plugin_admin_page() || !current_user_can('edit_posts')) {
237 453 return;
238 454 }
455 + if (get_user_meta(get_current_user_id(), 'aibui_pagora_promo_dismissed', true)) {
456 + return;
457 + }
239 458
240 - $settings_url = admin_url('admin.php?page=aibui-settings');
459 + $pagora_url = 'https://wordpress.org/plugins/pagora-ai/';
241 460 ?>
242 - <div class="notice" style="border-left:4px solid #2563eb;padding:12px 16px;margin:12px 0;background:#eff6ff;color:#111827;">
243 - <p style="margin:0;font-size:13px;line-height:1.5;">
244 - <strong>Make AI Builder more personal for your site.</strong>
245 - Configure your site preferences in <a href="<?php echo esc_url($settings_url); ?>" style="font-weight:500;color:#1d4ed8;text-decoration:underline;">AI Builder Settings</a> for better, more personalized results.
461 + <div class="aibui-pagora-promo" role="complementary" aria-label="<?php echo esc_attr__('Pagora AI', 'ai-builder'); ?>">
462 + <span class="aibui-pagora-promo__icon" aria-hidden="true">✦</span>
463 + <p class="aibui-pagora-promo__text">
464 + <?php echo esc_html__('Try our new Pagora AI plugin if you want to create pages outside of Gutenberg!', 'ai-builder'); ?>
465 + <a class="aibui-pagora-promo__link" href="<?php echo esc_url($pagora_url); ?>" target="_blank" rel="noopener noreferrer">
466 + <?php echo esc_html__('Discover Pagora AI', 'ai-builder'); ?>
467 + <span aria-hidden="true">&rarr;</span>
468 + <span class="screen-reader-text"><?php echo esc_html__('(opens in a new tab)', 'ai-builder'); ?></span>
469 + </a>
246 470 </p>
471 + <button type="button" class="aibui-pagora-promo__dismiss" aria-label="<?php echo esc_attr__('Dismiss this message', 'ai-builder'); ?>"
472 + data-nonce="<?php echo esc_attr(wp_create_nonce('aibui_dismiss_pagora_promo')); ?>">&times;</button>
247 473 </div>
474 + <script>
475 + (function () {
476 + var box = document.querySelector('.aibui-pagora-promo');
477 + if (!box) { return; }
478 + var btn = box.querySelector('.aibui-pagora-promo__dismiss');
479 + btn.addEventListener('click', function () {
480 + box.classList.add('is-dismissed');
481 + setTimeout(function () { box.remove(); }, 200);
482 + var body = new URLSearchParams({ action: 'aibui_dismiss_pagora_promo', _ajax_nonce: btn.getAttribute('data-nonce') });
483 + fetch(<?php echo wp_json_encode(admin_url('admin-ajax.php')); ?>, { method: 'POST', credentials: 'same-origin', body: body });
484 + });
485 + })();
486 + </script>
248 487 <?php
249 488 }
250 -add_action('admin_notices', 'aibui_personalization_notice');
489 +add_action('admin_notices', 'aibui_render_pagora_promo');
251 490
491 +add_action('wp_ajax_aibui_dismiss_pagora_promo', function () {
492 + check_ajax_referer('aibui_dismiss_pagora_promo');
493 + if (!current_user_can('edit_posts')) {
494 + wp_send_json_error(null, 403);
495 + }
496 + update_user_meta(get_current_user_id(), 'aibui_pagora_promo_dismissed', 1);
497 + wp_send_json_success();
498 +});
499 +
500 +/**
501 + * Check if the user has never generated AI content.
502 + *
503 + * We query the remote user profile API and check if hasGeneratedAIContent is false.
504 + */
505 +function aibui_user_has_not_generated_content()
506 +{
507 + // Require an authenticated session with the cloud API.
508 + $jwt_token = get_option('aibui_jwt_token', '');
509 + if (empty($jwt_token)) {
510 + return false;
511 + }
512 +
513 + // Use transient to cache the result to avoid excessive API calls
514 + $cache_key = 'aibui_has_generated_content_' . md5($jwt_token);
515 + $cached = get_transient($cache_key);
516 + if ($cached !== false) {
517 + return $cached === 'no';
518 + }
519 +
520 + $api_url = 'https://api.wordpress-ai-builder.com/api/user/profile';
521 + $response = wp_remote_get($api_url, array(
522 + 'timeout' => 10,
523 + 'headers' => array(
524 + 'Authorization' => 'Bearer ' . $jwt_token,
525 + 'Content-Type' => 'application/json',
526 + ),
527 + ));
528 +
529 + if (is_wp_error($response)) {
530 + return false;
531 + }
532 +
533 + $code = wp_remote_retrieve_response_code($response);
534 + if ($code !== 200) {
535 + return false;
536 + }
537 +
538 + $body = wp_remote_retrieve_body($response);
539 + $data = json_decode($body, true);
540 +
541 + if (!is_array($data)) {
542 + return false;
543 + }
544 +
545 + $user = isset($data['user']) ? $data['user'] : null;
546 +
547 + if ($user === null) {
548 + return false;
549 + }
550 +
551 + // Get hasGeneratedAIContent from user object (not from root data)
552 + $hasGeneratedAIContent = isset($user['hasGeneratedAIContent']) ? (bool) $user['hasGeneratedAIContent'] : true;
553 +
554 +
555 + // Cache the result for 10 seconds
556 + set_transient($cache_key, $hasGeneratedAIContent ? 'yes' : 'no', 10);
557 +
558 + return !$hasGeneratedAIContent;
559 +}
560 +
561 +// NOTE: the first-generation onboarding is now rendered via aibui_render_onboarding_blocks()
562 +// inside Account/Credits/Tutorial page templates (not as a global admin notice).
563 +
252 564 // Charger les menus admin
253 565 require_once plugin_dir_path(__FILE__) . 'admin/menu.php';
254 566
255 567 // Charger le gestionnaire AJAX
@@ -257,8 +569,16 @@
257 569
258 570 // Charger le gestionnaire CSS
259 571 require_once plugin_dir_path(__FILE__) . 'includes/class-css-handler.php';
260 572
573 +// Charger le gestionnaire JS
574 +require_once plugin_dir_path(__FILE__) . 'includes/class-js-handler.php';
575 +
576 +// Charger l'injection CSS/JS pour les templates et template parts (FSE).
577 +// Réutilise les post meta existantes (ai_builder_css_content / ai_builder_js_content)
578 +// stockées sur les posts wp_template et wp_template_part.
579 +require_once plugin_dir_path(__FILE__) . 'includes/class-template-assets-renderer.php';
580 +
261 581 // Charger le gestionnaire de traduction
262 582 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-handler.php';
263 583 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
264 584 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
@@ -323,10 +643,23 @@
323 643 'aiBuilderVars',
324 644 array(
325 645 'ajaxurl' => admin_url('admin-ajax.php'),
326 646 'nonce' => wp_create_nonce('aibui_nonce'),
647 + 'adminBaseUrl' => admin_url(),
648 + // Base URL for plugin assets (e.g. chat style preview images)
649 + 'pluginUrl' => plugin_dir_url(__FILE__),
327 650 // Flag to hint we are on the Site Editor (patterns/template parts)
328 651 'isPatternEditor' => ($hook === 'site-editor.php'),
652 + // WooCommerce detection
653 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
654 + // Active theme name
655 + 'activeThemeName' => aibui_get_active_theme_name(),
656 + // Active plugins context (max 15)
657 + 'sitePlugins' => aibui_get_active_plugins_context(),
658 + // Current WordPress version
659 + 'wordpressVersion' => aibui_get_wordpress_version(),
660 + // Shown in the diagnostics line under chat error messages
661 + 'pluginVersion' => AIBUI_VERSION,
329 662 )
330 663 );
331 664
332 665 // Enqueue Multi-Page apply script to support applying generations via URL param
@@ -342,14 +675,27 @@
342 675 'aiBuilderVars',
343 676 array(
344 677 'ajaxurl' => admin_url('admin-ajax.php'),
345 678 'nonce' => wp_create_nonce('aibui_nonce'),
679 + // Keep editor context keys to avoid overriding data needed by chat-widget:
680 + // this localisation is printed last and replaces the object above.
681 + 'adminBaseUrl' => admin_url(),
682 + 'pluginUrl' => plugin_dir_url(__FILE__),
683 + 'pluginVersion' => AIBUI_VERSION,
684 + 'isPatternEditor' => ($hook === 'site-editor.php'),
685 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
686 + // Active theme name
687 + 'activeThemeName' => aibui_get_active_theme_name(),
688 + // Active plugins context (max 15)
689 + 'sitePlugins' => aibui_get_active_plugins_context(),
690 + // Current WordPress version
691 + 'wordpressVersion' => aibui_get_wordpress_version(),
346 692 )
347 693 );
348 694 }
349 695
696 + $current_screen = get_current_screen();
350 697 // Charger les styles et scripts pour la page account du plugin AI Builder
351 - $current_screen = get_current_screen();
352 698 if ($current_screen && strpos($current_screen->id, 'aibui-assistant') !== false) {
353 699 wp_enqueue_style(
354 700 'ai-builder-account-style',
355 701 plugin_dir_url(__FILE__) . 'assets/css/account.css',
@@ -378,8 +724,9 @@
378 724 'ajaxurl' => admin_url('admin-ajax.php'),
379 725 'nonce' => wp_create_nonce('aibui_nonce'),
380 726 'accountUrl' => admin_url('admin.php?page=aibui-account'),
381 727 'siteDomain' => parse_url(home_url(), PHP_URL_HOST),
728 + 'installationId' => aibui_get_installation_id(),
382 729 )
383 730 );
384 731 } else if ($current_screen && strpos($current_screen->id, 'aibui-credits') !== false) {
385 732 wp_enqueue_script(
@@ -520,8 +867,15 @@
520 867 array(
521 868 'ajaxurl' => admin_url('admin-ajax.php'),
522 869 'nonce' => wp_create_nonce('aibui_nonce'),
523 870 'adminBaseUrl' => admin_url(),
871 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
872 + // Active theme name
873 + 'activeThemeName' => aibui_get_active_theme_name(),
874 + // Active plugins context (max 15)
875 + 'sitePlugins' => aibui_get_active_plugins_context(),
876 + // Current WordPress version
877 + 'wordpressVersion' => aibui_get_wordpress_version(),
524 878 )
525 879 );
526 880 } else if ($current_screen && strpos($current_screen->id, 'aibui-agent-chat') !== false) {
527 881 // Agent Chat page scripts and styles
@@ -560,11 +914,51 @@
560 914 )
561 915 );
562 916 }
563 917
918 + // Bandeau de review : uniquement sur les pages admin du plugin
919 + if ($current_screen) {
920 + $screen_id = $current_screen->id;
921 + $is_plugin_screen =
922 + strpos($screen_id, 'aibui-assistant') !== false ||
923 + strpos($screen_id, 'aibui-credits') !== false ||
924 + strpos($screen_id, 'aibui-tuto') !== false ||
925 + strpos($screen_id, 'aibui-multi-page') !== false ||
926 + strpos($screen_id, 'aibui-agent-chat') !== false ||
927 + strpos($screen_id, 'aibui-translation-settings') !== false ||
928 + strpos($screen_id, 'aibui-headers-footers') !== false ||
929 + strpos($screen_id, 'aibui-settings') !== false;
930 +
931 + if ($is_plugin_screen) {
932 + // S'assurer que config.js est chargé
933 + wp_enqueue_script(
934 + 'ai-builder-config',
935 + plugin_dir_url(__FILE__) . 'config.js',
936 + [],
937 + AIBUI_VERSION,
938 + true
939 + );
940 + wp_enqueue_script(
941 + 'ai-builder-review-banner',
942 + plugin_dir_url(__FILE__) . 'assets/js/review-banner.js',
943 + ['ai-builder-config'],
944 + AIBUI_VERSION,
945 + true
946 + );
947 + wp_localize_script(
948 + 'ai-builder-review-banner',
949 + 'aiBuilderReviewVars',
950 + array(
951 + 'ajaxurl' => admin_url('admin-ajax.php'),
952 + 'nonce' => wp_create_nonce('aibui_nonce'),
953 + 'reviewUrl' => 'https://wordpress.org/support/plugin/ai-builder/reviews/#new-post',
954 + )
955 + );
956 + }
957 + }
958 +
564 959 });
565 960
566 -
567 961 add_action('wp_enqueue_scripts', function () {
568 962 // Single combined frontend CSS
569 963 $combined_css = aibui_get_combined_css_url();
570 964 if ($combined_css) {
@@ -732,8 +1126,26 @@
732 1126 filemtime(plugin_dir_path(__FILE__) . 'assets/js/language-switcher-block.js'),
733 1127 true
734 1128 );
735 1129
1130 + // Unregister legacy/unused AI Builder blocks from the inserter
1131 + wp_enqueue_script(
1132 + 'ai-builder-unregister-ai-blocks',
1133 + plugin_dir_url(__FILE__) . 'assets/js/unregister-ai-blocks.js',
1134 + array('ai-builder-blocks', 'wp-blocks'),
1135 + AIBUI_VERSION,
1136 + true
1137 + );
1138 +
1139 + // CSS Class Inspector: button in block sidebar to jump to CSS
1140 + wp_enqueue_script(
1141 + 'ai-builder-css-class-inspector',
1142 + plugin_dir_url(__FILE__) . 'assets/js/css-class-inspector.js',
1143 + array('wp-hooks', 'wp-compose', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'),
1144 + AIBUI_VERSION,
1145 + true
1146 + );
1147 +
736 1148 $translation_settings = AIBUI_Translation_Settings::get_settings();
737 1149 $supported_languages = AIBUI_Translation_Handler::get_supported_languages();
738 1150 $available_langs = isset($translation_settings['available_langs']) && is_array($translation_settings['available_langs'])
739 1151 ? array_values(array_unique($translation_settings['available_langs']))
@@ -976,8 +1388,19 @@
976 1388 array('ai-builder-config'),
977 1389 AIBUI_VERSION,
978 1390 true
979 1391 );
1392 +
1393 + // Localize WooCommerce detection for block editor scripts
1394 + wp_localize_script(
1395 + 'ai-builder-config',
1396 + 'aiBuilderEditorVars',
1397 + array(
1398 + 'wooCommerceInstalled' => aibui_is_woocommerce_installed(),
1399 + // Active theme name
1400 + 'activeThemeName' => aibui_get_active_theme_name(),
1401 + )
1402 + );
980 1403 }
981 1404 add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts');
982 1405
983 1406 add_action('wp_enqueue_scripts', function () {
@@ -1084,4 +1507,89 @@
1084 1507 }, 1);
1085 1508
1086 1509 // Initialiser le gestionnaire AJAX
1087 1510 new AIBUI_Ajax_Handler();
1511 +
1512 +// AJAX: create a header or footer template part for the active block theme
1513 +add_action('wp_ajax_aibui_create_template_part', function () {
1514 + check_ajax_referer('aibui_nonce', 'nonce');
1515 +
1516 + if (!current_user_can('edit_theme_options')) {
1517 + wp_send_json_error('Permission denied');
1518 + }
1519 +
1520 + $area = sanitize_text_field($_POST['area'] ?? '');
1521 + if (!in_array($area, array('header', 'footer'), true)) {
1522 + wp_send_json_error('Invalid area');
1523 + }
1524 +
1525 + $theme_slug = get_stylesheet();
1526 + $title = $area === 'header' ? 'Header' : 'Footer';
1527 +
1528 + $post_id = wp_insert_post(array(
1529 + 'post_title' => $title,
1530 + 'post_name' => $area,
1531 + 'post_content' => '',
1532 + 'post_status' => 'publish',
1533 + 'post_type' => 'wp_template_part',
1534 + ));
1535 +
1536 + if (is_wp_error($post_id)) {
1537 + wp_send_json_error($post_id->get_error_message());
1538 + }
1539 +
1540 + wp_set_object_terms($post_id, $area, 'wp_template_part_area');
1541 + wp_set_object_terms($post_id, $theme_slug, 'wp_theme');
1542 +
1543 + $edit_url = admin_url(
1544 + 'site-editor.php?postType=wp_template_part&postId='
1545 + . urlencode($theme_slug . '//' . $area)
1546 + . '&canvas=edit'
1547 + );
1548 +
1549 + wp_send_json_success(array('edit_url' => $edit_url));
1550 +});
1551 +
1552 +
1553 +// -------------------------------
1554 +// Multi-Page Generator: Cleanup cron and migration
1555 +// -------------------------------
1556 +function aibui_cleanup_old_generations() {
1557 + require_once plugin_dir_path(__FILE__) . 'includes/class-generations-storage.php';
1558 + $storage = new AIBUI_Generations_Storage();
1559 + $deleted_count = $storage->cleanup_old(30); // Delete applied generations older than 30 days
1560 +
1561 +
1562 +}
1563 +add_action('aibui_daily_cleanup', 'aibui_cleanup_old_generations');
1564 +
1565 +// Schedule daily cleanup if not already scheduled
1566 +if (!wp_next_scheduled('aibui_daily_cleanup')) {
1567 + wp_schedule_event(time(), 'daily', 'aibui_daily_cleanup');
1568 +}
1569 +
1570 +// Migrate old wp_options data to files (one-time migration on activation/update)
1571 +function aibui_migrate_generations_to_files() {
1572 + // Check if migration already done
1573 + if (get_option('aibui_generations_migrated_to_files', false)) {
1574 + return;
1575 + }
1576 +
1577 + require_once plugin_dir_path(__FILE__) . 'includes/class-generations-storage.php';
1578 + $storage = new AIBUI_Generations_Storage();
1579 + $migrated_count = $storage->migrate_from_options();
1580 +
1581 + if ($migrated_count > 0) {
1582 + // Mark migration as done
1583 + update_option('aibui_generations_migrated_to_files', true, false);
1584 +
1585 +
1586 + }
1587 +}
1588 +// Run migration on admin init (only once)
1589 +add_action('admin_init', function() {
1590 + static $migration_done = false;
1591 + if (!$migration_done && current_user_can('manage_options')) {
1592 + aibui_migrate_generations_to_files();
1593 + $migration_done = true;
1594 + }
1595 +}, 5);