init_hooks(); $this->load_dependencies(); } /** * Initialize WordPress hooks */ private function init_hooks() { // Load text domain for translations add_action('init', array($this, 'load_textdomain')); // Admin menu add_action('admin_menu', array($this, 'add_admin_menu'), 60); // Enqueue assets add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets')); // Floating chat widget add_action('wp_footer', array($this, 'render_floating_widget'), 999); // AJAX handler for saving training content selection add_action('wp_ajax_aieo_chat_save_content_selection', array($this, 'ajax_save_content_selection')); // AJAX handler for saving settings (deduplication mode, etc.) add_action('wp_ajax_aieo_chat_save_settings', array($this, 'ajax_save_settings')); // Check and create tables if needed add_action('admin_init', array($this, 'check_tables')); // Custom footer text for admin pages add_filter('admin_footer_text', array($this, 'aieo_chat_footer_text'), 11); // Cron schedule filters add_filter('cron_schedules', array($this, 'add_cron_schedules')); // Cron event handlers add_action('aieo_auto_analytics_event', array($this, 'run_auto_analytics')); add_action('aieo_auto_embeddings_event', array($this, 'run_auto_embeddings')); // Activation/Deactivation hooks register_activation_hook(AIEO_CHAT_FILE, array($this, 'activate')); register_deactivation_hook(AIEO_CHAT_FILE, array($this, 'deactivate')); } /** * Check if tables exist and create if needed */ public function check_tables() { global $wpdb; $embeddings_table = $wpdb->prefix . 'aieo_chat_embeddings'; $history_table = $wpdb->prefix . 'aieo_chat_history'; // Check if tables exist $embeddings_exists = $wpdb->get_var("SHOW TABLES LIKE '$embeddings_table'") === $embeddings_table; $history_exists = $wpdb->get_var("SHOW TABLES LIKE '$history_table'") === $history_table; if (!$embeddings_exists || !$history_exists) { $this->load_dependencies(); // Make sure classes are loaded AI_eShop_Chat_Database_Manager::get_instance()->create_tables(); } } /** * Load required dependencies */ private function load_dependencies() { // Core classes require_once AIEO_CHAT_DIR . 'includes/chat/class-anthropic-api.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-openai-api.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-database-manager.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-product-vitals-helper.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-embedding-manager.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-chat-api.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-content-extractor.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-shortcode-handler.php'; require_once AIEO_CHAT_DIR . 'includes/chat/class-rest-api.php'; // Initialize components AI_eShop_Chat_Database_Manager::get_instance(); AI_eShop_Chat_Embedding_Manager::get_instance(); AI_eShop_Chat_API::get_instance(); AI_eShop_Chat_Shortcode::get_instance(); AI_eShop_Chat_REST_API::get_instance(); } /** * Load plugin text domain for translations * * The chat addon uses the same text domain as the main plugin ('ai-eshop-optimizer') * so translations are shared between both plugins. * * Note: As of WordPress 4.6+, language packs are automatically loaded from WordPress.org * for plugins hosted on the repository. Manual loading is no longer required. */ public function load_textdomain() { // For plugins hosted on WordPress.org, translations are loaded automatically as of WP 4.6+ // Manual loading kept for backwards compatibility with older WordPress versions if (version_compare(get_bloginfo('version'), '4.6', '<')) { load_plugin_textdomain( 'ai-eshop-optimizer', false, dirname(plugin_basename(AIEO_FILE)) . '/languages' ); } } /** * Add admin menu */ public function add_admin_menu() { global $menu; // Check if AI eShop Optimizer parent menu exists $parent_exists = false; if (!empty($menu)) { foreach ($menu as $item) { if (isset($item[2]) && $item[2] === 'ai-eshop-optimizer') { $parent_exists = true; break; } } } if ($parent_exists) { // Add as submenu under AI eShop Optimizer if it exists add_submenu_page( 'ai-eshop-optimizer', // Parent slug __('AI Chat', 'ai-eshop-optimizer'), // Page title __('🤖 AI Chat', 'ai-eshop-optimizer'), // Menu title 'manage_options', // Capability 'ai-eshop-chat', // Menu slug array($this, 'render_admin_page') // Callback ); } else { // Create standalone top-level menu if parent doesn't exist add_menu_page( __('AI Chat', 'ai-eshop-optimizer'), // Page title __('AI Chat', 'ai-eshop-optimizer'), // Menu title 'manage_options', // Capability 'ai-eshop-chat', // Menu slug array($this, 'render_admin_page'), // Callback 'dashicons-format-chat', // Icon (chat bubble) 58 // Position (after Comments) ); } } /** * Render admin page */ public function render_admin_page() { require_once AIEO_CHAT_DIR . 'assets/pages/ai-eshop-chat.php'; } /** * Custom footer text for chat settings page */ public function aieo_chat_footer_text($content) { // Check if we're on the AI Chat settings page by checking the page parameter // This works regardless of menu structure (submenu, top-level, under WooCommerce, etc.) if (isset($_GET['page']) && $_GET['page'] === 'ai-eshop-chat') { // Check if WooCommerce is active to customize the message if (class_exists('WooCommerce')) { return "Thank you for choosing AI eShop Optimizer for Woocommerce by Oxford Metadata Ltd"; } else { return "Thank you for choosing AI eShop Optimizer for Wordpress by Oxford Metadata Ltd"; } } return $content; } /** * Enqueue admin assets */ public function enqueue_admin_assets($hook) { // Check if we're on the AI Chat page (submenu of AI eShop Optimizer) // Also check for the page query parameter as a fallback if ('ai-eshop-optimizer_page_ai-eshop-chat' !== $hook && (!isset($_GET['page']) || $_GET['page'] !== 'ai-eshop-chat')) { return; } // Enqueue the MAIN plugin's admin CSS (this has all the styles we need!) wp_enqueue_style( 'aieo-admin-panel', AIEO_URI . 'assets/css/aieo-admin-panel.css', array(), AIEO_VERSION ); // Enqueue Chat Admin CSS for AI Chat page specific styling wp_enqueue_style( 'aieo-chat-admin', AIEO_CHAT_URI . 'assets/css/chat-admin.css', array('aieo-admin-panel'), AIEO_CHAT_VERSION ); // Enqueue WordPress color picker wp_enqueue_style('wp-color-picker'); wp_enqueue_script('wp-color-picker'); // Enqueue WordPress media library wp_enqueue_media(); // Note: export-progress.js is NOT needed on chat page - it's for AIEO export functionality // Remove the enqueue to prevent aieoExport undefined error } /** * Enqueue frontend assets */ public function enqueue_frontend_assets() { wp_enqueue_style( 'aieo-chat-frontend', AIEO_CHAT_URI . 'assets/css/chat-frontend.css', array(), AIEO_CHAT_VERSION ); wp_enqueue_script( 'aieo-chat-frontend', AIEO_CHAT_URI . 'assets/js/chat-frontend.js', array('jquery'), AIEO_CHAT_VERSION, true ); wp_localize_script('aieo-chat-frontend', 'aieoChatConfig', array( 'restUrl' => rest_url('aieo-chat/v1/'), 'restNonce' => wp_create_nonce('wp_rest'), 'chatEnabled' => get_option('aieo_chat_enabled', '0'), 'floatingEnabled' => get_option('aieo_chat_floating_enabled', '0'), 'floatingPosition' => get_option('aieo_chat_floating_position', 'bottom-right'), 'botName' => get_option('aieo_chat_bot_name', 'AI Assistant by Oxford Metadata'), 'welcomeMessage' => get_option('aieo_chat_welcome_message', 'Hello! How can I help you find the perfect product today?'), 'inputPlaceholder' => get_option('aieo_chat_input_placeholder', 'Ask about anything...'), 'maxResults' => get_option('aieo_chat_max_results', 10), 'hideImages' => get_option('aieo_chat_hide_images', '0'), 'requireLogin' => get_option('aieo_chat_require_login', '0'), 'showTerms' => get_option('aieo_chat_show_terms', '0') )); } /** * Render floating chat widget */ public function render_floating_widget() { // Only render if floating widget is enabled if (get_option('aieo_chat_floating_enabled', '0') !== '1') { return; } // Don't show on admin pages if (is_admin()) { return; } // Get all settings $position = get_option('aieo_chat_floating_position', 'bottom-right'); $mobile_position = get_option('aieo_chat_floating_mobile_position', 'bottom-right'); $bot_name = get_option('aieo_chat_bot_name', 'AI Assistant by Oxford Metadata'); $custom_icon_id = intval(get_option('aieo_chat_floating_custom_icon', 0)); $welcome_bubble = get_option('aieo_chat_floating_welcome_bubble', 'Hi! How can I help you?'); $popup_width_raw = get_option('aieo_chat_floating_popup_width', '390'); $popup_height_raw = get_option('aieo_chat_floating_popup_height', '600'); $button_color = sanitize_hex_color(get_option('aieo_chat_floating_button_color', '#03163C')); $primary_color = sanitize_hex_color(get_option('aieo_chat_primary_color', '#47ACFE')); $chat_theme = get_option('aieo_chat_theme', 'rounded'); // Fallback colors if (empty($button_color)) { $button_color = '#03163C'; } if (empty($primary_color)) { $primary_color = '#47ACFE'; } // Process dimensions - support px, vh, %, or plain numbers $popup_width = $this->sanitize_dimension($popup_width_raw, '390px', 320, 800); $popup_height = $this->sanitize_dimension($popup_height_raw, '600px', 400, 900); // Get custom icon URL if set $custom_icon_url = $custom_icon_id ? wp_get_attachment_image_url($custom_icon_id, 'thumbnail') : ''; $use_custom_icon = !empty($custom_icon_url); // Convert hex to RGB for light variant and gradient $primary_rgb = sscanf($primary_color, "#%02x%02x%02x"); $primary_color_light = sprintf('rgba(%d, %d, %d, 0.1)', $primary_rgb[0], $primary_rgb[1], $primary_rgb[2]); // Convert button color hex to RGB for gradients $button_rgb = sscanf($button_color, "#%02x%02x%02x"); // Create slightly darker variant for gradient end (reduce each channel by 15%) $button_rgb_dark = array( max(0, $button_rgb[0] * 0.85), max(0, $button_rgb[1] * 0.85), max(0, $button_rgb[2] * 0.85) ); $button_color_dark = sprintf('#%02x%02x%02x', $button_rgb_dark[0], $button_rgb_dark[1], $button_rgb_dark[2]); $button_gradient = sprintf('linear-gradient(135deg, %s 0%%, %s 100%%)', $button_color, $button_color_dark); $button_shadow = sprintf('rgba(%d, %d, %d, 0.3)', $button_rgb[0], $button_rgb[1], $button_rgb[2]); ?>
0) { $number = max($min_px, min($max_px, $number)); return $number . 'px'; } return $default; } /** * Activation hook */ public function activate() { // Create database tables AI_eShop_Chat_Database_Manager::get_instance()->create_tables(); // Set default options $this->set_default_options(); // Flush rewrite rules flush_rewrite_rules(); } /** * Deactivation hook */ public function deactivate() { // Flush rewrite rules flush_rewrite_rules(); } /** * Set default options */ private function set_default_options() { $defaults = array( 'aieo_chat_enabled' => '0', 'aieo_chat_bot_name' => 'AI Assistant by Oxford Metadata', 'aieo_chat_welcome_message' => 'Hello! How can I help you find the perfect product today?', 'aieo_chat_input_placeholder' => 'Ask about anything...', 'aieo_chat_model' => 'claude-sonnet-4-5', 'aieo_chat_max_tokens' => '500', 'aieo_chat_temperature' => '0.5', 'aieo_chat_max_results' => '10', 'aieo_chat_rate_limit' => '1000', 'aieo_chat_hide_images' => '0', 'aieo_chat_require_login' => '0', 'aieo_chat_history_enabled' => '1', 'aieo_chat_show_terms' => '0', 'aieo_chat_floating_enabled' => '0', 'aieo_chat_floating_position' => 'bottom-right', 'aieo_chat_floating_mobile_position' => 'bottom-right', 'aieo_chat_floating_popup_width' => '390px', 'aieo_chat_floating_popup_height' => '600px', 'aieo_chat_enabled_post_types' => array('product') ); foreach ($defaults as $key => $value) { if (get_option($key) === false) { add_option($key, $value, '', 'no'); } } } /** * Save the currently selected post types and taxonomies for training */ public function ajax_save_content_selection() { check_ajax_referer('aieo_chat_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Insufficient permissions', 'ai-eshop-optimizer')), 403); } $post_types = isset($_POST['post_types']) ? (array) $_POST['post_types'] : array(); $post_types = array_map('sanitize_text_field', $post_types); update_option('aieo_chat_enabled_post_types', $post_types); $taxonomies = isset($_POST['taxonomies']) ? (array) $_POST['taxonomies'] : array(); $taxonomies = array_map('sanitize_text_field', $taxonomies); update_option('aieo_chat_enabled_taxonomies', $taxonomies); wp_send_json_success(array( 'post_types_saved' => $post_types, 'taxonomies_saved' => $taxonomies )); } /** * AJAX handler for saving settings (deduplication mode, etc.) */ public function ajax_save_settings() { check_ajax_referer('aieo_chat_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Insufficient permissions', 'ai-eshop-optimizer')), 403); } $saved_settings = array(); // Save deduplication mode if provided if (isset($_POST['aieo_chat_deduplication_mode'])) { $dedup_mode = intval($_POST['aieo_chat_deduplication_mode']); update_option('aieo_chat_deduplication_mode', $dedup_mode); $saved_settings['deduplication_mode'] = $dedup_mode; } wp_send_json_success(array( 'message' => __('Settings saved successfully', 'ai-eshop-optimizer'), 'saved' => $saved_settings )); } /** * Add custom cron schedules */ public function add_cron_schedules($schedules) { if (!isset($schedules['weekly'])) { $schedules['weekly'] = array( 'interval' => 7 * DAY_IN_SECONDS, 'display' => __('Once Weekly', 'ai-eshop-optimizer') ); } if (!isset($schedules['monthly'])) { $schedules['monthly'] = array( 'interval' => 30 * DAY_IN_SECONDS, 'display' => __('Once Monthly', 'ai-eshop-optimizer') ); } return $schedules; } /** * Check if user has valid Pro subscription * * @return bool True if user has active Pro subscription, false otherwise */ private function has_valid_pro_subscription() { // Check if user has Pro membership if (get_option('aieo_user_membership') !== 'Pro') { return false; } // Check subscription expiration $expiration_date = get_option('aieo_subscription_expiration', ''); if (!empty($expiration_date)) { // Parse the expiration date (format: MM/DD/YYYY or similar) $expiration_timestamp = strtotime($expiration_date); $current_timestamp = current_time('timestamp'); // If expired, disable automation and clear cron events if ($expiration_timestamp < $current_timestamp) { error_log('AIEO: Pro subscription expired on ' . $expiration_date . ' - disabling automation'); // Disable automation settings update_option('aieo_auto_analytics_enabled', '0'); update_option('aieo_auto_embeddings_enabled', '0'); // Clear cron events wp_clear_scheduled_hook('aieo_auto_analytics_event'); wp_clear_scheduled_hook('aieo_auto_embeddings_event'); return false; } } return true; } /** * Run automated analytics update */ public function run_auto_analytics() { error_log('AIEO: Running automated analytics update'); // Check if user has valid Pro subscription if (!$this->has_valid_pro_subscription()) { error_log('AIEO: Skipping analytics automation - Valid Pro subscription required'); return; } // Trigger the same process as "Save Settings & Analyze Data" button // This is handled by the main plugin's export/analysis functionality do_action('aieo_run_export_and_analysis'); error_log('AIEO: Completed automated analytics update'); } /** * Run automated embeddings generation */ public function run_auto_embeddings() { error_log('AIEO: Running automated embeddings generation'); // Check if user has valid Pro subscription if (!$this->has_valid_pro_subscription()) { error_log('AIEO: Skipping embeddings automation - Valid Pro subscription required'); return; } // Get current embedding settings $embedding_provider = get_option('aieo_embedding_provider', 'voyage'); $dedup_mode = get_option('aieo_chat_deduplication_mode', 1); $enabled_post_types = get_option('aieo_chat_enabled_post_types', array('product')); if (empty($enabled_post_types)) { error_log('AIEO: No post types selected for embedding generation'); return; } // Get embedding engine based on provider if ($embedding_provider === 'voyage') { $embedding_engine = get_option('aieo_chat_embedding_model', 'voyage-3'); } else { $embedding_engine = get_option('aieo_chat_openai_embedding_model', 'text-embedding-3-large'); } error_log('AIEO: Generating embeddings with engine: ' . $embedding_engine . ', dedup: ' . $dedup_mode); // Get all content IDs to process global $wpdb; $content_ids = array(); foreach ($enabled_post_types as $post_type) { $posts = get_posts(array( 'post_type' => $post_type, 'post_status' => 'publish', 'numberposts' => -1, 'fields' => 'ids' )); $content_ids = array_merge($content_ids, $posts); } if (empty($content_ids)) { error_log('AIEO: No content found for embedding generation'); return; } error_log('AIEO: Processing ' . count($content_ids) . ' items for embedding generation'); // Process embeddings in batches $batch_size = 10; $batches = array_chunk($content_ids, $batch_size); $processed = 0; foreach ($batches as $batch) { foreach ($batch as $content_id) { // Generate embedding using the same logic as manual training do_action('aieo_generate_single_embedding', $content_id, $embedding_engine, $dedup_mode); $processed++; } // Small delay between batches to avoid rate limits sleep(1); } error_log('AIEO: Completed automated embeddings generation - processed ' . $processed . ' items'); } } /** * Initialize the addon */ function aieo_chat_init() { AI_eShop_Chat::get_instance(); } add_action('plugins_loaded', 'aieo_chat_init', 20);