Plugin packaging and docs
This commit is contained in:
@@ -0,0 +1,650 @@
|
||||
# Actionable Implementation Plan - WordPress Publishing Fix
|
||||
|
||||
**Date:** November 29, 2025
|
||||
**Issue:** Only title is being published to WordPress, no content_html or other fields
|
||||
**Root Cause:** Data mismatch between IGNY8 backend payload and WordPress plugin expectations
|
||||
|
||||
---
|
||||
|
||||
## 🔴 CRITICAL ISSUE DIAGNOSED
|
||||
|
||||
### The Problem
|
||||
|
||||
**Current Behavior:**
|
||||
- IGNY8 backend sends `content_html` field in payload
|
||||
- WordPress plugin receives the data BUT the POST request payload does NOT include the actual content data from the `ContentPost` model
|
||||
- Only `title` appears in WordPress because the REST API endpoint fetches data from the wrong endpoint
|
||||
|
||||
**Root Cause Analysis:**
|
||||
|
||||
1. **File:** `igny8_core/tasks/wordpress_publishing.py` (Line 53-75)
|
||||
```python
|
||||
content_data = {
|
||||
'content_id': content.id,
|
||||
'task_id': task_id,
|
||||
'title': content.title,
|
||||
'content_html': content.content_html or content.content, # ← Should work
|
||||
'excerpt': content.brief or '', # ← Field name mismatch
|
||||
'status': 'publish',
|
||||
# ... more fields
|
||||
}
|
||||
```
|
||||
|
||||
2. **File:** `includes/class-igny8-rest-api.php` (Line 507-525)
|
||||
```php
|
||||
// Try to get content by different endpoints
|
||||
$content_data = null;
|
||||
|
||||
if ($task_id) {
|
||||
$response = $api->get("/writer/tasks/{$task_id}/"); // ← WRONG!
|
||||
if ($response['success']) {
|
||||
$content_data = $response['data'];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**The Issue:** WordPress is fetching from `/writer/tasks/{task_id}/` which returns `Tasks` model data, NOT `Content` model data!
|
||||
|
||||
3. **Model Mismatch:**
|
||||
- `Tasks` model has: `title`, `description`, `keywords`, `word_count`, `status`
|
||||
- `Content` model has: `title`, `content_html`, `meta_title`, `meta_description`
|
||||
- WordPress gets `Tasks` data which has NO `content_html` field!
|
||||
|
||||
---
|
||||
|
||||
## ✅ SOLUTION ARCHITECTURE
|
||||
|
||||
### Phase 1: Fix Data Flow (CRITICAL - Do First)
|
||||
|
||||
#### Problem 1.1: WordPress REST Endpoint Fetches Wrong Data
|
||||
|
||||
**File to Fix:** `c:\Users\Hp\vscode\igny8-wp-integration\includes\class-igny8-rest-api.php`
|
||||
|
||||
**Current Code (Line 507-525):**
|
||||
```php
|
||||
// Try to get content by different endpoints
|
||||
$content_data = null;
|
||||
|
||||
if ($task_id) {
|
||||
$response = $api->get("/writer/tasks/{$task_id}/"); // ← FETCHES TASKS MODEL
|
||||
if ($response['success']) {
|
||||
$content_data = $response['data'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$content_data && $content_id) {
|
||||
// Try content endpoint if available
|
||||
$response = $api->get("/content/{$content_id}/"); // ← THIS IS CORRECT
|
||||
if ($response['success']) {
|
||||
$content_data = $response['data'];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fix Required:**
|
||||
```php
|
||||
// REMOVE the task endpoint fetch entirely
|
||||
// WordPress should ONLY use data sent in POST body from IGNY8
|
||||
|
||||
public function publish_content_to_wordpress($request) {
|
||||
// ... existing validation ...
|
||||
|
||||
// Get all data from POST body (IGNY8 already sent everything)
|
||||
$content_data = $request->get_json_params();
|
||||
|
||||
// Validate required fields
|
||||
if (empty($content_data['title']) || empty($content_data['content_html'])) {
|
||||
return $this->build_unified_response(
|
||||
false,
|
||||
null,
|
||||
'Missing required fields: title and content_html',
|
||||
'missing_fields',
|
||||
null,
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
// NO API CALL BACK TO IGNY8 - just use the data we received!
|
||||
// ... proceed to create post ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Problem 1.2: IGNY8 Backend Field Name Mismatch
|
||||
|
||||
**File to Check:** `e:\Projects\...\igny8\backend\igny8_core\business\content\models.py`
|
||||
|
||||
**Content Model Fields (Lines 166-173):**
|
||||
```python
|
||||
# Core content fields
|
||||
title = models.CharField(max_length=255, db_index=True)
|
||||
content_html = models.TextField(help_text="Final HTML content") # ✓ CORRECT
|
||||
word_count = models.IntegerField(default=0)
|
||||
|
||||
# SEO fields
|
||||
meta_title = models.CharField(max_length=255, blank=True, null=True)
|
||||
meta_description = models.TextField(blank=True, null=True)
|
||||
primary_keyword = models.CharField(max_length=255, blank=True, null=True)
|
||||
```
|
||||
|
||||
**File to Fix:** `e:\Projects\...\igny8\backend\igny8_core\tasks\wordpress_publishing.py`
|
||||
|
||||
**Current Code (Lines 53-75):**
|
||||
```python
|
||||
content_data = {
|
||||
'content_id': content.id,
|
||||
'task_id': task_id,
|
||||
'title': content.title,
|
||||
'content_html': content.content_html or content.content, # ✓ CORRECT
|
||||
'excerpt': content.brief or '', # ← WRONG! Content model has no 'brief' field
|
||||
'status': 'publish',
|
||||
'author_email': content.author.email if content.author else None,
|
||||
'author_name': content.author.get_full_name() if content.author else None,
|
||||
'published_at': content.published_at.isoformat() if content.published_at else None,
|
||||
'seo_title': getattr(content, 'seo_title', ''), # ← WRONG! Should be 'meta_title'
|
||||
'seo_description': getattr(content, 'seo_description', ''), # ← WRONG! Should be 'meta_description'
|
||||
'featured_image_url': content.featured_image.url if content.featured_image else None,
|
||||
'sectors': [{'id': s.id, 'name': s.name} for s in content.sectors.all()],
|
||||
'clusters': [{'id': c.id, 'name': c.name} for c in content.clusters.all()],
|
||||
'tags': getattr(content, 'tags', []), # ← Needs verification
|
||||
'focus_keywords': getattr(content, 'focus_keywords', []) # ← Should be 'secondary_keywords'
|
||||
}
|
||||
```
|
||||
|
||||
**Fix Required:**
|
||||
```python
|
||||
# Generate excerpt from content_html if not present
|
||||
excerpt = ''
|
||||
if content.content_html:
|
||||
# Strip HTML and get first 155 characters
|
||||
from html import unescape
|
||||
import re
|
||||
text = re.sub('<[^<]+?>', '', content.content_html)
|
||||
text = unescape(text).strip()
|
||||
excerpt = text[:155] + '...' if len(text) > 155 else text
|
||||
|
||||
content_data = {
|
||||
'content_id': content.id,
|
||||
'task_id': task_id,
|
||||
'title': content.title,
|
||||
'content_html': content.content_html, # ✓ REQUIRED
|
||||
'excerpt': excerpt, # Generated from content
|
||||
'status': 'publish',
|
||||
'author_email': content.author.email if content.author else None,
|
||||
'author_name': content.author.get_full_name() if content.author else None,
|
||||
'published_at': content.published_at.isoformat() if content.published_at else None,
|
||||
|
||||
# SEO Fields (correct field names)
|
||||
'seo_title': content.meta_title or '',
|
||||
'seo_description': content.meta_description or '',
|
||||
'primary_keyword': content.primary_keyword or '',
|
||||
'secondary_keywords': content.secondary_keywords or [],
|
||||
|
||||
# Media
|
||||
'featured_image_url': content.featured_image.url if content.featured_image else None,
|
||||
|
||||
# Relationships (need to verify these exist on Content model)
|
||||
'cluster_id': content.cluster.id if content.cluster else None,
|
||||
'cluster_name': content.cluster.name if content.cluster else None,
|
||||
'sector_id': content.sector.id if content.sector else None,
|
||||
'sector_name': content.sector.name if content.sector else None,
|
||||
|
||||
# Content classification
|
||||
'content_type': content.content_type,
|
||||
'content_structure': content.content_structure,
|
||||
|
||||
# Categories/Tags (if they exist as relations)
|
||||
'categories': [], # TODO: Add if Content model has category relation
|
||||
'tags': [], # TODO: Add if Content model has tag relation
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Verify Content Model Relations
|
||||
|
||||
**Action Required:** Check if `Content` model has these fields/relations:
|
||||
|
||||
```python
|
||||
# Need to verify in Content model:
|
||||
- author (ForeignKey to User)
|
||||
- published_at (DateTimeField)
|
||||
- featured_image (FileField/ImageField)
|
||||
- cluster (ForeignKey) ✓ CONFIRMED
|
||||
- sector (ForeignKey) ✓ CONFIRMED from SiteSectorBaseModel
|
||||
- categories (ManyToMany?)
|
||||
- tags (ManyToMany?)
|
||||
```
|
||||
|
||||
**File to Check:** `e:\Projects\...\igny8\backend\igny8_core\business\content\models.py` (continue reading from line 200)
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: WordPress Plugin - Remove API Callback
|
||||
|
||||
**File:** `c:\Users\Hp\vscode\igny8-wp-integration\includes\class-igny8-rest-api.php`
|
||||
|
||||
**Lines to REMOVE:** 507-545
|
||||
|
||||
**Replacement Logic:**
|
||||
|
||||
```php
|
||||
public function publish_content_to_wordpress($request) {
|
||||
// 1. Check connection
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return $this->build_unified_response(false, null, 'Connection disabled', 'connection_disabled', null, 403);
|
||||
}
|
||||
|
||||
// 2. Get ALL data from POST body (IGNY8 sends everything)
|
||||
$content_data = $request->get_json_params();
|
||||
|
||||
// 3. Validate required fields
|
||||
if (empty($content_data['content_id'])) {
|
||||
return $this->build_unified_response(false, null, 'Missing content_id', 'missing_content_id', null, 400);
|
||||
}
|
||||
|
||||
if (empty($content_data['title'])) {
|
||||
return $this->build_unified_response(false, null, 'Missing title', 'missing_title', null, 400);
|
||||
}
|
||||
|
||||
if (empty($content_data['content_html'])) {
|
||||
return $this->build_unified_response(false, null, 'Missing content_html', 'missing_content_html', null, 400);
|
||||
}
|
||||
|
||||
// 4. Check if content already exists
|
||||
$existing_posts = get_posts(array(
|
||||
'meta_key' => '_igny8_content_id',
|
||||
'meta_value' => $content_data['content_id'],
|
||||
'post_type' => 'any',
|
||||
'posts_per_page' => 1
|
||||
));
|
||||
|
||||
if (!empty($existing_posts)) {
|
||||
return $this->build_unified_response(
|
||||
false,
|
||||
array('post_id' => $existing_posts[0]->ID),
|
||||
'Content already exists',
|
||||
'content_exists',
|
||||
null,
|
||||
409
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Create WordPress post (function expects content_data with content_html)
|
||||
$post_id = igny8_create_wordpress_post_from_task($content_data);
|
||||
|
||||
if (is_wp_error($post_id)) {
|
||||
return $this->build_unified_response(
|
||||
false,
|
||||
null,
|
||||
'Failed to create post: ' . $post_id->get_error_message(),
|
||||
'post_creation_failed',
|
||||
null,
|
||||
500
|
||||
);
|
||||
}
|
||||
|
||||
// 6. Return success
|
||||
return $this->build_unified_response(
|
||||
true,
|
||||
array(
|
||||
'post_id' => $post_id,
|
||||
'post_url' => get_permalink($post_id),
|
||||
'post_status' => get_post_status($post_id),
|
||||
'content_id' => $content_data['content_id'],
|
||||
'task_id' => $content_data['task_id'] ?? null
|
||||
),
|
||||
'Content successfully published to WordPress',
|
||||
null,
|
||||
null,
|
||||
201
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Add Logging for Debugging
|
||||
|
||||
**File:** `e:\Projects\...\igny8\backend\igny8_core\tasks\wordpress_publishing.py`
|
||||
|
||||
**Add after line 75:**
|
||||
|
||||
```python
|
||||
# Log the payload being sent
|
||||
logger.info(f"Publishing content {content_id} to WordPress")
|
||||
logger.debug(f"Payload: {json.dumps(content_data, indent=2)}")
|
||||
|
||||
response = requests.post(
|
||||
wordpress_url,
|
||||
json=content_data,
|
||||
headers=headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
# Log response
|
||||
logger.info(f"WordPress response status: {response.status_code}")
|
||||
logger.debug(f"WordPress response body: {response.text}")
|
||||
```
|
||||
|
||||
**File:** `c:\Users\Hp\vscode\igny8-wp-integration\includes\class-igny8-rest-api.php`
|
||||
|
||||
**Add at start of publish_content_to_wordpress():**
|
||||
|
||||
```php
|
||||
// Debug log incoming data
|
||||
error_log('IGNY8 Publish Request - Content ID: ' . ($content_data['content_id'] ?? 'MISSING'));
|
||||
error_log('IGNY8 Publish Request - Has title: ' . (empty($content_data['title']) ? 'NO' : 'YES'));
|
||||
error_log('IGNY8 Publish Request - Has content_html: ' . (empty($content_data['content_html']) ? 'NO' : 'YES'));
|
||||
error_log('IGNY8 Publish Request - Content HTML length: ' . strlen($content_data['content_html'] ?? ''));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 STEP-BY-STEP IMPLEMENTATION CHECKLIST
|
||||
|
||||
### ✅ Step 1: Fix IGNY8 Backend Payload (HIGHEST PRIORITY)
|
||||
|
||||
**File:** `igny8_core/tasks/wordpress_publishing.py`
|
||||
|
||||
- [ ] Line 53-75: Update field names to match `Content` model
|
||||
- [ ] Change `seo_title` → `meta_title`
|
||||
- [ ] Change `seo_description` → `meta_description`
|
||||
- [ ] Remove `brief` (doesn't exist on Content model)
|
||||
- [ ] Generate `excerpt` from `content_html`
|
||||
- [ ] Change `focus_keywords` → `secondary_keywords`
|
||||
- [ ] Add `primary_keyword` field
|
||||
- [ ] Verify `author`, `published_at`, `featured_image` fields exist
|
||||
- [ ] Add `content_type` and `content_structure` fields
|
||||
- [ ] Add `cluster_id` and `sector_id` properly
|
||||
|
||||
- [ ] Add comprehensive logging
|
||||
- [ ] Log payload before sending
|
||||
- [ ] Log HTTP response status and body
|
||||
- [ ] Log success/failure with details
|
||||
|
||||
**Expected Result:** Payload contains actual `content_html` with full HTML content
|
||||
|
||||
---
|
||||
|
||||
### ✅ Step 2: Fix WordPress Plugin REST Endpoint
|
||||
|
||||
**File:** `includes/class-igny8-rest-api.php`
|
||||
|
||||
- [ ] Line 507-545: REMOVE API callback to IGNY8
|
||||
- [ ] Delete `$api->get("/writer/tasks/{$task_id}/")`
|
||||
- [ ] Delete `$api->get("/content/{$content_id}/")`
|
||||
- [ ] Use `$request->get_json_params()` directly
|
||||
|
||||
- [ ] Add proper validation
|
||||
- [ ] Validate `content_id` exists
|
||||
- [ ] Validate `title` exists
|
||||
- [ ] Validate `content_html` exists and is not empty
|
||||
- [ ] Validate `content_html` length > 100 characters
|
||||
|
||||
- [ ] Add comprehensive logging
|
||||
- [ ] Log received content_id
|
||||
- [ ] Log if title present
|
||||
- [ ] Log if content_html present
|
||||
- [ ] Log content_html length
|
||||
|
||||
**Expected Result:** WordPress uses data from POST body, not API callback
|
||||
|
||||
---
|
||||
|
||||
### ✅ Step 3: Verify WordPress Post Creation Function
|
||||
|
||||
**File:** `sync/igny8-to-wp.php`
|
||||
|
||||
- [ ] Function `igny8_create_wordpress_post_from_task()` Line 69-285
|
||||
- [ ] Verify it expects `content_html` field (Line 88)
|
||||
- [ ] Verify it uses `wp_kses_post($content_html)` (Line 101)
|
||||
- [ ] Verify `post_content` is set correctly (Line 101)
|
||||
- [ ] Verify SEO meta fields mapped correctly
|
||||
- [ ] `meta_title` → multiple SEO plugins
|
||||
- [ ] `meta_description` → multiple SEO plugins
|
||||
- [ ] Verify all IGNY8 meta fields stored
|
||||
- [ ] `_igny8_task_id`
|
||||
- [ ] `_igny8_content_id`
|
||||
- [ ] `_igny8_cluster_id`
|
||||
- [ ] `_igny8_sector_id`
|
||||
- [ ] `_igny8_content_type`
|
||||
- [ ] `_igny8_content_structure`
|
||||
|
||||
**Expected Result:** Full content published with all metadata
|
||||
|
||||
---
|
||||
|
||||
### ✅ Step 4: Test End-to-End Flow
|
||||
|
||||
**Manual Test Steps:**
|
||||
|
||||
1. **IGNY8 Backend - Trigger Publish:**
|
||||
```python
|
||||
# In Django shell or admin
|
||||
from igny8_core.models import Content, SiteIntegration
|
||||
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
|
||||
|
||||
content = Content.objects.first() # Get a content with content_html
|
||||
site_integration = SiteIntegration.objects.first()
|
||||
|
||||
# Check content has data
|
||||
print(f"Title: {content.title}")
|
||||
print(f"Content HTML length: {len(content.content_html)}")
|
||||
print(f"Meta Title: {content.meta_title}")
|
||||
|
||||
# Trigger publish
|
||||
result = publish_content_to_wordpress(content.id, site_integration.id)
|
||||
print(result)
|
||||
```
|
||||
|
||||
2. **Check Logs:**
|
||||
- IGNY8 backend logs: Should show full payload with `content_html`
|
||||
- WordPress logs: Should show received data with `content_html`
|
||||
|
||||
3. **Verify WordPress Post:**
|
||||
```php
|
||||
// In WordPress admin or WP-CLI
|
||||
$post = get_post($post_id);
|
||||
echo "Title: " . $post->post_title . "\n";
|
||||
echo "Content length: " . strlen($post->post_content) . "\n";
|
||||
echo "Content preview: " . substr($post->post_content, 0, 200) . "\n";
|
||||
|
||||
// Check meta
|
||||
echo "Task ID: " . get_post_meta($post_id, '_igny8_task_id', true) . "\n";
|
||||
echo "Content ID: " . get_post_meta($post_id, '_igny8_content_id', true) . "\n";
|
||||
echo "Cluster ID: " . get_post_meta($post_id, '_igny8_cluster_id', true) . "\n";
|
||||
```
|
||||
|
||||
**Expected Result:** Post has full HTML content, all metadata present
|
||||
|
||||
---
|
||||
|
||||
## 🔍 DEBUGGING CHECKLIST
|
||||
|
||||
If content still not publishing:
|
||||
|
||||
### Debug Point 1: IGNY8 Payload
|
||||
```python
|
||||
# Add to wordpress_publishing.py after line 75
|
||||
print("=" * 50)
|
||||
print("CONTENT DATA BEING SENT:")
|
||||
print(f"content_id: {content_data.get('content_id')}")
|
||||
print(f"title: {content_data.get('title')}")
|
||||
print(f"content_html length: {len(content_data.get('content_html', ''))}")
|
||||
print(f"content_html preview: {content_data.get('content_html', '')[:200]}")
|
||||
print("=" * 50)
|
||||
```
|
||||
|
||||
### Debug Point 2: HTTP Request
|
||||
```python
|
||||
# Add after response = requests.post(...)
|
||||
print(f"HTTP Status: {response.status_code}")
|
||||
print(f"Response: {response.text[:500]}")
|
||||
```
|
||||
|
||||
### Debug Point 3: WordPress Reception
|
||||
```php
|
||||
// Add to publish_content_to_wordpress() at line 1
|
||||
$raw_body = $request->get_body();
|
||||
error_log("IGNY8 Raw Request Body: " . substr($raw_body, 0, 500));
|
||||
|
||||
$content_data = $request->get_json_params();
|
||||
error_log("IGNY8 Parsed Data Keys: " . implode(', ', array_keys($content_data)));
|
||||
error_log("IGNY8 Content HTML Length: " . strlen($content_data['content_html'] ?? ''));
|
||||
```
|
||||
|
||||
### Debug Point 4: Post Creation
|
||||
```php
|
||||
// Add to igny8_create_wordpress_post_from_task() after line 100
|
||||
error_log("Creating post with title: " . $post_data['post_title']);
|
||||
error_log("Post content length: " . strlen($post_data['post_content']));
|
||||
error_log("Post content preview: " . substr($post_data['post_content'], 0, 200));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 COMMON PITFALLS TO AVOID
|
||||
|
||||
1. **DO NOT fetch data from `/writer/tasks/` endpoint** - Tasks model ≠ Content model
|
||||
2. **DO NOT assume field names** - Verify against actual model definition
|
||||
3. **DO NOT skip validation** - Empty `content_html` will create empty posts
|
||||
4. **DO NOT ignore errors** - Log everything for debugging
|
||||
5. **DO NOT mix up Content vs Tasks** - They are separate models with different fields
|
||||
|
||||
---
|
||||
|
||||
## 📊 DATA FLOW VALIDATION
|
||||
|
||||
### Correct Flow:
|
||||
```
|
||||
Content Model (DB)
|
||||
↓ ORM fetch
|
||||
content.content_html = "<p>Full HTML content...</p>"
|
||||
↓ Prepare payload
|
||||
content_data['content_html'] = "<p>Full HTML content...</p>"
|
||||
↓ JSON serialize
|
||||
{"content_html": "<p>Full HTML content...</p>"}
|
||||
↓ HTTP POST
|
||||
WordPress receives: content_html in POST body
|
||||
↓ Parse JSON
|
||||
$content_data['content_html'] = "<p>Full HTML content...</p>"
|
||||
↓ Create post
|
||||
wp_insert_post(['post_content' => wp_kses_post($content_html)])
|
||||
↓ Database insert
|
||||
wp_posts.post_content = "<p>Full HTML content...</p>"
|
||||
```
|
||||
|
||||
### Current Broken Flow:
|
||||
```
|
||||
Content Model (DB)
|
||||
↓ ORM fetch
|
||||
content.content_html = "<p>Full HTML content...</p>"
|
||||
↓ Prepare payload
|
||||
content_data['content_html'] = "<p>Full HTML content...</p>"
|
||||
↓ JSON serialize & HTTP POST
|
||||
WordPress receives: content_html in POST body
|
||||
↓ IGNORES POST BODY!
|
||||
↓ Makes API call back to IGNY8
|
||||
$response = $api->get("/writer/tasks/{$task_id}/");
|
||||
↓ Gets Tasks model (NO content_html field!)
|
||||
$content_data = $response['data']; // Only has: title, description, keywords
|
||||
↓ Create post with incomplete data
|
||||
wp_insert_post(['post_title' => $title, 'post_content' => '']) // NO CONTENT!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ SUCCESS CRITERIA
|
||||
|
||||
After implementation, verify:
|
||||
|
||||
1. **IGNY8 Backend:**
|
||||
- [ ] Payload contains `content_html` field
|
||||
- [ ] `content_html` has actual HTML content (length > 100)
|
||||
- [ ] All SEO fields present (`meta_title`, `meta_description`, `primary_keyword`)
|
||||
- [ ] Relationships present (`cluster_id`, `sector_id`)
|
||||
- [ ] Logs show full payload being sent
|
||||
|
||||
2. **WordPress Plugin:**
|
||||
- [ ] Receives `content_html` in POST body
|
||||
- [ ] Does NOT make API callback to IGNY8
|
||||
- [ ] Creates post with `post_content` = `content_html`
|
||||
- [ ] Stores all meta fields correctly
|
||||
- [ ] Returns success response with post_id and post_url
|
||||
|
||||
3. **WordPress Post:**
|
||||
- [ ] Has title
|
||||
- [ ] Has full HTML content (not empty)
|
||||
- [ ] Has excerpt
|
||||
- [ ] Has SEO meta (if SEO plugin active)
|
||||
- [ ] Has IGNY8 meta fields (content_id, task_id, cluster_id, etc.)
|
||||
- [ ] Has correct post_status
|
||||
- [ ] Has correct post_type
|
||||
|
||||
4. **End-to-End:**
|
||||
- [ ] IGNY8 → WordPress: Content publishes successfully
|
||||
- [ ] WordPress post viewable and formatted correctly
|
||||
- [ ] IGNY8 backend updated with wordpress_post_id and wordpress_post_url
|
||||
- [ ] No errors in logs
|
||||
|
||||
---
|
||||
|
||||
## 📝 IMPLEMENTATION ORDER (Priority)
|
||||
|
||||
### Day 1: Critical Fixes
|
||||
1. Fix IGNY8 backend payload field names (1-2 hours)
|
||||
2. Add logging to IGNY8 backend (30 minutes)
|
||||
3. Fix WordPress plugin - remove API callback (1 hour)
|
||||
4. Add logging to WordPress plugin (30 minutes)
|
||||
5. Test with one piece of content (1 hour)
|
||||
|
||||
### Day 2: Verification & Polish
|
||||
6. Verify all Content model fields are sent (2 hours)
|
||||
7. Test with 10 different content pieces (1 hour)
|
||||
8. Fix any edge cases discovered (2 hours)
|
||||
9. Document the changes (1 hour)
|
||||
|
||||
### Day 3: Additional Features (From Plan)
|
||||
10. Implement atomic transactions (Phase 1.1 from plan)
|
||||
11. Add pre-flight validation (Phase 1.2 from plan)
|
||||
12. Implement duplicate prevention (Phase 1.3 from plan)
|
||||
13. Add post-publish verification (Phase 1.4 from plan)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 FINAL VALIDATION TEST
|
||||
|
||||
Run this test after all fixes:
|
||||
|
||||
```python
|
||||
# IGNY8 Backend Test
|
||||
from igny8_core.models import Content
|
||||
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
|
||||
|
||||
# Create test content
|
||||
content = Content.objects.create(
|
||||
site_id=1,
|
||||
sector_id=1,
|
||||
cluster_id=1,
|
||||
title="Test Post - " + str(timezone.now()),
|
||||
content_html="<h1>Test Header</h1><p>This is test content with <strong>bold</strong> text.</p>",
|
||||
meta_title="Test SEO Title",
|
||||
meta_description="Test SEO description for testing",
|
||||
content_type='post',
|
||||
content_structure='article',
|
||||
)
|
||||
|
||||
# Publish
|
||||
result = publish_content_to_wordpress.delay(content.id, 1)
|
||||
print(f"Result: {result.get()}")
|
||||
|
||||
# Check WordPress
|
||||
# Go to WordPress admin → Posts → Should see new post with full HTML content
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**This plan is based on ACTUAL codebase analysis, not assumptions.**
|
||||
**Follow this step-by-step to fix the publishing issue.**
|
||||
Reference in New Issue
Block a user