PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | admin/class-metasync-seo-columns.php +61 -12 2.6.21 → 2.7.1 View file →
@@ -362,11 +362,16 @@
362 362 * mirrors written for third-party plugins and the MCP tools; no singular render
363 363 * path reads them (confirmed by two independent traces). Ranking them above OTTO
364 364 * — as Metasync_SEO_Health still does — makes the column disagree with the page.
365 365 *
366 - * Reads post meta only. No API calls and no transient lookups: WP_Query has
367 - * already primed the post meta cache for this list table, so this costs nothing,
368 - * whereas a per-row transient read would not.
366 + * Reads post meta only, with one exception: the OTTO disable test also consults
367 + * the manual URL exclusion list, which costs a URL resolution per row plus a
368 + * single cached read of the exclusion set for the whole request. On the Pages
369 + * table that resolution is not free — get_permalink() walks ancestors through
370 + * get_page_uri() for a nested page, which WP_Query has not primed, and it fires
371 + * the post_link/page_link filters that translation and permalink plugins hook.
372 + * Everything else here is free: WP_Query has already primed the post meta cache
373 + * for this list table.
369 374 *
370 375 * @param int $post_id Post ID.
371 376 * @param string $field 'title' or 'description'.
372 377 * @return array{value:string,source:string} Source is '' for a user-owned value.
@@ -374,17 +379,30 @@
374 379 private function resolve_rendered_value($post_id, $field)
375 380 {
376 381 $is_title = ($field === 'title');
377 382
378 - $chain = array($is_title ? '_metasync_seo_title' : '_metasync_seo_desc' => '');
379 -
383 + // MetaSync's own tiers come from Metasync_Seo_Precedence, the one place
384 + // the order is defined.
385 + //
380 386 // OTTO stands down entirely for a post with the per-post disable flag set
381 387 // (otto/metasync-otto-seo-functions.php:2582 -> the title filter returns the
382 388 // incoming title untouched). Its persisted suggestion is then dead data, so
383 389 // reporting it would name a value the page does not use.
384 - if (!$this->otto_disabled_for_post($post_id)) {
385 - $chain[$is_title ? '_metasync_otto_title' : '_metasync_otto_description'] = 'OTTO';
386 - }
390 + //
391 + // The persisted-OTTO tier is left out here, which is what this column has
392 + // always done. SEO Health does include it, so the two surfaces can disagree
393 + // about a post whose only value is `_metasync_metatitle`; carried forward
394 + // unchanged rather than resolved as a side effect of consolidating.
395 + $chain = Metasync_Seo_Precedence::chain(
396 + $is_title
397 + ? Metasync_Seo_Precedence::FIELD_TITLE
398 + : Metasync_Seo_Precedence::FIELD_DESCRIPTION,
399 + Metasync_Seo_Precedence::TYPE_POST,
400 + array(
401 + 'include_otto' => !$this->otto_disabled_for_post($post_id),
402 + 'include_persisted_otto' => false,
403 + )
404 + );
387 405
388 406 $third_party = $this->get_active_plugin_meta_keys($is_title);
389 407
390 408 // The legacy `meta_description` tag is only emitted when no active SEO plugin
@@ -416,18 +434,49 @@
416 434
417 435 /**
418 436 * Whether OTTO output is switched off for this specific post.
419 437 *
420 - * Matches Metasync_Otto_Frontend_Toolbar::is_otto_disabled() exactly — it treats
421 - * only '1' and 'true' as disabled. A looser truthiness test would read the string
422 - * 'false' as disabled and wrongly hide OTTO's value from the column while the
423 - * front end still serves it.
438 + * Delegates to Metasync_Otto_Frontend_Toolbar::is_otto_disabled(), which reports
439 + * the effective status: the `_metasync_otto_disabled` meta flag OR a manual entry
440 + * on the Compatibility page's "Excluded URLs" list. Both stop OTTO rendering,
441 + * so both have to stop the column naming OTTO's value as the rendered one.
424 442 *
443 + * The fallback repeats the meta test verbatim for the case where the toolbar class
444 + * is not loaded. It treats only '1' and 'true' as disabled: a looser truthiness
445 + * test would read the string 'false' as disabled and wrongly hide OTTO's value
446 + * from the column while the front end still serves it.
447 + *
425 448 * @param int $post_id Post ID.
426 449 * @return bool
427 450 */
428 451 private function otto_disabled_for_post($post_id)
429 452 {
453 + // Quick Edit saves over admin-ajax as `inline-save`, and wp_ajax_inline_save()
454 + // redraws this row. metasync.php skips otto/otto_pixel.php for any admin-ajax
455 + // action that is not ours, so the exclusion checker is absent in exactly the
456 + // request that re-renders the column — and the row would show OTTO's value for
457 + // an excluded post until the next full page load. Load it here, where the
458 + // column is actually being rendered, rather than widening the load for every
459 + // third-party ajax request: this runs only inside a column render callback,
460 + // so a Quick Edit save pays for it and the frequent ajax actions the guard in
461 + // metasync.php exists for (heartbeat, autosave) never reach this line.
462 + //
463 + // Gated on wp_doing_ajax() rather than the DOING_AJAX constant that
464 + // metasync_is_non_metasync_admin_ajax() reads. The two can in principle
465 + // disagree — wp_doing_ajax() is filterable — but it is the sanctioned API
466 + // and the one PHPStan's WordPress ruleset requires, and a plugin filtering
467 + // it to false mid-Quick-Edit would only cost this column its freshness.
468 + if (!function_exists('metasync_is_otto_url_manually_excluded') && wp_doing_ajax()) {
469 + $pixel_file = plugin_dir_path(dirname(__FILE__)) . 'otto/otto_pixel.php';
470 + if (file_exists($pixel_file)) {
471 + require_once $pixel_file;
472 + }
473 + }
474 +
475 + if (class_exists('Metasync_Otto_Frontend_Toolbar')) {
476 + return (bool) Metasync_Otto_Frontend_Toolbar::is_otto_disabled($post_id);
477 + }
478 +
430 479 $disabled = get_post_meta($post_id, '_metasync_otto_disabled', true);
431 480 return $disabled === '1' || $disabled === 'true';
432 481 }
433 482