PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.11.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.11.0
2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk All 53 releases
← All changes | includes/api/class-seo-score-endpoint.php +44 -4 2.0.0 → 2.11.0 View file →
@@ -286,13 +286,28 @@
286 286 $metadata,
287 287 $score_options
288 288 );
289 289
290 - // Add readability_score and content_quality from frontend if provided
291 - if (!empty($readability_score)) {
290 + // Add readability_score and content_quality from frontend if
291 + // provided. `!== null`, not `!empty()`: 0 is a legitimate score and
292 + // empty() discarded it, so a post the editor scored as 0 kept
293 + // whatever the calculator had produced instead (#394). Both params
294 + // already default to null above, so null means "not sent".
295 + //
296 + // Only when the editor actually measured something, though. Inside a
297 + // page builder there is no editor content to read, so Refresh sent an
298 + // empty `live_content` along with the two labels derived from that
299 + // empty string — the literal words "No content" — and they were
300 + // applied over the correct server-side analysis this request had just
301 + // run, then persisted by save_score(). That is what put "No content"
302 + // on the dashboard for a page with a valid score (#778). With nothing
303 + // measured, the analysis above is the better answer.
304 + $editor_measured = '' !== trim((string) $live_content);
305 +
306 + if ($editor_measured && null !== $readability_score) {
292 307 $score_data['readability_score'] = $readability_score;
293 308 }
294 - if (!empty($content_quality)) {
309 + if ($editor_measured && null !== $content_quality) {
295 310 $score_data['content_quality'] = $content_quality;
296 311 }
297 312
298 313 // Save score if requested
@@ -332,8 +347,10 @@
332 347 // Get existing score data from database
333 348 $existing_data = $this->calculator->get_existing_score_data($post_id);
334 349
335 350 if ($existing_data) {
351 + $existing_data['keywords'] = $this->keyword_placements((int) $post_id);
352 +
336 353 return new WP_REST_Response([
337 354 'success' => true,
338 355 'data' => $existing_data,
339 356 'message' => __('Existing SEO score retrieved successfully', 'thinkrank')
@@ -351,8 +368,27 @@
351 368 }
352 369 }
353 370
354 371 /**
372 + * Where each focus keyword sits (#729), for a stored score. A stored score
373 + * predates the editor's current keywords and content, so this is read
374 + * from the post as saved now — a text pass, no request, no AI.
375 + *
376 + * @param int $post_id Post ID.
377 + * @return array<int, array<string, mixed>>
378 + */
379 + private function keyword_placements(int $post_id): array {
380 + return $this->calculator->keyword_placements(
381 + $this->calculator->analyze_post_content($post_id),
382 + [
383 + 'title' => \ThinkRank\SEO\Pattern_Resolver::effective_title($post_id),
384 + 'description' => \ThinkRank\SEO\Pattern_Resolver::effective_description($post_id),
385 + ],
386 + \ThinkRank\SEO\Focus_Keywords::get($post_id)
387 + );
388 + }
389 +
390 + /**
355 391 * Get score history for a post
356 392 *
357 393 * @param WP_REST_Request $request Request object
358 394 * @return WP_REST_Response|WP_Error Response object
@@ -387,10 +423,14 @@
387 423 public function get_latest_score(WP_REST_Request $request) {
388 424 try {
389 425 $post_id = $request->get_param('post_id');
390 426
427 + // The stored row only — no keyword gauges (#729). The dashboard
428 + // asks for five posts at once and reads overall_score; a gauge
429 + // pass would analyse and render each whole post for nothing. The
430 + // editor reads its gauges from /seo-score/get.
391 431 $latest_score = $this->calculator->get_latest_score($post_id);
392 -
432 +
393 433 return new WP_REST_Response([
394 434 'success' => true,
395 435 'data' => $latest_score,
396 436 ], 200);