1
This commit is contained in:
347
igny8-wp-plugin/admin/assets/css/admin.css
Normal file
347
igny8-wp-plugin/admin/assets/css/admin.css
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
188
igny8-wp-plugin/admin/assets/js/admin.js
Normal file
188
igny8-wp-plugin/admin/assets/js/admin.js
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 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);
|
||||
|
||||
200
igny8-wp-plugin/admin/assets/js/post-editor.js
Normal file
200
igny8-wp-plugin/admin/assets/js/post-editor.js
Normal file
@@ -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('<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);
|
||||
|
||||
306
igny8-wp-plugin/admin/class-admin-columns.php
Normal file
306
igny8-wp-plugin/admin/class-admin-columns.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?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'));
|
||||
|
||||
596
igny8-wp-plugin/admin/class-admin.php
Normal file
596
igny8-wp-plugin/admin/class-admin.php
Normal file
@@ -0,0 +1,596 @@
|
||||
<?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() {
|
||||
register_setting('igny8_settings', 'igny8_email');
|
||||
register_setting('igny8_settings', 'igny8_site_id');
|
||||
register_setting('igny8_settings', 'igny8_enable_two_way_sync', 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_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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle webhook secret regeneration (use wp_verify_nonce)
|
||||
if (isset($_POST['igny8_regenerate_secret'])) {
|
||||
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_regenerate_secret')) {
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_nonce_regen',
|
||||
__('Security check failed. Could not regenerate secret.', 'igny8-bridge'),
|
||||
'error'
|
||||
);
|
||||
} else {
|
||||
$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'] ?? '';
|
||||
$api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
|
||||
|
||||
// Check if API key is the placeholder (asterisks) - if so, get the stored key
|
||||
$is_placeholder = (strpos($api_key, '***') !== false || $api_key === '********');
|
||||
if ($is_placeholder) {
|
||||
// Get the existing API key
|
||||
$api_key = function_exists('igny8_get_secure_option')
|
||||
? igny8_get_secure_option('igny8_api_key')
|
||||
: get_option('igny8_api_key');
|
||||
}
|
||||
|
||||
// Require email, password AND API key per updated policy
|
||||
if (empty($email) || empty($password) || empty($api_key)) {
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_error',
|
||||
__('Email, password and API key are all required to establish the connection.', 'igny8-bridge'),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// First, attempt login with email/password
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->login($email, $password)) {
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_error',
|
||||
__('Failed to connect to IGNY8 API with provided credentials.', 'igny8-bridge'),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store email
|
||||
update_option('igny8_email', $email);
|
||||
|
||||
// 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'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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'));
|
||||
|
||||
469
igny8-wp-plugin/admin/class-post-meta-boxes.php
Normal file
469
igny8-wp-plugin/admin/class-post-meta-boxes.php
Normal file
@@ -0,0 +1,469 @@
|
||||
<?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();
|
||||
|
||||
621
igny8-wp-plugin/admin/settings.php
Normal file
621
igny8-wp-plugin/admin/settings.php
Normal file
@@ -0,0 +1,621 @@
|
||||
<?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();
|
||||
$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>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field('igny8_settings_nonce'); ?>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="igny8_email"><?php _e('Email', 'igny8-bridge'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="email"
|
||||
id="igny8_email"
|
||||
name="igny8_email"
|
||||
value="<?php echo esc_attr($email); ?>"
|
||||
class="regular-text"
|
||||
required
|
||||
/>
|
||||
<p class="description">
|
||||
<?php _e('Your IGNY8 account email address.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<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"
|
||||
name="igny8_api_key"
|
||||
value="<?php echo esc_attr($api_key ? '********' : ''); ?>"
|
||||
class="regular-text"
|
||||
placeholder="<?php _e('Paste your IGNY8 API key here (optional)', 'igny8-bridge'); ?>"
|
||||
/>
|
||||
<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>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="igny8_password"><?php _e('Password', 'igny8-bridge'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="password"
|
||||
id="igny8_password"
|
||||
name="igny8_password"
|
||||
class="regular-text"
|
||||
required
|
||||
/>
|
||||
<p class="description">
|
||||
<?php _e('Your IGNY8 account password.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php submit_button(__('Connect to IGNY8', 'igny8-bridge'), 'primary', 'igny8_connect'); ?>
|
||||
</form>
|
||||
|
||||
<?php if ($api_key) : ?>
|
||||
<form method="post" action="" style="margin-top:15px;">
|
||||
<?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 stored API key? This will remove the key from this site.', 'igny8-bridge'); ?>');">
|
||||
<?php _e('Revoke API Key', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
</form>
|
||||
<?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"><?php _e('Connection Status', 'igny8-bridge'); ?></th>
|
||||
<td>
|
||||
<?php if ($connection_enabled) : ?>
|
||||
<span class="igny8-status-connected">
|
||||
<?php _e('Connected & Active', 'igny8-bridge'); ?>
|
||||
</span>
|
||||
<?php else : ?>
|
||||
<span class="igny8-status-disconnected">
|
||||
<?php _e('Connected but Disabled', 'igny8-bridge'); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="igny8_connection_enabled"><?php _e('Enable Sync Operations', 'igny8-bridge'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="igny8_connection_enabled"
|
||||
name="igny8_connection_enabled"
|
||||
value="1"
|
||||
<?php checked($connection_enabled, 1); ?>
|
||||
/>
|
||||
<?php _e('Allow sending and receiving data from IGNY8 SaaS', 'igny8-bridge'); ?>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php _e('When disabled, all sync operations are stopped but your credentials and settings are preserved.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="igny8_enable_two_way_sync"><?php _e('Enable Two-Way Sync', 'igny8-bridge'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="igny8_enable_two_way_sync"
|
||||
name="igny8_enable_two_way_sync"
|
||||
value="1"
|
||||
<?php checked($two_way_sync, 1); ?>
|
||||
/>
|
||||
<?php _e('Allow bi-directional sync (IGNY8 ↔ WordPress). When disabled, outbound/inbound sync actions are suppressed but API endpoints remain accessible.', 'igny8-bridge'); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if ($email) : ?>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Email', 'igny8-bridge'); ?></th>
|
||||
<td><?php echo esc_html($email); ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php if ($site_id) : ?>
|
||||
<tr>
|
||||
<th scope="row"><?php _e('Site ID', 'igny8-bridge'); ?></th>
|
||||
<td><?php echo esc_html($site_id); ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
|
||||
<?php submit_button(__('Save Connection 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 Data', '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('Include products, categories, and inventory during site scans.', '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">Taxonomy ID</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="igny8_taxonomy_id"
|
||||
name="igny8_taxonomy_id"
|
||||
value="<?php echo esc_attr(get_option('igny8_taxonomy_id', '')); ?>"
|
||||
class="regular-text"
|
||||
/>
|
||||
<p class="description">
|
||||
<?php _e('The taxonomy ID used for IGNY8 synchronization.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Attribute ID</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="igny8_attribute_id"
|
||||
name="igny8_attribute_id"
|
||||
value="<?php echo esc_attr(get_option('igny8_attribute_id', '')); ?>"
|
||||
class="regular-text"
|
||||
/>
|
||||
<p class="description">
|
||||
<?php _e('The attribute ID used for IGNY8 synchronization.', '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>
|
||||
|
||||
<div class="igny8-settings-card">
|
||||
<h2><?php _e('Link Queue', 'igny8-bridge'); ?></h2>
|
||||
|
||||
<?php if (!empty($pending_links)) : ?>
|
||||
<p>
|
||||
<strong><?php echo count($pending_links); ?></strong> <?php _e('link(s) pending insertion', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Post ID', 'igny8-bridge'); ?></th>
|
||||
<th><?php _e('Anchor', 'igny8-bridge'); ?></th>
|
||||
<th><?php _e('Target URL', 'igny8-bridge'); ?></th>
|
||||
<th><?php _e('Status', 'igny8-bridge'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (array_slice($pending_links, 0, 10) as $link) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($link['post_id']); ?></td>
|
||||
<td><?php echo esc_html($link['anchor']); ?></td>
|
||||
<td><code><?php echo esc_html($link['target_url']); ?></code></td>
|
||||
<td><?php echo esc_html($link['status']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else : ?>
|
||||
<p><?php _e('No pending links in queue.', 'igny8-bridge'); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="igny8-settings-card">
|
||||
<h2><?php _e('Recent Webhook Activity', 'igny8-bridge'); ?></h2>
|
||||
|
||||
<?php if (!empty($webhook_logs)) : ?>
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Event', 'igny8-bridge'); ?></th>
|
||||
<th><?php _e('Status', 'igny8-bridge'); ?></th>
|
||||
<th><?php _e('Time', 'igny8-bridge'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($webhook_logs as $log) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($log['event']); ?></td>
|
||||
<td>
|
||||
<span class="igny8-status-badge igny8-status-<?php echo esc_attr(strtolower($log['status'])); ?>">
|
||||
<?php echo esc_html($log['status']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($log['received_at']))); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else : ?>
|
||||
<p><?php _e('No webhook activity yet.', 'igny8-bridge'); ?></p>
|
||||
<?php endif; ?>
|
||||
</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 Sync Operations" above to use these features.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="igny8-sync-actions">
|
||||
<button type="button" id="igny8-sync-posts" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
|
||||
<?php _e('Sync Posts to IGNY8', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
<button type="button" id="igny8-sync-taxonomies" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
|
||||
<?php _e('Sync Taxonomies', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
<button type="button" id="igny8-sync-from-igny8" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
|
||||
<?php _e('Sync from IGNY8', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
<button type="button" id="igny8-collect-site-data" class="button button-primary" <?php disabled(!$connection_enabled); ?>>
|
||||
<?php _e('Collect & Send Site Data', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
</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-item">
|
||||
<div class="igny8-stat-label"><?php _e('Synced Posts', 'igny8-bridge'); ?></div>
|
||||
<div class="igny8-stat-value" id="igny8-stat-posts">-</div>
|
||||
</div>
|
||||
<div class="igny8-stat-item">
|
||||
<div class="igny8-stat-label"><?php _e('Last Sync', 'igny8-bridge'); ?></div>
|
||||
<div class="igny8-stat-value" id="igny8-stat-last-sync">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user