From 2a57509a1e7c1d5ecf08d5304b8ac262d8c03bf3 Mon Sep 17 00:00:00 2001 From: alorig <220087330+alorig@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:04:18 +0500 Subject: [PATCH] 123 --- docs/WORDPRESS_INTEGRATION_REFERENCE.md | 1367 +++++++++++++++++++++++ 1 file changed, 1367 insertions(+) create mode 100644 docs/WORDPRESS_INTEGRATION_REFERENCE.md diff --git a/docs/WORDPRESS_INTEGRATION_REFERENCE.md b/docs/WORDPRESS_INTEGRATION_REFERENCE.md new file mode 100644 index 00000000..30f37a9f --- /dev/null +++ b/docs/WORDPRESS_INTEGRATION_REFERENCE.md @@ -0,0 +1,1367 @@ +# IGNY8 WordPress Integration Reference + +**Version:** 1.0.0 +**Last Updated:** November 29, 2025 +**Purpose:** Complete reference for the bidirectional WordPress ↔ IGNY8 integration, covering architecture, data flows, sync mechanisms, and troubleshooting. + +--- + +## Table of Contents + +1. [Integration Overview](#integration-overview) +2. [Architecture](#architecture) +3. [WordPress Plugin Structure](#wordpress-plugin-structure) +4. [IGNY8 Backend Integration Points](#igny8-backend-integration-points) +5. [Authentication & Security](#authentication--security) +6. [Bidirectional Sync: Data Elements](#bidirectional-sync-data-elements) +7. [Publishing Flow (IGNY8 → WordPress)](#publishing-flow-igny8--wordpress) +8. [Sync Flow (WordPress → IGNY8)](#sync-flow-wordpress--igny8) +9. [REST API Endpoints](#rest-api-endpoints) +10. [Data Models & Meta Fields](#data-models--meta-fields) +11. [Status Mapping](#status-mapping) +12. [Troubleshooting Guide](#troubleshooting-guide) +13. [Developer Quick Reference](#developer-quick-reference) + +--- + +## Integration Overview + +The IGNY8 WordPress Bridge enables **bidirectional synchronization** between IGNY8 (SaaS platform) and WordPress sites, allowing content created in IGNY8 to be published to WordPress and WordPress content/metadata to be synced back to IGNY8. + +### Core Capabilities + +| Feature | Direction | Status | +|---------|-----------|--------| +| **Content Publishing** | IGNY8 → WP | ✅ Live | +| **Post Status Sync** | WP → IGNY8 | ✅ Live | +| **Taxonomy Sync** | WP ↔ IGNY8 | ✅ Live | +| **SEO Metadata** | IGNY8 → WP | ✅ Live | +| **Featured Images** | IGNY8 → WP | ✅ Live | +| **Site Discovery** | WP → IGNY8 | ✅ Live | +| **Keyword Tracking** | IGNY8 → WP | ✅ Live | +| **Internal Linking** | IGNY8 → WP | 🔄 Pending | +| **Content Optimization** | WP ↔ IGNY8 | 🔄 Pending | + +### Integration Model + +``` +┌─────────────────────────────────────────────────────────────┐ +│ IGNY8 SaaS Platform │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Django Backend (api.igny8.com) │ │ +│ │ - Publisher Service │ │ +│ │ - WordPress Adapter │ │ +│ │ - Content Models │ │ +│ │ - Integration Models │ │ +│ │ - Celery Background Tasks │ │ +│ └──────────────────────────────────────────────────────┘ │ +└───────────────────────┬─────────────────────────────────────┘ + │ + │ REST API (JSON) + │ - JWT Auth + API Key + │ - Unified Response Format + │ +┌───────────────────────▼─────────────────────────────────────┐ +│ WordPress Site (client.com) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ IGNY8 Bridge Plugin │ │ +│ │ - API Client (class-igny8-api.php) │ │ +│ │ - REST Endpoints (/wp-json/igny8/v1/) │ │ +│ │ - Sync Handlers (sync/*.php) │ │ +│ │ - Post Meta Storage (_igny8_* fields) │ │ +│ │ - Custom Taxonomies (igny8_clusters, igny8_sectors)│ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Architecture + +### High-Level Data Flow + +``` +IGNY8 → WordPress Publishing Flow: +┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Frontend │ -> │ Publisher │ -> │ Publisher │ -> │ WordPress │ -> │ WordPress │ +│ (Review.tsx)│ │ ViewSet │ │ Service │ │ Adapter │ │ REST API │ +│ Publish │ │ /publish/ │ │publish_content()│ │ publish() │ │ /igny8/v1/ │ +│ Button │ │ │ │ │ │ │ │publish-content│ +└─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘ └──────────────┘ + │ + ▼ + ┌──────────────┐ + │ WordPress │ + │ Post │ + │ Created │ + └──────────────┘ + +WordPress → IGNY8 Sync Flow: +┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ +│ WordPress │ -> │ WP Hook │ -> │ Igny8API │ -> │ IGNY8 API │ +│ save_post │ │ post-sync.php│ │ PUT/POST │ │ /writer/tasks│ +│ Event │ │ │ │ │ │ │ +└─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘ + │ + ▼ + ┌──────────────┐ + │ Content │ + │ Updated │ + └──────────────┘ +``` + +### Component Interaction + +**IGNY8 Backend Components:** +- **PublisherViewSet** (`igny8_core/modules/publisher/views.py`) - REST API endpoint +- **PublisherService** (`igny8_core/business/publishing/services/publisher_service.py`) - Business logic +- **WordPressAdapter** (`igny8_core/business/publishing/services/adapters/wordpress_adapter.py`) - WP integration +- **Celery Tasks** (`igny8_core/tasks/wordpress_publishing.py`) - Async background publishing +- **Content Model** (`igny8_core/business/content/models.py`) - Content data +- **SiteIntegration Model** (`igny8_core/business/integration/models.py`) - WordPress connection config + +**WordPress Plugin Components:** +- **Igny8API** (`includes/class-igny8-api.php`) - API client for IGNY8 +- **Igny8RestAPI** (`includes/class-igny8-rest-api.php`) - REST endpoints for IGNY8 to call +- **Post Sync** (`sync/post-sync.php`) - WP → IGNY8 synchronization +- **IGNY8 to WP** (`sync/igny8-to-wp.php`) - IGNY8 → WP content creation +- **Hooks** (`sync/hooks.php`) - WordPress action/filter hooks +- **Admin** (`admin/settings.php`) - Plugin settings UI + +--- + +## WordPress Plugin Structure + +``` +igny8-wp-integration/ +├── igny8-bridge.php # Main plugin file +│ - Plugin bootstrap +│ - Registers all classes +│ - Defines constants +│ - Version: 1.0.0 +│ +├── includes/ # Core functionality +│ ├── class-igny8-api.php # API Client +│ │ - login() - JWT authentication +│ │ - refresh_token() - Token refresh +│ │ - get/post/put/delete() - HTTP methods +│ │ - parse_response() - Unified response handling +│ │ +│ ├── class-igny8-rest-api.php # WordPress REST Endpoints +│ │ Endpoints: +│ │ - GET /wp-json/igny8/v1/post-by-content-id/{id} +│ │ - GET /wp-json/igny8/v1/post-by-task-id/{id} +│ │ - GET /wp-json/igny8/v1/post-status/{content_id} +│ │ - GET /wp-json/igny8/v1/site-metadata/ +│ │ - GET /wp-json/igny8/v1/status +│ │ - POST /wp-json/igny8/v1/publish-content/ +│ │ +│ ├── class-igny8-site.php # Site data collection +│ ├── class-igny8-webhooks.php # Webhook handlers +│ ├── class-igny8-link-queue.php # Link processing +│ └── functions.php # Helper functions +│ +├── sync/ # Synchronization +│ ├── hooks.php # WP hooks registration +│ │ - save_post +│ │ - publish_post +│ │ - transition_post_status +│ │ +│ ├── post-sync.php # WP → IGNY8 sync +│ │ - igny8_sync_post_status_to_igny8() +│ │ - igny8_update_keywords_on_post_publish() +│ │ - igny8_map_wp_status_to_igny8() +│ │ +│ ├── taxonomy-sync.php # Taxonomy sync +│ │ - Clusters (igny8_clusters) +│ │ - Sectors (igny8_sectors) +│ │ - Categories/Tags mapping +│ │ +│ └── igny8-to-wp.php # IGNY8 → WP content +│ - igny8_create_wordpress_post_from_task() +│ - igny8_import_seo_metadata() +│ - igny8_import_featured_image() +│ - igny8_import_taxonomies() +│ +├── admin/ # Admin interface +│ ├── settings.php # Settings page UI +│ │ - API credentials +│ │ - Connection status +│ │ - Post type toggles +│ │ +│ ├── class-admin.php # Settings controller +│ ├── class-admin-columns.php # Custom post columns +│ └── class-post-meta-boxes.php # Post editor meta boxes +│ +├── data/ # Data collection +│ ├── site-collection.php # Full site scan +│ ├── semantic-mapping.php # Semantic metadata +│ ├── link-graph.php # Internal link graph +│ └── woocommerce.php # WooCommerce integration +│ +└── docs/ # Documentation + ├── WORDPRESS-PLUGIN-INTEGRATION.md + ├── AUTHENTICATION-AUDIT.md + └── SYNC-DATA-FLOW-DIAGRAM.md +``` + +### Key Plugin Classes + +**Igny8API** +- **Purpose:** Communication with IGNY8 REST API +- **Location:** `includes/class-igny8-api.php` +- **Key Methods:** + - `login($email, $password)` - Authenticate and get JWT tokens + - `refresh_token()` - Refresh expired access token + - `get($endpoint)` - GET request with auto-retry on 401 + - `post($endpoint, $data)` - POST request + - `put($endpoint, $data)` - PUT request (for updates) + - `parse_response($response)` - Parse unified response format + +**Igny8RestAPI** +- **Purpose:** Provide REST endpoints for IGNY8 to call +- **Location:** `includes/class-igny8-rest-api.php` +- **Authentication:** API key via `X-IGNY8-API-KEY` header or Bearer token +- **Response Format:** Unified `{success, data, message, request_id}` + +--- + +## IGNY8 Backend Integration Points + +### Database Models + +**Content Model** (`igny8_core/business/content/models.py`) +```python +class Content(SiteSectorBaseModel): + """Main content storage in IGNY8""" + + # Core fields + title = CharField(max_length=255) + content_html = TextField() # Final HTML content + word_count = IntegerField() + + # SEO fields + meta_title = CharField(max_length=255) + meta_description = TextField() + primary_keyword = CharField(max_length=255) + secondary_keywords = JSONField(default=list) + + # Content classification + cluster = ForeignKey('planner.Clusters') # Required + content_type = CharField(choices=['post', 'page', 'product', 'taxonomy']) + content_structure = CharField(choices=['article', 'guide', 'comparison', ...]) + + # WordPress sync fields + external_id = CharField() # WordPress post ID + external_url = URLField() # WordPress post URL + external_type = CharField() # WordPress post type + sync_status = CharField() # Sync status + + # Status + status = CharField(choices=['draft', 'review', 'published']) + source = CharField(choices=['igny8', 'wordpress']) +``` + +**SiteIntegration Model** (`igny8_core/business/integration/models.py`) +```python +class SiteIntegration(AccountBaseModel): + """WordPress connection configuration""" + + site = ForeignKey('Site') + platform = CharField(choices=['wordpress', 'shopify', 'custom']) + + # Configuration + config_json = JSONField() # {site_url, endpoints, ...} + credentials_json = JSONField() # {api_key, username, password} + + # Sync tracking + is_active = BooleanField(default=True) + sync_enabled = BooleanField(default=False) + last_sync_at = DateTimeField() + sync_status = CharField(choices=['success', 'failed', 'pending', 'syncing']) +``` + +**PublishingRecord Model** (`igny8_core/business/publishing/models.py`) +```python +class PublishingRecord(SiteSectorBaseModel): + """Track content publishing to destinations""" + + content = ForeignKey('writer.Content') + destination = CharField() # 'wordpress', 'sites', 'shopify' + destination_id = CharField() # External platform ID + destination_url = URLField() # Published URL + + status = CharField(choices=['pending', 'publishing', 'published', 'failed']) + published_at = DateTimeField() + error_message = TextField() + metadata = JSONField() +``` + +### Backend Services + +**PublisherService** (`igny8_core/business/publishing/services/publisher_service.py`) +- **Purpose:** Main publishing orchestrator +- **Key Method:** `publish_content(content_id, destinations, account)` +- **Flow:** + 1. Lookup Content by ID + 2. For each destination → call `_publish_to_destination()` + 3. Create PublishingRecord + 4. Get adapter (WordPressAdapter) + 5. Get SiteIntegration config + 6. Call adapter.publish() + 7. Update PublishingRecord status + +**WordPressAdapter** (`igny8_core/business/publishing/services/adapters/wordpress_adapter.py`) +- **Purpose:** WordPress-specific publishing logic +- **Authentication:** Auto-detects API key vs username/password +- **Key Methods:** + - `publish(content, destination_config)` - Main publish method + - `_publish_via_api_key()` - Uses `/wp-json/igny8/v1/publish-content/` + - `_publish_via_username_password()` - Uses standard WP REST API +- **Payload:** + ```python + { + 'content_id': content.id, + 'title': content.title, + 'content_html': content.content_html, + 'content_type': content.content_type, + 'content_structure': content.content_structure, + 'meta_title': content.meta_title, + 'meta_description': content.meta_description, + 'status': content.status, + 'cluster_id': content.cluster.id, + 'sector_id': content.sector.id, + 'featured_image': {...}, + 'categories': [...], + 'tags': [...] + } + ``` + +**Celery Tasks** (`igny8_core/tasks/wordpress_publishing.py`) +- **Purpose:** Async background publishing +- **Task:** `publish_content_to_wordpress(content_id, site_integration_id)` +- **Features:** + - Retry logic (3 retries, exponential backoff) + - Duplicate check (prevents re-publishing) + - Updates Content.external_id and external_url + - Comprehensive logging + +--- + +## Authentication & Security + +### IGNY8 → WordPress Authentication + +**Method 1: API Key (Preferred)** +- **Storage:** `SiteIntegration.credentials_json['api_key']` +- **WordPress Storage:** `igny8_api_key` option (encrypted if available) +- **Header:** `X-IGNY8-API-KEY: {api_key}` or `Authorization: Bearer {api_key}` +- **Endpoint:** `/wp-json/igny8/v1/publish-content/` +- **Security:** API key validated in `Igny8RestAPI::check_permission()` + +**Method 2: Username/Password (Legacy)** +- **Storage:** `SiteIntegration.credentials_json['username']` and `password` +- **Flow:** Uses standard WordPress REST API authentication +- **Limitation:** Requires Application Password or JWT plugin + +### WordPress → IGNY8 Authentication + +**JWT Token Flow** +1. **Login:** `POST /api/v1/auth/login/` with `{email, password}` +2. **Response:** `{access, refresh}` tokens +3. **Storage:** `igny8_access_token` and `igny8_refresh_token` WordPress options +4. **Usage:** `Authorization: Bearer {access_token}` header +5. **Refresh:** On 401, call `POST /api/v1/auth/refresh/` with `{refresh}` +6. **Auto-retry:** `Igny8API` class automatically refreshes and retries on 401 + +**Credentials Required (WordPress Settings)** +- **Email:** `igny8_email` option +- **Password:** Stored temporarily for token refresh +- **API Key:** `igny8_api_key` option (for incoming requests from IGNY8) + +--- + +## Bidirectional Sync: Data Elements + +### IGNY8 → WordPress (Publishing) + +**Content Fields Synced:** + +| IGNY8 Field | WordPress Destination | Notes | +|-------------|----------------------|-------| +| `title` | `post_title` | Sanitized | +| `content_html` | `post_content` | Passed through `wp_kses_post()` | +| `meta_title` | `_yoast_wpseo_title`
`_seopress_titles_title`
`_aioseo_title`
`_igny8_meta_title` | Multi-SEO plugin support | +| `meta_description` | `_yoast_wpseo_metadesc`
`_seopress_titles_desc`
`_aioseo_description`
`_igny8_meta_description` | Multi-SEO plugin support | +| `status` | `post_status` | Mapped via `igny8_map_igny8_status_to_wp()` | +| `content_type` | `post_type` | `post`, `page`, `product` | +| `content_structure` | `_igny8_content_structure` | Meta field for tracking | +| `cluster.id` | `_igny8_cluster_id`
`igny8_clusters` taxonomy | Both meta and taxonomy term | +| `sector.id` | `_igny8_sector_id`
`igny8_sectors` taxonomy | Both meta and taxonomy term | +| `task_id` | `_igny8_task_id` | Task reference | +| `id` (Content) | `_igny8_content_id` | Content reference | +| `primary_keyword` | `_igny8_primary_keyword` | SEO keyword | +| `secondary_keywords` | `_igny8_secondary_keywords` | JSON array | +| `featured_image.url` | Featured Image (attachment) | Downloaded and attached | +| `gallery_images[]` | `_igny8_gallery_images` | Image attachments | +| `categories[]` | `category` taxonomy | Standard WP categories | +| `tags[]` | `post_tag` taxonomy | Standard WP tags | + +**Post Meta Fields Created:** + +```php +// IGNY8 identification +_igny8_task_id // Task ID from IGNY8 +_igny8_content_id // Content ID from IGNY8 +_igny8_cluster_id // Cluster ID +_igny8_sector_id // Sector ID +_igny8_source // 'igny8' or 'wordpress' + +// Content metadata +_igny8_content_type // 'post', 'page', 'product' +_igny8_content_structure // 'article', 'guide', etc. + +// SEO fields (generic) +_igny8_meta_title // SEO title +_igny8_meta_description // SEO description +_igny8_primary_keyword // Primary keyword +_igny8_secondary_keywords // JSON array + +// Sync tracking +_igny8_wordpress_status // WordPress post_status +_igny8_last_synced // Last sync timestamp +_igny8_brief_cached_at // Brief cache timestamp +_igny8_task_brief // Cached task brief +``` + +### WordPress → IGNY8 (Status Sync) + +**Fields Synced on Post Save:** + +| WordPress Field | IGNY8 Endpoint | IGNY8 Field | Trigger | +|----------------|----------------|-------------|---------| +| `post_status` | `PUT /writer/tasks/{id}/` | `status` (mapped)
`wordpress_status` (raw) | `save_post` hook | +| `ID` | `PUT /writer/tasks/{id}/` | `assigned_post_id` | `save_post` hook | +| `permalink` | `PUT /writer/tasks/{id}/` | `post_url` | `save_post` hook | +| `post_type` | `PUT /writer/tasks/{id}/` | `post_type` | `save_post` hook | +| Post updated | `PUT /writer/tasks/{id}/` | `synced_at` | `save_post` hook | + +**Sync Conditions:** +- Only syncs if post has `_igny8_task_id` meta (IGNY8-managed posts) +- Skips autosaves and revisions +- Only runs if connection is enabled (`igny8_is_connection_enabled()`) +- Implemented in `sync/post-sync.php` + +### Site Metadata Discovery (WordPress → IGNY8) + +**Endpoint:** `GET /wp-json/igny8/v1/site-metadata/` + +**Data Collected:** + +```json +{ + "success": true, + "data": { + "site_info": { + "name": "Site Name", + "url": "https://example.com", + "description": "Site description", + "language": "en_US", + "timezone": "America/New_York" + }, + "post_types": [ + { + "name": "post", + "label": "Posts", + "count": 150, + "supports": ["title", "editor", "thumbnail", "excerpt"] + }, + { + "name": "page", + "label": "Pages", + "count": 25 + }, + { + "name": "product", + "label": "Products", + "count": 80 + } + ], + "taxonomies": [ + { + "name": "category", + "label": "Categories", + "count": 20, + "terms": [...] + }, + { + "name": "post_tag", + "label": "Tags", + "count": 150 + } + ], + "plugins": { + "woocommerce": true, + "yoast_seo": true, + "advanced_custom_fields": false + } + } +} +``` + +**Cache:** Transient `igny8_site_metadata_v1` (5 minutes) + +--- + +## Publishing Flow (IGNY8 → WordPress) + +### Complete Flow (Frontend to WordPress) + +**Step 1: User Clicks Publish Button** +- **File:** `frontend/src/pages/Writer/Review.tsx` +- **Method:** `handlePublishSingle()` +- **Action:** `POST /api/v1/publisher/publish/` +- **Payload:** `{content_id, destinations: ['wordpress']}` + +**Step 2: Publisher API Endpoint** +- **File:** `igny8_core/modules/publisher/views.py` +- **Class:** `PublisherViewSet` +- **Method:** `publish()` action +- **Logging:** `🚀 Publish request received` +- **Action:** Call `PublisherService.publish_content()` + +**Step 3: Publisher Service** +- **File:** `igny8_core/business/publishing/services/publisher_service.py` +- **Method:** `publish_content(content_id, destinations, account)` +- **Steps:** + 1. Load Content from database + 2. For each destination, call `_publish_to_destination()` + 3. Create `PublishingRecord` (status='pending') + 4. Get adapter for destination + 5. Lookup `SiteIntegration` for site + 6. Merge config: `{**config_json, **credentials_json, 'site_url': ...}` + 7. Call `adapter.publish(content, config)` + 8. Update `PublishingRecord` (status='published' or 'failed') + +**Step 4: WordPress Adapter** +- **File:** `igny8_core/business/publishing/services/adapters/wordpress_adapter.py` +- **Method:** `publish(content, destination_config)` +- **Auto-detection:** + ```python + if config.get('api_key'): + return _publish_via_api_key(...) + else: + return _publish_via_username_password(...) + ``` + +**Step 4a: API Key Method (Preferred)** +- **Method:** `_publish_via_api_key()` +- **Endpoint:** `POST {site_url}/wp-json/igny8/v1/publish-content/` +- **Headers:** `X-IGNY8-API-KEY: {api_key}` +- **Payload:** + ```json + { + "content_id": 123, + "title": "Post Title", + "content_html": "

