wp plugin
This commit is contained in:
192
igny8-wp-plugin/data/link-graph.php
Normal file
192
igny8-wp-plugin/data/link-graph.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* Link Graph Collection
|
||||
*
|
||||
* Extracts WordPress internal link graph for IGNY8 Linker module
|
||||
*
|
||||
* @package Igny8Bridge
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract internal links from post content
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @return array Array of link objects with source_url, target_url, anchor
|
||||
*/
|
||||
function igny8_extract_post_links($post_id) {
|
||||
$post = get_post($post_id);
|
||||
if (!$post) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$content = $post->post_content;
|
||||
$source_url = get_permalink($post_id);
|
||||
$site_url = get_site_url();
|
||||
$links = array();
|
||||
|
||||
// Match all anchor tags with href attributes
|
||||
preg_match_all('/<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/is', $content, $matches, PREG_SET_ORDER);
|
||||
|
||||
foreach ($matches as $match) {
|
||||
$href = $match[1];
|
||||
$anchor = strip_tags($match[2]);
|
||||
|
||||
// Skip empty anchors
|
||||
if (empty(trim($anchor))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only process internal links
|
||||
if (strpos($href, $site_url) === 0 || strpos($href, '/') === 0) {
|
||||
// Convert relative URLs to absolute
|
||||
if (strpos($href, '/') === 0 && strpos($href, '//') !== 0) {
|
||||
$href = $site_url . $href;
|
||||
}
|
||||
|
||||
// Normalize URL (remove trailing slash, fragments, query params for matching)
|
||||
$target_url = rtrim($href, '/');
|
||||
|
||||
// Skip if source and target are the same
|
||||
if ($source_url === $target_url) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$links[] = array(
|
||||
'source_url' => $source_url,
|
||||
'target_url' => $target_url,
|
||||
'anchor' => trim($anchor),
|
||||
'post_id' => $post_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract link graph from all posts
|
||||
*
|
||||
* @param array $post_ids Optional array of post IDs to process. If empty, processes all enabled posts.
|
||||
* @return array Link graph array
|
||||
*/
|
||||
function igny8_extract_link_graph($post_ids = array()) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$enabled_post_types = igny8_get_enabled_post_types();
|
||||
|
||||
if (empty($post_ids)) {
|
||||
// Get all published posts of enabled types
|
||||
$query_args = array(
|
||||
'post_type' => $enabled_post_types,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'suppress_filters' => true
|
||||
);
|
||||
|
||||
$post_ids = get_posts($query_args);
|
||||
}
|
||||
|
||||
$link_graph = array();
|
||||
$processed = 0;
|
||||
|
||||
foreach ($post_ids as $post_id) {
|
||||
$links = igny8_extract_post_links($post_id);
|
||||
|
||||
if (!empty($links)) {
|
||||
$link_graph = array_merge($link_graph, $links);
|
||||
}
|
||||
|
||||
$processed++;
|
||||
|
||||
// Limit processing to prevent timeout (can be increased or made configurable)
|
||||
if ($processed >= 1000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $link_graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send link graph to IGNY8 Linker module
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @param array $link_graph Link graph array (optional, will extract if not provided)
|
||||
* @return array|false Response data or false on failure
|
||||
*/
|
||||
function igny8_send_link_graph_to_igny8($site_id, $link_graph = null) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract link graph if not provided
|
||||
if ($link_graph === null) {
|
||||
$link_graph = igny8_extract_link_graph();
|
||||
}
|
||||
|
||||
if (empty($link_graph)) {
|
||||
return array('success' => true, 'message' => 'No links found', 'links_count' => 0);
|
||||
}
|
||||
|
||||
// Send in batches (max 500 links per batch)
|
||||
$batch_size = 500;
|
||||
$batches = array_chunk($link_graph, $batch_size);
|
||||
$total_sent = 0;
|
||||
$errors = array();
|
||||
|
||||
foreach ($batches as $batch) {
|
||||
$response = $api->post("/linker/link-map/", array(
|
||||
'site_id' => $site_id,
|
||||
'links' => $batch,
|
||||
'total_links' => count($link_graph),
|
||||
'batch_number' => count($batches) > 1 ? (count($batches) - count($batches) + array_search($batch, $batches) + 1) : 1,
|
||||
'total_batches' => count($batches)
|
||||
));
|
||||
|
||||
if ($response['success']) {
|
||||
$total_sent += count($batch);
|
||||
} else {
|
||||
$errors[] = $response['error'] ?? 'Unknown error';
|
||||
}
|
||||
}
|
||||
|
||||
if ($total_sent > 0) {
|
||||
update_option('igny8_last_link_graph_sync', current_time('timestamp'));
|
||||
update_option('igny8_last_link_graph_count', $total_sent);
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'links_sent' => $total_sent,
|
||||
'total_links' => count($link_graph),
|
||||
'batches' => count($batches),
|
||||
'errors' => $errors
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
225
igny8-wp-plugin/data/semantic-mapping.php
Normal file
225
igny8-wp-plugin/data/semantic-mapping.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Semantic Strategy Mapping
|
||||
*
|
||||
* Maps WordPress site data to IGNY8 semantic structure
|
||||
* Follows WORDPRESS-PLUGIN-INTEGRATION.md guidelines
|
||||
*
|
||||
* @package Igny8Bridge
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map WordPress site data to IGNY8 semantic strategy
|
||||
* This creates sectors, clusters, and keywords based on site structure
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @param array $site_data Site data from igny8_collect_site_data()
|
||||
* @return array Response from IGNY8 API
|
||||
*/
|
||||
function igny8_map_site_to_semantic_strategy($site_id, $site_data) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return array('success' => false, 'error' => 'Connection disabled');
|
||||
}
|
||||
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return array('success' => false, 'error' => 'Not authenticated');
|
||||
}
|
||||
|
||||
// Extract semantic structure from site data
|
||||
$semantic_map = array(
|
||||
'sectors' => array(),
|
||||
'clusters' => array(),
|
||||
'keywords' => array()
|
||||
);
|
||||
|
||||
// Map taxonomies to sectors
|
||||
foreach ($site_data['taxonomies'] as $tax_name => $tax_data) {
|
||||
if ($tax_data['taxonomy']['hierarchical']) {
|
||||
// Hierarchical taxonomies (categories) become sectors
|
||||
$sector = array(
|
||||
'name' => $tax_data['taxonomy']['label'],
|
||||
'slug' => $tax_data['taxonomy']['name'],
|
||||
'description' => $tax_data['taxonomy']['description'],
|
||||
'source' => 'wordpress_taxonomy',
|
||||
'source_id' => $tax_name
|
||||
);
|
||||
|
||||
// Map terms to clusters
|
||||
$clusters = array();
|
||||
foreach ($tax_data['terms'] as $term) {
|
||||
$clusters[] = array(
|
||||
'name' => $term['name'],
|
||||
'slug' => $term['slug'],
|
||||
'description' => $term['description'],
|
||||
'source' => 'wordpress_term',
|
||||
'source_id' => $term['id']
|
||||
);
|
||||
|
||||
// Extract keywords from posts in this term
|
||||
$keywords = igny8_extract_keywords_from_term_posts($term['id'], $tax_name);
|
||||
$semantic_map['keywords'] = array_merge($semantic_map['keywords'], $keywords);
|
||||
}
|
||||
|
||||
$sector['clusters'] = $clusters;
|
||||
$semantic_map['sectors'][] = $sector;
|
||||
}
|
||||
}
|
||||
|
||||
// Map WooCommerce product categories to sectors
|
||||
if (!empty($site_data['product_categories'])) {
|
||||
$product_sector = array(
|
||||
'name' => 'Products',
|
||||
'slug' => 'products',
|
||||
'description' => 'WooCommerce product categories',
|
||||
'source' => 'woocommerce',
|
||||
'clusters' => array()
|
||||
);
|
||||
|
||||
foreach ($site_data['product_categories'] as $category) {
|
||||
$product_sector['clusters'][] = array(
|
||||
'name' => $category['name'],
|
||||
'slug' => $category['slug'],
|
||||
'description' => $category['description'],
|
||||
'source' => 'woocommerce_category',
|
||||
'source_id' => $category['id']
|
||||
);
|
||||
}
|
||||
|
||||
$semantic_map['sectors'][] = $product_sector;
|
||||
}
|
||||
|
||||
// Send semantic map to IGNY8
|
||||
$response = $api->post("/planner/sites/{$site_id}/semantic-map/", array(
|
||||
'semantic_map' => $semantic_map,
|
||||
'site_data' => $site_data
|
||||
));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract keywords from posts associated with a taxonomy term
|
||||
*
|
||||
* @param int $term_id Term ID
|
||||
* @param string $taxonomy Taxonomy name
|
||||
* @return array Formatted keywords array
|
||||
*/
|
||||
function igny8_extract_keywords_from_term_posts($term_id, $taxonomy) {
|
||||
$args = array(
|
||||
'post_type' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
'tax_query' => array(
|
||||
array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'field' => 'term_id',
|
||||
'terms' => $term_id
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$query = new WP_Query($args);
|
||||
$keywords = array();
|
||||
|
||||
if ($query->have_posts()) {
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
|
||||
// Extract keywords from post title and content
|
||||
$title_words = str_word_count(get_the_title(), 1);
|
||||
$content_words = str_word_count(strip_tags(get_the_content()), 1);
|
||||
|
||||
// Combine and get unique keywords
|
||||
$all_words = array_merge($title_words, $content_words);
|
||||
$unique_words = array_unique(array_map('strtolower', $all_words));
|
||||
|
||||
// Filter out common words (stop words)
|
||||
$stop_words = array('the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by');
|
||||
$keywords = array_merge($keywords, array_diff($unique_words, $stop_words));
|
||||
}
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
// Format keywords
|
||||
$formatted_keywords = array();
|
||||
foreach (array_unique($keywords) as $keyword) {
|
||||
if (strlen($keyword) > 3) { // Only keywords longer than 3 characters
|
||||
$formatted_keywords[] = array(
|
||||
'keyword' => $keyword,
|
||||
'source' => 'wordpress_post',
|
||||
'source_term_id' => $term_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $formatted_keywords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete workflow: Fetch site data → Map to semantic strategy → Restructure content
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @return array|false Analysis result or false on failure
|
||||
*/
|
||||
function igny8_analyze_and_restructure_site($site_id) {
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 1: Collect all site data
|
||||
$site_data = igny8_collect_site_data();
|
||||
|
||||
// Step 2: Send to IGNY8 for analysis
|
||||
$analysis_response = $api->post("/system/sites/{$site_id}/analyze/", array(
|
||||
'site_data' => $site_data,
|
||||
'analysis_type' => 'full_site_restructure'
|
||||
));
|
||||
|
||||
if (!$analysis_response['success']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$analysis_id = $analysis_response['data']['analysis_id'] ?? null;
|
||||
|
||||
// Step 3: Map to semantic strategy
|
||||
$mapping_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
|
||||
|
||||
if (!$mapping_response['success']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4: Get restructuring recommendations
|
||||
$recommendations_response = $api->get("/system/sites/{$site_id}/recommendations/");
|
||||
|
||||
if (!$recommendations_response['success']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get keywords count from mapping response
|
||||
$keywords_count = 0;
|
||||
if (isset($mapping_response['data']['keywords'])) {
|
||||
$keywords_count = count($mapping_response['data']['keywords']);
|
||||
}
|
||||
|
||||
return array(
|
||||
'analysis_id' => $analysis_id,
|
||||
'semantic_map' => $mapping_response['data'] ?? null,
|
||||
'recommendations' => $recommendations_response['data'] ?? null,
|
||||
'site_data_summary' => array(
|
||||
'total_posts' => count($site_data['posts']),
|
||||
'total_taxonomies' => count($site_data['taxonomies']),
|
||||
'total_products' => count($site_data['products'] ?? array()),
|
||||
'total_keywords' => $keywords_count
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
601
igny8-wp-plugin/data/site-collection.php
Normal file
601
igny8-wp-plugin/data/site-collection.php
Normal file
@@ -0,0 +1,601 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress Site Data Collection
|
||||
*
|
||||
* Collects WordPress posts, taxonomies, and site data for IGNY8
|
||||
* Follows WORDPRESS-PLUGIN-INTEGRATION.md guidelines
|
||||
*
|
||||
* @package Igny8Bridge
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all posts of a specific type from WordPress
|
||||
*
|
||||
* @param string $post_type Post type
|
||||
* @param int $per_page Posts per page
|
||||
* @return array|false Formatted posts array or false on failure
|
||||
*/
|
||||
function igny8_fetch_wordpress_posts($post_type = 'post', $per_page = 100, $args = array()) {
|
||||
$defaults = array(
|
||||
'status' => 'publish',
|
||||
'after' => null,
|
||||
'max_pages' => 5,
|
||||
);
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$post_type_object = get_post_type_object($post_type);
|
||||
$rest_base = ($post_type_object && !empty($post_type_object->rest_base)) ? $post_type_object->rest_base : $post_type;
|
||||
|
||||
$base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
|
||||
|
||||
$query_args = array(
|
||||
'per_page' => min($per_page, 100),
|
||||
'status' => $args['status'],
|
||||
'orderby' => 'modified',
|
||||
'order' => 'desc',
|
||||
);
|
||||
|
||||
if (!empty($args['after'])) {
|
||||
$query_args['after'] = gmdate('c', $args['after']);
|
||||
}
|
||||
|
||||
$formatted_posts = array();
|
||||
$page = 1;
|
||||
|
||||
do {
|
||||
$query_args['page'] = $page;
|
||||
$response = wp_remote_get(add_query_arg($query_args, $base_url));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$posts = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!is_array($posts) || empty($posts)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$content = $post['content']['rendered'] ?? '';
|
||||
$word_count = str_word_count(strip_tags($content));
|
||||
|
||||
$formatted_posts[] = array(
|
||||
'id' => $post['id'],
|
||||
'title' => html_entity_decode($post['title']['rendered'] ?? ''),
|
||||
'content' => $content,
|
||||
'excerpt' => $post['excerpt']['rendered'] ?? '',
|
||||
'status' => $post['status'] ?? 'draft',
|
||||
'url' => $post['link'] ?? '',
|
||||
'published' => $post['date'] ?? '',
|
||||
'modified' => $post['modified'] ?? '',
|
||||
'author' => $post['author'] ?? 0,
|
||||
'post_type' => $post['type'] ?? $post_type,
|
||||
'taxonomies' => array(
|
||||
'categories' => $post['categories'] ?? array(),
|
||||
'tags' => $post['tags'] ?? array(),
|
||||
),
|
||||
'meta' => array(
|
||||
'word_count' => $word_count,
|
||||
'reading_time' => $word_count ? ceil($word_count / 200) : 0,
|
||||
'featured_media' => $post['featured_media'] ?? 0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (count($posts) < $query_args['per_page']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$page++;
|
||||
} while ($page <= $args['max_pages']);
|
||||
|
||||
return $formatted_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all available post types from WordPress
|
||||
*
|
||||
* @return array|false Post types array or false on failure
|
||||
*/
|
||||
function igny8_fetch_all_post_types() {
|
||||
$wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/types');
|
||||
|
||||
if (is_wp_error($wp_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$types = json_decode(wp_remote_retrieve_body($wp_response), true);
|
||||
|
||||
if (!is_array($types)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post_types = array();
|
||||
foreach ($types as $type_name => $type_data) {
|
||||
if ($type_data['public']) {
|
||||
$post_types[] = array(
|
||||
'name' => $type_name,
|
||||
'label' => $type_data['name'],
|
||||
'description' => $type_data['description'] ?? '',
|
||||
'rest_base' => $type_data['rest_base'] ?? $type_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all posts from all post types
|
||||
*
|
||||
* @param int $per_page Posts per page
|
||||
* @return array All posts
|
||||
*/
|
||||
function igny8_fetch_all_wordpress_posts($per_page = 100) {
|
||||
$post_types = igny8_fetch_all_post_types();
|
||||
|
||||
if (!$post_types) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$all_posts = array();
|
||||
foreach ($post_types as $type) {
|
||||
$posts = igny8_fetch_wordpress_posts($type['name'], $per_page);
|
||||
if ($posts) {
|
||||
$all_posts = array_merge($all_posts, $posts);
|
||||
}
|
||||
}
|
||||
|
||||
return $all_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all taxonomies from WordPress
|
||||
*
|
||||
* @return array|false Taxonomies array or false on failure
|
||||
*/
|
||||
function igny8_fetch_wordpress_taxonomies() {
|
||||
$wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/taxonomies');
|
||||
|
||||
if (is_wp_error($wp_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$taxonomies = json_decode(wp_remote_retrieve_body($wp_response), true);
|
||||
|
||||
if (!is_array($taxonomies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$formatted_taxonomies = array();
|
||||
foreach ($taxonomies as $tax_name => $tax_data) {
|
||||
if ($tax_data['public']) {
|
||||
$formatted_taxonomies[] = array(
|
||||
'name' => $tax_name,
|
||||
'label' => $tax_data['name'],
|
||||
'description' => $tax_data['description'] ?? '',
|
||||
'hierarchical' => $tax_data['hierarchical'],
|
||||
'rest_base' => $tax_data['rest_base'] ?? $tax_name,
|
||||
'object_types' => $tax_data['types'] ?? array()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $formatted_taxonomies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all terms for a specific taxonomy
|
||||
*
|
||||
* @param string $taxonomy Taxonomy name
|
||||
* @param int $per_page Terms per page
|
||||
* @return array|false Formatted terms array or false on failure
|
||||
*/
|
||||
function igny8_fetch_taxonomy_terms($taxonomy, $per_page = 100) {
|
||||
$taxonomy_obj = get_taxonomy($taxonomy);
|
||||
$rest_base = ($taxonomy_obj && !empty($taxonomy_obj->rest_base)) ? $taxonomy_obj->rest_base : $taxonomy;
|
||||
|
||||
$base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
|
||||
|
||||
$formatted_terms = array();
|
||||
$page = 1;
|
||||
|
||||
do {
|
||||
$response = wp_remote_get(add_query_arg(array(
|
||||
'per_page' => min($per_page, 100),
|
||||
'page' => $page
|
||||
), $base_url));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$terms = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!is_array($terms) || empty($terms)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($terms as $term) {
|
||||
$formatted_terms[] = array(
|
||||
'id' => $term['id'],
|
||||
'name' => $term['name'],
|
||||
'slug' => $term['slug'],
|
||||
'description' => $term['description'] ?? '',
|
||||
'count' => $term['count'],
|
||||
'parent' => $term['parent'] ?? 0,
|
||||
'taxonomy' => $taxonomy,
|
||||
'url' => $term['link'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
if (count($terms) < min($per_page, 100)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$page++;
|
||||
} while (true);
|
||||
|
||||
return $formatted_terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all terms from all taxonomies
|
||||
*
|
||||
* @param int $per_page Terms per page
|
||||
* @return array All terms organized by taxonomy
|
||||
*/
|
||||
function igny8_fetch_all_taxonomy_terms($per_page = 100) {
|
||||
$taxonomies = igny8_fetch_wordpress_taxonomies();
|
||||
|
||||
if (!$taxonomies) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$all_terms = array();
|
||||
foreach ($taxonomies as $taxonomy) {
|
||||
$terms = igny8_fetch_taxonomy_terms($taxonomy['rest_base'], $per_page);
|
||||
if ($terms) {
|
||||
$all_terms[$taxonomy['name']] = $terms;
|
||||
}
|
||||
}
|
||||
|
||||
return $all_terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all WordPress site data for IGNY8 semantic mapping
|
||||
*
|
||||
* @return array Complete site data
|
||||
*/
|
||||
function igny8_collect_site_data($args = array()) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return array('disabled' => true, 'reason' => 'connection_disabled');
|
||||
}
|
||||
|
||||
if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('sites')) {
|
||||
return array('disabled' => true);
|
||||
}
|
||||
|
||||
$settings = igny8_get_site_scan_settings($args);
|
||||
|
||||
$site_data = array(
|
||||
'site_url' => get_site_url(),
|
||||
'site_name' => get_bloginfo('name'),
|
||||
'site_description' => get_bloginfo('description'),
|
||||
'collected_at' => current_time('mysql'),
|
||||
'settings' => $settings,
|
||||
'posts' => array(),
|
||||
'taxonomies' => array(),
|
||||
'products' => array(),
|
||||
'product_categories' => array(),
|
||||
'product_attributes' => array()
|
||||
);
|
||||
|
||||
foreach ((array) $settings['post_types'] as $post_type) {
|
||||
if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$posts = igny8_fetch_wordpress_posts($post_type, $settings['per_page'], array(
|
||||
'after' => $settings['since'],
|
||||
'status' => 'publish'
|
||||
));
|
||||
|
||||
if ($posts) {
|
||||
$site_data['posts'] = array_merge($site_data['posts'], $posts);
|
||||
}
|
||||
}
|
||||
|
||||
$tracked_taxonomies = array('category', 'post_tag', 'igny8_sectors', 'igny8_clusters');
|
||||
|
||||
// Get enabled taxonomies from settings
|
||||
if (function_exists('igny8_get_enabled_taxonomies')) {
|
||||
$enabled_taxonomies = igny8_get_enabled_taxonomies();
|
||||
if (!empty($enabled_taxonomies)) {
|
||||
$tracked_taxonomies = $enabled_taxonomies;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tracked_taxonomies as $taxonomy) {
|
||||
if (!taxonomy_exists($taxonomy)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$terms = igny8_fetch_taxonomy_terms($taxonomy, 100);
|
||||
|
||||
// Filter product attribute terms: only sync those created by IGNY8
|
||||
if (strpos($taxonomy, 'pa_') === 0) {
|
||||
$filtered_terms = array();
|
||||
foreach ($terms as $term) {
|
||||
$origin = get_term_meta($term['id'], 'igny8_origin', true);
|
||||
if ($origin === 'igny8_app') {
|
||||
$filtered_terms[] = $term;
|
||||
}
|
||||
}
|
||||
$terms = $filtered_terms;
|
||||
}
|
||||
|
||||
if ($terms) {
|
||||
$tax_obj = get_taxonomy($taxonomy);
|
||||
$site_data['taxonomies'][$taxonomy] = array(
|
||||
'taxonomy' => array(
|
||||
'name' => $taxonomy,
|
||||
'label' => $tax_obj ? $tax_obj->label : $taxonomy,
|
||||
'description' => $tax_obj->description ?? '',
|
||||
'hierarchical' => $tax_obj ? $tax_obj->hierarchical : false,
|
||||
),
|
||||
'terms' => $terms
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($settings['include_products']) && function_exists('igny8_is_woocommerce_active') && igny8_is_woocommerce_active()) {
|
||||
require_once IGNY8_BRIDGE_PLUGIN_DIR . 'data/woocommerce.php';
|
||||
|
||||
$products = igny8_fetch_woocommerce_products(100);
|
||||
if ($products) {
|
||||
$site_data['products'] = $products;
|
||||
}
|
||||
|
||||
$product_categories = igny8_fetch_product_categories(100);
|
||||
if ($product_categories) {
|
||||
$site_data['product_categories'] = $product_categories;
|
||||
}
|
||||
|
||||
$product_attributes = igny8_fetch_product_attributes();
|
||||
if ($product_attributes) {
|
||||
$site_data['product_attributes'] = $product_attributes;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract link graph if Linker module is enabled
|
||||
if (function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
|
||||
$post_ids = wp_list_pluck($site_data['posts'], 'id');
|
||||
$link_graph = igny8_extract_link_graph($post_ids);
|
||||
|
||||
if (!empty($link_graph)) {
|
||||
$site_data['link_graph'] = $link_graph;
|
||||
}
|
||||
}
|
||||
|
||||
$site_data['summary'] = array(
|
||||
'posts' => count($site_data['posts']),
|
||||
'taxonomies' => count($site_data['taxonomies']),
|
||||
'products' => count($site_data['products']),
|
||||
'links' => isset($site_data['link_graph']) ? count($site_data['link_graph']) : 0
|
||||
);
|
||||
|
||||
update_option('igny8_last_site_snapshot', array(
|
||||
'timestamp' => current_time('timestamp'),
|
||||
'summary' => $site_data['summary']
|
||||
));
|
||||
|
||||
return $site_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send WordPress site data to IGNY8 for semantic strategy mapping
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @return array|false Response data or false on failure
|
||||
*/
|
||||
function igny8_send_site_data_to_igny8($site_id, $site_data = null, $args = array()) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Collect all site data if not provided
|
||||
if (empty($site_data)) {
|
||||
$site_data = igny8_collect_site_data($args);
|
||||
}
|
||||
|
||||
if (empty($site_data) || isset($site_data['disabled'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send to IGNY8 API
|
||||
$response = $api->post("/system/sites/{$site_id}/import/", array(
|
||||
'site_data' => $site_data,
|
||||
'import_type' => $args['mode'] ?? 'full_site_scan'
|
||||
));
|
||||
|
||||
if ($response['success']) {
|
||||
// Store import ID for tracking
|
||||
update_option('igny8_last_site_import_id', $response['data']['import_id'] ?? null);
|
||||
update_option('igny8_last_site_sync', current_time('timestamp'));
|
||||
|
||||
// Send link graph separately to Linker module if available
|
||||
if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
|
||||
$link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
|
||||
if ($link_result) {
|
||||
error_log(sprintf('IGNY8: Sent %d links to Linker module', $link_result['links_sent'] ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
return $response['data'];
|
||||
} else {
|
||||
error_log("IGNY8: Failed to send site data: " . ($response['error'] ?? 'Unknown error'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync only changed posts/taxonomies since last sync
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @return array|false Sync result or false on failure
|
||||
*/
|
||||
function igny8_sync_incremental_site_data($site_id, $settings = array()) {
|
||||
// Skip if connection is disabled
|
||||
if (!igny8_is_connection_enabled()) {
|
||||
return array('synced' => 0, 'message' => 'Connection disabled');
|
||||
}
|
||||
|
||||
$api = new Igny8API();
|
||||
|
||||
if (!$api->is_authenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$settings = igny8_get_site_scan_settings(wp_parse_args($settings, array('mode' => 'incremental')));
|
||||
$since = $settings['since'] ?? intval(get_option('igny8_last_site_sync', 0));
|
||||
|
||||
$formatted_posts = array();
|
||||
|
||||
foreach ((array) $settings['post_types'] as $post_type) {
|
||||
if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$query_args = array(
|
||||
'post_type' => $post_type,
|
||||
'post_status' => array('publish', 'pending', 'draft', 'future'),
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'modified',
|
||||
'order' => 'DESC',
|
||||
'suppress_filters' => true,
|
||||
);
|
||||
|
||||
if ($since) {
|
||||
$query_args['date_query'] = array(
|
||||
array(
|
||||
'column' => 'post_modified_gmt',
|
||||
'after' => gmdate('Y-m-d H:i:s', $since)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$posts = get_posts($query_args);
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$word_count = str_word_count(strip_tags($post->post_content));
|
||||
|
||||
$formatted_posts[] = array(
|
||||
'id' => $post->ID,
|
||||
'title' => get_the_title($post),
|
||||
'content' => $post->post_content,
|
||||
'status' => $post->post_status,
|
||||
'modified' => $post->post_modified_gmt,
|
||||
'post_type' => $post->post_type,
|
||||
'url' => get_permalink($post),
|
||||
'taxonomies' => array(
|
||||
'categories' => wp_get_post_terms($post->ID, 'category', array('fields' => 'ids')),
|
||||
'tags' => wp_get_post_terms($post->ID, 'post_tag', array('fields' => 'ids')),
|
||||
),
|
||||
'meta' => array(
|
||||
'task_id' => get_post_meta($post->ID, '_igny8_task_id', true),
|
||||
'cluster_id' => get_post_meta($post->ID, '_igny8_cluster_id', true),
|
||||
'sector_id' => get_post_meta($post->ID, '_igny8_sector_id', true),
|
||||
'word_count' => $word_count,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($formatted_posts)) {
|
||||
return array('synced' => 0, 'message' => 'No changes since last sync');
|
||||
}
|
||||
|
||||
$response = $api->post("/system/sites/{$site_id}/sync/", array(
|
||||
'posts' => $formatted_posts,
|
||||
'sync_type' => 'incremental',
|
||||
'last_sync' => $since,
|
||||
'post_types' => $settings['post_types']
|
||||
));
|
||||
|
||||
if ($response['success']) {
|
||||
update_option('igny8_last_site_sync', current_time('timestamp'));
|
||||
update_option('igny8_last_incremental_site_sync', array(
|
||||
'timestamp' => current_time('timestamp'),
|
||||
'count' => count($formatted_posts)
|
||||
));
|
||||
|
||||
return array(
|
||||
'synced' => count($formatted_posts),
|
||||
'message' => 'Incremental sync completed'
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a full site scan and semantic mapping
|
||||
*
|
||||
* @param int $site_id IGNY8 site ID
|
||||
* @param array $settings Scan settings
|
||||
* @return array|false
|
||||
*/
|
||||
function igny8_perform_full_site_scan($site_id, $settings = array()) {
|
||||
$site_data = igny8_collect_site_data($settings);
|
||||
|
||||
if (empty($site_data) || isset($site_data['disabled'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$import = igny8_send_site_data_to_igny8($site_id, $site_data, array('mode' => 'full_site_scan'));
|
||||
|
||||
if (!$import) {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_option('igny8_last_full_site_scan', current_time('timestamp'));
|
||||
|
||||
// Map to semantic strategy (requires Planner module)
|
||||
if (!function_exists('igny8_is_module_enabled') || igny8_is_module_enabled('planner')) {
|
||||
$map_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
|
||||
if (!empty($map_response['success'])) {
|
||||
update_option('igny8_last_semantic_map', current_time('timestamp'));
|
||||
update_option('igny8_last_semantic_map_summary', array(
|
||||
'sectors' => count($map_response['data']['sectors'] ?? array()),
|
||||
'keywords' => count($map_response['data']['keywords'] ?? array())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Send link graph to Linker module if available
|
||||
if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
|
||||
$link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
|
||||
if ($link_result) {
|
||||
error_log(sprintf('IGNY8: Sent %d links to Linker module during full scan', $link_result['links_sent'] ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
return $import;
|
||||
}
|
||||
|
||||
226
igny8-wp-plugin/data/woocommerce.php
Normal file
226
igny8-wp-plugin/data/woocommerce.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Integration
|
||||
*
|
||||
* Fetches WooCommerce products, categories, and attributes
|
||||
* Follows WORDPRESS-PLUGIN-INTEGRATION.md guidelines
|
||||
*
|
||||
* @package Igny8Bridge
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if WooCommerce is active
|
||||
*
|
||||
* @return bool True if WooCommerce is active
|
||||
*/
|
||||
function igny8_is_woocommerce_active() {
|
||||
return class_exists('WooCommerce');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all WooCommerce products
|
||||
*
|
||||
* @param int $per_page Products per page
|
||||
* @return array|false Formatted products array or false on failure
|
||||
*/
|
||||
function igny8_fetch_woocommerce_products($per_page = 100) {
|
||||
// Check if WooCommerce is active
|
||||
if (!igny8_is_woocommerce_active()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get WooCommerce API credentials
|
||||
$consumer_key = get_option('woocommerce_api_consumer_key', '');
|
||||
$consumer_secret = get_option('woocommerce_api_consumer_secret', '');
|
||||
|
||||
if (empty($consumer_key) || empty($consumer_secret)) {
|
||||
// Try to use basic auth if API keys not set
|
||||
$auth = '';
|
||||
} else {
|
||||
$auth = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
|
||||
}
|
||||
|
||||
$headers = array();
|
||||
if ($auth) {
|
||||
$headers['Authorization'] = $auth;
|
||||
}
|
||||
|
||||
$wp_response = wp_remote_get(sprintf(
|
||||
'%s/wp-json/wc/v3/products?per_page=%d&status=publish',
|
||||
get_site_url(),
|
||||
$per_page
|
||||
), array(
|
||||
'headers' => $headers
|
||||
));
|
||||
|
||||
if (is_wp_error($wp_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$products = json_decode(wp_remote_retrieve_body($wp_response), true);
|
||||
|
||||
if (!is_array($products)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$formatted_products = array();
|
||||
foreach ($products as $product) {
|
||||
$formatted_products[] = array(
|
||||
'id' => $product['id'],
|
||||
'name' => $product['name'],
|
||||
'slug' => $product['slug'],
|
||||
'sku' => $product['sku'],
|
||||
'type' => $product['type'],
|
||||
'status' => $product['status'],
|
||||
'description' => $product['description'],
|
||||
'short_description' => $product['short_description'],
|
||||
'price' => $product['price'],
|
||||
'regular_price' => $product['regular_price'],
|
||||
'sale_price' => $product['sale_price'],
|
||||
'on_sale' => $product['on_sale'],
|
||||
'stock_status' => $product['stock_status'],
|
||||
'stock_quantity' => $product['stock_quantity'],
|
||||
'categories' => $product['categories'] ?? array(),
|
||||
'tags' => $product['tags'] ?? array(),
|
||||
'images' => $product['images'] ?? array(),
|
||||
'attributes' => $product['attributes'] ?? array(),
|
||||
'variations' => $product['variations'] ?? array(),
|
||||
'url' => $product['permalink']
|
||||
);
|
||||
}
|
||||
|
||||
return $formatted_products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch WooCommerce product categories
|
||||
*
|
||||
* @param int $per_page Categories per page
|
||||
* @return array|false Formatted categories array or false on failure
|
||||
*/
|
||||
function igny8_fetch_product_categories($per_page = 100) {
|
||||
if (!igny8_is_woocommerce_active()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$consumer_key = get_option('woocommerce_api_consumer_key', '');
|
||||
$consumer_secret = get_option('woocommerce_api_consumer_secret', '');
|
||||
|
||||
$headers = array();
|
||||
if ($consumer_key && $consumer_secret) {
|
||||
$headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
|
||||
}
|
||||
|
||||
$wp_response = wp_remote_get(sprintf(
|
||||
'%s/wp-json/wc/v3/products/categories?per_page=%d',
|
||||
get_site_url(),
|
||||
$per_page
|
||||
), array(
|
||||
'headers' => $headers
|
||||
));
|
||||
|
||||
if (is_wp_error($wp_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$categories = json_decode(wp_remote_retrieve_body($wp_response), true);
|
||||
|
||||
if (!is_array($categories)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$formatted_categories = array();
|
||||
foreach ($categories as $category) {
|
||||
$formatted_categories[] = array(
|
||||
'id' => $category['id'],
|
||||
'name' => $category['name'],
|
||||
'slug' => $category['slug'],
|
||||
'description' => $category['description'] ?? '',
|
||||
'count' => $category['count'],
|
||||
'parent' => $category['parent'] ?? 0,
|
||||
'image' => $category['image']['src'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
return $formatted_categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch WooCommerce product attributes
|
||||
*
|
||||
* @return array|false Formatted attributes array or false on failure
|
||||
*/
|
||||
function igny8_fetch_product_attributes() {
|
||||
if (!igny8_is_woocommerce_active()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$consumer_key = get_option('woocommerce_api_consumer_key', '');
|
||||
$consumer_secret = get_option('woocommerce_api_consumer_secret', '');
|
||||
|
||||
$headers = array();
|
||||
if ($consumer_key && $consumer_secret) {
|
||||
$headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
|
||||
}
|
||||
|
||||
$wp_response = wp_remote_get(
|
||||
get_site_url() . '/wp-json/wc/v3/products/attributes',
|
||||
array(
|
||||
'headers' => $headers
|
||||
)
|
||||
);
|
||||
|
||||
if (is_wp_error($wp_response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attributes = json_decode(wp_remote_retrieve_body($wp_response), true);
|
||||
|
||||
if (!is_array($attributes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$formatted_attributes = array();
|
||||
foreach ($attributes as $attribute) {
|
||||
// Get attribute terms
|
||||
$terms_response = wp_remote_get(sprintf(
|
||||
'%s/wp-json/wc/v3/products/attributes/%d/terms',
|
||||
get_site_url(),
|
||||
$attribute['id']
|
||||
), array(
|
||||
'headers' => $headers
|
||||
));
|
||||
|
||||
$terms = array();
|
||||
if (!is_wp_error($terms_response)) {
|
||||
$terms_data = json_decode(wp_remote_retrieve_body($terms_response), true);
|
||||
if (is_array($terms_data)) {
|
||||
foreach ($terms_data as $term) {
|
||||
$terms[] = array(
|
||||
'id' => $term['id'],
|
||||
'name' => $term['name'],
|
||||
'slug' => $term['slug']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$formatted_attributes[] = array(
|
||||
'id' => $attribute['id'],
|
||||
'name' => $attribute['name'],
|
||||
'slug' => $attribute['slug'],
|
||||
'type' => $attribute['type'],
|
||||
'order_by' => $attribute['order_by'],
|
||||
'has_archives' => $attribute['has_archives'],
|
||||
'terms' => $terms
|
||||
);
|
||||
}
|
||||
|
||||
return $formatted_attributes;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user