load_options(); $this->llm_service = new LLM_Service(); add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'register_settings')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); add_action('wp_ajax_test_api_connection', array($this, 'ajax_test_api_connection')); add_action('wp_ajax_regenerate_excerpt', array($this, 'ajax_regenerate_excerpt')); add_action('wp_ajax_bulk_generate_excerpts', array($this, 'ajax_bulk_generate_excerpts')); add_action('wp_ajax_get_bulk_progress', array($this, 'ajax_get_bulk_progress')); add_action('wp_ajax_reset_bulk_progress', array($this, 'ajax_reset_bulk_progress')); add_action('wp_ajax_restart_bulk_process', array($this, 'ajax_restart_bulk_process')); add_action('wp_ajax_get_excerpt_status', array($this, 'ajax_get_excerpt_status')); add_action('wp_ajax_debug_test', array($this, 'ajax_debug_test')); add_action('wp_ajax_generate_excerpt_preview', array($this, 'ajax_generate_excerpt_preview')); add_action('wp_ajax_save_excerpt_manual', array($this, 'ajax_save_excerpt_manual')); add_action('wp_ajax_generate_keywords_preview', array($this, 'ajax_generate_keywords_preview')); add_action('wp_ajax_save_keywords_manual', array($this, 'ajax_save_keywords_manual')); // SEO integration AJAX handlers add_action('wp_ajax_get_seo_status', array($this, 'ajax_get_seo_status')); add_action('wp_ajax_bulk_seo_process', array($this, 'ajax_bulk_seo_process')); add_action('wp_ajax_get_seo_bulk_progress', array($this, 'ajax_get_seo_bulk_progress')); add_action('wp_ajax_debug_seo_bulk', array($this, 'ajax_debug_seo_bulk')); add_action('wp_ajax_force_seo_batch', array($this, 'ajax_force_seo_batch')); // Force regeneration AJAX handlers add_action('wp_ajax_force_regenerate_excerpts', array($this, 'ajax_force_regenerate_excerpts')); add_action('wp_ajax_force_regenerate_seo', array($this, 'ajax_force_regenerate_seo')); // Post editor meta box add_action('add_meta_boxes', array($this, 'add_excerpt_meta_box')); add_action('save_post', array($this, 'save_excerpt_meta_box'), 5, 2); } /** * Load options (allows for refresh). */ private function load_options() { $defaults = array( 'is_active' => false, 'admin_only' => false, 'llm_provider' => 'claude', 'claude_model' => 'claude-haiku-4-5', 'openai_model' => 'gpt-5-mini', 'gemini_model' => 'gemini-1.5-flash', 'api_key' => '', 'excerpt_type' => 'seo', 'max_length' => 300, 'min_content_length' => 100, 'featured_image' => false, 'featured_size' => 'thumbnail', 'featured_align' => 'alignleft', 'search_featured' => false, 'more_link_text' => '', 'skip_manual' => false, 'fallback_enabled' => true, 'background_processing' => true, 'immediate_threshold' => 1000, 'batch_size' => 5, 'batch_delay' => 300, // SEO Integration options 'seo_integration_enabled' => true, 'auto_assign_categories' => true, 'auto_assign_tags' => true, 'max_auto_tags' => 8, 'create_new_categories' => false, 'seo_focus_keyword' => true, 'taxonomy_confidence_threshold' => 0.7, ); $this->options = wp_parse_args(get_option('ai_smart_excerpt_options', array()), $defaults); } /** * Add admin menu. */ public function add_admin_menu() { add_options_page( esc_html__('AI Smart Excerpt Settings', 'ai-smart-excerpt'), esc_html__('AI Smart Excerpt', 'ai-smart-excerpt'), 'manage_options', 'ai-smart-excerpt', array($this, 'admin_page') ); } /** * Register plugin settings. */ public function register_settings() { register_setting( 'ai_smart_excerpt_options', 'ai_smart_excerpt_options', array($this, 'validate_options') ); // General Settings Section add_settings_section( 'general_settings', esc_html__('General Settings', 'ai-smart-excerpt'), array($this, 'general_settings_callback'), 'ai-smart-excerpt' ); // AI Provider Settings Section add_settings_section( 'ai_settings', esc_html__('AI Provider Settings', 'ai-smart-excerpt'), array($this, 'ai_settings_callback'), 'ai-smart-excerpt' ); // Excerpt Settings Section add_settings_section( 'excerpt_settings', esc_html__('Excerpt Settings', 'ai-smart-excerpt'), array($this, 'excerpt_settings_callback'), 'ai-smart-excerpt' ); // SEO Integration Settings Section add_settings_section( 'seo_settings', esc_html__('SEO Integration', 'ai-smart-excerpt'), array($this, 'seo_settings_callback'), 'ai-smart-excerpt' ); // Display Settings Section add_settings_section( 'display_settings', esc_html__('Display Settings', 'ai-smart-excerpt'), array($this, 'display_settings_callback'), 'ai-smart-excerpt' ); // Advanced Settings Section add_settings_section( 'advanced_settings', esc_html__('Advanced Settings', 'ai-smart-excerpt'), array($this, 'advanced_settings_callback'), 'ai-smart-excerpt' ); $this->add_settings_fields(); } /** * Add settings fields. */ private function add_settings_fields() { // General Settings add_settings_field( 'is_active', esc_html__('Enable Plugin', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'general_settings', array('field' => 'is_active', 'description' => esc_html__('Enable AI-powered excerpt generation', 'ai-smart-excerpt')) ); add_settings_field( 'admin_only', esc_html__('Admin Only Mode', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'general_settings', array('field' => 'admin_only', 'description' => esc_html__('Only generate excerpts for admin users (for testing)', 'ai-smart-excerpt')) ); // AI Provider Settings add_settings_field( 'llm_provider', esc_html__('AI Provider', 'ai-smart-excerpt'), array($this, 'select_field'), 'ai-smart-excerpt', 'ai_settings', array( 'field' => 'llm_provider', 'options' => LLM_Service::get_providers(), 'description' => esc_html__('Choose your preferred AI provider', 'ai-smart-excerpt') ) ); add_settings_field( 'api_key', esc_html__('API Key', 'ai-smart-excerpt'), array($this, 'password_field'), 'ai-smart-excerpt', 'ai_settings', array('field' => 'api_key', 'description' => esc_html__('Enter your API key for the selected provider', 'ai-smart-excerpt')) ); // Excerpt Settings add_settings_field( 'excerpt_type', esc_html__('Excerpt Type', 'ai-smart-excerpt'), array($this, 'select_field'), 'ai-smart-excerpt', 'excerpt_settings', array( 'field' => 'excerpt_type', 'options' => array( 'seo' => esc_html__('SEO Optimized', 'ai-smart-excerpt'), 'social' => esc_html__('Social Media Optimized', 'ai-smart-excerpt'), 'summary' => esc_html__('Summary', 'ai-smart-excerpt'), 'teaser' => esc_html__('Engaging Teaser', 'ai-smart-excerpt') ), 'description' => esc_html__('Choose the type of excerpt to generate', 'ai-smart-excerpt') ) ); add_settings_field( 'max_length', esc_html__('Maximum Length', 'ai-smart-excerpt'), array($this, 'number_field'), 'ai-smart-excerpt', 'excerpt_settings', array( 'field' => 'max_length', 'min' => 50, 'max' => 500, 'description' => esc_html__('Maximum excerpt length in characters', 'ai-smart-excerpt') ) ); add_settings_field( 'min_content_length', esc_html__('Minimum Content Length', 'ai-smart-excerpt'), array($this, 'number_field'), 'ai-smart-excerpt', 'excerpt_settings', array( 'field' => 'min_content_length', 'min' => 50, 'max' => 1000, 'description' => esc_html__('Minimum post content length to process (shorter posts will be skipped)', 'ai-smart-excerpt') ) ); // Display Settings add_settings_field( 'featured_image', esc_html__('Show Featured Image', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'display_settings', array('field' => 'featured_image', 'description' => esc_html__('Display featured image with excerpt', 'ai-smart-excerpt')) ); add_settings_field( 'featured_size', esc_html__('Image Size', 'ai-smart-excerpt'), array($this, 'select_field'), 'ai-smart-excerpt', 'display_settings', array( 'field' => 'featured_size', 'options' => array( 'thumbnail' => esc_html__('Thumbnail', 'ai-smart-excerpt'), 'medium' => esc_html__('Medium', 'ai-smart-excerpt'), 'large' => esc_html__('Large', 'ai-smart-excerpt'), 'full' => esc_html__('Full Size', 'ai-smart-excerpt') ), 'description' => esc_html__('Size of the featured image to display', 'ai-smart-excerpt') ) ); add_settings_field( 'featured_align', esc_html__('Image Alignment', 'ai-smart-excerpt'), array($this, 'select_field'), 'ai-smart-excerpt', 'display_settings', array( 'field' => 'featured_align', 'options' => array( 'alignleft' => esc_html__('Left', 'ai-smart-excerpt'), 'alignright' => esc_html__('Right', 'ai-smart-excerpt'), 'aligncenter' => esc_html__('Center', 'ai-smart-excerpt') ), 'description' => esc_html__('Alignment of the featured image', 'ai-smart-excerpt') ) ); add_settings_field( 'search_featured', esc_html__('Search for Images', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'display_settings', array('field' => 'search_featured', 'description' => esc_html__('If no featured image, search for first image in content', 'ai-smart-excerpt')) ); add_settings_field( 'more_link_text', esc_html__('Read More Text', 'ai-smart-excerpt'), array($this, 'text_field'), 'ai-smart-excerpt', 'display_settings', array('field' => 'more_link_text', 'description' => esc_html__('Text for "read more" link (leave empty to disable)', 'ai-smart-excerpt')) ); // Advanced Settings add_settings_field( 'skip_manual', esc_html__('Override Manual Excerpts', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'advanced_settings', array('field' => 'skip_manual', 'description' => esc_html__('Generate AI excerpts even when manual excerpts exist', 'ai-smart-excerpt')) ); add_settings_field( 'fallback_enabled', esc_html__('Enable Fallback', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'advanced_settings', array('field' => 'fallback_enabled', 'description' => esc_html__('Use simple excerpt generation if AI fails', 'ai-smart-excerpt')) ); add_settings_field( 'auto_excerpt_on_schedule', esc_html__('Auto-Generate Excerpt on Save', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'advanced_settings', array('field' => 'auto_excerpt_on_schedule', 'description' => esc_html__('Automatically generate excerpt when a post is saved and excerpt is empty', 'ai-smart-excerpt')) ); add_settings_field( 'auto_seo_on_schedule', esc_html__('Auto-Generate SEO on Save', 'ai-smart-excerpt'), array($this, 'checkbox_field'), 'ai-smart-excerpt', 'advanced_settings', array('field' => 'auto_seo_on_schedule', 'description' => esc_html__('Automatically generate SEO content (categories, tags, keywords) when a post is saved', 'ai-smart-excerpt')) ); } /** * Section callbacks. */ public function general_settings_callback() { echo '

