'post', 'page' => 'page', 'product' => 'product', 'article' => 'post', 'blog' => 'post' ); $post_type = isset($post_type_map[$content_type]) ? $post_type_map[$content_type] : $content_type; $post_type = apply_filters('igny8_post_type_for_task', $post_type, $content_data); if (!post_type_exists($post_type)) { $post_type = 'post'; } return $post_type; } /** * Cache writer brief for a task * * @param int $task_id IGNY8 task ID * @param int $post_id WordPress post ID * @param Igny8API|null $api Optional API client */ function igny8_cache_task_brief($task_id, $post_id, $api = null) { if (!$task_id || !$post_id) { return; } $api = $api ?: new Igny8API(); if (!$api->is_authenticated()) { return; } $response = $api->get("/writer/tasks/{$task_id}/brief/"); if ($response && !empty($response['success']) && !empty($response['data'])) { update_post_meta($post_id, '_igny8_task_brief', $response['data']); update_post_meta($post_id, '_igny8_brief_cached_at', current_time('mysql')); } } /** * Create WordPress post from IGNY8 task/content * * @param array $content_data Content data from IGNY8 * @param array $allowed_post_types Post types allowed to be created automatically * @return int|WP_Error WordPress post ID or error */ function igny8_create_wordpress_post_from_task($content_data, $allowed_post_types = array()) { // Get site information for logging $site_id = get_option('igny8_site_id', 'unknown'); $site_domain = parse_url(home_url(), PHP_URL_HOST); $log_prefix = "[{$site_id}-{$site_domain}]"; Igny8_Logger::separator("{$log_prefix} 🎯 CREATE WORDPRESS POST FROM IGNY8"); Igny8_Logger::info("{$log_prefix} Content ID: " . ($content_data['content_id'] ?? 'N/A')); Igny8_Logger::info("{$log_prefix} Task ID: " . ($content_data['task_id'] ?? 'N/A')); Igny8_Logger::info("{$log_prefix} Title: " . ($content_data['title'] ?? 'N/A')); Igny8_Logger::separator(); $api = new Igny8API(); if (!$api->is_authenticated()) { Igny8_Logger::error("{$log_prefix} ❌ NOT AUTHENTICATED - Aborting post creation"); return new WP_Error('igny8_not_authenticated', 'IGNY8 API not authenticated'); } Igny8_Logger::info("{$log_prefix} ✅ API authenticated"); $post_type = igny8_resolve_post_type_for_task($content_data); Igny8_Logger::info("{$log_prefix} STEP 1: Resolved post type: {$post_type}"); if (!empty($allowed_post_types) && !in_array($post_type, $allowed_post_types, true)) { return new WP_Error('igny8_post_type_disabled', sprintf('Post type %s is disabled for automation', $post_type)); } // Prepare post data // Stage 1 Schema: accept content_html (new) or content (legacy fallback) $content_html = $content_data['content_html'] ?? $content_data['content'] ?? ''; // Map author $author_id = igny8_map_content_author($content_data); // Prepare excerpt $excerpt = $content_data['excerpt'] ?? $content_data['brief'] ?? ''; if (empty($excerpt) && !empty($content_html)) { $excerpt = wp_trim_excerpt($content_html); } $post_data = array( 'post_title' => sanitize_text_field($content_data['title'] ?? 'Untitled'), 'post_content' => wp_kses_post($content_html), 'post_excerpt' => sanitize_text_field($excerpt), 'post_status' => igny8_map_igny8_status_to_wp($content_data['status'] ?? 'draft'), 'post_type' => $post_type, 'post_author' => $author_id, 'meta_input' => array() ); // Set publication date if provided if (!empty($content_data['published_at'])) { $post_data['post_date'] = $content_data['published_at']; $post_data['post_date_gmt'] = get_gmt_from_date($content_data['published_at']); } // Add IGNY8 meta if (!empty($content_data['task_id'])) { $post_data['meta_input']['_igny8_task_id'] = $content_data['task_id']; } if (!empty($content_data['content_id'])) { $post_data['meta_input']['_igny8_content_id'] = $content_data['content_id']; } if (!empty($content_data['cluster_id'])) { $post_data['meta_input']['_igny8_cluster_id'] = $content_data['cluster_id']; } // Stage 1: New meta fields if (!empty($content_data['content_type'])) { $post_data['meta_input']['_igny8_content_type'] = $content_data['content_type']; } if (!empty($content_data['content_structure'])) { $post_data['meta_input']['_igny8_content_structure'] = $content_data['content_structure']; } // Store source field for tracking content origin (Stage 1) if (!empty($content_data['source'])) { $post_data['meta_input']['_igny8_source'] = $content_data['source']; } if (!empty($content_data['sector_id'])) { $post_data['meta_input']['_igny8_sector_id'] = $content_data['sector_id']; } if (!empty($content_data['keyword_ids'])) { $post_data['meta_input']['_igny8_keyword_ids'] = $content_data['keyword_ids']; } // Create post $post_id = wp_insert_post($post_data); if (is_wp_error($post_id)) { error_log("IGNY8: Failed to create WordPress post: " . $post_id->get_error_message()); return $post_id; } // Handle SEO metadata igny8_import_seo_metadata($post_id, $content_data); // Handle featured image igny8_import_featured_image($post_id, $content_data); // Handle categories and tags igny8_import_taxonomies($post_id, $content_data); // Import and process content images igny8_import_content_images($post_id, $content_html); // Assign taxonomies if cluster/sector IDs exist if (!empty($content_data['cluster_id'])) { // Find cluster term $cluster_terms = get_terms(array( 'taxonomy' => 'igny8_clusters', 'meta_key' => '_igny8_cluster_id', 'meta_value' => $content_data['cluster_id'], 'hide_empty' => false )); if (!is_wp_error($cluster_terms) && !empty($cluster_terms)) { wp_set_post_terms($post_id, array($cluster_terms[0]->term_id), 'igny8_clusters'); } } if (!empty($content_data['sector_id'])) { // Find sector term $sector_terms = get_terms(array( 'taxonomy' => 'igny8_sectors', 'meta_key' => '_igny8_sector_id', 'meta_value' => $content_data['sector_id'], 'hide_empty' => false )); if (!is_wp_error($sector_terms) && !empty($sector_terms)) { wp_set_post_terms($post_id, array($sector_terms[0]->term_id), 'igny8_sectors'); } } // Handle categories if (!empty($content_data['categories'])) { Igny8_Logger::info("{$log_prefix} STEP: Processing " . count($content_data['categories']) . " categories"); $category_ids = igny8_process_categories($content_data['categories'], $post_id); if (!empty($category_ids)) { wp_set_post_terms($post_id, $category_ids, 'category'); Igny8_Logger::info("{$log_prefix} ✅ Assigned " . count($category_ids) . " categories to post {$post_id}"); } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No category IDs returned from processing"); } } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No categories in content_data"); } // Handle tags if (!empty($content_data['tags'])) { Igny8_Logger::info("{$log_prefix} STEP: Processing " . count($content_data['tags']) . " tags"); $tag_ids = igny8_process_tags($content_data['tags'], $post_id); if (!empty($tag_ids)) { wp_set_post_terms($post_id, $tag_ids, 'post_tag'); Igny8_Logger::info("{$log_prefix} ✅ Assigned " . count($tag_ids) . " tags to post {$post_id}"); } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No tag IDs returned from processing"); } } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No tags in content_data"); } // Handle featured image if (!empty($content_data['featured_image'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting featured image from featured_image field"); igny8_set_featured_image($post_id, $content_data['featured_image']); } elseif (!empty($content_data['featured_image_url'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting featured image from URL: " . $content_data['featured_image_url']); igny8_set_featured_image($post_id, $content_data['featured_image_url']); } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No featured image in content_data"); } // Handle meta title and meta description (SEO) if (!empty($content_data['meta_title'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting SEO meta title: " . $content_data['meta_title']); update_post_meta($post_id, '_yoast_wpseo_title', $content_data['meta_title']); update_post_meta($post_id, '_seopress_titles_title', $content_data['meta_title']); update_post_meta($post_id, '_aioseo_title', $content_data['meta_title']); // Generic meta update_post_meta($post_id, '_igny8_meta_title', $content_data['meta_title']); } elseif (!empty($content_data['seo_title'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting SEO meta title from seo_title: " . $content_data['seo_title']); update_post_meta($post_id, '_yoast_wpseo_title', $content_data['seo_title']); update_post_meta($post_id, '_seopress_titles_title', $content_data['seo_title']); update_post_meta($post_id, '_aioseo_title', $content_data['seo_title']); update_post_meta($post_id, '_igny8_meta_title', $content_data['seo_title']); } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No SEO title in content_data"); } if (!empty($content_data['meta_description'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting SEO meta description"); update_post_meta($post_id, '_yoast_wpseo_metadesc', $content_data['meta_description']); update_post_meta($post_id, '_seopress_titles_desc', $content_data['meta_description']); update_post_meta($post_id, '_aioseo_description', $content_data['meta_description']); // Generic meta update_post_meta($post_id, '_igny8_meta_description', $content_data['meta_description']); } elseif (!empty($content_data['seo_description'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting SEO meta description from seo_description"); update_post_meta($post_id, '_yoast_wpseo_metadesc', $content_data['seo_description']); update_post_meta($post_id, '_seopress_titles_desc', $content_data['seo_description']); update_post_meta($post_id, '_aioseo_description', $content_data['seo_description']); update_post_meta($post_id, '_igny8_meta_description', $content_data['seo_description']); } else { Igny8_Logger::warning("{$log_prefix} ⚠️ No SEO description in content_data"); } // Handle gallery images if (!empty($content_data['gallery_images'])) { Igny8_Logger::info("{$log_prefix} STEP: Setting gallery with " . count($content_data['gallery_images']) . " images"); igny8_set_gallery_images($post_id, $content_data['gallery_images']); } // Get the actual WordPress post status (after creation) $created_post = get_post($post_id); $wp_status = $created_post ? $created_post->post_status : 'draft'; // Store WordPress status in meta for IGNY8 to read update_post_meta($post_id, '_igny8_wordpress_status', $wp_status); // Map WordPress status back to IGNY8 status $igny8_status = igny8_map_wp_status_to_igny8($wp_status); // Update IGNY8 task with WordPress post ID, URL, and status if (!empty($content_data['task_id'])) { $update_data = array( 'assigned_post_id' => $post_id, 'post_url' => get_permalink($post_id), 'wordpress_status' => $wp_status, // WordPress actual status (publish/pending/draft) 'status' => $igny8_status, // IGNY8 mapped status (completed/pending/draft) 'synced_at' => current_time('mysql'), 'post_type' => $post_type, // WordPress post type 'content_type' => $content_type // IGNY8 content type ); // Include content_id if provided if (!empty($content_data['content_id'])) { $update_data['content_id'] = $content_data['content_id']; } $response = $api->put("/writer/tasks/{$content_data['task_id']}/", $update_data); if ($response['success']) { Igny8_Logger::info("{$log_prefix} ✅ Updated IGNY8 task {$content_data['task_id']} with WordPress post {$post_id} (status: {$wp_status})"); } else { Igny8_Logger::error("{$log_prefix} ❌ Failed to update IGNY8 task: " . ($response['error'] ?? 'Unknown error')); } } // Store content_id if provided (for IGNY8 to query) if (!empty($content_data['content_id'])) { update_post_meta($post_id, '_igny8_content_id', $content_data['content_id']); } // Send status webhook to IGNY8 igny8_send_status_webhook($post_id, $content_data, $wp_status); Igny8_Logger::separator("{$log_prefix} 🎉 POST CREATION COMPLETED SUCCESSFULLY"); Igny8_Logger::info("{$log_prefix} WordPress Post ID: {$post_id}"); Igny8_Logger::info("{$log_prefix} Post URL: " . get_permalink($post_id)); Igny8_Logger::info("{$log_prefix} Post Status: {$wp_status}"); Igny8_Logger::separator(); return $post_id; } /** * Map IGNY8 task status to WordPress post status * * @param string $igny8_status IGNY8 task status * @return string WordPress post status */ function igny8_map_igny8_status_to_wp($igny8_status) { $status_map = array( 'completed' => 'publish', 'draft' => 'draft', 'pending' => 'pending', 'scheduled' => 'future', 'archived' => 'trash' ); return isset($status_map[$igny8_status]) ? $status_map[$igny8_status] : 'draft'; } /** * Sync IGNY8 tasks to WordPress posts * Fetches tasks from IGNY8 and creates/updates WordPress posts * * @param array $filters Optional filters (status, cluster_id, etc.) * @return array Sync results */ function igny8_sync_igny8_tasks_to_wp($filters = array()) { // Skip if connection is disabled if (!igny8_is_connection_enabled()) { return array('success' => false, 'error' => 'Connection disabled', 'disabled' => true); } $api = new Igny8API(); if (!$api->is_authenticated()) { return array('success' => false, 'error' => 'Not authenticated'); } if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('writer')) { return array('success' => true, 'created' => 0, 'updated' => 0, 'failed' => 0, 'skipped' => 0, 'total' => 0, 'disabled' => true); } $site_id = get_option('igny8_site_id'); if (!$site_id) { return array('success' => false, 'error' => 'Site ID not configured'); } $enabled_post_types = function_exists('igny8_get_enabled_post_types') ? igny8_get_enabled_post_types() : array('post', 'page'); // Build endpoint with filters $endpoint = '/writer/tasks/'; $query_params = array(); $query_params[] = 'site_id=' . intval($site_id); if (!empty($filters['status'])) { $query_params[] = 'status=' . urlencode($filters['status']); } if (!empty($filters['cluster_id'])) { $query_params[] = 'cluster_id=' . intval($filters['cluster_id']); } if (!empty($query_params)) { $endpoint .= '?' . implode('&', $query_params); } // Get tasks from IGNY8 $response = $api->get($endpoint); if (!$response['success']) { return array('success' => false, 'error' => $response['error'] ?? 'Unknown error'); } $tasks = $response['data']['results'] ?? $response['data'] ?? $response['results'] ?? array(); $created = 0; $updated = 0; $failed = 0; $skipped = 0; foreach ($tasks as $task) { // Check if post already exists $existing_posts = get_posts(array( 'meta_key' => '_igny8_task_id', 'meta_value' => $task['id'], 'post_type' => 'any', 'posts_per_page' => 1 )); if (!empty($existing_posts)) { // Update existing post $post_id = $existing_posts[0]->ID; $update_data = array( 'ID' => $post_id, 'post_title' => $task['title'] ?? get_the_title($post_id), 'post_status' => igny8_map_igny8_status_to_wp($task['status'] ?? 'draft') ); // Stage 1 Schema: accept content_html or content $content_html = $task['content_html'] ?? $task['content'] ?? ''; if (!empty($content_html)) { $update_data['post_content'] = $content_html; } $result = wp_update_post($update_data); // Update categories, tags, images, and meta if ($result && !is_wp_error($result)) { // Update categories if (!empty($task['categories'])) { $category_ids = igny8_process_categories($task['categories'], $post_id); if (!empty($category_ids)) { wp_set_post_terms($post_id, $category_ids, 'category'); } } // Update tags if (!empty($task['tags'])) { $tag_ids = igny8_process_tags($task['tags'], $post_id); if (!empty($tag_ids)) { wp_set_post_terms($post_id, $tag_ids, 'post_tag'); } } // Update featured image if (!empty($task['featured_image']) || !empty($task['featured_media'])) { igny8_set_featured_image($post_id, $task['featured_image'] ?? $task['featured_media']); } // Update gallery if (!empty($task['gallery_images']) || !empty($task['images'])) { igny8_set_image_gallery($post_id, $task['gallery_images'] ?? $task['images']); } // Update meta title and description if (!empty($task['meta_title']) || !empty($task['seo_title'])) { $meta_title = $task['meta_title'] ?? $task['seo_title']; update_post_meta($post_id, '_yoast_wpseo_title', $meta_title); update_post_meta($post_id, '_seopress_titles_title', $meta_title); update_post_meta($post_id, '_aioseo_title', $meta_title); update_post_meta($post_id, '_igny8_meta_title', $meta_title); } if (!empty($task['meta_description']) || !empty($task['seo_description'])) { $meta_desc = $task['meta_description'] ?? $task['seo_description']; update_post_meta($post_id, '_yoast_wpseo_metadesc', $meta_desc); update_post_meta($post_id, '_seopress_titles_desc', $meta_desc); update_post_meta($post_id, '_aioseo_description', $meta_desc); update_post_meta($post_id, '_igny8_meta_description', $meta_desc); } } if ($result && !is_wp_error($result)) { igny8_cache_task_brief($task['id'], $post_id, $api); $updated++; } else { $failed++; } } else { // Create new post $task_post_type = igny8_resolve_post_type_for_task($task); if (!empty($enabled_post_types) && !in_array($task_post_type, $enabled_post_types, true)) { $skipped++; continue; } // Stage 1 Schema: use content_html with fallback $content_html = $task['content_html'] ?? $task['content'] ?? ''; $content_data = array( 'task_id' => $task['id'], 'content_id' => $task['content_id'] ?? null, 'title' => $task['title'] ?? 'Untitled', 'content_html' => $content_html, 'content' => $content_html, // Keep for backward compatibility in function 'status' => $task['status'] ?? 'draft', 'cluster_id' => $task['cluster_id'] ?? null, 'sector_id' => $task['sector_id'] ?? null, 'keyword_ids' => $task['keyword_ids'] ?? array(), 'content_type' => $task['content_type'] ?? $task['post_type'] ?? 'post', 'content_structure' => $task['content_structure'] ?? 'article', 'post_type' => $task['post_type'] ?? null, // Keep for backward compatibility 'categories' => $task['categories'] ?? array(), 'tags' => $task['tags'] ?? array(), 'featured_image' => $task['featured_image'] ?? $task['featured_media'] ?? null, 'gallery_images' => $task['gallery_images'] ?? $task['images'] ?? array(), 'meta_title' => $task['meta_title'] ?? $task['seo_title'] ?? null, 'meta_description' => $task['meta_description'] ?? $task['seo_description'] ?? null ); $post_id = igny8_create_wordpress_post_from_task($content_data, $enabled_post_types); if (is_wp_error($post_id)) { if ($post_id->get_error_code() === 'igny8_post_type_disabled') { $skipped++; } else { $failed++; } } elseif ($post_id) { igny8_cache_task_brief($task['id'], $post_id, $api); $created++; } else { $failed++; } } } return array( 'success' => true, 'created' => $created, 'updated' => $updated, 'failed' => $failed, 'skipped' => $skipped, 'total' => count($tasks) ); } /** * Handle webhook from IGNY8 (when content is published from IGNY8) * This can be called via REST API endpoint or scheduled sync * * @param array $webhook_data Webhook data from IGNY8 * @return int|false WordPress post ID or false on failure */ function igny8_handle_igny8_webhook($webhook_data) { if (empty($webhook_data['task_id'])) { return false; } $api = new Igny8API(); // Get full task data from IGNY8 $task_response = $api->get("/writer/tasks/{$webhook_data['task_id']}/"); if (!$task_response['success']) { return false; } $task = $task_response['data']; // Prepare content data // Stage 1 Schema: use content_html with fallback to content $content_html = $task['content_html'] ?? $task['content'] ?? ''; $content_data = array( 'task_id' => $task['id'], 'content_id' => $task['content_id'] ?? null, 'title' => $task['title'] ?? 'Untitled', 'content_html' => $content_html, 'content' => $content_html, // Keep for backward compatibility 'status' => $task['status'] ?? 'draft', 'cluster_id' => $task['cluster_id'] ?? null, 'sector_id' => $task['sector_id'] ?? null, 'keyword_ids' => $task['keyword_ids'] ?? array(), 'content_type' => $task['content_type'] ?? 'post', 'content_structure' => $task['content_structure'] ?? 'article', 'post_type' => $task['post_type'] ?? $task['content_type'] ?? 'post' ); return igny8_create_wordpress_post_from_task($content_data); } /** * Process categories from IGNY8 * * @param array $categories Category data (IDs, names, or slugs) * @param int $post_id Post ID * @return array Category term IDs */ function igny8_process_categories($categories, $post_id) { $category_ids = array(); foreach ($categories as $category) { $term_id = null; // If it's an ID if (is_numeric($category)) { $term = get_term($category, 'category'); if ($term && !is_wp_error($term)) { $term_id = $term->term_id; } } // If it's an array with name/slug elseif (is_array($category)) { $name = $category['name'] ?? $category['slug'] ?? null; $slug = $category['slug'] ?? sanitize_title($name); if ($name) { // Try to find existing term $term = get_term_by('slug', $slug, 'category'); if (!$term) { $term = get_term_by('name', $name, 'category'); } // Create if doesn't exist if (!$term || is_wp_error($term)) { $term_result = wp_insert_term($name, 'category', array('slug' => $slug)); if (!is_wp_error($term_result)) { $term_id = $term_result['term_id']; } } else { $term_id = $term->term_id; } } } // If it's a string (name or slug) elseif (is_string($category)) { $term = get_term_by('slug', $category, 'category'); if (!$term) { $term = get_term_by('name', $category, 'category'); } if ($term && !is_wp_error($term)) { $term_id = $term->term_id; } else { // Create new category $term_result = wp_insert_term($category, 'category'); if (!is_wp_error($term_result)) { $term_id = $term_result['term_id']; } } } if ($term_id) { $category_ids[] = $term_id; } } return array_unique($category_ids); } /** * Process tags from IGNY8 * * @param array $tags Tag data (IDs, names, or slugs) * @param int $post_id Post ID * @return array Tag term IDs */ function igny8_process_tags($tags, $post_id) { $tag_ids = array(); foreach ($tags as $tag) { $term_id = null; // If it's an ID if (is_numeric($tag)) { $term = get_term($tag, 'post_tag'); if ($term && !is_wp_error($term)) { $term_id = $term->term_id; } } // If it's an array with name/slug elseif (is_array($tag)) { $name = $tag['name'] ?? $tag['slug'] ?? null; $slug = $tag['slug'] ?? sanitize_title($name); if ($name) { // Try to find existing term $term = get_term_by('slug', $slug, 'post_tag'); if (!$term) { $term = get_term_by('name', $name, 'post_tag'); } // Create if doesn't exist if (!$term || is_wp_error($term)) { $term_result = wp_insert_term($name, 'post_tag', array('slug' => $slug)); if (!is_wp_error($term_result)) { $term_id = $term_result['term_id']; } } else { $term_id = $term->term_id; } } } // If it's a string (name or slug) elseif (is_string($tag)) { $term = get_term_by('slug', $tag, 'post_tag'); if (!$term) { $term = get_term_by('name', $tag, 'post_tag'); } if ($term && !is_wp_error($term)) { $term_id = $term->term_id; } else { // Create new tag $term_result = wp_insert_term($tag, 'post_tag'); if (!is_wp_error($term_result)) { $term_id = $term_result['term_id']; } } } if ($term_id) { $tag_ids[] = $term_id; } } return array_unique($tag_ids); } /** * Set featured image for post * * @param int $post_id Post ID * @param string|array $image_data Image URL or array with image data * @return int|false Attachment ID or false on failure */ function igny8_set_featured_image($post_id, $image_data) { $image_url = is_array($image_data) ? ($image_data['url'] ?? $image_data['src'] ?? '') : $image_data; if (empty($image_url)) { return false; } // Check if image already exists $attachment_id = igny8_get_attachment_by_url($image_url); if (!$attachment_id) { // Download and attach image $attachment_id = igny8_import_image($image_url, $post_id); } if ($attachment_id) { set_post_thumbnail($post_id, $attachment_id); return $attachment_id; } return false; } /** * Set image gallery for post (1-5 images) * * @param int $post_id Post ID * @param array $gallery_images Array of image URLs or image data * @return array Attachment IDs */ function igny8_set_image_gallery($post_id, $gallery_images) { $attachment_ids = array(); // Limit to 5 images $gallery_images = array_slice($gallery_images, 0, 5); foreach ($gallery_images as $image_data) { $image_url = is_array($image_data) ? ($image_data['url'] ?? $image_data['src'] ?? '') : $image_data; if (empty($image_url)) { continue; } // Check if image already exists $attachment_id = igny8_get_attachment_by_url($image_url); if (!$attachment_id) { // Download and attach image $attachment_id = igny8_import_image($image_url, $post_id); } if ($attachment_id) { $attachment_ids[] = $attachment_id; } } // Store gallery as post meta (WordPress native) if (!empty($attachment_ids)) { update_post_meta($post_id, '_igny8_gallery_images', $attachment_ids); // Also store in format compatible with plugins update_post_meta($post_id, '_product_image_gallery', implode(',', $attachment_ids)); // WooCommerce update_post_meta($post_id, '_gallery_images', $attachment_ids); // Generic } return $attachment_ids; } /** * Get attachment ID by image URL * * @param string $image_url Image URL * @return int|false Attachment ID or false */ function igny8_get_attachment_by_url($image_url) { global $wpdb; $attachment_id = $wpdb->get_var($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE guid = %s AND post_type = 'attachment'", $image_url )); return $attachment_id ? intval($attachment_id) : false; } /** * Import image from URL and attach to post * * @param string $image_url Image URL * @param int $post_id Post ID to attach to * @return int|false Attachment ID or false on failure */ function igny8_import_image($image_url, $post_id) { require_once(ABSPATH . 'wp-admin/includes/image.php'); require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); // Download image $tmp = download_url($image_url); if (is_wp_error($tmp)) { error_log("IGNY8: Failed to download image {$image_url}: " . $tmp->get_error_message()); return false; } // Get file extension $file_array = array( 'name' => basename(parse_url($image_url, PHP_URL_PATH)), 'tmp_name' => $tmp ); // Upload to WordPress media library $attachment_id = media_handle_sideload($file_array, $post_id); // Clean up temp file @unlink($tmp); if (is_wp_error($attachment_id)) { error_log("IGNY8: Failed to import image {$image_url}: " . $attachment_id->get_error_message()); return false; } return $attachment_id; } /** * Map IGNY8 author to WordPress user * * @param array $content_data Content data from IGNY8 * @return int WordPress user ID */ function igny8_map_content_author($content_data) { $author_email = $content_data['author_email'] ?? $content_data['author'] ?? ''; if (empty($author_email)) { return 1; // Default to admin user } // Try to find existing user by email $user = get_user_by('email', $author_email); if ($user) { return $user->ID; } // Create new user if enabled and author name provided $author_name = $content_data['author_name'] ?? $content_data['author_display_name'] ?? ''; if (!empty($author_name) && apply_filters('igny8_auto_create_authors', true)) { $user_data = array( 'user_login' => sanitize_user($author_email), 'user_email' => $author_email, 'display_name' => sanitize_text_field($author_name), 'role' => apply_filters('igny8_default_author_role', 'author') ); $user_id = wp_insert_user($user_data); if (!is_wp_error($user_id)) { error_log("IGNY8: Created new author user: {$author_email}"); return $user_id; } } return 1; // Fallback to admin } /** * Import SEO metadata for post * * @param int $post_id WordPress post ID * @param array $content_data Content data from IGNY8 */ function igny8_import_seo_metadata($post_id, $content_data) { // SEO title if (!empty($content_data['seo_title'])) { update_post_meta($post_id, '_igny8_seo_title', sanitize_text_field($content_data['seo_title'])); // For Yoast SEO compatibility if (defined('WPSEO_VERSION')) { update_post_meta($post_id, '_yoast_wpseo_title', sanitize_text_field($content_data['seo_title'])); } } // SEO description if (!empty($content_data['seo_description'])) { update_post_meta($post_id, '_igny8_seo_description', sanitize_text_field($content_data['seo_description'])); // For Yoast SEO compatibility if (defined('WPSEO_VERSION')) { update_post_meta($post_id, '_yoast_wpseo_metadesc', sanitize_text_field($content_data['seo_description'])); } } // Focus keywords if (!empty($content_data['focus_keywords'])) { update_post_meta($post_id, '_igny8_focus_keywords', $content_data['focus_keywords']); // For Yoast SEO compatibility if (defined('WPSEO_VERSION') && is_array($content_data['focus_keywords'])) { update_post_meta($post_id, '_yoast_wpseo_focuskw', $content_data['focus_keywords'][0]); } } } /** * Import featured image for post * * @param int $post_id WordPress post ID * @param array $content_data Content data from IGNY8 */ function igny8_import_featured_image($post_id, $content_data) { $featured_image_url = $content_data['featured_image_url'] ?? $content_data['featured_image'] ?? ''; if (empty($featured_image_url)) { return; } // Check if URL is valid if (!filter_var($featured_image_url, FILTER_VALIDATE_URL)) { error_log("IGNY8: Invalid featured image URL: {$featured_image_url}"); return; } // Import image $attachment_id = igny8_import_image($featured_image_url, $post_id); if ($attachment_id) { set_post_thumbnail($post_id, $attachment_id); update_post_meta($post_id, '_igny8_featured_image_imported', current_time('mysql')); error_log("IGNY8: Featured image imported for post {$post_id}: attachment {$attachment_id}"); } } /** * Import taxonomies (categories and tags) for post * * @param int $post_id WordPress post ID * @param array $content_data Content data from IGNY8 */ function igny8_import_taxonomies($post_id, $content_data) { // Handle categories from sectors if (!empty($content_data['sectors']) && is_array($content_data['sectors'])) { $category_ids = array(); foreach ($content_data['sectors'] as $sector) { $sector_name = is_array($sector) ? $sector['name'] : $sector; if (empty($sector_name)) { continue; } // Find or create category $term = get_term_by('name', $sector_name, 'category'); if (!$term) { $term_result = wp_insert_term($sector_name, 'category'); if (!is_wp_error($term_result)) { $category_ids[] = $term_result['term_id']; // Store IGNY8 sector ID if provided if (is_array($sector) && !empty($sector['id'])) { update_term_meta($term_result['term_id'], '_igny8_sector_id', $sector['id']); } } } else { $category_ids[] = $term->term_id; } } if (!empty($category_ids)) { wp_set_post_categories($post_id, $category_ids); } } // Handle tags from clusters if (!empty($content_data['clusters']) && is_array($content_data['clusters'])) { $tag_names = array(); foreach ($content_data['clusters'] as $cluster) { $cluster_name = is_array($cluster) ? $cluster['name'] : $cluster; if (!empty($cluster_name)) { $tag_names[] = $cluster_name; } } if (!empty($tag_names)) { wp_set_post_tags($post_id, $tag_names); } } // Handle custom tags if provided if (!empty($content_data['tags']) && is_array($content_data['tags'])) { wp_set_post_tags($post_id, $content_data['tags'], true); // Append to existing } } /** * Import and process images in content * * @param int $post_id WordPress post ID * @param string $content_html HTML content */ function igny8_import_content_images($post_id, $content_html) { if (empty($content_html)) { return; } // Find all images in content preg_match_all('/]+src="([^"]+)"[^>]*>/i', $content_html, $matches); if (empty($matches[1])) { return; } $imported_images = array(); foreach ($matches[1] as $image_url) { // Skip if already a WordPress URL if (strpos($image_url, site_url()) === 0) { continue; } // Skip invalid URLs if (!filter_var($image_url, FILTER_VALIDATE_URL)) { continue; } // Import image $attachment_id = igny8_import_image($image_url, $post_id); if ($attachment_id) { $new_url = wp_get_attachment_url($attachment_id); if ($new_url) { // Replace URL in content $content_html = str_replace($image_url, $new_url, $content_html); $imported_images[] = $attachment_id; } } } // Update post content with new image URLs if (!empty($imported_images)) { wp_update_post(array( 'ID' => $post_id, 'post_content' => $content_html )); update_post_meta($post_id, '_igny8_imported_images', $imported_images); update_post_meta($post_id, '_igny8_images_imported_at', current_time('mysql')); error_log("IGNY8: Imported " . count($imported_images) . " content images for post {$post_id}"); } } /** * Scheduled sync from IGNY8 to WordPress * Fetches new/updated tasks from IGNY8 and creates/updates WordPress posts */ function igny8_cron_sync_from_igny8() { // Skip if connection is disabled if (!igny8_is_connection_enabled()) { error_log('IGNY8: Connection disabled, skipping sync from IGNY8'); return; } $site_id = get_option('igny8_site_id'); if (!$site_id) { error_log('IGNY8: Site ID not set, skipping sync from IGNY8'); return; } if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('writer')) { error_log('IGNY8: Writer module disabled, skipping sync from IGNY8'); return; } // Get last sync time $last_sync = get_option('igny8_last_sync_from_igny8', 0); // Sync only completed/published tasks $filters = array( 'status' => 'completed' ); // If we have a last sync time, we could filter by updated date // For now, sync all completed tasks (API should handle deduplication) $result = igny8_sync_igny8_tasks_to_wp($filters); if ($result['success']) { update_option('igny8_last_sync_from_igny8', time()); update_option('igny8_last_writer_sync', current_time('timestamp')); error_log(sprintf( 'IGNY8: Synced from IGNY8 - Created %d posts, updated %d posts, %d failed, %d skipped', $result['created'], $result['updated'], $result['failed'], $result['skipped'] ?? 0 )); } else { error_log('IGNY8: Failed to sync from IGNY8: ' . ($result['error'] ?? 'Unknown error')); } } /** * Send status webhook to IGNY8 backend * Notifies IGNY8 when WordPress post status changes * * @param int $post_id WordPress post ID * @param array $content_data Content data containing content_id * @param string $post_status WordPress post status */ function igny8_send_status_webhook($post_id, $content_data, $post_status) { // Get site information for logging $site_id = get_option('igny8_site_id', 'unknown'); $site_domain = parse_url(home_url(), PHP_URL_HOST); $log_prefix = "[{$site_id}-{$site_domain}]"; Igny8_Logger::separator("{$log_prefix} 📤 SENDING STATUS WEBHOOK TO IGNY8"); // Only send webhook if connection is enabled if (!igny8_is_connection_enabled()) { Igny8_Logger::warning("{$log_prefix} ⚠️ Connection not enabled, skipping webhook", 'webhooks'); return; } // Get required data $content_id = $content_data['content_id'] ?? get_post_meta($post_id, '_igny8_content_id', true); if (!$content_id) { Igny8_Logger::error("{$log_prefix} ❌ Cannot send status webhook - no content_id", 'webhooks'); return; } Igny8_Logger::info("{$log_prefix} Content ID: {$content_id}", 'webhooks'); Igny8_Logger::info("{$log_prefix} Post ID: {$post_id}", 'webhooks'); Igny8_Logger::info("{$log_prefix} Post Status: {$post_status}", 'webhooks'); // Get API endpoint from settings $api = new Igny8API(); $api_base = $api->get_api_base(); if (!$api_base) { Igny8_Logger::error("{$log_prefix} ❌ Cannot send status webhook - no API base URL", 'webhooks'); return; } $webhook_url = rtrim($api_base, '/') . '/integration/webhooks/wordpress/status/'; Igny8_Logger::info("{$log_prefix} Webhook URL: {$webhook_url}", 'webhooks'); // Get API key $api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key'); if (!$api_key) { Igny8_Logger::error("{$log_prefix} ❌ Cannot send status webhook - no API key", 'webhooks'); return; } Igny8_Logger::info("{$log_prefix} API Key: ***" . substr($api_key, -4), 'webhooks'); // Prepare webhook payload $payload = array( 'post_id' => $post_id, 'content_id' => intval($content_id), 'post_status' => $post_status, 'post_url' => get_permalink($post_id), 'post_title' => get_the_title($post_id), 'site_url' => get_site_url(), ); Igny8_Logger::info("{$log_prefix} Payload: " . json_encode($payload), 'webhooks'); // Send webhook asynchronously Igny8_Logger::info("{$log_prefix} Sending POST request to IGNY8...", 'webhooks'); $response = wp_remote_post($webhook_url, array( 'headers' => array( 'Content-Type' => 'application/json', 'X-IGNY8-API-KEY' => $api_key, ), 'body' => json_encode($payload), 'timeout' => 10, 'blocking' => false, // Non-blocking for better performance )); if (is_wp_error($response)) { Igny8_Logger::error("{$log_prefix} ❌ Status webhook failed: " . $response->get_error_message(), 'webhooks'); } else { Igny8_Logger::info("{$log_prefix} ✅ Status webhook sent successfully for content {$content_id}, post {$post_id}, status {$post_status}", 'webhooks'); } Igny8_Logger::separator("{$log_prefix} ✅ WEBHOOK SEND COMPLETED"); }