reference plugin and image gen analysis
This commit is contained in:
14
igny8-ai-seo-wp-plugin/core/_README.php
Normal file
14
igny8-ai-seo-wp-plugin/core/_README.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* ==============================
|
||||
* 📁 Folder Scope Declaration
|
||||
* ==============================
|
||||
* Folder: /core/
|
||||
* Purpose: Layout, init, DB, CRON - Core system functionality
|
||||
* Rules:
|
||||
* - Can be reused globally across all modules
|
||||
* - Contains WordPress integration logic
|
||||
* - Database operations and schema management
|
||||
* - Admin interface and routing
|
||||
* - CRON and automation systems
|
||||
*/
|
||||
5223
igny8-ai-seo-wp-plugin/core/admin/ajax.php
Normal file
5223
igny8-ai-seo-wp-plugin/core/admin/ajax.php
Normal file
File diff suppressed because it is too large
Load Diff
1084
igny8-ai-seo-wp-plugin/core/admin/global-helpers.php
Normal file
1084
igny8-ai-seo-wp-plugin/core/admin/global-helpers.php
Normal file
File diff suppressed because it is too large
Load Diff
135
igny8-ai-seo-wp-plugin/core/admin/init.php
Normal file
135
igny8-ai-seo-wp-plugin/core/admin/init.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : init.php
|
||||
* @location : /core/admin/init.php
|
||||
* @type : Function Library
|
||||
* @scope : Global
|
||||
* @allowed : Admin initialization, settings registration, asset enqueuing
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : WordPress admin initialization and settings management
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* ADMIN INITIALIZATION BOOTSTRAP
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
add_action('admin_init', 'igny8_register_settings');
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* SETTINGS REGISTRATION
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function igny8_register_settings() {
|
||||
$groups = igny8_get_settings_config();
|
||||
|
||||
foreach ($groups as $group => $settings) {
|
||||
foreach ($settings as $name => $config) {
|
||||
register_setting($group, $name, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings Configuration (grouped)
|
||||
*/
|
||||
function igny8_get_settings_config() {
|
||||
return [
|
||||
'igny8_table_settings' => [
|
||||
'igny8_records_per_page' => [
|
||||
'type' => 'integer',
|
||||
'default' => 20,
|
||||
'sanitize_callback' => 'absint'
|
||||
]
|
||||
],
|
||||
'igny8_ai_integration_settings' => [
|
||||
'igny8_ai_cluster_building' => ['type' => 'string', 'default' => 'enabled', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_ai_content_ideas' => ['type' => 'string', 'default' => 'enabled', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_ai_auto_mapping' => ['type' => 'string', 'default' => 'enabled', 'sanitize_callback' => 'sanitize_text_field']
|
||||
],
|
||||
'igny8_api_settings' => [
|
||||
'igny8_api_key' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_runware_api_key' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_model' => ['type' => 'string', 'default' => 'gpt-4.1', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_image_service' => ['type' => 'string', 'default' => 'openai', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_image_model' => ['type' => 'string', 'default' => 'dall-e-3', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_runware_model' => ['type' => 'string', 'default' => 'runware:97@1', 'sanitize_callback' => 'sanitize_text_field']
|
||||
],
|
||||
'igny8_personalize_settings_group' => [
|
||||
'igny8_content_engine_global_status' => ['sanitize_callback' => 'igny8_sanitize_checkbox_setting'],
|
||||
'igny8_content_engine_enabled_post_types' => ['sanitize_callback' => 'igny8_sanitize_array_setting'],
|
||||
'igny8_content_engine_insertion_position' => ['sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_content_engine_display_mode' => ['sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_content_engine_teaser_text' => ['sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'igny8_content_engine_save_variations' => ['sanitize_callback' => 'intval'],
|
||||
'igny8_content_engine_field_mode' => ['sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_content_engine_detection_prompt' => ['sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'igny8_content_engine_context_source' => ['sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'igny8_content_engine_include_page_context' => ['sanitize_callback' => 'intval'],
|
||||
'igny8_content_engine_content_length' => ['sanitize_callback' => 'sanitize_text_field'],
|
||||
'igny8_content_engine_rewrite_prompt' => ['sanitize_callback' => 'sanitize_textarea_field'],
|
||||
'igny8_content_engine_fixed_fields_config' => ['sanitize_callback' => 'igny8_sanitize_fields_config']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* SANITIZATION HELPERS
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function igny8_sanitize_checkbox_setting($raw) {
|
||||
return isset($_POST['igny8_content_engine_global_status']) ? 'enabled' : 'disabled';
|
||||
}
|
||||
|
||||
function igny8_sanitize_array_setting($raw) {
|
||||
return is_array($raw) ? array_map('sanitize_text_field', $raw) : [];
|
||||
}
|
||||
|
||||
function igny8_sanitize_fields_config($raw) {
|
||||
if (!is_array($raw)) return [];
|
||||
$sanitized = [];
|
||||
foreach ($raw as $index => $field) {
|
||||
$sanitized[$index] = [
|
||||
'label' => sanitize_text_field($field['label'] ?? ''),
|
||||
'type' => sanitize_text_field($field['type'] ?? 'text'),
|
||||
'options' => sanitize_text_field($field['options'] ?? '')
|
||||
];
|
||||
}
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
|
||||
// MOVED TO: igny8.php - Admin assets enqueuing moved to main plugin file
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// WORDPRESS FEATURE REGISTRATION
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function igny8_init_wordpress_features() {
|
||||
// Initialize module manager
|
||||
add_action('init', 'igny8_module_manager');
|
||||
|
||||
// Register taxonomies
|
||||
add_action('init', 'igny8_register_taxonomies');
|
||||
|
||||
// Register post meta once
|
||||
add_action('init', function() {
|
||||
if (!get_option('igny8_post_meta_registered')) {
|
||||
igny8_register_post_meta();
|
||||
update_option('igny8_post_meta_registered', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Initialize WordPress features
|
||||
igny8_init_wordpress_features();
|
||||
356
igny8-ai-seo-wp-plugin/core/admin/menu.php
Normal file
356
igny8-ai-seo-wp-plugin/core/admin/menu.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : menu.php
|
||||
* @location : /core/admin/menu.php
|
||||
* @type : Admin Menu Handler
|
||||
* @scope : Global
|
||||
* @allowed : WordPress admin menu registration, navigation helpers, layout functions
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Registers admin menus and provides breadcrumb/submenu rendering functions
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render breadcrumb navigation
|
||||
*/
|
||||
function igny8_render_breadcrumb() {
|
||||
$current_page = $_GET['page'] ?? '';
|
||||
$sm = $_GET['sm'] ?? '';
|
||||
$breadcrumb = '<nav class="igny8-breadcrumb-nav">';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-home') . '">Igny8 Home</a></span>';
|
||||
|
||||
if ($current_page === 'igny8-planner') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-planner') . '">Planner</a></span>';
|
||||
|
||||
if ($sm === 'keywords') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Keywords</span>';
|
||||
} elseif ($sm === 'clusters') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Clusters</span>';
|
||||
} elseif ($sm === 'ideas') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Ideas</span>';
|
||||
} elseif ($sm === 'mapping') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Mapping</span>';
|
||||
}
|
||||
} elseif ($current_page === 'igny8-writer') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-writer') . '">Writer</a></span>';
|
||||
|
||||
if ($sm === 'drafts') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Drafts</span>';
|
||||
} elseif ($sm === 'templates') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Templates</span>';
|
||||
}
|
||||
} elseif ($current_page === 'igny8-optimizer') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-optimizer') . '">Optimizer</a></span>';
|
||||
|
||||
if ($sm === 'audits') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Audits</span>';
|
||||
} elseif ($sm === 'suggestions') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Suggestions</span>';
|
||||
}
|
||||
} elseif ($current_page === 'igny8-linker') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-linker') . '">Linker</a></span>';
|
||||
|
||||
if ($sm === 'backlinks') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Backlinks</span>';
|
||||
} elseif ($sm === 'campaigns') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Campaigns</span>';
|
||||
}
|
||||
} elseif ($current_page === 'igny8-personalize') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item"><a href="' . admin_url('admin.php?page=igny8-personalize') . '">Personalize</a></span>';
|
||||
|
||||
if ($sm === 'settings') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Settings</span>';
|
||||
} elseif ($sm === 'content-generation') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Content Generation</span>';
|
||||
} elseif ($sm === 'rewrites') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Rewrites</span>';
|
||||
} elseif ($sm === 'front-end') {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Front-end</span>';
|
||||
}
|
||||
} elseif (strpos($current_page, 'igny8-analytics') !== false) {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Analytics</span>';
|
||||
} elseif (strpos($current_page, 'igny8-schedules') !== false) {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Schedules</span>';
|
||||
} elseif (strpos($current_page, 'igny8-settings') !== false) {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Settings</span>';
|
||||
} elseif (strpos($current_page, 'igny8-help') !== false) {
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-separator">›</span>';
|
||||
$breadcrumb .= '<span class="igny8-breadcrumb-item active">Help</span>';
|
||||
}
|
||||
|
||||
$breadcrumb .= '</nav>';
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render submenu navigation
|
||||
*/
|
||||
function igny8_render_submenu() {
|
||||
$current_page = $_GET['page'] ?? '';
|
||||
$sm = $_GET['sm'] ?? '';
|
||||
$submenu = '';
|
||||
|
||||
if ($current_page === 'igny8-planner') {
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-planner&sm=home') . '" class="igny8-btn igny8-btn-sm igny8-btn-success igny8-btn-submenu' . ($sm === 'home' || $sm === '' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-planner&sm=keywords') . '" class="igny8-btn igny8-btn-sm igny8-btn-success igny8-btn-submenu' . ($sm === 'keywords' ? ' active' : '') . '">Keywords</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-planner&sm=clusters') . '" class="igny8-btn igny8-btn-sm igny8-btn-success igny8-btn-submenu' . ($sm === 'clusters' ? ' active' : '') . '">Clusters</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-planner&sm=ideas') . '" class="igny8-btn igny8-btn-sm igny8-btn-success igny8-btn-submenu' . ($sm === 'ideas' ? ' active' : '') . '">Ideas</a>';
|
||||
} elseif ($current_page === 'igny8-writer') {
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-writer&sm=home') . '" class="igny8-btn igny8-btn-sm igny8-btn-primary igny8-btn-submenu' . ($sm === 'home' || $sm === '' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-writer&sm=tasks') . '" class="igny8-btn igny8-btn-sm igny8-btn-primary igny8-btn-submenu' . ($sm === 'tasks' ? ' active' : '') . '">Tasks</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-writer&sm=drafts') . '" class="igny8-btn igny8-btn-sm igny8-btn-primary igny8-btn-submenu' . ($sm === 'drafts' ? ' active' : '') . '">Drafts</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-writer&sm=published') . '" class="igny8-btn igny8-btn-sm igny8-btn-primary igny8-btn-submenu' . ($sm === 'published' ? ' active' : '') . '">Published</a>';
|
||||
} elseif ($current_page === 'igny8-thinker') {
|
||||
$sp = $_GET['sp'] ?? 'main';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-thinker&sp=main') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'main' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-thinker&sp=prompts') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'prompts' ? ' active' : '') . '">Prompts</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-thinker&sp=profile') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'profile' ? ' active' : '') . '">Profile</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-thinker&sp=strategies') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'strategies' ? ' active' : '') . '">Strategies</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-thinker&sp=image-testing') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'image-testing' ? ' active' : '') . '">Image Testing</a>';
|
||||
} elseif ($current_page === 'igny8-optimizer') {
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-optimizer') . '" class="igny8-btn igny8-btn-sm igny8-btn-warning igny8-btn-submenu' . ($sm === '' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-optimizer&sm=audits') . '" class="igny8-btn igny8-btn-sm igny8-btn-warning igny8-btn-submenu' . ($sm === 'audits' ? ' active' : '') . '">Audits</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-optimizer&sm=suggestions') . '" class="igny8-btn igny8-btn-sm igny8-btn-warning igny8-btn-submenu' . ($sm === 'suggestions' ? ' active' : '') . '">Suggestions</a>';
|
||||
} elseif ($current_page === 'igny8-linker') {
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-linker') . '" class="igny8-btn igny8-btn-sm igny8-btn-info igny8-btn-submenu' . ($sm === '' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-linker&sm=backlinks') . '" class="igny8-btn igny8-btn-sm igny8-btn-info igny8-btn-submenu' . ($sm === 'backlinks' ? ' active' : '') . '">Backlinks</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-linker&sm=campaigns') . '" class="igny8-btn igny8-btn-sm igny8-btn-info igny8-btn-submenu' . ($sm === 'campaigns' ? ' active' : '') . '">Campaigns</a>';
|
||||
} elseif ($current_page === 'igny8-personalize') {
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-personalize') . '" class="igny8-btn igny8-btn-sm igny8-btn-secondary igny8-btn-submenu' . ($sm === '' ? ' active' : '') . '">Dashboard</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-personalize&sm=settings') . '" class="igny8-btn igny8-btn-sm igny8-btn-secondary igny8-btn-submenu' . ($sm === 'settings' ? ' active' : '') . '">Settings</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-personalize&sm=content-generation') . '" class="igny8-btn igny8-btn-sm igny8-btn-secondary igny8-btn-submenu' . ($sm === 'content-generation' ? ' active' : '') . '">Content Generation</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-personalize&sm=rewrites') . '" class="igny8-btn igny8-btn-sm igny8-btn-secondary igny8-btn-submenu' . ($sm === 'rewrites' ? ' active' : '') . '">Rewrites</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-personalize&sm=front-end') . '" class="igny8-btn igny8-btn-sm igny8-btn-secondary igny8-btn-submenu' . ($sm === 'front-end' ? ' active' : '') . '">Front-end</a>';
|
||||
} elseif ($current_page === 'igny8-settings') {
|
||||
$sp = $_GET['sp'] ?? 'general';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-settings&sp=general') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'general' ? ' active' : '') . '">Settings</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-settings&sp=status') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'status' ? ' active' : '') . '">Status</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-settings&sp=integration') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'integration' ? ' active' : '') . '">Integration</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-settings&sp=import-export') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'import-export' ? ' active' : '') . '">Import/Export</a>';
|
||||
} elseif ($current_page === 'igny8-help') {
|
||||
$sp = $_GET['sp'] ?? 'help';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-help&sp=help') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'help' ? ' active' : '') . '">Help & Support</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-help&sp=docs') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'docs' ? ' active' : '') . '">Documentation</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-help&sp=system-testing') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'system-testing' ? ' active' : '') . '">System Testing</a>';
|
||||
$submenu .= '<a href="' . admin_url('admin.php?page=igny8-help&sp=function-testing') . '" class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-btn-submenu' . ($sp === 'function-testing' ? ' active' : '') . '">Function Testing</a>';
|
||||
}
|
||||
|
||||
return $submenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register admin menu pages
|
||||
*/
|
||||
function igny8_register_admin_menu() {
|
||||
// Ensure module manager is available
|
||||
if (!function_exists('igny8_is_module_enabled')) {
|
||||
return;
|
||||
}
|
||||
// Main menu page
|
||||
add_menu_page(
|
||||
'Igny8 AI SEO', // Page title
|
||||
'Igny8 AI SEO', // Menu title
|
||||
'manage_options', // Capability
|
||||
'igny8-home', // Menu slug
|
||||
'igny8_home_page', // Callback function
|
||||
'dashicons-chart-line', // Icon
|
||||
30 // Position
|
||||
);
|
||||
|
||||
// Home page
|
||||
add_submenu_page(
|
||||
'igny8-home', // Parent slug
|
||||
'Dashboard', // Page title
|
||||
'Dashboard', // Menu title
|
||||
'manage_options', // Capability
|
||||
'igny8-home', // Menu slug
|
||||
'igny8_home_page' // Callback function
|
||||
);
|
||||
|
||||
// Module submenus (only if enabled)
|
||||
if (igny8_is_module_enabled('planner')) {
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Content Planner',
|
||||
'Planner',
|
||||
'manage_options',
|
||||
'igny8-planner',
|
||||
'igny8_planner_page'
|
||||
);
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('writer')) {
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Content Writer',
|
||||
'Writer',
|
||||
'manage_options',
|
||||
'igny8-writer',
|
||||
'igny8_writer_page'
|
||||
);
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('thinker')) {
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'AI Thinker',
|
||||
'Thinker',
|
||||
'manage_options',
|
||||
'igny8-thinker',
|
||||
'igny8_thinker_page'
|
||||
);
|
||||
|
||||
// Prompts subpage under Thinker
|
||||
add_submenu_page(
|
||||
'igny8-thinker',
|
||||
'AI Prompts',
|
||||
'Prompts',
|
||||
'manage_options',
|
||||
'igny8-thinker&sp=prompts',
|
||||
'igny8_thinker_page'
|
||||
);
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('schedules')) {
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Smart Automation Schedules',
|
||||
'Schedules',
|
||||
'manage_options',
|
||||
'igny8-schedules',
|
||||
'igny8_schedules_page'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Analytics before Settings (only if enabled)
|
||||
if (igny8_is_module_enabled('analytics')) {
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Analytics',
|
||||
'Analytics',
|
||||
'manage_options',
|
||||
'igny8-analytics',
|
||||
'igny8_analytics_page'
|
||||
);
|
||||
}
|
||||
|
||||
// Cron Health page
|
||||
|
||||
// Settings page
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Settings',
|
||||
'Settings',
|
||||
'manage_options',
|
||||
'igny8-settings',
|
||||
'igny8_settings_page'
|
||||
);
|
||||
|
||||
|
||||
// Help page
|
||||
add_submenu_page(
|
||||
'igny8-home',
|
||||
'Help',
|
||||
'Help',
|
||||
'manage_options',
|
||||
'igny8-help',
|
||||
'igny8_help_page'
|
||||
);
|
||||
|
||||
// Documentation subpage under Help
|
||||
add_submenu_page(
|
||||
'igny8-help',
|
||||
'Documentation',
|
||||
'Documentation',
|
||||
'manage_options',
|
||||
'igny8-help&sp=docs',
|
||||
'igny8_help_page'
|
||||
);
|
||||
|
||||
// System Testing subpage under Help
|
||||
add_submenu_page(
|
||||
'igny8-help',
|
||||
'System Testing',
|
||||
'System Testing',
|
||||
'manage_options',
|
||||
'igny8-help&sp=system-testing',
|
||||
'igny8_help_page'
|
||||
);
|
||||
|
||||
// Function Testing subpage under Help
|
||||
add_submenu_page(
|
||||
'igny8-help',
|
||||
'Function Testing',
|
||||
'Function Testing',
|
||||
'manage_options',
|
||||
'igny8-help&sp=function-testing',
|
||||
'igny8_help_page'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Static page wrapper functions - each page handles its own layout
|
||||
function igny8_home_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/home.php';
|
||||
}
|
||||
|
||||
function igny8_planner_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/planner/planner.php';
|
||||
}
|
||||
|
||||
function igny8_writer_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/writer/writer.php';
|
||||
}
|
||||
|
||||
function igny8_thinker_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/thinker/thinker.php';
|
||||
}
|
||||
|
||||
function igny8_settings_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/settings/general-settings.php';
|
||||
}
|
||||
|
||||
function igny8_analytics_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/analytics/analytics.php';
|
||||
}
|
||||
|
||||
function igny8_schedules_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/settings/schedules.php';
|
||||
}
|
||||
|
||||
function igny8_help_page() {
|
||||
include_once plugin_dir_path(__FILE__) . '../../modules/help/help.php';
|
||||
}
|
||||
|
||||
// Hook into admin_menu
|
||||
add_action('admin_menu', 'igny8_register_admin_menu');
|
||||
387
igny8-ai-seo-wp-plugin/core/admin/meta-boxes.php
Normal file
387
igny8-ai-seo-wp-plugin/core/admin/meta-boxes.php
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : meta-boxes.php
|
||||
* @location : /core/admin/meta-boxes.php
|
||||
* @type : Function Library
|
||||
* @scope : Global
|
||||
* @allowed : Meta box registration, SEO field management
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : SEO meta boxes for post editor
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// === SEO Meta Box ===
|
||||
add_action('add_meta_boxes', function() {
|
||||
// SEO fields
|
||||
add_meta_box('igny8_seo_meta', 'Igny8 SEO Fields', function($post) {
|
||||
$meta_title = get_post_meta($post->ID, '_igny8_meta_title', true);
|
||||
$meta_desc = get_post_meta($post->ID, '_igny8_meta_description', true);
|
||||
$primary_kw = get_post_meta($post->ID, '_igny8_primary_keywords', true);
|
||||
$secondary_kw = get_post_meta($post->ID, '_igny8_secondary_keywords', true);
|
||||
?>
|
||||
<div style="padding:8px 4px;">
|
||||
<label><strong>Meta Title:</strong></label><br>
|
||||
<input type="text" name="_igny8_meta_title" value="<?php echo esc_attr($meta_title); ?>" style="width:100%;"><br><br>
|
||||
|
||||
<label><strong>Meta Description:</strong></label><br>
|
||||
<textarea name="_igny8_meta_description" rows="3" style="width:100%;"><?php echo esc_textarea($meta_desc); ?></textarea><br><br>
|
||||
|
||||
<label><strong>Primary Keyword:</strong></label><br>
|
||||
<input type="text" name="_igny8_primary_keywords" value="<?php echo esc_attr($primary_kw); ?>" style="width:100%;"><br><br>
|
||||
|
||||
<label><strong>Secondary Keywords (comma-separated):</strong></label><br>
|
||||
<input type="text" name="_igny8_secondary_keywords" value="<?php echo esc_attr($secondary_kw); ?>" style="width:100%;">
|
||||
</div>
|
||||
<?php
|
||||
}, ['post','page','product'], 'normal', 'high');
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
// === Save Meta Box Data ===
|
||||
add_action('save_post', function($post_id) {
|
||||
// Security checks
|
||||
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save SEO fields
|
||||
$fields = [
|
||||
'_igny8_meta_title',
|
||||
'_igny8_meta_description',
|
||||
'_igny8_primary_keywords',
|
||||
'_igny8_secondary_keywords',
|
||||
];
|
||||
foreach ($fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
update_post_meta($post_id, $field, sanitize_text_field($_POST[$field]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||
error_log("Igny8 Metabox: SEO fields saved for post $post_id");
|
||||
}
|
||||
});
|
||||
|
||||
// === In-Article Image Gallery Meta Box ===
|
||||
add_action('add_meta_boxes', function() {
|
||||
$enabled_post_types = get_option('igny8_enable_image_metabox', []);
|
||||
foreach ((array) $enabled_post_types as $pt) {
|
||||
add_meta_box(
|
||||
'igny8_image_gallery',
|
||||
'Igny8 In-Article Images',
|
||||
'igny8_render_image_gallery_metabox',
|
||||
$pt,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function igny8_render_image_gallery_metabox($post) {
|
||||
wp_nonce_field('igny8_save_image_gallery', 'igny8_image_gallery_nonce');
|
||||
$images = get_post_meta($post->ID, '_igny8_inarticle_images', true);
|
||||
if (!is_array($images)) $images = [];
|
||||
|
||||
// Add CSS for grid layout and remove button
|
||||
?>
|
||||
<style>
|
||||
.igny8-image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 5px;
|
||||
}
|
||||
.igny8-image-list li {
|
||||
width: 200px;
|
||||
margin: 0 5px 5px 0;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
border: 1px solid #eee;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 180px;
|
||||
}
|
||||
.igny8-image-list li img {
|
||||
display: block;
|
||||
margin: 0 auto 5px auto;
|
||||
}
|
||||
.igny8-image-actions {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
z-index: 10;
|
||||
}
|
||||
.igny8-image-list li:hover .igny8-image-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
.igny8-remove-btn, .igny8-replace-btn {
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
color: #333;
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.igny8-remove-btn:hover {
|
||||
background: #dc3232;
|
||||
color: #fff;
|
||||
border-color: #dc3232;
|
||||
}
|
||||
.igny8-replace-btn:hover {
|
||||
background: #0073aa;
|
||||
color: #fff;
|
||||
border-color: #0073aa;
|
||||
}
|
||||
.igny8-image-list li strong {
|
||||
font-size: 0.8em;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
|
||||
echo '<div id="igny8-image-gallery">';
|
||||
echo '<p><label><input type="radio" name="igny8_image_type" value="desktop" checked> Desktop</label>
|
||||
<label><input type="radio" name="igny8_image_type" value="mobile"> Mobile</label></p>';
|
||||
echo '<button type="button" class="button button-primary" id="igny8-add-image">Add Image</button>';
|
||||
|
||||
|
||||
|
||||
|
||||
echo '<ul class="igny8-image-list">';
|
||||
|
||||
// Sort images by device type and ID
|
||||
$sorted_images = [];
|
||||
foreach ($images as $label => $data) {
|
||||
$sorted_images[$label] = $data;
|
||||
}
|
||||
|
||||
// Custom sort function to order by device type (desktop first) then by ID
|
||||
uksort($sorted_images, function($a, $b) {
|
||||
$a_parts = explode('-', $a);
|
||||
$b_parts = explode('-', $b);
|
||||
|
||||
$a_device = $a_parts[0];
|
||||
$b_device = $b_parts[0];
|
||||
|
||||
// Desktop comes before mobile
|
||||
if ($a_device === 'desktop' && $b_device === 'mobile') return -1;
|
||||
if ($a_device === 'mobile' && $b_device === 'desktop') return 1;
|
||||
|
||||
// If same device, sort by ID number
|
||||
if ($a_device === $b_device) {
|
||||
$a_id = intval($a_parts[1]);
|
||||
$b_id = intval($b_parts[1]);
|
||||
return $a_id - $b_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
foreach ($sorted_images as $label => $data) {
|
||||
echo '<li data-label="' . esc_attr($label) . '">';
|
||||
echo '<strong>' . esc_html($label) . '</strong><br>';
|
||||
echo wp_get_attachment_image($data['attachment_id'], 'thumbnail', false, ['style' => 'width: 150px; height: 150px; object-fit: cover;']);
|
||||
echo '<div class="igny8-image-actions">';
|
||||
echo '<button type="button" class="igny8-replace-btn">Replace</button>';
|
||||
echo '<button type="button" class="igny8-remove-btn">Remove</button>';
|
||||
echo '</div>';
|
||||
echo '<input type="hidden" name="igny8_image_data[' . esc_attr($label) . '][attachment_id]" value="' . esc_attr($data['attachment_id']) . '">';
|
||||
echo '<input type="hidden" name="igny8_image_data[' . esc_attr($label) . '][device]" value="' . esc_attr($data['device']) . '">';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
|
||||
// Inline JS
|
||||
?>
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
// Function to get first available ID for a device type
|
||||
function getFirstAvailableId(deviceType) {
|
||||
let existingIds = [];
|
||||
$('.igny8-image-list li').each(function() {
|
||||
let label = $(this).data('label');
|
||||
if (label && label.startsWith(deviceType + '-')) {
|
||||
let id = parseInt(label.split('-')[1]);
|
||||
if (!isNaN(id)) {
|
||||
existingIds.push(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort existing IDs and find first gap
|
||||
existingIds.sort((a, b) => a - b);
|
||||
for (let i = 1; i <= existingIds.length + 1; i++) {
|
||||
if (!existingIds.includes(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 1; // Fallback
|
||||
}
|
||||
|
||||
$('#igny8-add-image').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
let type = $('input[name="igny8_image_type"]:checked').val() || 'desktop';
|
||||
let availableId = getFirstAvailableId(type);
|
||||
let label = type + '-' + availableId;
|
||||
|
||||
const frame = wp.media({
|
||||
title: 'Select Image',
|
||||
button: { text: 'Use this image' },
|
||||
multiple: false
|
||||
});
|
||||
|
||||
frame.on('select', function() {
|
||||
let attachment = frame.state().get('selection').first().toJSON();
|
||||
let html = '<li data-label="' + label + '">' +
|
||||
'<strong>' + label + '</strong><br>' +
|
||||
'<img src="' + attachment.sizes.thumbnail.url + '" style="width: 150px; height: 150px; object-fit: cover;" /><br>' +
|
||||
'<div class="igny8-image-actions">' +
|
||||
'<button type="button" class="igny8-replace-btn">Replace</button>' +
|
||||
'<button type="button" class="igny8-remove-btn">Remove</button>' +
|
||||
'</div>' +
|
||||
'<input type="hidden" name="igny8_image_data[' + label + '][attachment_id]" value="' + attachment.id + '">' +
|
||||
'<input type="hidden" name="igny8_image_data[' + label + '][device]" value="' + type + '">' +
|
||||
'</li>';
|
||||
$('.igny8-image-list').append(html);
|
||||
});
|
||||
|
||||
frame.open();
|
||||
});
|
||||
|
||||
// Handle image removal (event delegation for dynamically added elements)
|
||||
$(document).on('click', '.igny8-remove-btn', function() {
|
||||
$(this).closest('li').remove();
|
||||
});
|
||||
|
||||
// Handle image replacement (event delegation for dynamically added elements)
|
||||
$(document).on('click', '.igny8-replace-btn', function() {
|
||||
let $li = $(this).closest('li');
|
||||
let label = $li.data('label');
|
||||
let type = label.split('-')[0];
|
||||
|
||||
const frame = wp.media({
|
||||
title: 'Replace Image',
|
||||
button: { text: 'Replace this image' },
|
||||
multiple: false
|
||||
});
|
||||
|
||||
frame.on('select', function() {
|
||||
let attachment = frame.state().get('selection').first().toJSON();
|
||||
|
||||
// Replace the entire img element to force reload
|
||||
let $img = $li.find('img');
|
||||
let newImg = $('<img>').attr({
|
||||
'src': attachment.sizes.thumbnail.url,
|
||||
'style': 'width: 150px; height: 150px; object-fit: cover;'
|
||||
});
|
||||
$img.replaceWith(newImg);
|
||||
|
||||
// Update the hidden input
|
||||
$li.find('input[name*="[attachment_id]"]').val(attachment.id);
|
||||
});
|
||||
|
||||
frame.open();
|
||||
});
|
||||
|
||||
// Handle Convert Content to Blocks
|
||||
$('#igny8-convert-to-blocks').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!confirm('This will convert the post content from HTML to WordPress blocks. Continue?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let $button = $(this);
|
||||
let originalText = $button.text();
|
||||
$button.prop('disabled', true).text('Converting...');
|
||||
|
||||
// Get post ID from the current post
|
||||
let postId = $('#post_ID').val();
|
||||
|
||||
// Make AJAX request to convert content to blocks
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'igny8_convert_content_to_blocks',
|
||||
post_id: postId,
|
||||
nonce: '<?php echo wp_create_nonce('igny8_convert_to_blocks'); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('Convert to Blocks Response:', response);
|
||||
if (response.success) {
|
||||
console.log('Success data:', response.data);
|
||||
alert('Content converted successfully! ' + response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
console.error('Error data:', response.data);
|
||||
alert('Error: ' + (response.data || 'Unknown error'));
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Convert to Blocks Error:', {status, error, responseText: xhr.responseText});
|
||||
alert('Error converting content. Check console for details.');
|
||||
},
|
||||
complete: function() {
|
||||
$button.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
// SAVE HANDLER for Image Gallery
|
||||
add_action('save_post', function($post_id) {
|
||||
if (!isset($_POST['igny8_image_gallery_nonce']) || !wp_verify_nonce($_POST['igny8_image_gallery_nonce'], 'igny8_save_image_gallery')) return;
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||||
if (!current_user_can('edit_post', $post_id)) return;
|
||||
|
||||
$images = $_POST['igny8_image_data'] ?? [];
|
||||
$filtered = [];
|
||||
foreach ($images as $label => $data) {
|
||||
if (!empty($data['attachment_id'])) {
|
||||
$filtered[$label] = [
|
||||
'label' => sanitize_text_field($label),
|
||||
'attachment_id' => intval($data['attachment_id']),
|
||||
'url' => wp_get_attachment_url(intval($data['attachment_id'])),
|
||||
'device' => sanitize_text_field($data['device'])
|
||||
];
|
||||
}
|
||||
}
|
||||
update_post_meta($post_id, '_igny8_inarticle_images', $filtered);
|
||||
|
||||
if (WP_DEBUG === true) {
|
||||
error_log("[IGNY8 DEBUG] Saving In-Article Images for Post ID: $post_id");
|
||||
error_log(print_r($filtered, true));
|
||||
}
|
||||
});
|
||||
|
||||
181
igny8-ai-seo-wp-plugin/core/admin/module-manager-class.php
Normal file
181
igny8-ai-seo-wp-plugin/core/admin/module-manager-class.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : module-manager-class.php
|
||||
* @location : /core/admin/module-manager-class.php
|
||||
* @type : Function Library
|
||||
* @scope : Global
|
||||
* @allowed : Module management, class definitions, core functionality
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Module manager class for core functionality
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Igny8 Module Manager - Controls which modules are active
|
||||
*/
|
||||
class Igny8_Module_Manager {
|
||||
|
||||
private static $instance = null;
|
||||
private $modules = [];
|
||||
|
||||
public static function get_instance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {
|
||||
$this->init_modules();
|
||||
add_action('admin_init', [$this, 'register_module_settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize module definitions - main modules only
|
||||
*/
|
||||
private function init_modules() {
|
||||
$this->modules = [
|
||||
'planner' => [
|
||||
'name' => 'Planner',
|
||||
'description' => 'Keyword research and content planning with clusters, ideas, and mapping tools.',
|
||||
'default' => true,
|
||||
'icon' => 'dashicons-search',
|
||||
'category' => 'main',
|
||||
'cron_jobs' => [
|
||||
'igny8_auto_cluster_cron',
|
||||
'igny8_auto_generate_ideas_cron',
|
||||
'igny8_auto_queue_cron'
|
||||
]
|
||||
],
|
||||
'writer' => [
|
||||
'name' => 'Writer',
|
||||
'description' => 'AI-powered content generation with drafts and templates management.',
|
||||
'default' => false,
|
||||
'icon' => 'dashicons-edit',
|
||||
'category' => 'main',
|
||||
'cron_jobs' => [
|
||||
'igny8_auto_generate_content_cron',
|
||||
'igny8_auto_generate_images_cron',
|
||||
'igny8_auto_publish_drafts_cron'
|
||||
]
|
||||
],
|
||||
'analytics' => [
|
||||
'name' => 'Analytics',
|
||||
'description' => 'Performance tracking and data analysis with comprehensive reporting.',
|
||||
'default' => false,
|
||||
'icon' => 'dashicons-chart-bar',
|
||||
'category' => 'admin',
|
||||
'cron_jobs' => [
|
||||
'igny8_process_ai_queue_cron',
|
||||
'igny8_auto_recalc_cron',
|
||||
'igny8_health_check_cron'
|
||||
]
|
||||
],
|
||||
'schedules' => [
|
||||
'name' => 'Schedules',
|
||||
'description' => 'Content scheduling and automation with calendar management.',
|
||||
'default' => false,
|
||||
'icon' => 'dashicons-calendar-alt',
|
||||
'category' => 'admin'
|
||||
],
|
||||
'thinker' => [
|
||||
'name' => 'AI Thinker',
|
||||
'description' => 'AI-powered content strategy, prompts, and intelligent content planning tools.',
|
||||
'default' => true,
|
||||
'icon' => 'dashicons-lightbulb',
|
||||
'category' => 'admin'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a module is enabled
|
||||
*/
|
||||
public function is_module_enabled($module) {
|
||||
$settings = get_option('igny8_module_settings', []);
|
||||
return isset($settings[$module]) ? (bool) $settings[$module] : (isset($this->modules[$module]) ? $this->modules[$module]['default'] : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all enabled modules
|
||||
*/
|
||||
public function get_enabled_modules() {
|
||||
$enabled = [];
|
||||
foreach ($this->modules as $key => $module) {
|
||||
if ($this->is_module_enabled($key)) {
|
||||
$enabled[$key] = $module;
|
||||
}
|
||||
}
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all modules
|
||||
*/
|
||||
public function get_modules() {
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register module settings
|
||||
*/
|
||||
public function register_module_settings() {
|
||||
register_setting('igny8_module_settings', 'igny8_module_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save module settings
|
||||
*/
|
||||
public function save_module_settings() {
|
||||
if (!isset($_POST['igny8_module_nonce']) || !wp_verify_nonce($_POST['igny8_module_nonce'], 'igny8_module_settings')) {
|
||||
wp_die('Security check failed');
|
||||
}
|
||||
|
||||
$settings = $_POST['igny8_module_settings'] ?? [];
|
||||
|
||||
// Initialize all modules as disabled first
|
||||
$all_modules = $this->get_modules();
|
||||
$final_settings = [];
|
||||
foreach ($all_modules as $module_key => $module) {
|
||||
$final_settings[$module_key] = false; // Default to disabled
|
||||
}
|
||||
|
||||
// Set enabled modules to true
|
||||
foreach ($settings as $key => $value) {
|
||||
if (isset($final_settings[$key])) {
|
||||
$final_settings[$key] = (bool) $value;
|
||||
}
|
||||
}
|
||||
|
||||
update_option('igny8_module_settings', $final_settings);
|
||||
|
||||
// Force page reload using JavaScript
|
||||
echo '<script>window.location.reload();</script>';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the module manager
|
||||
function igny8_module_manager() {
|
||||
return Igny8_Module_Manager::get_instance();
|
||||
}
|
||||
|
||||
// Helper functions for easy access
|
||||
function igny8_is_module_enabled($module) {
|
||||
return igny8_module_manager()->is_module_enabled($module);
|
||||
}
|
||||
|
||||
function igny8_get_enabled_modules() {
|
||||
return igny8_module_manager()->get_enabled_modules();
|
||||
}
|
||||
|
||||
function igny8_get_modules() {
|
||||
return igny8_module_manager()->get_modules();
|
||||
}
|
||||
1610
igny8-ai-seo-wp-plugin/core/cron/igny8-cron-handlers.php
Normal file
1610
igny8-ai-seo-wp-plugin/core/cron/igny8-cron-handlers.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : igny8-cron-master-dispatcher.php
|
||||
* @location : /core/cron/igny8-cron-master-dispatcher.php
|
||||
* @type : CRON Handler
|
||||
* @scope : Global
|
||||
* @allowed : Cron scheduling, automation dispatch, resource management
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Central cron dispatcher for all automation jobs
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Master Dispatcher - Main execution function
|
||||
*
|
||||
* Runs every 5 minutes via cPanel, checks database schedules,
|
||||
* and executes only due automations with proper limits and timing.
|
||||
*/
|
||||
function igny8_master_dispatcher_run() {
|
||||
echo "<div style='background:#e8f4fd;padding:10px;margin:5px;border:1px solid #2196F3;'>";
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Starting smart automation check</strong><br>";
|
||||
error_log("Igny8 MASTER DISPATCHER: Starting smart automation check");
|
||||
|
||||
// Get all defined cron jobs
|
||||
$cron_jobs = igny8_get_defined_cron_jobs();
|
||||
$current_time = current_time('timestamp');
|
||||
$executed_jobs = [];
|
||||
$skipped_jobs = [];
|
||||
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Found " . count($cron_jobs) . " defined jobs</strong><br>";
|
||||
|
||||
// Get settings and limits
|
||||
$cron_settings = get_option('igny8_cron_settings', []);
|
||||
$cron_limits = get_option('igny8_cron_limits', []);
|
||||
|
||||
// Initialize default settings if missing
|
||||
if (empty($cron_settings)) {
|
||||
$cron_settings = igny8_get_default_cron_settings();
|
||||
update_option('igny8_cron_settings', $cron_settings);
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Initialized default settings</strong><br>";
|
||||
}
|
||||
|
||||
if (empty($cron_limits)) {
|
||||
$cron_limits = igny8_get_default_cron_limits();
|
||||
update_option('igny8_cron_limits', $cron_limits);
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Initialized default limits</strong><br>";
|
||||
}
|
||||
|
||||
// Process each job in priority order
|
||||
foreach ($cron_jobs as $job_name => $job_config) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Checking job: " . $job_name . "</strong><br>";
|
||||
|
||||
// Check if job is enabled
|
||||
$job_settings = $cron_settings[$job_name] ?? [];
|
||||
if (!($job_settings['enabled'] ?? false)) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job disabled, skipping</strong><br>";
|
||||
$skipped_jobs[] = $job_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if job is due (simplified - just check if enabled and not recently run)
|
||||
$last_run = $job_settings['last_run'] ?? 0;
|
||||
$time_since_last_run = $current_time - $last_run;
|
||||
|
||||
// Run job if it hasn't been run in the last 5 minutes (to prevent duplicate runs)
|
||||
if ($time_since_last_run < 300) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job run recently, skipping</strong><br>";
|
||||
$skipped_jobs[] = $job_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if job is already running (duplicate prevention)
|
||||
$lock_key = 'igny8_cron_running_' . $job_name;
|
||||
if (get_transient($lock_key)) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job already running, skipping</strong><br>";
|
||||
$skipped_jobs[] = $job_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set lock for this job
|
||||
$max_execution_time = $job_config['max_execution_time'] ?? 300;
|
||||
set_transient($lock_key, true, $max_execution_time);
|
||||
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Executing job: " . $job_name . "</strong><br>";
|
||||
|
||||
try {
|
||||
// Get job limit
|
||||
$job_limit = $cron_limits[$job_name] ?? 1;
|
||||
|
||||
// Set limit as global variable for handlers to use
|
||||
$GLOBALS['igny8_cron_limit'] = $job_limit;
|
||||
|
||||
// Execute the job
|
||||
$start_time = microtime(true);
|
||||
do_action($job_name);
|
||||
$execution_time = microtime(true) - $start_time;
|
||||
|
||||
// Update last run time
|
||||
$cron_settings[$job_name]['last_run'] = $current_time;
|
||||
update_option('igny8_cron_settings', $cron_settings);
|
||||
|
||||
// Track individual job execution with detailed logging
|
||||
$processed_count = $GLOBALS['igny8_cron_processed_count'] ?? 0;
|
||||
$result_details = $GLOBALS['igny8_cron_result_details'] ?? '';
|
||||
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Global variables - processed_count: $processed_count, result_details: $result_details</strong><br>";
|
||||
|
||||
$job_health = [
|
||||
'last_run' => $current_time,
|
||||
'success' => true,
|
||||
'last_success' => true,
|
||||
'execution_time' => round($execution_time, 2),
|
||||
'error_message' => '',
|
||||
'processed_count' => $processed_count,
|
||||
'result_details' => $result_details,
|
||||
'execution_method' => (isset($_GET['import_key']) && !empty($_GET['import_key'])) ? 'external_url' : 'server_cron'
|
||||
];
|
||||
update_option('igny8_cron_health_' . $job_name, $job_health);
|
||||
|
||||
$executed_jobs[] = [
|
||||
'job' => $job_name,
|
||||
'execution_time' => round($execution_time, 2),
|
||||
'success' => true
|
||||
];
|
||||
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job completed successfully in " . round($execution_time, 2) . "s</strong><br>";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job failed: " . $e->getMessage() . "</strong><br>";
|
||||
error_log("Igny8 MASTER DISPATCHER: Job $job_name failed - " . $e->getMessage());
|
||||
|
||||
// Track individual job failure
|
||||
$job_health = [
|
||||
'last_run' => $current_time,
|
||||
'success' => false,
|
||||
'last_success' => false,
|
||||
'execution_time' => 0,
|
||||
'error_message' => $e->getMessage(),
|
||||
'processed_count' => $GLOBALS['igny8_cron_processed_count'] ?? 0,
|
||||
'result_details' => 'FAILED: ' . $e->getMessage(),
|
||||
'execution_method' => (isset($_GET['import_key']) && !empty($_GET['import_key'])) ? 'external_url' : 'server_cron'
|
||||
];
|
||||
update_option('igny8_cron_health_' . $job_name, $job_health);
|
||||
|
||||
$executed_jobs[] = [
|
||||
'job' => $job_name,
|
||||
'execution_time' => 0,
|
||||
'success' => false,
|
||||
'last_success' => false,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Job fatal error: " . $e->getMessage() . "</strong><br>";
|
||||
error_log("Igny8 MASTER DISPATCHER: Job $job_name fatal error - " . $e->getMessage());
|
||||
|
||||
// Track individual job failure
|
||||
$job_health = [
|
||||
'last_run' => $current_time,
|
||||
'success' => false,
|
||||
'last_success' => false,
|
||||
'execution_time' => 0,
|
||||
'error_message' => $e->getMessage(),
|
||||
'processed_count' => $GLOBALS['igny8_cron_processed_count'] ?? 0,
|
||||
'result_details' => 'FAILED: ' . $e->getMessage(),
|
||||
'execution_method' => (isset($_GET['import_key']) && !empty($_GET['import_key'])) ? 'external_url' : 'server_cron'
|
||||
];
|
||||
update_option('igny8_cron_health_' . $job_name, $job_health);
|
||||
|
||||
$executed_jobs[] = [
|
||||
'job' => $job_name,
|
||||
'execution_time' => 0,
|
||||
'success' => false,
|
||||
'last_success' => false,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
} finally {
|
||||
// Always release the lock
|
||||
delete_transient($lock_key);
|
||||
}
|
||||
}
|
||||
|
||||
// Log summary
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Execution summary</strong><br>";
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Jobs executed: " . count($executed_jobs) . "</strong><br>";
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Jobs skipped: " . count($skipped_jobs) . "</strong><br>";
|
||||
|
||||
// Store execution log
|
||||
update_option('igny8_cron_last_execution', [
|
||||
'timestamp' => $current_time,
|
||||
'executed' => $executed_jobs,
|
||||
'skipped' => $skipped_jobs
|
||||
]);
|
||||
|
||||
echo "<strong>Igny8 MASTER DISPATCHER: Smart automation check completed</strong><br>";
|
||||
echo "</div>";
|
||||
|
||||
// Return success response for external cron
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Master dispatcher executed successfully',
|
||||
'executed' => count($executed_jobs),
|
||||
'skipped' => count($skipped_jobs),
|
||||
'timestamp' => current_time('mysql')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined cron jobs with their configurations
|
||||
*/
|
||||
function igny8_get_defined_cron_jobs() {
|
||||
return [
|
||||
'igny8_auto_cluster_cron' => [
|
||||
'handler' => 'igny8_auto_cluster_cron_handler',
|
||||
'priority' => 1,
|
||||
'max_execution_time' => 600, // 10 minutes
|
||||
'description' => 'Auto cluster unmapped keywords',
|
||||
'module' => 'planner'
|
||||
],
|
||||
'igny8_auto_generate_ideas_cron' => [
|
||||
'handler' => 'igny8_auto_generate_ideas_cron_handler',
|
||||
'priority' => 2,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Auto generate ideas from clusters',
|
||||
'module' => 'planner'
|
||||
],
|
||||
'igny8_auto_queue_cron' => [
|
||||
'handler' => 'igny8_auto_queue_cron_handler',
|
||||
'priority' => 3,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Auto queue new ideas',
|
||||
'module' => 'planner'
|
||||
],
|
||||
'igny8_auto_generate_content_cron' => [
|
||||
'handler' => 'igny8_auto_generate_content_cron_handler',
|
||||
'priority' => 4,
|
||||
'max_execution_time' => 600, // 10 minutes
|
||||
'description' => 'Auto generate content from queued tasks',
|
||||
'module' => 'writer'
|
||||
],
|
||||
'igny8_auto_generate_images_cron' => [
|
||||
'handler' => 'igny8_auto_generate_images_cron_handler',
|
||||
'priority' => 5,
|
||||
'max_execution_time' => 900, // 15 minutes
|
||||
'description' => 'Auto generate images for content',
|
||||
'module' => 'writer'
|
||||
],
|
||||
'igny8_auto_publish_drafts_cron' => [
|
||||
'handler' => 'igny8_auto_publish_drafts_cron_handler',
|
||||
'priority' => 6,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Auto publish completed drafts',
|
||||
'module' => 'writer'
|
||||
],
|
||||
'igny8_process_ai_queue_cron' => [
|
||||
'handler' => 'igny8_process_ai_queue_cron_handler',
|
||||
'priority' => 7,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Process AI queue tasks',
|
||||
'module' => 'ai'
|
||||
],
|
||||
'igny8_auto_recalc_cron' => [
|
||||
'handler' => 'igny8_auto_recalc_cron_handler',
|
||||
'priority' => 8,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Auto recalculate metrics',
|
||||
'module' => 'analytics'
|
||||
],
|
||||
'igny8_auto_optimizer_cron' => [
|
||||
'handler' => 'igny8_auto_optimizer_cron_handler',
|
||||
'priority' => 9,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'Auto optimize content and keywords',
|
||||
'module' => 'optimizer'
|
||||
],
|
||||
'igny8_health_check_cron' => [
|
||||
'handler' => 'igny8_health_check_cron_handler',
|
||||
'priority' => 10,
|
||||
'max_execution_time' => 300, // 5 minutes
|
||||
'description' => 'System health check and cleanup',
|
||||
'module' => 'system'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get default cron settings
|
||||
*/
|
||||
function igny8_get_default_cron_settings() {
|
||||
$jobs = igny8_get_defined_cron_jobs();
|
||||
$settings = [];
|
||||
|
||||
foreach ($jobs as $job_name => $config) {
|
||||
$settings[$job_name] = [
|
||||
'enabled' => false, // Default to disabled
|
||||
'last_run' => 0
|
||||
];
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default cron limits
|
||||
*/
|
||||
function igny8_get_default_cron_limits() {
|
||||
return [
|
||||
'igny8_auto_cluster_cron' => 1,
|
||||
'igny8_auto_generate_ideas_cron' => 1,
|
||||
'igny8_auto_queue_cron' => 1,
|
||||
'igny8_auto_generate_content_cron' => 1,
|
||||
'igny8_auto_generate_images_cron' => 1,
|
||||
'igny8_auto_publish_drafts_cron' => 1,
|
||||
'igny8_process_ai_queue_cron' => 1,
|
||||
'igny8_auto_recalc_cron' => 1,
|
||||
'igny8_auto_optimizer_cron' => 1,
|
||||
'igny8_health_check_cron' => 1
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cron settings for a specific job
|
||||
*/
|
||||
function igny8_update_cron_job_settings($job_name, $settings) {
|
||||
$cron_settings = get_option('igny8_cron_settings', []);
|
||||
$cron_settings[$job_name] = array_merge($cron_settings[$job_name] ?? [], $settings);
|
||||
update_option('igny8_cron_settings', $cron_settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cron limits for a specific job
|
||||
*/
|
||||
function igny8_update_cron_job_limits($job_name, $limit) {
|
||||
$cron_limits = get_option('igny8_cron_limits', []);
|
||||
$cron_limits[$job_name] = $limit;
|
||||
update_option('igny8_cron_limits', $cron_limits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get health status for a specific job
|
||||
*/
|
||||
function igny8_get_job_health_status($job_name) {
|
||||
$health = get_option('igny8_cron_health_' . $job_name, []);
|
||||
$cron_settings = get_option('igny8_cron_settings', []);
|
||||
$job_settings = $cron_settings[$job_name] ?? [];
|
||||
|
||||
return [
|
||||
'enabled' => $job_settings['enabled'] ?? false,
|
||||
'last_run' => isset($health['last_run']) ? date('Y-m-d H:i:s', $health['last_run']) : 'Never',
|
||||
'last_success' => $health['success'] ?? null,
|
||||
'execution_time' => isset($health['execution_time']) ? round($health['execution_time'], 2) : 0,
|
||||
'error_message' => $health['error_message'] ?? '',
|
||||
'processed_count' => $health['processed_count'] ?? 0,
|
||||
'result_details' => $health['result_details'] ?? '',
|
||||
'execution_method' => $health['execution_method'] ?? 'unknown'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cron job status and next run time
|
||||
*/
|
||||
function igny8_get_cron_job_status($job_name) {
|
||||
$cron_settings = get_option('igny8_cron_settings', []);
|
||||
$job_settings = $cron_settings[$job_name] ?? [];
|
||||
|
||||
if (empty($job_settings)) {
|
||||
return [
|
||||
'enabled' => false,
|
||||
'last_run' => 'Never'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'enabled' => $job_settings['enabled'] ?? false,
|
||||
'last_run' => isset($job_settings['last_run']) && $job_settings['last_run'] ? date('Y-m-d H:i:s', $job_settings['last_run']) : 'Never'
|
||||
];
|
||||
}
|
||||
253
igny8-ai-seo-wp-plugin/core/db/db-migration.php
Normal file
253
igny8-ai-seo-wp-plugin/core/db/db-migration.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : db-migration.php
|
||||
* @location : /core/db/db-migration.php
|
||||
* @type : Function Library
|
||||
* @scope : Global
|
||||
* @allowed : Database migrations, schema updates, version tracking
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Database migration system for schema updates
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* MIGRATION SYSTEM TEMPLATE
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get current database version
|
||||
*/
|
||||
function igny8_get_db_version() {
|
||||
return get_option('igny8_db_version', '0.1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set database version
|
||||
*/
|
||||
function igny8_set_db_version($version) {
|
||||
update_option('igny8_db_version', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if migration is needed
|
||||
*/
|
||||
function igny8_is_migration_needed($target_version = null) {
|
||||
if (!$target_version) {
|
||||
$target_version = '0.1'; // Current version
|
||||
}
|
||||
|
||||
$current_version = igny8_get_db_version();
|
||||
return version_compare($current_version, $target_version, '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all pending migrations
|
||||
*/
|
||||
function igny8_run_migrations() {
|
||||
$current_version = igny8_get_db_version();
|
||||
$target_version = '0.1';
|
||||
|
||||
if (!igny8_is_migration_needed($target_version)) {
|
||||
return true; // No migration needed
|
||||
}
|
||||
|
||||
// Example migration structure:
|
||||
// if (version_compare($current_version, '2.7.0', '<')) {
|
||||
// igny8_migration_270();
|
||||
// }
|
||||
|
||||
// if (version_compare($current_version, '2.7.1', '<')) {
|
||||
// igny8_migration_271();
|
||||
// }
|
||||
|
||||
// Update to latest version
|
||||
igny8_set_db_version($target_version);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* MIGRATION FUNCTIONS TEMPLATE
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example migration function template
|
||||
*
|
||||
* @param string $from_version Version migrating from
|
||||
* @param string $to_version Version migrating to
|
||||
*/
|
||||
function igny8_migration_template($from_version, $to_version) {
|
||||
global $wpdb;
|
||||
|
||||
try {
|
||||
// Example: Add new column
|
||||
// $table_name = $wpdb->prefix . 'igny8_example_table';
|
||||
// $column_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'new_column'");
|
||||
// if (!$column_exists) {
|
||||
// $wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `new_column` VARCHAR(255) DEFAULT NULL");
|
||||
// }
|
||||
|
||||
// Example: Create new table
|
||||
// $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}igny8_new_table (
|
||||
// id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
// name VARCHAR(255) NOT NULL,
|
||||
// created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
// PRIMARY KEY (id)
|
||||
// ) {$wpdb->get_charset_collate()};";
|
||||
// dbDelta($sql);
|
||||
|
||||
// Example: Migrate data
|
||||
// $wpdb->query("UPDATE {$wpdb->prefix}igny8_table SET old_field = new_field WHERE condition");
|
||||
|
||||
error_log("Igny8 Migration: Successfully migrated from $from_version to $to_version");
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Igny8 Migration Error: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* MIGRATION UTILITIES
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Backup table before migration
|
||||
*/
|
||||
function igny8_backup_table($table_name, $suffix = null) {
|
||||
global $wpdb;
|
||||
|
||||
if (!$suffix) {
|
||||
$suffix = '_backup_' . date('Y_m_d_H_i_s');
|
||||
}
|
||||
|
||||
$backup_table = $table_name . $suffix;
|
||||
|
||||
try {
|
||||
$wpdb->query("CREATE TABLE `$backup_table` LIKE `$table_name`");
|
||||
$wpdb->query("INSERT INTO `$backup_table` SELECT * FROM `$table_name`");
|
||||
return $backup_table;
|
||||
} catch (Exception $e) {
|
||||
error_log("Igny8 Migration: Failed to backup table $table_name - " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore table from backup
|
||||
*/
|
||||
function igny8_restore_table($table_name, $backup_table) {
|
||||
global $wpdb;
|
||||
|
||||
try {
|
||||
$wpdb->query("DROP TABLE IF EXISTS `$table_name`");
|
||||
$wpdb->query("CREATE TABLE `$table_name` LIKE `$backup_table`");
|
||||
$wpdb->query("INSERT INTO `$table_name` SELECT * FROM `$backup_table`");
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
error_log("Igny8 Migration: Failed to restore table $table_name - " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if table exists
|
||||
*/
|
||||
function igny8_table_exists($table_name) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if column exists in table
|
||||
*/
|
||||
function igny8_column_exists($table_name, $column_name) {
|
||||
global $wpdb;
|
||||
$result = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE '$column_name'");
|
||||
return !empty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table structure
|
||||
*/
|
||||
function igny8_get_table_structure($table_name) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results("DESCRIBE `$table_name`", ARRAY_A);
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* AUTO-MIGRATION ON PLUGIN UPDATE
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-run migrations on plugin update
|
||||
*/
|
||||
function igny8_auto_run_migrations() {
|
||||
if (current_user_can('manage_options') && igny8_is_migration_needed()) {
|
||||
igny8_run_migrations();
|
||||
}
|
||||
}
|
||||
|
||||
// Hook to auto-run migrations on admin_init
|
||||
add_action('admin_init', 'igny8_auto_run_migrations');
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* MIGRATION STATUS & LOGGING
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Log migration event
|
||||
*/
|
||||
function igny8_log_migration($from_version, $to_version, $status = 'success', $message = '') {
|
||||
$log_entry = [
|
||||
'timestamp' => current_time('mysql'),
|
||||
'from_version' => $from_version,
|
||||
'to_version' => $to_version,
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
'user_id' => get_current_user_id()
|
||||
];
|
||||
|
||||
// Store in options (you could also use the logs table)
|
||||
$migration_logs = get_option('igny8_migration_logs', []);
|
||||
$migration_logs[] = $log_entry;
|
||||
|
||||
// Keep only last 50 migration logs
|
||||
if (count($migration_logs) > 50) {
|
||||
$migration_logs = array_slice($migration_logs, -50);
|
||||
}
|
||||
|
||||
update_option('igny8_migration_logs', $migration_logs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration logs
|
||||
*/
|
||||
function igny8_get_migration_logs($limit = 10) {
|
||||
$logs = get_option('igny8_migration_logs', []);
|
||||
return array_slice(array_reverse($logs), 0, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear migration logs
|
||||
*/
|
||||
function igny8_clear_migration_logs() {
|
||||
delete_option('igny8_migration_logs');
|
||||
}
|
||||
970
igny8-ai-seo-wp-plugin/core/db/db.php
Normal file
970
igny8-ai-seo-wp-plugin/core/db/db.php
Normal file
@@ -0,0 +1,970 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : db.php
|
||||
* @location : /core/db/db.php
|
||||
* @type : Function Library
|
||||
* @scope : Global
|
||||
* @allowed : Database operations, schema management, data queries
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Central database operations and schema management
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up legacy database structures (if any exist from old installations)
|
||||
*
|
||||
* This function handles cleanup of any legacy structures that might exist
|
||||
* from previous plugin versions, but all new installations use the correct schema.
|
||||
*/
|
||||
function igny8_cleanup_legacy_structures() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'igny8_content_ideas';
|
||||
|
||||
// Only run cleanup if table exists
|
||||
if (!$wpdb->get_var("SHOW TABLES LIKE '$table_name'")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Remove legacy priority column if it exists (from very old versions)
|
||||
$priority_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'priority'");
|
||||
if ($priority_exists) {
|
||||
// Remove index first if it exists
|
||||
$index_exists = $wpdb->get_var("SHOW INDEX FROM `$table_name` WHERE Key_name = 'idx_priority'");
|
||||
if ($index_exists) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` DROP INDEX `idx_priority`");
|
||||
}
|
||||
|
||||
// Drop the column
|
||||
$wpdb->query("ALTER TABLE `$table_name` DROP COLUMN `priority`");
|
||||
error_log('Igny8 Cleanup: Removed legacy priority column');
|
||||
}
|
||||
|
||||
// Remove legacy ai_generated column if it exists (should be source now)
|
||||
$ai_generated_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'ai_generated'");
|
||||
if ($ai_generated_exists) {
|
||||
// Check if source column exists
|
||||
$source_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'source'");
|
||||
|
||||
if (!$source_exists) {
|
||||
// Migrate data from ai_generated to source before dropping
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `source` ENUM('AI','Manual') DEFAULT 'Manual'");
|
||||
$wpdb->query("UPDATE `$table_name` SET source = CASE WHEN ai_generated = 1 THEN 'AI' ELSE 'Manual' END");
|
||||
error_log('Igny8 Cleanup: Migrated ai_generated to source field');
|
||||
}
|
||||
|
||||
// Drop the old ai_generated column
|
||||
$wpdb->query("ALTER TABLE `$table_name` DROP COLUMN `ai_generated`");
|
||||
error_log('Igny8 Cleanup: Removed legacy ai_generated column');
|
||||
}
|
||||
|
||||
// Update any old status values to new format
|
||||
$wpdb->query("UPDATE `$table_name` SET status = 'new' WHERE status NOT IN ('new','scheduled','published')");
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('Igny8 Cleanup Error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if legacy cleanup is needed
|
||||
*/
|
||||
function igny8_is_legacy_cleanup_needed() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'igny8_content_ideas';
|
||||
|
||||
// Check if table exists
|
||||
if (!$wpdb->get_var("SHOW TABLES LIKE '$table_name'")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for legacy columns
|
||||
$priority_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'priority'");
|
||||
$ai_generated_exists = $wpdb->get_var("SHOW COLUMNS FROM `$table_name` LIKE 'ai_generated'");
|
||||
|
||||
return $priority_exists || $ai_generated_exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-run legacy cleanup on admin_init if needed
|
||||
*/
|
||||
function igny8_auto_run_legacy_cleanup() {
|
||||
if (current_user_can('manage_options') && igny8_is_legacy_cleanup_needed()) {
|
||||
igny8_cleanup_legacy_structures();
|
||||
}
|
||||
}
|
||||
|
||||
// Hook to auto-run legacy cleanup (only for existing installations)
|
||||
add_action('admin_init', 'igny8_auto_run_legacy_cleanup');
|
||||
|
||||
/**
|
||||
* Auto-run logs table migration on admin_init if needed
|
||||
*/
|
||||
function igny8_auto_run_logs_migration() {
|
||||
if (current_user_can('manage_options')) {
|
||||
igny8_migrate_logs_table();
|
||||
}
|
||||
}
|
||||
|
||||
// Hook to auto-run logs migration (only for existing installations)
|
||||
add_action('admin_init', 'igny8_auto_run_logs_migration');
|
||||
|
||||
/**
|
||||
* Remove old migration option on plugin activation
|
||||
*/
|
||||
function igny8_cleanup_migration_options() {
|
||||
delete_option('igny8_migration_ideas_schema_updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================================================
|
||||
* COMPLETE DATABASE SCHEMA CREATION
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create all Igny8 database tables (15 tables total)
|
||||
*/
|
||||
function igny8_create_all_tables() {
|
||||
global $wpdb;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
// Keywords table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_keywords (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
keyword VARCHAR(255) NOT NULL,
|
||||
search_volume INT UNSIGNED DEFAULT 0,
|
||||
difficulty INT UNSIGNED DEFAULT 0,
|
||||
cpc FLOAT DEFAULT 0.00,
|
||||
intent VARCHAR(50) DEFAULT 'informational',
|
||||
cluster_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
sector_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
mapped_post_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
status ENUM('unmapped','mapped','queued','published') DEFAULT 'unmapped',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_keyword (keyword),
|
||||
KEY idx_cluster_id (cluster_id),
|
||||
KEY idx_sector_id (sector_id),
|
||||
KEY idx_mapped_post_id (mapped_post_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Tasks table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_tasks (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT DEFAULT NULL,
|
||||
status ENUM('pending','in_progress','completed','cancelled','draft','queued','review','published') DEFAULT 'pending',
|
||||
priority ENUM('low','medium','high','urgent') DEFAULT 'medium',
|
||||
due_date DATETIME DEFAULT NULL,
|
||||
content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub',
|
||||
content_type ENUM('post','product','page','CPT') DEFAULT 'post',
|
||||
cluster_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
keywords TEXT DEFAULT NULL,
|
||||
meta_title VARCHAR(255) DEFAULT NULL,
|
||||
meta_description TEXT DEFAULT NULL,
|
||||
word_count INT UNSIGNED DEFAULT 0,
|
||||
raw_ai_response LONGTEXT DEFAULT NULL,
|
||||
schedule_at DATETIME DEFAULT NULL,
|
||||
assigned_post_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
idea_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
ai_writer ENUM('ai','human') DEFAULT 'ai',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_content_structure (content_structure),
|
||||
KEY idx_content_type (content_type),
|
||||
KEY idx_cluster_id (cluster_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_priority (priority),
|
||||
KEY idx_assigned_post_id (assigned_post_id),
|
||||
KEY idx_schedule_at (schedule_at),
|
||||
KEY idx_idea_id (idea_id),
|
||||
KEY idx_ai_writer (ai_writer),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Data table for personalization
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_data (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_id BIGINT UNSIGNED NOT NULL,
|
||||
data_type VARCHAR(50) NOT NULL,
|
||||
data JSON NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_post_id (post_id),
|
||||
KEY idx_data_type (data_type),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Personalization variations table - stores AI-generated personalized content
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_variations (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_id BIGINT UNSIGNED NOT NULL,
|
||||
fields_hash CHAR(64) NOT NULL,
|
||||
fields_json LONGTEXT NOT NULL,
|
||||
content LONGTEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_post_id (post_id),
|
||||
KEY idx_fields_hash (fields_hash),
|
||||
KEY idx_created_at (created_at),
|
||||
UNIQUE KEY unique_variation (post_id, fields_hash)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Rankings table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_rankings (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_id BIGINT UNSIGNED NOT NULL,
|
||||
keyword VARCHAR(255) NOT NULL,
|
||||
impressions INT UNSIGNED DEFAULT 0,
|
||||
clicks INT UNSIGNED DEFAULT 0,
|
||||
ctr FLOAT DEFAULT 0.00,
|
||||
avg_position FLOAT DEFAULT NULL,
|
||||
source ENUM('gsc','ahrefs','manual') DEFAULT 'manual',
|
||||
fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_post_id (post_id),
|
||||
KEY idx_keyword (keyword),
|
||||
KEY idx_source (source),
|
||||
KEY idx_fetched_at (fetched_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Suggestions table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_suggestions (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_id BIGINT UNSIGNED NOT NULL,
|
||||
cluster_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
suggestion_type ENUM('internal_link','keyword_injection','rewrite') NOT NULL,
|
||||
payload JSON DEFAULT NULL,
|
||||
status ENUM('pending','applied','rejected') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
applied_at DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_post_id (post_id),
|
||||
KEY idx_cluster_id (cluster_id),
|
||||
KEY idx_suggestion_type (suggestion_type),
|
||||
KEY idx_status (status),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Campaigns table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_campaigns (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
cluster_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
target_post_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
status ENUM('active','completed','paused') DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_cluster_id (cluster_id),
|
||||
KEY idx_target_post_id (target_post_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Content Ideas table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_content_ideas (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
idea_title VARCHAR(255) NOT NULL,
|
||||
idea_description LONGTEXT DEFAULT NULL,
|
||||
content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub',
|
||||
content_type ENUM('post','product','page','CPT') DEFAULT 'post',
|
||||
keyword_cluster_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
status ENUM('new','scheduled','published') DEFAULT 'new',
|
||||
estimated_word_count INT UNSIGNED DEFAULT 0,
|
||||
target_keywords TEXT DEFAULT NULL,
|
||||
image_prompts TEXT DEFAULT NULL,
|
||||
source ENUM('AI','Manual') DEFAULT 'Manual',
|
||||
mapped_post_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
tasks_count INT UNSIGNED DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_idea_title (idea_title),
|
||||
KEY idx_content_structure (content_structure),
|
||||
KEY idx_content_type (content_type),
|
||||
KEY idx_status (status),
|
||||
KEY idx_keyword_cluster_id (keyword_cluster_id),
|
||||
KEY idx_mapped_post_id (mapped_post_id),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Clusters table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_clusters (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
cluster_name VARCHAR(255) NOT NULL,
|
||||
sector_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
cluster_term_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
status ENUM('active','inactive','archived') DEFAULT 'active',
|
||||
keyword_count INT UNSIGNED DEFAULT 0,
|
||||
total_volume INT UNSIGNED DEFAULT 0,
|
||||
avg_difficulty DECIMAL(5,2) DEFAULT 0.00,
|
||||
mapped_pages_count INT UNSIGNED DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_cluster_name (cluster_name),
|
||||
KEY idx_sector_id (sector_id),
|
||||
KEY idx_cluster_term_id (cluster_term_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Sites table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_sites (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
site_url VARCHAR(500) NOT NULL,
|
||||
site_name VARCHAR(255) DEFAULT NULL,
|
||||
domain_authority INT UNSIGNED DEFAULT 0,
|
||||
referring_domains INT UNSIGNED DEFAULT 0,
|
||||
organic_traffic INT UNSIGNED DEFAULT 0,
|
||||
status ENUM('active','inactive','blocked') DEFAULT 'active',
|
||||
last_crawled DATETIME DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_site_url (site_url),
|
||||
KEY idx_domain_authority (domain_authority),
|
||||
KEY idx_status (status),
|
||||
KEY idx_last_crawled (last_crawled),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Backlinks table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_backlinks (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
source_url VARCHAR(500) NOT NULL,
|
||||
target_url VARCHAR(500) NOT NULL,
|
||||
anchor_text VARCHAR(255) DEFAULT NULL,
|
||||
link_type ENUM('dofollow','nofollow','sponsored','ugc') DEFAULT 'dofollow',
|
||||
domain_authority INT UNSIGNED DEFAULT 0,
|
||||
page_authority INT UNSIGNED DEFAULT 0,
|
||||
status ENUM('pending','live','lost','disavowed') DEFAULT 'pending',
|
||||
campaign_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
discovered_date DATE DEFAULT NULL,
|
||||
lost_date DATE DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_source_url (source_url(191)),
|
||||
KEY idx_target_url (target_url(191)),
|
||||
KEY idx_link_type (link_type),
|
||||
KEY idx_status (status),
|
||||
KEY idx_campaign_id (campaign_id),
|
||||
KEY idx_domain_authority (domain_authority),
|
||||
KEY idx_discovered_date (discovered_date),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Mapping table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_mapping (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
source_type ENUM('keyword','cluster','idea','task') NOT NULL,
|
||||
source_id BIGINT UNSIGNED NOT NULL,
|
||||
target_type ENUM('post','page','product') NOT NULL,
|
||||
target_id BIGINT UNSIGNED NOT NULL,
|
||||
mapping_type ENUM('primary','secondary','related') DEFAULT 'primary',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_source_type_id (source_type, source_id),
|
||||
KEY idx_target_type_id (target_type, target_id),
|
||||
KEY idx_mapping_type (mapping_type),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Prompts table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_prompts (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
prompt_name VARCHAR(255) NOT NULL,
|
||||
prompt_type ENUM('content','optimization','generation','custom') DEFAULT 'content',
|
||||
prompt_text LONGTEXT NOT NULL,
|
||||
variables JSON DEFAULT NULL,
|
||||
is_active TINYINT(1) DEFAULT 1,
|
||||
usage_count INT UNSIGNED DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_prompt_name (prompt_name),
|
||||
KEY idx_prompt_type (prompt_type),
|
||||
KEY idx_is_active (is_active),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Logs table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_logs (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
event_type VARCHAR(191) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
context LONGTEXT NULL,
|
||||
api_id VARCHAR(255) NULL,
|
||||
status VARCHAR(50) NULL,
|
||||
level VARCHAR(50) NULL,
|
||||
source VARCHAR(100) NULL,
|
||||
user_id BIGINT UNSIGNED NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_event_type (event_type),
|
||||
KEY idx_created_at (created_at),
|
||||
KEY idx_source (source),
|
||||
KEY idx_status (status),
|
||||
KEY idx_user_id (user_id)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// AI Queue table
|
||||
$sql = "CREATE TABLE {$wpdb->prefix}igny8_ai_queue (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
data LONGTEXT NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
status ENUM('pending','processing','completed','failed') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
processed_at TIMESTAMP NULL,
|
||||
result LONGTEXT NULL,
|
||||
error_message TEXT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_user_id (user_id),
|
||||
KEY idx_status (status),
|
||||
KEY idx_action (action),
|
||||
KEY idx_created_at (created_at)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql);
|
||||
|
||||
// Update database version
|
||||
update_option('igny8_db_version', '0.1');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Register Igny8 taxonomies with WordPress
|
||||
*/
|
||||
function igny8_register_taxonomies() {
|
||||
// Register sectors taxonomy (hierarchical) - only if not exists
|
||||
if (!taxonomy_exists('sectors')) {
|
||||
register_taxonomy('sectors', ['post', 'page', 'product'], [
|
||||
'hierarchical' => true,
|
||||
'labels' => [
|
||||
'name' => 'Sectors',
|
||||
'singular_name' => 'Sector',
|
||||
'menu_name' => 'Sectors',
|
||||
'all_items' => 'All Sectors',
|
||||
'edit_item' => 'Edit Sector',
|
||||
'view_item' => 'View Sector',
|
||||
'update_item' => 'Update Sector',
|
||||
'add_new_item' => 'Add New Sector',
|
||||
'new_item_name' => 'New Sector Name',
|
||||
'parent_item' => 'Parent Sector',
|
||||
'parent_item_colon' => 'Parent Sector:',
|
||||
'search_items' => 'Search Sectors',
|
||||
'popular_items' => 'Popular Sectors',
|
||||
'separate_items_with_commas' => 'Separate sectors with commas',
|
||||
'add_or_remove_items' => 'Add or remove sectors',
|
||||
'choose_from_most_used' => 'Choose from most used sectors',
|
||||
'not_found' => 'No sectors found',
|
||||
],
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'show_in_nav_menus' => true,
|
||||
'show_tagcloud' => true,
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => [
|
||||
'slug' => 'sectors',
|
||||
'with_front' => false,
|
||||
],
|
||||
'capabilities' => [
|
||||
'manage_terms' => 'manage_categories',
|
||||
'edit_terms' => 'manage_categories',
|
||||
'delete_terms' => 'manage_categories',
|
||||
'assign_terms' => 'edit_posts',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// Register clusters taxonomy (hierarchical) - only if not exists
|
||||
if (!taxonomy_exists('clusters')) {
|
||||
register_taxonomy('clusters', ['post', 'page', 'product'], [
|
||||
'hierarchical' => true,
|
||||
'labels' => [
|
||||
'name' => 'Content Clusters',
|
||||
'singular_name' => 'Cluster',
|
||||
'menu_name' => 'Clusters',
|
||||
'all_items' => 'All Clusters',
|
||||
'edit_item' => 'Edit Cluster',
|
||||
'view_item' => 'View Cluster',
|
||||
'update_item' => 'Update Cluster',
|
||||
'add_new_item' => 'Add New Cluster',
|
||||
'new_item_name' => 'New Cluster Name',
|
||||
'parent_item' => 'Parent Cluster',
|
||||
'parent_item_colon' => 'Parent Cluster:',
|
||||
'search_items' => 'Search Clusters',
|
||||
'popular_items' => 'Popular Clusters',
|
||||
'separate_items_with_commas' => 'Separate clusters with commas',
|
||||
'add_or_remove_items' => 'Add or remove clusters',
|
||||
'choose_from_most_used' => 'Choose from most used clusters',
|
||||
'not_found' => 'No clusters found',
|
||||
],
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'show_in_nav_menus' => true,
|
||||
'show_tagcloud' => true,
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => [
|
||||
'slug' => 'clusters',
|
||||
'with_front' => false,
|
||||
],
|
||||
'capabilities' => [
|
||||
'manage_terms' => 'manage_categories',
|
||||
'edit_terms' => 'manage_categories',
|
||||
'delete_terms' => 'manage_categories',
|
||||
'assign_terms' => 'edit_posts',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// SEO: Prevent indexing of Cluster and Sector taxonomy pages
|
||||
// ==========================================================
|
||||
add_action('wp_head', function() {
|
||||
if (is_tax(['clusters', 'sectors'])) {
|
||||
echo '<meta name="robots" content="noindex,follow" />' . "\n";
|
||||
}
|
||||
}, 1);
|
||||
|
||||
/**
|
||||
* Register Igny8 post meta fields with WordPress
|
||||
*/
|
||||
function igny8_register_post_meta() {
|
||||
$post_types = ['post', 'page', 'product'];
|
||||
|
||||
// Define all meta fields with proper schema for REST API
|
||||
$meta_fields = [
|
||||
'_igny8_cluster_id' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Assigns content to a cluster',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_keyword_ids' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Maps multiple keywords to content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> [
|
||||
'schema' => [
|
||||
'type' => 'array',
|
||||
'items' => [
|
||||
'type' => 'integer'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'_igny8_task_id' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Links WP content back to Writer task',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_campaign_ids' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Associates content with backlink campaigns',
|
||||
'single' => true,
|
||||
'show_in_rest'=> [
|
||||
'schema' => [
|
||||
'type' => 'array',
|
||||
'items' => [
|
||||
'type' => 'integer'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'_igny8_backlink_count' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Quick summary count of backlinks to content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_last_optimized' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Tracks last optimization timestamp',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_meta_title' => [
|
||||
'type' => 'string',
|
||||
'description' => 'SEO meta title for the content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_meta_description' => [
|
||||
'type' => 'string',
|
||||
'description' => 'SEO meta description for the content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_primary_keywords' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Primary keywords for the content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
'_igny8_secondary_keywords' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Secondary keywords for the content',
|
||||
'single' => true,
|
||||
'show_in_rest'=> true,
|
||||
],
|
||||
];
|
||||
|
||||
// Register each meta field for all relevant post types
|
||||
foreach ($meta_fields as $meta_key => $config) {
|
||||
foreach ($post_types as $post_type) {
|
||||
register_post_meta($post_type, $meta_key, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default plugin options
|
||||
*/
|
||||
function igny8_set_default_options() {
|
||||
// Set default options if they don't exist
|
||||
if (!get_option('igny8_api_key')) {
|
||||
add_option('igny8_api_key', '');
|
||||
}
|
||||
if (!get_option('igny8_ai_enabled')) {
|
||||
add_option('igny8_ai_enabled', 1);
|
||||
}
|
||||
if (!get_option('igny8_debug_enabled')) {
|
||||
add_option('igny8_debug_enabled', 0);
|
||||
}
|
||||
if (!get_option('igny8_monitoring_enabled')) {
|
||||
add_option('igny8_monitoring_enabled', 1);
|
||||
}
|
||||
if (!get_option('igny8_version')) {
|
||||
add_option('igny8_version', '0.1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate logs table to add missing columns for OpenAI API logging
|
||||
*/
|
||||
function igny8_migrate_logs_table() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'igny8_logs';
|
||||
|
||||
// Check if table exists
|
||||
if (!$wpdb->get_var("SHOW TABLES LIKE '$table_name'")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if migration is needed
|
||||
$columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
|
||||
$column_names = array_column($columns, 'Field');
|
||||
|
||||
$needed_columns = ['api_id', 'status', 'level', 'source', 'user_id'];
|
||||
$missing_columns = array_diff($needed_columns, $column_names);
|
||||
|
||||
if (empty($missing_columns)) {
|
||||
return true; // Migration not needed
|
||||
}
|
||||
|
||||
try {
|
||||
// Add missing columns
|
||||
if (in_array('api_id', $missing_columns)) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `api_id` VARCHAR(255) NULL");
|
||||
}
|
||||
if (in_array('status', $missing_columns)) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `status` VARCHAR(50) NULL");
|
||||
}
|
||||
if (in_array('level', $missing_columns)) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `level` VARCHAR(50) NULL");
|
||||
}
|
||||
if (in_array('source', $missing_columns)) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `source` VARCHAR(100) NULL");
|
||||
}
|
||||
if (in_array('user_id', $missing_columns)) {
|
||||
$wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `user_id` BIGINT UNSIGNED NULL");
|
||||
}
|
||||
|
||||
// Add indexes for new columns
|
||||
$indexes_to_add = [
|
||||
'source' => "ALTER TABLE `$table_name` ADD INDEX `idx_source` (`source`)",
|
||||
'status' => "ALTER TABLE `$table_name` ADD INDEX `idx_status` (`status`)",
|
||||
'user_id' => "ALTER TABLE `$table_name` ADD INDEX `idx_user_id` (`user_id`)"
|
||||
];
|
||||
|
||||
foreach ($indexes_to_add as $column => $sql) {
|
||||
if (in_array($column, $missing_columns)) {
|
||||
$wpdb->query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
error_log('Igny8: Logs table migration completed successfully');
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('Igny8 Logs Migration Error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete plugin installation function
|
||||
*/
|
||||
function igny8_install_database() {
|
||||
// Create all database tables
|
||||
igny8_create_all_tables();
|
||||
|
||||
// Migrate logs table if needed
|
||||
igny8_migrate_logs_table();
|
||||
|
||||
// Register taxonomies
|
||||
igny8_register_taxonomies();
|
||||
|
||||
// Register post meta fields
|
||||
igny8_register_post_meta();
|
||||
|
||||
// Set default options
|
||||
igny8_set_default_options();
|
||||
|
||||
// Update version
|
||||
update_option('igny8_version', '0.1');
|
||||
update_option('igny8_db_version', '0.1');
|
||||
|
||||
// Add word_count field to tasks table if it doesn't exist
|
||||
igny8_add_word_count_to_tasks();
|
||||
|
||||
// Add raw_ai_response field to tasks table if it doesn't exist
|
||||
igny8_add_raw_ai_response_to_tasks();
|
||||
|
||||
// Add tasks_count field to content_ideas table if it doesn't exist
|
||||
igny8_add_tasks_count_to_content_ideas();
|
||||
|
||||
// Add image_prompts field to content_ideas table if it doesn't exist
|
||||
igny8_add_image_prompts_to_content_ideas();
|
||||
|
||||
// Update idea_description field to LONGTEXT for structured JSON descriptions
|
||||
igny8_update_idea_description_to_longtext();
|
||||
|
||||
// Migrate ideas and tasks table structure
|
||||
igny8_migrate_ideas_tasks_structure();
|
||||
|
||||
// Run legacy cleanup if needed
|
||||
igny8_cleanup_legacy_structures();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add word_count field to tasks table if it doesn't exist
|
||||
*/
|
||||
function igny8_add_word_count_to_tasks() {
|
||||
global $wpdb;
|
||||
|
||||
// Check if word_count column exists
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_tasks LIKE 'word_count'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Add word_count column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD COLUMN word_count INT UNSIGNED DEFAULT 0 AFTER keywords");
|
||||
error_log('Igny8: Added word_count column to tasks table');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw_ai_response field to tasks table if it doesn't exist
|
||||
*/
|
||||
function igny8_add_raw_ai_response_to_tasks() {
|
||||
global $wpdb;
|
||||
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_tasks LIKE 'raw_ai_response'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Add raw_ai_response column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD COLUMN raw_ai_response LONGTEXT DEFAULT NULL AFTER word_count");
|
||||
error_log('Igny8: Added raw_ai_response column to tasks table');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tasks_count field to content_ideas table if it doesn't exist
|
||||
*/
|
||||
function igny8_add_tasks_count_to_content_ideas() {
|
||||
global $wpdb;
|
||||
|
||||
// Check if tasks_count column exists
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'tasks_count'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Add tasks_count column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD COLUMN tasks_count INT UNSIGNED DEFAULT 0 AFTER mapped_post_id");
|
||||
error_log('Igny8: Added tasks_count column to content_ideas table');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add image_prompts field to content_ideas table if it doesn't exist
|
||||
*/
|
||||
function igny8_add_image_prompts_to_content_ideas() {
|
||||
global $wpdb;
|
||||
|
||||
// Check if image_prompts column exists
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'image_prompts'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Add image_prompts column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD COLUMN image_prompts TEXT DEFAULT NULL AFTER target_keywords");
|
||||
error_log('Igny8: Added image_prompts column to content_ideas table');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update idea_description field to LONGTEXT to support structured JSON descriptions
|
||||
*/
|
||||
function igny8_update_idea_description_to_longtext() {
|
||||
global $wpdb;
|
||||
|
||||
// Check current column type
|
||||
$column_info = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'idea_description'");
|
||||
|
||||
if (!empty($column_info)) {
|
||||
$column_type = $column_info[0]->Type;
|
||||
|
||||
// Only update if it's not already LONGTEXT
|
||||
if (strpos(strtoupper($column_type), 'LONGTEXT') === false) {
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas MODIFY COLUMN idea_description LONGTEXT DEFAULT NULL");
|
||||
error_log('Igny8: Updated idea_description column to LONGTEXT');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate ideas and tasks table structure
|
||||
*/
|
||||
function igny8_migrate_ideas_tasks_structure() {
|
||||
global $wpdb;
|
||||
|
||||
// Migrate ideas table
|
||||
igny8_migrate_ideas_table();
|
||||
|
||||
// Migrate tasks table
|
||||
igny8_migrate_tasks_table();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate ideas table structure
|
||||
*/
|
||||
function igny8_migrate_ideas_table() {
|
||||
global $wpdb;
|
||||
|
||||
// Check if idea_type column exists (old column) and remove it
|
||||
$old_column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'idea_type'");
|
||||
if (!empty($old_column_exists)) {
|
||||
// Drop the old idea_type column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas DROP COLUMN idea_type");
|
||||
error_log('Igny8: Removed idea_type column from ideas table');
|
||||
}
|
||||
|
||||
// Check if content_structure column exists
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'content_structure'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Add content_structure column with new options
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD COLUMN content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub' AFTER idea_description");
|
||||
error_log('Igny8: Added content_structure column to ideas table');
|
||||
} else {
|
||||
// Update existing content_structure column with new options
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas MODIFY COLUMN content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub'");
|
||||
error_log('Igny8: Updated content_structure column options in ideas table');
|
||||
}
|
||||
|
||||
// Check if content_type column exists
|
||||
$content_type_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_content_ideas LIKE 'content_type'");
|
||||
|
||||
if (empty($content_type_exists)) {
|
||||
// Add content_type column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD COLUMN content_type ENUM('post','product','page','CPT') DEFAULT 'post' AFTER content_structure");
|
||||
error_log('Igny8: Added content_type column to ideas table');
|
||||
}
|
||||
|
||||
// Update indexes
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas DROP INDEX IF EXISTS idx_idea_type");
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD INDEX idx_content_structure (content_structure)");
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_content_ideas ADD INDEX idx_content_type (content_type)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate tasks table structure
|
||||
*/
|
||||
function igny8_migrate_tasks_table() {
|
||||
global $wpdb;
|
||||
|
||||
// Check if content_structure column exists
|
||||
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_tasks LIKE 'content_structure'");
|
||||
|
||||
if (empty($column_exists)) {
|
||||
// Check if content_type column exists (old column)
|
||||
$old_column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_tasks LIKE 'content_type'");
|
||||
|
||||
if (!empty($old_column_exists)) {
|
||||
// Rename content_type to content_structure with new options
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks CHANGE COLUMN content_type content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub'");
|
||||
error_log('Igny8: Renamed content_type to content_structure in tasks table');
|
||||
} else {
|
||||
// Add content_structure column with new options
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD COLUMN content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub' AFTER due_date");
|
||||
error_log('Igny8: Added content_structure column to tasks table');
|
||||
}
|
||||
} else {
|
||||
// Update existing content_structure column with new options
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks MODIFY COLUMN content_structure ENUM('cluster_hub','landing_page','guide_tutorial','how_to','comparison','review','top_listicle','question','product_description','service_page','home_page') DEFAULT 'cluster_hub'");
|
||||
error_log('Igny8: Updated content_structure column options in tasks table');
|
||||
}
|
||||
|
||||
// Check if content_type column exists (new column)
|
||||
$content_type_exists = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}igny8_tasks LIKE 'content_type'");
|
||||
|
||||
if (empty($content_type_exists)) {
|
||||
// Add content_type column
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD COLUMN content_type ENUM('post','product','page','CPT') DEFAULT 'post' AFTER content_structure");
|
||||
error_log('Igny8: Added content_type column to tasks table');
|
||||
}
|
||||
|
||||
// Update indexes
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks DROP INDEX IF EXISTS idx_content_type");
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD INDEX idx_content_structure (content_structure)");
|
||||
$wpdb->query("ALTER TABLE {$wpdb->prefix}igny8_tasks ADD INDEX idx_content_type (content_type)");
|
||||
}
|
||||
463
igny8-ai-seo-wp-plugin/core/global-layout.php
Normal file
463
igny8-ai-seo-wp-plugin/core/global-layout.php
Normal file
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : global-layout.php
|
||||
* @location : /core/global-layout.php
|
||||
* @type : Layout
|
||||
* @scope : Global
|
||||
* @allowed : HTML layout, CSS/JS includes, navigation rendering
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Master layout template for all admin pages
|
||||
*/
|
||||
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Load helper functions
|
||||
require_once plugin_dir_path(__FILE__) . 'admin/global-helpers.php';
|
||||
|
||||
// Evidence functions removed - no longer needed
|
||||
|
||||
// Component functions are loaded globally in igny8.php
|
||||
|
||||
// KPI configuration is loaded globally in igny8.php
|
||||
|
||||
// Load KPI data for header metrics based on current module/submodule
|
||||
$kpi_data = [];
|
||||
if (isset($GLOBALS['igny8_kpi_config']) && !empty($GLOBALS['igny8_kpi_config'])) {
|
||||
$kpi_config = $GLOBALS['igny8_kpi_config'];
|
||||
|
||||
// Determine the table_id based on current module and submodule
|
||||
$current_module = $GLOBALS['current_module'] ?? '';
|
||||
$current_submodule = $GLOBALS['current_submodule'] ?? '';
|
||||
$current_page = $_GET['page'] ?? '';
|
||||
|
||||
// Special handling for home pages
|
||||
if ($current_page === 'igny8-planner' && empty($current_submodule)) {
|
||||
$table_id = 'planner_home';
|
||||
} elseif ($current_page === 'igny8-writer' && empty($current_submodule)) {
|
||||
$table_id = 'writer_home';
|
||||
} elseif (!empty($current_module) && !empty($current_submodule)) {
|
||||
$table_id = $current_module . '_' . $current_submodule;
|
||||
} else {
|
||||
$table_id = '';
|
||||
}
|
||||
|
||||
// Load KPI data if configuration exists for this table
|
||||
if (!empty($table_id) && isset($kpi_config[$table_id])) {
|
||||
$kpi_data = igny8_get_kpi_data_safe($table_id, $kpi_config[$table_id]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="igny8-page-wrapper">
|
||||
|
||||
<!-- SIDEBAR SECTION -->
|
||||
<aside class="igny8-sidebar">
|
||||
<!-- LOGO / BRAND -->
|
||||
<div class="igny8-sidebar-logo">
|
||||
<h2>IGNY8 AI SEO</h2>
|
||||
</div>
|
||||
|
||||
<!-- VERSION BADGE -->
|
||||
<div class="igny8-version-badge">
|
||||
<span class="igny8-badge igny8-btn-danger">v<?php echo get_plugin_data(plugin_dir_path(__FILE__) . '../igny8.php')['Version']; ?></span>
|
||||
</div>
|
||||
|
||||
<!-- BREADCRUMB NAVIGATION -->
|
||||
<div class="igny8-breadcrumb">
|
||||
<?php echo igny8_render_breadcrumb(); ?>
|
||||
</div>
|
||||
|
||||
<!-- DEBUG STATUS CIRCLES (submodule pages only) -->
|
||||
<?php
|
||||
$is_debug_enabled = defined('WP_DEBUG') && WP_DEBUG;
|
||||
$is_monitoring_enabled = get_option('igny8_debug_enabled', false);
|
||||
$is_submodule_page = !empty($_GET['sm']) || !empty($GLOBALS['current_submodule']);
|
||||
|
||||
if ($is_debug_enabled && $is_monitoring_enabled && $is_submodule_page):
|
||||
// Simple debug circles - will be updated by JavaScript based on actual debug card states
|
||||
$debug_circles = [
|
||||
'database' => ['title' => 'Database', 'status' => 'secondary'],
|
||||
'table' => ['title' => 'Table', 'status' => 'secondary'],
|
||||
'filters' => ['title' => 'Filters', 'status' => 'secondary'],
|
||||
'forms' => ['title' => 'Forms', 'status' => 'secondary'],
|
||||
'automation' => ['title' => 'Automation', 'status' => 'secondary'],
|
||||
'ai_logs' => ['title' => 'AI Logs', 'status' => 'secondary']
|
||||
];
|
||||
?>
|
||||
<div class="igny8-sidebar-status-bar" style="padding: 12px 16px; border-bottom: 1px solid var(--border);">
|
||||
<div class="igny8-status-row">
|
||||
<div class="igny8-flex" style="justify-content: center; gap: 8px;">
|
||||
<?php foreach ($debug_circles as $circle_key => $circle_data): ?>
|
||||
<div class="bg-circle-sm bg-<?php echo esc_attr($circle_data['status']); ?> igny8-debug-circle" data-component="<?php echo esc_attr($circle_key); ?>" title="<?php echo esc_attr($circle_data['title']); ?>"></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- MAIN NAVIGATION -->
|
||||
<nav class="igny8-sidebar-nav">
|
||||
<?php
|
||||
$current_page = $_GET['page'] ?? '';
|
||||
$home_active = ($current_page === 'igny8-home') ? 'active' : '';
|
||||
$settings_active = (strpos($current_page, 'igny8-settings') !== false) ? 'active' : '';
|
||||
$help_active = (strpos($current_page, 'igny8-help') !== false) ? 'active' : '';
|
||||
|
||||
// Always show home, settings, and help
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-home'); ?>" class="igny8-sidebar-link <?php echo $home_active; ?>">
|
||||
<span class="dashicons dashicons-dashboard"></span>
|
||||
<span class="label">Igny8 Home</span>
|
||||
</a>
|
||||
|
||||
<?php
|
||||
// Show modules only if they are enabled
|
||||
if (function_exists('igny8_is_module_enabled')) {
|
||||
// Main modules
|
||||
if (igny8_is_module_enabled('planner')) {
|
||||
$planner_active = (strpos($current_page, 'igny8-planner') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-planner'); ?>" class="igny8-sidebar-link <?php echo $planner_active; ?>">
|
||||
<span class="dashicons dashicons-tag"></span>
|
||||
<span class="label">Planner</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('writer')) {
|
||||
$writer_active = (strpos($current_page, 'igny8-writer') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-writer'); ?>" class="igny8-sidebar-link <?php echo $writer_active; ?>">
|
||||
<span class="dashicons dashicons-edit"></span>
|
||||
<span class="label">Writer</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('linker')) {
|
||||
$linker_active = (strpos($current_page, 'igny8-linker') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-linker'); ?>" class="igny8-sidebar-link <?php echo $linker_active; ?>">
|
||||
<span class="dashicons dashicons-admin-links"></span>
|
||||
<span class="label">Linker</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
if (igny8_is_module_enabled('personalize')) {
|
||||
$personalize_active = (strpos($current_page, 'igny8-personalize') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-personalize'); ?>" class="igny8-sidebar-link <?php echo $personalize_active; ?>">
|
||||
<span class="dashicons dashicons-admin-customizer"></span>
|
||||
<span class="label">Personalize</span>
|
||||
</a>
|
||||
<div class="igny8-sidebar-divider"></div> <?php
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php
|
||||
// Thinker before schedules
|
||||
if (igny8_is_module_enabled('thinker')) {
|
||||
$thinker_active = (strpos($current_page, 'igny8-thinker') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-thinker'); ?>" class="igny8-sidebar-link <?php echo $thinker_active; ?>">
|
||||
<span class="dashicons dashicons-lightbulb"></span>
|
||||
<span class="label">Thinker</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Admin modules
|
||||
if (igny8_is_module_enabled('schedules')) {
|
||||
$schedules_active = (strpos($current_page, 'igny8-schedules') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-schedules'); ?>" class="igny8-sidebar-link <?php echo $schedules_active; ?>">
|
||||
<span class="dashicons dashicons-calendar-alt"></span>
|
||||
<span class="label">Schedules</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
// Analytics before Settings
|
||||
if (igny8_is_module_enabled('analytics')) {
|
||||
$analytics_active = (strpos($current_page, 'igny8-analytics') !== false) ? 'active' : '';
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-analytics'); ?>" class="igny8-sidebar-link <?php echo $analytics_active; ?>">
|
||||
<span class="dashicons dashicons-chart-line"></span>
|
||||
<span class="label">Analytics</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fallback: show all modules if module manager is not available
|
||||
?>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-planner'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-planner') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-tag"></span>
|
||||
<span class="label">Planner</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-writer'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-writer') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-edit"></span>
|
||||
<span class="label">Writer</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-optimizer'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-optimizer') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-performance"></span>
|
||||
<span class="label">Optimizer</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-linker'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-linker') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-admin-links"></span>
|
||||
<span class="label">Linker</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-personalize'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-personalize') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-admin-customizer"></span>
|
||||
<span class="label">Personalize</span>
|
||||
</a>
|
||||
|
||||
<div class="igny8-sidebar-divider"></div>
|
||||
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-thinker'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-thinker') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-lightbulb"></span>
|
||||
<span class="label">Thinker</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-schedules'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-schedules') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-calendar-alt"></span>
|
||||
<span class="label">Schedules</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-analytics'); ?>" class="igny8-sidebar-link <?php echo (strpos($current_page, 'igny8-analytics') !== false) ? 'active' : ''; ?>">
|
||||
<span class="dashicons dashicons-chart-line"></span>
|
||||
<span class="label">Analytics</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-settings'); ?>" class="igny8-sidebar-link <?php echo $settings_active; ?>">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
<a href="<?php echo admin_url('admin.php?page=igny8-help'); ?>" class="igny8-sidebar-link <?php echo $help_active; ?>">
|
||||
<span class="dashicons dashicons-sos"></span>
|
||||
<span class="label">Help</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="igny8-sidebar-divider"></div>
|
||||
|
||||
|
||||
<!-- JavaScript to update sidebar debug circles based on debug card states -->
|
||||
<?php if ($is_debug_enabled && $is_monitoring_enabled && $is_submodule_page): ?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Function to update sidebar debug circles based on debug card states
|
||||
function updateSidebarDebugCircles() {
|
||||
const circles = document.querySelectorAll('.igny8-debug-circle');
|
||||
|
||||
circles.forEach(circle => {
|
||||
const component = circle.getAttribute('data-component');
|
||||
let matchingCard = null;
|
||||
|
||||
if (component === 'ai_logs') {
|
||||
// Special handling for AI logs - it's in the automation section
|
||||
const debugCards = document.querySelectorAll('.igny8-debug-item');
|
||||
debugCards.forEach(card => {
|
||||
const cardText = card.textContent.toLowerCase();
|
||||
if (cardText.includes('ai logs') || cardText.includes('ai_logs')) {
|
||||
matchingCard = card;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Regular handling for other components
|
||||
const debugCards = document.querySelectorAll('.igny8-debug-item');
|
||||
debugCards.forEach(card => {
|
||||
const cardText = card.textContent.toLowerCase();
|
||||
const componentName = component.toLowerCase();
|
||||
|
||||
if (cardText.includes(componentName)) {
|
||||
matchingCard = card;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (matchingCard) {
|
||||
// Get the status from the background color
|
||||
const bgColor = matchingCard.style.backgroundColor;
|
||||
let status = 'secondary'; // default
|
||||
|
||||
if (bgColor.includes('212, 237, 218') || bgColor.includes('rgb(212, 237, 218)')) { // success green
|
||||
status = 'success';
|
||||
} else if (bgColor.includes('255, 243, 205') || bgColor.includes('rgb(255, 243, 205)')) { // warning yellow
|
||||
status = 'warning';
|
||||
} else if (bgColor.includes('248, 215, 218') || bgColor.includes('rgb(248, 215, 218)')) { // error red
|
||||
status = 'danger';
|
||||
}
|
||||
|
||||
// Update circle classes
|
||||
circle.className = `bg-circle-sm bg-${status} igny8-debug-circle`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update circles when page loads
|
||||
setTimeout(updateSidebarDebugCircles, 1000); // Wait for debug cards to load
|
||||
|
||||
// Update circles periodically to catch dynamic changes
|
||||
setInterval(updateSidebarDebugCircles, 3000);
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- FOOTER SHORTCUTS -->
|
||||
<div class="igny8-sidebar-footer-container">
|
||||
<div class="igny8-sidebar-footer">
|
||||
<a href="<?php echo admin_url('options-general.php'); ?>" class="igny8-sidebar-link">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<span class="label">Settings</span>
|
||||
</a>
|
||||
<a href="<?php echo wp_logout_url(); ?>" class="igny8-sidebar-link">
|
||||
<span class="dashicons dashicons-migrate"></span>
|
||||
<span class="label">Logout</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- MAIN CONTAINER -->
|
||||
<div class="igny8-main-area">
|
||||
|
||||
<!-- HEADER SECTION -->
|
||||
<header class="igny8-header">
|
||||
<!-- LEFT: Dynamic Submenu Navigation -->
|
||||
<div class="igny8-header-left">
|
||||
<div class="igny8-submenu">
|
||||
<div class="igny8-submenu-buttons">
|
||||
<?php echo igny8_render_submenu(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CENTER: Page Title & Description -->
|
||||
<div class="igny8-header-center">
|
||||
<div class="igny8-page-title">
|
||||
<h1><?php
|
||||
// Hardcoded page titles
|
||||
$page_titles = [
|
||||
'igny8-home' => 'Dashboard',
|
||||
'igny8-planner' => 'Planner',
|
||||
'igny8-writer' => 'Writer',
|
||||
'igny8-thinker' => 'Thinker',
|
||||
'igny8-analytics' => 'Analytics',
|
||||
'igny8-settings' => 'Settings',
|
||||
'igny8-schedules' => 'Schedules',
|
||||
'igny8-help' => 'Help'
|
||||
];
|
||||
echo $page_titles[$current_page] ?? 'IGNY8 AI SEO';
|
||||
?></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: Metrics Bar + Icons -->
|
||||
<div class="igny8-header-right">
|
||||
<!-- Metrics - Compact Format -->
|
||||
<div class="igny8-metrics-compact">
|
||||
<?php
|
||||
// Use the same KPI logic as before but inline
|
||||
if (empty($kpi_data)) {
|
||||
$max_fallback = ($current_module === 'planner') ? 4 : 6;
|
||||
$all_fallback_metrics = [
|
||||
|
||||
['value' => 0, 'label' => 'Active', 'color' => 'green'],
|
||||
['value' => 0, 'label' => 'Pending', 'color' => 'amber'],
|
||||
['value' => 0, 'label' => 'Completed', 'color' => 'purple'],
|
||||
['value' => 0, 'label' => 'Recent', 'color' => 'blue']
|
||||
|
||||
];
|
||||
$fallback_metrics = array_slice($all_fallback_metrics, 0, $max_fallback);
|
||||
|
||||
foreach ($fallback_metrics as $metric):
|
||||
?>
|
||||
<div class="metric <?php echo $metric['color']; ?>">
|
||||
<span class="val"><?php echo number_format($metric['value']); ?></span>
|
||||
<span class="lbl"><?php echo $metric['label']; ?></span>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
} else {
|
||||
// Get metrics - 4 for planner pages, 6 for others
|
||||
$max_metrics = ($current_module === 'planner') ? 4 : 6;
|
||||
$metrics = array_slice($kpi_data, 0, $max_metrics, true);
|
||||
$color_map = ['', 'green', 'amber', 'purple', 'blue', 'teal'];
|
||||
$color_index = 0;
|
||||
|
||||
foreach ($metrics as $metric_key => $metric_value):
|
||||
$kpi_config = $GLOBALS['igny8_kpi_config'] ?? [];
|
||||
$color = '';
|
||||
if (isset($kpi_config[$table_id][$metric_key]['color'])) {
|
||||
$color = $kpi_config[$table_id][$metric_key]['color'];
|
||||
} else {
|
||||
$color = $color_map[$color_index] ?? '';
|
||||
}
|
||||
|
||||
$label = $metric_key;
|
||||
if (isset($kpi_config[$table_id][$metric_key]['label'])) {
|
||||
$label = $kpi_config[$table_id][$metric_key]['label'];
|
||||
}
|
||||
?>
|
||||
<div class="metric <?php echo $color; ?>">
|
||||
<span class="val"><?php echo number_format($metric_value); ?></span>
|
||||
<span class="lbl"><?php echo $label; ?></span>
|
||||
</div>
|
||||
<?php
|
||||
$color_index++;
|
||||
endforeach;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<!-- Header User Icon -->
|
||||
<div class="igny8-header-icons">
|
||||
<span class="dashicons dashicons-admin-users" title="User"></span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- MAIN CONTENT AREA -->
|
||||
<main class="igny8-content">
|
||||
<?php echo $igny8_page_content ?? '<p>No content provided.</p>'; ?>
|
||||
|
||||
<!-- MODULE DEBUG SECTION (conditional based on toggle and submodule pages) -->
|
||||
<?php
|
||||
// Only show module debug if:
|
||||
// 1. WP_DEBUG is enabled
|
||||
// 2. Debug monitoring toggle is enabled
|
||||
// 3. We're on a submodule page (not home page)
|
||||
$is_debug_enabled = defined('WP_DEBUG') && WP_DEBUG;
|
||||
$is_monitoring_enabled = get_option('igny8_debug_enabled', false);
|
||||
$is_submodule_page = !empty($_GET['sm']) || !empty($GLOBALS['current_submodule']);
|
||||
|
||||
if ($is_debug_enabled && $is_monitoring_enabled && $is_submodule_page) {
|
||||
require_once plugin_dir_path(__FILE__) . '../debug/module-debug.php';
|
||||
$debug_content = igny8_get_module_debug_content();
|
||||
if (!empty($debug_content)) {
|
||||
echo $debug_content;
|
||||
}
|
||||
}
|
||||
?>
|
||||
</main>
|
||||
|
||||
<!-- FOOTER SECTION -->
|
||||
<footer class="igny8-footer">
|
||||
<div class="igny8-footer-content">
|
||||
<span>© <?php echo date('Y'); ?> Igny8 Plugin</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user