' . esc_html__('Basic plugin activation and testing settings.', 'ai-smart-excerpt') . '

'; } public function ai_settings_callback() { echo '

' . esc_html__('Configure your AI provider and API credentials.', 'ai-smart-excerpt') . '

'; } public function excerpt_settings_callback() { echo '

' . esc_html__('Customize how excerpts are generated.', 'ai-smart-excerpt') . '

'; } public function display_settings_callback() { echo '

' . esc_html__('Control how excerpts are displayed on your site.', 'ai-smart-excerpt') . '

'; } public function advanced_settings_callback() { echo '

' . esc_html__('Advanced configuration options.', 'ai-smart-excerpt') . '

'; } /** * Field callbacks. */ public function checkbox_field($args) { $field = $args['field']; $value = isset($this->options[$field]) ? $this->options[$field] : false; $description = isset($args['description']) ? $args['description'] : ''; echo ''; } public function text_field($args) { $field = $args['field']; $value = isset($this->options[$field]) ? $this->options[$field] : ''; $description = isset($args['description']) ? $args['description'] : ''; echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } } public function password_field($args) { $field = $args['field']; $value = isset($this->options[$field]) ? $this->options[$field] : ''; $description = isset($args['description']) ? $args['description'] : ''; echo ''; if ($field === 'api_key') { echo ' '; echo '
'; } if ($description) { echo '

' . esc_html($description) . '

