This commit is contained in:
alorig
2025-11-22 17:38:57 +05:00
parent a0d9bccb05
commit abd5518cf1
55 changed files with 0 additions and 16846 deletions

View File

@@ -1,469 +0,0 @@
/**
* Admin Styles - IGNY8 Bridge
* Updated with IGNY8 brand colors and modern UI
*
* @package Igny8Bridge
*/
/* ============================================
IGNY8 Brand Colors
============================================ */
:root {
--igny8-primary: #3B82F6;
--igny8-primary-hover: #2563EB;
--igny8-success: #10B981;
--igny8-warning: #F59E0B;
--igny8-error: #EF4444;
--igny8-purple: #8B5CF6;
--igny8-gray: #6B7280;
--igny8-light-gray: #F3F4F6;
}
/* ============================================
Container & Layout
============================================ */
.igny8-settings-container {
max-width: 1400px;
}
.igny8-settings-card {
background: #fff;
border: 1px solid #E5E7EB;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
padding: 24px;
margin: 24px 0;
border-radius: 8px;
}
.igny8-settings-card h2 {
margin-top: 0;
padding-bottom: 12px;
border-bottom: 2px solid var(--igny8-light-gray);
color: #111827;
font-size: 20px;
font-weight: 600;
}
/* ============================================
Toggle Switch
============================================ */
.igny8-toggle-wrapper {
display: flex;
align-items: center;
gap: 12px;
}
.igny8-toggle-input {
position: relative;
width: 48px;
height: 24px;
-webkit-appearance: none;
appearance: none;
background: var(--igny8-gray);
outline: none;
border-radius: 24px;
cursor: pointer;
transition: 0.3s;
}
.igny8-toggle-input:checked {
background: var(--igny8-success);
}
.igny8-toggle-input::before {
content: '';
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
top: 2px;
left: 2px;
background: #fff;
transition: 0.3s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.igny8-toggle-input:checked::before {
left: 26px;
}
/* ============================================
Sync Operations Grid
============================================ */
.igny8-sync-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 20px;
}
.igny8-sync-card {
background: #fff;
border: 2px solid #E5E7EB;
border-radius: 12px;
padding: 24px;
transition: all 0.3s ease;
}
.igny8-sync-card:hover {
border-color: var(--igny8-primary);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
transform: translateY(-2px);
}
.igny8-sync-card-highlight {
border-color: var(--igny8-primary);
background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);
}
.igny8-sync-icon {
width: 48px;
height: 48px;
background: var(--igny8-primary);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
color: white;
}
.igny8-sync-card h3 {
margin: 0 0 8px 0;
font-size: 16px;
font-weight: 600;
color: #111827;
}
.igny8-sync-description {
font-size: 14px;
color: #6B7280;
line-height: 1.5;
margin-bottom: 12px;
}
.igny8-sync-meta {
font-size: 12px;
color: #9CA3AF;
margin-bottom: 16px;
}
.igny8-sync-time {
display: inline-flex;
align-items: center;
gap: 4px;
}
.igny8-sync-button {
width: 100%;
height: 40px;
background: var(--igny8-primary) !important;
border-color: var(--igny8-primary) !important;
color: white !important;
font-weight: 500 !important;
border-radius: 8px !important;
transition: all 0.2s ease !important;
}
.igny8-sync-button:hover:not(:disabled) {
background: var(--igny8-primary-hover) !important;
border-color: var(--igny8-primary-hover) !important;
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
}
.igny8-sync-button:disabled {
opacity: 0.5 !important;
cursor: not-allowed !important;
}
.button-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
/* ============================================
Statistics Cards
============================================ */
.igny8-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.igny8-stat-card {
background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
border: 1px solid #E5E7EB;
border-radius: 12px;
padding: 20px;
display: flex;
align-items: flex-start;
gap: 16px;
transition: all 0.3s ease;
}
.igny8-stat-card:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transform: translateY(-2px);
}
.igny8-stat-icon {
width: 48px;
height: 48px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.igny8-stat-content {
flex: 1;
}
.igny8-stat-label {
font-size: 12px;
color: #6B7280;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 4px;
font-weight: 500;
}
.igny8-stat-value {
font-size: 28px;
font-weight: 700;
color: #111827;
line-height: 1.2;
margin-bottom: 4px;
}
.igny8-stat-meta {
font-size: 12px;
color: #9CA3AF;
}
/* ============================================
Semantic Summary
============================================ */
.igny8-semantic-summary {
margin-top: 24px;
padding: 20px;
background: linear-gradient(135deg, #F3E8FF 0%, #E9D5FF 100%);
border-radius: 12px;
border: 1px solid #D8B4FE;
}
.igny8-semantic-summary h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #6B21A8;
}
.igny8-semantic-stats {
display: flex;
gap: 32px;
flex-wrap: wrap;
}
.igny8-semantic-stat {
display: flex;
flex-direction: column;
gap: 4px;
}
.igny8-semantic-stat .value {
font-size: 24px;
font-weight: 700;
color: #7C3AED;
}
.igny8-semantic-stat .label {
font-size: 12px;
color: #8B5CF6;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* ============================================
Status Indicators
============================================ */
.igny8-status-connected {
color: var(--igny8-success);
font-weight: 600;
}
.igny8-status-disconnected {
color: var(--igny8-error);
font-weight: 600;
}
/* ============================================
Diagnostics
============================================ */
.igny8-diagnostics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
margin-top: 20px;
}
.igny8-diagnostic-item {
padding: 16px;
background: linear-gradient(135deg, #F9FAFB 0%, #F3F4F6 100%);
border: 1px solid #E5E7EB;
border-radius: 8px;
transition: all 0.2s ease;
}
.igny8-diagnostic-item:hover {
border-color: var(--igny8-primary);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.igny8-diagnostic-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #6B7280;
margin-bottom: 8px;
font-weight: 500;
}
.igny8-diagnostic-value {
font-size: 16px;
font-weight: 600;
color: #111827;
}
.igny8-diagnostic-item .description {
margin: 6px 0 0;
color: #9CA3AF;
font-size: 12px;
}
/* ============================================
Sync Status Messages
============================================ */
.igny8-sync-status {
margin-top: 20px;
padding: 16px;
border-radius: 8px;
display: none;
font-size: 14px;
}
.igny8-sync-status.igny8-sync-status-success {
background-color: #D1FAE5;
border: 1px solid #6EE7B7;
color: #065F46;
display: block;
}
.igny8-sync-status.igny8-sync-status-error {
background-color: #FEE2E2;
border: 1px solid #FCA5A5;
color: #991B1B;
display: block;
}
.igny8-sync-status.igny8-sync-status-loading {
background-color: #DBEAFE;
border: 1px solid #93C5FD;
color: #1E40AF;
display: block;
}
/* ============================================
Loading States
============================================ */
.igny8-loading {
opacity: 0.6;
pointer-events: none;
}
@keyframes igny8-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* ============================================
Messages & Notifications
============================================ */
.igny8-message {
padding: 16px;
margin: 15px 0;
border-left: 4px solid;
background: #fff;
border-radius: 4px;
}
.igny8-message.igny8-message-success {
border-color: var(--igny8-success);
background-color: #F0FDF4;
color: #065F46;
}
.igny8-message.igny8-message-error {
border-color: var(--igny8-error);
background-color: #FEF2F2;
color: #991B1B;
}
.igny8-message.igny8-message-info {
border-color: var(--igny8-primary);
background-color: #EFF6FF;
color: #1E40AF;
}
.igny8-message.igny8-message-warning {
border-color: var(--igny8-warning);
background-color: #FFFBEB;
color: #92400E;
}
/* ============================================
Responsive
============================================ */
@media (max-width: 782px) {
.igny8-sync-grid {
grid-template-columns: 1fr;
}
.igny8-stats-grid {
grid-template-columns: 1fr;
}
.igny8-diagnostics-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 600px) {
.igny8-settings-card {
padding: 16px;
}
.igny8-sync-card {
padding: 16px;
}
.igny8-stat-card {
flex-direction: column;
}
}

View File

@@ -1,188 +0,0 @@
/**
* 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('<span class="igny8-loading">Testing...</span>');
$.ajax({
url: igny8Admin.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_test_connection',
nonce: igny8Admin.nonce
},
success: function(response) {
if (response.success) {
$result.html('<span class="igny8-success">✓ ' + (response.data.message || 'Connection successful') + '</span>');
} else {
var errorMsg = response.data.message || 'Connection failed';
var httpStatus = response.data.http_status || '';
var fullMsg = errorMsg;
if (httpStatus) {
fullMsg += ' (HTTP ' + httpStatus + ')';
}
$result.html('<span class="igny8-error">✗ ' + fullMsg + '</span>');
// Log full error to console for debugging
console.error('IGNY8 Connection Test Failed:', response.data);
}
},
error: function(xhr, status, error) {
$result.html('<span class="igny8-error">✗ Request failed: ' + error + '</span>');
console.error('IGNY8 AJAX Error:', xhr, status, error);
},
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('<span class="igny8-spinner"></span>' + 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);

View File

@@ -1,200 +0,0 @@
/**
* 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('<p>' + response.data.message + '</p>')
.show();
// Reload page to show updated brief
setTimeout(function() {
location.reload();
}, 1000);
} else {
$message.addClass('notice notice-error inline')
.html('<p>' + (response.data.message || 'Failed to fetch brief') + '</p>')
.show();
$button.prop('disabled', false).text('Fetch Brief');
}
},
error: function() {
$message.addClass('notice notice-error inline')
.html('<p>Request failed</p>')
.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('<p>' + response.data.message + '</p>')
.show();
} else {
$message.addClass('notice notice-error inline')
.html('<p>' + (response.data.message || 'Failed to request refresh') + '</p>')
.show();
}
$button.prop('disabled', false).text('Request Refresh');
},
error: function() {
$message.addClass('notice notice-error inline')
.html('<p>Request failed</p>')
.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('<p>' + response.data.message + '</p>')
.show();
// Reload page to show updated status
setTimeout(function() {
location.reload();
}, 1000);
} else {
$message.addClass('notice notice-error inline')
.html('<p>' + (response.data.message || 'Failed to create job') + '</p>')
.show();
$button.prop('disabled', false).text('Request Optimization');
}
},
error: function() {
$message.addClass('notice notice-error inline')
.html('<p>Request failed</p>')
.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('<p>Status: <strong>' + response.data.status + '</strong></p>')
.show();
// Reload page to show updated status
setTimeout(function() {
location.reload();
}, 1000);
} else {
$message.addClass('notice notice-error inline')
.html('<p>' + (response.data.message || 'Failed to get status') + '</p>')
.show();
}
$button.prop('disabled', false).text('Check Status');
},
error: function() {
$message.addClass('notice notice-error inline')
.html('<p>Request failed</p>')
.show();
$button.prop('disabled', false).text('Check Status');
}
});
});
});
})(jQuery);

View File

@@ -1,306 +0,0 @@
<?php
/**
* Admin Columns and Row Actions
*
* Adds custom columns and actions to post/page/product list tables
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Igny8AdminColumns Class
*/
class Igny8AdminColumns {
/**
* Constructor
*/
public function __construct() {
// Add columns for posts, pages, and products
add_filter('manage_posts_columns', array($this, 'add_columns'));
add_filter('manage_pages_columns', array($this, 'add_columns'));
// Add column content
add_action('manage_posts_custom_column', array($this, 'render_column_content'), 10, 2);
add_action('manage_pages_custom_column', array($this, 'render_column_content'), 10, 2);
// Make columns sortable
add_filter('manage_edit-post_sortable_columns', array($this, 'make_columns_sortable'));
add_filter('manage_edit-page_sortable_columns', array($this, 'make_columns_sortable'));
// Add row actions
add_filter('post_row_actions', array($this, 'add_row_actions'), 10, 2);
add_filter('page_row_actions', array($this, 'add_row_actions'), 10, 2);
// Handle WooCommerce products
if (class_exists('WooCommerce')) {
add_filter('manage_product_posts_columns', array($this, 'add_columns'));
add_action('manage_product_posts_custom_column', array($this, 'render_column_content'), 10, 2);
add_filter('manage_edit-product_sortable_columns', array($this, 'make_columns_sortable'));
add_filter('product_row_actions', array($this, 'add_row_actions'), 10, 2);
}
// Handle AJAX actions
add_action('wp_ajax_igny8_send_to_igny8', array($this, 'send_to_igny8'));
}
/**
* Render taxonomy column
*
* @param int $post_id Post ID
*/
private function render_taxonomy_column($post_id) {
$taxonomy = get_post_meta($post_id, '_igny8_taxonomy_id', true);
if ($taxonomy) {
echo '<span class="igny8-badge igny8-badge-igny8" title="' . esc_attr__('Synced Taxonomy', 'igny8-bridge') . '">';
echo esc_html($taxonomy);
echo '</span>';
} else {
echo '<span class="igny8-empty">—</span>';
}
}
/**
* Render attribute column
*
* @param int $post_id Post ID
*/
private function render_attribute_column($post_id) {
$attribute = get_post_meta($post_id, '_igny8_attribute_id', true);
if ($attribute) {
echo '<span class="igny8-badge igny8-badge-igny8" title="' . esc_attr__('Synced Attribute', 'igny8-bridge') . '">';
echo esc_html($attribute);
echo '</span>';
} else {
echo '<span class="igny8-empty">—</span>';
}
}
/**
* Add custom columns
*
* @param array $columns Existing columns
* @return array Modified columns
*/
public function add_columns($columns) {
$new_columns = array();
foreach ($columns as $key => $value) {
$new_columns[$key] = $value;
if ($key === 'title') {
$new_columns['igny8_taxonomy'] = __('Taxonomy', 'igny8-bridge');
$new_columns['igny8_attribute'] = __('Attribute', '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_taxonomy':
$this->render_taxonomy_column($post_id);
break;
case 'igny8_attribute':
$this->render_attribute_column($post_id);
break;
}
}
/**
* 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(
'<a href="%s" class="igny8-action-link" data-post-id="%d" data-action="update">%s</a>',
'#',
$post->ID,
__('Update in IGNY8', 'igny8-bridge')
);
} else {
// Not synced - show send action
$actions['igny8_send'] = sprintf(
'<a href="%s" class="igny8-action-link" data-post-id="%d" data-action="send">%s</a>',
'#',
$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'));

View File

@@ -1,598 +0,0 @@
<?php
/**
* Admin Interface Class
*
* Handles all admin functionality
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Igny8Admin Class
*/
class Igny8Admin {
/**
* Single instance of the class
*
* @var Igny8Admin
*/
private static $instance = null;
/**
* Get single instance
*
* @return Igny8Admin
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
add_action('admin_menu', array($this, 'add_menu_pages'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
}
/**
* Add admin menu pages
*/
public function add_menu_pages() {
add_options_page(
__('IGNY8 API Settings', 'igny8-bridge'),
__('IGNY8 API', 'igny8-bridge'),
'manage_options',
'igny8-settings',
array($this, 'render_settings_page')
);
}
/**
* Register settings
*/
public function register_settings() {
// Email/password settings removed - using API key only
register_setting('igny8_settings', 'igny8_site_id');
register_setting('igny8_bridge_connection', 'igny8_connection_enabled', array(
'type' => 'boolean',
'sanitize_callback' => array($this, 'sanitize_boolean'),
'default' => 1
));
register_setting('igny8_bridge_connection', 'igny8_connection_enabled', array(
'type' => '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_enabled_taxonomies', array(
'type' => 'array',
'sanitize_callback' => array($this, 'sanitize_taxonomies'),
'default' => array('category', 'post_tag', 'product_cat', 'igny8_sectors', 'igny8_clusters')
));
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 (use wp_verify_nonce to avoid wp_die on failure)
if (isset($_POST['igny8_connect'])) {
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_settings_nonce')) {
add_settings_error(
'igny8_settings',
'igny8_nonce',
__('Security check failed. Please refresh the page and try again.', 'igny8-bridge'),
'error'
);
} else {
$this->handle_connection();
}
}
// Handle revoke API key (use wp_verify_nonce)
if (isset($_POST['igny8_revoke_api_key'])) {
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_revoke_api_key')) {
add_settings_error(
'igny8_settings',
'igny8_nonce_revoke',
__('Security check failed. Could not revoke API key.', 'igny8-bridge'),
'error'
);
} else {
self::revoke_api_key();
add_settings_error(
'igny8_settings',
'igny8_api_key_revoked',
__('API key revoked and removed from this site.', 'igny8-bridge'),
'updated'
);
}
}
// Webhook secret regeneration removed - using API key only
// Include settings template
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
}
/**
* Handle API connection - API key only
*/
private function handle_connection() {
$api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
// API key is required
if (empty($api_key)) {
add_settings_error(
'igny8_settings',
'igny8_error',
__('API key is required to connect to IGNY8.', 'igny8-bridge'),
'error'
);
return;
}
// Connect using API key only
$api = new Igny8API();
if (!$api->connect($api_key)) {
add_settings_error(
'igny8_settings',
'igny8_error',
__('Failed to connect to IGNY8 API. Please verify your API key is correct.', 'igny8-bridge'),
'error'
);
return;
}
// Store API key securely and also set access token to the API key for subsequent calls
// Only store if it's not the placeholder
if (!$is_placeholder) {
if (function_exists('igny8_store_secure_option')) {
igny8_store_secure_option('igny8_api_key', $api_key);
igny8_store_secure_option('igny8_access_token', $api_key);
} else {
update_option('igny8_api_key', $api_key);
update_option('igny8_access_token', $api_key);
}
}
// Try to get site ID (if available) using the authenticated client
$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 and stored API key.', 'igny8-bridge'),
'updated'
);
// Sync site structure to backend (post types, taxonomies, etc.)
igny8_sync_site_structure_to_backend();
}
/**
* Revoke stored API key (secure delete)
*
* Public so tests can call it directly.
*/
public static function revoke_api_key() {
if (function_exists('igny8_delete_secure_option')) {
igny8_delete_secure_option('igny8_api_key');
igny8_delete_secure_option('igny8_access_token');
igny8_delete_secure_option('igny8_refresh_token');
} else {
delete_option('igny8_api_key');
delete_option('igny8_access_token');
delete_option('igny8_refresh_token');
}
// Also clear token-issued timestamps
delete_option('igny8_token_refreshed_at');
delete_option('igny8_access_token_issued');
}
/**
* 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'));
}
// Try multiple endpoints to find one that works
$test_endpoints = array(
'/system/ping/' => 'System ping endpoint',
'/planner/keywords/?page_size=1' => 'Planner keywords list',
'/system/sites/' => 'Sites list'
);
$last_error = '';
$last_status = 0;
foreach ($test_endpoints as $endpoint => $description) {
$response = $api->get($endpoint);
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 (tested: ' . $description . ')',
'endpoint' => $endpoint,
'checked_at' => $checked_at
));
return;
}
$last_error = $response['error'] ?? 'Unknown error';
$last_status = $response['http_status'] ?? 0;
}
// All endpoints failed
wp_send_json_error(array(
'message' => 'Connection failed: ' . $last_error,
'http_status' => $last_status,
'full_error' => $last_error,
'endpoints_tested' => array_keys($test_endpoints)
));
}
/**
* 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 taxonomies option
*
* @param mixed $value Raw value
* @return array
*/
public function sanitize_taxonomies($value) {
$supported = array_keys(igny8_get_supported_taxonomies());
if (!is_array($value)) {
return array('category', 'post_tag', 'product_cat', 'igny8_sectors', 'igny8_clusters');
}
$clean = array();
foreach ($value as $taxonomy) {
$taxonomy = sanitize_key($taxonomy);
if (in_array($taxonomy, $supported, true)) {
$clean[] = $taxonomy;
}
}
// Return defaults if nothing selected
return !empty($clean) ? $clean : array('category', 'post_tag');
}
/**
* 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'));

View File

@@ -1,469 +0,0 @@
<?php
/**
* Post Meta Boxes
*
* Adds meta boxes to post editor for IGNY8 features
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Igny8PostMetaBoxes Class
*/
class Igny8PostMetaBoxes {
/**
* Constructor
*/
public function __construct() {
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
// AJAX handlers
add_action('wp_ajax_igny8_fetch_planner_brief', array($this, 'fetch_planner_brief'));
add_action('wp_ajax_igny8_refresh_planner_task', array($this, 'refresh_planner_task'));
add_action('wp_ajax_igny8_create_optimizer_job', array($this, 'create_optimizer_job'));
add_action('wp_ajax_igny8_get_optimizer_status', array($this, 'get_optimizer_status'));
}
/**
* Add meta boxes to post editor
*/
public function add_meta_boxes() {
$post_types = array('post', 'page', 'product');
foreach ($post_types as $post_type) {
add_meta_box(
'igny8-planner-brief',
__('IGNY8 Planner Brief', 'igny8-bridge'),
array($this, 'render_planner_brief_box'),
$post_type,
'side',
'default'
);
add_meta_box(
'igny8-optimizer',
__('IGNY8 Optimizer', 'igny8-bridge'),
array($this, 'render_optimizer_box'),
$post_type,
'side',
'default'
);
}
}
/**
* Enqueue scripts for post editor
*/
public function enqueue_scripts($hook) {
if (!in_array($hook, array('post.php', 'post-new.php'), true)) {
return;
}
wp_enqueue_script(
'igny8-post-editor',
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/post-editor.js',
array('jquery'),
IGNY8_BRIDGE_VERSION,
true
);
wp_localize_script('igny8-post-editor', 'igny8PostEditor', array(
'ajaxUrl' => 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 '<p class="description">';
_e('This post is not linked to an IGNY8 task or cluster.', 'igny8-bridge');
echo '</p>';
return;
}
wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
?>
<div id="igny8-planner-brief-content">
<?php if ($brief) : ?>
<div class="igny8-brief-display">
<?php if (is_array($brief)) : ?>
<?php if (!empty($brief['title'])) : ?>
<h4><?php echo esc_html($brief['title']); ?></h4>
<?php endif; ?>
<?php if (!empty($brief['content'])) : ?>
<div class="igny8-brief-content">
<?php echo wp_kses_post(wpautop($brief['content'])); ?>
</div>
<?php endif; ?>
<?php if (!empty($brief['outline'])) : ?>
<div class="igny8-brief-outline">
<strong><?php _e('Outline:', 'igny8-bridge'); ?></strong>
<?php if (is_array($brief['outline'])) : ?>
<ul>
<?php foreach ($brief['outline'] as $item) : ?>
<li><?php echo esc_html($item); ?></li>
<?php endforeach; ?>
</ul>
<?php else : ?>
<p><?php echo esc_html($brief['outline']); ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($brief['keywords'])) : ?>
<div class="igny8-brief-keywords">
<strong><?php _e('Keywords:', 'igny8-bridge'); ?></strong>
<?php
$keywords = is_array($brief['keywords']) ? $brief['keywords'] : explode(',', $brief['keywords']);
echo '<span class="igny8-keyword-tags">';
foreach ($keywords as $keyword) {
echo '<span class="igny8-keyword-tag">' . esc_html(trim($keyword)) . '</span>';
}
echo '</span>';
?>
</div>
<?php endif; ?>
<?php if (!empty($brief['tone'])) : ?>
<div class="igny8-brief-tone">
<strong><?php _e('Tone:', 'igny8-bridge'); ?></strong>
<?php echo esc_html($brief['tone']); ?>
</div>
<?php endif; ?>
<?php else : ?>
<p><?php echo esc_html($brief); ?></p>
<?php endif; ?>
<?php if ($brief_cached_at) : ?>
<p class="description">
<?php
printf(
__('Cached: %s', 'igny8-bridge'),
date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($brief_cached_at))
);
?>
</p>
<?php endif; ?>
</div>
<?php else : ?>
<p class="description">
<?php _e('No brief cached. Click "Fetch Brief" to load from IGNY8.', 'igny8-bridge'); ?>
</p>
<?php endif; ?>
</div>
<p>
<button type="button"
id="igny8-fetch-brief"
class="button button-secondary"
data-post-id="<?php echo esc_attr($post->ID); ?>"
data-task-id="<?php echo esc_attr($task_id); ?>">
<?php _e('Fetch Brief', 'igny8-bridge'); ?>
</button>
<?php if ($task_id) : ?>
<button type="button"
id="igny8-refresh-task"
class="button button-secondary"
data-post-id="<?php echo esc_attr($post->ID); ?>"
data-task-id="<?php echo esc_attr($task_id); ?>">
<?php _e('Request Refresh', 'igny8-bridge'); ?>
</button>
<?php endif; ?>
</p>
<div id="igny8-planner-brief-message" class="igny8-message" style="display: none;"></div>
<?php
}
/**
* Render Optimizer meta box
*/
public function render_optimizer_box($post) {
$task_id = get_post_meta($post->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 '<p class="description">';
_e('This post is not linked to an IGNY8 task.', 'igny8-bridge');
echo '</p>';
return;
}
wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
?>
<div id="igny8-optimizer-content">
<?php if ($optimizer_job_id) : ?>
<div class="igny8-optimizer-status">
<p>
<strong><?php _e('Job ID:', 'igny8-bridge'); ?></strong>
<?php echo esc_html($optimizer_job_id); ?>
</p>
<?php if ($optimizer_status) : ?>
<p>
<strong><?php _e('Status:', 'igny8-bridge'); ?></strong>
<span class="igny8-status-badge igny8-status-<?php echo esc_attr(strtolower($optimizer_status)); ?>">
<?php echo esc_html($optimizer_status); ?>
</span>
</p>
<?php endif; ?>
<p>
<button type="button"
id="igny8-check-optimizer-status"
class="button button-secondary"
data-post-id="<?php echo esc_attr($post->ID); ?>"
data-job-id="<?php echo esc_attr($optimizer_job_id); ?>">
<?php _e('Check Status', 'igny8-bridge'); ?>
</button>
</p>
</div>
<?php else : ?>
<p class="description">
<?php _e('No optimizer job created yet.', 'igny8-bridge'); ?>
</p>
<?php endif; ?>
</div>
<p>
<button type="button"
id="igny8-create-optimizer-job"
class="button button-primary"
data-post-id="<?php echo esc_attr($post->ID); ?>"
data-task-id="<?php echo esc_attr($task_id); ?>">
<?php _e('Request Optimization', 'igny8-bridge'); ?>
</button>
</p>
<div id="igny8-optimizer-message" class="igny8-message" style="display: none;"></div>
<?php
}
/**
* Fetch Planner brief (AJAX handler)
*/
public static function fetch_planner_brief() {
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'));
}
// 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();

View File

@@ -1,701 +0,0 @@
<?php
/**
* Settings Page Template
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Get current settings
$email = get_option('igny8_email', '');
$site_id = get_option('igny8_site_id', '');
$access_token = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_access_token') : get_option('igny8_access_token');
$is_connected = !empty($access_token);
$api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key');
$date_format = get_option('date_format');
$time_format = get_option('time_format');
$now = current_time('timestamp');
$token_issued = intval(get_option('igny8_access_token_issued', 0));
$token_age_text = $token_issued ? sprintf(__('%s ago', 'igny8-bridge'), human_time_diff($token_issued, $now)) : __('Not generated yet', 'igny8-bridge');
$token_issued_formatted = $token_issued ? date_i18n($date_format . ' ' . $time_format, $token_issued) : __('—', 'igny8-bridge');
$last_health_check = intval(get_option('igny8_last_api_health_check', 0));
$last_health_check_formatted = $last_health_check ? date_i18n($date_format . ' ' . $time_format, $last_health_check) : __('Never', 'igny8-bridge');
$last_site_sync = intval(get_option('igny8_last_site_sync', 0));
$last_site_sync_formatted = $last_site_sync ? date_i18n($date_format . ' ' . $time_format, $last_site_sync) : __('Never', 'igny8-bridge');
$last_taxonomy_sync = intval(get_option('igny8_last_taxonomy_sync', 0));
$last_taxonomy_sync_formatted = $last_taxonomy_sync ? date_i18n($date_format . ' ' . $time_format, $last_taxonomy_sync) : __('Never', 'igny8-bridge');
$last_keyword_sync = intval(get_option('igny8_last_keyword_sync', 0));
$last_keyword_sync_formatted = $last_keyword_sync ? date_i18n($date_format . ' ' . $time_format, $last_keyword_sync) : __('Never', 'igny8-bridge');
$last_writer_sync = intval(get_option('igny8_last_writer_sync', 0));
$last_writer_sync_formatted = $last_writer_sync ? date_i18n($date_format . ' ' . $time_format, $last_writer_sync) : __('Never', 'igny8-bridge');
$last_full_site_scan = intval(get_option('igny8_last_full_site_scan', 0));
$last_full_site_scan_formatted = $last_full_site_scan ? date_i18n($date_format . ' ' . $time_format, $last_full_site_scan) : __('Never', 'igny8-bridge');
$last_semantic_map = intval(get_option('igny8_last_semantic_map', 0));
$last_semantic_map_formatted = $last_semantic_map ? date_i18n($date_format . ' ' . $time_format, $last_semantic_map) : __('Never', 'igny8-bridge');
$semantic_summary = get_option('igny8_last_semantic_map_summary', array());
$next_site_sync = wp_next_scheduled('igny8_sync_site_data');
$next_site_sync_formatted = $next_site_sync ? date_i18n($date_format . ' ' . $time_format, $next_site_sync) : __('Not scheduled', 'igny8-bridge');
$available_post_types = igny8_get_supported_post_types();
$enabled_post_types = igny8_get_enabled_post_types();
$available_taxonomies = igny8_get_supported_taxonomies();
$enabled_taxonomies = igny8_get_enabled_taxonomies();
$control_mode = igny8_get_control_mode();
$woocommerce_enabled = (int) get_option('igny8_enable_woocommerce', class_exists('WooCommerce') ? 1 : 0);
$woocommerce_detected = class_exists('WooCommerce');
$available_modules = igny8_get_available_modules();
$enabled_modules = igny8_get_enabled_modules();
$connection_enabled = igny8_is_connection_enabled();
$webhook_secret = igny8_get_webhook_secret();
$webhook_url = rest_url('igny8/v1/event');
$link_queue = get_option('igny8_link_queue', array());
$pending_links = array_filter($link_queue, function($item) {
return $item['status'] === 'pending';
});
$webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
$two_way_sync = (int) get_option('igny8_enable_two_way_sync', 1);
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<?php settings_errors('igny8_settings'); ?>
<div class="notice notice-info inline" style="margin-top:10px;">
<p>
<strong><?php _e('Integration modes explained:', 'igny8-bridge'); ?></strong><br />
<?php _e('• Enable Sync Operations: controls whether background and manual sync actions occur (cron jobs, webhooks, sync buttons).', 'igny8-bridge'); ?><br />
<?php _e('• Enable Two-Way Sync: controls whether bi-directional syncing (IGNY8 → WordPress and WordPress → IGNY8) is permitted. Disabling this will suppress sync actions but API endpoints remain accessible for discovery and diagnostics.', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-settings-container">
<div class="igny8-settings-card">
<h2><?php _e('API Connection', 'igny8-bridge'); ?></h2>
<?php if (!$is_connected) : ?>
<form method="post" action="">
<?php wp_nonce_field('igny8_settings_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_api_key"><?php _e('API Key', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="text"
id="igny8_api_key"
name="igny8_api_key"
value=""
class="regular-text"
placeholder="<?php _e('Paste your IGNY8 API key here', 'igny8-bridge'); ?>"
required
/>
<p class="description">
<?php _e('If you have an API key from the IGNY8 SaaS app, paste it here to authenticate the bridge. Leave blank to use email/password.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
<?php submit_button(__('Connect to IGNY8', 'igny8-bridge'), 'primary', 'igny8_connect'); ?>
</form>
<?php else : ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_api_key"><?php _e('API Key', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="password"
id="igny8_api_key"
value="<?php echo esc_attr(str_repeat('•', min(strlen($api_key), 40))); ?>"
class="regular-text"
disabled
style="background-color: #f0f0f0;"
/>
<p class="description">
<?php _e('Connected with API key. Use the revoke button below to disconnect.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
<div style="margin-top: 15px;">
<form method="post" action="" style="display: inline-block;">
<?php wp_nonce_field('igny8_revoke_api_key'); ?>
<button type="submit" name="igny8_revoke_api_key" class="button button-secondary" onclick="return confirm('<?php _e('Revoke API key? This will disconnect the plugin from IGNY8.', 'igny8-bridge'); ?>');">
<?php _e('Revoke API Key', 'igny8-bridge'); ?>
</button>
</form>
</div>
<?php endif; ?>
</div>
<?php if ($is_connected) : ?>
<div class="igny8-settings-card">
<h2><?php _e('Connection Status', 'igny8-bridge'); ?></h2>
<form method="post" action="options.php">
<?php settings_fields('igny8_bridge_connection'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_connection_enabled"><?php _e('Enable Communication', 'igny8-bridge'); ?></label>
</th>
<td>
<label class="igny8-toggle-wrapper">
<input
type="checkbox"
id="igny8_connection_enabled"
name="igny8_connection_enabled"
value="1"
<?php checked($connection_enabled, 1); ?>
class="igny8-toggle-input"
/>
<span class="igny8-toggle-slider"></span>
<span class="igny8-toggle-label">
<?php if ($connection_enabled) : ?>
<strong style="color: #10B981;"><?php _e('Connected', 'igny8-bridge'); ?></strong>
<?php else : ?>
<strong style="color: #6B7280;"><?php _e('Disconnected', 'igny8-bridge'); ?></strong>
<?php endif; ?>
</span>
</label>
<p class="description">
<?php _e('Controls whether the plugin can communicate with IGNY8. API key stays connected but no data is synced when disabled.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
<?php submit_button(__('Save Settings', 'igny8-bridge')); ?>
</form>
<p>
<button type="button" id="igny8-test-connection" class="button" <?php disabled(!$connection_enabled); ?>>
<?php _e('Test Connection', 'igny8-bridge'); ?>
</button>
<span id="igny8-test-result" class="igny8-test-result"></span>
</p>
<?php if (defined('WP_DEBUG') && WP_DEBUG) : ?>
<p class="description" style="color: #0073aa;">
<strong><?php _e('Debug Mode Active', 'igny8-bridge'); ?>:</strong>
<?php _e('Check wp-content/debug.log for detailed API request/response logs.', 'igny8-bridge'); ?>
</p>
<?php endif; ?>
</div>
<div class="igny8-settings-card">
<h2><?php _e('Diagnostics', 'igny8-bridge'); ?></h2>
<div class="igny8-diagnostics-grid">
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Access Token Age', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($token_age_text); ?></div>
<p class="description">
<?php echo esc_html($token_issued_formatted); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last API Health Check', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_health_check_formatted); ?></div>
<p class="description">
<?php _e('Updated when tests succeed', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Site Data Sync', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_site_sync_formatted); ?></div>
<p class="description">
<?php _e('Triggered automatically after setup', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Full Site Scan', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_full_site_scan_formatted); ?></div>
<p class="description">
<?php _e('Runs weekly or when requested manually', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Semantic Map', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_semantic_map_formatted); ?></div>
<p class="description">
<?php
if (!empty($semantic_summary)) {
printf(
/* translators: %1$d: sectors count, %2$d: keywords count */
esc_html__('Sectors: %1$d · Keywords: %2$d', 'igny8-bridge'),
intval($semantic_summary['sectors'] ?? 0),
intval($semantic_summary['keywords'] ?? 0)
);
} else {
_e('Planner semantic map generated after full scan', 'igny8-bridge');
}
?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Taxonomy Sync', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_taxonomy_sync_formatted); ?></div>
<p class="description">
<?php _e('Sectors & clusters imported from Planner', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Keyword Sync', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_keyword_sync_formatted); ?></div>
<p class="description">
<?php _e('Planner keywords cached to WordPress posts', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Last Writer Sync', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($last_writer_sync_formatted); ?></div>
<p class="description">
<?php _e('New IGNY8 tasks imported automatically', 'igny8-bridge'); ?>
</p>
</div>
<div class="igny8-diagnostic-item">
<div class="igny8-diagnostic-label"><?php _e('Next Scheduled Site Scan', 'igny8-bridge'); ?></div>
<div class="igny8-diagnostic-value"><?php echo esc_html($next_site_sync_formatted); ?></div>
</div>
</div>
</div>
<?php else : ?>
<div class="igny8-settings-card">
<h2><?php _e('Connection Status', 'igny8-bridge'); ?></h2>
<p>
<span class="igny8-status-disconnected">
<?php _e('Not Connected', 'igny8-bridge'); ?>
</span>
</p>
<p class="description">
<?php _e('Enter your IGNY8 credentials above and click "Connect to IGNY8" to establish a connection.', 'igny8-bridge'); ?>
</p>
</div>
<?php endif; ?>
<div class="igny8-settings-card">
<h2><?php _e('Automation Settings', 'igny8-bridge'); ?></h2>
<form method="post" action="options.php">
<?php settings_fields('igny8_bridge_controls'); ?>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Post Types to Sync', 'igny8-bridge'); ?></th>
<td>
<?php foreach ($available_post_types as $slug => $label) : ?>
<label>
<input
type="checkbox"
name="igny8_enabled_post_types[]"
value="<?php echo esc_attr($slug); ?>"
<?php checked(in_array($slug, $enabled_post_types, true)); ?>
/>
<?php echo esc_html($label); ?>
</label>
<br />
<?php endforeach; ?>
<p class="description">
<?php _e('Select the content types IGNY8 should manage automatically.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('IGNY8 Modules', 'igny8-bridge'); ?></th>
<td>
<?php foreach ($available_modules as $module_key => $module_label) : ?>
<label>
<input
type="checkbox"
name="igny8_enabled_modules[]"
value="<?php echo esc_attr($module_key); ?>"
<?php checked(in_array($module_key, $enabled_modules, true)); ?>
/>
<?php echo esc_html($module_label); ?>
</label>
<br />
<?php endforeach; ?>
<p class="description">
<?php _e('Disable modules temporarily if a feature is not ready in the SaaS app.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Control Mode', 'igny8-bridge'); ?></th>
<td>
<label>
<input
type="radio"
name="igny8_control_mode"
value="mirror"
<?php checked($control_mode, 'mirror'); ?>
/>
<strong><?php _e('Mirror', 'igny8-bridge'); ?></strong>
<span class="description"><?php _e('IGNY8 is the source of truth; WordPress reflects changes only.', 'igny8-bridge'); ?></span>
</label>
<br />
<label>
<input
type="radio"
name="igny8_control_mode"
value="hybrid"
<?php checked($control_mode, 'hybrid'); ?>
/>
<strong><?php _e('Hybrid', 'igny8-bridge'); ?></strong>
<span class="description"><?php _e('Allow editors to update content in WordPress and sync back to IGNY8.', 'igny8-bridge'); ?></span>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e('WooCommerce Products', 'igny8-bridge'); ?></th>
<td>
<label>
<input
type="checkbox"
name="igny8_enable_woocommerce"
value="1"
<?php checked($woocommerce_enabled, 1); ?>
<?php disabled(!$woocommerce_detected); ?>
/>
<?php _e('Sync WooCommerce products and categories.', 'igny8-bridge'); ?>
</label>
<?php if (!$woocommerce_detected) : ?>
<p class="description">
<?php _e('WooCommerce is not active on this site. Enable the plugin to sync product data.', 'igny8-bridge'); ?>
</p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Taxonomies to Sync', 'igny8-bridge'); ?></th>
<td>
<?php foreach ($available_taxonomies as $taxonomy_slug => $taxonomy_label) : ?>
<label>
<input
type="checkbox"
name="igny8_enabled_taxonomies[]"
value="<?php echo esc_attr($taxonomy_slug); ?>"
<?php checked(in_array($taxonomy_slug, $enabled_taxonomies, true)); ?>
/>
<?php echo esc_html($taxonomy_label); ?> (<?php echo esc_html($taxonomy_slug); ?>)
</label>
<br />
<?php endforeach; ?>
<p class="description">
<?php _e('Select which taxonomies to synchronize bidirectionally with IGNY8.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
<?php submit_button(__('Save Automation Settings', 'igny8-bridge')); ?>
</form>
<p class="description">
<?php _e('Once these settings are saved, the bridge schedules automatic jobs that keep IGNY8 in sync without manual actions.', 'igny8-bridge'); ?>
</p>
</div>
<?php if ($is_connected) : ?>
<div class="igny8-settings-card">
<h2><?php _e('Webhook Configuration', 'igny8-bridge'); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Webhook URL', 'igny8-bridge'); ?></th>
<td>
<code><?php echo esc_html($webhook_url); ?></code>
<button type="button" class="button button-small" onclick="navigator.clipboard.writeText('<?php echo esc_js($webhook_url); ?>'); alert('<?php _e('Webhook URL copied to clipboard', 'igny8-bridge'); ?>');">
<?php _e('Copy', 'igny8-bridge'); ?>
</button>
<p class="description">
<?php _e('Configure this URL in your IGNY8 SaaS app settings.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Webhook Secret', 'igny8-bridge'); ?></th>
<td>
<code style="word-break: break-all;"><?php echo esc_html($webhook_secret); ?></code>
<button type="button" class="button button-small" onclick="navigator.clipboard.writeText('<?php echo esc_js($webhook_secret); ?>'); alert('<?php _e('Secret copied to clipboard', 'igny8-bridge'); ?>');">
<?php _e('Copy', 'igny8-bridge'); ?>
</button>
<form method="post" action="" style="display: inline-block; margin-left: 10px;">
<?php wp_nonce_field('igny8_regenerate_secret'); ?>
<button type="submit" name="igny8_regenerate_secret" class="button button-small" onclick="return confirm('<?php _e('Are you sure? You will need to update the secret in IGNY8 SaaS app.', 'igny8-bridge'); ?>');">
<?php _e('Regenerate', 'igny8-bridge'); ?>
</button>
</form>
<p class="description">
<?php _e('Use this secret to verify webhook requests in IGNY8 SaaS app. Keep it secure.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
</div>
<?php endif; ?>
<div class="igny8-settings-card">
<h2><?php _e('About', 'igny8-bridge'); ?></h2>
<p>
<?php _e('The IGNY8 WordPress Bridge plugin connects your WordPress site to the IGNY8 API, enabling two-way synchronization of posts, taxonomies, and site data.', 'igny8-bridge'); ?>
</p>
<p>
<strong><?php _e('Version:', 'igny8-bridge'); ?></strong> <?php echo esc_html(IGNY8_BRIDGE_VERSION); ?>
</p>
</div>
<?php if ($is_connected) : ?>
<div class="igny8-settings-card">
<h2><?php _e('Sync Operations', 'igny8-bridge'); ?></h2>
<?php if (!$connection_enabled) : ?>
<div class="notice notice-warning inline">
<p>
<strong><?php _e('Connection Disabled', 'igny8-bridge'); ?></strong><br />
<?php _e('Sync operations are currently disabled. Enable "Enable Communication" above to use these features.', 'igny8-bridge'); ?>
</p>
</div>
<?php endif; ?>
<?php
// Get counts for sync operations
$post_count = wp_count_posts('post');
$page_count = wp_count_posts('page');
$product_count = class_exists('WooCommerce') ? wp_count_posts('product') : null;
$total_posts = $post_count->publish + $page_count->publish;
if ($product_count) $total_posts += $product_count->publish;
$taxonomies = get_taxonomies(['public' => true], 'objects');
$taxonomy_count = 0;
foreach ($taxonomies as $taxonomy) {
$taxonomy_count += wp_count_terms(['taxonomy' => $taxonomy->name, 'hide_empty' => false]);
}
?>
<div class="igny8-sync-grid">
<div class="igny8-sync-card">
<div class="igny8-sync-icon">
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</div>
<h3><?php _e('Sync Posts to IGNY8', 'igny8-bridge'); ?></h3>
<p class="igny8-sync-description">
<?php printf(
__('Send %d posts, %d pages%s from WordPress to IGNY8', 'igny8-bridge'),
$post_count->publish,
$page_count->publish,
$product_count ? sprintf(', %d products', $product_count->publish) : ''
); ?>
</p>
<p class="igny8-sync-meta">
<?php if ($last_site_sync) : ?>
<span class="igny8-sync-time">⏱ <?php echo sprintf(__('Last sync: %s', 'igny8-bridge'), human_time_diff($last_site_sync, current_time('timestamp')) . ' ago'); ?></span>
<?php else : ?>
<span class="igny8-sync-time">⏱ <?php _e('Never synced', 'igny8-bridge'); ?></span>
<?php endif; ?>
</p>
<button type="button" id="igny8-sync-posts" class="button button-primary igny8-sync-button" <?php disabled(!$connection_enabled); ?>>
<span class="button-text"><?php _e('Sync Now', 'igny8-bridge'); ?></span>
<span class="button-loading" style="display: none;">
<span class="spinner is-active"></span> <?php _e('Syncing...', 'igny8-bridge'); ?>
</span>
</button>
</div>
<div class="igny8-sync-card">
<div class="igny8-sync-icon">
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
</svg>
</div>
<h3><?php _e('Sync Taxonomies', 'igny8-bridge'); ?></h3>
<p class="igny8-sync-description">
<?php printf(
__('Sync %d taxonomy terms (categories, tags, sectors, clusters) to IGNY8', 'igny8-bridge'),
$taxonomy_count
); ?>
</p>
<p class="igny8-sync-meta">
<?php if ($last_taxonomy_sync) : ?>
<span class="igny8-sync-time">⏱ <?php echo sprintf(__('Last sync: %s', 'igny8-bridge'), human_time_diff($last_taxonomy_sync, current_time('timestamp')) . ' ago'); ?></span>
<?php else : ?>
<span class="igny8-sync-time">⏱ <?php _e('Never synced', 'igny8-bridge'); ?></span>
<?php endif; ?>
</p>
<button type="button" id="igny8-sync-taxonomies" class="button button-primary igny8-sync-button" <?php disabled(!$connection_enabled); ?>>
<span class="button-text"><?php _e('Sync Now', 'igny8-bridge'); ?></span>
<span class="button-loading" style="display: none;">
<span class="spinner is-active"></span> <?php _e('Syncing...', 'igny8-bridge'); ?>
</span>
</button>
</div>
<div class="igny8-sync-card">
<div class="igny8-sync-icon">
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
</svg>
</div>
<h3><?php _e('Sync from IGNY8', 'igny8-bridge'); ?></h3>
<p class="igny8-sync-description">
<?php _e('Import new content, tasks, and updates from IGNY8 AI OS to WordPress', 'igny8-bridge'); ?>
</p>
<p class="igny8-sync-meta">
<?php if ($last_writer_sync) : ?>
<span class="igny8-sync-time">⏱ <?php echo sprintf(__('Last sync: %s', 'igny8-bridge'), human_time_diff($last_writer_sync, current_time('timestamp')) . ' ago'); ?></span>
<?php else : ?>
<span class="igny8-sync-time">⏱ <?php _e('Never synced', 'igny8-bridge'); ?></span>
<?php endif; ?>
</p>
<button type="button" id="igny8-sync-from-igny8" class="button button-primary igny8-sync-button" <?php disabled(!$connection_enabled); ?>>
<span class="button-text"><?php _e('Sync Now', 'igny8-bridge'); ?></span>
<span class="button-loading" style="display: none;">
<span class="spinner is-active"></span> <?php _e('Syncing...', 'igny8-bridge'); ?>
</span>
</button>
</div>
<div class="igny8-sync-card igny8-sync-card-highlight">
<div class="igny8-sync-icon">
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
</svg>
</div>
<h3><?php _e('Collect & Send Site Data', 'igny8-bridge'); ?></h3>
<p class="igny8-sync-description">
<?php _e('Perform full site scan: collect structure, links, SEO data, and send comprehensive report to IGNY8', 'igny8-bridge'); ?>
</p>
<p class="igny8-sync-meta">
<?php if ($last_full_site_scan) : ?>
<span class="igny8-sync-time">⏱ <?php echo sprintf(__('Last scan: %s', 'igny8-bridge'), human_time_diff($last_full_site_scan, current_time('timestamp')) . ' ago'); ?></span>
<?php else : ?>
<span class="igny8-sync-time">⏱ <?php _e('Never scanned', 'igny8-bridge'); ?></span>
<?php endif; ?>
</p>
<button type="button" id="igny8-collect-site-data" class="button button-primary igny8-sync-button" <?php disabled(!$connection_enabled); ?>>
<span class="button-text"><?php _e('Start Full Scan', 'igny8-bridge'); ?></span>
<span class="button-loading" style="display: none;">
<span class="spinner is-active"></span> <?php _e('Scanning...', 'igny8-bridge'); ?>
</span>
</button>
</div>
</div>
<div id="igny8-sync-status" class="igny8-sync-status"></div>
</div>
<div class="igny8-settings-card">
<h2><?php _e('Sync Statistics', 'igny8-bridge'); ?></h2>
<div class="igny8-stats-grid">
<div class="igny8-stat-card">
<div class="igny8-stat-icon" style="background-color: #3B82F6;">
<svg width="20" height="20" fill="none" stroke="white" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</div>
<div class="igny8-stat-content">
<div class="igny8-stat-label"><?php _e('Synced Posts', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value" id="igny8-stat-posts"><?php echo number_format($total_posts); ?></div>
<div class="igny8-stat-meta"><?php printf(__('%d posts · %d pages', 'igny8-bridge'), $post_count->publish, $page_count->publish); ?></div>
</div>
</div>
<div class="igny8-stat-card">
<div class="igny8-stat-icon" style="background-color: #10B981;">
<svg width="20" height="20" fill="none" stroke="white" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
</svg>
</div>
<div class="igny8-stat-content">
<div class="igny8-stat-label"><?php _e('Taxonomy Terms', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value" id="igny8-stat-taxonomies"><?php echo number_format($taxonomy_count); ?></div>
<div class="igny8-stat-meta"><?php _e('Categories, tags, sectors', 'igny8-bridge'); ?></div>
</div>
</div>
<div class="igny8-stat-card">
<div class="igny8-stat-icon" style="background-color: #F59E0B;">
<svg width="20" height="20" fill="none" stroke="white" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="igny8-stat-content">
<div class="igny8-stat-label"><?php _e('Last Sync', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value" id="igny8-stat-last-sync">
<?php if ($last_site_sync) : ?>
<?php echo human_time_diff($last_site_sync, current_time('timestamp')); ?> <?php _e('ago', 'igny8-bridge'); ?>
<?php else : ?>
<?php _e('Never', 'igny8-bridge'); ?>
<?php endif; ?>
</div>
<div class="igny8-stat-meta">
<?php if ($last_site_sync) : ?>
<?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_site_sync); ?>
<?php else : ?>
<?php _e('No sync performed yet', 'igny8-bridge'); ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="igny8-stat-card">
<div class="igny8-stat-icon" style="background-color: #8B5CF6;">
<svg width="20" height="20" fill="none" stroke="white" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<div class="igny8-stat-content">
<div class="igny8-stat-label"><?php _e('Connection Status', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value">
<?php if ($connection_enabled) : ?>
<span style="color: #10B981; font-size: 18px; font-weight: 600;"><?php _e('Active', 'igny8-bridge'); ?></span>
<?php else : ?>
<span style="color: #6B7280; font-size: 18px; font-weight: 600;"><?php _e('Disabled', 'igny8-bridge'); ?></span>
<?php endif; ?>
</div>
<div class="igny8-stat-meta">
<?php if ($last_health_check) : ?>
<?php _e('Last checked:', 'igny8-bridge'); ?> <?php echo human_time_diff($last_health_check, current_time('timestamp')); ?> <?php _e('ago', 'igny8-bridge'); ?>
<?php else : ?>
<?php _e('Health check pending', 'igny8-bridge'); ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php if ($last_semantic_map && !empty($semantic_summary)) : ?>
<div class="igny8-semantic-summary">
<h3><?php _e('Latest Site Analysis', 'igny8-bridge'); ?></h3>
<div class="igny8-semantic-stats">
<div class="igny8-semantic-stat">
<span class="value"><?php echo number_format($semantic_summary['sectors'] ?? 0); ?></span>
<span class="label"><?php _e('Sectors', 'igny8-bridge'); ?></span>
</div>
<div class="igny8-semantic-stat">
<span class="value"><?php echo number_format($semantic_summary['keywords'] ?? 0); ?></span>
<span class="label"><?php _e('Keywords', 'igny8-bridge'); ?></span>
</div>
<div class="igny8-semantic-stat">
<span class="value"><?php echo human_time_diff($last_semantic_map, current_time('timestamp')); ?></span>
<span class="label"><?php _e('ago', 'igny8-bridge'); ?></span>
</div>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>