597 lines
20 KiB
PHP
597 lines
20 KiB
PHP
<?php
|
|
/**
|
|
* Admin Interface Class
|
|
*
|
|
* Handles all admin functionality
|
|
*
|
|
* @package Igny8Bridge
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Igny8Admin Class
|
|
*/
|
|
class Igny8Admin {
|
|
|
|
/**
|
|
* Single instance of the class
|
|
*
|
|
* @var Igny8Admin
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Get single instance
|
|
*
|
|
* @return Igny8Admin
|
|
*/
|
|
public static function get_instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
add_action('admin_menu', array($this, 'add_menu_pages'));
|
|
add_action('admin_init', array($this, 'register_settings'));
|
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
|
|
}
|
|
|
|
/**
|
|
* Add admin menu pages
|
|
*/
|
|
public function add_menu_pages() {
|
|
add_options_page(
|
|
__('IGNY8 API Settings', 'igny8-bridge'),
|
|
__('IGNY8 API', 'igny8-bridge'),
|
|
'manage_options',
|
|
'igny8-settings',
|
|
array($this, 'render_settings_page')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Register settings
|
|
*/
|
|
public function register_settings() {
|
|
register_setting('igny8_settings', 'igny8_email');
|
|
register_setting('igny8_settings', 'igny8_site_id');
|
|
register_setting('igny8_settings', 'igny8_enable_two_way_sync', array(
|
|
'type' => 'boolean',
|
|
'sanitize_callback' => array($this, 'sanitize_boolean'),
|
|
'default' => 1
|
|
));
|
|
|
|
register_setting('igny8_bridge_connection', 'igny8_connection_enabled', array(
|
|
'type' => 'boolean',
|
|
'sanitize_callback' => array($this, 'sanitize_boolean'),
|
|
'default' => 1
|
|
));
|
|
|
|
register_setting('igny8_bridge_controls', 'igny8_enabled_post_types', array(
|
|
'type' => 'array',
|
|
'sanitize_callback' => array($this, 'sanitize_post_types'),
|
|
'default' => array_keys(igny8_get_supported_post_types())
|
|
));
|
|
|
|
register_setting('igny8_bridge_controls', 'igny8_enable_woocommerce', array(
|
|
'type' => 'boolean',
|
|
'sanitize_callback' => array($this, 'sanitize_boolean'),
|
|
'default' => class_exists('WooCommerce') ? 1 : 0
|
|
));
|
|
|
|
register_setting('igny8_bridge_controls', 'igny8_control_mode', array(
|
|
'type' => 'string',
|
|
'sanitize_callback' => array($this, 'sanitize_control_mode'),
|
|
'default' => 'mirror'
|
|
));
|
|
|
|
register_setting('igny8_bridge_controls', 'igny8_enabled_modules', array(
|
|
'type' => 'array',
|
|
'sanitize_callback' => array($this, 'sanitize_modules'),
|
|
'default' => array_keys(igny8_get_available_modules())
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Enqueue admin scripts and styles
|
|
*
|
|
* @param string $hook Current admin page hook
|
|
*/
|
|
public function enqueue_scripts($hook) {
|
|
// Enqueue on settings page
|
|
if ($hook === 'settings_page_igny8-settings') {
|
|
wp_enqueue_style(
|
|
'igny8-admin-style',
|
|
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
|
|
array(),
|
|
IGNY8_BRIDGE_VERSION
|
|
);
|
|
|
|
wp_enqueue_script(
|
|
'igny8-admin-script',
|
|
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
|
|
array('jquery'),
|
|
IGNY8_BRIDGE_VERSION,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('igny8-admin-script', 'igny8Admin', array(
|
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('igny8_admin_nonce'),
|
|
));
|
|
}
|
|
|
|
// Enqueue on post/page/product list pages
|
|
if (strpos($hook, 'edit.php') !== false) {
|
|
$screen = get_current_screen();
|
|
if ($screen && in_array($screen->post_type, array('post', 'page', 'product', ''))) {
|
|
wp_enqueue_style(
|
|
'igny8-admin-style',
|
|
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
|
|
array(),
|
|
IGNY8_BRIDGE_VERSION
|
|
);
|
|
|
|
wp_enqueue_script(
|
|
'igny8-admin-script',
|
|
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
|
|
array('jquery'),
|
|
IGNY8_BRIDGE_VERSION,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('igny8-admin-script', 'igny8Admin', array(
|
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('igny8_admin_nonce'),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render settings page
|
|
*/
|
|
public function render_settings_page() {
|
|
// Handle form submission (use wp_verify_nonce to avoid wp_die on failure)
|
|
if (isset($_POST['igny8_connect'])) {
|
|
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_settings_nonce')) {
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_nonce',
|
|
__('Security check failed. Please refresh the page and try again.', 'igny8-bridge'),
|
|
'error'
|
|
);
|
|
} else {
|
|
$this->handle_connection();
|
|
}
|
|
}
|
|
|
|
// Handle revoke API key (use wp_verify_nonce)
|
|
if (isset($_POST['igny8_revoke_api_key'])) {
|
|
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_revoke_api_key')) {
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_nonce_revoke',
|
|
__('Security check failed. Could not revoke API key.', 'igny8-bridge'),
|
|
'error'
|
|
);
|
|
} else {
|
|
self::revoke_api_key();
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_api_key_revoked',
|
|
__('API key revoked and removed from this site.', 'igny8-bridge'),
|
|
'updated'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Handle webhook secret regeneration (use wp_verify_nonce)
|
|
if (isset($_POST['igny8_regenerate_secret'])) {
|
|
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_regenerate_secret')) {
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_nonce_regen',
|
|
__('Security check failed. Could not regenerate secret.', 'igny8-bridge'),
|
|
'error'
|
|
);
|
|
} else {
|
|
$new_secret = igny8_regenerate_webhook_secret();
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_secret_regenerated',
|
|
__('Webhook secret regenerated. Update it in your IGNY8 SaaS app settings.', 'igny8-bridge'),
|
|
'updated'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Include settings template
|
|
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
|
|
}
|
|
|
|
/**
|
|
* Handle API connection
|
|
*/
|
|
private function handle_connection() {
|
|
$email = sanitize_email($_POST['igny8_email'] ?? '');
|
|
$password = $_POST['igny8_password'] ?? '';
|
|
$api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
|
|
|
|
// Check if API key is the placeholder (asterisks) - if so, get the stored key
|
|
$is_placeholder = (strpos($api_key, '***') !== false || $api_key === '********');
|
|
if ($is_placeholder) {
|
|
// Get the existing API key
|
|
$api_key = function_exists('igny8_get_secure_option')
|
|
? igny8_get_secure_option('igny8_api_key')
|
|
: get_option('igny8_api_key');
|
|
}
|
|
|
|
// Require email, password AND API key per updated policy
|
|
if (empty($email) || empty($password) || empty($api_key)) {
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_error',
|
|
__('Email, password and API key are all required to establish the connection.', 'igny8-bridge'),
|
|
'error'
|
|
);
|
|
return;
|
|
}
|
|
|
|
// First, attempt login with email/password
|
|
$api = new Igny8API();
|
|
|
|
if (!$api->login($email, $password)) {
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_error',
|
|
__('Failed to connect to IGNY8 API with provided credentials.', 'igny8-bridge'),
|
|
'error'
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Store email
|
|
update_option('igny8_email', $email);
|
|
|
|
// Store API key securely and also set access token to the API key for subsequent calls
|
|
// Only store if it's not the placeholder
|
|
if (!$is_placeholder) {
|
|
if (function_exists('igny8_store_secure_option')) {
|
|
igny8_store_secure_option('igny8_api_key', $api_key);
|
|
igny8_store_secure_option('igny8_access_token', $api_key);
|
|
} else {
|
|
update_option('igny8_api_key', $api_key);
|
|
update_option('igny8_access_token', $api_key);
|
|
}
|
|
}
|
|
|
|
// Try to get site ID (if available) using the authenticated client
|
|
$site_response = $api->get('/system/sites/');
|
|
if ($site_response['success'] && !empty($site_response['results'])) {
|
|
$site = $site_response['results'][0];
|
|
update_option('igny8_site_id', $site['id']);
|
|
}
|
|
|
|
add_settings_error(
|
|
'igny8_settings',
|
|
'igny8_connected',
|
|
__('Successfully connected to IGNY8 API and stored API key.', 'igny8-bridge'),
|
|
'updated'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Revoke stored API key (secure delete)
|
|
*
|
|
* Public so tests can call it directly.
|
|
*/
|
|
public static function revoke_api_key() {
|
|
if (function_exists('igny8_delete_secure_option')) {
|
|
igny8_delete_secure_option('igny8_api_key');
|
|
igny8_delete_secure_option('igny8_access_token');
|
|
igny8_delete_secure_option('igny8_refresh_token');
|
|
} else {
|
|
delete_option('igny8_api_key');
|
|
delete_option('igny8_access_token');
|
|
delete_option('igny8_refresh_token');
|
|
}
|
|
|
|
// Also clear token-issued timestamps
|
|
delete_option('igny8_token_refreshed_at');
|
|
delete_option('igny8_access_token_issued');
|
|
}
|
|
|
|
/**
|
|
* Test API connection (AJAX handler)
|
|
*/
|
|
public static function test_connection() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
if (!igny8_is_connection_enabled()) {
|
|
wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations to test.'));
|
|
}
|
|
|
|
$api = new Igny8API();
|
|
|
|
if (!$api->is_authenticated()) {
|
|
wp_send_json_error(array('message' => 'Not authenticated'));
|
|
}
|
|
|
|
// Try multiple endpoints to find one that works
|
|
$test_endpoints = array(
|
|
'/system/ping/' => 'System ping endpoint',
|
|
'/planner/keywords/?page_size=1' => 'Planner keywords list',
|
|
'/system/sites/' => 'Sites list'
|
|
);
|
|
|
|
$last_error = '';
|
|
$last_status = 0;
|
|
|
|
foreach ($test_endpoints as $endpoint => $description) {
|
|
$response = $api->get($endpoint);
|
|
|
|
if ($response['success']) {
|
|
$checked_at = current_time('timestamp');
|
|
update_option('igny8_last_api_health_check', $checked_at);
|
|
wp_send_json_success(array(
|
|
'message' => 'Connection successful (tested: ' . $description . ')',
|
|
'endpoint' => $endpoint,
|
|
'checked_at' => $checked_at
|
|
));
|
|
return;
|
|
}
|
|
|
|
$last_error = $response['error'] ?? 'Unknown error';
|
|
$last_status = $response['http_status'] ?? 0;
|
|
}
|
|
|
|
// All endpoints failed
|
|
wp_send_json_error(array(
|
|
'message' => 'Connection failed: ' . $last_error,
|
|
'http_status' => $last_status,
|
|
'full_error' => $last_error,
|
|
'endpoints_tested' => array_keys($test_endpoints)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Sync posts to IGNY8 (AJAX handler)
|
|
*/
|
|
public static function sync_posts() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
if (!igny8_is_connection_enabled()) {
|
|
wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
|
|
}
|
|
|
|
$result = igny8_batch_sync_post_statuses();
|
|
|
|
wp_send_json_success(array(
|
|
'message' => sprintf('Synced %d posts, %d failed', $result['synced'], $result['failed']),
|
|
'data' => $result
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Sync taxonomies (AJAX handler)
|
|
*/
|
|
public static function sync_taxonomies() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
if (!igny8_is_connection_enabled()) {
|
|
wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
|
|
}
|
|
|
|
$api = new Igny8API();
|
|
if (!$api->is_authenticated()) {
|
|
wp_send_json_error(array('message' => 'Not authenticated'));
|
|
}
|
|
|
|
// Sync sectors and clusters from IGNY8
|
|
$sectors_result = igny8_sync_igny8_sectors_to_wp();
|
|
$clusters_result = igny8_sync_igny8_clusters_to_wp();
|
|
|
|
wp_send_json_success(array(
|
|
'message' => sprintf('Synced %d sectors, %d clusters',
|
|
$sectors_result['synced'] ?? 0,
|
|
$clusters_result['synced'] ?? 0
|
|
),
|
|
'data' => array(
|
|
'sectors' => $sectors_result,
|
|
'clusters' => $clusters_result
|
|
)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Sync from IGNY8 (AJAX handler)
|
|
*/
|
|
public static function sync_from_igny8() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
if (!igny8_is_connection_enabled()) {
|
|
wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
|
|
}
|
|
|
|
$result = igny8_sync_igny8_tasks_to_wp();
|
|
|
|
if ($result['success']) {
|
|
wp_send_json_success(array(
|
|
'message' => sprintf('Created %d posts, updated %d posts',
|
|
$result['created'],
|
|
$result['updated']
|
|
),
|
|
'data' => $result
|
|
));
|
|
} else {
|
|
wp_send_json_error(array(
|
|
'message' => $result['error'] ?? 'Sync failed'
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collect and send site data (AJAX handler)
|
|
*/
|
|
public static function collect_site_data() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
if (!igny8_is_connection_enabled()) {
|
|
wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
|
|
}
|
|
|
|
$site_id = get_option('igny8_site_id');
|
|
if (!$site_id) {
|
|
wp_send_json_error(array('message' => 'Site ID not set'));
|
|
}
|
|
|
|
$result = igny8_send_site_data_to_igny8($site_id);
|
|
|
|
if ($result) {
|
|
wp_send_json_success(array(
|
|
'message' => 'Site data collected and sent successfully',
|
|
'data' => $result
|
|
));
|
|
} else {
|
|
wp_send_json_error(array('message' => 'Failed to send site data'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get sync statistics (AJAX handler)
|
|
*/
|
|
public static function get_stats() {
|
|
check_ajax_referer('igny8_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(array('message' => 'Unauthorized'));
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
// Count synced posts
|
|
$synced_posts = $wpdb->get_var("
|
|
SELECT COUNT(DISTINCT post_id)
|
|
FROM {$wpdb->postmeta}
|
|
WHERE meta_key = '_igny8_task_id'
|
|
");
|
|
|
|
// Get last sync time
|
|
$last_sync = get_option('igny8_last_site_sync', 0);
|
|
$last_sync_formatted = $last_sync ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_sync) : 'Never';
|
|
|
|
wp_send_json_success(array(
|
|
'synced_posts' => intval($synced_posts),
|
|
'last_sync' => $last_sync_formatted
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Sanitize post types option
|
|
*
|
|
* @param mixed $value Raw value
|
|
* @return array
|
|
*/
|
|
public function sanitize_post_types($value) {
|
|
$supported = array_keys(igny8_get_supported_post_types());
|
|
|
|
if (!is_array($value)) {
|
|
return $supported;
|
|
}
|
|
|
|
$clean = array();
|
|
foreach ($value as $post_type) {
|
|
$post_type = sanitize_key($post_type);
|
|
if (in_array($post_type, $supported, true)) {
|
|
$clean[] = $post_type;
|
|
}
|
|
}
|
|
|
|
return !empty($clean) ? $clean : $supported;
|
|
}
|
|
|
|
/**
|
|
* Sanitize boolean option
|
|
*
|
|
* @param mixed $value Raw value
|
|
* @return int
|
|
*/
|
|
public function sanitize_boolean($value) {
|
|
return $value ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Sanitize control mode
|
|
*
|
|
* @param mixed $value Raw value
|
|
* @return string
|
|
*/
|
|
public function sanitize_control_mode($value) {
|
|
$value = is_string($value) ? strtolower($value) : 'mirror';
|
|
return in_array($value, array('mirror', 'hybrid'), true) ? $value : 'mirror';
|
|
}
|
|
|
|
/**
|
|
* Sanitize module toggles
|
|
*
|
|
* @param mixed $value Raw value
|
|
* @return array
|
|
*/
|
|
public function sanitize_modules($value) {
|
|
$supported = array_keys(igny8_get_available_modules());
|
|
|
|
if (!is_array($value)) {
|
|
return $supported;
|
|
}
|
|
|
|
$clean = array();
|
|
foreach ($value as $module) {
|
|
$module = sanitize_key($module);
|
|
if (in_array($module, $supported, true)) {
|
|
$clean[] = $module;
|
|
}
|
|
}
|
|
|
|
return !empty($clean) ? $clean : $supported;
|
|
}
|
|
}
|
|
|
|
// Register AJAX handlers
|
|
add_action('wp_ajax_igny8_test_connection', array('Igny8Admin', 'test_connection'));
|
|
add_action('wp_ajax_igny8_sync_posts', array('Igny8Admin', 'sync_posts'));
|
|
add_action('wp_ajax_igny8_sync_taxonomies', array('Igny8Admin', 'sync_taxonomies'));
|
|
add_action('wp_ajax_igny8_sync_from_igny8', array('Igny8Admin', 'sync_from_igny8'));
|
|
add_action('wp_ajax_igny8_collect_site_data', array('Igny8Admin', 'collect_site_data'));
|
|
add_action('wp_ajax_igny8_get_stats', array('Igny8Admin', 'get_stats'));
|
|
|