'; } } public function number_field($args) { $field = $args['field']; $value = isset($this->options[$field]) ? $this->options[$field] : ''; $min = isset($args['min']) ? $args['min'] : ''; $max = isset($args['max']) ? $args['max'] : ''; $description = isset($args['description']) ? $args['description'] : ''; echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } } public function select_field($args) { $field = $args['field']; $value = isset($this->options[$field]) ? $this->options[$field] : ''; $options = $args['options']; $description = isset($args['description']) ? $args['description'] : ''; echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } } /** * Validate options. * * @param array $input Raw input data. * @return array Validated options. */ public function validate_options($input) { // Start with existing options to preserve values not in current form $existing_options = get_option('ai_smart_excerpt_options', array()); $validated = $existing_options; // Determine which form section was submitted $form_section = isset($input['ai_smart_excerpt_form_section']) ? $input['ai_smart_excerpt_form_section'] : 'unknown'; // Define which fields belong to each form section $section_fields = array( 'general' => array('is_active', 'admin_only'), 'ai_provider' => array('llm_provider', 'api_key', 'claude_model', 'openai_model', 'gemini_model'), 'excerpts' => array('excerpt_type', 'max_length', 'min_content_length'), 'seo' => array('seo_integration_enabled', 'seo_focus_keyword', 'auto_assign_categories', 'create_new_categories', 'auto_assign_tags', 'max_auto_tags', 'taxonomy_confidence_threshold', 'keywords_enabled', 'keyword_type', 'max_keywords', 'keywords_as_tags', 'keywords_display_meta'), 'display' => array('featured_image', 'featured_size', 'featured_align', 'search_featured', 'more_link_text'), 'advanced' => array('skip_manual', 'fallback_enabled', 'auto_excerpt_on_schedule', 'auto_seo_on_schedule') ); // Boolean fields $boolean_fields = array('is_active', 'admin_only', 'featured_image', 'search_featured', 'skip_manual', 'fallback_enabled', 'seo_integration_enabled', 'seo_focus_keyword', 'auto_assign_categories', 'create_new_categories', 'auto_assign_tags', 'keywords_enabled', 'keywords_as_tags', 'keywords_display_meta', 'auto_excerpt_on_schedule', 'auto_seo_on_schedule'); // Only update fields that belong to the submitted form section if (isset($section_fields[$form_section])) { $fields_to_update = $section_fields[$form_section]; foreach ($fields_to_update as $field) { if (in_array($field, $boolean_fields)) { // Handle boolean fields - now they will always be present due to hidden field $validated[$field] = isset($input[$field]) && $input[$field] === '1'; } else { // Handle other fields if present in input if (isset($input[$field])) { $validated[$field] = $input[$field]; // Will be sanitized below } } } } else { // Fallback: if no section specified, handle all boolean fields for backward compatibility foreach ($boolean_fields as $field) { if (array_key_exists($field, $input)) { $validated[$field] = isset($input[$field]) && $input[$field] === '1'; } } // Also handle non-boolean fields if they exist in input $all_fields = array_merge( $section_fields['general'] ?? array(), $section_fields['ai_provider'] ?? array(), $section_fields['excerpts'] ?? array(), $section_fields['keywords'] ?? array(), $section_fields['display'] ?? array(), $section_fields['advanced'] ?? array() ); foreach ($all_fields as $field) { if (!in_array($field, $boolean_fields) && isset($input[$field])) { $validated[$field] = $input[$field]; } } } // Apply proper sanitization to validated fields // Text fields $text_fields = array('api_key', 'more_link_text'); foreach ($text_fields as $field) { if (isset($validated[$field]) && $validated[$field] !== $existing_options[$field]) { $validated[$field] = sanitize_text_field($validated[$field]); } } // Select fields with specific options $select_fields = array( 'llm_provider' => array_keys(LLM_Service::get_providers()), 'claude_model' => array_keys(LLM_Service::get_models_for_provider('claude')), 'openai_model' => array_keys(LLM_Service::get_models_for_provider('openai')), 'gemini_model' => array_keys(LLM_Service::get_models_for_provider('gemini')), 'excerpt_type' => array('seo', 'social', 'summary', 'teaser'), 'keyword_type' => array('seo', 'tags', 'focus'), 'featured_size' => array('thumbnail', 'medium', 'large', 'full'), 'featured_align' => array('alignleft', 'alignright', 'aligncenter') ); foreach ($select_fields as $field => $valid_options) { if (isset($validated[$field])) { if (!in_array($validated[$field], $valid_options)) { // Invalid value, revert to existing or default if (isset($existing_options[$field]) && in_array($existing_options[$field], $valid_options)) { $validated[$field] = $existing_options[$field]; } else { $validated[$field] = $valid_options[0]; // Use first valid option as default } } // Value is valid, keep it as is } else if (isset($input[$field]) && in_array($input[$field], $valid_options)) { // Field wasn't processed by section logic but exists in input and is valid $validated[$field] = $input[$field]; } } // Number fields $number_fields = array( 'max_length' => array('min' => 50, 'max' => 500), 'min_content_length' => array('min' => 50, 'max' => 1000), 'max_keywords' => array('min' => 1, 'max' => 20), ); foreach ($number_fields as $field => $limits) { if (isset($validated[$field])) { if ($validated[$field] !== $existing_options[$field]) { $value = intval($validated[$field]); $validated[$field] = max($limits['min'], min($limits['max'], $value)); } } else if (isset($input[$field])) { // Field wasn't processed by section logic but exists in input $value = intval($input[$field]); $validated[$field] = max($limits['min'], min($limits['max'], $value)); } } // Validate API key format if (!empty($validated['api_key']) && !empty($validated['llm_provider'])) { if (!$this->llm_service->validate_api_key_format($validated['llm_provider'], $validated['api_key'])) { add_settings_error( 'ai_smart_excerpt_options', 'invalid_api_key', esc_html__('API key format is invalid for the selected provider.', 'ai-smart-excerpt') ); } } return $validated; } /** * Admin page display. */ public function admin_page() { if (!current_user_can('manage_options')) { return; } // Reload options to ensure we have the latest values $this->load_options(); $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'overview'; ?>

