Refactor IGNY8 Bridge to use API key authentication exclusively

- Removed email/password authentication and related settings from the plugin.
- Updated API connection logic to utilize only the API key for authentication.
- Simplified the admin interface by removing webhook-related settings and messages.
- Enhanced the settings page with improved UI and status indicators for API connection.
- Added a new REST API endpoint to check plugin status and connection health.
- Updated styles for a modernized look and feel across the admin interface.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-22 10:31:07 +00:00
parent 3b3be535d6
commit a0d9bccb05
10 changed files with 1064 additions and 590 deletions

View File

@@ -1,32 +1,297 @@
/**
* Admin Styles
*
* All styles for IGNY8 Bridge admin interface
* Update this file to change global design
* 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: 1200px;
max-width: 1400px;
}
.igny8-settings-card {
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
padding: 20px;
margin: 20px 0;
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: 10px;
border-bottom: 1px solid #eee;
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;
}
/* ============================================
@@ -34,103 +299,13 @@
============================================ */
.igny8-status-connected {
color: #46b450;
font-weight: bold;
color: var(--igny8-success);
font-weight: 600;
}
.igny8-status-disconnected {
color: #dc3232;
font-weight: bold;
}
.igny8-test-result {
margin-left: 10px;
}
.igny8-test-result .igny8-success {
color: #46b450;
}
.igny8-test-result .igny8-error {
color: #dc3232;
}
.igny8-test-result .igny8-loading {
color: #2271b1;
}
/* ============================================
Sync Operations
============================================ */
.igny8-sync-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.igny8-sync-actions .button {
min-width: 150px;
}
.igny8-sync-status {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
display: none;
}
.igny8-sync-status.igny8-sync-status-success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
display: block;
}
.igny8-sync-status.igny8-sync-status-error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
display: block;
}
.igny8-sync-status.igny8-sync-status-loading {
background-color: #d1ecf1;
border: 1px solid #bee5eb;
color: #0c5460;
display: block;
}
/* ============================================
Statistics
============================================ */
.igny8-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 15px;
}
.igny8-stat-item {
padding: 15px;
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
}
.igny8-stat-label {
font-size: 12px;
color: #666;
text-transform: uppercase;
margin-bottom: 8px;
}
.igny8-stat-value {
font-size: 24px;
font-weight: bold;
color: #2271b1;
color: var(--igny8-error);
font-weight: 600;
}
/* ============================================
@@ -141,47 +316,74 @@
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
margin-top: 15px;
margin-top: 20px;
}
.igny8-diagnostic-item {
padding: 15px;
background-color: #f6f7f7;
border: 1px solid #dcdcde;
border-radius: 4px;
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: #555d66;
margin-bottom: 6px;
color: #6B7280;
margin-bottom: 8px;
font-weight: 500;
}
.igny8-diagnostic-value {
font-size: 18px;
font-size: 16px;
font-weight: 600;
color: #1d2327;
color: #111827;
}
.igny8-diagnostic-item .description {
margin: 6px 0 0;
color: #646970;
color: #9CA3AF;
font-size: 12px;
}
/* ============================================
Buttons
Sync Status Messages
============================================ */
.igny8-button-group {
display: flex;
gap: 10px;
margin: 15px 0;
.igny8-sync-status {
margin-top: 20px;
padding: 16px;
border-radius: 8px;
display: none;
font-size: 14px;
}
.igny8-button-group .button {
flex: 1;
.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;
}
/* ============================================
@@ -193,18 +395,6 @@
pointer-events: none;
}
.igny8-spinner {
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid #f3f3f3;
border-top: 2px solid #2271b1;
border-radius: 50%;
animation: igny8-spin 1s linear infinite;
margin-right: 8px;
vertical-align: middle;
}
@keyframes igny8-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
@@ -215,112 +405,35 @@
============================================ */
.igny8-message {
padding: 12px;
padding: 16px;
margin: 15px 0;
border-left: 4px solid;
background: #fff;
border-radius: 4px;
}
.igny8-message.igny8-message-success {
border-color: #46b450;
background-color: #f0f8f0;
border-color: var(--igny8-success);
background-color: #F0FDF4;
color: #065F46;
}
.igny8-message.igny8-message-error {
border-color: #dc3232;
background-color: #fff5f5;
border-color: var(--igny8-error);
background-color: #FEF2F2;
color: #991B1B;
}
.igny8-message.igny8-message-info {
border-color: #2271b1;
background-color: #f0f6fc;
border-color: var(--igny8-primary);
background-color: #EFF6FF;
color: #1E40AF;
}
.igny8-message.igny8-message-warning {
border-color: #f0b849;
background-color: #fffbf0;
}
/* ============================================
Tables
============================================ */
.igny8-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.igny8-table th,
.igny8-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.igny8-table th {
background-color: #f9f9f9;
font-weight: 600;
}
.igny8-table tr:hover {
background-color: #f9f9f9;
}
/* ============================================
Admin Columns
============================================ */
.igny8-badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
line-height: 1.4;
}
.igny8-badge-igny8 {
background-color: #2271b1;
color: #fff;
}
.igny8-badge-wordpress {
background-color: #646970;
color: #fff;
}
.igny8-terms-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.igny8-term-badge {
display: inline-block;
padding: 2px 6px;
background-color: #f0f0f1;
border: 1px solid #c3c4c7;
border-radius: 2px;
font-size: 11px;
color: #50575e;
}
.igny8-empty {
color: #a7aaad;
font-style: italic;
}
.igny8-action-link {
color: #2271b1;
text-decoration: none;
cursor: pointer;
}
.igny8-action-link:hover {
color: #135e96;
text-decoration: underline;
border-color: var(--igny8-warning);
background-color: #FFFBEB;
color: #92400E;
}
/* ============================================
@@ -328,12 +441,8 @@
============================================ */
@media (max-width: 782px) {
.igny8-sync-actions {
flex-direction: column;
}
.igny8-sync-actions .button {
width: 100%;
.igny8-sync-grid {
grid-template-columns: 1fr;
}
.igny8-stats-grid {
@@ -345,3 +454,16 @@
}
}
@media (max-width: 600px) {
.igny8-settings-card {
padding: 16px;
}
.igny8-sync-card {
padding: 16px;
}
.igny8-stat-card {
flex-direction: column;
}
}

View File

@@ -62,9 +62,9 @@ class Igny8Admin {
* Register settings
*/
public function register_settings() {
register_setting('igny8_settings', 'igny8_email');
// Email/password settings removed - using API key only
register_setting('igny8_settings', 'igny8_site_id');
register_setting('igny8_settings', 'igny8_enable_two_way_sync', array(
register_setting('igny8_bridge_connection', 'igny8_connection_enabled', array(
'type' => 'boolean',
'sanitize_callback' => array($this, 'sanitize_boolean'),
'default' => 1
@@ -201,74 +201,42 @@ class Igny8Admin {
}
}
// Handle webhook secret regeneration (use wp_verify_nonce)
if (isset($_POST['igny8_regenerate_secret'])) {
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_regenerate_secret')) {
add_settings_error(
'igny8_settings',
'igny8_nonce_regen',
__('Security check failed. Could not regenerate secret.', 'igny8-bridge'),
'error'
);
} else {
$new_secret = igny8_regenerate_webhook_secret();
add_settings_error(
'igny8_settings',
'igny8_secret_regenerated',
__('Webhook secret regenerated. Update it in your IGNY8 SaaS app settings.', 'igny8-bridge'),
'updated'
);
}
}
// Webhook secret regeneration removed - using API key only
// Include settings template
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
}
/**
* Handle API connection
* Handle API connection - API key only
*/
private function handle_connection() {
$email = sanitize_email($_POST['igny8_email'] ?? '');
$password = $_POST['igny8_password'] ?? '';
$api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
// Check if API key is the placeholder (asterisks) - if so, get the stored key
$is_placeholder = (strpos($api_key, '***') !== false || $api_key === '********');
if ($is_placeholder) {
// Get the existing API key
$api_key = function_exists('igny8_get_secure_option')
? igny8_get_secure_option('igny8_api_key')
: get_option('igny8_api_key');
}
// Require email, password AND API key per updated policy
if (empty($email) || empty($password) || empty($api_key)) {
// API key is required
if (empty($api_key)) {
add_settings_error(
'igny8_settings',
'igny8_error',
__('Email, password and API key are all required to establish the connection.', 'igny8-bridge'),
__('API key is required to connect to IGNY8.', 'igny8-bridge'),
'error'
);
return;
}
// First, attempt login with email/password
// Connect using API key only
$api = new Igny8API();
if (!$api->login($email, $password)) {
if (!$api->connect($api_key)) {
add_settings_error(
'igny8_settings',
'igny8_error',
__('Failed to connect to IGNY8 API with provided credentials.', 'igny8-bridge'),
__('Failed to connect to IGNY8 API. Please verify your API key is correct.', 'igny8-bridge'),
'error'
);
return;
}
// Store email
update_option('igny8_email', $email);
// Store API key securely and also set access token to the API key for subsequent calls
// Only store if it's not the placeholder
if (!$is_placeholder) {

View File

@@ -76,60 +76,27 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
<div class="igny8-settings-card">
<h2><?php _e('API Connection', 'igny8-bridge'); ?></h2>
<?php if (!$is_connected) : ?>
<form method="post" action="">
<?php wp_nonce_field('igny8_settings_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_email"><?php _e('Email', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="email"
id="igny8_email"
name="igny8_email"
value="<?php echo esc_attr($email); ?>"
class="regular-text"
required
/>
<p class="description">
<?php _e('Your IGNY8 account email address.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igny8_api_key"><?php _e('API Key', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="password"
type="text"
id="igny8_api_key"
name="igny8_api_key"
value="<?php echo esc_attr($api_key ? '********' : ''); ?>"
value=""
class="regular-text"
placeholder="<?php _e('Paste your IGNY8 API key here (optional)', 'igny8-bridge'); ?>"
/>
<p class="description">
<?php _e('If you have an API key from the IGNY8 SaaS app, paste it here to authenticate the bridge. Leave blank to use email/password.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igny8_password"><?php _e('Password', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="password"
id="igny8_password"
name="igny8_password"
class="regular-text"
placeholder="<?php _e('Paste your IGNY8 API key here', 'igny8-bridge'); ?>"
required
/>
<p class="description">
<?php _e('Your IGNY8 account password.', 'igny8-bridge'); ?>
<?php _e('If you have an API key from the IGNY8 SaaS app, paste it here to authenticate the bridge. Leave blank to use email/password.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
@@ -137,14 +104,36 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
<?php submit_button(__('Connect to IGNY8', 'igny8-bridge'), 'primary', 'igny8_connect'); ?>
</form>
<?php else : ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_api_key"><?php _e('API Key', 'igny8-bridge'); ?></label>
</th>
<td>
<input
type="password"
id="igny8_api_key"
value="<?php echo esc_attr(str_repeat('•', min(strlen($api_key), 40))); ?>"
class="regular-text"
disabled
style="background-color: #f0f0f0;"
/>
<p class="description">
<?php _e('Connected with API key. Use the revoke button below to disconnect.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
</table>
<?php if ($api_key) : ?>
<form method="post" action="" style="margin-top:15px;">
<div style="margin-top: 15px;">
<form method="post" action="" style="display: inline-block;">
<?php wp_nonce_field('igny8_revoke_api_key'); ?>
<button type="submit" name="igny8_revoke_api_key" class="button button-secondary" onclick="return confirm('<?php _e('Revoke stored API key? This will remove the key from this site.', 'igny8-bridge'); ?>');">
<button type="submit" name="igny8_revoke_api_key" class="button button-secondary" onclick="return confirm('<?php _e('Revoke API key? This will disconnect the plugin from IGNY8.', 'igny8-bridge'); ?>');">
<?php _e('Revoke API Key', 'igny8-bridge'); ?>
</button>
</form>
</div>
<?php endif; ?>
</div>
@@ -156,72 +145,37 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
<?php settings_fields('igny8_bridge_connection'); ?>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Connection Status', 'igny8-bridge'); ?></th>
<td>
<?php if ($connection_enabled) : ?>
<span class="igny8-status-connected">
<?php _e('Connected & Active', 'igny8-bridge'); ?>
</span>
<?php else : ?>
<span class="igny8-status-disconnected">
<?php _e('Connected but Disabled', 'igny8-bridge'); ?>
</span>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row">
<label for="igny8_connection_enabled"><?php _e('Enable Sync Operations', 'igny8-bridge'); ?></label>
<label for="igny8_connection_enabled"><?php _e('Enable Communication', 'igny8-bridge'); ?></label>
</th>
<td>
<label>
<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"
/>
<?php _e('Allow sending and receiving data from IGNY8 SaaS', 'igny8-bridge'); ?>
<span class="igny8-toggle-slider"></span>
<span class="igny8-toggle-label">
<?php if ($connection_enabled) : ?>
<strong style="color: #10B981;"><?php _e('Connected', 'igny8-bridge'); ?></strong>
<?php else : ?>
<strong style="color: #6B7280;"><?php _e('Disconnected', 'igny8-bridge'); ?></strong>
<?php endif; ?>
</span>
</label>
<p class="description">
<?php _e('When disabled, all sync operations are stopped but your credentials and settings are preserved.', 'igny8-bridge'); ?>
<?php _e('Controls whether the plugin can communicate with IGNY8. API key stays connected but no data is synced when disabled.', 'igny8-bridge'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igny8_enable_two_way_sync"><?php _e('Enable Two-Way Sync', 'igny8-bridge'); ?></label>
</th>
<td>
<label>
<input
type="checkbox"
id="igny8_enable_two_way_sync"
name="igny8_enable_two_way_sync"
value="1"
<?php checked($two_way_sync, 1); ?>
/>
<?php _e('Allow bi-directional sync (IGNY8 ↔ WordPress). When disabled, outbound/inbound sync actions are suppressed but API endpoints remain accessible.', 'igny8-bridge'); ?>
</label>
</td>
</tr>
<?php if ($email) : ?>
<tr>
<th scope="row"><?php _e('Email', 'igny8-bridge'); ?></th>
<td><?php echo esc_html($email); ?></td>
</tr>
<?php endif; ?>
<?php if ($site_id) : ?>
<tr>
<th scope="row"><?php _e('Site ID', 'igny8-bridge'); ?></th>
<td><?php echo esc_html($site_id); ?></td>
</tr>
<?php endif; ?>
</table>
<?php submit_button(__('Save Connection Settings', 'igny8-bridge')); ?>
<?php submit_button(__('Save Settings', 'igny8-bridge')); ?>
</form>
<p>
@@ -487,69 +441,6 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
</tr>
</table>
</div>
<div class="igny8-settings-card">
<h2><?php _e('Link Queue', 'igny8-bridge'); ?></h2>
<?php if (!empty($pending_links)) : ?>
<p>
<strong><?php echo count($pending_links); ?></strong> <?php _e('link(s) pending insertion', 'igny8-bridge'); ?>
</p>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php _e('Post ID', 'igny8-bridge'); ?></th>
<th><?php _e('Anchor', 'igny8-bridge'); ?></th>
<th><?php _e('Target URL', 'igny8-bridge'); ?></th>
<th><?php _e('Status', 'igny8-bridge'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach (array_slice($pending_links, 0, 10) as $link) : ?>
<tr>
<td><?php echo esc_html($link['post_id']); ?></td>
<td><?php echo esc_html($link['anchor']); ?></td>
<td><code><?php echo esc_html($link['target_url']); ?></code></td>
<td><?php echo esc_html($link['status']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p><?php _e('No pending links in queue.', 'igny8-bridge'); ?></p>
<?php endif; ?>
</div>
<div class="igny8-settings-card">
<h2><?php _e('Recent Webhook Activity', 'igny8-bridge'); ?></h2>
<?php if (!empty($webhook_logs)) : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php _e('Event', 'igny8-bridge'); ?></th>
<th><?php _e('Status', 'igny8-bridge'); ?></th>
<th><?php _e('Time', 'igny8-bridge'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($webhook_logs as $log) : ?>
<tr>
<td><?php echo esc_html($log['event']); ?></td>
<td>
<span class="igny8-status-badge igny8-status-<?php echo esc_attr(strtolower($log['status'])); ?>">
<?php echo esc_html($log['status']); ?>
</span>
</td>
<td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($log['received_at']))); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else : ?>
<p><?php _e('No webhook activity yet.', 'igny8-bridge'); ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="igny8-settings-card">
@@ -570,24 +461,134 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
<div class="notice notice-warning inline">
<p>
<strong><?php _e('Connection Disabled', 'igny8-bridge'); ?></strong><br />
<?php _e('Sync operations are currently disabled. Enable "Enable Sync Operations" above to use these features.', 'igny8-bridge'); ?>
<?php _e('Sync operations are currently disabled. Enable "Enable Communication" above to use these features.', 'igny8-bridge'); ?>
</p>
</div>
<?php endif; ?>
<div class="igny8-sync-actions">
<button type="button" id="igny8-sync-posts" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
<?php _e('Sync Posts to IGNY8', 'igny8-bridge'); ?>
</button>
<button type="button" id="igny8-sync-taxonomies" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
<?php _e('Sync Taxonomies', 'igny8-bridge'); ?>
</button>
<button type="button" id="igny8-sync-from-igny8" class="button button-secondary" <?php disabled(!$connection_enabled); ?>>
<?php _e('Sync from IGNY8', 'igny8-bridge'); ?>
</button>
<button type="button" id="igny8-collect-site-data" class="button button-primary" <?php disabled(!$connection_enabled); ?>>
<?php _e('Collect & Send Site Data', 'igny8-bridge'); ?>
</button>
<?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>
@@ -597,15 +598,102 @@ $webhook_logs = igny8_get_webhook_logs(array('limit' => 10));
<h2><?php _e('Sync Statistics', 'igny8-bridge'); ?></h2>
<div class="igny8-stats-grid">
<div class="igny8-stat-item">
<div class="igny8-stat-label"><?php _e('Synced Posts', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value" id="igny8-stat-posts">-</div>
<div 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-item">
<div class="igny8-stat-label"><?php _e('Last Sync', 'igny8-bridge'); ?></div>
<div class="igny8-stat-value" id="igny8-stat-last-sync">-</div>
<div 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>