0) { igny8_log_ai_event('AI Queue Processed', 'ai', 'queue_processing', 'success', 'AI queue tasks processed', "Tasks processed: $processed"); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $processed; $GLOBALS['igny8_cron_result_details'] = "Processed {$processed} AI queue tasks"; echo "Igny8 AI QUEUE HANDLER: Set global variables - processed_count: $processed, result_details: Processed {$processed} AI queue tasks
"; } else { igny8_log_ai_event('AI Queue Empty', 'ai', 'queue_processing', 'info', 'No tasks in queue', 'All tasks are already processed'); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "No AI queue tasks to process"; } } catch (Exception $e) { igny8_log_ai_event('AI Queue Error', 'ai', 'queue_processing', 'error', 'AI queue processing failed', $e->getMessage()); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: " . $e->getMessage(); } finally { // Always release the lock delete_transient($lock_key); } } /** * Auto Cluster Cron Handler * * Automatically clusters unmapped keywords with taxonomy safety. */ function igny8_auto_cluster_cron_handler() { // Suppress PHP warnings for model rates in cron context $old_error_reporting = error_reporting(); error_reporting($old_error_reporting & ~E_WARNING); echo "
"; echo "Igny8 CRON HANDLER: auto_cluster started
"; error_log("Igny8 CRON HANDLER: auto_cluster started"); // Check if automation is enabled via cron settings echo "Igny8 CRON HANDLER: Checking if automation is enabled
"; $cron_settings = get_option('igny8_cron_settings', []); $job_settings = $cron_settings['igny8_auto_cluster_cron'] ?? []; $auto_cluster_enabled = $job_settings['enabled'] ?? false; echo "Igny8 CRON HANDLER: auto_cluster_enabled = " . ($auto_cluster_enabled ? 'enabled' : 'disabled') . "
"; if (!$auto_cluster_enabled) { echo "Igny8 CRON HANDLER: Automation disabled, exiting
"; error_log("Igny8 CRON HANDLER: auto_cluster automation disabled"); echo "
"; return; } echo "Igny8 CRON HANDLER: Automation enabled, continuing
"; // Check if AI mode is enabled echo "Igny8 CRON HANDLER: Checking AI mode
"; $planner_mode = igny8_get_ai_setting('planner_mode', 'manual'); echo "Igny8 CRON HANDLER: planner_mode = " . $planner_mode . "
"; if ($planner_mode !== 'ai') { echo "Igny8 CRON HANDLER: AI mode disabled, exiting
"; error_log("Igny8 CRON HANDLER: AI mode disabled"); echo ""; return; } echo "Igny8 CRON HANDLER: AI mode enabled, continuing
"; // Check if sector is selected echo "Igny8 CRON HANDLER: Checking sector options
"; // Check if function exists first if (!function_exists('igny8_get_sector_options')) { echo "Igny8 CRON HANDLER: ERROR - igny8_get_sector_options function not found
"; igny8_log_ai_event('Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'Sector options function not available', 'Function igny8_get_sector_options not found'); echo "Igny8 CRON HANDLER: Exiting due to missing function
"; echo ""; return; } try { echo "Igny8 CRON HANDLER: Calling igny8_get_sector_options()
"; $sector_options = igny8_get_sector_options(); echo "Igny8 CRON HANDLER: sector_options count = " . count($sector_options) . "
"; echo "Igny8 CRON HANDLER: sector_options content: " . print_r($sector_options, true) . "
"; } catch (Exception $e) { echo "Igny8 CRON HANDLER: ERROR - Exception in igny8_get_sector_options: " . $e->getMessage() . "
"; igny8_log_ai_event('Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'Exception in sector options function', $e->getMessage()); echo "Igny8 CRON HANDLER: Exiting due to sector options exception
"; echo ""; return; } if (empty($sector_options)) { echo "Igny8 CRON HANDLER: No sector selected, checking alternatives
"; echo "Igny8 CRON HANDLER: Checking if igny8_get_sector_options function exists: " . (function_exists('igny8_get_sector_options') ? 'YES' : 'NO') . "
"; // Try to get sectors directly from database global $wpdb; $sectors = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}igny8_sectors WHERE status = 'active'"); echo "Igny8 CRON HANDLER: Direct DB query found " . count($sectors) . " active sectors
"; if (empty($sectors)) { echo "Igny8 CRON HANDLER: ERROR - No active sectors found, cannot proceed
"; igny8_log_ai_event('Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'No active sectors configured', 'Sector configuration required for clustering'); echo "Igny8 CRON HANDLER: Exiting due to missing sectors
"; echo ""; return; } else { echo "Igny8 CRON HANDLER: Found sectors in DB, using them
"; $sector_options = $sectors; } } echo "Igny8 CRON HANDLER: Sector selected, continuing
"; global $wpdb; echo "Igny8 CRON HANDLER: Database connection established
"; // Get unmapped keywords (use dynamic limit from settings) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_cluster_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Cluster Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } echo "Igny8 CRON HANDLER: Querying unmapped keywords (limit: $limit)
"; $query = "SELECT id, keyword FROM {$wpdb->prefix}igny8_keywords WHERE cluster_id IS NULL OR cluster_id = 0 LIMIT $limit"; echo "Igny8 CRON HANDLER: SQL Query: " . $query . "
"; $unmapped_keywords = $wpdb->get_results($query); echo "Igny8 CRON HANDLER: Found " . count($unmapped_keywords) . " unmapped keywords
"; if (!empty($unmapped_keywords)) { echo "Igny8 CRON HANDLER: Sample keywords:
"; foreach (array_slice($unmapped_keywords, 0, 3) as $keyword) { echo "- ID: " . $keyword->id . ", Keyword: " . $keyword->keyword . "
"; } } if (empty($unmapped_keywords)) { echo "Igny8 CRON HANDLER: ERROR - No unmapped keywords found
"; igny8_log_ai_event('Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'No unmapped keywords available for clustering', 'All keywords are already clustered or no keywords exist'); echo "Igny8 CRON HANDLER: Exiting due to no keywords to process
"; echo ""; return; } $keyword_ids = array_column($unmapped_keywords, 'id'); echo "Igny8 CRON HANDLER: Processing " . count($keyword_ids) . " keywords
"; // Log automation start echo "Igny8 CRON HANDLER: Logging automation start
"; igny8_log_ai_event('Auto Cluster Started', 'planner', 'auto_cluster', 'info', 'Starting automated clustering', 'Keywords: ' . count($keyword_ids)); // Direct clustering without AJAX simulation echo "Igny8 CRON HANDLER: Starting direct clustering process
"; // Set up user context for permissions if (!current_user_can('manage_options')) { // Get admin user for cron context $admin_users = get_users(['role' => 'administrator', 'number' => 1]); if (!empty($admin_users)) { wp_set_current_user($admin_users[0]->ID); echo "Igny8 CRON HANDLER: Set admin user context for permissions
"; } } // Get keywords data echo "Igny8 CRON HANDLER: Getting keywords data
"; $placeholders = implode(',', array_fill(0, count($keyword_ids), '%d')); $keywords = $wpdb->get_results($wpdb->prepare(" SELECT * FROM {$wpdb->prefix}igny8_keywords WHERE id IN ({$placeholders}) ", $keyword_ids)); echo "Igny8 CRON HANDLER: Found " . count($keywords) . " keywords in database
"; if (empty($keywords)) { echo "Igny8 CRON HANDLER: ERROR - No valid keywords found
"; igny8_log_ai_event('Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'No valid keywords found in database', 'Keyword IDs: ' . implode(',', $keyword_ids)); echo ""; return; } // Check if keywords already have clusters $keywords_with_clusters = array_filter($keywords, function($keyword) { return !empty($keyword->cluster_id) && $keyword->cluster_id > 0; }); if (!empty($keywords_with_clusters)) { echo "Igny8 CRON HANDLER: WARNING - Some keywords already have clusters
"; $keyword_names = array_column($keywords_with_clusters, 'keyword'); echo "Igny8 CRON HANDLER: Already clustered: " . implode(', ', array_slice($keyword_names, 0, 3)) . "
"; } // Get clustering prompt echo "Igny8 CRON HANDLER: Getting clustering prompt
"; $prompt_template = wp_unslash(igny8_get_ai_setting('clustering_prompt', igny8_get_default_clustering_prompt())); echo "Igny8 CRON HANDLER: Prompt length: " . strlen($prompt_template) . "
"; // Generate session ID for progress tracking $session_id = 'cron_clustering_' . time() . '_' . wp_generate_password(8, false); echo "Igny8 CRON HANDLER: Session ID: " . $session_id . "
"; // Log AI request initiation igny8_log_ai_event('Cron AI Request Initiated', 'planner', 'clustering', 'info', 'Starting cron AI clustering process', 'Keywords: ' . count($keyword_ids) . ', Session: ' . $session_id); // Process with AI echo "Igny8 CRON HANDLER: Calling AI processing
"; try { $ai_result = igny8_process_ai_request('clustering', $keywords, $prompt_template); echo "Igny8 CRON HANDLER: AI processing completed
"; if ($ai_result === false) { echo "Igny8 CRON HANDLER: ERROR - AI processing returned false
"; igny8_log_ai_event('Cron AI Processing Failed', 'planner', 'clustering', 'error', 'AI processing returned false', 'Check OpenAI API configuration'); echo ""; return; } if (!is_array($ai_result) || !isset($ai_result['clusters'])) { echo "Igny8 CRON HANDLER: ERROR - AI returned invalid result
"; igny8_log_ai_event('Cron AI Processing Failed', 'planner', 'clustering', 'error', 'AI returned invalid result', 'Result type: ' . gettype($ai_result)); echo ""; return; } echo "Igny8 CRON HANDLER: AI returned " . count($ai_result['clusters']) . " clusters
"; // Debug: Show AI response structure echo "Igny8 CRON HANDLER: AI Response Debug:
"; echo "Igny8 CRON HANDLER: Full AI result structure: " . json_encode($ai_result, JSON_PRETTY_PRINT) . "
"; igny8_log_ai_event('Cron AI Processing Complete', 'planner', 'clustering', 'success', 'AI returned ' . count($ai_result['clusters']) . ' clusters', 'Clusters: ' . json_encode(array_column($ai_result['clusters'], 'name'))); // Log database operations start echo "Igny8 CRON HANDLER: Starting database operations
"; igny8_log_ai_event('Cron Database Operations Started', 'planner', 'clustering', 'info', 'Starting to create clusters in database', 'Clusters to create: ' . count($ai_result['clusters'])); // Get sector options for assignment logic (same as AJAX handler) echo "Igny8 CRON HANDLER: Getting sector options for assignment
"; $sector_options = igny8_get_sector_options(); $sector_count = count($sector_options); echo "Igny8 CRON HANDLER: Found " . $sector_count . " sectors
"; // Create clusters in database (following AJAX handler process) $created_clusters = []; $clusters_created = 0; $keywords_processed = 0; foreach ($ai_result['clusters'] as $cluster_data) { echo "Igny8 CRON HANDLER: Processing cluster: " . $cluster_data['name'] . "
"; try { // Determine sector_id based on sector count (same logic as AJAX handler) $sector_id = 1; // Default fallback echo "Igny8 CRON HANDLER: Determining sector assignment
"; if ($sector_count == 1) { // Only 1 sector: assign all clusters to that sector $sector_id = $sector_options[0]['value']; echo "Igny8 CRON HANDLER: Single sector found, assigning to sector ID: " . $sector_id . "
"; } elseif ($sector_count > 1) { // Multiple sectors: use AI response sector assignment if (isset($cluster_data['sector']) && !empty($cluster_data['sector'])) { echo "Igny8 CRON HANDLER: AI provided sector: " . $cluster_data['sector'] . "
"; // Find sector ID by matching sector name from AI response foreach ($sector_options as $sector) { if (strtolower(trim($sector['label'])) === strtolower(trim($cluster_data['sector']))) { $sector_id = $sector['value']; echo "Igny8 CRON HANDLER: Matched sector ID: " . $sector_id . "
"; break; } } } // If no match found or no sector in AI response, use first sector as fallback if ($sector_id == 1 && !isset($cluster_data['sector'])) { $sector_id = $sector_options[0]['value']; echo "Igny8 CRON HANDLER: No sector match, using first sector ID: " . $sector_id . "
"; } } // Create cluster record in database (same as AJAX handler) echo "Igny8 CRON HANDLER: Creating cluster record in database
"; $result = $wpdb->insert( $wpdb->prefix . 'igny8_clusters', [ 'cluster_name' => sanitize_text_field($cluster_data['name']), 'sector_id' => $sector_id, 'status' => 'active', 'keyword_count' => count($cluster_data['keywords']), 'total_volume' => 0, 'avg_difficulty' => 0, 'mapped_pages_count' => 0, 'created_at' => current_time('mysql') ], ['%s', '%d', '%s', '%d', '%d', '%f', '%d', '%s'] ); if ($result) { $cluster_id = $wpdb->insert_id; $created_clusters[] = $cluster_id; $clusters_created++; echo "Igny8 CRON HANDLER: SUCCESS - Created cluster record with ID: " . $cluster_id . "
"; // Trigger taxonomy term creation for AI-generated cluster (same as AJAX handler) echo "Igny8 CRON HANDLER: Triggering taxonomy term creation
"; try { do_action('igny8_cluster_added', $cluster_id); echo "Igny8 CRON HANDLER: SUCCESS - Taxonomy term creation triggered
"; igny8_log_ai_event('Cron Cluster Taxonomy Triggered', 'planner', 'clustering', 'info', 'Triggered igny8_cluster_added action', "Cluster: {$cluster_data['name']} (ID: {$cluster_id})"); } catch (Exception $e) { echo "Igny8 CRON HANDLER: WARNING - Taxonomy term creation failed: " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Cluster Taxonomy Failed', 'planner', 'clustering', 'warning', 'Taxonomy term creation failed', "Cluster: {$cluster_data['name']} (ID: {$cluster_id}), Error: " . $e->getMessage()); } catch (Throwable $e) { echo "Igny8 CRON HANDLER: ERROR - Fatal error in taxonomy term creation: " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Cluster Taxonomy Fatal Error', 'planner', 'clustering', 'error', 'Fatal error in taxonomy term creation', "Cluster: {$cluster_data['name']} (ID: {$cluster_id}), Error: " . $e->getMessage()); } // Log cluster creation igny8_log_ai_event('Cron Cluster Created', 'planner', 'clustering', 'success', 'Cluster created successfully', "Cluster: {$cluster_data['name']} (ID: {$cluster_id})"); // Update keywords with cluster_id (same as AJAX handler) if (isset($cluster_data['keywords']) && is_array($cluster_data['keywords'])) { echo "Igny8 CRON HANDLER: Processing " . count($cluster_data['keywords']) . " keywords for this cluster
"; foreach ($cluster_data['keywords'] as $keyword_name) { echo "Igny8 CRON HANDLER: Looking for keyword: " . $keyword_name . "
"; $update_result = $wpdb->update( $wpdb->prefix . 'igny8_keywords', ['cluster_id' => $cluster_id], ['keyword' => $keyword_name], ['%d'], ['%s'] ); if ($update_result !== false) { $keywords_processed++; echo "Igny8 CRON HANDLER: SUCCESS - Assigned keyword '" . $keyword_name . "' to cluster ID " . $cluster_id . "
"; } else { echo "Igny8 CRON HANDLER: ERROR - Failed to update keyword '" . $keyword_name . "': " . $wpdb->last_error . "
"; } } // Log keyword updates igny8_log_ai_event('Cron Keywords Updated', 'planner', 'clustering', 'success', 'Keywords assigned to cluster', "Cluster: {$cluster_data['name']}, Keywords: " . count($cluster_data['keywords'])); } else { echo "Igny8 CRON HANDLER: WARNING - No keywords found in cluster data
"; } // Update cluster metrics (same as AJAX handler) echo "Igny8 CRON HANDLER: Updating cluster metrics
"; try { $metrics_result = igny8_update_cluster_metrics($cluster_id); if ($metrics_result) { echo "Igny8 CRON HANDLER: SUCCESS - Cluster metrics updated
"; igny8_log_ai_event('Cron Metrics Updated', 'planner', 'clustering', 'success', 'Cluster metrics calculated', "Cluster: {$cluster_data['name']}"); } else { echo "Igny8 CRON HANDLER: WARNING - Failed to update cluster metrics
"; igny8_log_ai_event('Cron Metrics Update Failed', 'planner', 'clustering', 'warning', 'Failed to update cluster metrics', "Cluster: {$cluster_data['name']}"); } } catch (Exception $e) { echo "Igny8 CRON HANDLER: ERROR - Exception during metrics update: " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Metrics Update Error', 'planner', 'clustering', 'error', 'Exception during metrics update', "Cluster: {$cluster_data['name']}, Error: " . $e->getMessage()); } } else { // Log cluster creation failure echo "Igny8 CRON HANDLER: ERROR - Failed to create cluster record: " . $wpdb->last_error . "
"; igny8_log_ai_event('Cron Cluster Creation Failed', 'planner', 'clustering', 'error', 'Failed to create cluster in database', "Cluster: {$cluster_data['name']}, Error: " . $wpdb->last_error); } } catch (Exception $e) { echo "Igny8 CRON HANDLER: ERROR - Exception processing cluster '{$cluster_data['name']}': " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Cluster Processing Error', 'planner', 'clustering', 'error', 'Exception processing cluster', "Cluster: {$cluster_data['name']}, Error: " . $e->getMessage()); echo "Igny8 CRON HANDLER: Continuing with next cluster
"; continue; } catch (Throwable $e) { echo "Igny8 CRON HANDLER: FATAL ERROR - Fatal error processing cluster '{$cluster_data['name']}': " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Cluster Processing Fatal Error', 'planner', 'clustering', 'error', 'Fatal error processing cluster', "Cluster: {$cluster_data['name']}, Error: " . $e->getMessage()); echo "Igny8 CRON HANDLER: Continuing with next cluster
"; continue; } } echo "Igny8 CRON HANDLER: Clustering completed successfully
"; echo "Igny8 CRON HANDLER: Database clusters created: " . $clusters_created . "
"; echo "Igny8 CRON HANDLER: Keywords processed: " . $keywords_processed . "
"; // Verify the results by checking database echo "Igny8 CRON HANDLER: Verifying results in database
"; $verify_clusters = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_clusters WHERE status = 'active'"); $verify_keywords = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords WHERE cluster_id IS NOT NULL AND cluster_id > 0"); $verify_taxonomy_terms = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->terms} t INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id WHERE tt.taxonomy = 'clusters'"); echo "Igny8 CRON HANDLER: Total active clusters in database: " . $verify_clusters . "
"; echo "Igny8 CRON HANDLER: Total clustered keywords in database: " . $verify_keywords . "
"; echo "Igny8 CRON HANDLER: Total taxonomy terms for clusters: " . $verify_taxonomy_terms . "
"; // Log completion with verification igny8_log_ai_event('Cron Auto Cluster Complete', 'planner', 'auto_cluster', 'success', 'Cron automated clustering completed', 'Database clusters: ' . $clusters_created . ', Keywords processed: ' . $keywords_processed . ', Verified clusters: ' . $verify_clusters . ', Verified keywords: ' . $verify_keywords . ', Verified taxonomy terms: ' . $verify_taxonomy_terms); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $keywords_processed; $GLOBALS['igny8_cron_result_details'] = "Processed {$keywords_processed} keywords, created {$clusters_created} clusters"; } catch (Exception $e) { echo "Igny8 CRON HANDLER: ERROR - Exception during AI processing: " . $e->getMessage() . "
"; igny8_log_ai_event('Cron Auto Cluster Failed', 'planner', 'auto_cluster', 'error', 'Exception during AI processing', $e->getMessage()); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: " . $e->getMessage(); echo ""; return; } echo "Igny8 CRON HANDLER: auto_cluster completed
"; echo ""; // Close the handler div // Restore error reporting error_reporting($old_error_reporting); } /** * Auto Generate Ideas Cron Handler * * Automatically generates ideas from clusters without ideas. */ function igny8_auto_generate_ideas_cron_handler() { // Suppress PHP warnings for model rates in cron context $old_error_reporting = error_reporting(); error_reporting($old_error_reporting & ~E_WARNING); echo "
"; echo "Igny8 CRON HANDLER: auto_generate_ideas started
"; error_log("Igny8 CRON HANDLER: auto_generate_ideas started"); // Check if automation is enabled via cron settings echo "Igny8 CRON HANDLER: Checking if automation is enabled
"; $cron_settings = get_option('igny8_cron_settings', []); $job_settings = $cron_settings['igny8_auto_generate_ideas_cron'] ?? []; $auto_ideas_enabled = $job_settings['enabled'] ?? false; echo "Igny8 CRON HANDLER: auto_ideas_enabled = " . ($auto_ideas_enabled ? 'enabled' : 'disabled') . "
"; if (!$auto_ideas_enabled) { echo "Igny8 CRON HANDLER: Automation disabled, exiting
"; error_log("Igny8 CRON HANDLER: auto_generate_ideas automation disabled"); echo "
"; return; } echo "Igny8 CRON HANDLER: Automation enabled, continuing
"; // Check if AI mode is enabled echo "Igny8 CRON HANDLER: Checking AI mode
"; $planner_mode = igny8_get_ai_setting('planner_mode', 'manual'); echo "Igny8 CRON HANDLER: planner_mode = " . $planner_mode . "
"; if ($planner_mode !== 'ai') { echo "Igny8 CRON HANDLER: AI mode disabled, exiting
"; error_log("Igny8 CRON HANDLER: AI mode disabled"); echo ""; return; } echo "Igny8 CRON HANDLER: AI mode enabled, continuing
"; global $wpdb; echo "Igny8 CRON HANDLER: Database connection established
"; // Get clusters without ideas (use dynamic limit from settings) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { error_log('Igny8 Auto Cluster Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Querying clusters without ideas (limit: $limit)
"; $clusters_without_ideas = $wpdb->get_results(" SELECT c.id FROM {$wpdb->prefix}igny8_clusters c LEFT JOIN {$wpdb->prefix}igny8_content_ideas i ON c.id = i.keyword_cluster_id WHERE i.id IS NULL LIMIT $limit "); echo "Igny8 CRON HANDLER: Found " . count($clusters_without_ideas) . " clusters without ideas
"; if (empty($clusters_without_ideas)) { echo "Igny8 CRON HANDLER: No clusters without ideas found, exiting
"; igny8_log_ai_event('Auto Generate Ideas Skipped', 'planner', 'auto_generate_ideas', 'info', 'No clusters without ideas found', 'All clusters already have ideas'); echo ""; return; } $cluster_ids = array_column($clusters_without_ideas, 'id'); echo "Igny8 CRON HANDLER: Processing " . count($cluster_ids) . " clusters
"; // Log automation start echo "Igny8 CRON HANDLER: Logging automation start
"; igny8_log_ai_event('Auto Generate Ideas Started', 'planner', 'auto_generate_ideas', 'info', 'Starting automated idea generation', 'Clusters: ' . count($cluster_ids)); // Set up user context for permissions echo "Igny8 CRON HANDLER: Setting up user context
"; if (!current_user_can('manage_options')) { // Get admin user for cron context $admin_users = get_users(['role' => 'administrator', 'number' => 1]); if (!empty($admin_users)) { wp_set_current_user($admin_users[0]->ID); echo "Igny8 CRON HANDLER: Set admin user context for permissions
"; } } // Simulate AJAX request to existing AI function echo "Igny8 CRON HANDLER: Starting AJAX simulation
"; $_POST['cluster_ids'] = json_encode($cluster_ids); $_POST['nonce'] = wp_create_nonce('igny8_planner_settings'); // Capture output to prevent JSON response in cron ob_start(); igny8_ajax_ai_generate_ideas(); $output = ob_get_clean(); echo "Igny8 CRON HANDLER: AJAX execution completed
"; // Parse JSON response $response = json_decode($output, true); echo "Igny8 CRON HANDLER: Response parsed, success = " . ($response['success'] ?? 'false') . "
"; if ($response && $response['success']) { $ideas_created = $response['data']['ideas_created'] ?? count($cluster_ids); echo "Igny8 CRON HANDLER: SUCCESS - Ideas generated: " . $ideas_created . "
"; igny8_log_ai_event('Auto Generate Ideas Complete', 'planner', 'auto_generate_ideas', 'success', 'Automated idea generation completed', 'Ideas created: ' . $ideas_created); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $ideas_created; $GLOBALS['igny8_cron_result_details'] = "Processed {$ideas_created} clusters, created {$ideas_created} ideas"; } else { $error_msg = $response['data']['message'] ?? 'Unknown error'; echo "Igny8 CRON HANDLER: ERROR - " . $error_msg . "
"; igny8_log_ai_event('Auto Generate Ideas Failed', 'planner', 'auto_generate_ideas', 'error', 'Automated idea generation failed', $error_msg); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: " . $error_msg; } echo "Igny8 CRON HANDLER: auto_generate_ideas completed
"; echo ""; // Close the handler div // Restore error reporting error_reporting($old_error_reporting); } /** * Auto Queue Cron Handler * * Automatically queues new ideas for content generation. */ function igny8_auto_queue_cron_handler() { // Suppress PHP warnings for model rates in cron context $old_error_reporting = error_reporting(); error_reporting($old_error_reporting & ~E_WARNING); echo "
"; echo "Igny8 CRON HANDLER: auto_queue started
"; error_log("Igny8 CRON HANDLER: auto_queue started"); // Check if automation is enabled via cron settings echo "Igny8 CRON HANDLER: Checking if automation is enabled
"; $cron_settings = get_option('igny8_cron_settings', []); $job_settings = $cron_settings['igny8_auto_queue_cron'] ?? []; $auto_queue_enabled = $job_settings['enabled'] ?? false; echo "Igny8 CRON HANDLER: auto_queue_enabled = " . ($auto_queue_enabled ? 'enabled' : 'disabled') . "
"; if (!$auto_queue_enabled) { echo "Igny8 CRON HANDLER: Automation disabled, exiting
"; error_log("Igny8 CRON HANDLER: auto_queue automation disabled"); echo "
"; return; } echo "Igny8 CRON HANDLER: Automation enabled, continuing
"; global $wpdb; echo "Igny8 CRON HANDLER: Database connection established
"; // Get new ideas (use dynamic limit from settings) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_queue_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Queue Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } echo "Igny8 CRON HANDLER: Querying new ideas (limit: $limit)
"; $new_ideas = $wpdb->get_results(" SELECT id FROM {$wpdb->prefix}igny8_content_ideas WHERE status = 'new' LIMIT $limit "); echo "Igny8 CRON HANDLER: Found " . count($new_ideas) . " new ideas
"; if (empty($new_ideas)) { echo "Igny8 CRON HANDLER: No new ideas found, exiting
"; igny8_log_ai_event('Auto Queue Skipped', 'planner', 'auto_queue', 'info', 'No new ideas found', 'All ideas are already queued'); echo ""; return; } $idea_ids = array_column($new_ideas, 'id'); echo "Igny8 CRON HANDLER: Processing " . count($idea_ids) . " ideas
"; // Log automation start echo "Igny8 CRON HANDLER: Logging automation start
"; igny8_log_ai_event('Auto Queue Started', 'planner', 'auto_queue', 'info', 'Starting automated queueing', 'Ideas: ' . count($idea_ids)); // Set up user context for permissions echo "Igny8 CRON HANDLER: Setting up user context
"; if (!current_user_can('edit_posts')) { // Get admin user for cron context $admin_users = get_users(['role' => 'administrator', 'number' => 1]); if (!empty($admin_users)) { wp_set_current_user($admin_users[0]->ID); echo "Igny8 CRON HANDLER: Set admin user context for permissions
"; } } // Skip AJAX simulation and go directly to function calls // (AJAX has issues in cron context with nonce verification) echo "Igny8 CRON HANDLER: Skipping AJAX simulation (cron context issues)
"; echo "Igny8 CRON HANDLER: Using direct function calls
"; $created = []; $skipped = []; $failed = []; foreach ($idea_ids as $idea_id) { echo "Igny8 CRON HANDLER: Processing idea ID: " . $idea_id . "
"; $result = igny8_create_task_from_idea($idea_id); if ($result['success']) { if (!empty($result['task_id'])) { $created[] = $result['task_id']; echo "Igny8 CRON HANDLER: SUCCESS - Created task ID: " . $result['task_id'] . "
"; } else { $skipped[] = $idea_id; echo "Igny8 CRON HANDLER: SKIPPED - " . $result['message'] . "
"; } } else { $failed[] = $idea_id; echo "Igny8 CRON HANDLER: FAILED - " . $result['message'] . "
"; } } // Update metrics for processed ideas foreach ($idea_ids as $idea_id) { igny8_update_idea_metrics($idea_id); } $created_count = count($created); $skipped_count = count($skipped); $failed_count = count($failed); echo "Igny8 CRON HANDLER: DIRECT SUCCESS - Tasks created: " . $created_count . ", Skipped: " . $skipped_count . ", Failed: " . $failed_count . "
"; igny8_log_ai_event('Auto Queue Complete', 'planner', 'auto_queue', 'success', 'Automated queueing completed (direct)', 'Tasks created: ' . $created_count . ', Skipped: ' . $skipped_count . ', Failed: ' . $failed_count); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $created_count; $GLOBALS['igny8_cron_result_details'] = "Processed {$created_count} ideas, created {$created_count} tasks"; echo "Igny8 CRON HANDLER: auto_queue completed
"; echo ""; // Close the handler div // Restore error reporting error_reporting($old_error_reporting); } /** * Auto Generate Content Cron Handler * * Automatically generates content from queued tasks. */ function igny8_auto_generate_content_cron_handler() { // Suppress PHP warnings for model rates in cron context $old_error_reporting = error_reporting(); error_reporting($old_error_reporting & ~E_WARNING); echo "
"; echo "Igny8 CRON HANDLER: auto_generate_content started
"; error_log("Igny8 CRON HANDLER: auto_generate_content started"); // Check if automation is enabled via cron settings echo "Igny8 CRON HANDLER: Checking if automation is enabled
"; $cron_settings = get_option('igny8_cron_settings', []); $job_settings = $cron_settings['igny8_auto_generate_content_cron'] ?? []; $auto_content_enabled = $job_settings['enabled'] ?? false; echo "Igny8 CRON HANDLER: auto_content_enabled = " . ($auto_content_enabled ? 'enabled' : 'disabled') . "
"; if (!$auto_content_enabled) { echo "Igny8 CRON HANDLER: Automation disabled, exiting
"; error_log("Igny8 CRON HANDLER: auto_generate_content automation disabled"); echo "
"; return; } echo "Igny8 CRON HANDLER: Automation enabled, continuing
"; // Check if AI mode is enabled echo "Igny8 CRON HANDLER: Checking AI mode
"; $writer_mode = igny8_get_ai_setting('writer_mode', 'manual'); echo "Igny8 CRON HANDLER: writer_mode = " . $writer_mode . "
"; if ($writer_mode !== 'ai') { echo "Igny8 CRON HANDLER: AI mode disabled, exiting
"; error_log("Igny8 CRON HANDLER: AI mode disabled"); echo ""; return; } echo "Igny8 CRON HANDLER: AI mode enabled, continuing
"; global $wpdb; echo "Igny8 CRON HANDLER: Database connection established
"; // Get queued tasks (use dynamic limit from settings) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_generate_content_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Generate Content Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } echo "Igny8 CRON HANDLER: Querying queued tasks (limit: $limit)
"; $queued_tasks = $wpdb->get_results(" SELECT id FROM {$wpdb->prefix}igny8_tasks WHERE status = 'queued' LIMIT $limit "); echo "Igny8 CRON HANDLER: Found " . count($queued_tasks) . " queued tasks
"; if (empty($queued_tasks)) { echo "Igny8 CRON HANDLER: No queued tasks found, exiting
"; igny8_log_ai_event('Auto Generate Content Skipped', 'writer', 'auto_generate_content', 'info', 'No queued tasks found', 'All tasks are already processed'); echo ""; return; } $task_ids = array_column($queued_tasks, 'id'); echo "Igny8 CRON HANDLER: Processing " . count($task_ids) . " tasks
"; // Log automation start echo "Igny8 CRON HANDLER: Logging automation start
"; igny8_log_ai_event('Auto Generate Content Started', 'writer', 'auto_generate_content', 'info', 'Starting automated content generation', 'Tasks: ' . count($task_ids)); // Set up user context for permissions echo "Igny8 CRON HANDLER: Setting up user context
"; if (!current_user_can('manage_options')) { // Get admin user for cron context $admin_users = get_users(['role' => 'administrator', 'number' => 1]); if (!empty($admin_users)) { wp_set_current_user($admin_users[0]->ID); echo "Igny8 CRON HANDLER: Set admin user context for permissions
"; } } $completed = 0; $failed = 0; foreach ($task_ids as $task_id) { echo "

