can_use_premium_code() ? esc_html__('Free Templates', 'king-addons') : esc_html__('Templates Pro', 'king-addons')), 'manage_options', 'king-addons-templates', [Templates::instance(), 'render_template_catalog_page'], KING_ADDONS_URL . 'includes/admin/img/icon-for-menu-templates.svg', 58.71 ); } if (KING_ADDONS_EXT_HEADER_FOOTER_BUILDER) { self::showHeaderFooterBuilder(); } if (KING_ADDONS_EXT_POPUP_BUILDER) { self::showPopupBuilder(); } // Add AI Settings submenu under King Addons add_submenu_page( 'king-addons', esc_html__('AI Settings', 'king-addons'), esc_html__('AI Settings', 'king-addons'), 'manage_options', 'king-addons-ai-settings', [$this, 'showAiSettingsPage'] ); } function addUpgradeMenu(): void { // Don't add menu if Freemius is showing opt-in/activation $fs = king_addons_freemius(); // Check if we're on any Freemius-related page if (isset($_GET['fs_action']) || $fs->is_activation_mode() || (!$fs->is_registered() && !$fs->is_anonymous() && !$fs->is_tracking_prohibited())) { return; } // Add Upgrade submenu under King Addons (only if premium is not active) if (!$fs->can_use_premium_code()) { add_submenu_page( 'king-addons', esc_html__('Upgrade Now', 'king-addons'), esc_html__('Upgrade Now', 'king-addons'), 'manage_options', 'https://kingaddons.com/pricing/?utm_source=kng-top-menu&utm_medium=plugin&utm_campaign=kng', '' ); } } function showPopupBuilder(): void { add_menu_page( 'Popup Builder', 'Popup Builder', 'manage_options', 'king-addons-popup-builder', [Popup_Builder::instance(), 'renderPopupBuilder'], KING_ADDONS_URL . 'includes/admin/img/icon-for-popup-builder.svg', 58.73 ); } function showHeaderFooterBuilder(): void { $post_type = 'king-addons-el-hf'; $menu_slug = 'edit.php?post_type=' . $post_type; // Add Main Menu add_menu_page( esc_html__('Elementor Header & Footer Builder', 'king-addons'), esc_html__('Header & Footer', 'king-addons'), 'manage_options', $menu_slug, // Menu slug points to the custom post type edit screen '', // No callback function needed KING_ADDONS_URL . 'includes/admin/img/icon-for-header-footer-builder.svg', 58.72 ); // Add 'All Templates' Submenu - this will be the first submenu item add_submenu_page( $menu_slug, // Parent slug matches the main menu slug esc_html__('All Templates', 'king-addons'), esc_html__('All Templates', 'king-addons'), 'edit_posts', $menu_slug ); } function showAdminPage(): void { if (!current_user_can('manage_options')) { return; } self::enqueueAdminAssets(); require_once(KING_ADDONS_PATH . 'includes/admin/layouts/admin-page.php'); } function showSettingsPage(): void { if (!current_user_can('manage_options')) { return; } require_once(KING_ADDONS_PATH . 'includes/admin/layouts/settings-page.php'); self::enqueueSettingsAssets(); } function createSettings(): void { // Register a new setting for "king-addons" page. register_setting('king_addons', 'king_addons_options'); // Register a new section in the "king-addons" page. add_settings_section( 'king_addons_section_widgets', '', [$this, 'king_addons_section_widgets_callback'], 'king-addons' ); // Register a new section in the "king-addons" page. add_settings_section( 'king_addons_section_features', '', [$this, 'king_addons_section_features_callback'], 'king-addons' ); foreach (ModulesMap::getModulesMapArray()['widgets'] as $widget_id => $widget_array) { add_settings_field( $widget_id, $widget_array['title'], '', 'king-addons', 'king_addons_section_widgets', array( 'label_for' => $widget_id, 'description' => $widget_array['description'], 'docs_link' => $widget_array['docs-link'], 'demo_link' => $widget_array['demo-link'], 'class' => 'kng-tr kng-tr-' . $widget_id . (!empty($widget_array['has-pro']) ? ' kng-tr-freemium' : '') ) ); } foreach (ModulesMap::getModulesMapArray()['features'] as $feature_id => $feature_array) { add_settings_field( $feature_id, $feature_array['title'], '', 'king-addons', 'king_addons_section_features', array( 'label_for' => $feature_id, 'description' => $feature_array['description'], 'docs_link' => $feature_array['docs-link'], 'demo_link' => $feature_array['demo-link'], 'class' => 'kng-tr kng-tr-' . $feature_id ) ); } } function king_addons_section_widgets_callback($args): void { ?>