render_overview_tab(); break; case 'ai-provider': $this->render_ai_provider_tab(); break; case 'excerpts': $this->render_excerpt_settings_tab(); break; case 'seo': $this->render_seo_settings_tab(); break; case 'display': $this->render_display_settings_tab(); break; case 'advanced': $this->render_advanced_settings_tab(); break; case 'status': $this->render_status_tab(); break; case 'recommendations': $this->render_recommendations_tab(); break; default: $this->render_overview_tab(); break; } ?>
admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('ai_smart_excerpt_ajax'), 'has_api_key' => !empty($this->options['api_key']), 'strings' => array( 'testing' => esc_html__('Testing...', 'ai-smart-excerpt'), 'success' => esc_html__('Success!', 'ai-smart-excerpt'), 'error' => esc_html__('Error occurred', 'ai-smart-excerpt'), 'generating' => esc_html__('Generating...', 'ai-smart-excerpt'), 'generated' => esc_html__('Generated successfully', 'ai-smart-excerpt'), 'test_connection' => esc_html__('Test Connection', 'ai-smart-excerpt'), 'please_enter_api_key' => esc_html__('Please enter an API key first.', 'ai-smart-excerpt'), 'bulk_confirm' => esc_html__('This will generate AI excerpts for all posts that don\'t have them yet. This process runs in the background and may take some time. Continue?', 'ai-smart-excerpt'), 'starting' => esc_html__('Starting...', 'ai-smart-excerpt'), 'initializing' => esc_html__('Initializing...', 'ai-smart-excerpt'), 'calculating' => esc_html__('Calculating...', 'ai-smart-excerpt'), 'processing' => esc_html__('Processing...', 'ai-smart-excerpt'), 'restarting' => esc_html__('Restarting...', 'ai-smart-excerpt'), 'resetting' => esc_html__('Resetting...', 'ai-smart-excerpt'), 'debugging' => esc_html__('Debugging...', 'ai-smart-excerpt'), 'forcing' => esc_html__('Forcing...', 'ai-smart-excerpt'), 'generate_all_missing' => esc_html__('Generate All Missing Excerpts', 'ai-smart-excerpt'), 'restart_processing' => esc_html__('Restart Processing', 'ai-smart-excerpt'), 'reset_bulk_progress' => esc_html__('Reset Bulk Progress', 'ai-smart-excerpt'), 'debug_test' => esc_html__('Debug Test', 'ai-smart-excerpt'), 'bulk_seo_processing' => esc_html__('Bulk SEO Processing', 'ai-smart-excerpt'), 'debug_seo_bulk' => esc_html__('Debug SEO Bulk', 'ai-smart-excerpt'), 'force_next_batch' => esc_html__('Force Next Batch', 'ai-smart-excerpt'), 'processing_seo' => esc_html__('Processing SEO...', 'ai-smart-excerpt'), 'seo_completed' => esc_html__('SEO Completed', 'ai-smart-excerpt'), 'total_posts' => esc_html__('Total Posts:', 'ai-smart-excerpt'), 'with_ai_excerpts' => esc_html__('With AI Excerpts:', 'ai-smart-excerpt'), 'without_excerpts' => esc_html__('Without Excerpts:', 'ai-smart-excerpt'), 'progress' => esc_html__('Progress:', 'ai-smart-excerpt'), 'pending_jobs' => esc_html__('Pending Jobs:', 'ai-smart-excerpt'), 'status' => esc_html__('Status:', 'ai-smart-excerpt'), 'bulk_processing_active' => esc_html__('Bulk processing active', 'ai-smart-excerpt'), 'error_loading_status' => esc_html__('Error loading status:', 'ai-smart-excerpt'), 'failed_load_status' => esc_html__('Failed to load excerpt status. Please check your server logs.', 'ai-smart-excerpt'), 'processing_stalled' => esc_html__('Bulk processing appears stalled. Last update was %d minutes ago. Try "Restart Processing" button.', 'ai-smart-excerpt'), 'processing_stopped' => esc_html__('Bulk processing stopped unexpectedly. You can start a new bulk operation or check the Status & Tools tab for more information.', 'ai-smart-excerpt'), 'seo_processing_stopped' => esc_html__('SEO processing stopped unexpectedly.', 'ai-smart-excerpt'), 'seo_plugin' => esc_html__('SEO Plugin:', 'ai-smart-excerpt'), 'ai_categories' => esc_html__('AI Categories:', 'ai-smart-excerpt'), 'ai_tags' => esc_html__('AI Tags:', 'ai-smart-excerpt'), 'focus_keywords' => esc_html__('Focus Keywords:', 'ai-smart-excerpt'), 'seo_integration' => esc_html__('SEO Integration:', 'ai-smart-excerpt'), 'error_loading_seo_status' => esc_html__('Error loading SEO status:', 'ai-smart-excerpt'), 'failed_load_seo_status' => esc_html__('Failed to load SEO status. AJAX Error:', 'ai-smart-excerpt'), 'restart_confirm' => esc_html__('This will restart the current bulk processing operation. Continue?', 'ai-smart-excerpt'), 'reset_confirm' => esc_html__('This will reset any stuck bulk processing operations. Are you sure?', 'ai-smart-excerpt'), 'seo_confirm' => esc_html__('This will add missing tags and focus keywords to all your posts. This process runs in the background and may take some time. Continue?', 'ai-smart-excerpt'), 'starting_seo' => esc_html__('Starting SEO Processing...', 'ai-smart-excerpt'), 'debug_result' => esc_html__('Debug test result: ', 'ai-smart-excerpt'), 'debug_failed' => esc_html__('Debug test failed: ', 'ai-smart-excerpt'), 'debug_error' => esc_html__('Debug Error: ', 'ai-smart-excerpt'), 'force_error' => esc_html__('Force Error: ', 'ai-smart-excerpt'), 'force_failed' => esc_html__('Force failed: ', 'ai-smart-excerpt'), 'batch_forced' => esc_html__('Batch forced. Progress: ', 'ai-smart-excerpt'), 'unknown_error' => esc_html__('Unknown error occurred', 'ai-smart-excerpt'), 'failed_update_bulk' => esc_html__('Failed to update bulk progress', 'ai-smart-excerpt'), 'failed_update_seo_bulk' => esc_html__('Failed to update SEO bulk progress', 'ai-smart-excerpt'), 'could_not_check_bulk' => esc_html__('Could not check existing bulk progress', 'ai-smart-excerpt'), 'could_not_check_seo_bulk' => esc_html__('Could not check existing SEO bulk progress', 'ai-smart-excerpt'), 'bulk_completed' => esc_html__('Bulk generation completed! Processed: ', 'ai-smart-excerpt'), 'seo_bulk_completed' => esc_html__('Bulk SEO processing completed! Processed: ', 'ai-smart-excerpt'), 'failed_generate_excerpt' => esc_html__('Failed to generate excerpt', 'ai-smart-excerpt'), 'last_generated' => esc_html__('Last generated: ', 'ai-smart-excerpt'), 'copied' => esc_html__('Copied!', 'ai-smart-excerpt'), 'copy' => esc_html__('Copy', 'ai-smart-excerpt'), 'choose_excerpt_type' => esc_html__('Choose the type of excerpt to generate', 'ai-smart-excerpt'), 'max_length_chars' => esc_html__('Maximum length in characters', 'ai-smart-excerpt'), 'failed_generate_keywords' => esc_html__('Failed to generate keywords', 'ai-smart-excerpt'), 'choose_keyword_type' => esc_html__('Choose the type of keywords to generate', 'ai-smart-excerpt'), 'max_num_keywords' => esc_html__('Maximum number of keywords', 'ai-smart-excerpt'), 'api_key_claude' => esc_html__('Enter your Claude API key (starts with sk-ant-)', 'ai-smart-excerpt'), 'api_key_openai' => esc_html__('Enter your OpenAI API key (starts with sk-)', 'ai-smart-excerpt'), 'api_key_gemini' => esc_html__('Enter your Google Gemini API key', 'ai-smart-excerpt') ) ); // Add post ID for post editor pages if (in_array($hook, array('post.php', 'post-new.php'))) { global $post; if ($post) { $localize_data['post_id'] = $post->ID; } } wp_localize_script('ai-smart-excerpt-admin', 'aiSmartExcerpt', $localize_data); } /** * AJAX handler for testing API connection. */ public function ajax_test_api_connection() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $provider = sanitize_text_field($_POST['provider']); $api_key = sanitize_text_field($_POST['api_key']); $result = $this->llm_service->test_api_connection($provider, $api_key); wp_send_json($result); } /** * AJAX handler for regenerating single excerpt. */ public function ajax_regenerate_excerpt() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $post_id = intval($_POST['post_id']); $post = get_post($post_id); if (!$post) { wp_send_json_error(esc_html__('Post not found', 'ai-smart-excerpt')); } $plugin = AI_Smart_Excerpt::get_instance(); $plugin->generate_excerpt_background($post_id, true); $excerpt = get_post_meta($post_id, '_ai_smart_excerpt', true); if ($excerpt) { wp_send_json_success(array( 'message' => esc_html__('Excerpt regenerated successfully', 'ai-smart-excerpt'), 'excerpt' => wp_trim_words($excerpt, 20) )); } else { wp_send_json_error(esc_html__('Failed to generate excerpt', 'ai-smart-excerpt')); } } /** * AJAX handler for bulk generating excerpts. */ public function ajax_bulk_generate_excerpts() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } // Check if bulk processing is already running $bulk_progress = get_option('ai_smart_excerpt_bulk_progress', array('status' => 'idle')); if ($bulk_progress['status'] === 'running') { wp_send_json_error(esc_html__('Bulk processing is already running', 'ai-smart-excerpt')); return; } // Reset progress for new bulk operation update_option('ai_smart_excerpt_bulk_progress', array( 'status' => 'idle', 'total' => 0, 'processed' => 0, 'current_batch' => 0, 'errors' => 0, 'started_at' => 0 )); // Schedule bulk processing to start immediately wp_schedule_single_event(time() + 5, 'ai_smart_excerpt_bulk_process'); // Also schedule a backup trigger in case the first one fails wp_schedule_single_event(time() + 60, 'ai_smart_excerpt_bulk_process_backup'); wp_send_json_success(array( 'message' => esc_html__('Bulk excerpt generation started. Progress will be shown below.', 'ai-smart-excerpt') )); } /** * AJAX handler for getting bulk generation progress. */ public function ajax_get_bulk_progress() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $bulk_progress = get_option('ai_smart_excerpt_bulk_progress', array( 'status' => 'idle', 'total' => 0, 'processed' => 0, 'current_batch' => 0, 'errors' => 0, 'started_at' => 0 )); // Calculate additional progress info $percentage = 0; if ($bulk_progress['total'] > 0) { $percentage = round(($bulk_progress['processed'] / $bulk_progress['total']) * 100, 1); } $remaining = max(0, $bulk_progress['total'] - $bulk_progress['processed']); // Calculate estimated time remaining $estimated_time = ''; if ($bulk_progress['status'] === 'running' && $bulk_progress['processed'] > 0) { $elapsed = time() - $bulk_progress['started_at']; $rate = $bulk_progress['processed'] / $elapsed; // posts per second if ($rate > 0 && $remaining > 0) { $estimated_seconds = $remaining / $rate; if ($estimated_seconds < 60) { // translators: %d is the number of seconds remaining $estimated_time = sprintf(esc_html__('%d seconds', 'ai-smart-excerpt'), ceil($estimated_seconds)); } else { // translators: %d is the number of minutes remaining $estimated_time = sprintf(esc_html__('%d minutes', 'ai-smart-excerpt'), ceil($estimated_seconds / 60)); } } } // Check if bulk processing is scheduled $is_scheduled = wp_next_scheduled('ai_smart_excerpt_bulk_process') ? true : false; wp_send_json_success(array( 'status' => $bulk_progress['status'], 'total' => $bulk_progress['total'], 'processed' => $bulk_progress['processed'], 'remaining' => $remaining, 'percentage' => $percentage, 'current_batch' => $bulk_progress['current_batch'], 'errors' => $bulk_progress['errors'], 'estimated_time' => $estimated_time, 'is_scheduled' => $is_scheduled, 'last_updated' => isset($bulk_progress['last_updated']) ? $bulk_progress['last_updated'] : 0, 'completion_reason' => isset($bulk_progress['completion_reason']) ? $bulk_progress['completion_reason'] : null, 'last_error' => isset($bulk_progress['last_error']) ? $bulk_progress['last_error'] : null, 'last_post_time' => isset($bulk_progress['last_post_time']) ? $bulk_progress['last_post_time'] : null, 'started_at' => isset($bulk_progress['started_at']) ? $bulk_progress['started_at'] : 0 )); } /** * AJAX handler for resetting bulk generation progress. */ public function ajax_reset_bulk_progress() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } // Clear all scheduled bulk processing events wp_clear_scheduled_hook('ai_smart_excerpt_bulk_process'); // Reset progress delete_option('ai_smart_excerpt_bulk_progress'); wp_send_json_success(array( 'message' => esc_html__('Bulk processing has been reset. You can now start a new bulk generation.', 'ai-smart-excerpt') )); } /** * AJAX handler for restarting bulk processing. */ public function ajax_restart_bulk_process() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } // Clear any existing scheduled events wp_clear_scheduled_hook('ai_smart_excerpt_bulk_process'); wp_clear_scheduled_hook('ai_smart_excerpt_bulk_process_backup'); // Schedule immediate restart wp_schedule_single_event(time() + 5, 'ai_smart_excerpt_bulk_process'); wp_schedule_single_event(time() + 60, 'ai_smart_excerpt_bulk_process_backup'); wp_send_json_success(array( 'message' => esc_html__('Bulk processing restarted. Monitoring will resume.', 'ai-smart-excerpt') )); } /** * AJAX handler for getting excerpt status. */ public function ajax_get_excerpt_status() { // Skip nonce check temporarily for debugging // check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_send_json_error('Insufficient permissions'); return; } try { // Simple database queries $posts = get_posts(array( 'post_type' => array('post', 'page'), 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids' )); $total_posts = count($posts); // Count posts with AI excerpts $with_excerpts = 0; foreach ($posts as $post_id) { $excerpt = get_post_meta($post_id, '_ai_smart_excerpt', true); if (!empty($excerpt)) { $with_excerpts++; } } $without_excerpts = $total_posts - $with_excerpts; $percentage = $total_posts > 0 ? round(($with_excerpts / $total_posts) * 100, 1) : 0; wp_send_json_success(array( 'total_posts' => $total_posts, 'with_excerpts' => $with_excerpts, 'without_excerpts' => $without_excerpts, 'pending_jobs' => 0, 'bulk_running' => false, 'percentage' => $percentage )); } catch (Exception $e) { wp_send_json_error('Exception: ' . $e->getMessage()); } } /** * Simple AJAX debug test. */ public function ajax_debug_test() { wp_send_json_success(array( 'message' => 'AJAX is working!', 'timestamp' => current_time('mysql'), 'user_id' => get_current_user_id() )); } /** * Add excerpt meta box to post editor. */ public function add_excerpt_meta_box() { $post_types = array('post', 'page'); foreach ($post_types as $post_type) { add_meta_box( 'ai-smart-excerpt-meta-box', esc_html__('AI Smart Excerpt & Keywords', 'ai-smart-excerpt'), array($this, 'excerpt_meta_box_callback'), $post_type, 'normal', 'high' ); } } /** * Meta box callback for excerpt generation. * * @param WP_Post $post Current post object. */ public function excerpt_meta_box_callback($post) { // Add nonce for security wp_nonce_field('ai_smart_excerpt_meta_box', 'ai_smart_excerpt_meta_box_nonce'); // Get current AI excerpt $ai_excerpt = get_post_meta($post->ID, '_ai_smart_excerpt', true); $generated_at = get_post_meta($post->ID, '_ai_excerpt_generated_at', true); $is_active = $this->options['is_active']; // Get current AI keywords $ai_keywords = get_post_meta($post->ID, '_ai_smart_keywords', true); $keywords_generated_at = get_post_meta($post->ID, '_ai_keywords_generated_at', true); $keywords_enabled_setting = $this->options['keywords_enabled']; $keywords_display_meta = $this->options['keywords_display_meta']; ?>

