Changeset 3498103
- Timestamp:
- 04/03/2026 09:51:48 AM (12 hours ago)
- Location:
- soro-seo
- Files:
-
- 4 edited
- 1 copied
-
tags/1.4.0 (copied) (copied from soro-seo/trunk)
-
tags/1.4.0/readme.txt (modified) (3 diffs)
-
tags/1.4.0/soro-seo.php (modified) (8 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/soro-seo.php (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
soro-seo/tags/1.4.0/readme.txt
r3493781 r3498103 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1. 3.67 Stable tag: 1.4.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 91 91 == Changelog == 92 92 93 = 1.4.0 = 94 * Improved: Enhanced publishing reliability on slower hosting environments 95 * New: Added smart duplicate prevention to handle interrupted connections gracefully 96 * New: Intelligent slug detection prevents accidental conflicts with your existing pages 97 * Tweak: Cleaner Soro settings interface with streamlined notifications 98 93 99 = 1.3.6 = 94 100 * Fixed connection failures on sites where the REST API is restricted to authenticated users … … 137 143 == Upgrade Notice == 138 144 145 = 1.4.0 = 146 Important reliability update for the automated publishing system. Recommended for all users. 147 139 148 = 1.3.6 = 140 149 Fixes "invalid API key" errors on sites that restrict the REST API. Recommended for all users. -
soro-seo/tags/1.4.0/soro-seo.php
r3493781 r3498103 4 4 * Plugin URI: https://trysoro.com/wordpress 5 5 * Description: Connect your WordPress site to Soro for automatic AI-powered article publishing. 6 * Version: 1. 3.66 * Version: 1.4.0 7 7 * Author: Soro 8 8 * Author URI: https://trysoro.com … … 19 19 } 20 20 21 define('SORO_CONNECTOR_VERSION', '1. 3.6');21 define('SORO_CONNECTOR_VERSION', '1.4.0'); 22 22 define('SORO_CONNECTOR_PLUGIN_DIR', plugin_dir_path(__FILE__)); 23 23 define('SORO_CONNECTOR_PLUGIN_URL', plugin_dir_url(__FILE__)); … … 38 38 39 39 add_filter('rest_authentication_errors', array($this, 'bypass_rest_restriction_for_soro'), 999); 40 add_action('in_admin_header', array($this, 'hide_admin_notices_on_soro_page')); 40 41 41 42 register_activation_hook(__FILE__, array($this, 'activate')); … … 64 65 65 66 return $result; 67 } 68 69 /** 70 * Hide all admin notices (update nags, plugin promos, etc.) on the Soro settings page 71 */ 72 public function hide_admin_notices_on_soro_page() { 73 $screen = get_current_screen(); 74 if ($screen && $screen->id === 'settings_page_soro-seo') { 75 remove_all_actions('admin_notices'); 76 remove_all_actions('all_admin_notices'); 77 } 66 78 } 67 79 … … 663 675 } 664 676 677 // Idempotency: if this exact Soro article was already published, return the existing post 678 if (!empty($params['soro_article_id'])) { 679 $existing = get_posts(array( 680 'post_type' => 'post', 681 'post_status' => array('publish', 'draft', 'pending', 'private'), 682 'meta_key' => '_soro_article_id', 683 'meta_value' => sanitize_text_field($params['soro_article_id']), 684 'numberposts' => 1, 685 'fields' => 'ids', 686 )); 687 688 if (!empty($existing)) { 689 $existing_id = $existing[0]; 690 return new WP_REST_Response(array( 691 'success' => true, 692 'post_id' => $existing_id, 693 'post_url' => get_permalink($existing_id), 694 'edit_url' => get_edit_post_link($existing_id, 'raw'), 695 'duplicate' => true, 696 ), 200); 697 } 698 } 699 700 // Prevent duplicate slugs: if any post or page with this slug already exists, 701 // still create the article as draft (so Soro stops retrying) but don't publish 702 // to avoid SEO cannibalization. 703 $slug_conflict = false; 704 if (!empty($params['slug'])) { 705 $existing_by_slug = get_posts(array( 706 'post_type' => array('post', 'page'), 707 'post_status' => array('publish', 'draft', 'pending', 'private'), 708 'name' => sanitize_title($params['slug']), 709 'numberposts' => 1, 710 'fields' => 'ids', 711 )); 712 if (!empty($existing_by_slug)) { 713 $slug_conflict = true; 714 } 715 } 716 665 717 $allowed_statuses = array('draft', 'publish', 'pending', 'private'); 666 718 $status = isset($params['status']) ? sanitize_text_field($params['status']) : 'draft'; … … 669 721 } 670 722 723 if ($slug_conflict) { 724 $status = 'draft'; 725 } 726 671 727 // Build meta_input so SEO meta is set DURING wp_insert_post, before 672 728 // Yoast/RankMath/AIOSEO fire their save_post hooks to build indexables. 673 // Previously we used update_post_meta after wp_insert_post, which meant674 // Yoast would build its indexable without the focus keyword, causing675 // the SEO score to not appear until the user re-entered the keyphrase.676 729 $meta_input = array( 677 730 '_soro_published_at' => current_time('mysql'), … … 741 794 } 742 795 743 // Trigger Yoast indexable rebuild as a safety net.744 // Even though meta_input sets the data before save_post fires,745 // some Yoast versions may need an explicit rebuild to pick up746 // the focus keyword in their indexables table.747 796 $this->rebuild_yoast_indexable($post_id); 748 797 798 // Featured image is non-critical — if it crashes, we still return the post_id 799 // so Soro records the publish and never retries (which would create duplicates). 749 800 $featured_image_id = null; 750 801 if (!empty($params['featured_image_url'])) { 751 $featured_image_id = $this->attach_featured_image($post_id, $params['featured_image_url'], $params['title']); 752 } 753 754 return new WP_REST_Response(array( 802 try { 803 $featured_image_id = $this->attach_featured_image($post_id, $params['featured_image_url'], $params['title']); 804 } catch (\Throwable $e) { 805 // Log but don't fail — the post is already created 806 } 807 } 808 809 $response_data = array( 755 810 'success' => true, 756 811 'post_id' => $post_id, … … 758 813 'edit_url' => get_edit_post_link($post_id, 'raw'), 759 814 'featured_image_id' => $featured_image_id, 760 ), 201); 815 ); 816 817 if ($slug_conflict) { 818 $response_data['slug_conflict'] = true; 819 $response_data['saved_as_draft'] = true; 820 } 821 822 return new WP_REST_Response($response_data, 201); 761 823 } 762 824 -
soro-seo/trunk/readme.txt
r3493781 r3498103 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1. 3.67 Stable tag: 1.4.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 91 91 == Changelog == 92 92 93 = 1.4.0 = 94 * Improved: Enhanced publishing reliability on slower hosting environments 95 * New: Added smart duplicate prevention to handle interrupted connections gracefully 96 * New: Intelligent slug detection prevents accidental conflicts with your existing pages 97 * Tweak: Cleaner Soro settings interface with streamlined notifications 98 93 99 = 1.3.6 = 94 100 * Fixed connection failures on sites where the REST API is restricted to authenticated users … … 137 143 == Upgrade Notice == 138 144 145 = 1.4.0 = 146 Important reliability update for the automated publishing system. Recommended for all users. 147 139 148 = 1.3.6 = 140 149 Fixes "invalid API key" errors on sites that restrict the REST API. Recommended for all users. -
soro-seo/trunk/soro-seo.php
r3493781 r3498103 4 4 * Plugin URI: https://trysoro.com/wordpress 5 5 * Description: Connect your WordPress site to Soro for automatic AI-powered article publishing. 6 * Version: 1. 3.66 * Version: 1.4.0 7 7 * Author: Soro 8 8 * Author URI: https://trysoro.com … … 19 19 } 20 20 21 define('SORO_CONNECTOR_VERSION', '1. 3.6');21 define('SORO_CONNECTOR_VERSION', '1.4.0'); 22 22 define('SORO_CONNECTOR_PLUGIN_DIR', plugin_dir_path(__FILE__)); 23 23 define('SORO_CONNECTOR_PLUGIN_URL', plugin_dir_url(__FILE__)); … … 38 38 39 39 add_filter('rest_authentication_errors', array($this, 'bypass_rest_restriction_for_soro'), 999); 40 add_action('in_admin_header', array($this, 'hide_admin_notices_on_soro_page')); 40 41 41 42 register_activation_hook(__FILE__, array($this, 'activate')); … … 64 65 65 66 return $result; 67 } 68 69 /** 70 * Hide all admin notices (update nags, plugin promos, etc.) on the Soro settings page 71 */ 72 public function hide_admin_notices_on_soro_page() { 73 $screen = get_current_screen(); 74 if ($screen && $screen->id === 'settings_page_soro-seo') { 75 remove_all_actions('admin_notices'); 76 remove_all_actions('all_admin_notices'); 77 } 66 78 } 67 79 … … 663 675 } 664 676 677 // Idempotency: if this exact Soro article was already published, return the existing post 678 if (!empty($params['soro_article_id'])) { 679 $existing = get_posts(array( 680 'post_type' => 'post', 681 'post_status' => array('publish', 'draft', 'pending', 'private'), 682 'meta_key' => '_soro_article_id', 683 'meta_value' => sanitize_text_field($params['soro_article_id']), 684 'numberposts' => 1, 685 'fields' => 'ids', 686 )); 687 688 if (!empty($existing)) { 689 $existing_id = $existing[0]; 690 return new WP_REST_Response(array( 691 'success' => true, 692 'post_id' => $existing_id, 693 'post_url' => get_permalink($existing_id), 694 'edit_url' => get_edit_post_link($existing_id, 'raw'), 695 'duplicate' => true, 696 ), 200); 697 } 698 } 699 700 // Prevent duplicate slugs: if any post or page with this slug already exists, 701 // still create the article as draft (so Soro stops retrying) but don't publish 702 // to avoid SEO cannibalization. 703 $slug_conflict = false; 704 if (!empty($params['slug'])) { 705 $existing_by_slug = get_posts(array( 706 'post_type' => array('post', 'page'), 707 'post_status' => array('publish', 'draft', 'pending', 'private'), 708 'name' => sanitize_title($params['slug']), 709 'numberposts' => 1, 710 'fields' => 'ids', 711 )); 712 if (!empty($existing_by_slug)) { 713 $slug_conflict = true; 714 } 715 } 716 665 717 $allowed_statuses = array('draft', 'publish', 'pending', 'private'); 666 718 $status = isset($params['status']) ? sanitize_text_field($params['status']) : 'draft'; … … 669 721 } 670 722 723 if ($slug_conflict) { 724 $status = 'draft'; 725 } 726 671 727 // Build meta_input so SEO meta is set DURING wp_insert_post, before 672 728 // Yoast/RankMath/AIOSEO fire their save_post hooks to build indexables. 673 // Previously we used update_post_meta after wp_insert_post, which meant674 // Yoast would build its indexable without the focus keyword, causing675 // the SEO score to not appear until the user re-entered the keyphrase.676 729 $meta_input = array( 677 730 '_soro_published_at' => current_time('mysql'), … … 741 794 } 742 795 743 // Trigger Yoast indexable rebuild as a safety net.744 // Even though meta_input sets the data before save_post fires,745 // some Yoast versions may need an explicit rebuild to pick up746 // the focus keyword in their indexables table.747 796 $this->rebuild_yoast_indexable($post_id); 748 797 798 // Featured image is non-critical — if it crashes, we still return the post_id 799 // so Soro records the publish and never retries (which would create duplicates). 749 800 $featured_image_id = null; 750 801 if (!empty($params['featured_image_url'])) { 751 $featured_image_id = $this->attach_featured_image($post_id, $params['featured_image_url'], $params['title']); 752 } 753 754 return new WP_REST_Response(array( 802 try { 803 $featured_image_id = $this->attach_featured_image($post_id, $params['featured_image_url'], $params['title']); 804 } catch (\Throwable $e) { 805 // Log but don't fail — the post is already created 806 } 807 } 808 809 $response_data = array( 755 810 'success' => true, 756 811 'post_id' => $post_id, … … 758 813 'edit_url' => get_edit_post_link($post_id, 'raw'), 759 814 'featured_image_id' => $featured_image_id, 760 ), 201); 815 ); 816 817 if ($slug_conflict) { 818 $response_data['slug_conflict'] = true; 819 $response_data['saved_as_draft'] = true; 820 } 821 822 return new WP_REST_Response($response_data, 201); 761 823 } 762 824
Note: See TracChangeset
for help on using the changeset viewer.