From 1eba4a4e156c70c1fb0119f414ec347a72d60fc5 Mon Sep 17 00:00:00 2001
From: alorig <220087330+alorig@users.noreply.github.com>
Date: Fri, 21 Nov 2025 19:18:24 +0500
Subject: [PATCH] wp plugin loaded
---
igny8-wp-integration-plugin/.gitattributes | 2 +
igny8-wp-integration-plugin/README.md | 367 +++
.../admin/assets/css/admin.css | 347 +++
.../admin/assets/js/admin.js | 178 ++
.../admin/assets/js/post-editor.js | 200 ++
.../admin/class-admin-columns.php | 335 +++
.../admin/class-admin.php | 488 ++++
.../admin/class-post-meta-boxes.php | 469 ++++
.../admin/settings.php | 532 +++++
.../data/link-graph.php | 192 ++
.../data/semantic-mapping.php | 225 ++
.../data/site-collection.php | 579 +++++
.../data/woocommerce.php | 226 ++
.../docs/PHASE_5_IMPLEMENTATION_SUMMARY.md | 185 ++
.../docs/PHASE_6_IMPLEMENTATION_SUMMARY.md | 298 +++
.../docs/STATUS_SYNC_DOCUMENTATION.md | 249 ++
.../docs/STYLE_GUIDE.md | 186 ++
.../docs/VERIFICATION_REPORT_PHASES_1-5.md | 430 ++++
.../docs/WORDPRESS-PLUGIN-INTEGRATION.md | 2084 +++++++++++++++++
.../docs/missing-saas-api-endpoints.md | 27 +
.../docs/wp-bridge-implementation-plan.md | 84 +
igny8-wp-integration-plugin/igny8-bridge.php | 183 ++
.../includes/class-igny8-api.php | 328 +++
.../includes/class-igny8-link-queue.php | 202 ++
.../includes/class-igny8-rest-api.php | 294 +++
.../includes/class-igny8-site.php | 118 +
.../includes/class-igny8-webhook-logs.php | 147 ++
.../includes/class-igny8-webhooks.php | 381 +++
.../includes/functions.php | 605 +++++
.../languages/igny8-bridge.pot | 100 +
igny8-wp-integration-plugin/sync/hooks.php | 41 +
.../sync/igny8-to-wp.php | 807 +++++++
.../sync/post-sync.php | 363 +++
.../sync/taxonomy-sync.php | 425 ++++
igny8-wp-integration-plugin/uninstall.php | 53 +
35 files changed, 11730 insertions(+)
create mode 100644 igny8-wp-integration-plugin/.gitattributes
create mode 100644 igny8-wp-integration-plugin/README.md
create mode 100644 igny8-wp-integration-plugin/admin/assets/css/admin.css
create mode 100644 igny8-wp-integration-plugin/admin/assets/js/admin.js
create mode 100644 igny8-wp-integration-plugin/admin/assets/js/post-editor.js
create mode 100644 igny8-wp-integration-plugin/admin/class-admin-columns.php
create mode 100644 igny8-wp-integration-plugin/admin/class-admin.php
create mode 100644 igny8-wp-integration-plugin/admin/class-post-meta-boxes.php
create mode 100644 igny8-wp-integration-plugin/admin/settings.php
create mode 100644 igny8-wp-integration-plugin/data/link-graph.php
create mode 100644 igny8-wp-integration-plugin/data/semantic-mapping.php
create mode 100644 igny8-wp-integration-plugin/data/site-collection.php
create mode 100644 igny8-wp-integration-plugin/data/woocommerce.php
create mode 100644 igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md
create mode 100644 igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md
create mode 100644 igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md
create mode 100644 igny8-wp-integration-plugin/docs/STYLE_GUIDE.md
create mode 100644 igny8-wp-integration-plugin/docs/VERIFICATION_REPORT_PHASES_1-5.md
create mode 100644 igny8-wp-integration-plugin/docs/WORDPRESS-PLUGIN-INTEGRATION.md
create mode 100644 igny8-wp-integration-plugin/docs/missing-saas-api-endpoints.md
create mode 100644 igny8-wp-integration-plugin/docs/wp-bridge-implementation-plan.md
create mode 100644 igny8-wp-integration-plugin/igny8-bridge.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-api.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-link-queue.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-rest-api.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-site.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-webhook-logs.php
create mode 100644 igny8-wp-integration-plugin/includes/class-igny8-webhooks.php
create mode 100644 igny8-wp-integration-plugin/includes/functions.php
create mode 100644 igny8-wp-integration-plugin/languages/igny8-bridge.pot
create mode 100644 igny8-wp-integration-plugin/sync/hooks.php
create mode 100644 igny8-wp-integration-plugin/sync/igny8-to-wp.php
create mode 100644 igny8-wp-integration-plugin/sync/post-sync.php
create mode 100644 igny8-wp-integration-plugin/sync/taxonomy-sync.php
create mode 100644 igny8-wp-integration-plugin/uninstall.php
diff --git a/igny8-wp-integration-plugin/.gitattributes b/igny8-wp-integration-plugin/.gitattributes
new file mode 100644
index 00000000..dfe07704
--- /dev/null
+++ b/igny8-wp-integration-plugin/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/igny8-wp-integration-plugin/README.md b/igny8-wp-integration-plugin/README.md
new file mode 100644
index 00000000..4b91d333
--- /dev/null
+++ b/igny8-wp-integration-plugin/README.md
@@ -0,0 +1,367 @@
+# IGNY8 WordPress Bridge Plugin
+
+**Version**: 1.0.0
+**Last Updated**: 2025-10-17
+**Requires**: WordPress 5.0+, PHP 7.4+
+
+---
+
+## Overview
+
+The IGNY8 WordPress Bridge Plugin is a **lightweight synchronization interface** that connects WordPress sites to the IGNY8 API. This plugin acts as a bridge, not a content management system, using WordPress native structures (taxonomies, post meta) to sync data bidirectionally with IGNY8.
+
+### Key Principles
+
+- ✅ **No Custom Database Tables** - Uses WordPress native taxonomies and post meta
+- ✅ **Lightweight Bridge** - Minimal code, maximum efficiency
+- ✅ **Two-Way Sync** - WordPress ↔ IGNY8 API synchronization
+- ✅ **WordPress Native** - Leverages existing WordPress structures
+- ✅ **API-First** - IGNY8 API is the source of truth
+
+---
+
+## Features
+
+### Core Functionality
+
+1. **API Authentication**
+ - Secure token management
+ - Automatic token refresh
+ - Encrypted credential storage
+
+2. **Two-Way Synchronization**
+ - WordPress → IGNY8: Post status changes sync to IGNY8 tasks
+ - IGNY8 → WordPress: Content published from IGNY8 creates WordPress posts
+
+3. **Taxonomy Mapping**
+ - WordPress taxonomies → IGNY8 Sectors/Clusters
+ - Hierarchical taxonomies map to IGNY8 Sectors
+ - Taxonomy terms map to IGNY8 Clusters
+
+4. **Post Meta Integration**
+ - `_igny8_task_id` - Links WordPress posts to IGNY8 tasks
+ - `_igny8_cluster_id` - Links posts to IGNY8 clusters
+ - `_igny8_sector_id` - Links posts to IGNY8 sectors
+ - `_igny8_keyword_ids` - Links posts to IGNY8 keywords
+
+5. **Site Data Collection**
+ - Automatic collection of WordPress posts, taxonomies, products
+ - Semantic mapping to IGNY8 structure
+ - WooCommerce integration support
+
+6. **Status Mapping**
+ - WordPress post status → IGNY8 task status
+ - Automatic sync on post save/publish/status change
+
+---
+
+## Installation
+
+### Requirements
+
+- WordPress 5.0 or higher
+- PHP 7.4 or higher
+- WordPress REST API enabled
+- IGNY8 API account credentials
+
+### Installation Steps
+
+1. **Download/Clone Plugin**
+ ```bash
+ git clone [repository-url]
+ cd igny8-ai-os
+ ```
+
+2. **Install in WordPress**
+ - Copy the `igny8-ai-os` folder to `/wp-content/plugins/`
+ - Or create a symlink for development
+
+3. **Activate Plugin**
+ - Go to WordPress Admin → Plugins
+ - Activate "IGNY8 WordPress Bridge"
+
+4. **Configure API Connection**
+ - Go to Settings → IGNY8 API
+ - Enter your IGNY8 email and password
+ - Click "Connect to IGNY8"
+
+---
+
+## Configuration
+
+### API Settings
+
+Navigate to **Settings → IGNY8 API** to configure:
+
+- **Email**: Your IGNY8 account email
+- **Password**: Your IGNY8 account password
+- **Site ID**: Your IGNY8 site ID (auto-detected after connection)
+
+### WordPress Integration
+
+The plugin automatically:
+
+1. **Registers Taxonomies** (if needed):
+ - `sectors` - Maps to IGNY8 Sectors
+ - `clusters` - Maps to IGNY8 Clusters
+
+2. **Registers Post Meta Fields**:
+ - `_igny8_task_id`
+ - `_igny8_cluster_id`
+ - `_igny8_sector_id`
+ - `_igny8_keyword_ids`
+ - `_igny8_content_id`
+
+3. **Sets Up WordPress Hooks**:
+ - `save_post` - Syncs post changes to IGNY8
+ - `publish_post` - Updates keywords on publish
+ - `transition_post_status` - Handles status changes
+
+---
+
+## Usage
+
+### Basic Workflow
+
+#### 1. Connect to IGNY8 API
+
+```php
+// Automatically handled via Settings page
+// Or programmatically:
+$api = new Igny8API();
+$api->login('your@email.com', 'password');
+```
+
+#### 2. Sync WordPress Site Data
+
+```php
+// Collect and send site data to IGNY8
+$site_id = get_option('igny8_site_id');
+igny8_send_site_data_to_igny8($site_id);
+```
+
+#### 3. WordPress → IGNY8 Sync
+
+When you save/publish a WordPress post:
+
+1. Plugin checks for `_igny8_task_id` in post meta
+2. If found, syncs post status to IGNY8 task
+3. If published, updates related keywords to 'mapped' status
+
+#### 4. IGNY8 → WordPress Sync
+
+When content is published from IGNY8:
+
+1. IGNY8 triggers webhook (or scheduled sync)
+2. Plugin creates WordPress post via `wp_insert_post()`
+3. Post meta saved with `_igny8_task_id`
+4. IGNY8 task updated with WordPress post ID
+
+---
+
+## WordPress Structures Used
+
+### Taxonomies
+
+- **`sectors`** (hierarchical)
+ - Maps to IGNY8 Sectors
+ - Can be created manually or synced from IGNY8
+
+- **`clusters`** (hierarchical)
+ - Maps to IGNY8 Clusters
+ - Can be created manually or synced from IGNY8
+
+- **Native Taxonomies**
+ - `category` - Can map to IGNY8 Sectors
+ - `post_tag` - Can be used for keyword extraction
+
+### Post Meta Fields
+
+All stored in WordPress `wp_postmeta` table:
+
+- `_igny8_task_id` (integer) - IGNY8 task ID
+- `_igny8_cluster_id` (integer) - IGNY8 cluster ID
+- `_igny8_sector_id` (integer) - IGNY8 sector ID
+- `_igny8_keyword_ids` (array) - Array of IGNY8 keyword IDs
+- `_igny8_content_id` (integer) - IGNY8 content ID
+- `_igny8_last_synced` (datetime) - Last sync timestamp
+
+### Post Status Mapping
+
+| WordPress Status | IGNY8 Task Status |
+|------------------|-------------------|
+| `publish` | `completed` |
+| `draft` | `draft` |
+| `pending` | `pending` |
+| `private` | `completed` |
+| `trash` | `archived` |
+| `future` | `scheduled` |
+
+---
+
+## API Reference
+
+### Main Classes
+
+#### `Igny8API`
+
+Main API client class for all IGNY8 API interactions.
+
+```php
+$api = new Igny8API();
+
+// Login
+$api->login('email@example.com', 'password');
+
+// Get keywords
+$response = $api->get('/planner/keywords/');
+
+// Create task
+$response = $api->post('/writer/tasks/', $data);
+
+// Update task
+$response = $api->put('/writer/tasks/123/', $data);
+```
+
+#### `Igny8WordPressSync`
+
+Handles two-way synchronization between WordPress and IGNY8.
+
+```php
+$sync = new Igny8WordPressSync();
+// Automatically hooks into WordPress post actions
+```
+
+#### `Igny8SiteIntegration`
+
+Manages site data collection and semantic mapping.
+
+```php
+$integration = new Igny8SiteIntegration($site_id);
+$result = $integration->full_site_scan();
+```
+
+### Main Functions
+
+- `igny8_login($email, $password)` - Authenticate with IGNY8
+- `igny8_sync_post_status_to_igny8($post_id, $post, $update)` - Sync post to IGNY8
+- `igny8_collect_site_data()` - Collect all WordPress site data
+- `igny8_send_site_data_to_igny8($site_id)` - Send site data to IGNY8
+- `igny8_map_site_to_semantic_strategy($site_id, $site_data)` - Map to semantic structure
+
+---
+
+## File Structure
+
+```
+igny8-ai-os/
+├── igny8-bridge.php # Main plugin file
+├── README.md # This file
+├── docs/ # Documentation hub
+│ ├── README.md # Index of available docs
+│ ├── WORDPRESS-PLUGIN-INTEGRATION.md
+│ ├── wp-bridge-implementation-plan.md
+│ ├── missing-saas-api-endpoints.md
+│ ├── STATUS_SYNC_DOCUMENTATION.md
+│ └── STYLE_GUIDE.md
+├── includes/
+│ ├── class-igny8-api.php # API client class
+│ ├── class-igny8-sync.php # Sync handler class
+│ ├── class-igny8-site.php # Site integration class
+│ └── functions.php # Helper functions
+├── admin/
+│ ├── class-admin.php # Admin interface
+│ ├── settings.php # Settings page
+│ └── assets/
+│ ├── css/
+│ └── js/
+├── sync/
+│ ├── hooks.php # WordPress hooks
+│ ├── post-sync.php # Post synchronization
+│ └── taxonomy-sync.php # Taxonomy synchronization
+├── data/
+│ ├── site-collection.php # Site data collection
+│ └── semantic-mapping.php # Semantic mapping
+└── uninstall.php # Uninstall handler
+```
+
+---
+
+## Development
+
+### Code Standards
+
+- Follow WordPress Coding Standards
+- Use WordPress native functions
+- No custom database tables
+- All data in WordPress native structures
+
+### Testing
+
+```bash
+# Run WordPress unit tests
+phpunit
+
+# Test API connection
+wp eval 'var_dump((new Igny8API())->login("test@example.com", "password"));'
+```
+
+---
+
+## Troubleshooting
+
+### Authentication Issues
+
+**Problem**: Cannot connect to IGNY8 API
+
+**Solutions**:
+1. Verify email and password are correct
+2. Check API endpoint is accessible
+3. Check WordPress REST API is enabled
+4. Review error logs in WordPress debug log
+
+### Sync Issues
+
+**Problem**: Posts not syncing to IGNY8
+
+**Solutions**:
+1. Verify `_igny8_task_id` exists in post meta
+2. Check API token is valid (not expired)
+3. Review WordPress hooks are firing
+4. Check error logs
+
+### Token Expiration
+
+**Problem**: Token expires frequently
+
+**Solution**: Plugin automatically refreshes tokens. If issues persist, check token refresh logic.
+
+---
+
+## Support
+
+- **Documentation**: See `WORDPRESS-PLUGIN-INTEGRATION.md`
+- **API Documentation**: https://api.igny8.com/docs
+- **Issues**: [GitHub Issues](repository-url/issues)
+
+---
+
+## License
+
+[Your License Here]
+
+---
+
+## Changelog
+
+### 1.0.0 - 2025-10-17
+- Initial release
+- API authentication
+- Two-way sync
+- Site data collection
+- Semantic mapping
+
+---
+
+**Last Updated**: 2025-10-17
+
diff --git a/igny8-wp-integration-plugin/admin/assets/css/admin.css b/igny8-wp-integration-plugin/admin/assets/css/admin.css
new file mode 100644
index 00000000..fa2ef1ac
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/assets/css/admin.css
@@ -0,0 +1,347 @@
+/**
+ * Admin Styles
+ *
+ * All styles for IGNY8 Bridge admin interface
+ * Update this file to change global design
+ *
+ * @package Igny8Bridge
+ */
+
+/* ============================================
+ Container & Layout
+ ============================================ */
+
+.igny8-settings-container {
+ max-width: 1200px;
+}
+
+.igny8-settings-card {
+ background: #fff;
+ border: 1px solid #ccd0d4;
+ box-shadow: 0 1px 1px rgba(0,0,0,.04);
+ padding: 20px;
+ margin: 20px 0;
+}
+
+.igny8-settings-card h2 {
+ margin-top: 0;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+/* ============================================
+ Status Indicators
+ ============================================ */
+
+.igny8-status-connected {
+ color: #46b450;
+ font-weight: bold;
+}
+
+.igny8-status-disconnected {
+ color: #dc3232;
+ font-weight: bold;
+}
+
+.igny8-test-result {
+ margin-left: 10px;
+}
+
+.igny8-test-result .igny8-success {
+ color: #46b450;
+}
+
+.igny8-test-result .igny8-error {
+ color: #dc3232;
+}
+
+.igny8-test-result .igny8-loading {
+ color: #2271b1;
+}
+
+/* ============================================
+ Sync Operations
+ ============================================ */
+
+.igny8-sync-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 20px;
+}
+
+.igny8-sync-actions .button {
+ min-width: 150px;
+}
+
+.igny8-sync-status {
+ margin-top: 15px;
+ padding: 10px;
+ border-radius: 4px;
+ display: none;
+}
+
+.igny8-sync-status.igny8-sync-status-success {
+ background-color: #d4edda;
+ border: 1px solid #c3e6cb;
+ color: #155724;
+ display: block;
+}
+
+.igny8-sync-status.igny8-sync-status-error {
+ background-color: #f8d7da;
+ border: 1px solid #f5c6cb;
+ color: #721c24;
+ display: block;
+}
+
+.igny8-sync-status.igny8-sync-status-loading {
+ background-color: #d1ecf1;
+ border: 1px solid #bee5eb;
+ color: #0c5460;
+ display: block;
+}
+
+/* ============================================
+ Statistics
+ ============================================ */
+
+.igny8-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 20px;
+ margin-top: 15px;
+}
+
+.igny8-stat-item {
+ padding: 15px;
+ background: #f9f9f9;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.igny8-stat-label {
+ font-size: 12px;
+ color: #666;
+ text-transform: uppercase;
+ margin-bottom: 8px;
+}
+
+.igny8-stat-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #2271b1;
+}
+
+/* ============================================
+ Diagnostics
+ ============================================ */
+
+.igny8-diagnostics-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 16px;
+ margin-top: 15px;
+}
+
+.igny8-diagnostic-item {
+ padding: 15px;
+ background-color: #f6f7f7;
+ border: 1px solid #dcdcde;
+ border-radius: 4px;
+}
+
+.igny8-diagnostic-label {
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: #555d66;
+ margin-bottom: 6px;
+}
+
+.igny8-diagnostic-value {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1d2327;
+}
+
+.igny8-diagnostic-item .description {
+ margin: 6px 0 0;
+ color: #646970;
+}
+
+/* ============================================
+ Buttons
+ ============================================ */
+
+.igny8-button-group {
+ display: flex;
+ gap: 10px;
+ margin: 15px 0;
+}
+
+.igny8-button-group .button {
+ flex: 1;
+}
+
+/* ============================================
+ Loading States
+ ============================================ */
+
+.igny8-loading {
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+.igny8-spinner {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border: 2px solid #f3f3f3;
+ border-top: 2px solid #2271b1;
+ border-radius: 50%;
+ animation: igny8-spin 1s linear infinite;
+ margin-right: 8px;
+ vertical-align: middle;
+}
+
+@keyframes igny8-spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* ============================================
+ Messages & Notifications
+ ============================================ */
+
+.igny8-message {
+ padding: 12px;
+ margin: 15px 0;
+ border-left: 4px solid;
+ background: #fff;
+}
+
+.igny8-message.igny8-message-success {
+ border-color: #46b450;
+ background-color: #f0f8f0;
+}
+
+.igny8-message.igny8-message-error {
+ border-color: #dc3232;
+ background-color: #fff5f5;
+}
+
+.igny8-message.igny8-message-info {
+ border-color: #2271b1;
+ background-color: #f0f6fc;
+}
+
+.igny8-message.igny8-message-warning {
+ border-color: #f0b849;
+ background-color: #fffbf0;
+}
+
+/* ============================================
+ Tables
+ ============================================ */
+
+.igny8-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 15px 0;
+}
+
+.igny8-table th,
+.igny8-table td {
+ padding: 10px;
+ text-align: left;
+ border-bottom: 1px solid #ddd;
+}
+
+.igny8-table th {
+ background-color: #f9f9f9;
+ font-weight: 600;
+}
+
+.igny8-table tr:hover {
+ background-color: #f9f9f9;
+}
+
+/* ============================================
+ Admin Columns
+ ============================================ */
+
+.igny8-badge {
+ display: inline-block;
+ padding: 3px 8px;
+ border-radius: 3px;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ line-height: 1.4;
+}
+
+.igny8-badge-igny8 {
+ background-color: #2271b1;
+ color: #fff;
+}
+
+.igny8-badge-wordpress {
+ background-color: #646970;
+ color: #fff;
+}
+
+.igny8-terms-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+}
+
+.igny8-term-badge {
+ display: inline-block;
+ padding: 2px 6px;
+ background-color: #f0f0f1;
+ border: 1px solid #c3c4c7;
+ border-radius: 2px;
+ font-size: 11px;
+ color: #50575e;
+}
+
+.igny8-empty {
+ color: #a7aaad;
+ font-style: italic;
+}
+
+.igny8-action-link {
+ color: #2271b1;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.igny8-action-link:hover {
+ color: #135e96;
+ text-decoration: underline;
+}
+
+/* ============================================
+ Responsive
+ ============================================ */
+
+@media (max-width: 782px) {
+ .igny8-sync-actions {
+ flex-direction: column;
+ }
+
+ .igny8-sync-actions .button {
+ width: 100%;
+ }
+
+ .igny8-stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .igny8-diagnostics-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
diff --git a/igny8-wp-integration-plugin/admin/assets/js/admin.js b/igny8-wp-integration-plugin/admin/assets/js/admin.js
new file mode 100644
index 00000000..b6e0f306
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/assets/js/admin.js
@@ -0,0 +1,178 @@
+/**
+ * Admin JavaScript
+ *
+ * @package Igny8Bridge
+ */
+
+(function($) {
+ 'use strict';
+
+ $(document).ready(function() {
+ // Test connection button
+ $('#igny8-test-connection').on('click', function() {
+ var $button = $(this);
+ var $result = $('#igny8-test-result');
+
+ $button.prop('disabled', true).addClass('igny8-loading');
+ $result.html('Testing...');
+
+ $.ajax({
+ url: igny8Admin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_test_connection',
+ nonce: igny8Admin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ $result.html('✓ Connection successful');
+ } else {
+ $result.html('✗ ' + (response.data.message || 'Connection failed') + '');
+ }
+ },
+ error: function() {
+ $result.html('✗ Request failed');
+ },
+ complete: function() {
+ $button.prop('disabled', false).removeClass('igny8-loading');
+ }
+ });
+ });
+
+ // Sync posts to IGNY8
+ $('#igny8-sync-posts').on('click', function() {
+ igny8TriggerSync('igny8_sync_posts', 'Syncing posts to IGNY8...');
+ });
+
+ // Sync taxonomies
+ $('#igny8-sync-taxonomies').on('click', function() {
+ igny8TriggerSync('igny8_sync_taxonomies', 'Syncing taxonomies...');
+ });
+
+ // Sync from IGNY8
+ $('#igny8-sync-from-igny8').on('click', function() {
+ igny8TriggerSync('igny8_sync_from_igny8', 'Syncing from IGNY8...');
+ });
+
+ // Collect and send site data
+ $('#igny8-collect-site-data').on('click', function() {
+ igny8TriggerSync('igny8_collect_site_data', 'Collecting and sending site data...');
+ });
+
+ // Load sync statistics
+ igny8LoadStats();
+
+ // Handle row action links
+ $(document).on('click', '.igny8-action-link', function(e) {
+ e.preventDefault();
+
+ var $link = $(this);
+ var postId = $link.data('post-id');
+ var action = $link.data('action');
+
+ if (!postId) {
+ return;
+ }
+
+ if (!confirm('Are you sure you want to ' + (action === 'send' ? 'send' : 'update') + ' this post to IGNY8?')) {
+ return;
+ }
+
+ $link.text('Processing...').prop('disabled', true);
+
+ $.ajax({
+ url: igny8Admin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_send_to_igny8',
+ post_id: postId,
+ action_type: action,
+ nonce: igny8Admin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ alert(response.data.message || 'Success!');
+ location.reload();
+ } else {
+ alert(response.data.message || 'Failed to send to IGNY8');
+ $link.text(action === 'send' ? 'Send to IGNY8' : 'Update in IGNY8').prop('disabled', false);
+ }
+ },
+ error: function() {
+ alert('Request failed');
+ $link.text(action === 'send' ? 'Send to IGNY8' : 'Update in IGNY8').prop('disabled', false);
+ }
+ });
+ });
+ });
+
+ /**
+ * Trigger sync operation
+ */
+ function igny8TriggerSync(action, message) {
+ var $status = $('#igny8-sync-status');
+ var $button = $('#' + action.replace('igny8_', 'igny8-'));
+
+ $status.removeClass('igny8-sync-status-success igny8-sync-status-error')
+ .addClass('igny8-sync-status-loading')
+ .html('' + message);
+
+ $button.prop('disabled', true).addClass('igny8-loading');
+
+ $.ajax({
+ url: igny8Admin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: action,
+ nonce: igny8Admin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ $status.removeClass('igny8-sync-status-loading')
+ .addClass('igny8-sync-status-success')
+ .html('✓ ' + (response.data.message || 'Operation completed successfully'));
+
+ // Reload stats
+ igny8LoadStats();
+ } else {
+ $status.removeClass('igny8-sync-status-loading')
+ .addClass('igny8-sync-status-error')
+ .html('✗ ' + (response.data.message || 'Operation failed'));
+ }
+ },
+ error: function() {
+ $status.removeClass('igny8-sync-status-loading')
+ .addClass('igny8-sync-status-error')
+ .html('✗ Request failed');
+ },
+ complete: function() {
+ $button.prop('disabled', false).removeClass('igny8-loading');
+ }
+ });
+ }
+
+ /**
+ * Load sync statistics
+ */
+ function igny8LoadStats() {
+ $.ajax({
+ url: igny8Admin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_get_stats',
+ nonce: igny8Admin.nonce
+ },
+ success: function(response) {
+ if (response.success && response.data) {
+ if (response.data.synced_posts !== undefined) {
+ $('#igny8-stat-posts').text(response.data.synced_posts);
+ }
+ if (response.data.last_sync) {
+ $('#igny8-stat-last-sync').text(response.data.last_sync);
+ }
+ }
+ }
+ });
+ }
+})(jQuery);
+
diff --git a/igny8-wp-integration-plugin/admin/assets/js/post-editor.js b/igny8-wp-integration-plugin/admin/assets/js/post-editor.js
new file mode 100644
index 00000000..2be154c1
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/assets/js/post-editor.js
@@ -0,0 +1,200 @@
+/**
+ * Post Editor JavaScript
+ *
+ * Handles AJAX interactions for Planner and Optimizer meta boxes
+ *
+ * @package Igny8Bridge
+ */
+
+(function($) {
+ 'use strict';
+
+ $(document).ready(function() {
+ // Fetch Planner Brief
+ $('#igny8-fetch-brief').on('click', function() {
+ var $button = $(this);
+ var $message = $('#igny8-planner-brief-message');
+ var postId = $button.data('post-id');
+ var taskId = $button.data('task-id');
+
+ $button.prop('disabled', true).text('Fetching...');
+ $message.hide().removeClass('notice-success notice-error');
+
+ $.ajax({
+ url: igny8PostEditor.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_fetch_planner_brief',
+ nonce: igny8PostEditor.nonce,
+ post_id: postId,
+ task_id: taskId
+ },
+ success: function(response) {
+ if (response.success) {
+ $message.addClass('notice notice-success inline')
+ .html('
' + response.data.message + '
')
+ .show();
+
+ // Reload page to show updated brief
+ setTimeout(function() {
+ location.reload();
+ }, 1000);
+ } else {
+ $message.addClass('notice notice-error inline')
+ .html('' + (response.data.message || 'Failed to fetch brief') + '
')
+ .show();
+ $button.prop('disabled', false).text('Fetch Brief');
+ }
+ },
+ error: function() {
+ $message.addClass('notice notice-error inline')
+ .html('Request failed
')
+ .show();
+ $button.prop('disabled', false).text('Fetch Brief');
+ }
+ });
+ });
+
+ // Refresh Planner Task
+ $('#igny8-refresh-task').on('click', function() {
+ var $button = $(this);
+ var $message = $('#igny8-planner-brief-message');
+ var postId = $button.data('post-id');
+ var taskId = $button.data('task-id');
+
+ if (!confirm('Are you sure you want to request a refresh of this task from IGNY8 Planner?')) {
+ return;
+ }
+
+ $button.prop('disabled', true).text('Requesting...');
+ $message.hide().removeClass('notice-success notice-error');
+
+ $.ajax({
+ url: igny8PostEditor.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_refresh_planner_task',
+ nonce: igny8PostEditor.nonce,
+ post_id: postId,
+ task_id: taskId
+ },
+ success: function(response) {
+ if (response.success) {
+ $message.addClass('notice notice-success inline')
+ .html('' + response.data.message + '
')
+ .show();
+ } else {
+ $message.addClass('notice notice-error inline')
+ .html('' + (response.data.message || 'Failed to request refresh') + '
')
+ .show();
+ }
+ $button.prop('disabled', false).text('Request Refresh');
+ },
+ error: function() {
+ $message.addClass('notice notice-error inline')
+ .html('Request failed
')
+ .show();
+ $button.prop('disabled', false).text('Request Refresh');
+ }
+ });
+ });
+
+ // Create Optimizer Job
+ $('#igny8-create-optimizer-job').on('click', function() {
+ var $button = $(this);
+ var $message = $('#igny8-optimizer-message');
+ var postId = $button.data('post-id');
+ var taskId = $button.data('task-id');
+
+ if (!confirm('Create a new optimizer job for this post?')) {
+ return;
+ }
+
+ $button.prop('disabled', true).text('Creating...');
+ $message.hide().removeClass('notice-success notice-error');
+
+ $.ajax({
+ url: igny8PostEditor.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_create_optimizer_job',
+ nonce: igny8PostEditor.nonce,
+ post_id: postId,
+ task_id: taskId,
+ job_type: 'audit',
+ priority: 'normal'
+ },
+ success: function(response) {
+ if (response.success) {
+ $message.addClass('notice notice-success inline')
+ .html('' + response.data.message + '
')
+ .show();
+
+ // Reload page to show updated status
+ setTimeout(function() {
+ location.reload();
+ }, 1000);
+ } else {
+ $message.addClass('notice notice-error inline')
+ .html('' + (response.data.message || 'Failed to create job') + '
')
+ .show();
+ $button.prop('disabled', false).text('Request Optimization');
+ }
+ },
+ error: function() {
+ $message.addClass('notice notice-error inline')
+ .html('Request failed
')
+ .show();
+ $button.prop('disabled', false).text('Request Optimization');
+ }
+ });
+ });
+
+ // Check Optimizer Status
+ $('#igny8-check-optimizer-status').on('click', function() {
+ var $button = $(this);
+ var $message = $('#igny8-optimizer-message');
+ var postId = $button.data('post-id');
+ var jobId = $button.data('job-id');
+
+ $button.prop('disabled', true).text('Checking...');
+ $message.hide().removeClass('notice-success notice-error');
+
+ $.ajax({
+ url: igny8PostEditor.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'igny8_get_optimizer_status',
+ nonce: igny8PostEditor.nonce,
+ post_id: postId,
+ job_id: jobId
+ },
+ success: function(response) {
+ if (response.success) {
+ $message.addClass('notice notice-success inline')
+ .html('Status: ' + response.data.status + '
')
+ .show();
+
+ // Reload page to show updated status
+ setTimeout(function() {
+ location.reload();
+ }, 1000);
+ } else {
+ $message.addClass('notice notice-error inline')
+ .html('' + (response.data.message || 'Failed to get status') + '
')
+ .show();
+ }
+ $button.prop('disabled', false).text('Check Status');
+ },
+ error: function() {
+ $message.addClass('notice notice-error inline')
+ .html('Request failed
')
+ .show();
+ $button.prop('disabled', false).text('Check Status');
+ }
+ });
+ });
+ });
+
+})(jQuery);
+
diff --git a/igny8-wp-integration-plugin/admin/class-admin-columns.php b/igny8-wp-integration-plugin/admin/class-admin-columns.php
new file mode 100644
index 00000000..59f9de5b
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/class-admin-columns.php
@@ -0,0 +1,335 @@
+ $value) {
+ $new_columns[$key] = $value;
+
+ if ($key === 'title') {
+ $new_columns['igny8_source'] = __('Source', 'igny8-bridge');
+ $new_columns['igny8_sectors'] = __('Sectors', 'igny8-bridge');
+ $new_columns['igny8_clusters'] = __('Clusters', 'igny8-bridge');
+ }
+ }
+
+ return $new_columns;
+ }
+
+ /**
+ * Render column content
+ *
+ * @param string $column_name Column name
+ * @param int $post_id Post ID
+ */
+ public function render_column_content($column_name, $post_id) {
+ switch ($column_name) {
+ case 'igny8_source':
+ $this->render_source_column($post_id);
+ break;
+
+ case 'igny8_sectors':
+ $this->render_sectors_column($post_id);
+ break;
+
+ case 'igny8_clusters':
+ $this->render_clusters_column($post_id);
+ break;
+ }
+ }
+
+ /**
+ * Render source column
+ *
+ * @param int $post_id Post ID
+ */
+ private function render_source_column($post_id) {
+ $task_id = get_post_meta($post_id, '_igny8_task_id', true);
+
+ if ($task_id) {
+ echo '';
+ echo esc_html__('IGNY8', 'igny8-bridge');
+ echo '';
+ } else {
+ echo '';
+ echo esc_html__('WP', 'igny8-bridge');
+ echo '';
+ }
+ }
+
+ /**
+ * Render sectors column
+ *
+ * @param int $post_id Post ID
+ */
+ private function render_sectors_column($post_id) {
+ $sectors = wp_get_post_terms($post_id, 'igny8_sectors', array('fields' => 'names'));
+
+ if (!empty($sectors) && !is_wp_error($sectors)) {
+ echo '';
+ foreach ($sectors as $sector) {
+ echo '' . esc_html($sector) . '';
+ }
+ echo '
';
+ } else {
+ echo '—';
+ }
+ }
+
+ /**
+ * Render clusters column
+ *
+ * @param int $post_id Post ID
+ */
+ private function render_clusters_column($post_id) {
+ $clusters = wp_get_post_terms($post_id, 'igny8_clusters', array('fields' => 'names'));
+
+ if (!empty($clusters) && !is_wp_error($clusters)) {
+ echo '';
+ foreach ($clusters as $cluster) {
+ echo '' . esc_html($cluster) . '';
+ }
+ echo '
';
+ } else {
+ echo '—';
+ }
+ }
+
+ /**
+ * Make columns sortable
+ *
+ * @param array $columns Sortable columns
+ * @return array Modified columns
+ */
+ public function make_columns_sortable($columns) {
+ $columns['igny8_source'] = 'igny8_source';
+ return $columns;
+ }
+
+ /**
+ * Add row actions
+ *
+ * @param array $actions Existing actions
+ * @param WP_Post $post Post object
+ * @return array Modified actions
+ */
+ public function add_row_actions($actions, $post) {
+ // Only add for published posts
+ if ($post->post_status !== 'publish') {
+ return $actions;
+ }
+
+ // Check if already synced to IGNY8
+ $task_id = get_post_meta($post->ID, '_igny8_task_id', true);
+
+ if ($task_id) {
+ // Already synced - show update action
+ $actions['igny8_update'] = sprintf(
+ '%s',
+ '#',
+ $post->ID,
+ __('Update in IGNY8', 'igny8-bridge')
+ );
+ } else {
+ // Not synced - show send action
+ $actions['igny8_send'] = sprintf(
+ '%s',
+ '#',
+ $post->ID,
+ __('Send to IGNY8', 'igny8-bridge')
+ );
+ }
+
+ return $actions;
+ }
+
+ /**
+ * Send post to IGNY8 (AJAX handler)
+ */
+ public static function send_to_igny8() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('edit_posts')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+ $action = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : 'send';
+
+ if (!$post_id) {
+ wp_send_json_error(array('message' => 'Invalid post ID'));
+ }
+
+ $post = get_post($post_id);
+ if (!$post) {
+ wp_send_json_error(array('message' => 'Post not found'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated with IGNY8'));
+ }
+
+ $site_id = get_option('igny8_site_id');
+ if (!$site_id) {
+ wp_send_json_error(array('message' => 'Site ID not set'));
+ }
+
+ // Prepare post data for IGNY8
+ $post_data = array(
+ 'title' => $post->post_title,
+ 'content' => $post->post_content,
+ 'excerpt' => $post->post_excerpt,
+ 'status' => $post->post_status === 'publish' ? 'completed' : 'draft',
+ 'post_type' => $post->post_type,
+ 'url' => get_permalink($post_id),
+ 'wordpress_post_id' => $post_id
+ );
+
+ // Get categories
+ $categories = wp_get_post_categories($post_id, array('fields' => 'names'));
+ if (!empty($categories)) {
+ $post_data['categories'] = $categories;
+ }
+
+ // Get tags
+ $tags = wp_get_post_tags($post_id, array('fields' => 'names'));
+ if (!empty($tags)) {
+ $post_data['tags'] = $tags;
+ }
+
+ // Get featured image
+ $featured_image_id = get_post_thumbnail_id($post_id);
+ if ($featured_image_id) {
+ $post_data['featured_image'] = wp_get_attachment_image_url($featured_image_id, 'full');
+ }
+
+ // Get sectors and clusters
+ $sectors = wp_get_post_terms($post_id, 'igny8_sectors', array('fields' => 'ids'));
+ $clusters = wp_get_post_terms($post_id, 'igny8_clusters', array('fields' => 'ids'));
+
+ if (!empty($sectors)) {
+ // Get IGNY8 sector IDs from term meta
+ $igny8_sector_ids = array();
+ foreach ($sectors as $term_id) {
+ $igny8_sector_id = get_term_meta($term_id, '_igny8_sector_id', true);
+ if ($igny8_sector_id) {
+ $igny8_sector_ids[] = $igny8_sector_id;
+ }
+ }
+ if (!empty($igny8_sector_ids)) {
+ $post_data['sector_id'] = $igny8_sector_ids[0]; // Use first sector
+ }
+ }
+
+ if (!empty($clusters)) {
+ // Get IGNY8 cluster IDs from term meta
+ $igny8_cluster_ids = array();
+ foreach ($clusters as $term_id) {
+ $igny8_cluster_id = get_term_meta($term_id, '_igny8_cluster_id', true);
+ if ($igny8_cluster_id) {
+ $igny8_cluster_ids[] = $igny8_cluster_id;
+ }
+ }
+ if (!empty($igny8_cluster_ids)) {
+ $post_data['cluster_id'] = $igny8_cluster_ids[0]; // Use first cluster
+ }
+ }
+
+ // Check if post already has task ID
+ $existing_task_id = get_post_meta($post_id, '_igny8_task_id', true);
+
+ if ($existing_task_id && $action === 'update') {
+ // Update existing task
+ $response = $api->put("/writer/tasks/{$existing_task_id}/", $post_data);
+ } else {
+ // Create new task
+ $response = $api->post("/writer/tasks/", $post_data);
+ }
+
+ if ($response['success']) {
+ $task_id = $response['data']['id'] ?? $existing_task_id;
+
+ // Store task ID
+ update_post_meta($post_id, '_igny8_task_id', $task_id);
+ update_post_meta($post_id, '_igny8_last_synced', current_time('mysql'));
+
+ wp_send_json_success(array(
+ 'message' => $action === 'update' ? 'Post updated in IGNY8' : 'Post sent to IGNY8',
+ 'task_id' => $task_id
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Failed to send to IGNY8: ' . ($response['error'] ?? 'Unknown error')
+ ));
+ }
+ }
+}
+
+// Initialize
+new Igny8AdminColumns();
+
+// Register AJAX handler
+add_action('wp_ajax_igny8_send_to_igny8', array('Igny8AdminColumns', 'send_to_igny8'));
+
diff --git a/igny8-wp-integration-plugin/admin/class-admin.php b/igny8-wp-integration-plugin/admin/class-admin.php
new file mode 100644
index 00000000..423ff9f7
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/class-admin.php
@@ -0,0 +1,488 @@
+ 'boolean',
+ 'sanitize_callback' => array($this, 'sanitize_boolean'),
+ 'default' => 1
+ ));
+
+ register_setting('igny8_bridge_controls', 'igny8_enabled_post_types', array(
+ 'type' => 'array',
+ 'sanitize_callback' => array($this, 'sanitize_post_types'),
+ 'default' => array_keys(igny8_get_supported_post_types())
+ ));
+
+ register_setting('igny8_bridge_controls', 'igny8_enable_woocommerce', array(
+ 'type' => 'boolean',
+ 'sanitize_callback' => array($this, 'sanitize_boolean'),
+ 'default' => class_exists('WooCommerce') ? 1 : 0
+ ));
+
+ register_setting('igny8_bridge_controls', 'igny8_control_mode', array(
+ 'type' => 'string',
+ 'sanitize_callback' => array($this, 'sanitize_control_mode'),
+ 'default' => 'mirror'
+ ));
+
+ register_setting('igny8_bridge_controls', 'igny8_enabled_modules', array(
+ 'type' => 'array',
+ 'sanitize_callback' => array($this, 'sanitize_modules'),
+ 'default' => array_keys(igny8_get_available_modules())
+ ));
+ }
+
+ /**
+ * Enqueue admin scripts and styles
+ *
+ * @param string $hook Current admin page hook
+ */
+ public function enqueue_scripts($hook) {
+ // Enqueue on settings page
+ if ($hook === 'settings_page_igny8-settings') {
+ wp_enqueue_style(
+ 'igny8-admin-style',
+ IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
+ array(),
+ IGNY8_BRIDGE_VERSION
+ );
+
+ wp_enqueue_script(
+ 'igny8-admin-script',
+ IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
+ array('jquery'),
+ IGNY8_BRIDGE_VERSION,
+ true
+ );
+
+ wp_localize_script('igny8-admin-script', 'igny8Admin', array(
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('igny8_admin_nonce'),
+ ));
+ }
+
+ // Enqueue on post/page/product list pages
+ if (strpos($hook, 'edit.php') !== false) {
+ $screen = get_current_screen();
+ if ($screen && in_array($screen->post_type, array('post', 'page', 'product', ''))) {
+ wp_enqueue_style(
+ 'igny8-admin-style',
+ IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
+ array(),
+ IGNY8_BRIDGE_VERSION
+ );
+
+ wp_enqueue_script(
+ 'igny8-admin-script',
+ IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
+ array('jquery'),
+ IGNY8_BRIDGE_VERSION,
+ true
+ );
+
+ wp_localize_script('igny8-admin-script', 'igny8Admin', array(
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('igny8_admin_nonce'),
+ ));
+ }
+ }
+ }
+
+ /**
+ * Render settings page
+ */
+ public function render_settings_page() {
+ // Handle form submission
+ if (isset($_POST['igny8_connect']) && check_admin_referer('igny8_settings_nonce')) {
+ $this->handle_connection();
+ }
+
+ // Handle webhook secret regeneration
+ if (isset($_POST['igny8_regenerate_secret']) && check_admin_referer('igny8_regenerate_secret')) {
+ $new_secret = igny8_regenerate_webhook_secret();
+ add_settings_error(
+ 'igny8_settings',
+ 'igny8_secret_regenerated',
+ __('Webhook secret regenerated. Update it in your IGNY8 SaaS app settings.', 'igny8-bridge'),
+ 'updated'
+ );
+ }
+
+ // Include settings template
+ include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
+ }
+
+ /**
+ * Handle API connection
+ */
+ private function handle_connection() {
+ $email = sanitize_email($_POST['igny8_email'] ?? '');
+ $password = $_POST['igny8_password'] ?? '';
+
+ if (empty($email) || empty($password)) {
+ add_settings_error(
+ 'igny8_settings',
+ 'igny8_error',
+ __('Email and password are required.', 'igny8-bridge'),
+ 'error'
+ );
+ return;
+ }
+
+ $api = new Igny8API();
+
+ if ($api->login($email, $password)) {
+ update_option('igny8_email', $email);
+
+ // Try to get site ID (if available)
+ $site_response = $api->get('/system/sites/');
+ if ($site_response['success'] && !empty($site_response['results'])) {
+ $site = $site_response['results'][0];
+ update_option('igny8_site_id', $site['id']);
+ }
+
+ add_settings_error(
+ 'igny8_settings',
+ 'igny8_connected',
+ __('Successfully connected to IGNY8 API.', 'igny8-bridge'),
+ 'updated'
+ );
+ } else {
+ add_settings_error(
+ 'igny8_settings',
+ 'igny8_error',
+ __('Failed to connect to IGNY8 API. Please check your credentials.', 'igny8-bridge'),
+ 'error'
+ );
+ }
+ }
+
+ /**
+ * Test API connection (AJAX handler)
+ */
+ public static function test_connection() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations to test.'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ // Test with a simple API call
+ $response = $api->get('/planner/keywords/?page_size=1');
+
+ if ($response['success']) {
+ $checked_at = current_time('timestamp');
+ update_option('igny8_last_api_health_check', $checked_at);
+ wp_send_json_success(array(
+ 'message' => 'Connection successful',
+ 'data' => $response,
+ 'checked_at' => $checked_at
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Connection failed',
+ 'error' => $response['error']
+ ));
+ }
+ }
+
+ /**
+ * Sync posts to IGNY8 (AJAX handler)
+ */
+ public static function sync_posts() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $result = igny8_batch_sync_post_statuses();
+
+ wp_send_json_success(array(
+ 'message' => sprintf('Synced %d posts, %d failed', $result['synced'], $result['failed']),
+ 'data' => $result
+ ));
+ }
+
+ /**
+ * Sync taxonomies (AJAX handler)
+ */
+ public static function sync_taxonomies() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $api = new Igny8API();
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ // Sync sectors and clusters from IGNY8
+ $sectors_result = igny8_sync_igny8_sectors_to_wp();
+ $clusters_result = igny8_sync_igny8_clusters_to_wp();
+
+ wp_send_json_success(array(
+ 'message' => sprintf('Synced %d sectors, %d clusters',
+ $sectors_result['synced'] ?? 0,
+ $clusters_result['synced'] ?? 0
+ ),
+ 'data' => array(
+ 'sectors' => $sectors_result,
+ 'clusters' => $clusters_result
+ )
+ ));
+ }
+
+ /**
+ * Sync from IGNY8 (AJAX handler)
+ */
+ public static function sync_from_igny8() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $result = igny8_sync_igny8_tasks_to_wp();
+
+ if ($result['success']) {
+ wp_send_json_success(array(
+ 'message' => sprintf('Created %d posts, updated %d posts',
+ $result['created'],
+ $result['updated']
+ ),
+ 'data' => $result
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => $result['error'] ?? 'Sync failed'
+ ));
+ }
+ }
+
+ /**
+ * Collect and send site data (AJAX handler)
+ */
+ public static function collect_site_data() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $site_id = get_option('igny8_site_id');
+ if (!$site_id) {
+ wp_send_json_error(array('message' => 'Site ID not set'));
+ }
+
+ $result = igny8_send_site_data_to_igny8($site_id);
+
+ if ($result) {
+ wp_send_json_success(array(
+ 'message' => 'Site data collected and sent successfully',
+ 'data' => $result
+ ));
+ } else {
+ wp_send_json_error(array('message' => 'Failed to send site data'));
+ }
+ }
+
+ /**
+ * Get sync statistics (AJAX handler)
+ */
+ public static function get_stats() {
+ check_ajax_referer('igny8_admin_nonce', 'nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ global $wpdb;
+
+ // Count synced posts
+ $synced_posts = $wpdb->get_var("
+ SELECT COUNT(DISTINCT post_id)
+ FROM {$wpdb->postmeta}
+ WHERE meta_key = '_igny8_task_id'
+ ");
+
+ // Get last sync time
+ $last_sync = get_option('igny8_last_site_sync', 0);
+ $last_sync_formatted = $last_sync ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_sync) : 'Never';
+
+ wp_send_json_success(array(
+ 'synced_posts' => intval($synced_posts),
+ 'last_sync' => $last_sync_formatted
+ ));
+ }
+
+ /**
+ * Sanitize post types option
+ *
+ * @param mixed $value Raw value
+ * @return array
+ */
+ public function sanitize_post_types($value) {
+ $supported = array_keys(igny8_get_supported_post_types());
+
+ if (!is_array($value)) {
+ return $supported;
+ }
+
+ $clean = array();
+ foreach ($value as $post_type) {
+ $post_type = sanitize_key($post_type);
+ if (in_array($post_type, $supported, true)) {
+ $clean[] = $post_type;
+ }
+ }
+
+ return !empty($clean) ? $clean : $supported;
+ }
+
+ /**
+ * Sanitize boolean option
+ *
+ * @param mixed $value Raw value
+ * @return int
+ */
+ public function sanitize_boolean($value) {
+ return $value ? 1 : 0;
+ }
+
+ /**
+ * Sanitize control mode
+ *
+ * @param mixed $value Raw value
+ * @return string
+ */
+ public function sanitize_control_mode($value) {
+ $value = is_string($value) ? strtolower($value) : 'mirror';
+ return in_array($value, array('mirror', 'hybrid'), true) ? $value : 'mirror';
+ }
+
+ /**
+ * Sanitize module toggles
+ *
+ * @param mixed $value Raw value
+ * @return array
+ */
+ public function sanitize_modules($value) {
+ $supported = array_keys(igny8_get_available_modules());
+
+ if (!is_array($value)) {
+ return $supported;
+ }
+
+ $clean = array();
+ foreach ($value as $module) {
+ $module = sanitize_key($module);
+ if (in_array($module, $supported, true)) {
+ $clean[] = $module;
+ }
+ }
+
+ return !empty($clean) ? $clean : $supported;
+ }
+}
+
+// Register AJAX handlers
+add_action('wp_ajax_igny8_test_connection', array('Igny8Admin', 'test_connection'));
+add_action('wp_ajax_igny8_sync_posts', array('Igny8Admin', 'sync_posts'));
+add_action('wp_ajax_igny8_sync_taxonomies', array('Igny8Admin', 'sync_taxonomies'));
+add_action('wp_ajax_igny8_sync_from_igny8', array('Igny8Admin', 'sync_from_igny8'));
+add_action('wp_ajax_igny8_collect_site_data', array('Igny8Admin', 'collect_site_data'));
+add_action('wp_ajax_igny8_get_stats', array('Igny8Admin', 'get_stats'));
+
diff --git a/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php b/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php
new file mode 100644
index 00000000..d0d6ebcb
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php
@@ -0,0 +1,469 @@
+ admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('igny8_post_editor_nonce'),
+ ));
+ }
+
+ /**
+ * Render Planner Brief meta box
+ */
+ public function render_planner_brief_box($post) {
+ $task_id = get_post_meta($post->ID, '_igny8_task_id', true);
+ $brief = get_post_meta($post->ID, '_igny8_task_brief', true);
+ $brief_cached_at = get_post_meta($post->ID, '_igny8_brief_cached_at', true);
+ $cluster_id = get_post_meta($post->ID, '_igny8_cluster_id', true);
+
+ if (!$task_id && !$cluster_id) {
+ echo '';
+ _e('This post is not linked to an IGNY8 task or cluster.', 'igny8-bridge');
+ echo '
';
+ return;
+ }
+
+ wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ';
+ foreach ($keywords as $keyword) {
+ echo '' . esc_html(trim($keyword)) . '';
+ }
+ echo '';
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID, '_igny8_task_id', true);
+ $optimizer_job_id = get_post_meta($post->ID, '_igny8_optimizer_job_id', true);
+ $optimizer_status = get_post_meta($post->ID, '_igny8_optimizer_status', true);
+
+ if (!$task_id) {
+ echo '';
+ _e('This post is not linked to an IGNY8 task.', 'igny8-bridge');
+ echo '
';
+ return;
+ }
+
+ wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+ $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
+
+ if (!$post_id || !$task_id) {
+ wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ // Try to fetch from Planner first
+ $response = $api->get("/planner/tasks/{$task_id}/brief/");
+
+ if (!$response['success']) {
+ // Fallback to Writer brief
+ $response = $api->get("/writer/tasks/{$task_id}/brief/");
+ }
+
+ if ($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'));
+
+ wp_send_json_success(array(
+ 'message' => 'Brief fetched successfully',
+ 'brief' => $response['data']
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Failed to fetch brief: ' . ($response['error'] ?? 'Unknown error')
+ ));
+ }
+ }
+
+ /**
+ * Refresh Planner task (AJAX handler)
+ */
+ public static function refresh_planner_task() {
+ check_ajax_referer('igny8_post_editor_nonce', 'nonce');
+
+ if (!current_user_can('edit_posts')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+ $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
+
+ if (!$post_id || !$task_id) {
+ wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ $response = $api->post("/planner/tasks/{$task_id}/refresh/", array(
+ 'wordpress_post_id' => $post_id,
+ 'reason' => 'reoptimize',
+ 'notes' => 'Requested refresh from WordPress editor'
+ ));
+
+ if ($response['success']) {
+ wp_send_json_success(array(
+ 'message' => 'Refresh requested successfully',
+ 'data' => $response['data']
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Failed to request refresh: ' . ($response['error'] ?? 'Unknown error')
+ ));
+ }
+ }
+
+ /**
+ * Create Optimizer job (AJAX handler)
+ */
+ public static function create_optimizer_job() {
+ check_ajax_referer('igny8_post_editor_nonce', 'nonce');
+
+ if (!current_user_can('edit_posts')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+ $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
+ $job_type = isset($_POST['job_type']) ? sanitize_text_field($_POST['job_type']) : 'audit';
+ $priority = isset($_POST['priority']) ? sanitize_text_field($_POST['priority']) : 'normal';
+
+ if (!$post_id || !$task_id) {
+ wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ $response = $api->post("/optimizer/jobs/", array(
+ 'post_id' => $post_id,
+ 'task_id' => $task_id,
+ 'job_type' => $job_type,
+ 'priority' => $priority
+ ));
+
+ if ($response['success'] && !empty($response['data'])) {
+ $job_id = $response['data']['id'] ?? $response['data']['job_id'] ?? null;
+
+ if ($job_id) {
+ update_post_meta($post_id, '_igny8_optimizer_job_id', $job_id);
+ update_post_meta($post_id, '_igny8_optimizer_status', $response['data']['status'] ?? 'pending');
+ update_post_meta($post_id, '_igny8_optimizer_job_created_at', current_time('mysql'));
+ }
+
+ wp_send_json_success(array(
+ 'message' => 'Optimizer job created successfully',
+ 'job_id' => $job_id,
+ 'data' => $response['data']
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Failed to create optimizer job: ' . ($response['error'] ?? 'Unknown error')
+ ));
+ }
+ }
+
+ /**
+ * Get Optimizer job status (AJAX handler)
+ */
+ public static function get_optimizer_status() {
+ check_ajax_referer('igny8_post_editor_nonce', 'nonce');
+
+ if (!current_user_can('edit_posts')) {
+ wp_send_json_error(array('message' => 'Unauthorized'));
+ }
+
+ if (!igny8_is_connection_enabled()) {
+ wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
+ }
+
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
+ $job_id = isset($_POST['job_id']) ? intval($_POST['job_id']) : 0;
+
+ if (!$post_id || !$job_id) {
+ wp_send_json_error(array('message' => 'Invalid post ID or job ID'));
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ wp_send_json_error(array('message' => 'Not authenticated'));
+ }
+
+ $response = $api->get("/optimizer/jobs/{$job_id}/");
+
+ if ($response['success'] && !empty($response['data'])) {
+ $status = $response['data']['status'] ?? 'unknown';
+ update_post_meta($post_id, '_igny8_optimizer_status', $status);
+
+ if (!empty($response['data']['score_changes'])) {
+ update_post_meta($post_id, '_igny8_optimizer_score_changes', $response['data']['score_changes']);
+ }
+
+ if (!empty($response['data']['recommendations'])) {
+ update_post_meta($post_id, '_igny8_optimizer_recommendations', $response['data']['recommendations']);
+ }
+
+ wp_send_json_success(array(
+ 'message' => 'Status retrieved successfully',
+ 'status' => $status,
+ 'data' => $response['data']
+ ));
+ } else {
+ wp_send_json_error(array(
+ 'message' => 'Failed to get status: ' . ($response['error'] ?? 'Unknown error')
+ ));
+ }
+ }
+}
+
+// Initialize
+new Igny8PostMetaBoxes();
+
diff --git a/igny8-wp-integration-plugin/admin/settings.php b/igny8-wp-integration-plugin/admin/settings.php
new file mode 100644
index 00000000..4cbe9d46
--- /dev/null
+++ b/igny8-wp-integration-plugin/admin/settings.php
@@ -0,0 +1,532 @@
+ 10));
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+ |
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/igny8-wp-integration-plugin/data/link-graph.php b/igny8-wp-integration-plugin/data/link-graph.php
new file mode 100644
index 00000000..55011eff
--- /dev/null
+++ b/igny8-wp-integration-plugin/data/link-graph.php
@@ -0,0 +1,192 @@
+post_content;
+ $source_url = get_permalink($post_id);
+ $site_url = get_site_url();
+ $links = array();
+
+ // Match all anchor tags with href attributes
+ preg_match_all('/]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/is', $content, $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $match) {
+ $href = $match[1];
+ $anchor = strip_tags($match[2]);
+
+ // Skip empty anchors
+ if (empty(trim($anchor))) {
+ continue;
+ }
+
+ // Only process internal links
+ if (strpos($href, $site_url) === 0 || strpos($href, '/') === 0) {
+ // Convert relative URLs to absolute
+ if (strpos($href, '/') === 0 && strpos($href, '//') !== 0) {
+ $href = $site_url . $href;
+ }
+
+ // Normalize URL (remove trailing slash, fragments, query params for matching)
+ $target_url = rtrim($href, '/');
+
+ // Skip if source and target are the same
+ if ($source_url === $target_url) {
+ continue;
+ }
+
+ $links[] = array(
+ 'source_url' => $source_url,
+ 'target_url' => $target_url,
+ 'anchor' => trim($anchor),
+ 'post_id' => $post_id
+ );
+ }
+ }
+
+ return $links;
+}
+
+/**
+ * Extract link graph from all posts
+ *
+ * @param array $post_ids Optional array of post IDs to process. If empty, processes all enabled posts.
+ * @return array Link graph array
+ */
+function igny8_extract_link_graph($post_ids = array()) {
+ // Skip if connection is disabled
+ if (!igny8_is_connection_enabled()) {
+ return array();
+ }
+
+ if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
+ return array();
+ }
+
+ $enabled_post_types = igny8_get_enabled_post_types();
+
+ if (empty($post_ids)) {
+ // Get all published posts of enabled types
+ $query_args = array(
+ 'post_type' => $enabled_post_types,
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'suppress_filters' => true
+ );
+
+ $post_ids = get_posts($query_args);
+ }
+
+ $link_graph = array();
+ $processed = 0;
+
+ foreach ($post_ids as $post_id) {
+ $links = igny8_extract_post_links($post_id);
+
+ if (!empty($links)) {
+ $link_graph = array_merge($link_graph, $links);
+ }
+
+ $processed++;
+
+ // Limit processing to prevent timeout (can be increased or made configurable)
+ if ($processed >= 1000) {
+ break;
+ }
+ }
+
+ return $link_graph;
+}
+
+/**
+ * Send link graph to IGNY8 Linker module
+ *
+ * @param int $site_id IGNY8 site ID
+ * @param array $link_graph Link graph array (optional, will extract if not provided)
+ * @return array|false Response data or false on failure
+ */
+function igny8_send_link_graph_to_igny8($site_id, $link_graph = null) {
+ // Skip if connection is disabled
+ if (!igny8_is_connection_enabled()) {
+ return false;
+ }
+
+ if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
+ return false;
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ return false;
+ }
+
+ // Extract link graph if not provided
+ if ($link_graph === null) {
+ $link_graph = igny8_extract_link_graph();
+ }
+
+ if (empty($link_graph)) {
+ return array('success' => true, 'message' => 'No links found', 'links_count' => 0);
+ }
+
+ // Send in batches (max 500 links per batch)
+ $batch_size = 500;
+ $batches = array_chunk($link_graph, $batch_size);
+ $total_sent = 0;
+ $errors = array();
+
+ foreach ($batches as $batch) {
+ $response = $api->post("/linker/link-map/", array(
+ 'site_id' => $site_id,
+ 'links' => $batch,
+ 'total_links' => count($link_graph),
+ 'batch_number' => count($batches) > 1 ? (count($batches) - count($batches) + array_search($batch, $batches) + 1) : 1,
+ 'total_batches' => count($batches)
+ ));
+
+ if ($response['success']) {
+ $total_sent += count($batch);
+ } else {
+ $errors[] = $response['error'] ?? 'Unknown error';
+ }
+ }
+
+ if ($total_sent > 0) {
+ update_option('igny8_last_link_graph_sync', current_time('timestamp'));
+ update_option('igny8_last_link_graph_count', $total_sent);
+
+ return array(
+ 'success' => true,
+ 'links_sent' => $total_sent,
+ 'total_links' => count($link_graph),
+ 'batches' => count($batches),
+ 'errors' => $errors
+ );
+ }
+
+ return false;
+}
+
diff --git a/igny8-wp-integration-plugin/data/semantic-mapping.php b/igny8-wp-integration-plugin/data/semantic-mapping.php
new file mode 100644
index 00000000..1aeb58aa
--- /dev/null
+++ b/igny8-wp-integration-plugin/data/semantic-mapping.php
@@ -0,0 +1,225 @@
+ false, 'error' => 'Connection disabled');
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ return array('success' => false, 'error' => 'Not authenticated');
+ }
+
+ // Extract semantic structure from site data
+ $semantic_map = array(
+ 'sectors' => array(),
+ 'clusters' => array(),
+ 'keywords' => array()
+ );
+
+ // Map taxonomies to sectors
+ foreach ($site_data['taxonomies'] as $tax_name => $tax_data) {
+ if ($tax_data['taxonomy']['hierarchical']) {
+ // Hierarchical taxonomies (categories) become sectors
+ $sector = array(
+ 'name' => $tax_data['taxonomy']['label'],
+ 'slug' => $tax_data['taxonomy']['name'],
+ 'description' => $tax_data['taxonomy']['description'],
+ 'source' => 'wordpress_taxonomy',
+ 'source_id' => $tax_name
+ );
+
+ // Map terms to clusters
+ $clusters = array();
+ foreach ($tax_data['terms'] as $term) {
+ $clusters[] = array(
+ 'name' => $term['name'],
+ 'slug' => $term['slug'],
+ 'description' => $term['description'],
+ 'source' => 'wordpress_term',
+ 'source_id' => $term['id']
+ );
+
+ // Extract keywords from posts in this term
+ $keywords = igny8_extract_keywords_from_term_posts($term['id'], $tax_name);
+ $semantic_map['keywords'] = array_merge($semantic_map['keywords'], $keywords);
+ }
+
+ $sector['clusters'] = $clusters;
+ $semantic_map['sectors'][] = $sector;
+ }
+ }
+
+ // Map WooCommerce product categories to sectors
+ if (!empty($site_data['product_categories'])) {
+ $product_sector = array(
+ 'name' => 'Products',
+ 'slug' => 'products',
+ 'description' => 'WooCommerce product categories',
+ 'source' => 'woocommerce',
+ 'clusters' => array()
+ );
+
+ foreach ($site_data['product_categories'] as $category) {
+ $product_sector['clusters'][] = array(
+ 'name' => $category['name'],
+ 'slug' => $category['slug'],
+ 'description' => $category['description'],
+ 'source' => 'woocommerce_category',
+ 'source_id' => $category['id']
+ );
+ }
+
+ $semantic_map['sectors'][] = $product_sector;
+ }
+
+ // Send semantic map to IGNY8
+ $response = $api->post("/planner/sites/{$site_id}/semantic-map/", array(
+ 'semantic_map' => $semantic_map,
+ 'site_data' => $site_data
+ ));
+
+ return $response;
+}
+
+/**
+ * Extract keywords from posts associated with a taxonomy term
+ *
+ * @param int $term_id Term ID
+ * @param string $taxonomy Taxonomy name
+ * @return array Formatted keywords array
+ */
+function igny8_extract_keywords_from_term_posts($term_id, $taxonomy) {
+ $args = array(
+ 'post_type' => 'any',
+ 'posts_per_page' => -1,
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => $taxonomy,
+ 'field' => 'term_id',
+ 'terms' => $term_id
+ )
+ )
+ );
+
+ $query = new WP_Query($args);
+ $keywords = array();
+
+ if ($query->have_posts()) {
+ while ($query->have_posts()) {
+ $query->the_post();
+
+ // Extract keywords from post title and content
+ $title_words = str_word_count(get_the_title(), 1);
+ $content_words = str_word_count(strip_tags(get_the_content()), 1);
+
+ // Combine and get unique keywords
+ $all_words = array_merge($title_words, $content_words);
+ $unique_words = array_unique(array_map('strtolower', $all_words));
+
+ // Filter out common words (stop words)
+ $stop_words = array('the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by');
+ $keywords = array_merge($keywords, array_diff($unique_words, $stop_words));
+ }
+ wp_reset_postdata();
+ }
+
+ // Format keywords
+ $formatted_keywords = array();
+ foreach (array_unique($keywords) as $keyword) {
+ if (strlen($keyword) > 3) { // Only keywords longer than 3 characters
+ $formatted_keywords[] = array(
+ 'keyword' => $keyword,
+ 'source' => 'wordpress_post',
+ 'source_term_id' => $term_id
+ );
+ }
+ }
+
+ return $formatted_keywords;
+}
+
+/**
+ * Complete workflow: Fetch site data → Map to semantic strategy → Restructure content
+ *
+ * @param int $site_id IGNY8 site ID
+ * @return array|false Analysis result or false on failure
+ */
+function igny8_analyze_and_restructure_site($site_id) {
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ return false;
+ }
+
+ // Step 1: Collect all site data
+ $site_data = igny8_collect_site_data();
+
+ // Step 2: Send to IGNY8 for analysis
+ $analysis_response = $api->post("/system/sites/{$site_id}/analyze/", array(
+ 'site_data' => $site_data,
+ 'analysis_type' => 'full_site_restructure'
+ ));
+
+ if (!$analysis_response['success']) {
+ return false;
+ }
+
+ $analysis_id = $analysis_response['data']['analysis_id'] ?? null;
+
+ // Step 3: Map to semantic strategy
+ $mapping_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
+
+ if (!$mapping_response['success']) {
+ return false;
+ }
+
+ // Step 4: Get restructuring recommendations
+ $recommendations_response = $api->get("/system/sites/{$site_id}/recommendations/");
+
+ if (!$recommendations_response['success']) {
+ return false;
+ }
+
+ // Get keywords count from mapping response
+ $keywords_count = 0;
+ if (isset($mapping_response['data']['keywords'])) {
+ $keywords_count = count($mapping_response['data']['keywords']);
+ }
+
+ return array(
+ 'analysis_id' => $analysis_id,
+ 'semantic_map' => $mapping_response['data'] ?? null,
+ 'recommendations' => $recommendations_response['data'] ?? null,
+ 'site_data_summary' => array(
+ 'total_posts' => count($site_data['posts']),
+ 'total_taxonomies' => count($site_data['taxonomies']),
+ 'total_products' => count($site_data['products'] ?? array()),
+ 'total_keywords' => $keywords_count
+ )
+ );
+}
+
diff --git a/igny8-wp-integration-plugin/data/site-collection.php b/igny8-wp-integration-plugin/data/site-collection.php
new file mode 100644
index 00000000..e60f90f2
--- /dev/null
+++ b/igny8-wp-integration-plugin/data/site-collection.php
@@ -0,0 +1,579 @@
+ 'publish',
+ 'after' => null,
+ 'max_pages' => 5,
+ );
+ $args = wp_parse_args($args, $defaults);
+
+ $post_type_object = get_post_type_object($post_type);
+ $rest_base = ($post_type_object && !empty($post_type_object->rest_base)) ? $post_type_object->rest_base : $post_type;
+
+ $base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
+
+ $query_args = array(
+ 'per_page' => min($per_page, 100),
+ 'status' => $args['status'],
+ 'orderby' => 'modified',
+ 'order' => 'desc',
+ );
+
+ if (!empty($args['after'])) {
+ $query_args['after'] = gmdate('c', $args['after']);
+ }
+
+ $formatted_posts = array();
+ $page = 1;
+
+ do {
+ $query_args['page'] = $page;
+ $response = wp_remote_get(add_query_arg($query_args, $base_url));
+
+ if (is_wp_error($response)) {
+ break;
+ }
+
+ $posts = json_decode(wp_remote_retrieve_body($response), true);
+
+ if (!is_array($posts) || empty($posts)) {
+ break;
+ }
+
+ foreach ($posts as $post) {
+ $content = $post['content']['rendered'] ?? '';
+ $word_count = str_word_count(strip_tags($content));
+
+ $formatted_posts[] = array(
+ 'id' => $post['id'],
+ 'title' => html_entity_decode($post['title']['rendered'] ?? ''),
+ 'content' => $content,
+ 'excerpt' => $post['excerpt']['rendered'] ?? '',
+ 'status' => $post['status'] ?? 'draft',
+ 'url' => $post['link'] ?? '',
+ 'published' => $post['date'] ?? '',
+ 'modified' => $post['modified'] ?? '',
+ 'author' => $post['author'] ?? 0,
+ 'post_type' => $post['type'] ?? $post_type,
+ 'taxonomies' => array(
+ 'categories' => $post['categories'] ?? array(),
+ 'tags' => $post['tags'] ?? array(),
+ ),
+ 'meta' => array(
+ 'word_count' => $word_count,
+ 'reading_time' => $word_count ? ceil($word_count / 200) : 0,
+ 'featured_media' => $post['featured_media'] ?? 0,
+ )
+ );
+ }
+
+ if (count($posts) < $query_args['per_page']) {
+ break;
+ }
+
+ $page++;
+ } while ($page <= $args['max_pages']);
+
+ return $formatted_posts;
+}
+
+/**
+ * Fetch all available post types from WordPress
+ *
+ * @return array|false Post types array or false on failure
+ */
+function igny8_fetch_all_post_types() {
+ $wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/types');
+
+ if (is_wp_error($wp_response)) {
+ return false;
+ }
+
+ $types = json_decode(wp_remote_retrieve_body($wp_response), true);
+
+ if (!is_array($types)) {
+ return false;
+ }
+
+ $post_types = array();
+ foreach ($types as $type_name => $type_data) {
+ if ($type_data['public']) {
+ $post_types[] = array(
+ 'name' => $type_name,
+ 'label' => $type_data['name'],
+ 'description' => $type_data['description'] ?? '',
+ 'rest_base' => $type_data['rest_base'] ?? $type_name
+ );
+ }
+ }
+
+ return $post_types;
+}
+
+/**
+ * Fetch all posts from all post types
+ *
+ * @param int $per_page Posts per page
+ * @return array All posts
+ */
+function igny8_fetch_all_wordpress_posts($per_page = 100) {
+ $post_types = igny8_fetch_all_post_types();
+
+ if (!$post_types) {
+ return array();
+ }
+
+ $all_posts = array();
+ foreach ($post_types as $type) {
+ $posts = igny8_fetch_wordpress_posts($type['name'], $per_page);
+ if ($posts) {
+ $all_posts = array_merge($all_posts, $posts);
+ }
+ }
+
+ return $all_posts;
+}
+
+/**
+ * Fetch all taxonomies from WordPress
+ *
+ * @return array|false Taxonomies array or false on failure
+ */
+function igny8_fetch_wordpress_taxonomies() {
+ $wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/taxonomies');
+
+ if (is_wp_error($wp_response)) {
+ return false;
+ }
+
+ $taxonomies = json_decode(wp_remote_retrieve_body($wp_response), true);
+
+ if (!is_array($taxonomies)) {
+ return false;
+ }
+
+ $formatted_taxonomies = array();
+ foreach ($taxonomies as $tax_name => $tax_data) {
+ if ($tax_data['public']) {
+ $formatted_taxonomies[] = array(
+ 'name' => $tax_name,
+ 'label' => $tax_data['name'],
+ 'description' => $tax_data['description'] ?? '',
+ 'hierarchical' => $tax_data['hierarchical'],
+ 'rest_base' => $tax_data['rest_base'] ?? $tax_name,
+ 'object_types' => $tax_data['types'] ?? array()
+ );
+ }
+ }
+
+ return $formatted_taxonomies;
+}
+
+/**
+ * Fetch all terms for a specific taxonomy
+ *
+ * @param string $taxonomy Taxonomy name
+ * @param int $per_page Terms per page
+ * @return array|false Formatted terms array or false on failure
+ */
+function igny8_fetch_taxonomy_terms($taxonomy, $per_page = 100) {
+ $taxonomy_obj = get_taxonomy($taxonomy);
+ $rest_base = ($taxonomy_obj && !empty($taxonomy_obj->rest_base)) ? $taxonomy_obj->rest_base : $taxonomy;
+
+ $base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
+
+ $formatted_terms = array();
+ $page = 1;
+
+ do {
+ $response = wp_remote_get(add_query_arg(array(
+ 'per_page' => min($per_page, 100),
+ 'page' => $page
+ ), $base_url));
+
+ if (is_wp_error($response)) {
+ break;
+ }
+
+ $terms = json_decode(wp_remote_retrieve_body($response), true);
+
+ if (!is_array($terms) || empty($terms)) {
+ break;
+ }
+
+ foreach ($terms as $term) {
+ $formatted_terms[] = array(
+ 'id' => $term['id'],
+ 'name' => $term['name'],
+ 'slug' => $term['slug'],
+ 'description' => $term['description'] ?? '',
+ 'count' => $term['count'],
+ 'parent' => $term['parent'] ?? 0,
+ 'taxonomy' => $taxonomy,
+ 'url' => $term['link'] ?? ''
+ );
+ }
+
+ if (count($terms) < min($per_page, 100)) {
+ break;
+ }
+
+ $page++;
+ } while (true);
+
+ return $formatted_terms;
+}
+
+/**
+ * Fetch all terms from all taxonomies
+ *
+ * @param int $per_page Terms per page
+ * @return array All terms organized by taxonomy
+ */
+function igny8_fetch_all_taxonomy_terms($per_page = 100) {
+ $taxonomies = igny8_fetch_wordpress_taxonomies();
+
+ if (!$taxonomies) {
+ return array();
+ }
+
+ $all_terms = array();
+ foreach ($taxonomies as $taxonomy) {
+ $terms = igny8_fetch_taxonomy_terms($taxonomy['rest_base'], $per_page);
+ if ($terms) {
+ $all_terms[$taxonomy['name']] = $terms;
+ }
+ }
+
+ return $all_terms;
+}
+
+/**
+ * Collect all WordPress site data for IGNY8 semantic mapping
+ *
+ * @return array Complete site data
+ */
+function igny8_collect_site_data($args = array()) {
+ // Skip if connection is disabled
+ if (!igny8_is_connection_enabled()) {
+ return array('disabled' => true, 'reason' => 'connection_disabled');
+ }
+
+ if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('sites')) {
+ return array('disabled' => true);
+ }
+
+ $settings = igny8_get_site_scan_settings($args);
+
+ $site_data = array(
+ 'site_url' => get_site_url(),
+ 'site_name' => get_bloginfo('name'),
+ 'site_description' => get_bloginfo('description'),
+ 'collected_at' => current_time('mysql'),
+ 'settings' => $settings,
+ 'posts' => array(),
+ 'taxonomies' => array(),
+ 'products' => array(),
+ 'product_categories' => array(),
+ 'product_attributes' => array()
+ );
+
+ foreach ((array) $settings['post_types'] as $post_type) {
+ if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
+ continue;
+ }
+
+ $posts = igny8_fetch_wordpress_posts($post_type, $settings['per_page'], array(
+ 'after' => $settings['since'],
+ 'status' => 'publish'
+ ));
+
+ if ($posts) {
+ $site_data['posts'] = array_merge($site_data['posts'], $posts);
+ }
+ }
+
+ $tracked_taxonomies = array('category', 'post_tag', 'igny8_sectors', 'igny8_clusters');
+ foreach ($tracked_taxonomies as $taxonomy) {
+ if (!taxonomy_exists($taxonomy)) {
+ continue;
+ }
+
+ $terms = igny8_fetch_taxonomy_terms($taxonomy, 100);
+ if ($terms) {
+ $tax_obj = get_taxonomy($taxonomy);
+ $site_data['taxonomies'][$taxonomy] = array(
+ 'taxonomy' => array(
+ 'name' => $taxonomy,
+ 'label' => $tax_obj ? $tax_obj->label : $taxonomy,
+ 'description' => $tax_obj->description ?? '',
+ 'hierarchical' => $tax_obj ? $tax_obj->hierarchical : false,
+ ),
+ 'terms' => $terms
+ );
+ }
+ }
+
+ if (!empty($settings['include_products']) && function_exists('igny8_is_woocommerce_active') && igny8_is_woocommerce_active()) {
+ require_once IGNY8_BRIDGE_PLUGIN_DIR . 'data/woocommerce.php';
+
+ $products = igny8_fetch_woocommerce_products(100);
+ if ($products) {
+ $site_data['products'] = $products;
+ }
+
+ $product_categories = igny8_fetch_product_categories(100);
+ if ($product_categories) {
+ $site_data['product_categories'] = $product_categories;
+ }
+
+ $product_attributes = igny8_fetch_product_attributes();
+ if ($product_attributes) {
+ $site_data['product_attributes'] = $product_attributes;
+ }
+ }
+
+ // Extract link graph if Linker module is enabled
+ if (function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
+ $post_ids = wp_list_pluck($site_data['posts'], 'id');
+ $link_graph = igny8_extract_link_graph($post_ids);
+
+ if (!empty($link_graph)) {
+ $site_data['link_graph'] = $link_graph;
+ }
+ }
+
+ $site_data['summary'] = array(
+ 'posts' => count($site_data['posts']),
+ 'taxonomies' => count($site_data['taxonomies']),
+ 'products' => count($site_data['products']),
+ 'links' => isset($site_data['link_graph']) ? count($site_data['link_graph']) : 0
+ );
+
+ update_option('igny8_last_site_snapshot', array(
+ 'timestamp' => current_time('timestamp'),
+ 'summary' => $site_data['summary']
+ ));
+
+ return $site_data;
+}
+
+/**
+ * Send WordPress site data to IGNY8 for semantic strategy mapping
+ *
+ * @param int $site_id IGNY8 site ID
+ * @return array|false Response data or false on failure
+ */
+function igny8_send_site_data_to_igny8($site_id, $site_data = null, $args = array()) {
+ // Skip if connection is disabled
+ if (!igny8_is_connection_enabled()) {
+ return false;
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ return false;
+ }
+
+ // Collect all site data if not provided
+ if (empty($site_data)) {
+ $site_data = igny8_collect_site_data($args);
+ }
+
+ if (empty($site_data) || isset($site_data['disabled'])) {
+ return false;
+ }
+
+ // Send to IGNY8 API
+ $response = $api->post("/system/sites/{$site_id}/import/", array(
+ 'site_data' => $site_data,
+ 'import_type' => $args['mode'] ?? 'full_site_scan'
+ ));
+
+ if ($response['success']) {
+ // Store import ID for tracking
+ update_option('igny8_last_site_import_id', $response['data']['import_id'] ?? null);
+ update_option('igny8_last_site_sync', current_time('timestamp'));
+
+ // Send link graph separately to Linker module if available
+ if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
+ $link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
+ if ($link_result) {
+ error_log(sprintf('IGNY8: Sent %d links to Linker module', $link_result['links_sent'] ?? 0));
+ }
+ }
+
+ return $response['data'];
+ } else {
+ error_log("IGNY8: Failed to send site data: " . ($response['error'] ?? 'Unknown error'));
+ return false;
+ }
+}
+
+/**
+ * Sync only changed posts/taxonomies since last sync
+ *
+ * @param int $site_id IGNY8 site ID
+ * @return array|false Sync result or false on failure
+ */
+function igny8_sync_incremental_site_data($site_id, $settings = array()) {
+ // Skip if connection is disabled
+ if (!igny8_is_connection_enabled()) {
+ return array('synced' => 0, 'message' => 'Connection disabled');
+ }
+
+ $api = new Igny8API();
+
+ if (!$api->is_authenticated()) {
+ return false;
+ }
+
+ $settings = igny8_get_site_scan_settings(wp_parse_args($settings, array('mode' => 'incremental')));
+ $since = $settings['since'] ?? intval(get_option('igny8_last_site_sync', 0));
+
+ $formatted_posts = array();
+
+ foreach ((array) $settings['post_types'] as $post_type) {
+ if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
+ continue;
+ }
+
+ $query_args = array(
+ 'post_type' => $post_type,
+ 'post_status' => array('publish', 'pending', 'draft', 'future'),
+ 'posts_per_page' => -1,
+ 'orderby' => 'modified',
+ 'order' => 'DESC',
+ 'suppress_filters' => true,
+ );
+
+ if ($since) {
+ $query_args['date_query'] = array(
+ array(
+ 'column' => 'post_modified_gmt',
+ 'after' => gmdate('Y-m-d H:i:s', $since)
+ )
+ );
+ }
+
+ $posts = get_posts($query_args);
+
+ foreach ($posts as $post) {
+ $word_count = str_word_count(strip_tags($post->post_content));
+
+ $formatted_posts[] = array(
+ 'id' => $post->ID,
+ 'title' => get_the_title($post),
+ 'content' => $post->post_content,
+ 'status' => $post->post_status,
+ 'modified' => $post->post_modified_gmt,
+ 'post_type' => $post->post_type,
+ 'url' => get_permalink($post),
+ 'taxonomies' => array(
+ 'categories' => wp_get_post_terms($post->ID, 'category', array('fields' => 'ids')),
+ 'tags' => wp_get_post_terms($post->ID, 'post_tag', array('fields' => 'ids')),
+ ),
+ 'meta' => array(
+ 'task_id' => get_post_meta($post->ID, '_igny8_task_id', true),
+ 'cluster_id' => get_post_meta($post->ID, '_igny8_cluster_id', true),
+ 'sector_id' => get_post_meta($post->ID, '_igny8_sector_id', true),
+ 'word_count' => $word_count,
+ )
+ );
+ }
+ }
+
+ if (empty($formatted_posts)) {
+ return array('synced' => 0, 'message' => 'No changes since last sync');
+ }
+
+ $response = $api->post("/system/sites/{$site_id}/sync/", array(
+ 'posts' => $formatted_posts,
+ 'sync_type' => 'incremental',
+ 'last_sync' => $since,
+ 'post_types' => $settings['post_types']
+ ));
+
+ if ($response['success']) {
+ update_option('igny8_last_site_sync', current_time('timestamp'));
+ update_option('igny8_last_incremental_site_sync', array(
+ 'timestamp' => current_time('timestamp'),
+ 'count' => count($formatted_posts)
+ ));
+
+ return array(
+ 'synced' => count($formatted_posts),
+ 'message' => 'Incremental sync completed'
+ );
+ }
+
+ return false;
+}
+
+/**
+ * Run a full site scan and semantic mapping
+ *
+ * @param int $site_id IGNY8 site ID
+ * @param array $settings Scan settings
+ * @return array|false
+ */
+function igny8_perform_full_site_scan($site_id, $settings = array()) {
+ $site_data = igny8_collect_site_data($settings);
+
+ if (empty($site_data) || isset($site_data['disabled'])) {
+ return false;
+ }
+
+ $import = igny8_send_site_data_to_igny8($site_id, $site_data, array('mode' => 'full_site_scan'));
+
+ if (!$import) {
+ return false;
+ }
+
+ update_option('igny8_last_full_site_scan', current_time('timestamp'));
+
+ // Map to semantic strategy (requires Planner module)
+ if (!function_exists('igny8_is_module_enabled') || igny8_is_module_enabled('planner')) {
+ $map_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
+ if (!empty($map_response['success'])) {
+ update_option('igny8_last_semantic_map', current_time('timestamp'));
+ update_option('igny8_last_semantic_map_summary', array(
+ 'sectors' => count($map_response['data']['sectors'] ?? array()),
+ 'keywords' => count($map_response['data']['keywords'] ?? array())
+ ));
+ }
+ }
+
+ // Send link graph to Linker module if available
+ if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
+ $link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
+ if ($link_result) {
+ error_log(sprintf('IGNY8: Sent %d links to Linker module during full scan', $link_result['links_sent'] ?? 0));
+ }
+ }
+
+ return $import;
+}
+
diff --git a/igny8-wp-integration-plugin/data/woocommerce.php b/igny8-wp-integration-plugin/data/woocommerce.php
new file mode 100644
index 00000000..7f01218e
--- /dev/null
+++ b/igny8-wp-integration-plugin/data/woocommerce.php
@@ -0,0 +1,226 @@
+ $headers
+ ));
+
+ if (is_wp_error($wp_response)) {
+ return false;
+ }
+
+ $products = json_decode(wp_remote_retrieve_body($wp_response), true);
+
+ if (!is_array($products)) {
+ return false;
+ }
+
+ $formatted_products = array();
+ foreach ($products as $product) {
+ $formatted_products[] = array(
+ 'id' => $product['id'],
+ 'name' => $product['name'],
+ 'slug' => $product['slug'],
+ 'sku' => $product['sku'],
+ 'type' => $product['type'],
+ 'status' => $product['status'],
+ 'description' => $product['description'],
+ 'short_description' => $product['short_description'],
+ 'price' => $product['price'],
+ 'regular_price' => $product['regular_price'],
+ 'sale_price' => $product['sale_price'],
+ 'on_sale' => $product['on_sale'],
+ 'stock_status' => $product['stock_status'],
+ 'stock_quantity' => $product['stock_quantity'],
+ 'categories' => $product['categories'] ?? array(),
+ 'tags' => $product['tags'] ?? array(),
+ 'images' => $product['images'] ?? array(),
+ 'attributes' => $product['attributes'] ?? array(),
+ 'variations' => $product['variations'] ?? array(),
+ 'url' => $product['permalink']
+ );
+ }
+
+ return $formatted_products;
+}
+
+/**
+ * Fetch WooCommerce product categories
+ *
+ * @param int $per_page Categories per page
+ * @return array|false Formatted categories array or false on failure
+ */
+function igny8_fetch_product_categories($per_page = 100) {
+ if (!igny8_is_woocommerce_active()) {
+ return false;
+ }
+
+ $consumer_key = get_option('woocommerce_api_consumer_key', '');
+ $consumer_secret = get_option('woocommerce_api_consumer_secret', '');
+
+ $headers = array();
+ if ($consumer_key && $consumer_secret) {
+ $headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
+ }
+
+ $wp_response = wp_remote_get(sprintf(
+ '%s/wp-json/wc/v3/products/categories?per_page=%d',
+ get_site_url(),
+ $per_page
+ ), array(
+ 'headers' => $headers
+ ));
+
+ if (is_wp_error($wp_response)) {
+ return false;
+ }
+
+ $categories = json_decode(wp_remote_retrieve_body($wp_response), true);
+
+ if (!is_array($categories)) {
+ return false;
+ }
+
+ $formatted_categories = array();
+ foreach ($categories as $category) {
+ $formatted_categories[] = array(
+ 'id' => $category['id'],
+ 'name' => $category['name'],
+ 'slug' => $category['slug'],
+ 'description' => $category['description'] ?? '',
+ 'count' => $category['count'],
+ 'parent' => $category['parent'] ?? 0,
+ 'image' => $category['image']['src'] ?? null
+ );
+ }
+
+ return $formatted_categories;
+}
+
+/**
+ * Fetch WooCommerce product attributes
+ *
+ * @return array|false Formatted attributes array or false on failure
+ */
+function igny8_fetch_product_attributes() {
+ if (!igny8_is_woocommerce_active()) {
+ return false;
+ }
+
+ $consumer_key = get_option('woocommerce_api_consumer_key', '');
+ $consumer_secret = get_option('woocommerce_api_consumer_secret', '');
+
+ $headers = array();
+ if ($consumer_key && $consumer_secret) {
+ $headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
+ }
+
+ $wp_response = wp_remote_get(
+ get_site_url() . '/wp-json/wc/v3/products/attributes',
+ array(
+ 'headers' => $headers
+ )
+ );
+
+ if (is_wp_error($wp_response)) {
+ return false;
+ }
+
+ $attributes = json_decode(wp_remote_retrieve_body($wp_response), true);
+
+ if (!is_array($attributes)) {
+ return false;
+ }
+
+ $formatted_attributes = array();
+ foreach ($attributes as $attribute) {
+ // Get attribute terms
+ $terms_response = wp_remote_get(sprintf(
+ '%s/wp-json/wc/v3/products/attributes/%d/terms',
+ get_site_url(),
+ $attribute['id']
+ ), array(
+ 'headers' => $headers
+ ));
+
+ $terms = array();
+ if (!is_wp_error($terms_response)) {
+ $terms_data = json_decode(wp_remote_retrieve_body($terms_response), true);
+ if (is_array($terms_data)) {
+ foreach ($terms_data as $term) {
+ $terms[] = array(
+ 'id' => $term['id'],
+ 'name' => $term['name'],
+ 'slug' => $term['slug']
+ );
+ }
+ }
+ }
+
+ $formatted_attributes[] = array(
+ 'id' => $attribute['id'],
+ 'name' => $attribute['name'],
+ 'slug' => $attribute['slug'],
+ 'type' => $attribute['type'],
+ 'order_by' => $attribute['order_by'],
+ 'has_archives' => $attribute['has_archives'],
+ 'terms' => $terms
+ );
+ }
+
+ return $formatted_attributes;
+}
+
diff --git a/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md b/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md
new file mode 100644
index 00000000..eec33324
--- /dev/null
+++ b/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md
@@ -0,0 +1,185 @@
+# Phase 5 Implementation Summary
+
+## Overview
+Phase 5 implementation adds Planner, Linker, and Optimizer module hooks to the WordPress bridge plugin.
+
+## Implemented Features
+
+### 5.1 Planner Briefs Display & Refresh Actions ✅
+
+**Files Created:**
+- `admin/class-post-meta-boxes.php` - Meta boxes for post editor
+- `admin/assets/js/post-editor.js` - JavaScript for AJAX interactions
+
+**Features:**
+- **Planner Brief Meta Box** in post editor sidebar
+ - Displays cached brief with title, content, outline, keywords, and tone
+ - Shows cache timestamp
+ - "Fetch Brief" button to load from IGNY8 API
+ - "Request Refresh" button to trigger Planner refresh
+
+**API Endpoints Used:**
+- `GET /planner/tasks/{id}/brief/` - Fetch Planner brief (with fallback to Writer brief)
+- `POST /planner/tasks/{id}/refresh/` - Request Planner task refresh
+
+**Meta Fields Added:**
+- `_igny8_task_brief` - Cached brief data
+- `_igny8_brief_cached_at` - Brief cache timestamp
+
+**AJAX Handlers:**
+- `igny8_fetch_planner_brief` - Fetches and caches brief
+- `igny8_refresh_planner_task` - Requests Planner refresh
+
+---
+
+### 5.2 Link Graph Export ✅
+
+**Files Created:**
+- `data/link-graph.php` - Link graph extraction and export
+
+**Features:**
+- **Link Extraction** from post content
+ - Extracts all internal links (anchor tags)
+ - Captures source URL, target URL, and anchor text
+ - Filters to only internal links (same domain)
+ - Normalizes URLs for consistency
+
+- **Link Graph Collection**
+ - Processes all enabled post types
+ - Extracts links from published posts
+ - Configurable batch processing (max 1000 posts per run)
+
+- **Automatic Export During Site Scans**
+ - Integrated into `igny8_collect_site_data()`
+ - Included in site data payload
+ - Also sent separately to Linker module endpoint
+
+**API Endpoints Used:**
+- `POST /linker/link-map/` - Send link graph to Linker module
+
+**Functions:**
+- `igny8_extract_post_links($post_id)` - Extract links from single post
+- `igny8_extract_link_graph($post_ids)` - Extract links from multiple posts
+- `igny8_send_link_graph_to_igny8($site_id, $link_graph)` - Send to IGNY8 API
+
+**Integration Points:**
+- `igny8_collect_site_data()` - Includes link graph in site data
+- `igny8_send_site_data_to_igny8()` - Sends link graph after site import
+- `igny8_perform_full_site_scan()` - Sends link graph during full scans
+
+**Options Stored:**
+- `igny8_last_link_graph_sync` - Last sync timestamp
+- `igny8_last_link_graph_count` - Number of links sent
+
+---
+
+### 5.4 Optimizer Triggers ✅
+
+**Features:**
+- **Optimizer Meta Box** in post editor sidebar
+ - Displays current optimizer job ID and status
+ - "Request Optimization" button to create new job
+ - "Check Status" button to fetch latest job status
+ - Shows score changes and recommendations when available
+
+**API Endpoints Used:**
+- `POST /optimizer/jobs/` - Create optimizer job
+- `GET /optimizer/jobs/{id}/` - Get optimizer job status
+
+**Meta Fields Added:**
+- `_igny8_optimizer_job_id` - Optimizer job ID
+- `_igny8_optimizer_status` - Job status (pending, processing, completed, failed)
+- `_igny8_optimizer_score_changes` - Score changes from optimization
+- `_igny8_optimizer_recommendations` - Optimization recommendations
+- `_igny8_optimizer_job_created_at` - Job creation timestamp
+
+**AJAX Handlers:**
+- `igny8_create_optimizer_job` - Creates new optimizer job
+- `igny8_get_optimizer_status` - Fetches job status and updates meta
+
+**Job Parameters:**
+- `post_id` - WordPress post ID
+- `task_id` - IGNY8 task ID
+- `job_type` - Type of job (default: 'audit')
+- `priority` - Job priority (default: 'normal')
+
+---
+
+## Module Toggle Support
+
+All Phase 5 features respect the module toggle settings:
+- **Planner** module must be enabled for brief fetching/refresh
+- **Linker** module must be enabled for link graph export
+- **Optimizer** module must be enabled for optimizer jobs
+
+Features also check `igny8_is_connection_enabled()` to ensure sync operations are active.
+
+---
+
+## UI/UX Features
+
+### Post Editor Meta Boxes
+- Clean, organized display in sidebar
+- Real-time AJAX updates
+- Success/error message display
+- Auto-refresh after operations
+- Disabled state when connection is off
+
+### JavaScript Enhancements
+- Loading states on buttons
+- Confirmation dialogs for destructive actions
+- Error handling and user feedback
+- Non-blocking AJAX requests
+
+---
+
+## Integration with Existing Code
+
+### Modified Files:
+- `igny8-bridge.php` - Added meta boxes class loading
+- `includes/functions.php` - Added optimizer meta field registrations
+- `data/site-collection.php` - Integrated link graph extraction
+
+### New Dependencies:
+- None (uses existing Igny8API class)
+
+---
+
+## Testing Checklist
+
+- [ ] Planner brief displays correctly in post editor
+- [ ] Fetch brief button works and caches data
+- [ ] Request refresh button triggers Planner refresh
+- [ ] Link graph extraction works on posts with links
+- [ ] Link graph is included in site scans
+- [ ] Link graph is sent to Linker endpoint
+- [ ] Optimizer job creation works
+- [ ] Optimizer status check works
+- [ ] All features respect module toggles
+- [ ] All features respect connection enabled toggle
+- [ ] Meta boxes only show for posts with task IDs
+- [ ] Error handling works correctly
+
+---
+
+## Notes
+
+1. **Link Graph Processing**: Currently limited to 1000 posts per run to prevent timeouts. Can be increased or made configurable.
+
+2. **Brief Caching**: Briefs are cached in post meta to reduce API calls. Cache can be refreshed manually.
+
+3. **Optimizer Jobs**: Jobs are created asynchronously. Status must be checked manually or via webhook (Phase 6).
+
+4. **Module Dependencies**: All features check module enablement before executing.
+
+5. **Connection Toggle**: All features respect the master connection toggle added earlier.
+
+---
+
+## Next Steps (Phase 6)
+
+Phase 5.3 (Accept link recommendations via webhook) is deferred to Phase 6, which will implement:
+- REST endpoint `/wp-json/igny8/v1/event` with shared secret
+- Webhook handler for link recommendations
+- Link insertion queue system
+
diff --git a/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md b/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md
new file mode 100644
index 00000000..827c88b9
--- /dev/null
+++ b/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md
@@ -0,0 +1,298 @@
+# Phase 6 Implementation Summary
+
+## Overview
+Phase 6 implementation adds webhook support and remote control capabilities, allowing IGNY8 SaaS to send events to WordPress.
+
+## Implemented Features
+
+### 6.1 REST Route Registration with Shared Secret ✅
+
+**Files Created:**
+- `includes/class-igny8-webhooks.php` - Main webhook handler class
+
+**Features:**
+- **REST Endpoint**: `/wp-json/igny8/v1/event` (POST)
+- **Shared Secret Authentication**: HMAC-SHA256 signature verification
+- **Connection Check**: All webhook handlers verify `igny8_is_connection_enabled()` before processing
+
+**Security:**
+- Webhook secret stored in WordPress options (auto-generated on first use)
+- Signature verification via `X-IGNY8-Signature` header
+- Secret can be regenerated from settings page
+- Failed authentication attempts are logged
+
+**Functions:**
+- `igny8_get_webhook_secret()` - Get or generate webhook secret
+- `igny8_regenerate_webhook_secret()` - Regenerate secret
+
+---
+
+### 6.2 SaaS Event Handlers ✅
+
+**Event Types Supported:**
+
+#### 1. Task Published (`task_published`, `task_completed`)
+- Creates or updates WordPress post from IGNY8 task
+- Fetches full task data from Writer API
+- Respects enabled post types
+- Updates post status if task is completed
+
+#### 2. Link Recommendation (`link_recommendation`, `insert_link`)
+- Queues link insertion for processing
+- Validates required parameters (post_id, target_url, anchor)
+- Respects Linker module toggle
+- Processes links asynchronously via cron
+
+#### 3. Optimizer Request (`optimizer_request`, `optimizer_job_completed`)
+- Updates optimizer job status
+- Stores score changes and recommendations
+- Updates post meta with optimizer data
+- Respects Optimizer module toggle
+
+**Event Handler Flow:**
+1. Verify connection is enabled
+2. Verify module is enabled (if applicable)
+3. Validate event data
+4. Process event
+5. Log activity
+6. Return unified JSON response
+
+---
+
+### 6.3 Webhook Activity Logging ✅
+
+**Files Created:**
+- `includes/class-igny8-webhook-logs.php` - Logging functions
+
+**Features:**
+- **Log Storage**: WordPress options (last 500 logs)
+- **Log Fields**:
+ - Event type
+ - Event data
+ - IP address
+ - User agent
+ - Status (received, processed, failed)
+ - Response data
+ - Timestamps (received_at, processed_at)
+ - Error messages
+
+**Functions:**
+- `igny8_log_webhook_activity()` - Log webhook receipt
+- `igny8_update_webhook_log()` - Update log with processing result
+- `igny8_get_webhook_logs()` - Retrieve logs with filtering
+- `igny8_clear_old_webhook_logs()` - Cleanup old logs
+
+**UI Display:**
+- Recent webhook activity table in settings page
+- Shows last 10 webhook events
+- Displays event type, status, and timestamp
+
+---
+
+### Link Queue System ✅
+
+**Files Created:**
+- `includes/class-igny8-link-queue.php` - Link insertion queue
+
+**Features:**
+- **Queue Storage**: WordPress options
+- **Queue Processing**: Cron-based (processes 10 items per run)
+- **Retry Logic**: Up to 3 attempts per link
+- **Status Tracking**: pending, completed, failed
+
+**Link Insertion Logic:**
+1. Finds anchor text in post content
+2. Wraps anchor with link tag
+3. Avoids duplicate links
+4. Falls back to appending link if anchor not found
+
+**Queue Management:**
+- Automatic processing via cron
+- Manual trigger available
+- Queue size limit (1000 items)
+- Status tracking per item
+
+---
+
+## Connection Checks
+
+**All handlers check connection status:**
+
+✅ **Webhook Handler** (`verify_webhook_secret`)
+- Checks `igny8_is_connection_enabled()` before allowing webhook
+
+✅ **Event Handlers** (`handle_webhook`, `handle_task_published`, etc.)
+- Double-checks connection enabled before processing
+- Returns error if connection disabled
+
+✅ **Link Queue** (`igny8_queue_link_insertion`, `igny8_process_link_queue`)
+- Checks connection enabled before queuing/processing
+
+✅ **REST API Endpoints** (existing endpoints)
+- Updated to check connection enabled
+- Returns 403 if connection disabled
+
+---
+
+## Settings UI Enhancements
+
+**New Sections Added:**
+
+1. **Webhook Configuration**
+ - Webhook URL display with copy button
+ - Webhook secret display with copy button
+ - Regenerate secret button
+
+2. **Link Queue**
+ - Shows pending links count
+ - Displays queue table (last 10 items)
+ - Shows post ID, anchor, target URL, status
+
+3. **Recent Webhook Activity**
+ - Shows last 10 webhook events
+ - Displays event type, status, timestamp
+ - Color-coded status badges
+
+---
+
+## Security Features
+
+1. **HMAC Signature Verification**
+ - Uses SHA-256 HMAC
+ - Compares request body signature
+ - Prevents replay attacks
+
+2. **Connection Toggle Protection**
+ - All endpoints check connection status
+ - Webhooks rejected if connection disabled
+ - Clear error messages
+
+3. **Module Toggle Respect**
+ - Events only processed if module enabled
+ - Graceful error responses
+
+4. **Input Validation**
+ - All parameters sanitized
+ - Required fields validated
+ - Type checking
+
+---
+
+## API Response Format
+
+All webhook responses follow unified JSON format:
+
+**Success:**
+```json
+{
+ "success": true,
+ "message": "Event processed",
+ "data": { ... }
+}
+```
+
+**Error:**
+```json
+{
+ "success": false,
+ "error": "Error message",
+ "code": "error_code"
+}
+```
+
+---
+
+## Integration Points
+
+### Modified Files:
+- `igny8-bridge.php` - Added webhook classes loading
+- `includes/functions.php` - Added webhook secret functions
+- `includes/class-igny8-rest-api.php` - Added connection checks
+- `admin/settings.php` - Added webhook UI sections
+- `admin/class-admin.php` - Added secret regeneration handler
+
+### New Dependencies:
+- None (uses existing WordPress functions)
+
+---
+
+## Testing Checklist
+
+- [ ] Webhook endpoint accessible at `/wp-json/igny8/v1/event`
+- [ ] Signature verification works correctly
+- [ ] Invalid signatures are rejected
+- [ ] Connection disabled blocks webhooks
+- [ ] Task published event creates/updates posts
+- [ ] Link recommendation queues links
+- [ ] Link queue processes links correctly
+- [ ] Optimizer events update post meta
+- [ ] Webhook logs are created and updated
+- [ ] Settings UI displays webhook info
+- [ ] Secret regeneration works
+- [ ] All events respect module toggles
+- [ ] All events respect connection toggle
+
+---
+
+## Notes
+
+1. **Webhook Secret**: Auto-generated on first use. Must be configured in IGNY8 SaaS app.
+
+2. **Link Queue**: Processes 10 items per cron run to prevent timeouts. Can be adjusted.
+
+3. **Log Retention**: Keeps last 500 logs. Older logs can be cleared manually.
+
+4. **Signature Header**: IGNY8 SaaS must send `X-IGNY8-Signature` header with HMAC-SHA256 signature of request body.
+
+5. **Connection Toggle**: All webhook handlers check connection status before processing. This ensures no data is processed when connection is disabled.
+
+6. **Module Toggles**: Each event type checks if its module is enabled before processing.
+
+---
+
+## Webhook Payload Examples
+
+### Task Published
+```json
+{
+ "event": "task_published",
+ "data": {
+ "task_id": 123,
+ "status": "completed"
+ }
+}
+```
+
+### Link Recommendation
+```json
+{
+ "event": "link_recommendation",
+ "data": {
+ "post_id": 456,
+ "target_url": "https://example.com/page",
+ "anchor": "example link",
+ "priority": "normal"
+ }
+}
+```
+
+### Optimizer Request
+```json
+{
+ "event": "optimizer_job_completed",
+ "data": {
+ "post_id": 456,
+ "job_id": 789,
+ "status": "completed",
+ "score_changes": { ... },
+ "recommendations": [ ... ]
+ }
+}
+```
+
+---
+
+## Next Steps
+
+Phase 6 is complete. All webhook functionality is implemented with proper security, logging, and connection checks.
+
diff --git a/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md b/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md
new file mode 100644
index 00000000..14591a62
--- /dev/null
+++ b/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md
@@ -0,0 +1,249 @@
+# Status Sync & Content ID Documentation
+
+**Last Updated**: 2025-10-17
+
+---
+
+## Overview
+
+This document explains how the plugin handles status synchronization and content_id tracking between IGNY8 and WordPress.
+
+---
+
+## Status Mapping
+
+### IGNY8 → WordPress
+
+When content arrives from IGNY8, status is mapped as follows:
+
+| IGNY8 Status | WordPress Status | Description |
+|--------------|------------------|-------------|
+| `completed` | `publish` | Content is published |
+| `draft` | `draft` | Content is draft |
+| `pending` | `pending` | Content pending review |
+| `scheduled` | `future` | Content scheduled |
+| `archived` | `trash` | Content archived |
+
+### WordPress → IGNY8
+
+When WordPress post status changes, it's mapped back to IGNY8:
+
+| WordPress Status | IGNY8 Status | Description |
+|------------------|--------------|-------------|
+| `publish` | `completed` | Post is published |
+| `draft` | `draft` | Post is draft |
+| `pending` | `pending` | Post pending review |
+| `private` | `completed` | Post is private (published) |
+| `trash` | `archived` | Post is deleted |
+| `future` | `scheduled` | Post is scheduled |
+
+---
+
+## Content ID Tracking
+
+### Storage
+
+The plugin stores IGNY8 `content_id` in post meta:
+- **Meta Key**: `_igny8_content_id`
+- **Type**: Integer
+- **REST API**: Available via `/wp-json/wp/v2/posts?meta_key=_igny8_content_id&meta_value=123`
+
+### Task ID Tracking
+
+The plugin also stores IGNY8 `task_id`:
+- **Meta Key**: `_igny8_task_id`
+- **Type**: Integer
+- **REST API**: Available via `/wp-json/wp/v2/posts?meta_key=_igny8_task_id&meta_value=456`
+
+---
+
+## Response to IGNY8
+
+When content is created/updated in WordPress, the plugin responds to IGNY8 with:
+
+```json
+{
+ "assigned_post_id": 123,
+ "post_url": "https://example.com/post/",
+ "wordpress_status": "publish",
+ "status": "completed",
+ "synced_at": "2025-10-17 12:00:00",
+ "post_type": "post",
+ "content_type": "post",
+ "content_id": 789
+}
+```
+
+### Response Fields
+
+- **`assigned_post_id`**: WordPress post ID
+- **`post_url`**: Full URL to the post
+- **`wordpress_status`**: Actual WordPress status (publish/pending/draft)
+- **`status`**: IGNY8 mapped status (completed/pending/draft)
+- **`synced_at`**: Timestamp of sync
+- **`post_type`**: WordPress post type
+- **`content_type`**: IGNY8 content type
+- **`content_id`**: IGNY8 content ID (if provided)
+
+---
+
+## REST API Endpoints
+
+The plugin provides REST API endpoints for IGNY8 to query WordPress:
+
+### 1. Get Post by Content ID
+
+**Endpoint**: `GET /wp-json/igny8/v1/post-by-content-id/{content_id}`
+
+**Response**:
+```json
+{
+ "success": true,
+ "data": {
+ "post_id": 123,
+ "title": "Post Title",
+ "status": "publish",
+ "wordpress_status": "publish",
+ "igny8_status": "completed",
+ "url": "https://example.com/post/",
+ "post_type": "post",
+ "content_id": 789,
+ "task_id": 456,
+ "last_synced": "2025-10-17 12:00:00"
+ }
+}
+```
+
+### 2. Get Post by Task ID
+
+**Endpoint**: `GET /wp-json/igny8/v1/post-by-task-id/{task_id}`
+
+**Response**: Same format as above
+
+### 3. Get Post Status by Content ID
+
+**Endpoint**: `GET /wp-json/igny8/v1/post-status/{content_id}`
+
+**Response**:
+```json
+{
+ "success": true,
+ "data": {
+ "post_id": 123,
+ "wordpress_status": "publish",
+ "igny8_status": "completed",
+ "status_mapping": {
+ "publish": "completed",
+ "draft": "draft",
+ "pending": "pending",
+ "private": "completed",
+ "trash": "archived",
+ "future": "scheduled"
+ },
+ "content_id": 789,
+ "url": "https://example.com/post/",
+ "last_synced": "2025-10-17 12:00:00"
+ }
+}
+```
+
+---
+
+## Status Flow
+
+### When Content Arrives from IGNY8
+
+1. **Receive** content with `content_type`, `status`, `content_id`, `task_id`
+2. **Map** IGNY8 status to WordPress status
+3. **Create** WordPress post with mapped status
+4. **Store** `content_id` and `task_id` in post meta
+5. **Respond** to IGNY8 with:
+ - WordPress post ID
+ - WordPress actual status
+ - IGNY8 mapped status
+ - Post URL
+ - Content ID
+
+### When WordPress Status Changes
+
+1. **Detect** status change via `save_post` or `transition_post_status` hook
+2. **Get** `task_id` and `content_id` from post meta
+3. **Map** WordPress status to IGNY8 status
+4. **Update** IGNY8 task with:
+ - WordPress actual status
+ - IGNY8 mapped status
+ - Post URL
+ - Content ID (if available)
+ - Sync timestamp
+
+---
+
+## Available Meta Fields
+
+All fields are available for IGNY8 to read via REST API:
+
+- `_igny8_task_id` - IGNY8 task ID
+- `_igny8_content_id` - IGNY8 content ID
+- `_igny8_cluster_id` - IGNY8 cluster ID
+- `_igny8_sector_id` - IGNY8 sector ID
+- `_igny8_keyword_ids` - Array of keyword IDs
+- `_igny8_wordpress_status` - WordPress post status
+- `_igny8_last_synced` - Last sync timestamp
+
+---
+
+## Query Examples
+
+### Via WordPress REST API
+
+```bash
+# Get post by content_id
+GET /wp-json/wp/v2/posts?meta_key=_igny8_content_id&meta_value=123
+
+# Get post by task_id
+GET /wp-json/wp/v2/posts?meta_key=_igny8_task_id&meta_value=456
+
+# Get all IGNY8 posts
+GET /wp-json/wp/v2/posts?meta_key=_igny8_task_id
+```
+
+### Via IGNY8 REST API Endpoints
+
+```bash
+# Get post by content_id (with status info)
+GET /wp-json/igny8/v1/post-by-content-id/123
+
+# Get post status only
+GET /wp-json/igny8/v1/post-status/123
+
+# Get post by task_id
+GET /wp-json/igny8/v1/post-by-task-id/456
+```
+
+---
+
+## Authentication
+
+REST API endpoints require:
+- IGNY8 API authentication (access token)
+- Authorization header: `Bearer {access_token}`
+
+Or internal use when IGNY8 is connected.
+
+---
+
+## Status Flags Available
+
+✅ **Task ID** - Stored and queryable
+✅ **Content ID** - Stored and queryable
+✅ **WordPress Status** - Stored and sent to IGNY8
+✅ **IGNY8 Status** - Mapped and sent to IGNY8
+✅ **Post Type** - Stored and sent to IGNY8
+✅ **Content Type** - Stored and sent to IGNY8
+✅ **Sync Timestamp** - Stored and sent to IGNY8
+✅ **Post URL** - Sent to IGNY8
+
+---
+
+**All status information is available for IGNY8 to read and query!**
+
diff --git a/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md b/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md
new file mode 100644
index 00000000..059cd8ee
--- /dev/null
+++ b/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md
@@ -0,0 +1,186 @@
+# Style Guide - IGNY8 WordPress Bridge
+
+**Last Updated**: 2025-10-17
+
+---
+
+## CSS Architecture
+
+### No Inline CSS Policy
+
+✅ **All styles are in `admin/assets/css/admin.css`**
+❌ **No inline `style=""` attributes**
+❌ **No `