can_use_premium_code()) { wp_enqueue_script('jquery'); wp_add_inline_script('jquery', " jQuery(document).ready(function($) { $('#adminmenu #toplevel_page_king-addons a[href=\"https://kingaddons.com/pricing/?utm_source=kng-top-menu&utm_medium=plugin&utm_campaign=kng\"]').attr('target', '_blank'); }); "); } } function enqueueSettingsAssets(): void { wp_enqueue_style('king-addons-settings', KING_ADDONS_URL . 'includes/admin/css/settings.css', '', KING_ADDONS_VERSION); wp_enqueue_style('wp-color-picker'); wp_enqueue_script('jquery'); wp_enqueue_script('wp-color-picker'); wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-wpcolorpicker-wpcolorpicker'); wp_enqueue_script('king-addons-settings', KING_ADDONS_URL . 'includes/admin/js/settings.js', '', KING_ADDONS_VERSION); } /** * Registers AI Settings using the WordPress Settings API. * * @return void */ public function createAiSettings(): void { register_setting( 'king_addons_ai', 'king_addons_ai_options', [$this, 'sanitizeAiSettings'] ); add_settings_section( 'king_addons_ai_openai_section', esc_html__('OpenAI API Settings', 'king-addons'), [$this, 'renderAiOpenaiSection'], 'king-addons-ai-settings' ); add_settings_field( 'openai_api_key', esc_html__('OpenAI API Key', 'king-addons'), [$this, 'renderAiApiKeyField'], 'king-addons-ai-settings', 'king_addons_ai_openai_section' ); add_settings_field( 'openai_model', esc_html__('OpenAI Model', 'king-addons'), [$this, 'renderAiModelField'], 'king-addons-ai-settings', 'king_addons_ai_openai_section' ); // Add image model selector field add_settings_field( 'openai_image_model', esc_html__('OpenAI Image Model', 'king-addons'), [$this, 'renderAiImageModelField'], 'king-addons-ai-settings', 'king_addons_ai_openai_section' ); // Add Editor Integration section and field add_settings_section( 'king_addons_ai_editor_section', esc_html__('Editor Integration', 'king-addons'), [$this, 'renderAiEditorSection'], 'king-addons-ai-settings' ); add_settings_field( 'enable_ai_buttons', esc_html__('AI Text Editing Buttons', 'king-addons'), [$this, 'renderAiEnableButtonsField'], 'king-addons-ai-settings', 'king_addons_ai_editor_section' ); add_settings_field( 'enable_ai_image_generation_button', esc_html__('AI Image Generation Button', 'king-addons'), [$this, 'renderAiImageGenerationField'], 'king-addons-ai-settings', 'king_addons_ai_editor_section' ); // Add Alt Text Settings section add_settings_section( 'king_addons_ai_alt_text_section', esc_html__('Alt Text Settings', 'king-addons'), [$this, 'renderAiAltTextSection'], 'king-addons-ai-settings' ); add_settings_field( 'enable_ai_alt_text_button', esc_html__('AI Alt Text Button', 'king-addons'), [$this, 'renderAiAltTextButtonField'], 'king-addons-ai-settings', 'king_addons_ai_alt_text_section' ); add_settings_field( 'enable_ai_alt_text_auto_generation', ((king_addons_freemius()->can_use_premium_code()) ? esc_html__('Auto Generate Alt Text', 'king-addons') : esc_html__('Auto Generate Alt Text (PRO feature)', 'king-addons')), [$this, 'renderAiAltTextAutoGenerationField'], 'king-addons-ai-settings', 'king_addons_ai_alt_text_section' ); add_settings_field( 'ai_alt_text_generation_interval', esc_html__('Alt Text Generation Interval', 'king-addons'), [$this, 'renderAiAltTextIntervalField'], 'king-addons-ai-settings', 'king_addons_ai_alt_text_section' ); // Add Image Detail Level field add_settings_field( 'ai_alt_text_image_detail_level', esc_html__('Image Detail Level', 'king-addons'), [$this, 'renderAiAltTextImageDetailLevelField'], 'king-addons-ai-settings', 'king_addons_ai_alt_text_section' ); // Add Translation Settings section add_settings_section( 'king_addons_ai_translation_section', esc_html__('Translation Settings', 'king-addons'), [$this, 'renderAiTranslationSection'], 'king-addons-ai-settings' ); add_settings_field( 'enable_ai_page_translator', esc_html__('AI Page Translator Button', 'king-addons'), [$this, 'renderAiPageTranslatorField'], 'king-addons-ai-settings', 'king_addons_ai_translation_section' ); // Add Usage Quota Settings section and field add_settings_section( 'king_addons_ai_quota_section', esc_html__('Usage Quota Settings', 'king-addons'), [$this, 'renderAiQuotaSection'], 'king-addons-ai-settings' ); add_settings_field( 'daily_token_limit', esc_html__('Daily Token Limit', 'king-addons'), [$this, 'renderAiDailyLimitField'], 'king-addons-ai-settings', 'king_addons_ai_quota_section' ); // Add Usage Statistics section (read-only) add_settings_section( 'king_addons_ai_stats_section', esc_html__('Usage Statistics', 'king-addons'), [$this, 'renderAiStatsSection'], 'king-addons-ai-settings' ); // Clear models cache when options updated. add_action('update_option_king_addons_ai_options', [$this, 'clearAiModelsCache']); // AJAX handler for refreshing models. add_action('wp_ajax_king_addons_ai_refresh_models', [$this, 'handleAiRefreshModels']); // AJAX handler for generating text via AI add_action('wp_ajax_king_addons_ai_generate_text', [$this, 'handleAiGenerateText']); // AJAX handler to change text using AI based on user prompt and original text. add_action('wp_ajax_king_addons_ai_change_text', [$this, 'handleAiChangeText']); // AJAX handler to check token usage limits add_action('wp_ajax_king_addons_ai_check_tokens', [$this, 'handleAiCheckTokens']); // AJAX handler to check image generation limits add_action('wp_ajax_king_addons_ai_image_check_limits', [$this, 'handleAiImageCheckLimits']); // THIRD_EDIT: Register AJAX handler for AI image generation add_action('wp_ajax_king_addons_ai_generate_image', [$this, 'handleAiGenerateImage']); // AJAX handler for AI page translation add_action('wp_ajax_king_addons_ai_translate_text', [$this, 'handleAiTranslateText']); } /** * Sanitizes AI Settings options. * * @param array $input Raw input array. * @return array Sanitized input. */ public function sanitizeAiSettings(array $input): array { $sanitized = []; $sanitized['openai_api_key'] = isset($input['openai_api_key']) ? sanitize_text_field($input['openai_api_key']) : ''; $sanitized['openai_model'] = isset($input['openai_model']) ? sanitize_text_field($input['openai_model']) : ''; $sanitized['openai_image_model'] = isset($input['openai_image_model']) ? sanitize_text_field($input['openai_image_model']) : 'gpt-image-1'; // Sanitize Daily Token Limit. if (isset($input['daily_token_limit'])) { $daily_limit = absint($input['daily_token_limit']); $sanitized['daily_token_limit'] = max(0, $daily_limit); // Ensure non-negative } else { $sanitized['daily_token_limit'] = 1000000; // Default to 1 million tokens if not set } // Sanitize Enable AI Buttons option. $sanitized['enable_ai_buttons'] = ! empty($input['enable_ai_buttons']); // Sanitize Enable AI Image Generation button option. $sanitized['enable_ai_image_generation_button'] = ! empty($input['enable_ai_image_generation_button']); // Sanitize Enable AI Alt Text Button option. $sanitized['enable_ai_alt_text_button'] = ! empty($input['enable_ai_alt_text_button']); // Sanitize Enable AI Alt Text Auto Generation option. $sanitized['enable_ai_alt_text_auto_generation'] = ! empty($input['enable_ai_alt_text_auto_generation']); // Sanitize AI Alt Text Generation Interval. if (isset($input['ai_alt_text_generation_interval'])) { $interval = absint($input['ai_alt_text_generation_interval']); $sanitized['ai_alt_text_generation_interval'] = max(10, min(3600, $interval)); // Between 10 seconds and 1 hour } else { $sanitized['ai_alt_text_generation_interval'] = 60; // Default to 60 seconds } // Sanitize Image Detail Level $allowed_detail_levels = ['low', 'high']; $sanitized['ai_alt_text_image_detail_level'] = in_array(($input['ai_alt_text_image_detail_level'] ?? 'low'), $allowed_detail_levels, true) ? $input['ai_alt_text_image_detail_level'] : 'low'; // Sanitize Enable AI Page Translator option $sanitized['enable_ai_page_translator'] = ! empty($input['enable_ai_page_translator']); return $sanitized; } /** * Clears cached AI models list. * * @return void */ public function clearAiModelsCache(): void { delete_transient('king_addons_ai_models_cache'); } /** * Renders the AI Settings page content. * * @return void */ public function showAiSettingsPage(): void { if (! current_user_can('manage_options')) { return; } require_once KING_ADDONS_PATH . 'includes/admin/layouts/ai-settings-page.php'; $this->enqueueAiSettingsAssets(); } /** * Enqueues scripts and styles for the AI Settings page. * * @return void */ public function enqueueAiSettingsAssets(): void { // Enqueue admin base styles first for proper theming wp_enqueue_style('king-addons-admin', KING_ADDONS_URL . 'includes/admin/css/admin.css', '', KING_ADDONS_VERSION); wp_enqueue_style( 'king-addons-ai-settings', KING_ADDONS_URL . 'includes/admin/css/ai-settings.css', ['king-addons-admin'], // Depend on admin base styles KING_ADDONS_VERSION ); wp_enqueue_script( 'king-addons-ai-settings', KING_ADDONS_URL . 'includes/admin/js/ai-settings.js', ['jquery'], KING_ADDONS_VERSION, true ); wp_localize_script( 'king-addons-ai-settings', 'KingAddonsAiSettings', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('king_addons_ai_refresh_models_nonce'), 'refreshing_text' => esc_html__('Refreshing...', 'king-addons'), 'refreshed_text' => esc_html__('List updated.', 'king-addons'), 'error_text' => esc_html__('Error updating list.', 'king-addons'), ] ); } /** * Renders description for OpenAI API Settings section. * * @return void */ public function renderAiOpenaiSection(): void { echo '

' . esc_html__('Enter your OpenAI API key and select the model for AI features.', 'king-addons') . '

'; } /** * Renders the OpenAI API Key input field. * * @return void */ public function renderAiApiKeyField(): void { $options = get_option('king_addons_ai_options', []); $api_key = $options['openai_api_key'] ?? ''; printf( '', esc_attr($api_key) ); echo '

'; printf( esc_html__('Get your API key from the %1$sOpenAI Platform%2$s. Saving the key will attempt to fetch the available models.', 'king-addons'), '', '' ); echo '

'; echo '
'; echo '' . esc_html__('Important:', 'king-addons') . ' '; echo esc_html__('You must top up your OpenAI account balance by at least $5 for the API to work. Free accounts are not supported.', 'king-addons'); echo '
'; echo '
'; echo '' . esc_html__('Info:', 'king-addons') . ' '; echo esc_html__('With GPT-4o-mini, a $5 balance is enough for roughly 130,000–150,000 text generations.', 'king-addons'); echo '
'; echo '
'; echo '' . esc_html__('Useful OpenAI Links:', 'king-addons') . ''; echo '
'; } /** * Renders the model selection dropdown field with refresh button. * * @return void */ public function renderAiModelField(): void { $options = get_option('king_addons_ai_options', []); $selected = $options['openai_model'] ?? ''; $models = $this->getAiAvailableModels(); printf( ''; echo ''; echo ''; echo ''; echo '

' . esc_html__('Select an available OpenAI model capable of processing text. We recommend GPT-4o-mini or GPT-4.1-nano for best results. The list of models is cached indefinitely until manually refreshed.', 'king-addons') . '

'; } /** * Fetches the list of OpenAI models via API. * * @param string|null $api_key API key to use. * @return array|\WP_Error Model list or error. */ private function fetchAiOpenaiModels(?string $api_key) { if (empty($api_key)) { return new \WP_Error('missing_key', esc_html__('API key is required to fetch models.', 'king-addons')); } $endpoint = 'https://api.openai.com/v1/models'; $response = wp_remote_get($endpoint, [ 'headers' => ['Authorization' => 'Bearer ' . $api_key], 'timeout' => 20, ]); if (is_wp_error($response)) { return $response; } $code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if ($code !== 200 || empty($data['data']) || !is_array($data['data'])) { $message = $data['error']['message'] ?? esc_html__('Invalid response from API.', 'king-addons'); return new \WP_Error('api_error', $message, ['status' => $code]); } $list = []; foreach ($data['data'] as $model) { if (isset($model['id'])) { $list[$model['id']] = $model['id']; } } ksort($list); if (empty($list)) { return new \WP_Error('no_models', esc_html__('No models found via API.', 'king-addons')); } return $list; } /** * Retrieves available models, using cache if possible. * * @return array Model list. */ private function getAiAvailableModels(): array { $cached = get_transient('king_addons_ai_models_cache'); if (false !== $cached && is_array($cached)) { return $cached; } $options = get_option('king_addons_ai_options', []); $api_key = $options['openai_api_key'] ?? null; $fetched = $this->fetchAiOpenaiModels($api_key); if (!is_wp_error($fetched)) { set_transient('king_addons_ai_models_cache', $fetched, 0); return $fetched; } return ['gpt-4o-mini' => 'GPT-4o-mini', 'gpt-4.1-nano' => 'GPT-4.1-nano']; } /** * Handles AJAX request to refresh model list. * * @return void */ public function handleAiRefreshModels(): void { check_ajax_referer('king_addons_ai_refresh_models_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403); } $options = get_option('king_addons_ai_options', []); $api_key = $options['openai_api_key'] ?? null; if (empty($api_key)) { wp_send_json_error(['message' => esc_html__('API key is not set.', 'king-addons')], 400); } $this->clearAiModelsCache(); $models = $this->fetchAiOpenaiModels($api_key); if (is_wp_error($models)) { wp_send_json_error(['message' => $models->get_error_message()], 500); } if (empty($models)) { wp_send_json_error(['message' => esc_html__('No models returned by API.', 'king-addons')], 500); } set_transient('king_addons_ai_models_cache', $models, 0); wp_send_json_success(['models' => $models]); } /** * AJAX handler to generate text using OpenAI. * * @return void */ public function handleAiGenerateText(): void { check_ajax_referer('king_addons_ai_generate_nonce', 'nonce'); if (! current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403); } $field_name = sanitize_text_field($_POST['field_name'] ?? ''); // Accept 'prompt' parameter (new) but fall back to 'value' parameter (old) for backwards compatibility $prompt = isset($_POST['prompt']) ? sanitize_textarea_field($_POST['prompt']) : sanitize_textarea_field($_POST['value'] ?? ''); // Get editor type if provided $editor_type = sanitize_text_field($_POST['editor_type'] ?? 'text'); $options = get_option('king_addons_ai_options', []); $api_key = $options['openai_api_key'] ?? ''; $model = $options['openai_model'] ?? ''; if (empty($api_key) || empty($model)) { wp_send_json_error(['message' => esc_html__('API key or model not set.', 'king-addons')], 400); } if (empty($prompt)) { wp_send_json_error(['message' => esc_html__('Please provide a prompt.', 'king-addons')], 400); } // Check daily token limit $daily_limit = isset($options['daily_token_limit']) ? intval($options['daily_token_limit']) : self::DEFAULT_DAILY_TOKEN_LIMIT; $current_usage = $this->getAiDailyUsage(); if ($daily_limit > 0 && $current_usage >= $daily_limit) { wp_send_json_error([ 'message' => esc_html__('Daily token limit reached. Please try again tomorrow or increase the limit in AI Settings.', 'king-addons') ], 429); } // System instruction based on editor type $system_instruction = 'You are a helpful content assistant. Provide concise, well-written content based on the user\'s request.'; // Enhanced instruction for WYSIWYG editor if ($editor_type === 'wysiwyg') { $system_instruction = 'You are a helpful content assistant for a rich text editor. Provide content with proper HTML formatting. Use

tags for paragraphs with appropriate spacing between them. If relevant, use other HTML formatting like , ,