Changeset 3462935
- Timestamp:
- 02/16/2026 10:09:07 PM (6 weeks ago)
- Location:
- rationalseo/trunk
- Files:
-
- 5 edited
-
assets/js/meta-box.js (modified) (2 diffs)
-
includes/class-ai-assistant.php (modified) (4 diffs)
-
includes/class-sitemap.php (modified) (2 diffs)
-
rationalseo.php (modified) (2 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
rationalseo/trunk/assets/js/meta-box.js
r3459391 r3462935 417 417 var content = getEditorContent(); 418 418 var title = getPostTitle(); 419 var keyword = keywordField ? keywordField.value.trim() : ''; 419 420 420 421 if ( ! content && ! title ) { … … 430 431 formData.append( 'content', content ); 431 432 formData.append( 'title', title ); 433 formData.append( 'keyword', keyword ); 432 434 433 435 fetch( config.ajaxUrl, { -
rationalseo/trunk/includes/class-ai-assistant.php
r3459391 r3462935 232 232 $content = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : ''; 233 233 $title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : ''; 234 $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) : ''; 234 235 235 236 if ( empty( $content ) && empty( $title ) ) { … … 246 247 } 247 248 248 $prompt = "Analyze the following content and provide all three of these SEO elements:\n\n"; 249 $prompt .= "1. A focus keyword or keyphrase (2-4 words) that this content should rank for\n"; 250 $prompt .= "2. A compelling SEO title (50-60 characters) — do NOT include a site name or separator\n"; 251 $prompt .= "3. A meta description (150-160 characters) that includes the keyword naturally\n\n"; 249 if ( ! empty( $keyword ) ) { 250 $prompt = "You are given a focus keyword and content. Generate an SEO title and meta description that are built around this focus keyword.\n\n"; 251 $prompt .= "IMPORTANT: The focus keyword is: \"{$keyword}\"\n"; 252 $prompt .= "- The SEO title (50-60 characters) MUST naturally include the focus keyword. Do NOT include a site name or separator.\n"; 253 $prompt .= "- The meta description (150-160 characters) MUST naturally include the focus keyword.\n\n"; 254 } else { 255 $prompt = "Analyze the following content and provide all three of these SEO elements:\n\n"; 256 $prompt .= "1. First, determine a focus keyword or keyphrase (2-4 words) that this content should rank for\n"; 257 $prompt .= "2. Then, build a compelling SEO title (50-60 characters) that naturally includes that keyword — do NOT include a site name or separator\n"; 258 $prompt .= "3. Then, write a meta description (150-160 characters) that naturally includes that keyword\n\n"; 259 $prompt .= "The title and description MUST be based on and include the chosen keyword.\n\n"; 260 } 252 261 253 262 if ( ! empty( $title ) ) { … … 257 266 $prompt .= "Content: {$plain_content}\n\n"; 258 267 $prompt .= 'Respond with ONLY valid JSON in this exact format: {"keyword":"...","title":"...","description":"..."}'; 268 $prompt .= "\nDo NOT wrap the JSON in markdown code fences or backticks. Output raw JSON only."; 259 269 260 270 $response = $this->call_openai( $prompt, 300 ); … … 264 274 } 265 275 266 $data = json_decode( trim( $response ), true ); 267 268 if ( ! is_array( $data ) || empty( $data['keyword'] ) || empty( $data['title'] ) || empty( $data['description'] ) ) { 276 // Strip markdown code fences if present. 277 $clean = trim( $response ); 278 if ( preg_match( '/```(?:json)?\s*([\s\S]*?)```/', $clean, $matches ) ) { 279 $clean = trim( $matches[1] ); 280 } 281 282 $data = json_decode( $clean, true ); 283 284 if ( ! is_array( $data ) || empty( $data['title'] ) || empty( $data['description'] ) ) { 269 285 wp_send_json_error( array( 'message' => __( 'Invalid response from API.', 'rationalseo' ) ) ); 270 286 } 271 287 288 // Use the provided keyword if one was given, otherwise use the AI-generated one. 289 $result_keyword = ! empty( $keyword ) ? $keyword : ( isset( $data['keyword'] ) ? sanitize_text_field( $data['keyword'] ) : '' ); 290 291 if ( empty( $result_keyword ) ) { 292 wp_send_json_error( array( 'message' => __( 'Invalid response from API.', 'rationalseo' ) ) ); 293 } 294 272 295 wp_send_json_success( array( 273 'keyword' => sanitize_text_field( $data['keyword'] ),296 'keyword' => $result_keyword, 274 297 'title' => sanitize_text_field( $data['title'] ), 275 298 'description' => sanitize_text_field( $data['description'] ), -
rationalseo/trunk/includes/class-sitemap.php
r3459391 r3462935 59 59 add_filter( 'query_vars', array( $this, 'add_query_vars' ) ); 60 60 add_action( 'template_redirect', array( $this, 'handle_sitemap_request' ) ); 61 add_filter( 'redirect_canonical', array( $this, 'prevent_sitemap_redirect' ) ); 61 62 add_action( 'rationalseo_rebuild_sitemap', array( $this, 'rebuild_sitemap_cache' ), 10, 2 ); 62 63 … … 98 99 99 100 /** 101 * Prevent WordPress from adding a trailing slash to sitemap URLs. 102 * 103 * WordPress redirect_canonical() adds trailing slashes to URLs by default, 104 * which breaks sitemap.xml by redirecting to sitemap.xml/. 105 * 106 * @since 1.0.5 107 * 108 * @param string $redirect_url The redirect URL. 109 * @return string|false The redirect URL, or false to cancel the redirect. 110 */ 111 public function prevent_sitemap_redirect( $redirect_url ) { 112 if ( get_query_var( 'rationalseo_sitemap' ) ) { 113 return false; 114 } 115 116 // Fallback: check request URI directly in case rewrite rules are not flushed. 117 $path = trim( wp_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' ); 118 if ( preg_match( '/^sitemap(-[a-z0-9_-]+)?\.xml$/', $path ) ) { 119 return false; 120 } 121 122 return $redirect_url; 123 } 124 125 /** 100 126 * Handle sitemap request. 101 127 */ 102 128 public function handle_sitemap_request() { 103 129 $sitemap = get_query_var( 'rationalseo_sitemap' ); 130 131 // Fallback: match request URI directly if rewrite rules did not set query vars. 132 if ( empty( $sitemap ) ) { 133 $path = trim( wp_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' ); 134 135 if ( 'sitemap.xml' === $path ) { 136 $sitemap = 'index'; 137 } elseif ( preg_match( '/^sitemap-([a-z0-9_-]+)-?(\d*)\.xml$/', $path, $matches ) ) { 138 $sitemap = $matches[1]; 139 set_query_var( 'rationalseo_sitemap_page', ! empty( $matches[2] ) ? (int) $matches[2] : 1 ); 140 } 141 142 if ( ! empty( $sitemap ) ) { 143 set_query_var( 'rationalseo_sitemap', $sitemap ); 144 } 145 } 104 146 105 147 if ( empty( $sitemap ) ) { -
rationalseo/trunk/rationalseo.php
r3459391 r3462935 4 4 * Plugin URI: https://rationalwp.com/plugins/rationalseo 5 5 * Description: Technical SEO essentials with zero bloat. No dashboards, analytics, content scoring, or frontend assets. 6 * Version: 1.0. 46 * Version: 1.0.5 7 7 * Author: RationalWP 8 8 * Author URI: https://rationalwp.com … … 18 18 } 19 19 20 define( 'RATIONALSEO_VERSION', '1.0. 4' );20 define( 'RATIONALSEO_VERSION', '1.0.5' ); 21 21 define( 'RATIONALSEO_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 22 22 define( 'RATIONALSEO_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); -
rationalseo/trunk/readme.txt
r3459391 r3462935 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 47 Stable tag: 1.0.5 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 138 138 == Changelog == 139 139 140 = 1.0.5 = 141 * Improved: "Suggest All" now builds title and description around existing focus keyword when one is set 142 * Improved: AI response parsing handles markdown code fences from API 143 * Fixed: Sitemap URLs no longer redirect with trailing slash (breaks XML parsing) 144 * Fixed: Sitemaps now work even when rewrite rules are not flushed 145 140 146 = 1.0.4 = 141 147 * Fixed: Readme stable tag now matches plugin version … … 170 176 == Upgrade Notice == 171 177 178 = 1.0.5 = 179 AI "Suggest All" improvements and sitemap redirect fix. 180 172 181 = 1.0.4 = 173 182 Readme and version sync fix.
Note: See TracChangeset
for help on using the changeset viewer.