📋 SECTION 1: PROCESSING TASK " . $task_id . "

"; echo "Igny8 CRON HANDLER: Processing task ID: " . $task_id . "
"; echo "Igny8 CRON HANDLER: Sending to AI for content generation...
"; // Log AI request initiation igny8_log_ai_event('AI Request Initiated', 'writer', 'auto_content_generation', 'info', 'Starting AI content generation', 'Task ID: ' . $task_id); // Get AI configuration for logging $api_key = get_option('igny8_api_key'); $model = get_option('igny8_model', 'gpt-4.1'); // Log AI request details echo "Igny8 CRON HANDLER: AI Request Details - Model: " . $model . "
"; echo "Igny8 CRON HANDLER: API Key Status: " . (empty($api_key) ? 'Missing' : 'Configured') . "
"; igny8_log_ai_event('AI Request Details', 'writer', 'auto_content_generation', 'info', 'AI request configuration', 'Model: ' . $model . ', API Key: ' . (empty($api_key) ? 'Missing' : 'Configured')); // Log the actual AI request being made echo "Igny8 CRON HANDLER: Making AI API call to OpenAI...
"; igny8_log_ai_event('AI API Call', 'writer', 'auto_content_generation', 'info', 'Calling OpenAI API', 'Model: ' . $model . ', Task: ' . $task_id); // Simulate AJAX request to content generation function $_POST['task_id'] = $task_id; $_POST['nonce'] = wp_create_nonce('igny8_writer_settings'); // Capture the response from AI call ob_start(); igny8_ajax_ai_generate_content(); $ai_response = ob_get_clean(); // Log AI response echo "Igny8 CRON HANDLER: AI Response received
"; echo "Igny8 CRON HANDLER: Response Length: " . strlen($ai_response) . " characters
"; igny8_log_ai_event('AI Response Received', 'writer', 'auto_content_generation', 'info', 'AI processing completed', 'Response captured for task: ' . $task_id); // Parse the AI response to extract success/error status $json_response = null; $response_status = 'unknown'; $error_message = ''; // Try multiple methods to extract JSON from response if (preg_match('/\{.*\}/s', $ai_response, $matches)) { $json_response = json_decode($matches[0], true); } // If no JSON found, try to detect response type from content if (!$json_response) { if (strpos($ai_response, 'success') !== false && strpos($ai_response, 'true') !== false) { $response_status = 'success'; echo "Igny8 CRON HANDLER: ✅ AI Response - SUCCESS (detected from content)
"; igny8_log_ai_event('AI Response Success', 'writer', 'auto_content_generation', 'success', 'AI content generation successful', 'Task ID: ' . $task_id); } elseif (strpos($ai_response, 'error') !== false || strpos($ai_response, 'failed') !== false) { $response_status = 'error'; $error_message = 'Error detected in response content'; echo "Igny8 CRON HANDLER: ❌ AI Response - FAILED (detected from content)
"; echo "Igny8 CRON HANDLER: Error: " . $error_message . "
"; igny8_log_ai_event('AI Response Failed', 'writer', 'auto_content_generation', 'error', 'AI content generation failed', 'Task ID: ' . $task_id . ', Error: ' . $error_message); } else { $response_status = 'unknown'; echo "Igny8 CRON HANDLER: ⚠️ AI Response - UNKNOWN FORMAT
"; igny8_log_ai_event('AI Response Unknown', 'writer', 'auto_content_generation', 'warning', 'AI response format not recognized', 'Task ID: ' . $task_id . ', Raw response: ' . substr($ai_response, 0, 200)); } } else { // JSON response found, parse it if (isset($json_response['success']) && $json_response['success']) { $response_status = 'success'; echo "Igny8 CRON HANDLER: ✅ AI Response - SUCCESS (JSON parsed)
"; igny8_log_ai_event('AI Response Success', 'writer', 'auto_content_generation', 'success', 'AI content generation successful', 'Task ID: ' . $task_id); } else { $response_status = 'error'; $error_message = $json_response['data']['message'] ?? $json_response['message'] ?? 'Unknown error'; echo "Igny8 CRON HANDLER: ❌ AI Response - FAILED (JSON parsed)
"; echo "Igny8 CRON HANDLER: Error: " . $error_message . "
"; igny8_log_ai_event('AI Response Failed', 'writer', 'auto_content_generation', 'error', 'AI content generation failed', 'Task ID: ' . $task_id . ', Error: ' . $error_message); } } // Log response details for debugging echo "Igny8 CRON HANDLER: Response Status: " . $response_status . "
"; echo "Igny8 CRON HANDLER: Response Length: " . strlen($ai_response) . " characters
"; igny8_log_ai_event('AI Response Analysis', 'writer', 'auto_content_generation', 'info', 'Response analysis completed', 'Status: ' . $response_status . ', Length: ' . strlen($ai_response) . ', Task: ' . $task_id); // Get the actual post ID from the most recently created post by this user $recent_posts = get_posts([ 'post_type' => 'post', 'post_status' => 'draft', 'author' => get_current_user_id(), 'numberposts' => 1, 'orderby' => 'date', 'order' => 'DESC' ]); $actual_post_id = !empty($recent_posts) ? $recent_posts[0]->ID : null; $response = ['success' => true, 'data' => ['post_id' => $actual_post_id]]; if ($response && $response['success']) { echo "Igny8 CRON HANDLER: ✅ AI Response received successfully
"; echo "

