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.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 +647 -5 2.2.12.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.2.1
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.2.1');
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 '';
@@ -159,8 +242,326 @@
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 + * 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')) {
453 + return;
454 + }
455 + if (get_user_meta(get_current_user_id(), 'aibui_pagora_promo_dismissed', true)) {
456 + return;
457 + }
458 +
459 + $pagora_url = 'https://wordpress.org/plugins/pagora-ai/';
460 + ?>
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>
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>
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>
487 + <?php
488 +}
489 +add_action('admin_notices', 'aibui_render_pagora_promo');
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 +
163 564 // Charger les menus admin
164 565 require_once plugin_dir_path(__FILE__) . 'admin/menu.php';
165 566
166 567 // Charger le gestionnaire AJAX
@@ -168,8 +569,16 @@
168 569
169 570 // Charger le gestionnaire CSS
170 571 require_once plugin_dir_path(__FILE__) . 'includes/class-css-handler.php';
171 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 +
172 581 // Charger le gestionnaire de traduction
173 582 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-handler.php';
174 583 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
175 584 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
@@ -174,8 +583,14 @@
174 583 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-settings.php';
175 584 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-manager.php';
176 585 require_once plugin_dir_path(__FILE__) . 'includes/class-translation-switcher.php';
177 586
587 +// Charger les services de l'Agent Chat
588 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-discovery-service.php';
589 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-security-service.php';
590 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-execution-service.php';
591 +require_once plugin_dir_path(__FILE__) . 'includes/class-agent-chat-handler.php';
592 +
178 593 // Initialiser le gestionnaire de traduction
179 594 new AIBUI_Translation_Handler();
180 595 AIBUI_Translation_Settings::init();
181 596 $translation_manager = new AIBUI_Translation_Manager();
@@ -180,8 +595,11 @@
180 595 AIBUI_Translation_Settings::init();
181 596 $translation_manager = new AIBUI_Translation_Manager();
182 597 $translation_switcher = new AIBUI_Translation_Switcher($translation_manager);
183 598
599 +// Initialiser le gestionnaire de l'Agent Chat
600 +new AIBUI_Agent_Chat_Handler();
601 +
184 602 add_action('admin_enqueue_scripts', function ($hook) {
185 603 // Charger le CSS admin sur toutes les pages d'administration
186 604 wp_enqueue_style(
187 605 'ai-builder-admin-style',
@@ -225,10 +643,23 @@
225 643 'aiBuilderVars',
226 644 array(
227 645 'ajaxurl' => admin_url('admin-ajax.php'),
228 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__),
229 650 // Flag to hint we are on the Site Editor (patterns/template parts)
230 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,
231 662 )
232 663 );
233 664
234 665 // Enqueue Multi-Page apply script to support applying generations via URL param
@@ -244,14 +675,27 @@
244 675 'aiBuilderVars',
245 676 array(
246 677 'ajaxurl' => admin_url('admin-ajax.php'),
247 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(),
248 692 )
249 693 );
250 694 }
251 695
696 + $current_screen = get_current_screen();
252 697 // Charger les styles et scripts pour la page account du plugin AI Builder
253 - $current_screen = get_current_screen();
254 698 if ($current_screen && strpos($current_screen->id, 'aibui-assistant') !== false) {
255 699 wp_enqueue_style(
256 700 'ai-builder-account-style',
257 701 plugin_dir_url(__FILE__) . 'assets/css/account.css',
@@ -280,8 +724,9 @@
280 724 'ajaxurl' => admin_url('admin-ajax.php'),
281 725 'nonce' => wp_create_nonce('aibui_nonce'),
282 726 'accountUrl' => admin_url('admin.php?page=aibui-account'),
283 727 'siteDomain' => parse_url(home_url(), PHP_URL_HOST),
728 + 'installationId' => aibui_get_installation_id(),
284 729 )
285 730 );
286 731 } else if ($current_screen && strpos($current_screen->id, 'aibui-credits') !== false) {
287 732 wp_enqueue_script(
@@ -422,15 +867,98 @@
422 867 array(
423 868 'ajaxurl' => admin_url('admin-ajax.php'),
424 869 'nonce' => wp_create_nonce('aibui_nonce'),
425 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(),
426 878 )
427 879 );
880 + } else if ($current_screen && strpos($current_screen->id, 'aibui-agent-chat') !== false) {
881 + // Agent Chat page scripts and styles
882 + wp_enqueue_script(
883 + 'ai-builder-config',
884 + plugin_dir_url(__FILE__) . 'config.js',
885 + [],
886 + AIBUI_VERSION,
887 + true
888 + );
889 + wp_enqueue_script(
890 + 'ai-builder-agent-chat',
891 + plugin_dir_url(__FILE__) . 'assets/js/agent-chat.js',
892 + ['ai-builder-config'],
893 + AIBUI_VERSION,
894 + true
895 + );
896 + // Localize with nonce - IMPORTANT: use a different nonce for agent actions
897 + wp_localize_script(
898 + 'ai-builder-agent-chat',
899 + 'aibuiAgentVars',
900 + array(
901 + 'ajaxurl' => admin_url('admin-ajax.php'),
902 + 'nonce' => wp_create_nonce('aibui_agent_nonce'),
903 + 'restBase' => esc_url_raw(rest_url()),
904 + 'wpRestDocsBase' => 'https://developer.wordpress.org/rest-api/reference/',
905 + )
906 + );
907 + // Also expose standard AJAX nonce for shared endpoints like aibui_get_token
908 + wp_localize_script(
909 + 'ai-builder-agent-chat',
910 + 'aiBuilderVars',
911 + array(
912 + 'ajaxurl' => admin_url('admin-ajax.php'),
913 + 'nonce' => wp_create_nonce('aibui_nonce'),
914 + )
915 + );
428 916 }
429 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 +
430 959 });
431 960
432 -
433 961 add_action('wp_enqueue_scripts', function () {
434 962 // Single combined frontend CSS
435 963 $combined_css = aibui_get_combined_css_url();
436 964 if ($combined_css) {
@@ -598,8 +1126,26 @@
598 1126 filemtime(plugin_dir_path(__FILE__) . 'assets/js/language-switcher-block.js'),
599 1127 true
600 1128 );
601 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 +
602 1148 $translation_settings = AIBUI_Translation_Settings::get_settings();
603 1149 $supported_languages = AIBUI_Translation_Handler::get_supported_languages();
604 1150 $available_langs = isset($translation_settings['available_langs']) && is_array($translation_settings['available_langs'])
605 1151 ? array_values(array_unique($translation_settings['available_langs']))
@@ -842,8 +1388,19 @@
842 1388 array('ai-builder-config'),
843 1389 AIBUI_VERSION,
844 1390 true
845 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 + );
846 1403 }
847 1404 add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts');
848 1405
849 1406 add_action('wp_enqueue_scripts', function () {
@@ -950,4 +1507,89 @@
950 1507 }, 1);
951 1508
952 1509 // Initialiser le gestionnaire AJAX
953 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);