label */ function igny8_get_supported_post_types() { $types = array( 'post' => __('Posts', 'igny8-bridge'), 'page' => __('Pages', 'igny8-bridge'), ); if (post_type_exists('product')) { $types['product'] = __('Products', 'igny8-bridge'); } /** * Filter the list of selectable post types. * * @param array $types */ return apply_filters('igny8_supported_post_types', $types); } /** * Get enabled post types * * @return array */ function igny8_get_enabled_post_types() { $saved = get_option('igny8_enabled_post_types'); if (is_array($saved) && !empty($saved)) { return $saved; } return array_keys(igny8_get_supported_post_types()); } /** * Get configured control mode * * @return string mirror|hybrid */ function igny8_get_control_mode() { $mode = get_option('igny8_control_mode', 'mirror'); return in_array($mode, array('mirror', 'hybrid'), true) ? $mode : 'mirror'; } /** * Get available automation modules * * @return array Key => label */ function igny8_get_available_modules() { $modules = array( 'sites' => __('Sites (Data & Semantic Map)', 'igny8-bridge'), 'planner' => __('Planner (Keywords & Briefs)', 'igny8-bridge'), 'writer' => __('Writer (Tasks & Posts)', 'igny8-bridge'), 'linker' => __('Linker (Internal Links)', 'igny8-bridge'), 'optimizer' => __('Optimizer (Audits & Scores)', 'igny8-bridge'), ); /** * Filter the list of IGNY8 modules that can be toggled. * * @param array $modules */ return apply_filters('igny8_available_modules', $modules); } /** * Get enabled modules * * @return array */ function igny8_get_enabled_modules() { $saved = get_option('igny8_enabled_modules'); if (is_array($saved) && !empty($saved)) { return $saved; } return array_keys(igny8_get_available_modules()); } /** * Check if a module is enabled * * @param string $module Module key * @return bool */ function igny8_is_module_enabled($module) { $modules = igny8_get_enabled_modules(); return in_array($module, $modules, true); } /** * Check if a post type is enabled for automation * * @param string $post_type Post type key * @return bool */ function igny8_is_post_type_enabled($post_type) { $post_types = igny8_get_enabled_post_types(); return in_array($post_type, $post_types, true); } /** * Check if IGNY8 connection is enabled * This is a master switch that disables all sync operations while preserving credentials * * @return bool True if connection is enabled */ function igny8_is_connection_enabled() { $enabled = get_option('igny8_connection_enabled', 1); return (bool) $enabled; } /** * Get webhook shared secret * * @return string Webhook secret */ function igny8_get_webhook_secret() { $secret = get_option('igny8_webhook_secret'); if (empty($secret)) { // Generate secret if not exists $secret = wp_generate_password(64, false); update_option('igny8_webhook_secret', $secret); } return $secret; } /** * Regenerate webhook secret * * @return string New secret */ function igny8_regenerate_webhook_secret() { $secret = wp_generate_password(64, false); update_option('igny8_webhook_secret', $secret); return $secret; } /** * Get configuration for site scans * * @param array $overrides Override defaults * @return array */ function igny8_get_site_scan_settings($overrides = array()) { $defaults = array( 'post_types' => igny8_get_enabled_post_types(), 'include_products' => (bool) get_option('igny8_enable_woocommerce', class_exists('WooCommerce') ? 1 : 0), 'per_page' => 100, 'since' => null, 'mode' => 'full', ); $settings = wp_parse_args($overrides, $defaults); return apply_filters('igny8_site_scan_settings', $settings); } /** * Register IGNY8 post meta fields */ function igny8_register_post_meta() { $post_types = array('post', 'page', 'product'); // Define all meta fields with proper schema for REST API $meta_fields = array( '_igny8_task_id' => array( 'type' => 'integer', 'description' => 'IGNY8 task ID linked to this post', 'single' => true, 'show_in_rest' => true, ), '_igny8_cluster_id' => array( 'type' => 'integer', 'description' => 'IGNY8 cluster ID', 'single' => true, 'show_in_rest' => true, ), '_igny8_sector_id' => array( 'type' => 'integer', 'description' => 'IGNY8 sector ID', 'single' => true, 'show_in_rest' => true, ), '_igny8_keyword_ids' => array( 'type' => 'array', 'description' => 'Array of IGNY8 keyword IDs', 'single' => true, 'show_in_rest' => array( 'schema' => array( 'type' => 'array', 'items' => array( 'type' => 'integer' ) ) ) ), '_igny8_content_id' => array( 'type' => 'integer', 'description' => 'IGNY8 content ID', 'single' => true, 'show_in_rest' => true, ), '_igny8_wordpress_status' => array( 'type' => 'string', 'description' => 'WordPress post status (for IGNY8 reference)', 'single' => true, 'show_in_rest' => true, ), '_igny8_last_synced' => array( 'type' => 'string', 'description' => 'Last sync timestamp', 'single' => true, 'show_in_rest' => true, ), '_igny8_task_brief' => array( 'type' => 'array', 'description' => 'Cached IGNY8 writer brief data', 'single' => true, 'show_in_rest' => array( 'schema' => array( 'type' => 'object' ) ) ), '_igny8_brief_cached_at' => array( 'type' => 'string', 'description' => 'Timestamp of cached IGNY8 brief', 'single' => true, 'show_in_rest' => true, ), '_igny8_optimizer_job_id' => array( 'type' => 'integer', 'description' => 'IGNY8 optimizer job ID', 'single' => true, 'show_in_rest' => true, ), '_igny8_optimizer_status' => array( 'type' => 'string', 'description' => 'IGNY8 optimizer job status', 'single' => true, 'show_in_rest' => true, ), '_igny8_optimizer_score_changes' => array( 'type' => 'array', 'description' => 'IGNY8 optimizer score changes', 'single' => true, 'show_in_rest' => array( 'schema' => array( 'type' => 'object' ) ) ), '_igny8_optimizer_recommendations' => array( 'type' => 'array', 'description' => 'IGNY8 optimizer recommendations', 'single' => true, 'show_in_rest' => array( 'schema' => array( 'type' => 'array' ) ) ), ); // 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); } } } /** * Register IGNY8 taxonomies */ function igny8_register_taxonomies() { // Register sectors taxonomy (hierarchical) - only if not exists if (!taxonomy_exists('igny8_sectors')) { register_taxonomy('igny8_sectors', array('post', 'page', 'product'), array( 'hierarchical' => true, 'labels' => array( 'name' => 'IGNY8 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', 'not_found' => 'No sectors found', ), 'public' => true, 'show_ui' => true, 'show_admin_column' => false, 'show_in_nav_menus' => true, 'show_tagcloud' => false, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'sectors', 'with_front' => false, ), 'capabilities' => array( '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('igny8_clusters')) { register_taxonomy('igny8_clusters', array('post', 'page', 'product'), array( 'hierarchical' => true, 'labels' => array( 'name' => 'IGNY8 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', 'not_found' => 'No clusters found', ), 'public' => true, 'show_ui' => true, 'show_admin_column' => false, 'show_in_nav_menus' => true, 'show_tagcloud' => false, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'clusters', 'with_front' => false, ), 'capabilities' => array( 'manage_terms' => 'manage_categories', 'edit_terms' => 'manage_categories', 'delete_terms' => 'manage_categories', 'assign_terms' => 'edit_posts', ), )); } } /** * Map WordPress post status to IGNY8 task status * * @param string $wp_status WordPress post status * @return string IGNY8 task status */ function igny8_map_wp_status_to_igny8($wp_status) { $status_map = array( 'publish' => 'completed', 'draft' => 'draft', 'pending' => 'pending', 'private' => 'completed', 'trash' => 'archived', 'future' => 'scheduled' ); return isset($status_map[$wp_status]) ? $status_map[$wp_status] : 'draft'; } /** * Check if post is managed by IGNY8 * * @param int $post_id Post ID * @return bool True if IGNY8 managed */ function igny8_is_igny8_managed_post($post_id) { $task_id = get_post_meta($post_id, '_igny8_task_id', true); return !empty($task_id); } /** * Get post data for IGNY8 sync * * @param int $post_id Post ID * @return array|false Post data or false on failure */ function igny8_get_post_data_for_sync($post_id) { $post = get_post($post_id); if (!$post) { return false; } return array( 'id' => $post_id, 'title' => $post->post_title, 'status' => $post->post_status, 'url' => get_permalink($post_id), 'modified' => $post->post_modified, 'published' => $post->post_date, 'author' => get_the_author_meta('display_name', $post->post_author), 'word_count' => str_word_count(strip_tags($post->post_content)), 'meta' => array( 'task_id' => get_post_meta($post_id, '_igny8_task_id', true), 'content_id' => get_post_meta($post_id, '_igny8_content_id', true), 'cluster_id' => get_post_meta($post_id, '_igny8_cluster_id', true), 'sector_id' => get_post_meta($post_id, '_igny8_sector_id', true), ) ); } /** * Schedule cron jobs */ function igny8_schedule_cron_jobs() { // Schedule daily post status sync (WordPress → IGNY8) if (!wp_next_scheduled('igny8_sync_post_statuses')) { wp_schedule_event(time(), 'daily', 'igny8_sync_post_statuses'); } // Schedule daily site data sync (incremental) if (!wp_next_scheduled('igny8_sync_site_data')) { wp_schedule_event(time(), 'daily', 'igny8_sync_site_data'); } // Schedule periodic full site scan (runs at most once per week) if (!wp_next_scheduled('igny8_full_site_scan')) { wp_schedule_event(time(), 'daily', 'igny8_full_site_scan'); } // Schedule hourly sync from IGNY8 (IGNY8 → WordPress) if (!wp_next_scheduled('igny8_sync_from_igny8')) { wp_schedule_event(time(), 'hourly', 'igny8_sync_from_igny8'); } // Schedule taxonomy sync if (!wp_next_scheduled('igny8_sync_taxonomies')) { wp_schedule_event(time(), 'twicedaily', 'igny8_sync_taxonomies'); } // Schedule keyword sync if (!wp_next_scheduled('igny8_sync_keywords')) { wp_schedule_event(time(), 'daily', 'igny8_sync_keywords'); } } /** * Unschedule cron jobs */ function igny8_unschedule_cron_jobs() { $timestamp = wp_next_scheduled('igny8_sync_post_statuses'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_sync_post_statuses'); } $timestamp = wp_next_scheduled('igny8_sync_site_data'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_sync_site_data'); } $timestamp = wp_next_scheduled('igny8_sync_from_igny8'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_sync_from_igny8'); } $timestamp = wp_next_scheduled('igny8_full_site_scan'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_full_site_scan'); } $timestamp = wp_next_scheduled('igny8_sync_taxonomies'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_sync_taxonomies'); } $timestamp = wp_next_scheduled('igny8_sync_keywords'); if ($timestamp) { wp_unschedule_event($timestamp, 'igny8_sync_keywords'); } }