Plugin Directory

Changeset 3462935


Ignore:
Timestamp:
02/16/2026 10:09:07 PM (6 weeks ago)
Author:
rationalwp
Message:

Release v1.0.5

Location:
rationalseo/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • rationalseo/trunk/assets/js/meta-box.js

    r3459391 r3462935  
    417417        var content = getEditorContent();
    418418        var title = getPostTitle();
     419        var keyword = keywordField ? keywordField.value.trim() : '';
    419420
    420421        if ( ! content && ! title ) {
     
    430431        formData.append( 'content', content );
    431432        formData.append( 'title', title );
     433        formData.append( 'keyword', keyword );
    432434
    433435        fetch( config.ajaxUrl, {
  • rationalseo/trunk/includes/class-ai-assistant.php

    r3459391 r3462935  
    232232        $content = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : '';
    233233        $title   = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '';
     234        $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) : '';
    234235
    235236        if ( empty( $content ) && empty( $title ) ) {
     
    246247        }
    247248
    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        }
    252261
    253262        if ( ! empty( $title ) ) {
     
    257266        $prompt .= "Content: {$plain_content}\n\n";
    258267        $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.";
    259269
    260270        $response = $this->call_openai( $prompt, 300 );
     
    264274        }
    265275
    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'] ) ) {
    269285            wp_send_json_error( array( 'message' => __( 'Invalid response from API.', 'rationalseo' ) ) );
    270286        }
    271287
     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
    272295        wp_send_json_success( array(
    273             'keyword'     => sanitize_text_field( $data['keyword'] ),
     296            'keyword'     => $result_keyword,
    274297            'title'       => sanitize_text_field( $data['title'] ),
    275298            'description' => sanitize_text_field( $data['description'] ),
  • rationalseo/trunk/includes/class-sitemap.php

    r3459391 r3462935  
    5959        add_filter( 'query_vars', array( $this, 'add_query_vars' ) );
    6060        add_action( 'template_redirect', array( $this, 'handle_sitemap_request' ) );
     61        add_filter( 'redirect_canonical', array( $this, 'prevent_sitemap_redirect' ) );
    6162        add_action( 'rationalseo_rebuild_sitemap', array( $this, 'rebuild_sitemap_cache' ), 10, 2 );
    6263
     
    9899
    99100    /**
     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    /**
    100126     * Handle sitemap request.
    101127     */
    102128    public function handle_sitemap_request() {
    103129        $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        }
    104146
    105147        if ( empty( $sitemap ) ) {
  • rationalseo/trunk/rationalseo.php

    r3459391 r3462935  
    44 * Plugin URI: https://rationalwp.com/plugins/rationalseo
    55 * Description: Technical SEO essentials with zero bloat. No dashboards, analytics, content scoring, or frontend assets.
    6  * Version: 1.0.4
     6 * Version: 1.0.5
    77 * Author: RationalWP
    88 * Author URI: https://rationalwp.com
     
    1818}
    1919
    20 define( 'RATIONALSEO_VERSION', '1.0.4' );
     20define( 'RATIONALSEO_VERSION', '1.0.5' );
    2121define( 'RATIONALSEO_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2222define( 'RATIONALSEO_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • rationalseo/trunk/readme.txt

    r3459391 r3462935  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.0.4
     7Stable tag: 1.0.5
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    138138== Changelog ==
    139139
     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
    140146= 1.0.4 =
    141147* Fixed: Readme stable tag now matches plugin version
     
    170176== Upgrade Notice ==
    171177
     178= 1.0.5 =
     179AI "Suggest All" improvements and sitemap redirect fix.
     180
    172181= 1.0.4 =
    173182Readme and version sync fix.
Note: See TracChangeset for help on using the changeset viewer.