prepare_content($post); if (empty($content)) { wp_send_json_error(esc_html__('Post content is too short or empty', 'ai-smart-excerpt')); } // Generate excerpt $llm_service = new LLM_Service(); $excerpt = $llm_service->generate_excerpt( $content, $post->post_title, $excerpt_type, $max_length ); if ($excerpt) { wp_send_json_success(array( 'excerpt' => $excerpt, 'type' => $excerpt_type, 'length' => strlen($excerpt) )); } else { wp_send_json_error(esc_html__('Failed to generate excerpt', 'ai-smart-excerpt')); } } /** * AJAX handler for saving manual excerpt. */ public function ajax_save_excerpt_manual() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $post_id = intval($_POST['post_id']); $excerpt = sanitize_textarea_field($_POST['excerpt']); if (!get_post($post_id)) { wp_send_json_error(esc_html__('Post not found', 'ai-smart-excerpt')); } if (!empty($excerpt)) { update_post_meta($post_id, '_ai_smart_excerpt', $excerpt); update_post_meta($post_id, '_ai_excerpt_generated_at', current_time('timestamp')); wp_send_json_success(array( 'message' => esc_html__('Excerpt saved successfully', 'ai-smart-excerpt') )); } else { delete_post_meta($post_id, '_ai_smart_excerpt'); delete_post_meta($post_id, '_ai_excerpt_generated_at'); wp_send_json_success(array( 'message' => esc_html__('Excerpt removed', 'ai-smart-excerpt') )); } } /** * AJAX handler for generating keyword preview. */ public function ajax_generate_keywords_preview() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $post_id = intval($_POST['post_id']); $keyword_type = sanitize_text_field($_POST['keyword_type']); $max_keywords = intval($_POST['max_keywords']); $post = get_post($post_id); if (!$post) { wp_send_json_error(esc_html__('Post not found', 'ai-smart-excerpt')); } // Get post content $plugin = AI_Smart_Excerpt::get_instance(); $content = $plugin->prepare_content($post); if (empty($content)) { wp_send_json_error(esc_html__('Post content is too short or empty', 'ai-smart-excerpt')); } // Generate keywords based on type $llm_service = new LLM_Service(); $keywords = null; try { if ($keyword_type === 'tags') { $keywords = $llm_service->generate_tags($content, $post->post_title, $max_keywords); } elseif ($keyword_type === 'focus_keyword') { $focus_keyword = $llm_service->generate_focus_keyword($content, $post->post_title); $keywords = $focus_keyword ? array($focus_keyword) : null; } else { // Default to tags if type is unknown $keywords = $llm_service->generate_tags($content, $post->post_title, $max_keywords); } } catch (Exception $e) { error_log('AI Smart Excerpt: Keyword generation error: ' . $e->getMessage()); wp_send_json_error('API error: ' . $e->getMessage()); } if ($keywords && is_array($keywords)) { wp_send_json_success(array( 'keywords' => $keywords, 'type' => $keyword_type, 'count' => count($keywords), 'keywords_text' => implode(', ', $keywords) )); } else { wp_send_json_error(esc_html__('Failed to generate keywords', 'ai-smart-excerpt')); } } /** * AJAX handler for saving manual keywords. */ public function ajax_save_keywords_manual() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_die(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); } $post_id = intval($_POST['post_id']); $keywords_text = sanitize_textarea_field($_POST['keywords']); if (!get_post($post_id)) { wp_send_json_error(esc_html__('Post not found', 'ai-smart-excerpt')); } if (!empty($keywords_text)) { // Parse keywords from comma-separated text $keywords = array_map('trim', explode(',', $keywords_text)); $keywords = array_filter($keywords, function($keyword) { return !empty($keyword); }); // Save to plugin meta fields update_post_meta($post_id, '_ai_smart_keywords', $keywords); update_post_meta($post_id, '_ai_keywords_generated_at', current_time('timestamp')); // IMPORTANT: Also create WordPress tags (same as bulk process) $clean_tags = array(); foreach ($keywords as $tag) { $clean_tag = trim($tag); if (!empty($clean_tag) && strlen($clean_tag) <= 50) { $clean_tags[] = $clean_tag; } } $tags_created = 0; if (!empty($clean_tags)) { wp_set_post_tags($post_id, $clean_tags, true); // Append, don't replace $tags_created = count($clean_tags); } // IMPORTANT: Also integrate with Yoast SEO $ai_plugin = AI_Smart_Excerpt::get_instance(); $yoast_success = $ai_plugin->set_seo_focus_keywords($post_id, $keywords); wp_send_json_success(array( 'message' => esc_html__('Keywords saved successfully', 'ai-smart-excerpt'), 'count' => count($keywords), 'wordpress_tags_created' => $tags_created, 'yoast_integration' => $yoast_success ? 'SUCCESS' : 'FAILED' )); } else { delete_post_meta($post_id, '_ai_smart_keywords'); delete_post_meta($post_id, '_ai_keywords_generated_at'); wp_send_json_success(array( 'message' => esc_html__('Keywords removed', 'ai-smart-excerpt') )); } } /** * Render Overview & Settings tab. */ private function render_overview_tab() { // Ensure we have the latest options $this->load_options(); ?>