Introduction

...

", + "content_type": "post", + "content_structure": "article", + "meta_title": "SEO Title", + "meta_description": "SEO Description", + "status": "published", + "cluster_id": 45, + "sector_id": 12, + "featured_image": {...}, + "categories": [...], + "tags": [...] + } + ``` +- **Response:** `{success, data: {post_id, url}}` + +**Step 5: WordPress REST Endpoint** +- **File:** `includes/class-igny8-rest-api.php` +- **Method:** `publish_content_to_wordpress()` +- **Endpoint:** `/wp-json/igny8/v1/publish-content/` +- **Permission:** `check_permission()` validates API key +- **Action:** Call `igny8_create_wordpress_post_from_task($content_data)` + +**Step 6: WordPress Post Creation** +- **File:** `sync/igny8-to-wp.php` +- **Function:** `igny8_create_wordpress_post_from_task()` +- **Steps:** + 1. Resolve post type from content_type + 2. Map author (via `igny8_map_content_author()`) + 3. Prepare post data + 4. `wp_insert_post()` - Create WordPress post + 5. Import SEO metadata (`igny8_import_seo_metadata()`) + 6. Import featured image (`igny8_import_featured_image()`) + 7. Import taxonomies (`igny8_import_taxonomies()`) + 8. Set cluster/sector taxonomy terms + 9. Set categories/tags + 10. Store IGNY8 meta fields + 11. Update IGNY8 task via API (`PUT /writer/tasks/{id}/`) +- **Return:** WordPress post ID or WP_Error + +**Step 7: Response Propagation** +- WordPress → WordPress Adapter → Publisher Service → Publisher ViewSet → Frontend +- **Frontend Success:** + ```javascript + console.log('[Review.handlePublishSingle] ✅ Publish successful:', response) + toast.success('Published to WordPress!') + ``` + +### Alternative: Celery Background Task + +**When:** If using async publishing (currently available but not default) + +**Trigger:** `publish_content_to_wordpress.delay(content_id, site_integration_id)` + +**File:** `igny8_core/tasks/wordpress_publishing.py` + +**Benefits:** +- Non-blocking (user doesn't wait) +- Retry logic (3 attempts with exponential backoff) +- Duplicate check (prevents re-publishing same content) + +**Flow:** +1. Load Content and SiteIntegration from DB +2. Check if already published (has external_id) +3. Prepare payload +4. POST to WordPress API +5. Update Content.external_id and external_url +6. Log success/failure + +--- + +## Sync Flow (WordPress → IGNY8) + +### Post Status Sync + +**Trigger:** WordPress `save_post`, `publish_post`, `transition_post_status` hooks + +**File:** `sync/post-sync.php` + +**Function:** `igny8_sync_post_status_to_igny8($post_id, $post, $update)` + +**Flow:** +1. Check if connection enabled +2. Skip autosaves and revisions +3. Check if IGNY8-managed (has `_igny8_task_id` meta) +4. Get `$task_id` from post meta +5. Get WordPress `post_status` +6. Map to IGNY8 status: `igny8_map_wp_status_to_igny8($post_status)` +7. Prepare update payload: + ```php + [ + 'status' => $task_status, // IGNY8 mapped status + 'assigned_post_id' => $post_id, + 'post_url' => get_permalink($post_id), + 'wordpress_status' => $post_status, // Raw WP status + 'synced_at' => current_time('mysql'), + 'content_id' => $content_id // If available + ] + ``` +8. `PUT /writer/tasks/{task_id}/` via Igny8API +9. Update `_igny8_wordpress_status` and `_igny8_last_synced` meta + +**Logged in WordPress:** +``` +IGNY8: Synced post 123 status (publish) to task 456 +``` + +### Keyword Status Update on Publish + +**Trigger:** Post published + +**Function:** `igny8_update_keywords_on_post_publish($post_id)` + +**Flow:** +1. Get task_id from post meta +2. Get task details: `GET /writer/tasks/{task_id}/` +3. Extract cluster_id and keyword_ids +4. Update keyword statuses: `PUT /planner/keywords/bulk-update/` +5. Mark keywords as "published" or "in_content" + +--- + +## REST API Endpoints + +### IGNY8 Backend Endpoints (Called by WordPress) + +| Method | Endpoint | Purpose | Request | Response | +|--------|----------|---------|---------|----------| +| POST | `/api/v1/auth/login/` | Get JWT tokens | `{email, password}` | `{access, refresh}` | +| POST | `/api/v1/auth/refresh/` | Refresh access token | `{refresh}` | `{access, refresh}` | +| GET | `/api/v1/writer/tasks/{id}/` | Get task details | - | Task object | +| PUT | `/api/v1/writer/tasks/{id}/` | Update task status | `{status, assigned_post_id, post_url, wordpress_status}` | Updated task | +| GET | `/api/v1/writer/tasks/{id}/brief/` | Get task brief | - | Brief content | +| PUT | `/api/v1/planner/keywords/bulk-update/` | Update keyword statuses | `{keyword_ids, status}` | Updated keywords | +| POST | `/api/v1/system/sites/{id}/import/` | Import site data | Site metadata payload | Import result | + +### WordPress REST Endpoints (Called by IGNY8) + +| Method | Endpoint | Purpose | Auth | Response | +|--------|----------|---------|------|----------| +| GET | `/wp-json/igny8/v1/post-by-content-id/{id}` | Get post by Content ID | API key | `{success, data: {post_id, title, status, url}}` | +| GET | `/wp-json/igny8/v1/post-by-task-id/{id}` | Get post by Task ID | API key | `{success, data: {post_id, title, status, url}}` | +| GET | `/wp-json/igny8/v1/post-status/{content_id}` | Get post status | API key | `{success, data: {wordpress_status, igny8_status}}` | +| GET | `/wp-json/igny8/v1/site-metadata/` | Get site structure | API key | `{success, data: {site_info, post_types, taxonomies}}` | +| GET | `/wp-json/igny8/v1/status` | Plugin health check | Public | `{success, data: {connected, api_key_present}}` | +| POST | `/wp-json/igny8/v1/publish-content/` | Publish content from IGNY8 | API key | `{success, data: {post_id, url}}` | + +**Authentication:** +- **API Key:** `X-IGNY8-API-KEY` header or `Authorization: Bearer {api_key}` +- **Validation:** `Igny8RestAPI::check_permission()` compares with `igny8_api_key` option + +--- + +## Data Models & Meta Fields + +### WordPress Post Meta Taxonomy + +**All IGNY8 meta fields use `_igny8_` prefix** + +```php +// IGNY8 References +_igny8_task_id // INT - IGNY8 Task ID +_igny8_content_id // INT - IGNY8 Content ID +_igny8_cluster_id // INT - IGNY8 Cluster ID +_igny8_sector_id // INT - IGNY8 Sector ID + +// Content Classification +_igny8_content_type // STRING - 'post', 'page', 'product', 'taxonomy' +_igny8_content_structure // STRING - 'article', 'guide', 'comparison', 'review', etc. +_igny8_source // STRING - 'igny8' or 'wordpress' + +// SEO Fields (Generic - not plugin-specific) +_igny8_meta_title // STRING - SEO meta title +_igny8_meta_description // TEXT - SEO meta description +_igny8_primary_keyword // STRING - Primary SEO keyword +_igny8_secondary_keywords // JSON - ["keyword1", "keyword2"] + +// Keywords +_igny8_keyword_ids // JSON - [1, 2, 3] + +// Sync Tracking +_igny8_wordpress_status // STRING - WordPress post_status at last sync +_igny8_last_synced // DATETIME - Last sync timestamp +_igny8_brief_cached_at // DATETIME - Brief cache timestamp +_igny8_task_brief // JSON - Cached task brief data + +// Images +_igny8_gallery_images // JSON - Gallery image IDs +``` + +### WordPress Custom Taxonomies + +**Created by Plugin:** + +```php +// IGNY8 Clusters +register_taxonomy('igny8_clusters', ['post', 'page'], [ + 'label' => 'IGNY8 Clusters', + 'hierarchical' => true, + 'show_in_rest' => true +]); + +// IGNY8 Sectors +register_taxonomy('igny8_sectors', ['post', 'page'], [ + 'label' => 'IGNY8 Sectors', + 'hierarchical' => true, + 'show_in_rest' => true +]); +``` + +**Term Meta:** +- `_igny8_cluster_id` - IGNY8 cluster ID +- `_igny8_sector_id` - IGNY8 sector ID + +### WordPress Options + +```php +// Authentication +igny8_email // User email for IGNY8 account +igny8_access_token // JWT access token +igny8_refresh_token // JWT refresh token +igny8_api_key // API key (encrypted if available) + +// Connection +igny8_connection_enabled // BOOL - Enable/disable sync +igny8_connection_status // STRING - 'connected', 'disconnected', 'error' + +// Settings +igny8_allowed_post_types // JSON - ['post', 'page', 'product'] +igny8_auto_import_enabled // BOOL - Auto-import content from IGNY8 +igny8_sync_mode // STRING - 'manual', 'auto' + +// Cache +igny8_site_metadata_v1 // Transient (5 min) - Cached site metadata +``` + +### IGNY8 Database Tables + +**Content Table** (`igny8_content`) +- Primary table for all content +- Fields: title, content_html, meta_title, meta_description, cluster_id, sector_id, status, external_id, external_url + +**Tasks Table** (`igny8_tasks`) +- Content generation tasks +- Fields: title, cluster_id, status, assigned_post_id, post_url + +**SiteIntegration Table** (`igny8_site_integrations`) +- WordPress connection configs +- Fields: site_id, platform, config_json, credentials_json, is_active, sync_status + +**PublishingRecord Table** (`igny8_publishing_records`) +- Track all publish operations +- Fields: content_id, destination, destination_id, destination_url, status, published_at + +--- + +## Status Mapping + +### IGNY8 Status → WordPress Status + +**Function:** `igny8_map_igny8_status_to_wp($igny8_status)` (WordPress) + +| IGNY8 Status | WordPress Status | Notes | +|--------------|------------------|-------| +| `completed` | `publish` | Published and live | +| `draft` | `draft` | Draft/unpublished | +| `pending` | `pending` | Pending review | +| `scheduled` | `future` | Scheduled for future | +| `archived` | `trash` | Trashed | +| *(other)* | `draft` | Default fallback | + +### WordPress Status → IGNY8 Status + +**Function:** `igny8_map_wp_status_to_igny8($wp_status)` (WordPress) + +| WordPress Status | IGNY8 Status | Notes | +|-----------------|--------------|-------| +| `publish` | `completed` | Published | +| `future` | `completed` | Scheduled (counts as completed) | +| `private` | `completed` | Private but complete | +| `pending` | `pending` | Pending review | +| `draft` | `draft` | Draft | +| `auto-draft` | `draft` | Auto-draft | +| `trash` | `archived` | Trashed | +| *(other)* | `draft` | Default fallback | + +**Important:** WordPress sends raw `wordpress_status` AND mapped IGNY8 `status` to backend + +--- + +## Troubleshooting Guide + +### Common Issues + +#### 1. "Failed to publish: undefined" + +**Symptoms:** +- Error message in frontend when clicking publish +- No post created in WordPress + +**Diagnosis Steps:** +1. **Check browser console:** + ```javascript + [Review.handlePublishSingle] Starting publish for content: {id, title} + [Review.handlePublishSingle] Calling API endpoint /v1/publisher/publish/ + ``` + +2. **Check Django logs:** + ``` + [PublisherViewSet.publish] 🚀 Publish request received: content_id=X + [PublisherService.publish_content] 📄 Content found: title='...' + [WordPressAdapter.publish] 🎬 Starting WordPress publish + ``` + +3. **Check WordPress logs:** + ``` + IGNY8: Creating WordPress post from IGNY8 content + IGNY8: Post created with ID 123 + ``` + +**Common Causes:** +- ❌ SiteIntegration missing API key → Check `credentials_json['api_key']` +- ❌ WordPress plugin not active → Verify plugin activated +- ❌ API key mismatch → Compare IGNY8 and WordPress `api_key` +- ❌ `site_url` not set → Check `SiteIntegration.config_json['site_url']` +- ❌ Content missing `content_html` → Content.content_html is empty + +**Fixes:** +- Ensure SiteIntegration has both `config_json['site_url']` and `credentials_json['api_key']` +- Verify WordPress endpoint accessible: `GET {site_url}/wp-json/igny8/v1/status` +- Check Content model has `content_html` field populated + +#### 2. Posts Not Syncing Back to IGNY8 + +**Symptoms:** +- Edit post in WordPress, status not updated in IGNY8 +- Post published in WordPress, keyword status not changing in IGNY8 + +**Diagnosis:** +1. Check if post has `_igny8_task_id`: `get_post_meta($post_id, '_igny8_task_id', true)` +2. Check connection enabled: `get_option('igny8_connection_enabled')` +3. Check WordPress error logs for API failures + +**Common Causes:** +- Post not IGNY8-managed (missing `_igny8_task_id`) +- Connection disabled in settings +- Access token expired and refresh failed +- Network/firewall blocking outbound requests + +**Fixes:** +- Only IGNY8-created posts sync (have `_igny8_task_id`) +- Enable connection in Settings → IGNY8 API +- Re-authenticate: delete `igny8_access_token` and `igny8_refresh_token`, save settings again +- Check server can reach `api.igny8.com` + +#### 3. SEO Metadata Not Appearing + +**Symptoms:** +- Meta title/description not in Yoast/RankMath/SEOPress + +**Diagnosis:** +1. Check if generic meta exists: `get_post_meta($post_id, '_igny8_meta_title')` +2. Check SEO plugin-specific meta: `get_post_meta($post_id, '_yoast_wpseo_title')` + +**Common Causes:** +- Content model missing `meta_title` or `meta_description` +- SEO plugin not detected during import + +**Fixes:** +- Ensure Content has `meta_title` and `meta_description` populated +- Function `igny8_import_seo_metadata()` writes to all major SEO plugins +- Manually copy from `_igny8_meta_title` to SEO plugin field + +#### 4. Images Not Importing + +**Symptoms:** +- Featured image not set +- Images in content not downloaded + +**Diagnosis:** +1. Check if `featured_image` in payload +2. Check WordPress upload permissions +3. Check `igny8_import_featured_image()` logs + +**Common Causes:** +- Image URL not accessible (403, 404) +- WordPress upload directory not writable +- `allow_url_fopen` disabled in PHP + +**Fixes:** +- Verify image URLs publicly accessible +- Check `wp-content/uploads/` permissions (775 or 755) +- Enable `allow_url_fopen` in `php.ini` + +#### 5. Duplicate Posts Created + +**Symptoms:** +- Same content published multiple times in WordPress +- Multiple posts with same `_igny8_content_id` + +**Diagnosis:** +1. Check if Celery task has duplicate check +2. Check if `external_id` set on Content after first publish + +**Common Causes:** +- Celery task retrying after network error +- Content.external_id not being saved +- Multiple publish button clicks + +**Fixes:** +- Celery task has duplicate check: `if content.external_id: return` +- Ensure WordPressAdapter saves `external_id` after successful publish +- Disable publish button after first click (frontend) + +### Debug Mode + +**Enable WordPress Debug Logging:** +```php +// wp-config.php +define('WP_DEBUG', true); +define('WP_DEBUG_LOG', true); +define('WP_DEBUG_DISPLAY', false); +define('IGNY8_DEBUG', true); // Plugin-specific +``` + +**Enable Django Logging:** +```python +# settings.py +LOGGING = { + 'version': 1, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + 'igny8_core.business.publishing': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + }, +} +``` + +**Check Logs:** +- WordPress: `wp-content/debug.log` +- Django: Console output or configured log file +- Browser: Console (F12 → Console tab) + +### Health Check Endpoints + +**WordPress Plugin Status:** +```bash +GET https://yoursite.com/wp-json/igny8/v1/status +``` +Response: +```json +{ + "success": true, + "data": { + "plugin_version": "1.0.0", + "connected": true, + "api_key_present": true, + "token_present": true, + "connection_enabled": true + } +} +``` + +**IGNY8 API Health:** +```bash +GET https://api.igny8.com/api/v1/health/ +``` + +### Common WordPress Errors + +**Error:** `IGNY8 API key not authenticated` +- **Cause:** API key missing or invalid +- **Fix:** Settings → IGNY8 API → Enter API key + +**Error:** `IGNY8 connection is disabled` +- **Cause:** Connection toggle disabled in settings +- **Fix:** Settings → IGNY8 API → Enable "Enable Connection" + +**Error:** `Post not found for this content ID` +- **Cause:** WordPress post with `_igny8_content_id` doesn't exist +- **Fix:** Content not yet published to WordPress or meta field missing + +--- + +## Developer Quick Reference + +### Quick Start: New Developer Onboarding + +**1. Understand the Architecture** +- Read this document (WORDPRESS_INTEGRATION_REFERENCE.md) +- Read MASTER_REFERENCE.md for overall IGNY8 architecture +- Review `docs/WORDPRESS-PLUGIN-INTEGRATION.md` for plugin details + +**2. Setup Local Environment** + +**WordPress Plugin:** +```bash +# Clone plugin +cd wp-content/plugins/ +git clone igny8-bridge + +# Activate plugin in WordPress admin +# Go to Settings → IGNY8 API +# Enter credentials: Email, Password, API Key +``` + +**IGNY8 Backend:** +```bash +cd igny8/backend +pip install -r requirements.txt +python manage.py migrate +python manage.py runserver + +# Start Celery (for async tasks) +celery -A igny8_core worker -l info +``` + +**3. Test Publishing Flow** +```bash +# 1. Create Content in IGNY8 +# 2. Click Publish in Review page +# 3. Check browser console for logs +# 4. Check Django logs for backend flow +# 5. Check WordPress for created post +# 6. Edit post in WordPress +# 7. Check IGNY8 for updated status +``` + +### Key Files to Modify + +**To Change Publishing Payload:** +- `igny8_core/business/publishing/services/adapters/wordpress_adapter.py` (IGNY8) +- `sync/igny8-to-wp.php` → `igny8_create_wordpress_post_from_task()` (WordPress) + +**To Add New Meta Field:** +- `wordpress_adapter.py` → Add to payload +- `igny8-to-wp.php` → Add to `meta_input` array +- Update this document's meta fields table + +**To Add New REST Endpoint:** +- `includes/class-igny8-rest-api.php` → `register_routes()` +- Add permission callback +- Add handler method + +**To Change Status Mapping:** +- `sync/igny8-to-wp.php` → `igny8_map_igny8_status_to_wp()` +- `sync/post-sync.php` → `igny8_map_wp_status_to_igny8()` + +### Testing Checklist + +**IGNY8 → WordPress Publishing** +- [ ] Create content in IGNY8 Writer +- [ ] Content has title, content_html, cluster +- [ ] Click Publish button +- [ ] Verify post created in WordPress +- [ ] Check post has correct title and content +- [ ] Verify SEO meta fields populated +- [ ] Check featured image attached +- [ ] Verify categories/tags assigned +- [ ] Confirm cluster/sector taxonomies set +- [ ] Check all `_igny8_*` meta fields present + +**WordPress → IGNY8 sync** +- [ ] Create IGNY8 content and publish to WP +- [ ] Edit post in WordPress (change status to draft) +- [ ] Check IGNY8 task status updated to 'draft' +- [ ] Publish post in WordPress +- [ ] Check IGNY8 task status updated to 'completed' +- [ ] Verify `wordpress_status` field shows 'publish' + +**Site Metadata Discovery** +- [ ] Call `GET /wp-json/igny8/v1/site-metadata/` +- [ ] Verify post types listed +- [ ] Check taxonomies included +- [ ] Confirm counts accurate +- [ ] Check WooCommerce detected (if installed) + +### Code Snippets + +**WordPress: Get IGNY8 Content ID from Post** +```php +$content_id = get_post_meta($post_id, '_igny8_content_id', true); +if ($content_id) { + echo "This post is IGNY8 Content ID: " . $content_id; +} +``` + +**WordPress: Check if Post is IGNY8-Managed** +```php +function is_igny8_post($post_id) { + $task_id = get_post_meta($post_id, '_igny8_task_id', true); + return !empty($task_id); +} +``` + +**IGNY8: Get WordPress Post ID from Content** +```python +from igny8_core.business.content.models import Content + +content = Content.objects.get(id=123) +if content.external_id: + print(f"WordPress Post ID: {content.external_id}") + print(f"WordPress URL: {content.external_url}") +``` + +**IGNY8: Manually Trigger Publish** +```python +from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress + +# Queue Celery task +publish_content_to_wordpress.delay( + content_id=123, + site_integration_id=1 +) +``` + +**WordPress: Call IGNY8 API** +```php +$api = new Igny8API(); +$response = $api->get('/writer/tasks/123/'); + +if ($response['success']) { + $task = $response['data']; + echo $task['title']; +} +``` + +### Useful Queries + +**WordPress: Find all IGNY8-managed posts** +```sql +SELECT p.ID, p.post_title, pm.meta_value as content_id +FROM wp_posts p +INNER JOIN wp_postmeta pm ON p.ID = pm.post_id +WHERE pm.meta_key = '_igny8_content_id' +ORDER BY p.ID DESC; +``` + +**IGNY8: Find all content published to WordPress** +```sql +SELECT id, title, external_id, external_url, sync_status +FROM igny8_content +WHERE external_id IS NOT NULL +ORDER BY updated_at DESC; +``` + +**WordPress: Find posts missing IGNY8 sync** +```sql +SELECT p.ID, p.post_title +FROM wp_posts p +LEFT JOIN wp_postmeta pm ON p.ID = pm.post_id AND pm.meta_key = '_igny8_content_id' +WHERE p.post_type = 'post' + AND p.post_status = 'publish' + AND pm.meta_value IS NULL; +``` + +### Integration Roadmap + +**Phase 1: Core Publishing (✅ Complete)** +- [x] IGNY8 → WordPress content publishing +- [x] WordPress → IGNY8 status sync +- [x] Authentication (JWT + API Key) +- [x] SEO metadata sync +- [x] Featured images +- [x] Taxonomies (categories, tags, clusters, sectors) + +**Phase 2: Enhanced Sync (🔄 In Progress)** +- [x] Site metadata discovery +- [ ] Automated content import from WordPress +- [ ] Bulk publishing +- [ ] Scheduled publishing +- [ ] Content update sync (detect WordPress edits) + +**Phase 3: Advanced Features (📋 Planned)** +- [ ] Internal linking recommendations +- [ ] Content optimization scoring +- [ ] Link graph sync +- [ ] Automated interlinking +- [ ] Content analytics sync + +**Phase 4: Automation (📋 Planned)** +- [ ] Webhooks (IGNY8 → WordPress events) +- [ ] WP-CLI commands +- [ ] Automated content refresh +- [ ] SEO score monitoring +- [ ] Broken link detection + +--- + +## Appendix: Data Flow Diagrams + +### Complete Publishing Architecture + +``` +┌────────────────────────────────────────────────────────────────┐ +│ IGNY8 SaaS Platform │ +├────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Frontend (React) │ │ +│ │ ──────────────── │ │ +│ │ Review.tsx → Publish Button │ │ +│ │ POST /api/v1/publisher/publish/ │ │ +│ │ {content_id: 123, destinations: ['wordpress']} │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ API Layer (Django REST Framework) │ │ +│ │ ────────────────────────────────── │ │ +│ │ PublisherViewSet.publish() │ │ +│ │ - Validate request │ │ +│ │ - Call PublisherService │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Business Logic (Services) │ │ +│ │ ───────────────────────────── │ │ +│ │ PublisherService.publish_content() │ │ +│ │ 1. Load Content from DB │ │ +│ │ 2. Create PublishingRecord │ │ +│ │ 3. Get SiteIntegration config │ │ +│ │ 4. Call WordPressAdapter │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Adapter Layer (Platform-Specific) │ │ +│ │ ────────────────────────────────── │ │ +│ │ WordPressAdapter.publish() │ │ +│ │ - Detect auth method (API key vs user/pass) │ │ +│ │ - Prepare payload │ │ +│ │ - POST to WordPress REST API │ │ +│ │ - Handle response │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ │ HTTPS Request │ +│ │ POST /wp-json/igny8/v1/publish-content/ │ +│ │ X-IGNY8-API-KEY: *** │ +└───────────────────────┼─────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────────────┐ +│ WordPress Site (yoursite.com) │ +├────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ WordPress REST API │ │ +│ │ ────────────────── │ │ +│ │ /wp-json/igny8/v1/publish-content/ │ │ +│ │ - Validate API key │ │ +│ │ - Parse request │ │ +│ │ - Call content creation handler │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Content Handler (PHP) │ │ +│ │ ───────────────────────── │ │ +│ │ igny8_create_wordpress_post_from_task() │ │ +│ │ 1. Resolve post type │ │ +│ │ 2. Prepare post data │ │ +│ │ 3. wp_insert_post() │ │ +│ │ 4. Import SEO metadata │ │ +│ │ 5. Import featured image │ │ +│ │ 6. Set taxonomies │ │ +│ │ 7. Store IGNY8 meta fields │ │ +│ └────────────────────┬─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ WordPress Database │ │ +│ │ ────────────────── │ │ +│ │ wp_posts │ │ +│ │ - ID, post_title, post_content, post_status │ │ +│ │ │ │ +│ │ wp_postmeta │ │ +│ │ - _igny8_content_id, _igny8_task_id, _igny8_cluster_id │ │ +│ │ - _igny8_meta_title, _igny8_meta_description │ │ +│ │ │ │ +│ │ wp_term_relationships │ │ +│ │ - Categories, Tags, Clusters, Sectors │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +**End of Document** + +For questions or issues, consult: +- Main documentation: `/docs/MASTER_REFERENCE.md` +- Plugin docs: `/igny8-wp-integration/docs/WORDPRESS-PLUGIN-INTEGRATION.md` +- Publishing fixes: `/igny8/FIXES-PUBLISH-FAILURE.md` +- GitHub Issues: [Repository Issues](https://github.com/alorig/igny8-wp-integration/issues)