Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
6cbc70e
feat: expose all sidebar-editable post meta fields in the REST API
vraja-pro Jun 1, 2026
1a5e71a
feat: add wpseo_disable_metabox_in_block_editor filter to skip hidden…
vraja-pro Jun 1, 2026
00961c1
feat: route Fields helper reads/writes through core/editor when metab…
vraja-pro Jun 1, 2026
11b670d
feat: use core/editor meta for snippet and score reads/writes when me…
vraja-pro Jun 1, 2026
2ff66d4
feat: sync is_cornerstone from core/editor into yoast-seo/editor
vraja-pro Jun 1, 2026
239f5cf
test: fix and extend useYoastMetaSync and initialize tests for setCor…
vraja-pro Jun 1, 2026
fb6d339
refactor: extract meta key strings into camelCase constants to fix ca…
vraja-pro Jun 1, 2026
ce731ef
refactor: move meta-key constants to shared-admin/constants/meta-keys.js
vraja-pro Jun 1, 2026
f5d16a9
refactor: re-export meta-keys from shared-admin/constants index and u…
vraja-pro Jun 1, 2026
36c68de
refactor: replace inline meta key strings with shared-admin constants…
vraja-pro Jun 1, 2026
05bf66a
fix: wrap long useDispatch.mockReturnValue call to satisfy max-len li…
vraja-pro Jun 1, 2026
baa7659
refactor: add schema meta keys to constants file
vraja-pro Jun 1, 2026
209eb6b
fix linting
vraja-pro Jun 1, 2026
6792b23
fix unrelated linting
vraja-pro Jun 1, 2026
cad6d57
Merge branch 'trunk' into 1275-decouple-the-metabox-from-the-block-ed…
vraja-pro Jun 1, 2026
0b39e94
fix: align array double arrows in script_data after adding disableMet…
vraja-pro Jun 1, 2026
c2b4258
feat: skip add_meta_box entirely when wpseo_disable_metabox_in_block_…
vraja-pro Jun 1, 2026
c82633b
fix: support pprimary taxonomy id meta data
vraja-pro Jun 1, 2026
a249ebc
refactor: extract helpers from initializePostAnalysis to fix max-stat…
vraja-pro Jun 1, 2026
978060b
fix: guard editPost dispatches in AnalysisFields until editor is ready
vraja-pro Jun 1, 2026
0f0838a
fix: remove object_subtype restriction from REST meta registration
vraja-pro Jun 2, 2026
aa8cfea
fix: gate render_hidden_fields() behind is_metabox_disabled_in_block_…
vraja-pro Jun 2, 2026
4175b9b
Fix score persistence when metabox is disabled
vraja-pro Jun 2, 2026
8864bd1
refactor: register all REST-exposed meta in a single register_meta call
vraja-pro Jun 2, 2026
8ae6ee6
fix: set single to true in register_meta for all post meta fields
vraja-pro Jun 2, 2026
24d7611
fix: scope save_postdata REST bail to requests without a metabox nonce
vraja-pro Jun 2, 2026
bf32880
revert: restore isLarge prop on ClipboardButton in PrimaryTaxonomyFilter
vraja-pro Jun 2, 2026
5f13afc
fix: queue AnalysisFields meta writes until core/editor is ready
vraja-pro Jun 2, 2026
e8b1441
docs: explain why object_subtype is used in register_primary_term_met…
vraja-pro Jun 2, 2026
b43cded
refactor: extract PrimaryTermFields helper for primary term meta pers…
vraja-pro Jun 2, 2026
07fe84d
refactor: extract shared isRestMetaActive helper to eliminate duplica…
vraja-pro Jun 2, 2026
3596d35
fix php cs
vraja-pro Jun 2, 2026
e54569e
fix: coerce meta values to string in writeOrQueue to pass REST type v…
vraja-pro Jun 2, 2026
ec9b973
fix: reflect advanced settings correctly when metabox is disabled in …
vraja-pro Jun 2, 2026
c2e6a42
refactor: remove scheduleSettingsSync from AdvancedFields
vraja-pro Jun 2, 2026
95c6358
refactor: remove getLoadableSettings and inline reads into loadAdvanc…
vraja-pro Jun 2, 2026
2b7341a
revert: remove is_cornerstone sync from useYoastMetaSync
vraja-pro Jun 2, 2026
89a9d07
test: remove setCornerstoneContent assertions from useYoastMetaSync t…
vraja-pro Jun 2, 2026
cbcc517
cleanup of show_in_rest default
vraja-pro Jun 3, 2026
5709b39
refactor: replace inline disableMetaboxInBlockEditor checks with isRe…
vraja-pro Jun 3, 2026
2c38bf3
fix: defer cornerstone dispatch until core/editor meta is loaded in R…
vraja-pro Jun 3, 2026
7a09e56
fix: prevent duplicate undo entries and score writes from polluting u…
vraja-pro Jun 3, 2026
5a66346
removes comment that is not relevant
vraja-pro Jun 3, 2026
7f177d0
refactor: rename is-rest-meta-active.js to rest-meta.js
vraja-pro Jun 3, 2026
49e4b67
refactor: rename is-rest-meta-active.js to rest-meta.js
vraja-pro Jun 3, 2026
b86f05e
fix: exclude estimated reading time from undo stack and register hidd…
vraja-pro Jun 3, 2026
8664c5b
refactor: extract writeMetaWithoutUndo helper and use it for estimate…
vraja-pro Jun 3, 2026
2e7c2bf
refactor: use lodash get in isRestMetaActive for safe nested property…
vraja-pro Jun 3, 2026
932e493
refactor: extract register_meta as a public static method for addon r…
vraja-pro Jun 3, 2026
8513d38
fix: strip all meta keys with the Yoast prefix from unauthorized REST…
vraja-pro Jun 3, 2026
df67c4a
refactor: isRestMetaActive shouldn't be a function
vraja-pro Jun 3, 2026
98aaddb
refactor: source isRestMetaActive from the preferences store
vraja-pro Jun 4, 2026
6a58b4e
Revert "refactor: source isRestMetaActive from the preferences store"
vraja-pro Jun 4, 2026
61f8646
refactor: pass field_def to register_meta to control REST API visibility
vraja-pro Jun 5, 2026
9e17158
fix: add traversable type hint to register_meta field_def param
vraja-pro Jun 5, 2026
ed8ae36
fix: php cs
vraja-pro Jun 5, 2026
7bb0ff0
fix: remove duplicate field
vraja-pro Jun 12, 2026
b01800a
fix: hide post meta from REST for all post types
vraja-pro Jun 12, 2026
af0d6d3
fix: register primary term meta on init so CPT taxonomies are available
vraja-pro Jun 12, 2026
0bbfeb3
fix: coerce imageId to string before dispatching to REST meta
vraja-pro Jun 12, 2026
1936004
refactor: centralize REST meta read/write into getMetaValue/setMetaVa…
vraja-pro Jun 12, 2026
f54137b
fix: invert setMetaValue undo param to withoutUndo for clearer default
vraja-pro Jun 12, 2026
83ec10e
refactor: move SchemaFields into helpers/fields and use getMetaValue/…
vraja-pro Jun 12, 2026
2b0833f
fix: correct stale @param name in setMetaValue JSDoc
vraja-pro Jun 12, 2026
ca48fec
fix: use isRestMetaActive instead of re-reading wpseoScriptData in po…
vraja-pro Jun 12, 2026
a22f938
fix: remove redundant optional chaining in PrimaryTermFields.getIniti…
vraja-pro Jun 12, 2026
e578764
fix: explain direct filter call and return false in save_postdata bail
vraja-pro Jun 12, 2026
0741cb3
refactor: unexport readMeta and writeMeta as they are internal helpers
vraja-pro Jun 12, 2026
e08174a
fix: correct deprecated version to 28.0 in Estimated_Reading_Time
vraja-pro Jun 12, 2026
61c2236
test: add unit tests for rest-meta helpers and PrimaryTermFields
vraja-pro Jun 12, 2026
b2e2ff1
refactor: use meta key constants in rest-meta tests
vraja-pro Jun 12, 2026
1039044
test: add unit tests for all helpers/fields classes
vraja-pro Jun 12, 2026
626f75e
fix: register primary term meta in fields_index and defaults to fix s…
vraja-pro Jun 12, 2026
3edba17
fix: register estimated reading time field via add_extra_wpseo_meta_f…
vraja-pro Jun 12, 2026
d5f17ca
fix: php cs
vraja-pro Jun 12, 2026
f34a605
Merge branch 'trunk' of github.com:Yoast/wordpress-seo into 1275-deco…
FAMarfuaty Jun 16, 2026
fa49fed
fix: fix saving of advanced settings
vraja-pro Jun 16, 2026
03f3541
fix: move schema defaults and showArticleInput flag to wpseoScriptData
vraja-pro Jun 16, 2026
2083ee7
fix primary term save
vraja-pro Jun 17, 2026
afc017b
fix: tests for helpers fields
vraja-pro Jun 17, 2026
8c5b1c3
fix: separate rest registration and cleanup wpseo meta and added a c…
vraja-pro Jun 17, 2026
4c008b4
fix: defer initial keyphrase load in REST meta mode until entity meta…
vraja-pro Jun 17, 2026
d964ac6
fix: ensure CPTs expose meta in REST API by adding custom-fields support
vraja-pro Jun 17, 2026
258ccab
fix: guard schema_page_type access when get_meta_field_defs returns e…
vraja-pro Jun 17, 2026
93d473a
fix: restore unauthorized REST read filter and fix stale JSDoc
vraja-pro Jun 17, 2026
ad93352
fix: fire wpseo_saved_postdata after REST API post update when metabo…
vraja-pro Jun 17, 2026
c8044ee
tests: add unit tests for Post_Meta_Rest_Fields
vraja-pro Jun 17, 2026
30f19bb
fix: consolidate REST meta boot-time deferral into one subscriber
vraja-pro Jun 17, 2026
690dc99
fix: remove unused this.input assignment in PrimaryTaxonomyPicker
vraja-pro Jun 18, 2026
c578cef
fix: cleanup unused prop
vraja-pro Jun 18, 2026
c6480bf
fix: preserve undoIgnore flag for queued meta writes in AnalysisFields
vraja-pro Jun 18, 2026
c47f239
refactor: route PostDataCollector field access through AnalysisFields…
vraja-pro Jun 18, 2026
20a0b83
tests: add REST meta and pending-write flush coverage for field helpers
vraja-pro Jun 18, 2026
8854e29
refactor: extract initializeSnippetEditorSync to snippet-editor-sync.js
vraja-pro Jun 18, 2026
af484bc
refactor: move snippet-editor-sync from initializers to helpers
vraja-pro Jun 18, 2026
38e6a9c
refactor: extract post-scraper sub-steps into initializers/post-scraper/
vraja-pro Jun 18, 2026
799eaad
tests: drop redundant description REST meta mode tests
vraja-pro Jun 18, 2026
3ec4daa
fix: initializeSnippetEditorSync imports
vraja-pro Jun 18, 2026
81483df
restore refactor
vraja-pro Jun 18, 2026
9de6f06
refactor: inline initializeSnippetEditorSync, extract dispatchInitial…
vraja-pro Jun 18, 2026
5a6f161
restore refactor to avoid scoop creep
vraja-pro Jun 18, 2026
24ae9e6
fix: gate add_post_type_support behind filter and block editor check
vraja-pro Jun 18, 2026
495db0b
fix: add wpseo_edit_advanced_metadata cap check to advanced/schema fi…
vraja-pro Jun 18, 2026
ed540a8
tests: replace unit tests with WP integration tests for Post_Meta_Res…
vraja-pro Jun 19, 2026
7230c9b
fix: removes default value from rest registration
vraja-pro Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions admin/metabox/class-metabox.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ public function display_metabox( $identifier = null, $type = 'post_type' ) {
* @return void
*/
public function add_meta_box() {
if ( $this->is_metabox_disabled_in_block_editor() ) {
return;
}
Comment thread
vraja-pro marked this conversation as resolved.

$post_types = WPSEO_Post_Type::get_accessible_post_types();
$post_types = array_filter( $post_types, [ $this, 'display_metabox' ] );

Expand Down Expand Up @@ -334,6 +338,32 @@ public function meta_box() {
$this->render_tabs();
}
Comment thread
vraja-pro marked this conversation as resolved.

/**
* Returns whether the metabox hidden fields and $_POST-based save are disabled for the block editor.
*
* When this returns true the block editor uses the REST API meta path instead: all WPSEO post meta
* fields are registered with show_in_rest, so core/editor carries and saves them automatically.
*
* Filter: 'wpseo_disable_metabox_in_block_editor'
*
* @return bool
*/
protected function is_metabox_disabled_in_block_editor() {
$screen = WP_Screen::get();
if ( ! $screen || ! $screen->is_block_editor() ) {
return false;
}

/**
* Filter: 'wpseo_disable_metabox_in_block_editor' - Disables the Yoast metabox hidden fields
* and the $_POST-based save hook in the block editor, so that post meta is saved via the
* WordPress REST API instead (requires all relevant fields to have show_in_rest enabled).
*
* @param bool $disable Whether to disable the metabox. Default false.
*/
return (bool) apply_filters( 'wpseo_disable_metabox_in_block_editor', false );
}

/**
* Renders the metabox hidden fields.
*
Expand Down Expand Up @@ -700,6 +730,12 @@ public function save_postdata( $post_id ) {
return false;
}

// Bail when the REST API meta path is active: core/editor sends meta via REST,
// not via $_POST, so no nonce or hidden field values will be present.
if ( ( defined( 'REST_REQUEST' ) && REST_REQUEST ) && apply_filters( 'wpseo_disable_metabox_in_block_editor', false ) ) {
return;
}

if ( $post_id === null ) {
return false;
}
Expand Down Expand Up @@ -904,19 +940,20 @@ public function enqueue() {
$is_front_page = $homepage_is_page && $page_on_front === (int) $post_id;

$script_data = [
'metabox' => $this->get_metabox_script_data(),
'isPost' => true,
'isBlockEditor' => $is_block_editor,
'postId' => $post_id,
'postStatus' => get_post_status( $post_id ),
'postType' => get_post_type( $post_id ),
'isPage' => get_post_type( $post_id ) === 'page',
'usedKeywordsNonce' => wp_create_nonce( 'wpseo-keyword-usage-and-post-types' ),
'analysis' => [
'metabox' => $this->get_metabox_script_data(),
'isPost' => true,
'isBlockEditor' => $is_block_editor,
'disableMetaboxInBlockEditor' => $is_block_editor && (bool) apply_filters( 'wpseo_disable_metabox_in_block_editor', false ),
'postId' => $post_id,
'postStatus' => get_post_status( $post_id ),
'postType' => get_post_type( $post_id ),
'isPage' => get_post_type( $post_id ) === 'page',
'usedKeywordsNonce' => wp_create_nonce( 'wpseo-keyword-usage-and-post-types' ),
'analysis' => [
'plugins' => $plugins_script_data,
'worker' => $worker_script_data,
],
'isFrontPage' => $is_front_page,
'isFrontPage' => $is_front_page,
];

/**
Expand Down
30 changes: 28 additions & 2 deletions inc/class-wpseo-meta.php

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the REST registration of the meta data to an initializer class.

  • Separation of concerns
  • Support registration for CPT (registering the fields after the CPT is registered)
  • Easily add tests.

Keeping the regular register meta doesn't harm.

Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,26 @@ class WPSEO_Meta {
'linkdex' => [
'type' => 'hidden',
'default_value' => '0',
'show_in_rest' => true,
'single' => true,
],
Comment thread
vraja-pro marked this conversation as resolved.
'content_score' => [
'type' => 'hidden',
'default_value' => '0',
'show_in_rest' => true,
'single' => true,
],
'inclusive_language_score' => [
'type' => 'hidden',
'default_value' => '0',
'show_in_rest' => true,
'single' => true,
],
'is_cornerstone' => [
'type' => 'hidden',
'default_value' => 'false',
'show_in_rest' => true,
'single' => true,
],
],
'advanced' => [
Expand All @@ -146,6 +154,8 @@ class WPSEO_Meta {
'2' => '', // Index.
'1' => '', // No-index.
],
'show_in_rest' => true,
'single' => true,
],
'meta-robots-nofollow' => [
'type' => 'hidden',
Expand All @@ -154,6 +164,8 @@ class WPSEO_Meta {
'0' => '', // Follow.
'1' => '', // No-follow.
],
'show_in_rest' => true,
'single' => true,
],
'meta-robots-adv' => [
'type' => 'hidden',
Expand All @@ -163,30 +175,42 @@ class WPSEO_Meta {
'noarchive' => '',
'nosnippet' => '',
],
'show_in_rest' => true,
'single' => true,
],
'bctitle' => [
'type' => 'hidden',
'default_value' => '',
'show_in_rest' => true,
'single' => true,
],
'canonical' => [
'type' => 'hidden',
'default_value' => '',
'show_in_rest' => true,
'single' => true,
],
'redirect' => [
'type' => 'url',
'default_value' => '',
'show_in_rest' => true,
'single' => true,
],
],
'social' => [],
'schema' => [
'schema_page_type' => [
'type' => 'hidden',
'options' => Schema_Types::PAGE_TYPES,
'type' => 'hidden',
'options' => Schema_Types::PAGE_TYPES,
'show_in_rest' => true,
'single' => true,
],
'schema_article_type' => [
'type' => 'hidden',
'hide_on_pages' => true,
'options' => Schema_Types::ARTICLE_TYPES,
'show_in_rest' => true,
'single' => true,
],
],
/* Fields we should validate & save, but not show on any form. */
Expand Down Expand Up @@ -261,6 +285,8 @@ public static function init() {
self::$meta_fields['social'][ $network . '-' . $box ] = [
'type' => $type,
'default_value' => '',
'show_in_rest' => true,
'single' => true,
];
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"build": "cd ../.. && wp-scripts build --config config/webpack/webpack.config.js",
"test": "jest",
"lint": "eslint . --max-warnings=40"
"lint": "eslint . --max-warnings=37"
},
"dependencies": {
"@draft-js-plugins/mention": "^5.0.0",
Expand Down
10 changes: 4 additions & 6 deletions packages/js/src/ai-content-planner/hooks/use-apply-outline.js
Comment thread
FAMarfuaty marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback } from "@wordpress/element";
import { useDispatch, select } from "@wordpress/data";
import { metaKeyTitle, metaKeyMetaDesc, metaKeyFocusKw } from "../../shared-admin/constants";
import { buildBlocksFromOutline } from "../helpers/build-blocks-from-outline";
import { CONTENT_PLANNER_STORE } from "../constants";

Expand Down Expand Up @@ -48,12 +49,9 @@ export const useApplyOutline = ( { editedOutlineRef } ) => {
title: metaOutline.title,
blocks: buildBlocksFromOutline( structure ),
meta: {
// eslint-disable-next-line camelcase
_yoast_wpseo_title: metaOutline.title,
// eslint-disable-next-line camelcase
_yoast_wpseo_metadesc: metaOutline.metaDescription,
// eslint-disable-next-line camelcase
_yoast_wpseo_focuskw: metaOutline.focusKeyphrase,
[ metaKeyTitle ]: metaOutline.title,
[ metaKeyMetaDesc ]: metaOutline.metaDescription,
[ metaKeyFocusKw ]: metaOutline.focusKeyphrase,
},
};
if ( metaOutline.category?.id && metaOutline.category.id !== -1 ) {
Expand Down
26 changes: 17 additions & 9 deletions packages/js/src/ai-content-planner/hooks/use-yoast-meta-sync.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
import { useDispatch, useSelect } from "@wordpress/data";
import { useEffect } from "@wordpress/element";
import { metaKeyTitle, metaKeyMetaDesc, metaKeyFocusKw, metaKeyIsCornerstone } from "../../shared-admin/constants";

/**
* Mirrors core/editor meta changes into yoast-seo/editor. Fires on every meta change,
* including undo — which is intentional: undo should revert the Yoast fields too.
* Direct sidebar edits (yoast-seo/editor only) will be overwritten if core/editor meta
* changes afterwards — accepted trade-off for correct undo behaviour.
*
* This hook is the core/editor → yoast-seo/editor direction. The reverse direction
* (yoast-seo/editor → core/editor) is handled by the Redux action creators in
* packages/js/src/redux/actions/ via the Fields helpers, which dispatch to core/editor
* when wpseoScriptData.disableMetaboxInBlockEditor is true.
*
* @returns {void}
*/
export function useYoastMetaSync() {
const { yoastTitle, yoastMetaDesc, yoastFocusKw, isPost, titleTemplate, descTemplate } = useSelect( select => {
const { yoastTitle, yoastMetaDesc, yoastFocusKw, yoastIsCornerstone, isPost, titleTemplate, descTemplate } = useSelect( select => {
const editor = select( "core/editor" );
const meta = editor.getEditedPostAttribute( "meta" );
const { title, description } = select( "yoast-seo/editor" ).getSnippetEditorTemplates();
return {
yoastTitle: meta?._yoast_wpseo_title,
yoastMetaDesc: meta?._yoast_wpseo_metadesc,
yoastFocusKw: meta?._yoast_wpseo_focuskw,
yoastTitle: meta?.[ metaKeyTitle ],
yoastMetaDesc: meta?.[ metaKeyMetaDesc ],
yoastFocusKw: meta?.[ metaKeyFocusKw ],
yoastIsCornerstone: meta?.[ metaKeyIsCornerstone ],
isPost: editor.getCurrentPostType() === "post",
titleTemplate: title,
descTemplate: description,
};
}, [] );
const { updateData, setFocusKeyword } = useDispatch( "yoast-seo/editor" );
const { updateData, setFocusKeyword, setCornerstoneContent } = useDispatch( "yoast-seo/editor" );

useEffect( () => {
// These meta keys are only registered for the 'post' subtype; bail on all other post types
// to avoid dispatching undefined values into yoast-seo/editor.
if ( ! isPost ) {
return;
}
// Only sync non-empty values. An empty string means no custom value has been saved, in
// which case the snippet editor should keep showing the SEO title template instead of
// being overwritten with an empty string.
// Only sync non-empty values. An empty string means no custom value has been saved,
// in which case the snippet editor should keep showing the SEO title
// template instead of being overwritten with an empty string.
const dataToSync = {
title: titleTemplate,
description: descTemplate,
Expand All @@ -46,5 +53,6 @@ export function useYoastMetaSync() {
}
updateData( dataToSync );
setFocusKeyword( yoastFocusKw || "" );
}, [ isPost, yoastTitle, yoastMetaDesc, yoastFocusKw, titleTemplate, descTemplate ] );
setCornerstoneContent( yoastIsCornerstone === "1" );
}, [ isPost, yoastTitle, yoastMetaDesc, yoastFocusKw, yoastIsCornerstone, titleTemplate, descTemplate ] );
}
Loading
Loading