1

2

3

4

render_field_row('is_active', esc_html__('Enable Plugin', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Activate AI excerpt generation for your site', 'ai-smart-excerpt'))); $this->render_field_row('admin_only', esc_html__('Admin Only Mode', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Only show AI excerpts to logged-in administrators (for testing)', 'ai-smart-excerpt'))); ?>

load_options(); ?>

render_field_row('llm_provider', esc_html__('AI Provider', 'ai-smart-excerpt'), 'select', array( 'options' => array( 'claude' => esc_html__('Claude (Anthropic)', 'ai-smart-excerpt'), 'openai' => esc_html__('OpenAI (ChatGPT)', 'ai-smart-excerpt'), 'gemini' => esc_html__('Google Gemini', 'ai-smart-excerpt') ), 'description' => esc_html__('Choose your preferred AI service provider', 'ai-smart-excerpt') )); $this->render_field_row('api_key', esc_html__('API Key', 'ai-smart-excerpt'), 'password', array( 'description' => esc_html__('Enter your API key from the selected provider', 'ai-smart-excerpt') )); // Model selection for Claude $this->render_field_row('claude_model', esc_html__('Claude Model', 'ai-smart-excerpt'), 'select', array( 'options' => LLM_Service::get_models_for_provider('claude'), 'description' => esc_html__('Select the Claude model to use', 'ai-smart-excerpt'), 'container_class' => 'claude-model-row' )); // Model selection for OpenAI $this->render_field_row('openai_model', esc_html__('OpenAI Model', 'ai-smart-excerpt'), 'select', array( 'options' => LLM_Service::get_models_for_provider('openai'), 'description' => esc_html__('Select the OpenAI model to use', 'ai-smart-excerpt'), 'container_class' => 'openai-model-row' )); // Model selection for Gemini $this->render_field_row('gemini_model', esc_html__('Gemini Model', 'ai-smart-excerpt'), 'select', array( 'options' => LLM_Service::get_models_for_provider('gemini'), 'description' => esc_html__('Select the Gemini model to use', 'ai-smart-excerpt'), 'container_class' => 'gemini-model-row' )); ?>

load_options(); ?>

render_field_row('excerpt_type', esc_html__('Excerpt Type', 'ai-smart-excerpt'), 'select', array( 'options' => array( 'seo' => esc_html__('SEO Optimized', 'ai-smart-excerpt'), 'social' => esc_html__('Social Media', 'ai-smart-excerpt'), 'summary' => esc_html__('Summary', 'ai-smart-excerpt'), 'teaser' => esc_html__('Engaging Teaser', 'ai-smart-excerpt') ), 'description' => esc_html__('Choose the style and purpose of generated excerpts', 'ai-smart-excerpt') )); $this->render_field_row('max_length', esc_html__('Maximum Length', 'ai-smart-excerpt'), 'number', array( 'min' => 50, 'max' => 500, 'description' => esc_html__('Maximum excerpt length in characters', 'ai-smart-excerpt') )); $this->render_field_row('min_content_length', esc_html__('Minimum Content Length', 'ai-smart-excerpt'), 'number', array( 'min' => 50, 'max' => 1000, 'description' => esc_html__('Minimum post content length to process (shorter posts will be skipped)', 'ai-smart-excerpt') )); ?>

load_options(); $ai_plugin = AI_Smart_Excerpt::get_instance(); $seo_plugin = $ai_plugin->get_seo_plugin_integration(); ?>

render_field_row('seo_integration_enabled', esc_html__('Enable SEO Integration', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Enable AI-powered SEO features including taxonomy assignment and focus keywords', 'ai-smart-excerpt'))); $this->render_field_row('seo_focus_keyword', esc_html__('Generate Focus Keywords', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically generate and set focus keywords for SEO plugins', 'ai-smart-excerpt'))); ?>

