Changeset 3466002
- Timestamp:
- 02/20/2026 06:49:29 PM (5 weeks ago)
- Location:
- seo-rocket-integration
- Files:
-
- 5 added
- 2 edited
-
tags/1.7.1 (added)
-
tags/1.7.1/LICENSE (added)
-
tags/1.7.1/readme.txt (added)
-
tags/1.7.1/seo-rocket-integration.php (added)
-
tags/1.7.1/uninstall.php (added)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/seo-rocket-integration.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
seo-rocket-integration/trunk/readme.txt
r3460311 r3466002 5 5 Requires at least: 5.9 6 6 Tested up to: 6.9 7 Stable tag: 1. 6.07 Stable tag: 1.7.1 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 101 101 == Changelog == 102 102 103 = 1.7.1 = 104 * Removed error_log() calls to resolve WordPress.org PHPCS warnings 105 106 = 1.7.0 = 107 * Fixed Yoast green light not appearing reliably on all sites 108 * Added direct update_post_meta saving — bypasses REST API meta registration which fails when Yoast registers the same keys without show_in_rest 109 * Changed from register_meta to register_post_meta with explicit post-type subtypes 110 * Added unregister/re-register flow to override Yoast's and Rank Math's existing meta registrations 111 * Removed conditional class_exists checks from meta registration — all SEO fields are now always registered 112 * Reads meta from raw JSON body (get_json_params) to bypass REST API field filtering 113 * Split meta save (priority 5) and indexable rebuild (priority 10) into separate hooks for correct ordering 114 103 115 = 1.6.0 = 104 * Version bump for WordPress.org submission 105 * Tested up to WordPress 6.9 116 * Fixed Yoast SEO green light not appearing on REST API published posts 117 * Register _yoast_wpseo_linkdex and _yoast_wpseo_content_score meta fields for REST API 118 * Register rank_math_seo_score meta field for Rank Math REST API 119 * Added Yoast indexables rebuild hook for immediate SEO score indicator updates 120 * Refactored field registration to reduce code duplication 106 121 107 122 = 1.4.0 = … … 129 144 == Upgrade Notice == 130 145 146 = 1.7.1 = 147 Removes debug logging calls flagged by WordPress.org PHPCS. No functional changes. 148 149 = 1.7.0 = 150 Critical fix for Yoast green light on all sites. Uses direct update_post_meta to reliably save SEO scores regardless of Yoast's internal meta registration. Republish articles after updating. 151 131 152 = 1.6.0 = 132 Version bump for WordPress.org submission. Tested with WordPress 6.9.153 Fixes Yoast SEO gray light issue. After updating, republish articles from SEO Rocket to get green indicators without manually editing posts in WordPress. 133 154 134 155 = 1.4.0 = -
seo-rocket-integration/trunk/seo-rocket-integration.php
r3460311 r3466002 3 3 * Plugin Name: SEO Rocket Integration 4 4 * Description: Enables REST API access for Yoast SEO, Rank Math, and other SEO plugins. Allows SEO Rocket to publish articles with SEO metadata. 5 * Version: 1. 6.05 * Version: 1.7.1 6 6 * Author: SEO Rocket 7 7 * Author URI: https://www.seorocket.app … … 18 18 * Register SEO plugin meta fields for REST API access 19 19 * 20 * This allows SEO Rocket to set SEO metadata (focus keyword, meta description) 21 * via the WordPress REST API when publishing articles. 20 * This allows SEO Rocket to set SEO metadata (focus keyword, meta description, 21 * SEO score, readability score) via the WordPress REST API when publishing articles. 22 * 23 * We register for both 'post' and 'page' object subtypes using register_post_meta, 24 * which is the correct way to register post-type-specific meta in WordPress 5.x+. 25 * 26 * In WordPress 6.x, register_meta() silently overwrites existing registrations 27 * for the same key. By running on rest_api_init (which fires after Yoast's init 28 * registration), our show_in_rest => true setting takes effect. 22 29 */ 23 30 add_action('rest_api_init', function() { 24 // Yoast SEO fields (posts + pages) 25 if (class_exists('WPSEO_Options')) { 26 $yoast_fields = [ 27 '_yoast_wpseo_focuskw' => 'Yoast SEO focus keyword', 28 '_yoast_wpseo_metadesc' => 'Yoast SEO meta description', 29 '_yoast_wpseo_linkdex' => 'Yoast SEO linkdex score', 30 '_yoast_wpseo_content_score' => 'Yoast SEO content score', 31 ]; 32 33 foreach (['post', 'page'] as $post_type) { 34 $capability = $post_type === 'post' ? 'edit_posts' : 'edit_pages'; 35 36 foreach ($yoast_fields as $meta_key => $description) { 37 register_meta($post_type, $meta_key, [ 38 'show_in_rest' => true, 39 'single' => true, 40 'type' => 'string', 41 'description' => $description, 42 'auth_callback' => function() use ($capability) { 43 return current_user_can($capability); 44 } 45 ]); 31 // All SEO fields we want to expose in the REST API 32 $seo_fields = [ 33 // Yoast SEO fields 34 '_yoast_wpseo_focuskw' => 'Yoast SEO focus keyword', 35 '_yoast_wpseo_metadesc' => 'Yoast SEO meta description', 36 '_yoast_wpseo_linkdex' => 'Yoast SEO score', 37 '_yoast_wpseo_content_score' => 'Yoast readability score', 38 // Rank Math fields 39 'rank_math_focus_keyword' => 'Rank Math focus keyword', 40 'rank_math_description' => 'Rank Math meta description', 41 'rank_math_seo_score' => 'Rank Math SEO score', 42 ]; 43 44 $post_types = ['post', 'page']; 45 46 foreach ($post_types as $post_type) { 47 foreach ($seo_fields as $meta_key => $description) { 48 // Unregister first in case Yoast/Rank Math already registered 49 // without show_in_rest (which would block REST API access) 50 if (registered_meta_key_exists('post', $meta_key, $post_type)) { 51 unregister_meta_key('post', $meta_key, $post_type); 46 52 } 47 } 48 } 49 50 // Rank Math fields (posts + pages) 51 if (defined('RANK_MATH_FILE') || class_exists('RankMath\\Helper')) { 52 $rankmath_fields = [ 53 'rank_math_focus_keyword' => 'Rank Math focus keyword', 54 'rank_math_description' => 'Rank Math meta description', 55 'rank_math_seo_score' => 'Rank Math SEO score', 56 ]; 57 58 foreach (['post', 'page'] as $post_type) { 59 $capability = $post_type === 'post' ? 'edit_posts' : 'edit_pages'; 60 61 foreach ($rankmath_fields as $meta_key => $description) { 62 register_meta($post_type, $meta_key, [ 63 'show_in_rest' => true, 64 'single' => true, 65 'type' => 'string', 66 'description' => $description, 67 'auth_callback' => function() use ($capability) { 68 return current_user_can($capability); 69 } 70 ]); 53 // Also unregister the generic (no subtype) registration 54 if (registered_meta_key_exists('post', $meta_key, '')) { 55 unregister_meta_key('post', $meta_key, ''); 71 56 } 57 58 register_post_meta($post_type, $meta_key, [ 59 'show_in_rest' => true, 60 'single' => true, 61 'type' => 'string', 62 'description' => $description, 63 'auth_callback' => function() use ($post_type) { 64 return current_user_can($post_type === 'page' ? 'edit_pages' : 'edit_posts'); 65 } 66 ]); 72 67 } 73 68 } … … 129 124 130 125 /** 131 * Set SEO scores via update_post_meta after REST API post creation/update. 132 * 133 * This bypasses REST API meta field registration issues (Yoast registers its 134 * own meta keys with show_in_rest => false, so register_meta cannot reliably 135 * override them). By using update_post_meta directly, we write to wp_postmeta 136 * at the PHP level. 137 * 138 * Only acts on requests from SEO Rocket (detected via User-Agent header). 139 */ 140 function seo_rocket_set_seo_scores($post, $request) { 141 $ua = $request->get_header('user_agent'); 142 if ($ua === null || strpos($ua, 'SEO-Rocket') === false) { 143 return; 144 } 145 146 // Yoast SEO scores 147 if (class_exists('WPSEO_Options')) { 148 update_post_meta($post->ID, '_yoast_wpseo_linkdex', '80'); 149 update_post_meta($post->ID, '_yoast_wpseo_content_score', '80'); 150 } 151 152 // Rank Math scores 153 if (defined('RANK_MATH_FILE') || class_exists('RankMath\\Helper')) { 154 update_post_meta($post->ID, 'rank_math_seo_score', '80'); 155 } 156 } 157 add_action('rest_after_insert_post', 'seo_rocket_set_seo_scores', 10, 2); 158 add_action('rest_after_insert_page', 'seo_rocket_set_seo_scores', 10, 2); 126 * Directly save SEO meta fields from REST API requests using update_post_meta. 127 * 128 * This is the PRIMARY mechanism for saving SEO scores. It bypasses the REST API's 129 * meta registration system entirely, which can fail when Yoast/Rank Math register 130 * the same meta keys without show_in_rest. The register_post_meta calls above 131 * serve as a secondary/backup mechanism. 132 * 133 * This hook fires AFTER WordPress has saved the post and any registered meta fields, 134 * so our update_post_meta calls will either set new values or overwrite values that 135 * were already saved by the REST API meta handler. 136 * 137 * Hooks: rest_after_insert_post, rest_after_insert_page 138 * Priority: 5 (before our indexable rebuild at priority 10) 139 */ 140 function seo_rocket_save_seo_meta($post, $request, $creating) { 141 // Read meta from the raw JSON body to bypass REST API field filtering 142 $json_params = $request->get_json_params(); 143 $meta = isset($json_params['meta']) ? $json_params['meta'] : null; 144 145 if (empty($meta) || !is_array($meta)) { 146 return; 147 } 148 149 $post_id = $post->ID; 150 151 // All SEO meta fields we handle 152 $allowed_fields = [ 153 '_yoast_wpseo_focuskw', 154 '_yoast_wpseo_metadesc', 155 '_yoast_wpseo_linkdex', 156 '_yoast_wpseo_content_score', 157 'rank_math_focus_keyword', 158 'rank_math_description', 159 'rank_math_seo_score', 160 ]; 161 162 $saved_fields = []; 163 foreach ($allowed_fields as $key) { 164 if (isset($meta[$key]) && $meta[$key] !== '') { 165 update_post_meta($post_id, $key, sanitize_text_field($meta[$key])); 166 $saved_fields[] = $key; 167 } 168 } 169 170 } 171 add_action('rest_after_insert_post', 'seo_rocket_save_seo_meta', 5, 3); 172 add_action('rest_after_insert_page', 'seo_rocket_save_seo_meta', 5, 3); 173 174 /** 175 * Rebuild Yoast SEO indexable when a post is saved via REST API 176 * 177 * Yoast stores SEO scores in its own wp_yoast_indexable table, not just in 178 * post meta. The traffic light in the Posts list uses WPSEO_Meta_Columns which 179 * reads from wp_postmeta, but the Yoast sidebar/meta box uses the indexable. 180 * 181 * This hook rebuilds the indexable to keep both in sync. It runs AFTER 182 * seo_rocket_save_seo_meta (priority 5) so the meta values are guaranteed 183 * to be in wp_postmeta when we rebuild. 184 * 185 * Hooks: rest_after_insert_post, rest_after_insert_page 186 * Priority: 10 (after meta save at priority 5) 187 */ 188 function seo_rocket_rebuild_yoast_indexable($post, $request, $creating) { 189 if (!class_exists('WPSEO_Options')) { 190 return; 191 } 192 193 $post_id = $post->ID; 194 195 // Check if we have score fields to sync 196 $json_params = $request->get_json_params(); 197 $meta = isset($json_params['meta']) ? $json_params['meta'] : null; 198 199 if (empty($meta) || !is_array($meta)) { 200 return; 201 } 202 203 $has_score_field = isset($meta['_yoast_wpseo_linkdex']) 204 || isset($meta['_yoast_wpseo_content_score']); 205 206 if (!$has_score_field) { 207 return; 208 } 209 210 // Use Yoast's indexable repository to rebuild the indexable for this post. 211 // This updates the wp_yoast_indexable row with the latest meta values, 212 // ensuring the traffic-light indicator reflects the scores we just set. 213 if (class_exists('Yoast\\WP\\SEO\\Repositories\\Indexable_Repository')) { 214 try { 215 $container = \YoastSEO()->classes; 216 if ($container && method_exists($container, 'get')) { 217 $repository = $container->get('Yoast\\WP\\SEO\\Repositories\\Indexable_Repository'); 218 if ($repository && method_exists($repository, 'find_by_id_and_type')) { 219 $indexable = $repository->find_by_id_and_type($post_id, 'post', false); 220 if ($indexable) { 221 // Sync meta values into the indexable 222 if (isset($meta['_yoast_wpseo_linkdex'])) { 223 $indexable->primary_focus_keyword_score = intval($meta['_yoast_wpseo_linkdex']); 224 } 225 if (isset($meta['_yoast_wpseo_content_score'])) { 226 $indexable->readability_score = intval($meta['_yoast_wpseo_content_score']); 227 } 228 if (isset($meta['_yoast_wpseo_focuskw'])) { 229 $indexable->primary_focus_keyword = sanitize_text_field($meta['_yoast_wpseo_focuskw']); 230 } 231 $indexable->save(); 232 } 233 } 234 } 235 } catch (\Exception $e) { 236 // Silently fail — the meta fields are still saved via update_post_meta, 237 // so the traffic light column (which reads from wp_postmeta) will still 238 // show the correct color. The indexable will sync on next edit. 239 } 240 } 241 } 242 add_action('rest_after_insert_post', 'seo_rocket_rebuild_yoast_indexable', 10, 3); 243 add_action('rest_after_insert_page', 'seo_rocket_rebuild_yoast_indexable', 10, 3); 159 244 160 245 /**
Note: See TracChangeset
for help on using the changeset viewer.