Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View 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
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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();

View 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');

View 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));
}
});

View 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();
}

File diff suppressed because it is too large Load Diff

View File

@@ -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'
];
}

View 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');
}

View 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)");
}

View 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>&copy; <?php echo date('Y'); ?> Igny8 Plugin</span>
</div>
</footer>
</div>
</div>