plugin attached
This commit is contained in:
627
igny8-wp-plugin/admin/assets/css/admin.css
Normal file
627
igny8-wp-plugin/admin/assets/css/admin.css
Normal file
@@ -0,0 +1,627 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/* ============================================
|
||||
API Connection Form
|
||||
============================================ */
|
||||
|
||||
.igny8-api-connection-form {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
|
||||
border: 2px solid #E5E7EB;
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
margin: 0 0 24px 0;
|
||||
}
|
||||
|
||||
.igny8-api-form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.igny8-api-form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.igny8-api-form-group input[type="text"],
|
||||
.igny8-api-form-group input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #D1D5DB;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
font-family: 'Courier New', monospace;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.igny8-api-form-group input[type="text"]:focus,
|
||||
.igny8-api-form-group input[type="password"]:focus {
|
||||
outline: none;
|
||||
border-color: var(--igny8-primary);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.igny8-api-form-group input[type="text"]:disabled {
|
||||
background-color: #F3F4F6;
|
||||
color: #9CA3AF;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.igny8-api-form-description {
|
||||
font-size: 13px;
|
||||
color: #6B7280;
|
||||
margin-top: 6px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.igny8-api-form-description a {
|
||||
color: var(--igny8-primary);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.igny8-api-form-description a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.igny8-connection-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.igny8-connection-actions .button {
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.igny8-connection-actions .button-primary {
|
||||
background: var(--igny8-primary) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.igny8-connection-actions .button-primary:hover {
|
||||
background: var(--igny8-primary-hover) !important;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.igny8-connection-actions .button-secondary {
|
||||
background: #E5E7EB !important;
|
||||
color: #111827 !important;
|
||||
}
|
||||
|
||||
.igny8-connection-actions .button-secondary:hover {
|
||||
background: #D1D5DB !important;
|
||||
}
|
||||
|
||||
.igny8-connection-status-display {
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #F9FAFB 0%, #F3F4F6 100%);
|
||||
border: 1px solid #E5E7EB;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.igny8-connection-status-display .igny8-status-label {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #6B7280;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.igny8-connection-status-display .igny8-status-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.igny8-status-indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.igny8-status-indicator.connected {
|
||||
background-color: var(--igny8-success);
|
||||
}
|
||||
|
||||
.igny8-status-indicator.disconnected {
|
||||
background-color: var(--igny8-gray);
|
||||
}
|
||||
|
||||
.igny8-api-key-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background-color: #F3F4F6;
|
||||
border-radius: 8px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.igny8-api-key-mask {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #6B7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
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'));
|
||||
|
||||
629
igny8-wp-plugin/admin/class-admin.php
Normal file
629
igny8-wp-plugin/admin/class-admin.php
Normal file
@@ -0,0 +1,629 @@
|
||||
<?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
|
||||
* Calls /v1/integration/integrations/test-connection/ endpoint
|
||||
*/
|
||||
private function handle_connection() {
|
||||
$api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
|
||||
$site_id = sanitize_text_field($_POST['igny8_site_id'] ?? '');
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Site ID is required
|
||||
if (empty($site_id)) {
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_error',
|
||||
__('Site ID is required. Create a site in IGNY8 app first.', 'igny8-bridge'),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get site URL
|
||||
$site_url = get_site_url();
|
||||
|
||||
// Test connection using the correct integration test endpoint
|
||||
// The API class will handle authentication for test-connection endpoint
|
||||
// by using the API key from the request body
|
||||
$api = new Igny8API();
|
||||
|
||||
$test_response = $api->post('/v1/integration/integrations/test-connection/', array(
|
||||
'site_id' => (int) $site_id,
|
||||
'api_key' => $api_key,
|
||||
'site_url' => $site_url
|
||||
));
|
||||
|
||||
if (!$test_response['success']) {
|
||||
$error_message = $test_response['error'] ?? 'Unknown error';
|
||||
|
||||
// Provide more user-friendly message for throttling errors
|
||||
if (isset($test_response['http_status']) && $test_response['http_status'] === 429) {
|
||||
$error_message = __('Rate limit exceeded. The plugin will automatically retry, but if this persists, please wait a moment and try again.', 'igny8-bridge');
|
||||
}
|
||||
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_error',
|
||||
sprintf(
|
||||
__('Failed to connect to IGNY8 API: %s', 'igny8-bridge'),
|
||||
$error_message
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store API key securely
|
||||
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);
|
||||
}
|
||||
|
||||
// Store site ID
|
||||
update_option('igny8_site_id', sanitize_text_field($site_id));
|
||||
|
||||
// Store integration_id from response if provided
|
||||
$response_data = $test_response['data'] ?? array();
|
||||
$integration_id = isset($response_data['integration_id']) ? intval($response_data['integration_id']) : null;
|
||||
if ($integration_id) {
|
||||
update_option('igny8_integration_id', $integration_id);
|
||||
}
|
||||
|
||||
add_settings_error(
|
||||
'igny8_settings',
|
||||
'igny8_connected',
|
||||
__('Successfully connected to IGNY8 API. Site registered.', 'igny8-bridge'),
|
||||
'updated'
|
||||
);
|
||||
|
||||
// Sync site structure to backend (post types, taxonomies, etc.)
|
||||
// Pass integration_id if available to avoid querying
|
||||
igny8_sync_site_structure_to_backend($integration_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'));
|
||||
}
|
||||
|
||||
// Get site ID
|
||||
$site_id = get_option('igny8_site_id');
|
||||
if (empty($site_id)) {
|
||||
wp_send_json_error(array('message' => 'Site ID not configured. Connect to IGNY8 first.'));
|
||||
}
|
||||
|
||||
// Test connection using the integration test endpoint
|
||||
$api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key');
|
||||
|
||||
$test_response = $api->post('/v1/integration/integrations/test-connection/', array(
|
||||
'site_id' => (int) $site_id,
|
||||
'api_key' => $api_key,
|
||||
'site_url' => get_site_url()
|
||||
));
|
||||
|
||||
if ($test_response['success']) {
|
||||
$checked_at = current_time('timestamp');
|
||||
update_option('igny8_last_api_health_check', $checked_at);
|
||||
wp_send_json_success(array(
|
||||
'message' => __('Connection successful! IGNY8 API is responsive.', 'igny8-bridge'),
|
||||
'checked_at' => $checked_at
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection failed
|
||||
$error_message = $test_response['error'] ?? 'Unknown error';
|
||||
wp_send_json_error(array(
|
||||
'message' => __('Connection failed: ', 'igny8-bridge') . $error_message,
|
||||
'http_status' => $test_response['http_status'] ?? 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'));
|
||||
|
||||
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();
|
||||
|
||||
724
igny8-wp-plugin/admin/settings.php
Normal file
724
igny8-wp-plugin/admin/settings.php
Normal file
@@ -0,0 +1,724 @@
|
||||
<?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', '');
|
||||
$integration_id = get_option('igny8_integration_id', '');
|
||||
$access_token = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_access_token') : get_option('igny8_access_token');
|
||||
$last_structure_sync = get_option('igny8_last_structure_sync', 0);
|
||||
// Connection is complete only if: API key exists, integration_id exists, and structure has been synced
|
||||
$is_connected = !empty($access_token) && !empty($integration_id) && !empty($last_structure_sync);
|
||||
$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();
|
||||
$connection_state = igny8_get_connection_state();
|
||||
$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));
|
||||
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<?php settings_errors('igny8_settings'); ?>
|
||||
|
||||
<div class="igny8-settings-container">
|
||||
<div class="igny8-settings-card">
|
||||
<h2>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.658 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
|
||||
</svg>
|
||||
<?php _e('API Connection', 'igny8-bridge'); ?>
|
||||
</h2>
|
||||
|
||||
<?php if (!$is_connected) : ?>
|
||||
<div class="igny8-connection-status-display">
|
||||
<div class="igny8-status-label"><?php _e('Status', 'igny8-bridge'); ?></div>
|
||||
<div class="igny8-status-value">
|
||||
<span class="igny8-status-indicator disconnected"></span>
|
||||
<span style="color: #6B7280;"><?php _e('Not Connected', 'igny8-bridge'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field('igny8_settings_nonce'); ?>
|
||||
|
||||
<div class="igny8-api-form-group">
|
||||
<label for="igny8_site_id">
|
||||
<?php _e('Site ID', 'igny8-bridge'); ?>
|
||||
<span style="color: #EF4444;">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="igny8_site_id"
|
||||
name="igny8_site_id"
|
||||
value=""
|
||||
placeholder="<?php _e('e.g., 123', 'igny8-bridge'); ?>"
|
||||
required
|
||||
/>
|
||||
<p class="igny8-api-form-description">
|
||||
<?php _e('The numeric Site ID from your IGNY8 app. You can find it in several ways:', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
<ul style="margin-left: 20px; margin-top: 8px; color: #6B7280;">
|
||||
<li><?php _e('In the Site Settings page URL: look for a number after "/sites/" (e.g., https://app.igny8.com/sites/123/settings → Site ID is 123)', 'igny8-bridge'); ?></li>
|
||||
<li><?php _e('In your IGNY8 dashboard: navigate to Settings → Sites, and the Site ID is displayed next to each site', 'igny8-bridge'); ?></li>
|
||||
<li><?php _e('From your account admin: contact your IGNY8 account administrator if you need help finding your Site ID', 'igny8-bridge'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="igny8-api-form-group">
|
||||
<label for="igny8_api_key">
|
||||
<?php _e('API Key', 'igny8-bridge'); ?>
|
||||
<span style="color: #EF4444;">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="igny8_api_key"
|
||||
name="igny8_api_key"
|
||||
value=""
|
||||
placeholder="<?php _e('sk_live_xxxxxxxxxxxxx', 'igny8-bridge'); ?>"
|
||||
required
|
||||
/>
|
||||
<p class="igny8-api-form-description">
|
||||
<?php printf(
|
||||
__('Get your API key from the <a href="%s" target="_blank">IGNY8 app integrations page</a>. It starts with "sk_live_" or "sk_test_".', 'igny8-bridge'),
|
||||
'https://app.igny8.com/sites/5/settings?tab=integrations'
|
||||
); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="igny8-connection-actions">
|
||||
<?php submit_button(__('Connect to IGNY8', 'igny8-bridge'), 'primary', 'igny8_connect'); ?>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php else : ?>
|
||||
<div class="igny8-connection-status-display">
|
||||
<div class="igny8-status-label"><?php _e('Status', 'igny8-bridge'); ?></div>
|
||||
<div class="igny8-status-value">
|
||||
<span class="igny8-status-indicator connected"></span>
|
||||
<span style="color: var(--igny8-success);"><?php _e('Connected', 'igny8-bridge'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="igny8-api-form-group">
|
||||
<label><?php _e('Active API Key', 'igny8-bridge'); ?></label>
|
||||
<div class="igny8-api-key-display">
|
||||
<span class="igny8-api-key-mask"><?php echo esc_html(substr($api_key, 0, 7) . '••••••••••••' . substr($api_key, -7)); ?></span>
|
||||
<span style="color: #10B981; font-weight: 500;">✓ <?php _e('Verified', 'igny8-bridge'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="igny8-api-form-group">
|
||||
<label><?php _e('Site ID', 'igny8-bridge'); ?></label>
|
||||
<div style="padding: 10px 12px; background-color: #F9FAFB; border: 1px solid #E5E7EB; border-radius: 4px;">
|
||||
<strong><?php echo esc_html($site_id ?: __('Not set', 'igny8-bridge')); ?></strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="igny8-connection-actions" style="margin-top: 20px;">
|
||||
<form method="post" action="">
|
||||
<?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('Are you sure you want to disconnect? This will stop all syncing with IGNY8.', 'igny8-bridge'); ?>');">
|
||||
<?php _e('Disconnect', 'igny8-bridge'); ?>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($is_connected) : ?>
|
||||
<div class="igny8-settings-card">
|
||||
<h2>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<?php _e('Communication Settings', '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('Disabled', 'igny8-bridge'); ?></strong>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php _e('When enabled, the plugin will sync content and data with IGNY8. Disabling pauses all sync operations but keeps your API key stored.', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php submit_button(__('Save Settings', 'igny8-bridge')); ?>
|
||||
</form>
|
||||
|
||||
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #E5E7EB;">
|
||||
<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>
|
||||
</div>
|
||||
<?php if (defined('WP_DEBUG') && WP_DEBUG) : ?>
|
||||
<p class="description" style="color: #0073aa; margin-top: 10px;">
|
||||
<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>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<?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>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"/>
|
||||
</svg>
|
||||
<?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>
|
||||
|
||||
<div class="igny8-settings-card">
|
||||
<h2>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<?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>
|
||||
<p>
|
||||
<strong><?php _e('Authentication:', 'igny8-bridge'); ?></strong> <?php _e('API Key Only (secure, modern authentication)', 'igny8-bridge'); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php if ($is_connected) : ?>
|
||||
<div class="igny8-settings-card">
|
||||
<h2>
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="vertical-align: middle; margin-right: 8px; display: inline;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"/>
|
||||
</svg>
|
||||
<?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>
|
||||
|
||||
Reference in New Issue
Block a user