get_var($wpdb->prepare( "SELECT cluster_name FROM {$wpdb->prefix}igny8_clusters WHERE id = %d", $cluster_id )); if (!empty($cluster_name)) { return $cluster_name; } // If no cluster found, return empty string return ''; } /** * Convert cluster name to cluster ID * Used across multiple submodules and forms * * @param string $cluster_name The cluster name * @return int The cluster ID or 0 if not found */ function igny8_get_cluster_id_by_name($cluster_name) { if (empty($cluster_name) || !is_string($cluster_name)) { return 0; } global $wpdb; // Query the clusters table directly $cluster_id = $wpdb->get_var($wpdb->prepare( "SELECT id FROM {$wpdb->prefix}igny8_clusters WHERE cluster_name = %s", $cluster_name )); return $cluster_id ? (int)$cluster_id : 0; } /** * Get cluster options for dropdowns * Used across all modules that need cluster selection * * @return array Array of cluster options in format [['value' => id, 'label' => name], ...] */ function igny8_get_cluster_options() { global $wpdb; // Get all active clusters from database $clusters = $wpdb->get_results( "SELECT id, cluster_name FROM {$wpdb->prefix}igny8_clusters WHERE status = 'active' ORDER BY cluster_name ASC" ); $options = [ ['value' => '', 'label' => 'No Cluster'] ]; if ($clusters) { foreach ($clusters as $cluster) { $options[] = [ 'value' => $cluster->id, 'label' => $cluster->cluster_name ]; } } return $options; } /** * Get standardized cluster lookup configuration for tables * This eliminates the need to repeat lookup configuration in every table * * @return array Standardized cluster lookup configuration */ function igny8_get_cluster_lookup_config() { return [ 'type' => 'lookup', 'source_field' => 'cluster_id', 'display_field' => 'cluster_name', 'sortable' => true, 'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.cluster_id = c.id', 'select_field' => 'c.cluster_name as cluster_name' ]; } /** * Get standardized cluster lookup configuration for ideas table * Uses keyword_cluster_id instead of cluster_id * * @return array Standardized cluster lookup configuration for ideas */ function igny8_get_ideas_cluster_lookup_config() { return [ 'type' => 'lookup', 'source_field' => 'keyword_cluster_id', 'display_field' => 'cluster_name', 'sortable' => true, 'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.keyword_cluster_id = c.id', 'select_field' => 'c.cluster_name as cluster_name' ]; } /** * Get dynamic table configuration based on AI mode */ function igny8_get_dynamic_table_config($table_id) { $config = $GLOBALS['igny8_tables_config'][$table_id] ?? []; // Apply AI-specific modifications if ($table_id === 'writer_queue') { $writer_mode = igny8_get_ai_setting('writer_mode', 'manual'); // Hide due_date column for AI mode if ($writer_mode === 'ai' && isset($config['columns']['due_date'])) { unset($config['columns']['due_date']); // Also remove from filters if (isset($config['filters']['due_date'])) { unset($config['filters']['due_date']); } } } // Apply status filter for drafts table if ($table_id === 'writer_drafts') { $config['default_filter'] = [ 'status' => ['draft'] ]; } // Apply status filter for published table if ($table_id === 'writer_published') { $config['default_filter'] = [ 'status' => ['published'] ]; } return $config; } /** * Get dynamic form configuration based on table columns */ function igny8_get_dynamic_form_config($form_id) { $form_config = $GLOBALS['igny8_forms_config'][$form_id] ?? []; // For writer_queue form, conditionally include due_date field if ($form_id === 'writer_queue') { $writer_mode = igny8_get_ai_setting('writer_mode', 'manual'); // Remove due_date field for AI mode if ($writer_mode === 'ai') { $form_config['fields'] = array_filter($form_config['fields'], function($field) { return $field['name'] !== 'due_date'; }); // Re-index array $form_config['fields'] = array_values($form_config['fields']); } } return $form_config; } /** * Convert difficulty number to predefined difficulty range name * * @param float $difficulty The difficulty value (0-100) * @return string The difficulty range name */ function igny8_get_difficulty_range_name($difficulty) { if (empty($difficulty) || !is_numeric($difficulty)) { return ''; } $difficulty = floatval($difficulty); if ($difficulty <= 20) { return 'Very Easy'; } elseif ($difficulty <= 40) { return 'Easy'; } elseif ($difficulty <= 60) { return 'Medium'; } elseif ($difficulty <= 80) { return 'Hard'; } else { return 'Very Hard'; } } /** * Convert difficulty text label to numeric range for database queries * * @param string $difficulty_label The difficulty text label * @return array|false Array with 'min' and 'max' keys, or false if invalid */ function igny8_get_difficulty_numeric_range($difficulty_label) { if (empty($difficulty_label)) { return false; } switch ($difficulty_label) { case 'Very Easy': return ['min' => 0, 'max' => 20]; case 'Easy': return ['min' => 21, 'max' => 40]; case 'Medium': return ['min' => 41, 'max' => 60]; case 'Hard': return ['min' => 61, 'max' => 80]; case 'Very Hard': return ['min' => 81, 'max' => 100]; default: return false; } } /** * Get page description */ function igny8_get_page_description() { $current_page = $_GET['page'] ?? ''; $sm = $_GET['sm'] ?? ''; switch ($current_page) { case 'igny8-home': return 'Welcome to Igny8 AI SEO OS - Your comprehensive SEO management platform.'; case 'igny8-planner': if ($sm === 'keywords') { return 'Manage your keywords, track search volumes, and organize by intent and difficulty.'; } elseif ($sm === 'clusters') { return 'Group related keywords into content clusters for better topical authority.'; } elseif ($sm === 'ideas') { return 'Generate and organize content ideas based on your keyword research.'; } elseif ($sm === 'mapping') { return 'Map keywords and clusters to existing pages and content.'; } else { return 'Plan and organize your content strategy with keyword research and clustering.'; } case 'igny8-writer': if ($sm === 'drafts') { return 'Manage your content drafts and work in progress.'; } elseif ($sm === 'templates') { return 'Create and manage content templates for consistent writing.'; } else { return 'Content creation and writing tools for SEO-optimized content.'; } case 'igny8-optimizer': if ($sm === 'audits') { return 'Run comprehensive SEO audits on your content and pages.'; } elseif ($sm === 'suggestions') { return 'Get AI-powered optimization suggestions for better rankings.'; } else { return 'SEO optimization and performance tools for better rankings.'; } case 'igny8-linker': if ($sm === 'backlinks') { return 'Track and manage your backlink profile and authority.'; } elseif ($sm === 'campaigns') { return 'Plan and execute link building campaigns effectively.'; } else { return 'Link building and backlink management for improved authority.'; } case 'igny8-personalize': if ($sm === 'settings') { return 'Configure global settings, display options, and advanced personalization settings.'; } elseif ($sm === 'content-generation') { return 'Configure AI prompts, field detection, and content generation parameters.'; } elseif ($sm === 'rewrites') { return 'View and manage personalized content variations and rewrites.'; } elseif ($sm === 'front-end') { return 'Manage front-end display settings, shortcode usage, and implementation guides.'; } else { return 'Content personalization and targeting for better engagement.'; } case 'igny8-settings': $sp = $_GET['sp'] ?? 'general'; if ($sp === 'status') { return 'Monitor system health, database status, and module performance.'; } elseif ($sp === 'integration') { return 'Configure API keys and integrate with external services.'; } elseif ($sp === 'import-export') { return 'Import and export data, manage backups, and transfer content.'; } else { return 'Configure plugin settings, automation, and table preferences.'; } case 'igny8-analytics': return 'Performance analytics and reporting for data-driven decisions.'; case 'igny8-schedules': return 'Content scheduling and automation for consistent publishing.'; case 'igny8-help': return 'Documentation and support resources for getting started.'; default: return 'Igny8 AI SEO OS - Advanced SEO Management'; } } /** * Get page title */ function igny8_get_page_title() { $current_page = $_GET['page'] ?? ''; $sm = $_GET['sm'] ?? ''; switch ($current_page) { case 'igny8-home': return 'Igny8 Home'; case 'igny8-planner': if ($sm === 'keywords') { return 'Keywords'; } elseif ($sm === 'clusters') { return 'Clusters'; } elseif ($sm === 'ideas') { return 'Ideas'; } elseif ($sm === 'mapping') { return 'Mapping'; } else { return 'Planner'; } case 'igny8-writer': if ($sm === 'tasks') { return 'Content Queue / Tasks'; } elseif ($sm === 'drafts') { return 'Content Generated'; } elseif ($sm === 'published') { return 'Live Content'; } else { return 'Content Writer'; } case 'igny8-optimizer': if ($sm === 'audits') { return 'Audits'; } elseif ($sm === 'suggestions') { return 'Suggestions'; } else { return 'Optimizer'; } case 'igny8-linker': if ($sm === 'backlinks') { return 'Backlinks'; } elseif ($sm === 'campaigns') { return 'Campaigns'; } else { return 'Linker'; } case 'igny8-personalize': if ($sm === 'settings') { return 'Personalization Settings'; } elseif ($sm === 'content-generation') { return 'Content Generation'; } elseif ($sm === 'rewrites') { return 'Rewrites'; } elseif ($sm === 'front-end') { return 'Front-end'; } else { return 'Personalize'; } case 'igny8-thinker': $sp = $_GET['sp'] ?? 'main'; if ($sp === 'main') { return 'AI Thinker Dashboard'; } elseif ($sp === 'prompts') { return 'AI Prompts'; } elseif ($sp === 'profile') { return 'AI Profile'; } elseif ($sp === 'strategies') { return 'Content Strategies'; } elseif ($sp === 'image-testing') { return 'AI Image Testing'; } else { return 'AI Thinker'; } case 'igny8-settings': $sp = $_GET['sp'] ?? 'general'; if ($sp === 'status') { return 'System Status'; } elseif ($sp === 'integration') { return 'API Integration'; } elseif ($sp === 'import-export') { return 'Import/Export'; } else { return 'General Settings'; } case 'igny8-analytics': return 'Analytics'; case 'igny8-schedules': return 'Schedules'; case 'igny8-test': $sp = $_GET['sp'] ?? 'system-testing'; if ($sp === 'system-testing') { return 'System Testing'; } elseif ($sp === 'temp-function-testing') { return 'Function Testing'; } else { return 'Test Page'; } case 'igny8-help': return 'Help'; default: return 'Igny8 AI SEO OS'; } } // REMOVED: Task variations functionality - tasks don't need variations /** * Safe logging helper for Igny8 operations * * @param string $type Log type (e.g., 'queue_to_writer', 'task_created') * @param mixed $data Log data (string, array, or object) */ function igny8_write_log($type, $data) { global $wpdb; try { $wpdb->insert($wpdb->prefix . 'igny8_logs', [ 'level' => 'info', 'message' => sanitize_text_field($type), 'context' => is_array($data) ? wp_json_encode($data) : (is_string($data) ? $data : null), 'source' => 'igny8_plugin', 'user_id' => get_current_user_id(), ], ['%s', '%s', '%s', '%s', '%d']); } catch (Exception $e) { // Log to error log if database logging fails error_log('Igny8: Failed to write to logs table: ' . $e->getMessage()); } } /** * Get clusters for select dropdown * * @return array Array of cluster options for select dropdown */ function igny8_get_clusters_for_select() { global $wpdb; $clusters = $wpdb->get_results( "SELECT id, cluster_name FROM {$wpdb->prefix}igny8_clusters ORDER BY cluster_name ASC" ); $options = ['' => 'Select Cluster']; foreach ($clusters as $cluster) { $options[$cluster->id] = $cluster->cluster_name; } return $options; } /** * Get sector options for dropdowns * Used across all modules that need sector selection * Only returns sectors from saved planner settings * * @return array Array of sector options in format [['value' => id, 'label' => name], ...] */ function igny8_get_sector_options() { // Get saved sector selection from user meta $user_id = get_current_user_id(); $saved_selection = get_user_meta($user_id, 'igny8_planner_sector_selection', true); $options = []; // If no saved selection, return empty options if (empty($saved_selection)) { return $options; } // Add only child sectors (not parent) if (isset($saved_selection['children']) && is_array($saved_selection['children'])) { foreach ($saved_selection['children'] as $child) { if (isset($child['id']) && isset($child['name'])) { $options[] = [ 'value' => $child['id'], 'label' => $child['name'] ]; } } } return $options; } /** * Get sectors for select dropdown * Only returns sectors from saved planner settings * * @return array Array of sector options for select dropdown */ function igny8_get_sectors_for_select() { // Get saved sector selection from user meta $user_id = get_current_user_id(); $saved_selection = get_user_meta($user_id, 'igny8_planner_sector_selection', true); $options = []; // If no saved selection, return empty options if (empty($saved_selection)) { return $options; } // Add only child sectors (not parent) if (isset($saved_selection['children']) && is_array($saved_selection['children'])) { foreach ($saved_selection['children'] as $child) { if (isset($child['id']) && isset($child['name'])) { $options[$child['id']] = $child['name']; } } } return $options; } /** * Get ideas for select dropdown * * @return array Array of idea options for select dropdown */ function igny8_get_ideas_for_select() { global $wpdb; $ideas = $wpdb->get_results( "SELECT id, idea_title FROM {$wpdb->prefix}igny8_content_ideas ORDER BY idea_title ASC" ); $options = ['' => 'Select Idea']; foreach ($ideas as $idea) { $options[$idea->id] = $idea->idea_title; } return $options; } /** * Transform database field names to human-readable labels * * @param string $field_name The database field name (snake_case) * @return string The human-readable label (Title Case) */ function igny8_humanize_label($field) { // Handle non-string input safely if (!is_string($field)) { return ''; } // Remove any potentially dangerous characters $field = sanitize_key($field); // Specific field mappings for better readability $field_mappings = [ // Clusters table 'cluster_name' => 'Cluster Name', 'sector_id' => 'Sectors', 'keyword_count' => 'Keywords', 'total_volume' => 'Total Volume', 'avg_difficulty' => 'Avg KD', 'mapped_pages_count' => 'Mapped Pages', 'created_at' => 'Created', 'updated_at' => 'Updated', // Keywords table 'search_volume' => 'Volume', 'difficulty' => 'Difficulty', 'cpc' => 'CPC', 'intent' => 'Intent', 'status' => 'Status', 'cluster_id' => 'Cluster', // Ideas table 'idea_title' => 'Title', 'idea_description' => 'Description', 'content_structure' => 'Structure', 'content_angle' => 'Content Angle', 'keyword_cluster_id' => 'Cluster', 'estimated_word_count' => 'Words', 'ai_generated' => 'AI Generated', 'mapped_post_id' => 'Mapped Post', 'tasks_count' => 'Tasks Count', // Tasks table 'content_type' => 'Content Type', 'cluster_id' => 'Cluster', 'keywords' => 'Keywords', 'schedule_at' => 'Scheduled', 'assigned_post_id' => 'Assigned Post', 'created_at' => 'Created', 'updated_at' => 'Updated', // Campaigns table 'backlink_count' => 'Backlinks', 'live_links_count' => 'Live Links', // Mapping table 'page_id' => 'Page ID', 'coverage_percentage' => 'Coverage %', 'coverage_status' => 'Status', 'last_updated' => 'Updated' ]; // Check for specific mapping first if (isset($field_mappings[$field])) { return $field_mappings[$field]; } // Fallback: convert snake_case to Title Case return ucwords(str_replace('_', ' ', $field)); } /** * Transform database field names to human-readable labels (legacy function) * @deprecated Use igny8_humanize_label() instead */ function igny8_transform_field_label($field_name) { return igny8_humanize_label($field_name); } /** * Map cluster to keywords * * @param int $cluster_id Cluster ID to map keywords to * @param array $keyword_ids Array of keyword IDs to map * @return array ['success' => bool, 'message' => string, 'mapped_count' => int] */ function igny8_map_cluster_to_keywords($cluster_id, $keyword_ids) { global $wpdb; if (empty($cluster_id) || !is_numeric($cluster_id)) { return ['success' => false, 'message' => 'Invalid cluster ID provided', 'mapped_count' => 0]; } if (empty($keyword_ids) || !is_array($keyword_ids)) { return ['success' => false, 'message' => 'No keywords provided for mapping', 'mapped_count' => 0]; } $cluster_id = intval($cluster_id); // Verify cluster exists $cluster_exists = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}igny8_clusters WHERE id = %d", $cluster_id )); if (!$cluster_exists) { return ['success' => false, 'message' => 'Cluster not found', 'mapped_count' => 0]; } // Sanitize and validate keyword IDs $keyword_ids = array_map('intval', $keyword_ids); $keyword_ids = array_filter($keyword_ids, function($id) { return $id > 0; }); if (empty($keyword_ids)) { return ['success' => false, 'message' => 'No valid keyword IDs provided', 'mapped_count' => 0]; } // Get old cluster IDs for metrics update $placeholders = implode(',', array_fill(0, count($keyword_ids), '%d')); $old_cluster_ids = $wpdb->get_col($wpdb->prepare( "SELECT DISTINCT cluster_id FROM {$wpdb->prefix}igny8_keywords WHERE id IN ({$placeholders}) AND cluster_id IS NOT NULL", $keyword_ids )); // Update keywords to new cluster $mapped_count = $wpdb->query($wpdb->prepare( "UPDATE {$wpdb->prefix}igny8_keywords SET cluster_id = %d, status = 'mapped', updated_at = CURRENT_TIMESTAMP WHERE id IN ({$placeholders})", array_merge([$cluster_id], $keyword_ids) )); if ($mapped_count === false) { return ['success' => false, 'message' => 'Failed to map keywords to cluster', 'mapped_count' => 0]; } // Update metrics for old clusters (they lost keywords) if (!empty($old_cluster_ids)) { foreach (array_unique($old_cluster_ids) as $old_cluster_id) { if ($old_cluster_id && $old_cluster_id != $cluster_id) { igny8_update_cluster_metrics($old_cluster_id); } } } // Update metrics for new cluster (gained keywords) igny8_update_cluster_metrics($cluster_id); return [ 'success' => true, 'message' => "Successfully mapped {$mapped_count} keyword(s) to cluster", 'mapped_count' => $mapped_count ]; } /** * ============================================= * UNIFIED DATA VALIDATION LAYER * ============================================= */ /** * Unified record validation function for all Planner module tables * * @param string $table_id Table ID (e.g., 'planner_keywords', 'planner_clusters') * @param array $data Array of field data to validate * @return array ['valid' => bool, 'error' => string|null] */ /** * Check if automation is enabled for the current user * * @return bool True if automation is enabled */ function igny8_is_automation_enabled() { // Default ON for admin users, OFF for others if (current_user_can('manage_options')) { return true; } // Check if user has specific automation capability return current_user_can('edit_posts'); } /** * Helper function to get sector name * Used for table display to show sector names instead of IDs * * @param mixed $sector_id The sector ID * @return string The sector name or empty string if not found or 0/null */ function igny8_get_sector_name($sector_id) { // Return empty string for 0, null, or empty values if (empty($sector_id) || !is_numeric($sector_id) || intval($sector_id) == 0) { return ''; } // Check if sectors taxonomy exists if (!taxonomy_exists('sectors')) { return ''; } // Try to get from WordPress taxonomy $term = get_term(intval($sector_id), 'sectors'); if ($term && !is_wp_error($term)) { return $term->name; } // If term not found, return empty string instead of fallback return ''; } /** * Generate cluster name from keywords * * @param array $keywords Array of keyword objects * @return string Generated cluster name */ function igny8_generate_cluster_name_from_keywords($keywords) { if (empty($keywords)) { return 'Auto-Generated Cluster'; } // Get the most common words from keywords $all_words = []; foreach ($keywords as $keyword) { $words = explode(' ', strtolower($keyword->keyword)); $all_words = array_merge($all_words, $words); } // Count word frequency $word_count = array_count_values($all_words); arsort($word_count); // Get top 2-3 most common words $top_words = array_slice(array_keys($word_count), 0, 3); if (empty($top_words)) { return 'Auto-Generated Cluster'; } return ucwords(implode(' ', $top_words)) . ' Cluster'; } /** * Calculate relevance score for mapping suggestions * * @param string $term The search term * @param string $title The post title * @param string $slug The post slug * @return int Relevance score (0-100) */ function igny8_calculate_relevance_score($term, $title, $slug) { $score = 0; $term_lower = strtolower($term); $title_lower = strtolower($title); $slug_lower = strtolower($slug); // Exact match in title if (strpos($title_lower, $term_lower) !== false) { $score += 50; } // Exact match in slug if (strpos($slug_lower, $term_lower) !== false) { $score += 30; } // Word match in title $title_words = explode(' ', $title_lower); $term_words = explode(' ', $term_lower); foreach ($term_words as $word) { if (in_array($word, $title_words)) { $score += 10; } } return min($score, 100); } /** * Remove duplicate suggestions from mapping suggestions * * @param array $suggestions Array of suggestion objects * @return array Deduplicated suggestions */ function igny8_remove_duplicate_suggestions($suggestions) { $seen = []; $unique = []; foreach ($suggestions as $suggestion) { $key = $suggestion['page_id']; if (!isset($seen[$key])) { $seen[$key] = true; $unique[] = $suggestion; } } return $unique; } /** * Get Import/Export Configuration for a table * * @param string $table_id The table ID (e.g., 'planner_keywords') * @return array|false Configuration array or false if not found */ function igny8_get_import_export_config($table_id) { static $config_cache = null; if ($config_cache === null) { $config_path = plugin_dir_path(dirname(__FILE__)) . '../modules/config/import-export-config.php'; if (file_exists($config_path)) { $config_cache = include $config_path; } else { $config_cache = []; } } return isset($config_cache[$table_id]) ? $config_cache[$table_id] : false; } /** * AJAX handler for testing API connection */ function igny8_test_connection_ajax() { try { // Check if user has permission if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } // Verify nonce if (!check_ajax_referer('igny8_test_connection', 'nonce', false)) { wp_send_json_error('Invalid nonce'); return; } // Test basic HTTP functionality $test_url = 'https://httpbin.org/get'; $response = wp_remote_get($test_url, ['timeout' => 10]); if (is_wp_error($response)) { wp_send_json_error('HTTP request failed: ' . $response->get_error_message()); return; } $response_code = wp_remote_retrieve_response_code($response); if ($response_code !== 200) { wp_send_json_error('HTTP request returned status code: ' . $response_code); return; } wp_send_json_success('Connection test passed'); } catch (Exception $e) { wp_send_json_error('Exception: ' . $e->getMessage()); } } add_action('wp_ajax_igny8_test_connection', 'igny8_test_connection_ajax'); /** * AJAX handler for testing API with response */ function igny8_test_api_ajax() { try { // Check if user has permission if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); return; } // Verify nonce if (!check_ajax_referer('igny8_ajax_nonce', 'nonce', false)) { wp_send_json_error('Invalid nonce'); return; } // Get API key $api_key = get_option('igny8_api_key', ''); if (empty($api_key)) { wp_send_json_error('API key not configured'); return; } // Get parameters $with_response = isset($_POST['with_response']) ? (bool) $_POST['with_response'] : false; // Test API connection $result = igny8_test_connection($api_key, $with_response); if (is_wp_error($result)) { wp_send_json_error($result->get_error_message()); return; } if (is_array($result) && isset($result['success'])) { if ($result['success']) { wp_send_json_success($result); } else { wp_send_json_error($result['message'] ?? 'API test failed'); } } else { wp_send_json_success(['message' => 'API connection successful', 'response' => $result]); } } catch (Exception $e) { wp_send_json_error('Exception: ' . $e->getMessage()); } } add_action('wp_ajax_igny8_test_api', 'igny8_test_api_ajax'); /** * Get saved sector selection from user meta * * @return array|false Saved sector selection data or false if not set */ function igny8_get_saved_sector_selection() { $user_id = get_current_user_id(); $saved_selection = get_user_meta($user_id, 'igny8_planner_sector_selection', true); return !empty($saved_selection) ? $saved_selection : false; } /** * Get system-wide AI workflow data for main dashboard * * @return array Array of step data with status and counts for all 9 workflow steps */ function igny8_get_system_workflow_data() { global $wpdb; // Get counts for each step $keywords_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords"); $unmapped_keywords = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords WHERE cluster_id IS NULL OR cluster_id = 0"); $clusters_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_clusters"); $ideas_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_content_ideas"); $queued_ideas = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_content_ideas WHERE status = 'new'"); $queued_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('pending', 'queued', 'new')"); $draft_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('draft', 'in_progress', 'review')"); $published_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed'"); // Check sector selection $sector_selected = !empty(igny8_get_saved_sector_selection()); // Check if modules are enabled $planner_enabled = igny8_is_module_enabled('planner'); $writer_enabled = igny8_is_module_enabled('writer'); return [ 'keywords' => [ 'count' => $keywords_count, 'unmapped' => $unmapped_keywords, 'status' => $keywords_count > 0 ? 'completed' : 'missing', 'module_enabled' => $planner_enabled, 'url' => $planner_enabled ? '?page=igny8-planner&sm=keywords' : null ], 'sector' => [ 'selected' => $sector_selected, 'status' => $sector_selected ? 'completed' : 'missing', 'module_enabled' => $planner_enabled, 'url' => $planner_enabled ? '?page=igny8-planner' : null ], 'clusters' => [ 'count' => $clusters_count, 'unmapped_keywords' => $unmapped_keywords, 'status' => $unmapped_keywords == 0 && $clusters_count > 0 ? 'completed' : ($unmapped_keywords > 0 ? 'in_progress' : 'missing'), 'module_enabled' => $planner_enabled, 'url' => $planner_enabled ? '?page=igny8-planner&sm=clusters' : null ], 'ideas' => [ 'count' => $ideas_count, 'status' => $ideas_count > 0 ? 'completed' : 'missing', 'module_enabled' => $planner_enabled, 'url' => $planner_enabled ? '?page=igny8-planner&sm=ideas' : null ], 'queue' => [ 'queued_ideas' => $queued_ideas, 'status' => $queued_ideas == 0 && $ideas_count > 0 ? 'completed' : ($queued_ideas > 0 ? 'in_progress' : 'missing'), 'module_enabled' => $planner_enabled, 'url' => $planner_enabled ? '?page=igny8-planner&sm=ideas' : null ], 'drafts' => [ 'queued_tasks' => $queued_tasks, 'draft_tasks' => $draft_tasks, 'status' => $queued_tasks > 0 ? 'in_progress' : ($draft_tasks > 0 ? 'completed' : 'missing'), 'module_enabled' => $writer_enabled, 'url' => $writer_enabled ? '?page=igny8-writer&sm=tasks' : null ], 'publish' => [ 'published_tasks' => $published_tasks, 'draft_tasks' => $draft_tasks, 'status' => $published_tasks > 0 ? 'completed' : ($draft_tasks > 0 ? 'in_progress' : 'missing'), 'module_enabled' => $writer_enabled, 'url' => $writer_enabled ? '?page=igny8-writer&sm=drafts' : null ] ]; } /** * Display image prompts in a formatted way for table display * * @param string $image_prompts JSON string of image prompts * @return string Formatted display string */