🔧 SECTION 2: PROCESSING AI RESPONSE & SAVING DATA

"; echo "Igny8 CRON HANDLER: Processing AI response data...
"; // The detailed field processing logs will come from igny8_create_post_from_ai_response function // which already has all the debugging output we added echo "

✅ SECTION 3: FINAL VERIFICATION & SUCCESS SUMMARY

"; if (!empty($response['data']['post_id'])) { $post_id = $response['data']['post_id']; echo "Igny8 CRON HANDLER: ✅ Post Created Successfully - ID: " . $post_id . "
"; echo "Igny8 CRON HANDLER: Checking fields for post ID: " . $post_id . "
"; // Verify all saved fields $meta_title = get_post_meta($post_id, '_igny8_meta_title', true); $meta_description = get_post_meta($post_id, '_igny8_meta_description', true); $primary_keywords = get_post_meta($post_id, '_igny8_primary_keywords', true); $secondary_keywords = get_post_meta($post_id, '_igny8_secondary_keywords', true); $word_count = get_post_meta($post_id, '_igny8_word_count', true); echo "Igny8 CRON HANDLER: ✅ Meta Title: " . (!empty($meta_title) ? 'Saved' : 'Missing') . "
"; echo "Igny8 CRON HANDLER: ✅ Meta Description: " . (!empty($meta_description) ? 'Saved' : 'Missing') . "
"; echo "Igny8 CRON HANDLER: ✅ Primary Keywords: " . (!empty($primary_keywords) ? 'Saved' : 'Missing') . "
"; echo "Igny8 CRON HANDLER: ✅ Secondary Keywords: " . (!empty($secondary_keywords) ? 'Saved' : 'Missing') . "
"; echo "Igny8 CRON HANDLER: ✅ Word Count: " . (!empty($word_count) ? 'Saved' : 'Missing') . "
"; // Verify taxonomies $cluster_terms = wp_get_object_terms($post_id, 'clusters'); $sector_terms = wp_get_object_terms($post_id, 'sectors'); echo "Igny8 CRON HANDLER: ✅ Cluster Taxonomy: " . (!empty($cluster_terms) && !is_wp_error($cluster_terms) ? 'Associated' : 'Not Associated') . "
"; echo "Igny8 CRON HANDLER: ✅ Sector Taxonomy: " . (!empty($sector_terms) && !is_wp_error($sector_terms) ? 'Associated' : 'Not Associated') . "
"; // Verify Yoast meta $yoast_meta = get_post_meta($post_id, '_yoast_wpseo_metadesc', true); echo "Igny8 CRON HANDLER: ✅ Yoast Meta Description: " . (!empty($yoast_meta) ? 'Saved' : 'Missing') . "
"; $completed++; echo "🎉 TASK " . $task_id . " COMPLETED SUCCESSFULLY!
"; } else { echo "❌ Post creation failed - No post ID returned
"; $failed++; } } else { $failed++; $error_msg = $response['data']['message'] ?? 'Unknown error'; echo "❌ TASK " . $task_id . " FAILED: " . $error_msg . "
"; } echo "
"; } echo "Igny8 CRON HANDLER: Content generation completed - Success: " . $completed . ", Failed: " . $failed . "
"; if ($completed > 0) { igny8_log_ai_event('Auto Generate Content Complete', 'writer', 'auto_generate_content', 'success', 'Automated content generation completed', 'Content generated: ' . $completed . ', Failed: ' . $failed); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $completed; $GLOBALS['igny8_cron_result_details'] = "Processed {$completed} tasks, generated {$completed} content pieces"; echo "Igny8 AUTO GENERATE CONTENT HANDLER: Set global variables - processed_count: $completed, result_details: Processed {$completed} tasks, generated {$completed} content pieces
"; } else { igny8_log_ai_event('Auto Generate Content Failed', 'writer', 'auto_generate_content', 'error', 'Automated content generation failed', 'No content was generated'); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No content was generated"; } echo "Igny8 CRON HANDLER: auto_generate_content completed
"; echo ""; // Close the handler div // Restore error reporting error_reporting($old_error_reporting); } /** * Auto Publish Drafts Cron Handler * * Automatically publishes completed draft posts. */ function igny8_auto_publish_drafts_cron_handler() { echo "
"; echo "Igny8 CRON HANDLER: auto_publish_drafts started
"; error_log("Igny8 CRON HANDLER: auto_publish_drafts started"); // Check if automation is enabled via cron settings echo "Igny8 CRON HANDLER: Checking if automation is enabled
"; $cron_settings = get_option('igny8_cron_settings', []); $job_settings = $cron_settings['igny8_auto_publish_drafts_cron'] ?? []; $auto_publish_enabled = $job_settings['enabled'] ?? false; echo "Igny8 CRON HANDLER: auto_publish_enabled = " . ($auto_publish_enabled ? 'enabled' : 'disabled') . "
"; if (!$auto_publish_enabled) { echo "Igny8 CRON HANDLER: Automation disabled, exiting
"; error_log("Igny8 CRON HANDLER: auto_publish_drafts automation disabled"); echo "
"; return; } echo "Igny8 CRON HANDLER: Automation enabled, continuing
"; global $wpdb; // Get completed tasks with published content (use admin limit) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_publish_drafts_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Publish Drafts Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } echo "Igny8 CRON HANDLER: Querying completed tasks with draft posts (limit: $limit)
"; $completed_tasks = $wpdb->get_results(" SELECT t.id, t.assigned_post_id FROM {$wpdb->prefix}igny8_tasks t INNER JOIN {$wpdb->posts} p ON t.assigned_post_id = p.ID WHERE t.status = 'completed' AND p.post_status = 'draft' LIMIT $limit "); echo "Igny8 CRON HANDLER: Found " . count($completed_tasks) . " completed tasks with draft posts
"; if (empty($completed_tasks)) { echo "Igny8 CRON HANDLER: No draft posts found, exiting
"; igny8_log_ai_event('Auto Publish Drafts Skipped', 'writer', 'auto_publish_drafts', 'info', 'No draft posts found', 'All content is already published'); echo ""; return; } $post_ids = array_column($completed_tasks, 'assigned_post_id'); // Log automation start igny8_log_ai_event('Auto Publish Drafts Started', 'writer', 'auto_publish_drafts', 'info', 'Starting automated publishing', 'Posts: ' . count($post_ids)); $published = 0; echo "Igny8 CRON HANDLER: Processing " . count($post_ids) . " draft posts
"; foreach ($post_ids as $post_id) { echo "Igny8 CRON HANDLER: Publishing post ID: " . $post_id . "
"; $result = wp_update_post([ 'ID' => $post_id, 'post_status' => 'publish' ]); if (!is_wp_error($result) && $result) { $published++; echo "Igny8 CRON HANDLER: ✅ SUCCESS - Published post ID: " . $post_id . "
"; } else { echo "Igny8 CRON HANDLER: ❌ FAILED - Could not publish post ID: " . $post_id . "
"; } } echo "Igny8 CRON HANDLER: Publishing completed - Published: " . $published . " posts
"; if ($published > 0) { igny8_log_ai_event('Auto Publish Drafts Complete', 'writer', 'auto_publish_drafts', 'success', 'Automated publishing completed', 'Posts published: ' . $published); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $published; $GLOBALS['igny8_cron_result_details'] = "Processed {$published} drafts, published {$published} posts"; } else { igny8_log_ai_event('Auto Publish Drafts Failed', 'writer', 'auto_publish_drafts', 'error', 'Automated publishing failed', 'No posts were published'); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No posts were published"; } echo "Igny8 CRON HANDLER: auto_publish_drafts completed
"; echo ""; // Close the handler div } /** * Auto Optimizer Cron Handler (NEW) * * Automatically optimizes content and keywords. */ function igny8_auto_optimizer_cron_handler() { // Check if automation is enabled if (igny8_get_ai_setting('auto_optimizer_enabled', 'disabled') !== 'enabled') { return; } global $wpdb; // Get posts that need optimization (use admin limit) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_optimizer_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Optimizer Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } $posts_to_optimize = $wpdb->get_results(" SELECT p.ID, p.post_title FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'igny8_optimized' WHERE p.post_status = 'publish' AND p.post_type IN ('post', 'page') AND pm.meta_value IS NULL LIMIT $limit "); if (empty($posts_to_optimize)) { igny8_log_ai_event('Auto Optimizer Skipped', 'optimizer', 'auto_optimizer', 'info', 'No posts need optimization', 'All posts are already optimized'); return; } $post_ids = array_column($posts_to_optimize, 'ID'); // Log automation start igny8_log_ai_event('Auto Optimizer Started', 'optimizer', 'auto_optimizer', 'info', 'Starting automated optimization', 'Posts: ' . count($post_ids)); $optimized = 0; foreach ($post_ids as $post_id) { // Run optimization process $result = igny8_optimize_post_content($post_id); if ($result['success']) { $optimized++; // Mark as optimized update_post_meta($post_id, 'igny8_optimized', current_time('mysql')); } } if ($optimized > 0) { igny8_log_ai_event('Auto Optimizer Complete', 'optimizer', 'auto_optimizer', 'success', 'Automated optimization completed', 'Posts optimized: ' . $optimized); } else { igny8_log_ai_event('Auto Optimizer Failed', 'optimizer', 'auto_optimizer', 'error', 'Automated optimization failed', 'No posts were optimized'); } } /** * Auto Recalc Cron Handler (NEW) * * Automatically recalculates metrics for all entities. */ function igny8_auto_recalc_cron_handler() { global $wpdb; // Get entities that need recalculation $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { error_log('Igny8 Auto Cluster Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } $clusters_to_recalc = $wpdb->get_results(" SELECT id FROM {$wpdb->prefix}igny8_clusters WHERE status = 'active' LIMIT $limit "); $tasks_to_recalc = $wpdb->get_results(" SELECT id FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('completed', 'in_progress') LIMIT $limit "); $total_recalc = count($clusters_to_recalc) + count($tasks_to_recalc); if ($total_recalc === 0) { igny8_log_ai_event('Auto Recalc Skipped', 'analytics', 'auto_recalc', 'info', 'No entities need recalculation', 'All metrics are up to date'); return; } // Log automation start igny8_log_ai_event('Auto Recalc Started', 'analytics', 'auto_recalc', 'info', 'Starting automated recalculation', 'Entities: ' . $total_recalc); $recalculated = 0; // Recalculate cluster metrics foreach ($clusters_to_recalc as $cluster) { igny8_update_cluster_metrics($cluster->id); $recalculated++; } // Recalculate task metrics foreach ($tasks_to_recalc as $task) { igny8_update_task_metrics($task->id); $recalculated++; } if ($recalculated > 0) { igny8_log_ai_event('Auto Recalc Complete', 'analytics', 'auto_recalc', 'success', 'Automated recalculation completed', 'Entities recalculated: ' . $recalculated); } else { igny8_log_ai_event('Auto Recalc Failed', 'analytics', 'auto_recalc', 'error', 'Automated recalculation failed', 'No entities were recalculated'); } } /** * Health Check Cron Handler (NEW) * * Performs system health checks, cleanup, and dependency validation. */ function igny8_health_check_cron_handler() { global $wpdb; $health_issues = []; $cleanup_performed = []; $dependency_issues = []; // Check for orphaned records $orphaned_keywords = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords k LEFT JOIN {$wpdb->prefix}igny8_clusters c ON k.cluster_id = c.id WHERE k.cluster_id IS NOT NULL AND k.cluster_id > 0 AND c.id IS NULL "); if ($orphaned_keywords > 0) { $health_issues[] = "Found $orphaned_keywords orphaned keywords"; // Clean up orphaned keywords $wpdb->query(" UPDATE {$wpdb->prefix}igny8_keywords SET cluster_id = NULL WHERE cluster_id IS NOT NULL AND cluster_id > 0 AND cluster_id NOT IN (SELECT id FROM {$wpdb->prefix}igny8_clusters) "); $cleanup_performed[] = "Cleaned up $orphaned_keywords orphaned keywords"; } // Check for old failed AI tasks $old_failed_tasks = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}igny8_ai_queue WHERE status = 'failed' AND created_at < DATE_SUB(NOW(), INTERVAL 7 DAY) "); if ($old_failed_tasks > 0) { $wpdb->query(" DELETE FROM {$wpdb->prefix}igny8_ai_queue WHERE status = 'failed' AND created_at < DATE_SUB(NOW(), INTERVAL 7 DAY) "); $cleanup_performed[] = "Cleaned up $old_failed_tasks old failed AI tasks"; } // Dependency validation - check execution order $dependency_issues = igny8_validate_automation_dependencies(); // Check database table sizes $table_sizes = []; $tables = ['igny8_keywords', 'igny8_clusters', 'igny8_content_ideas', 'igny8_tasks', 'igny8_ai_queue']; foreach ($tables as $table) { $count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}$table"); $table_sizes[$table] = $count; } // Log health check results if (empty($health_issues) && empty($dependency_issues)) { igny8_log_ai_event('Health Check Passed', 'system', 'health_check', 'success', 'System health check completed', 'No issues found'); } else { $all_issues = array_merge($health_issues, $dependency_issues); igny8_log_ai_event('Health Check Issues', 'system', 'health_check', 'warning', 'Health check found issues', implode('; ', $all_issues)); } if (!empty($cleanup_performed)) { igny8_log_ai_event('Health Check Cleanup', 'system', 'health_check', 'info', 'Cleanup performed', implode('; ', $cleanup_performed)); } // Store health metrics update_option('igny8_health_metrics', [ 'last_check' => current_time('mysql'), 'table_sizes' => $table_sizes, 'issues_found' => count($health_issues), 'dependency_issues' => count($dependency_issues), 'cleanup_performed' => count($cleanup_performed) ]); } /** * Validate automation dependencies and execution order */ function igny8_validate_automation_dependencies() { $issues = []; global $wpdb; // Check if clusters exist but no ideas generated $clusters_without_ideas = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}igny8_clusters c LEFT JOIN {$wpdb->prefix}igny8_content_ideas i ON c.id = i.keyword_cluster_id WHERE c.status = 'active' AND i.id IS NULL "); if ($clusters_without_ideas > 0) { $issues[] = "Found $clusters_without_ideas clusters without content ideas (auto_generate_ideas may be disabled)"; } // Check if ideas exist but not queued $ideas_not_queued = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}igny8_content_ideas i LEFT JOIN {$wpdb->prefix}igny8_tasks t ON i.id = t.idea_id WHERE i.status = 'new' AND t.id IS NULL "); if ($ideas_not_queued > 0) { $issues[] = "Found $ideas_not_queued ideas not queued for content generation (auto_queue may be disabled)"; } // Check if tasks exist but no content generated $tasks_without_content = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks t LEFT JOIN {$wpdb->posts} p ON t.assigned_post_id = p.ID WHERE t.status = 'queued' AND (p.ID IS NULL OR p.post_content = '') "); if ($tasks_without_content > 0) { $issues[] = "Found $tasks_without_content tasks without content (auto_generate_content may be disabled)"; } // Check if content exists but no images $content_without_images = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'igny8_has_ai_image' WHERE p.post_status = 'publish' AND p.post_type IN ('post', 'page') AND p.post_content LIKE '%[image%' AND pm.meta_value IS NULL "); if ($content_without_images > 0) { $issues[] = "Found $content_without_images posts with image placeholders but no AI images (auto_generate_images may be disabled)"; } // Check if content exists but not published $drafts_not_published = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->posts} p INNER JOIN {$wpdb->prefix}igny8_tasks t ON p.ID = t.assigned_post_id WHERE p.post_status = 'draft' AND t.status = 'completed' "); if ($drafts_not_published > 0) { $issues[] = "Found $drafts_not_published completed tasks with unpublished content (auto_publish_drafts may be disabled)"; } return $issues; } /** * Auto Generate Images Cron Handler (NEW) * * Automatically generates images for content that needs them. */ function igny8_auto_generate_images_cron_handler() { // Check if automation is enabled if (igny8_get_ai_setting('auto_generate_images_enabled', 'disabled') !== 'enabled') { return; } // Check if AI mode is enabled if (igny8_get_ai_setting('writer_mode', 'manual') !== 'ai') { return; } global $wpdb; // Get posts that need images (use admin limit) $limit = $GLOBALS['igny8_cron_limit'] ?? null; if ($limit === null) { // Try to get limit from Smart Automation Jobs table directly $cron_limits = get_option('igny8_cron_limits', []); $limit = $cron_limits['igny8_auto_generate_images_cron'] ?? null; if ($limit === null) { error_log('Igny8 Auto Generate Images Cron: No limit set in Smart Automation Jobs table'); $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No limit set in Smart Automation Jobs table"; return; } echo "Igny8 CRON HANDLER: Using limit from Smart Automation Jobs table: $limit
"; } $posts_needing_images = $wpdb->get_results(" SELECT p.ID, p.post_title, p.post_content FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'igny8_has_ai_image' WHERE p.post_status = 'publish' AND p.post_type IN ('post', 'page') AND pm.meta_value IS NULL AND p.post_content LIKE '%[image%' LIMIT $limit "); if (empty($posts_needing_images)) { igny8_log_ai_event('Auto Generate Images Skipped', 'writer', 'auto_generate_images', 'info', 'No posts need images', 'All posts already have images or no image placeholders found'); return; } $post_ids = array_column($posts_needing_images, 'ID'); // Log automation start igny8_log_ai_event('Auto Generate Images Started', 'writer', 'auto_generate_images', 'info', 'Starting automated image generation', 'Posts: ' . count($post_ids)); $generated = 0; foreach ($post_ids as $post_id) { // Run image generation process $result = igny8_generate_post_images($post_id); if ($result['success']) { $generated++; // Mark as having AI image update_post_meta($post_id, 'igny8_has_ai_image', current_time('mysql')); } } if ($generated > 0) { igny8_log_ai_event('Auto Generate Images Complete', 'writer', 'auto_generate_images', 'success', 'Automated image generation completed', 'Images generated: ' . $generated); // Set global variables for detailed logging $GLOBALS['igny8_cron_processed_count'] = $generated; $GLOBALS['igny8_cron_result_details'] = "Processed {$generated} posts, generated {$generated} images"; } else { igny8_log_ai_event('Auto Generate Images Failed', 'writer', 'auto_generate_images', 'error', 'Automated image generation failed', 'No images were generated'); // Set global variables for detailed logging (failure case) $GLOBALS['igny8_cron_processed_count'] = 0; $GLOBALS['igny8_cron_result_details'] = "FAILED: No images were generated"; } } /** * Test Cron Endpoint Handler (NEW) * * Simple test handler for cron endpoint validation. */ function igny8_test_cron_endpoint() { igny8_log_ai_event('Cron Test', 'system', 'test_endpoint', 'info', 'Cron endpoint test executed', 'Test successful at ' . current_time('mysql')); // Return success response for external cron if (defined('DOING_CRON') && DOING_CRON) { return [ 'success' => true, 'message' => 'Igny8 CRON endpoint is working', 'timestamp' => current_time('mysql'), 'server' => $_SERVER['SERVER_NAME'] ?? 'unknown' ]; } } /** * Safe taxonomy term creation with duplicate prevention * * @param string $term_name Term name * @param string $taxonomy Taxonomy name * @param array $args Additional arguments * @return int|WP_Error Term ID or error */ function igny8_safe_create_term($term_name, $taxonomy, $args = []) { // Check if term already exists $existing_term = get_term_by('name', $term_name, $taxonomy); if ($existing_term) { return $existing_term->term_id; } // Create term with duplicate slug prevention $slug = sanitize_title($term_name); $counter = 1; $original_slug = $slug; while (term_exists($slug, $taxonomy)) { $slug = $original_slug . '-' . $counter; $counter++; } $args['slug'] = $slug; $result = wp_insert_term($term_name, $taxonomy, $args); if (is_wp_error($result)) { // If still error, try to get existing term by slug $existing = get_term_by('slug', $slug, $taxonomy); if ($existing) { return $existing->term_id; } return $result; } return $result['term_id']; } /** * Enhanced cluster term creation with safety checks * * @param int $cluster_id Cluster ID * @return int|false Term ID or false on failure */ function igny8_safe_create_cluster_term($cluster_id) { global $wpdb; // Get cluster data $cluster = $wpdb->get_row($wpdb->prepare( "SELECT cluster_name, cluster_term_id FROM {$wpdb->prefix}igny8_clusters WHERE id = %d", $cluster_id )); if (!$cluster) { return false; } // Skip if already mapped if (!empty($cluster->cluster_term_id)) { return $cluster->cluster_term_id; } // Ensure taxonomy exists if (!taxonomy_exists('clusters') && function_exists('igny8_register_taxonomies')) { igny8_register_taxonomies(); } // Create term safely $term_id = igny8_safe_create_term($cluster->cluster_name, 'clusters'); if ($term_id && !is_wp_error($term_id)) { // Update cluster with term ID $wpdb->update( "{$wpdb->prefix}igny8_clusters", ['cluster_term_id' => $term_id], ['id' => $cluster_id], ['%d'], ['%d'] ); return $term_id; } return false; } // =================================================================== // REGISTER CRON HOOKS // =================================================================== // Register all cron job hooks add_action('igny8_auto_cluster_cron', 'igny8_auto_cluster_cron_handler'); add_action('igny8_auto_generate_ideas_cron', 'igny8_auto_generate_ideas_cron_handler'); add_action('igny8_auto_queue_cron', 'igny8_auto_queue_cron_handler'); add_action('igny8_auto_generate_content_cron', 'igny8_auto_generate_content_cron_handler'); add_action('igny8_auto_generate_images_cron', 'igny8_auto_generate_images_cron_handler'); add_action('igny8_auto_publish_drafts_cron', 'igny8_auto_publish_drafts_cron_handler'); add_action('igny8_process_ai_queue_cron', 'igny8_process_ai_queue_cron_handler'); add_action('igny8_auto_recalc_cron', 'igny8_auto_recalc_cron_handler'); add_action('igny8_auto_optimizer_cron', 'igny8_auto_optimizer_cron_handler'); add_action('igny8_health_check_cron', 'igny8_health_check_cron_handler');