\(\)])\s*/', '$1', $css); // Final trims and unnecessary semicolons $css = str_replace(';}', '}', $css); return trim($css); } // Build or fetch a combined CSS file for plugin assets/css/*.css function aibui_get_combined_css_url() { $css_dir = plugin_dir_path(__FILE__) . 'assets/css/'; $css_url_base = plugin_dir_url(__FILE__) . 'assets/css/'; // If directory missing, bail to original behavior if (!is_dir($css_dir)) return ''; $files = glob($css_dir . '*.css'); if (!$files) return ''; // Compute a hash based on file mtimes and paths to invalidate cache when any source changes $sig_parts = []; foreach ($files as $path) { $sig_parts[] = basename($path) . ':' . filemtime($path); } $signature = md5(implode('|', $sig_parts)); // Store in uploads to keep plugin dir clean and writable $uploads = wp_upload_dir(); if (!empty($uploads['error'])) { return ''; } $cache_dir = rtrim($uploads['basedir'], '/').'/ai-builder-cache'; $cache_url = rtrim($uploads['baseurl'], '/').'/ai-builder-cache'; if (!is_dir($cache_dir)) { wp_mkdir_p($cache_dir); } $combined_filename = 'combined-'.$signature.'.css'; $combined_path = $cache_dir.'/'.$combined_filename; $combined_url = $cache_url.'/'.$combined_filename; if (!file_exists($combined_path)) { $buffer = ''; // Keep a stable order: alphabetical by filename sort($files, SORT_STRING); foreach ($files as $path) { // Skip admin-only stylesheet to avoid leaking to frontend bundle if (basename($path) === 'style-admin.css') continue; $content = file_get_contents($path); if ($content === false) continue; $buffer .= "\n/* ".basename($path)." */\n".$content; } $minified = aibui_minify_css($buffer); // Graceful write if (is_writable($cache_dir)) { file_put_contents($combined_path, $minified); } else { return ''; } } return $combined_url; } // Simple JS minifier (very conservative) function aibui_minify_js($js) { if (!is_string($js) || $js === '') return ''; // Remove block comments but keep /*! license comments */ $js = preg_replace('#/(?!\!)(\*[^*]*\*+(?:[^/*][^*]*\*+)*/)#', '', $js); // Remove line comments $js = preg_replace('#(^|\s)//.*$#m', '$1', $js); // Collapse whitespace $js = preg_replace('/\s+/', ' ', $js); return trim($js); } // Build a combined frontend JS bundle from selected plugin scripts function aibui_get_combined_js_url() { $js_list = array( 'assets/js/carousel-frontend.js', 'assets/js/map-frontend.js', 'assets/js/tabs-frontend.js', 'assets/js/table-frontend.js', 'assets/js/stats-tooltips.js', 'assets/js/snackbar-frontend.js', ); $sig_parts = array(); $contents = ''; foreach ($js_list as $rel) { $path = plugin_dir_path(__FILE__) . $rel; if (!file_exists($path)) continue; $sig_parts[] = $rel . ':' . filemtime($path); } if (empty($sig_parts)) return ''; $signature = md5(implode('|', $sig_parts)); $uploads = wp_upload_dir(); if (!empty($uploads['error'])) { return ''; } $cache_dir = rtrim($uploads['basedir'], '/').'/ai-builder-cache'; $cache_url = rtrim($uploads['baseurl'], '/').'/ai-builder-cache'; if (!is_dir($cache_dir)) { wp_mkdir_p($cache_dir); } $combined_filename = 'frontend-'.$signature.'.js'; $combined_path = $cache_dir.'/'.$combined_filename; $combined_url = $cache_url.'/'.$combined_filename; if (!file_exists($combined_path)) { $buffer = ''; foreach ($js_list as $rel) { $path = plugin_dir_path(__FILE__) . $rel; if (!file_exists($path)) continue; $content = file_get_contents($path); if ($content === false) continue; $buffer .= "\n/* ".$rel." */\n".$content."\n"; } $minified = aibui_minify_js($buffer); if (is_writable($cache_dir)) { file_put_contents($combined_path, $minified); } else { return ''; } } return $combined_url; } // Charger les menus admin require_once plugin_dir_path(__FILE__) . 'admin/menu.php'; // Charger le gestionnaire AJAX require_once plugin_dir_path(__FILE__) . 'includes/class-ajax-handler.php'; // Charger le gestionnaire CSS require_once plugin_dir_path(__FILE__) . 'includes/class-css-handler.php'; add_action('admin_enqueue_scripts', function ($hook) { // Charger le CSS admin sur toutes les pages d'administration wp_enqueue_style( 'ai-builder-admin-style', plugin_dir_url(__FILE__) . 'assets/css/style-admin.css', [], AIBUI_VERSION ); // Charger sur l'éditeur de page/article et l'éditeur de modèles (site editor) if ($hook === 'post.php' || $hook === 'post-new.php' || $hook === 'site-editor.php') { wp_enqueue_style( 'chat-widget-style', plugin_dir_url(__FILE__) . 'assets/css/chat-widget.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); wp_enqueue_script( 'chat-widget', plugin_dir_url(__FILE__) . 'assets/js/chat-widget.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Styles tabs pour s'assurer du chargement dans l'éditeur wp_enqueue_style( 'ai-builder-tabs-css-admin-editor', plugin_dir_url(__FILE__) . 'assets/css/tabs.css', [], AIBUI_VERSION ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'chat-widget', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), // Flag to hint we are on the Site Editor (patterns/template parts) 'isPatternEditor' => ($hook === 'site-editor.php'), ) ); // Enqueue Multi-Page apply script to support applying generations via URL param wp_enqueue_script( 'ai-builder-multi-page-apply', plugin_dir_url(__FILE__) . 'assets/js/multi-page-apply.js', ['ai-builder-config', 'chat-widget', 'wp-blocks', 'wp-data', 'wp-editor', 'wp-block-editor', 'wp-element'], AIBUI_VERSION, true ); wp_localize_script( 'ai-builder-multi-page-apply', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), ) ); } // Charger les styles et scripts pour la page account du plugin AI Builder $current_screen = get_current_screen(); if ($current_screen && strpos($current_screen->id, 'aibui-assistant') !== false) { wp_enqueue_style( 'ai-builder-account-style', plugin_dir_url(__FILE__) . 'assets/css/account.css', [], filemtime(plugin_dir_path(__FILE__) . 'assets/css/account.css') ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); wp_enqueue_script( 'ai-builder-account', plugin_dir_url(__FILE__) . 'assets/js/account.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'ai-builder-account', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), 'accountUrl' => admin_url('admin.php?page=aibui-account'), 'siteDomain' => parse_url(home_url(), PHP_URL_HOST), ) ); } else if ($current_screen && strpos($current_screen->id, 'aibui-credits') !== false) { wp_enqueue_script( 'ai-builder-credits', plugin_dir_url(__FILE__) . 'assets/js/credits.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'ai-builder-credits', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), ) ); wp_enqueue_style( 'ai-builder-credits-additional-style', plugin_dir_url(__FILE__) . 'assets/css/credits-additional.css', [], AIBUI_VERSION ); wp_enqueue_style( 'ai-builder-credits-style', plugin_dir_url(__FILE__) . 'assets/css/credits.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); } else if ($current_screen && strpos($current_screen->id, 'aibui-settings') !== false) { wp_enqueue_style( 'ai-builder-settings-style', plugin_dir_url(__FILE__) . 'assets/css/settings.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); wp_enqueue_script( 'ai-builder-settings', plugin_dir_url(__FILE__) . 'assets/js/settings.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'ai-builder-settings', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), ) ); } else if ($current_screen && strpos($current_screen->id, 'aibui-reset-password') !== false) { wp_enqueue_style( 'ai-builder-reset-password-style', plugin_dir_url(__FILE__) . 'assets/css/reset-password.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); wp_enqueue_script( 'ai-builder-reset-password', plugin_dir_url(__FILE__) . 'assets/js/reset-password.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'ai-builder-reset-password', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), 'accountUrl' => admin_url('admin.php?page=aibui-account'), ) ); } else if ($current_screen && strpos($current_screen->id, 'aibui-tuto') !== false) { wp_enqueue_style( 'ai-builder-tutorial-style', plugin_dir_url(__FILE__) . 'assets/css/tutorial.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); } else if ($current_screen && strpos($current_screen->id, 'aibui-multi-page') !== false) { wp_enqueue_style( 'ai-builder-multi-page-style', plugin_dir_url(__FILE__) . 'assets/css/multi-page.css', [], AIBUI_VERSION ); wp_enqueue_script( 'ai-builder-config', plugin_dir_url(__FILE__) . 'config.js', [], AIBUI_VERSION, true ); wp_enqueue_script( 'ai-builder-multi-page', plugin_dir_url(__FILE__) . 'assets/js/multi-page.js', ['ai-builder-config'], AIBUI_VERSION, true ); // Injection des variables JS pour AJAX et le nonce wp_localize_script( 'ai-builder-multi-page', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), 'adminBaseUrl' => admin_url(), ) ); } }); add_action('wp_enqueue_scripts', function () { // Single combined frontend CSS $combined_css = aibui_get_combined_css_url(); if ($combined_css) { wp_enqueue_style( 'ai-builder-combined', $combined_css, [], null ); } else { // Fallback: enqueue at least the essential stylesheet wp_enqueue_style( 'aibui-force-alignfull', plugin_dir_url(__FILE__) . 'assets/css/force-align-full.css', [], AIBUI_VERSION ); } // Charger les styles CSS du bloc AI Image côté frontend wp_enqueue_style( 'ai-builder-ai-image-frontend', plugin_dir_url(__FILE__) . 'assets/js/build/index.css', [], filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.css') ); // Frontend JS single bundle $combined_js = aibui_get_combined_js_url(); if ($combined_js) { wp_enqueue_script( 'ai-builder-frontend-bundle', $combined_js, [], null, true ); } else { // Fallback: at least enqueue carousel script if bundling failed wp_enqueue_script( 'ai-builder-carousel-frontend', plugin_dir_url(__FILE__) . 'assets/js/carousel-frontend.js', [], AIBUI_VERSION, true ); } // Load fixed background group block CSS for frontend wp_enqueue_style( 'ai-builder-fixed-bg-group-frontend', plugin_dir_url(__FILE__) . 'assets/js/src/editor-blocks/fixed-bg-group/style.css', [], AIBUI_VERSION ); // CSS above is covered by the combined bundle // Other JS are included in the combined bundle above // CSS above is covered by the combined bundle // Included in combined bundle // CSS above is covered by the combined bundle // Included in combined bundle }); // Enqueue contact form script only when the block is present add_action('wp_enqueue_scripts', function () { global $post; $should_load = false; // Check if current post has the block if (is_a($post, 'WP_Post') && has_block('ai-builder/aibui-contact-form', $post)) { $should_load = true; } // Fallback: check if we're on a page that might have the block if (!$should_load && (is_page() || is_single() || is_home() || is_front_page())) { $should_load = true; } if ($should_load) { wp_enqueue_script( 'ai-builder-contact-form', plugin_dir_url(__FILE__) . 'assets/js/contact-form.js', [], AIBUI_VERSION, true ); } }); add_action('enqueue_block_editor_assets', function () { // Single combined CSS for editor $combined_css = aibui_get_combined_css_url(); if ($combined_css) { wp_enqueue_style( 'ai-builder-combined-editor', $combined_css, [], null ); } else { wp_enqueue_style( 'aibui-force-alignfull-editor', plugin_dir_url(__FILE__) . 'assets/css/force-align-full.css', [], AIBUI_VERSION ); } // Charger les styles build des blocs dans l'éditeur wp_enqueue_style( 'ai-builder-blocks-editor-build', plugin_dir_url(__FILE__) . 'assets/js/build/index.css', [], filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.css') ); // Styles spécifiques d'aperçu du formulaire de contact wp_enqueue_style( 'ai-builder-contact-form-editor', plugin_dir_url(__FILE__) . 'assets/css/contact-form-editor.css', [], AIBUI_VERSION ); // Covered by combined bundle // Stats tooltips in editor preview wp_enqueue_script( 'ai-builder-stats-tooltips-editor', plugin_dir_url(__FILE__) . 'assets/js/stats-tooltips.js', [], AIBUI_VERSION, true ); }); add_action('enqueue_block_editor_assets', function () { wp_enqueue_script( 'ai-builder-blocks', plugin_dir_url(__FILE__) . 'assets/js/build/index.js', [ 'wp-blocks', 'wp-block-editor', 'wp-element', 'wp-components', 'wp-i18n', 'wp-api-fetch', 'wp-hooks' ], filemtime(plugin_dir_path(__FILE__) . 'assets/js/build/index.js'), true ); // Injection des variables JS pour AJAX et le nonce (utilisées par les blocs éditeur) wp_localize_script( 'ai-builder-blocks', 'aiBuilderVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aibui_nonce'), ) ); }); // Enregistrer le bloc côté PHP add_action('init', function () { // Bloc AI Block register_block_type('ai-builder/ai-block', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', 'render_callback' => function ($attributes) { // Le bloc AI Block ne s'affiche pas côté frontend car il est remplacé par le contenu généré return ''; } ]); // Bloc stat-bar (statique, pas de render_callback) register_block_type('ai-builder/stat-bar', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-stats (rendu simple côté éditeur, frontend statique) register_block_type('ai-builder/aibui-stats', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-carousel register_block_type('ai-builder/aibui-carousel', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-yt-video register_block_type('ai-builder/aibui-yt-video', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-map register_block_type('ai-builder/aibui-map', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-tabs // Bloc aibui-table register_block_type('ai-builder/aibui-table', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); register_block_type('ai-builder/aibui-tabs', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', ]); // Bloc aibui-contact-form (rendu via callback PHP pour sécurité) register_block_type('ai-builder/aibui-contact-form', [ 'editor_script' => 'ai-builder-blocks', 'editor_style' => 'ai-builder-blocks-style', 'style' => 'ai-builder-blocks-style', 'render_callback' => function ($attributes, $content, $block) { $recipient = isset($attributes['recipientEmail']) ? sanitize_email($attributes['recipientEmail']) : ''; if (empty($recipient) || !is_email($recipient)) { $recipient = sanitize_email(get_option('admin_email')); } if (empty($recipient) || !is_email($recipient)) { // Pas de destinataire valide disponible, n'arrête pas l'affichage, mais désactive l'envoi $recipient = ''; } $fields = isset($attributes['fields']) && is_array($attributes['fields']) ? $attributes['fields'] : []; $fields = array_slice($fields, 0, 5); $form_title = isset($attributes['formTitle']) ? wp_kses_post($attributes['formTitle']) : ''; $description = isset($attributes['description']) ? wp_kses_post($attributes['description']) : ''; $button_label = isset($attributes['buttonLabel']) ? sanitize_text_field($attributes['buttonLabel']) : __('Send', 'ai-builder'); $success_message = isset($attributes['successMessage']) ? sanitize_text_field($attributes['successMessage']) : __('Thank you, your message has been sent.', 'ai-builder'); $from_email_attr = isset($attributes['fromEmail']) ? sanitize_email($attributes['fromEmail']) : ''; $nonce = wp_create_nonce('aibui_contact_form'); $action = esc_url(admin_url('admin-ajax.php')); ob_start(); ?>
pour être disponible partout ); // Autres scripts qui utilisent config.js wp_enqueue_script( 'ai-builder-image-ai-controls', plugins_url('assets/js/src/editor-blocks/image-ai-blocks/image-ai-controls.js', __FILE__), array('ai-builder-config'), AIBUI_VERSION, true ); // Autres scripts qui utilisent config.js wp_enqueue_script( 'ai-builder-text-ai-controls', plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__), array('ai-builder-config'), AIBUI_VERSION, true ); // Autres scripts qui utilisent config.js wp_enqueue_script( 'ai-builder-text-ai-controls', plugins_url('assets/js/src/editor-blocks/text-ai-blocks/text-ai-controls.js', __FILE__), array('ai-builder-config'), AIBUI_VERSION, true ); // Autres scripts qui utilisent config.js wp_enqueue_script( 'ai-builder-ai-block', plugins_url('assets/js/src/editor-blocks/ai-block/ai-block.js', __FILE__), array('ai-builder-config'), AIBUI_VERSION, true ); } add_action('enqueue_block_editor_assets', 'enqueue_ai_builder_scripts'); add_action('wp_enqueue_scripts', function () { // Combined CSS already enqueued above; only enqueue any extra block scss-generated styles if needed // Table, snackbar, cards editor styles are not required on frontend separately when bundled // Load snackbar frontend JavaScript wp_enqueue_script( 'ai-builder-snackbar-frontend', plugin_dir_url(__FILE__) . 'assets/js/snackbar-frontend.js', [], AIBUI_VERSION, true ); }); add_action('enqueue_block_editor_assets', function () { // Combined CSS already enqueued above; keep editor-specific assets below // Load fixed background group block CSS specifically (already inside combined, but keep safe fallback if combined failed) $fixed_bg_css_url = plugin_dir_url(__FILE__) . 'assets/js/src/editor-blocks/fixed-bg-group/style.css'; wp_enqueue_style('ai-builder-fixed-bg-group-editor', $fixed_bg_css_url, [], AIBUI_VERSION); // Load WordPress media scripts for image selection wp_enqueue_media(); }); // ------------------------------- // Meta description per page/post // ------------------------------- add_action('add_meta_boxes', function () { add_meta_box( 'aibui_meta_description', __('Meta description', 'ai-builder'), function ($post) { $value = get_post_meta($post->ID, 'aibui_meta_description', true); wp_nonce_field('aibui_meta_description_save', 'aibui_meta_description_nonce'); echo '' . esc_html__('Add a meta description for this page/post.', 'ai-builder') . '
'; echo ''; echo '' . esc_html__('Tip: 140–160 characters for best SEO results.', 'ai-builder') . '
'; }, array('post', 'page'), 'normal', 'default' ); }); add_action('save_post', function ($post_id) { if (!isset($_POST['aibui_meta_description_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['aibui_meta_description_nonce'])), 'aibui_meta_description_save')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return; if (!current_user_can('edit_post', $post_id)) return; $raw = isset($_POST['aibui_meta_description']) ? wp_unslash($_POST['aibui_meta_description']) : ''; $san = trim(wp_strip_all_tags($raw)); if (strlen($san) > 320) { $san = mb_substr($san, 0, 320); } if ($san === '') { delete_post_meta($post_id, 'aibui_meta_description'); } else { update_post_meta($post_id, 'aibui_meta_description', $san); } }); add_action('wp_head', function () { if (is_admin()) return; if (!is_singular()) return; $post_id = get_queried_object_id(); if (!$post_id) return; $desc = get_post_meta($post_id, 'aibui_meta_description', true); if ($desc) { echo "\n\n"; } }, 1); // Initialiser le gestionnaire AJAX new AIBUI_Ajax_Handler();