wp plugin loaded
This commit is contained in:
605
igny8-wp-integration-plugin/includes/functions.php
Normal file
605
igny8-wp-integration-plugin/includes/functions.php
Normal file
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
/**
|
||||
* Helper Functions
|
||||
*
|
||||
* WordPress integration functions for IGNY8 Bridge
|
||||
*
|
||||
* @package Igny8Bridge
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encryption key for secure option storage
|
||||
*
|
||||
* @return string Binary key
|
||||
*/
|
||||
function igny8_get_encryption_key() {
|
||||
$salt = wp_salt('auth');
|
||||
return hash('sha256', 'igny8_bridge_' . $salt, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a value for storage
|
||||
*
|
||||
* @param string $value Plain text value
|
||||
* @return string Encrypted value with prefix or original value on failure
|
||||
*/
|
||||
function igny8_encrypt_value($value) {
|
||||
if ($value === '' || $value === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!function_exists('openssl_encrypt')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$iv = openssl_random_pseudo_bytes(16);
|
||||
$cipher = openssl_encrypt($value, 'AES-256-CBC', igny8_get_encryption_key(), OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
if ($cipher === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return 'igny8|' . base64_encode($iv . $cipher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a stored value
|
||||
*
|
||||
* @param string $value Stored value
|
||||
* @return string Decrypted value or original on failure
|
||||
*/
|
||||
function igny8_decrypt_value($value) {
|
||||
if (!is_string($value) || strpos($value, 'igny8|') !== 0) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!function_exists('openssl_decrypt')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$encoded = substr($value, 6);
|
||||
$data = base64_decode($encoded, true);
|
||||
|
||||
if ($data === false || strlen($data) <= 16) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$iv = substr($data, 0, 16);
|
||||
$cipher = substr($data, 16);
|
||||
|
||||
$plain = openssl_decrypt($cipher, 'AES-256-CBC', igny8_get_encryption_key(), OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
return ($plain === false) ? $value : $plain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an option securely
|
||||
*
|
||||
* @param string $option Option name
|
||||
* @param string $value Value to store
|
||||
*/
|
||||
function igny8_store_secure_option($option, $value) {
|
||||
if ($value === null || $value === '') {
|
||||
delete_option($option);
|
||||
return;
|
||||
}
|
||||
|
||||
update_option($option, igny8_encrypt_value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve secure option (with legacy fallback)
|
||||
*
|
||||
* @param string $option Option name
|
||||
* @return string Value
|
||||
*/
|
||||
function igny8_get_secure_option($option) {
|
||||
$stored = get_option($option);
|
||||
|
||||
if (!$stored) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = igny8_decrypt_value($stored);
|
||||
|
||||
return is_string($value) ? $value : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported post types for automation
|
||||
*
|
||||
* @return array Key => label
|
||||
*/
|
||||
function igny8_get_supported_post_types() {
|
||||
$types = array(
|
||||
'post' => __('Posts', 'igny8-bridge'),
|
||||
'page' => __('Pages', 'igny8-bridge'),
|
||||
);
|
||||
|
||||
if (post_type_exists('product')) {
|
||||
$types['product'] = __('Products', 'igny8-bridge');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the list of selectable post types.
|
||||
*
|
||||
* @param array $types
|
||||
*/
|
||||
return apply_filters('igny8_supported_post_types', $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enabled post types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function igny8_get_enabled_post_types() {
|
||||
$saved = get_option('igny8_enabled_post_types');
|
||||
|
||||
if (is_array($saved) && !empty($saved)) {
|
||||
return $saved;
|
||||
}
|
||||
|
||||
return array_keys(igny8_get_supported_post_types());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured control mode
|
||||
*
|
||||
* @return string mirror|hybrid
|
||||
*/
|
||||
function igny8_get_control_mode() {
|
||||
$mode = get_option('igny8_control_mode', 'mirror');
|
||||
return in_array($mode, array('mirror', 'hybrid'), true) ? $mode : 'mirror';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available automation modules
|
||||
*
|
||||
* @return array Key => label
|
||||
*/
|
||||
function igny8_get_available_modules() {
|
||||
$modules = array(
|
||||
'sites' => __('Sites (Data & Semantic Map)', 'igny8-bridge'),
|
||||
'planner' => __('Planner (Keywords & Briefs)', 'igny8-bridge'),
|
||||
'writer' => __('Writer (Tasks & Posts)', 'igny8-bridge'),
|
||||
'linker' => __('Linker (Internal Links)', 'igny8-bridge'),
|
||||
'optimizer' => __('Optimizer (Audits & Scores)', 'igny8-bridge'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter the list of IGNY8 modules that can be toggled.
|
||||
*
|
||||
* @param array $modules
|
||||
*/
|
||||
return apply_filters('igny8_available_modules', $modules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enabled modules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function igny8_get_enabled_modules() {
|
||||
$saved = get_option('igny8_enabled_modules');
|
||||
|
||||
if (is_array($saved) && !empty($saved)) {
|
||||
return $saved;
|
||||
}
|
||||
|
||||
return array_keys(igny8_get_available_modules());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a module is enabled
|
||||
*
|
||||
* @param string $module Module key
|
||||
* @return bool
|
||||
*/
|
||||
function igny8_is_module_enabled($module) {
|
||||
$modules = igny8_get_enabled_modules();
|
||||
return in_array($module, $modules, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a post type is enabled for automation
|
||||
*
|
||||
* @param string $post_type Post type key
|
||||
* @return bool
|
||||
*/
|
||||
function igny8_is_post_type_enabled($post_type) {
|
||||
$post_types = igny8_get_enabled_post_types();
|
||||
return in_array($post_type, $post_types, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IGNY8 connection is enabled
|
||||
* This is a master switch that disables all sync operations while preserving credentials
|
||||
*
|
||||
* @return bool True if connection is enabled
|
||||
*/
|
||||
function igny8_is_connection_enabled() {
|
||||
$enabled = get_option('igny8_connection_enabled', 1);
|
||||
return (bool) $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get webhook shared secret
|
||||
*
|
||||
* @return string Webhook secret
|
||||
*/
|
||||
function igny8_get_webhook_secret() {
|
||||
$secret = get_option('igny8_webhook_secret');
|
||||
|
||||
if (empty($secret)) {
|
||||
// Generate secret if not exists
|
||||
$secret = wp_generate_password(64, false);
|
||||
update_option('igny8_webhook_secret', $secret);
|
||||
}
|
||||
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate webhook secret
|
||||
*
|
||||
* @return string New secret
|
||||
*/
|
||||
function igny8_regenerate_webhook_secret() {
|
||||
$secret = wp_generate_password(64, false);
|
||||
update_option('igny8_webhook_secret', $secret);
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for site scans
|
||||
*
|
||||
* @param array $overrides Override defaults
|
||||
* @return array
|
||||
*/
|
||||
function igny8_get_site_scan_settings($overrides = array()) {
|
||||
$defaults = array(
|
||||
'post_types' => igny8_get_enabled_post_types(),
|
||||
'include_products' => (bool) get_option('igny8_enable_woocommerce', class_exists('WooCommerce') ? 1 : 0),
|
||||
'per_page' => 100,
|
||||
'since' => null,
|
||||
'mode' => 'full',
|
||||
);
|
||||
|
||||
$settings = wp_parse_args($overrides, $defaults);
|
||||
|
||||
return apply_filters('igny8_site_scan_settings', $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register IGNY8 post meta fields
|
||||
*/
|
||||
function igny8_register_post_meta() {
|
||||
$post_types = array('post', 'page', 'product');
|
||||
|
||||
// Define all meta fields with proper schema for REST API
|
||||
$meta_fields = array(
|
||||
'_igny8_task_id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'IGNY8 task ID linked to this post',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_cluster_id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'IGNY8 cluster ID',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_sector_id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'IGNY8 sector ID',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_keyword_ids' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'Array of IGNY8 keyword IDs',
|
||||
'single' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'integer'
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'_igny8_content_id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'IGNY8 content ID',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_wordpress_status' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'WordPress post status (for IGNY8 reference)',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_last_synced' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Last sync timestamp',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_task_brief' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'Cached IGNY8 writer brief data',
|
||||
'single' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'object'
|
||||
)
|
||||
)
|
||||
),
|
||||
'_igny8_brief_cached_at' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'Timestamp of cached IGNY8 brief',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_optimizer_job_id' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'IGNY8 optimizer job ID',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_optimizer_status' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'IGNY8 optimizer job status',
|
||||
'single' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'_igny8_optimizer_score_changes' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'IGNY8 optimizer score changes',
|
||||
'single' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'object'
|
||||
)
|
||||
)
|
||||
),
|
||||
'_igny8_optimizer_recommendations' => array(
|
||||
'type' => 'array',
|
||||
'description' => 'IGNY8 optimizer recommendations',
|
||||
'single' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'array'
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
// Register each meta field for all relevant post types
|
||||
foreach ($meta_fields as $meta_key => $config) {
|
||||
foreach ($post_types as $post_type) {
|
||||
register_post_meta($post_type, $meta_key, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register IGNY8 taxonomies
|
||||
*/
|
||||
function igny8_register_taxonomies() {
|
||||
// Register sectors taxonomy (hierarchical) - only if not exists
|
||||
if (!taxonomy_exists('igny8_sectors')) {
|
||||
register_taxonomy('igny8_sectors', array('post', 'page', 'product'), array(
|
||||
'hierarchical' => true,
|
||||
'labels' => array(
|
||||
'name' => 'IGNY8 Sectors',
|
||||
'singular_name' => 'Sector',
|
||||
'menu_name' => 'Sectors',
|
||||
'all_items' => 'All Sectors',
|
||||
'edit_item' => 'Edit Sector',
|
||||
'view_item' => 'View Sector',
|
||||
'update_item' => 'Update Sector',
|
||||
'add_new_item' => 'Add New Sector',
|
||||
'new_item_name' => 'New Sector Name',
|
||||
'parent_item' => 'Parent Sector',
|
||||
'parent_item_colon' => 'Parent Sector:',
|
||||
'search_items' => 'Search Sectors',
|
||||
'not_found' => 'No sectors found',
|
||||
),
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => false,
|
||||
'show_in_nav_menus' => true,
|
||||
'show_tagcloud' => false,
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => array(
|
||||
'slug' => 'sectors',
|
||||
'with_front' => false,
|
||||
),
|
||||
'capabilities' => array(
|
||||
'manage_terms' => 'manage_categories',
|
||||
'edit_terms' => 'manage_categories',
|
||||
'delete_terms' => 'manage_categories',
|
||||
'assign_terms' => 'edit_posts',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Register clusters taxonomy (hierarchical) - only if not exists
|
||||
if (!taxonomy_exists('igny8_clusters')) {
|
||||
register_taxonomy('igny8_clusters', array('post', 'page', 'product'), array(
|
||||
'hierarchical' => true,
|
||||
'labels' => array(
|
||||
'name' => 'IGNY8 Clusters',
|
||||
'singular_name' => 'Cluster',
|
||||
'menu_name' => 'Clusters',
|
||||
'all_items' => 'All Clusters',
|
||||
'edit_item' => 'Edit Cluster',
|
||||
'view_item' => 'View Cluster',
|
||||
'update_item' => 'Update Cluster',
|
||||
'add_new_item' => 'Add New Cluster',
|
||||
'new_item_name' => 'New Cluster Name',
|
||||
'parent_item' => 'Parent Cluster',
|
||||
'parent_item_colon' => 'Parent Cluster:',
|
||||
'search_items' => 'Search Clusters',
|
||||
'not_found' => 'No clusters found',
|
||||
),
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => false,
|
||||
'show_in_nav_menus' => true,
|
||||
'show_tagcloud' => false,
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => array(
|
||||
'slug' => 'clusters',
|
||||
'with_front' => false,
|
||||
),
|
||||
'capabilities' => array(
|
||||
'manage_terms' => 'manage_categories',
|
||||
'edit_terms' => 'manage_categories',
|
||||
'delete_terms' => 'manage_categories',
|
||||
'assign_terms' => 'edit_posts',
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map WordPress post status to IGNY8 task status
|
||||
*
|
||||
* @param string $wp_status WordPress post status
|
||||
* @return string IGNY8 task status
|
||||
*/
|
||||
function igny8_map_wp_status_to_igny8($wp_status) {
|
||||
$status_map = array(
|
||||
'publish' => 'completed',
|
||||
'draft' => 'draft',
|
||||
'pending' => 'pending',
|
||||
'private' => 'completed',
|
||||
'trash' => 'archived',
|
||||
'future' => 'scheduled'
|
||||
);
|
||||
|
||||
return isset($status_map[$wp_status]) ? $status_map[$wp_status] : 'draft';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if post is managed by IGNY8
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @return bool True if IGNY8 managed
|
||||
*/
|
||||
function igny8_is_igny8_managed_post($post_id) {
|
||||
$task_id = get_post_meta($post_id, '_igny8_task_id', true);
|
||||
return !empty($task_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post data for IGNY8 sync
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @return array|false Post data or false on failure
|
||||
*/
|
||||
function igny8_get_post_data_for_sync($post_id) {
|
||||
$post = get_post($post_id);
|
||||
|
||||
if (!$post) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $post_id,
|
||||
'title' => $post->post_title,
|
||||
'status' => $post->post_status,
|
||||
'url' => get_permalink($post_id),
|
||||
'modified' => $post->post_modified,
|
||||
'published' => $post->post_date,
|
||||
'author' => get_the_author_meta('display_name', $post->post_author),
|
||||
'word_count' => str_word_count(strip_tags($post->post_content)),
|
||||
'meta' => array(
|
||||
'task_id' => get_post_meta($post_id, '_igny8_task_id', true),
|
||||
'content_id' => get_post_meta($post_id, '_igny8_content_id', true),
|
||||
'cluster_id' => get_post_meta($post_id, '_igny8_cluster_id', true),
|
||||
'sector_id' => get_post_meta($post_id, '_igny8_sector_id', true),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron jobs
|
||||
*/
|
||||
function igny8_schedule_cron_jobs() {
|
||||
// Schedule daily post status sync (WordPress → IGNY8)
|
||||
if (!wp_next_scheduled('igny8_sync_post_statuses')) {
|
||||
wp_schedule_event(time(), 'daily', 'igny8_sync_post_statuses');
|
||||
}
|
||||
|
||||
// Schedule daily site data sync (incremental)
|
||||
if (!wp_next_scheduled('igny8_sync_site_data')) {
|
||||
wp_schedule_event(time(), 'daily', 'igny8_sync_site_data');
|
||||
}
|
||||
|
||||
// Schedule periodic full site scan (runs at most once per week)
|
||||
if (!wp_next_scheduled('igny8_full_site_scan')) {
|
||||
wp_schedule_event(time(), 'daily', 'igny8_full_site_scan');
|
||||
}
|
||||
|
||||
// Schedule hourly sync from IGNY8 (IGNY8 → WordPress)
|
||||
if (!wp_next_scheduled('igny8_sync_from_igny8')) {
|
||||
wp_schedule_event(time(), 'hourly', 'igny8_sync_from_igny8');
|
||||
}
|
||||
|
||||
// Schedule taxonomy sync
|
||||
if (!wp_next_scheduled('igny8_sync_taxonomies')) {
|
||||
wp_schedule_event(time(), 'twicedaily', 'igny8_sync_taxonomies');
|
||||
}
|
||||
|
||||
// Schedule keyword sync
|
||||
if (!wp_next_scheduled('igny8_sync_keywords')) {
|
||||
wp_schedule_event(time(), 'daily', 'igny8_sync_keywords');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unschedule cron jobs
|
||||
*/
|
||||
function igny8_unschedule_cron_jobs() {
|
||||
$timestamp = wp_next_scheduled('igny8_sync_post_statuses');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_sync_post_statuses');
|
||||
}
|
||||
|
||||
$timestamp = wp_next_scheduled('igny8_sync_site_data');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_sync_site_data');
|
||||
}
|
||||
|
||||
$timestamp = wp_next_scheduled('igny8_sync_from_igny8');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_sync_from_igny8');
|
||||
}
|
||||
|
||||
$timestamp = wp_next_scheduled('igny8_full_site_scan');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_full_site_scan');
|
||||
}
|
||||
|
||||
$timestamp = wp_next_scheduled('igny8_sync_taxonomies');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_sync_taxonomies');
|
||||
}
|
||||
|
||||
$timestamp = wp_next_scheduled('igny8_sync_keywords');
|
||||
if ($timestamp) {
|
||||
wp_unschedule_event($timestamp, 'igny8_sync_keywords');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user