render_field_row('auto_assign_categories', esc_html__('Auto-Assign Categories', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically assign relevant categories based on content analysis', 'ai-smart-excerpt'))); $this->render_field_row('create_new_categories', esc_html__('Create New Categories', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Allow creation of new categories when no suitable existing category is found', 'ai-smart-excerpt'))); $this->render_field_row('auto_assign_tags', esc_html__('Auto-Assign Tags', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically generate and assign relevant tags', 'ai-smart-excerpt'))); $this->render_field_row('max_auto_tags', esc_html__('Maximum Auto Tags', 'ai-smart-excerpt'), 'number', array( 'description' => esc_html__('Maximum number of tags to generate per post', 'ai-smart-excerpt'), 'min' => 1, 'max' => 15, 'default' => 8 )); $this->render_field_row('taxonomy_confidence_threshold', esc_html__('Assignment Confidence', 'ai-smart-excerpt'), 'select', array( 'description' => esc_html__('Minimum confidence level for automatic taxonomy assignment', 'ai-smart-excerpt'), 'options' => array( '0.5' => esc_html__('Low (50%)', 'ai-smart-excerpt'), '0.6' => esc_html__('Medium-Low (60%)', 'ai-smart-excerpt'), '0.7' => esc_html__('Medium (70%) - Recommended', 'ai-smart-excerpt'), '0.8' => esc_html__('High (80%)', 'ai-smart-excerpt'), '0.9' => esc_html__('Very High (90%)', 'ai-smart-excerpt') ), 'default' => '0.7' )); ?>

render_field_row('keywords_enabled', esc_html__('Enable Keyword Generation', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Enable AI-powered keyword generation', 'ai-smart-excerpt'))); $this->render_field_row('keyword_type', esc_html__('Keyword Type', 'ai-smart-excerpt'), 'select', array( 'description' => esc_html__('Choose the type of keywords to generate', 'ai-smart-excerpt'), 'options' => array( 'seo' => esc_html__('SEO Keywords', 'ai-smart-excerpt'), 'tags' => esc_html__('Tags', 'ai-smart-excerpt'), 'focus' => esc_html__('Focus Keywords', 'ai-smart-excerpt') ), 'default' => 'seo' )); $this->render_field_row('max_keywords', esc_html__('Maximum Keywords', 'ai-smart-excerpt'), 'number', array( 'description' => esc_html__('Maximum number of keywords to generate', 'ai-smart-excerpt'), 'min' => 1, 'max' => 20, 'default' => 10 )); $this->render_field_row('keywords_as_tags', esc_html__('Keywords as Tags', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically assign generated keywords as post tags', 'ai-smart-excerpt'))); $this->render_field_row('keywords_display_meta', esc_html__('Display in Meta Box', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Show keyword generation options in the post meta box', 'ai-smart-excerpt'))); ?>

load_options(); ?>

render_field_row('featured_image', esc_html__('Show Featured Image', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Display featured image with excerpt', 'ai-smart-excerpt'))); $this->render_field_row('featured_size', esc_html__('Image Size', 'ai-smart-excerpt'), 'select', array( 'options' => array( 'thumbnail' => esc_html__('Thumbnail', 'ai-smart-excerpt'), 'medium' => esc_html__('Medium', 'ai-smart-excerpt'), 'large' => esc_html__('Large', 'ai-smart-excerpt'), 'full' => esc_html__('Full Size', 'ai-smart-excerpt') ), 'description' => esc_html__('Size of the featured image to display', 'ai-smart-excerpt') )); $this->render_field_row('featured_align', esc_html__('Image Alignment', 'ai-smart-excerpt'), 'select', array( 'options' => array( 'alignleft' => esc_html__('Left', 'ai-smart-excerpt'), 'alignright' => esc_html__('Right', 'ai-smart-excerpt'), 'aligncenter' => esc_html__('Center', 'ai-smart-excerpt') ), 'description' => esc_html__('Alignment of the featured image', 'ai-smart-excerpt') )); $this->render_field_row('search_featured', esc_html__('Search for Images', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('If no featured image, search for first image in content', 'ai-smart-excerpt'))); ?>

render_field_row('more_link_text', esc_html__('Read More Text', 'ai-smart-excerpt'), 'text', array('description' => esc_html__('Text for "read more" link (leave empty to disable)', 'ai-smart-excerpt'))); ?>
load_options(); ?>

render_field_row('skip_manual', esc_html__('Override Manual Excerpts', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Generate AI excerpts even when manual excerpts exist', 'ai-smart-excerpt'))); $this->render_field_row('fallback_enabled', esc_html__('Enable Fallback', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Use simple excerpt generation if AI fails', 'ai-smart-excerpt'))); $this->render_field_row('auto_excerpt_on_schedule', esc_html__('Auto-Generate Excerpt on Save', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically generate excerpt when a post is saved and excerpt is empty', 'ai-smart-excerpt'))); $this->render_field_row('auto_seo_on_schedule', esc_html__('Auto-Generate SEO on Save', 'ai-smart-excerpt'), 'checkbox', array('description' => esc_html__('Automatically generate SEO content (categories, tags, keywords) when a post is saved', 'ai-smart-excerpt'))); ?>








render_feedback_status_card(); ?>
false, 'feedback_given' => false, 'successful_generations' => 0, 'bulk_completions' => 0, 'install_date' => time(), 'feedback_prompt_count' => 0 )); $days_used = ceil((time() - $feedback_options['install_date']) / DAY_IN_SECONDS); ?>

options[$field_name]) ? $this->options[$field_name] : ''; $description = isset($args['description']) ? $args['description'] : ''; $container_class = isset($args['container_class']) ? $args['container_class'] : ''; echo ''; echo ''; echo ''; switch ($type) { case 'checkbox': // Add hidden field to ensure unchecked value is sent echo ''; echo ''; break; case 'text': echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } break; case 'password': echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } break; case 'number': $min = isset($args['min']) ? $args['min'] : ''; $max = isset($args['max']) ? $args['max'] : ''; echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } break; case 'select': echo ''; if ($description) { echo '

' . esc_html($description) . '

'; } break; } echo ''; echo ''; } /** * AJAX handler for getting SEO status. */ public function ajax_get_seo_status() { // Skip nonce check like excerpt status (following existing pattern) // check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('edit_posts')) { wp_send_json_error('Insufficient permissions'); return; } try { $ai_plugin = AI_Smart_Excerpt::get_instance(); $stats = $ai_plugin->get_seo_impact_stats(); // Calculate percentages for better display $seo_percentage = 0; if ($stats['total_posts'] > 0) { $seo_percentage = round((($stats['posts_with_ai_categories'] + $stats['posts_with_ai_tags'] + $stats['focus_keywords_set']) / ($stats['total_posts'] * 3)) * 100, 1); } wp_send_json_success(array( 'seo_plugin' => $stats['seo_plugin_detected'] ?: 'None detected', 'total_posts' => (int) $stats['total_posts'], 'posts_with_categories' => (int) $stats['posts_with_ai_categories'], 'posts_with_tags' => (int) $stats['posts_with_ai_tags'], 'focus_keywords_set' => (int) $stats['focus_keywords_set'], 'seo_percentage' => $seo_percentage )); } catch (Exception $e) { wp_send_json_error('Failed to load SEO status: ' . $e->getMessage()); } } /** * AJAX handler for bulk SEO processing. */ public function ajax_bulk_seo_process() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } try { $ai_plugin = AI_Smart_Excerpt::get_instance(); // Reset any existing progress delete_option('ai_smart_excerpt_seo_bulk_progress'); // Start the bulk SEO processing immediately and continue with scheduling $ai_plugin->process_bulk_seo(); // Schedule immediate follow-up processing wp_schedule_single_event(time() + 3, 'ai_smart_excerpt_seo_bulk_process'); // Get updated progress after initial processing $progress = get_option('ai_smart_excerpt_seo_bulk_progress', array('status' => 'idle')); wp_send_json_success(array( 'message' => 'Bulk SEO processing started successfully', 'status' => 'running', 'initial_progress' => $progress )); } catch (Exception $e) { wp_send_json_error('Failed to start bulk SEO processing: ' . $e->getMessage()); } } /** * AJAX handler for getting bulk SEO processing progress. */ public function ajax_get_seo_bulk_progress() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } try { $seo_bulk_progress = get_option('ai_smart_excerpt_seo_bulk_progress', array( 'status' => 'idle', 'total' => 0, 'processed' => 0, 'start_time' => 0, 'errors' => 0 )); $percentage = 0; $eta = ''; if ($seo_bulk_progress['total'] > 0) { $percentage = round(($seo_bulk_progress['processed'] / $seo_bulk_progress['total']) * 100, 1); // Calculate ETA if ($seo_bulk_progress['processed'] > 0 && $seo_bulk_progress['start_time'] > 0) { $elapsed = time() - $seo_bulk_progress['start_time']; $rate = $seo_bulk_progress['processed'] / $elapsed; $remaining = $seo_bulk_progress['total'] - $seo_bulk_progress['processed']; $eta_seconds = $rate > 0 ? $remaining / $rate : 0; $eta = $eta_seconds > 0 ? gmdate('i:s', $eta_seconds) : ''; } } wp_send_json_success(array( 'status' => $seo_bulk_progress['status'], 'total' => (int) $seo_bulk_progress['total'], 'processed' => (int) $seo_bulk_progress['processed'], 'percentage' => $percentage, 'eta' => $eta, 'errors' => (int) $seo_bulk_progress['errors'] )); } catch (Exception $e) { wp_send_json_error('Failed to get SEO bulk progress: ' . $e->getMessage()); } } /** * Debug AJAX handler for SEO bulk processing. */ public function ajax_debug_seo_bulk() { if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } try { // Get posts that need SEO processing $posts = get_posts(array( 'post_type' => array('post', 'page'), 'post_status' => 'publish', 'numberposts' => 5, // Just first 5 for debugging 'fields' => 'ids' )); $ai_plugin = AI_Smart_Excerpt::get_instance(); $debug_info = array(); foreach ($posts as $post_id) { $post = get_post($post_id); $tags = wp_get_post_tags($post_id); $needs_seo = $ai_plugin->post_needs_seo_processing($post_id); $debug_info[] = array( 'id' => $post_id, 'title' => $post->post_title, 'tags_count' => count($tags), 'needs_seo' => $needs_seo ); } $current_progress = get_option('ai_smart_excerpt_seo_bulk_progress', array('status' => 'idle')); // Test LLM connectivity with a simple call $llm_test_result = 'Not tested'; try { $ai_plugin = AI_Smart_Excerpt::get_instance(); $llm_service = new LLM_Service(); $test_tags = $llm_service->generate_tags('This is a test article about health and wellness.', 'Test Article', 3); $llm_test_result = $test_tags ? 'SUCCESS - Got ' . count($test_tags) . ' tags' : 'FAILED - No tags returned'; } catch (Exception $e) { $llm_test_result = 'ERROR: ' . $e->getMessage(); } wp_send_json_success(array( 'total_posts' => count($posts), 'debug_posts' => $debug_info, 'current_progress' => $current_progress, 'llm_connectivity_test' => $llm_test_result, 'yoast_integration_check' => $this->check_yoast_integration($posts[0]) )); } catch (Exception $e) { wp_send_json_error('Debug failed: ' . $e->getMessage()); } } /** * Check Yoast SEO integration for a specific post. */ private function check_yoast_integration($post_id) { if (empty($post_id)) { return 'No post ID provided'; } $result = array(); // Check if Yoast is active $result['yoast_active'] = defined('WPSEO_VERSION') ? 'YES (' . WPSEO_VERSION . ')' : 'NO'; // Check Yoast meta fields $result['focus_keyword'] = get_post_meta($post_id, '_yoast_wpseo_focuskw', true) ?: 'EMPTY'; $result['keyword_synonyms'] = get_post_meta($post_id, '_yoast_wpseo_keywordsynonyms', true) ?: 'EMPTY'; // Check our AI assignments $ai_assignments = get_post_meta($post_id, '_ai_seo_assignments', true); $result['ai_assignments'] = $ai_assignments ? $ai_assignments : 'NONE'; return $result; } /** * Force next SEO batch processing. */ public function ajax_force_seo_batch() { if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } try { $ai_plugin = AI_Smart_Excerpt::get_instance(); // Force process the next batch $ai_plugin->process_bulk_seo(); $progress = get_option('ai_smart_excerpt_seo_bulk_progress', array('status' => 'idle')); wp_send_json_success(array( 'message' => 'Forced next batch processing', 'progress' => $progress )); } catch (Exception $e) { wp_send_json_error('Failed to force batch: ' . $e->getMessage()); } } /** * AJAX handler for force regenerating all excerpts (skips all checks). */ public function ajax_force_regenerate_excerpts() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); return; } // Check if bulk processing is already running $bulk_progress = get_option('ai_smart_excerpt_bulk_progress', array('status' => 'idle')); if ($bulk_progress['status'] === 'running') { wp_send_json_error(esc_html__('Bulk processing is already running', 'ai-smart-excerpt')); return; } // Reset progress for new bulk operation update_option('ai_smart_excerpt_bulk_progress', array( 'status' => 'idle', 'total' => 0, 'processed' => 0, 'current_batch' => 0, 'errors' => 0, 'started_at' => 0, 'force_regenerate' => true // Flag to skip all checks )); // Schedule bulk processing to start immediately wp_schedule_single_event(time() + 5, 'ai_smart_excerpt_bulk_process'); // Also schedule a backup trigger in case the first one fails wp_schedule_single_event(time() + 60, 'ai_smart_excerpt_bulk_process_backup'); wp_send_json_success(array( 'message' => esc_html__('Force excerpt regeneration started. This will regenerate ALL excerpts.', 'ai-smart-excerpt') )); } /** * AJAX handler for force regenerating all SEO content (skips all checks). */ public function ajax_force_regenerate_seo() { check_ajax_referer('ai_smart_excerpt_ajax', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('Insufficient permissions', 'ai-smart-excerpt')); return; } // Check if SEO bulk processing is already running $seo_bulk_progress = get_option('ai_smart_excerpt_seo_bulk_progress', array('status' => 'idle')); if ($seo_bulk_progress['status'] === 'running') { wp_send_json_error(esc_html__('SEO bulk processing is already running', 'ai-smart-excerpt')); return; } // Reset SEO progress for new bulk operation with force flag update_option('ai_smart_excerpt_seo_bulk_progress', array( 'status' => 'idle', 'total' => 0, 'processed' => 0, 'current_batch' => 0, 'batch_size' => 3, 'start_time' => 0, 'last_updated' => 0, 'errors' => 0, 'force_regenerate' => true // Flag to skip all checks )); // Schedule SEO bulk processing to start immediately wp_schedule_single_event(time() + 5, 'ai_smart_excerpt_seo_bulk_process'); wp_send_json_success(array( 'message' => esc_html__('Force SEO regeneration started. This will regenerate ALL SEO content for all posts.', 'ai-smart-excerpt') )); } /** * Render recommendations tab. */ private function render_recommendations_tab() { ?>

Link Automator Pro

Save hours of manual work with automatic link generation!

Link Automator Pro automatically converts keywords into affiliate links, internal links, or any custom URLs across your entire website. Perfect for affiliate marketers, bloggers, and anyone looking to improve their internal linking structure.

🚀 Key Features:

  • ✅ Automatic keyword to link conversion
  • ✅ Perfect for affiliate marketing campaigns
  • ✅ Improves SEO with smart internal linking
  • ✅ Bulk management of hundreds of keywords
  • ✅ Safe processing that respects existing links
  • ✅ Performance optimized for large sites

💡 Perfect For:

Affiliate Marketing: Set "iPhone 15" to automatically link to your Amazon affiliate URL across all posts

SEO Improvement: Build topic clusters by linking related keywords to pillar pages

Content Monetization: Convert product mentions into commission-earning links

🔗 Visit WordPress.org Page
📥 Download Now - Free

Knowledge AI Bot

Transform your site into an intelligent knowledge hub!

Knowledge AI Bot adds an intelligent AI chatbot to your website, specifically designed to help visitors understand and engage with your content. Unlike generic chatbots, it focuses on your site's actual content, perfect for knowledge-based websites.

✨ Key Features:

  • 🤖 Multi-AI platform support (OpenAI, Claude, Gemini)
  • 🎯 Contextually aware responses based on your content
  • 📱 Fully customizable appearance and positioning
  • 🌍 Mobile-responsive design with smooth animations
  • 🛡️ Security-focused architecture with API encryption
  • ⚡ Performance optimized, loads asynchronously

🎯 Perfect For:

Knowledge Base Sites: Help users understand complex documentation

Educational Websites: Explain concepts and answer student questions

Content Blogs: Engage readers with deeper explanations and discussions

🤖 Visit WordPress.org Page
📥 Download Now - Free