diff --git a/src/wp-includes/block-bindings/post-data.php b/src/wp-includes/block-bindings/post-data.php index 103aa2e850419..11caa1e1cde61 100644 --- a/src/wp-includes/block-bindings/post-data.php +++ b/src/wp-includes/block-bindings/post-data.php @@ -23,10 +23,29 @@ function _block_bindings_post_data_get_value( array $source_args, $block_instanc return null; } - if ( empty( $block_instance->context['postId'] ) ) { + /* + * BACKWARDS COMPATIBILITY: Hardcoded exception for navigation blocks. + * Required for WordPress 6.9+ navigation blocks. DO NOT REMOVE. + */ + $block_name = $block_instance->name ?? ''; + $is_navigation_block = in_array( + $block_name, + array( 'core/navigation-link', 'core/navigation-submenu' ), + true + ); + + if ( $is_navigation_block ) { + // Navigation blocks: read from block attributes. + $post_id = $block_instance->attributes['id'] ?? null; + } else { + // All other blocks: use context. + $post_id = $block_instance->context['postId'] ?? null; + } + + // If we don't have an entity ID, bail early. + if ( empty( $post_id ) ) { return null; } - $post_id = $block_instance->context['postId']; // If a post isn't public, we need to prevent unauthorized users from accessing the post data. $post = get_post( $post_id ); @@ -46,6 +65,11 @@ function _block_bindings_post_data_get_value( array $source_args, $block_instanc return ''; } } + + if ( 'link' === $source_args['key'] ) { + $permalink = get_permalink( $post_id ); + return false === $permalink ? null : esc_url( $permalink ); + } } /** diff --git a/src/wp-includes/block-bindings/term-data.php b/src/wp-includes/block-bindings/term-data.php new file mode 100644 index 0000000000000..c9522d88a07d1 --- /dev/null +++ b/src/wp-includes/block-bindings/term-data.php @@ -0,0 +1,119 @@ + "name" ). + * @param WP_Block $block_instance The block instance. + * @return mixed The value computed for the source. + */ +function _block_bindings_term_data_get_value( array $source_args, $block_instance ) { + if ( empty( $source_args['key'] ) ) { + return null; + } + + /* + * BACKWARDS COMPATIBILITY: Hardcoded exception for navigation blocks. + * Required for WordPress 6.9+ navigation blocks. DO NOT REMOVE. + */ + $block_name = $block_instance->name ?? ''; + $is_navigation_block = in_array( + $block_name, + array( 'core/navigation-link', 'core/navigation-submenu' ), + true + ); + + if ( $is_navigation_block ) { + // Navigation blocks: read from block attributes. + $term_id = $block_instance->attributes['id'] ?? null; + $type = $block_instance->attributes['type'] ?? ''; + // Map UI shorthand to taxonomy slug when using attributes. + $taxonomy = ( 'tag' === $type ) ? 'post_tag' : $type; + } else { + // All other blocks: use context + $term_id = $block_instance->context['termId'] ?? null; + $taxonomy = $block_instance->context['taxonomy'] ?? ''; + } + + // If we don't have required identifiers, bail early. + if ( empty( $term_id ) || empty( $taxonomy ) ) { + return null; + } + + // Get the term data. + $term = get_term( $term_id, $taxonomy ); + if ( is_wp_error( $term ) || ! $term ) { + return null; + } + + // Check if taxonomy exists and is publicly queryable. + $taxonomy_object = get_taxonomy( $taxonomy ); + if ( ! $taxonomy_object || ! $taxonomy_object->publicly_queryable ) { + if ( ! current_user_can( 'read' ) ) { + return null; + } + } + + switch ( $source_args['key'] ) { + case 'id': + return esc_html( (string) $term_id ); + + case 'name': + return esc_html( $term->name ); + + case 'link': + // Only taxonomy entities are supported by Term Data. + $term_link = get_term_link( $term ); + return is_wp_error( $term_link ) ? null : esc_url( $term_link ); + + case 'slug': + return esc_html( $term->slug ); + + case 'description': + return wp_kses_post( $term->description ); + + case 'parent': + return esc_html( (string) $term->parent ); + + case 'count': + return esc_html( (string) $term->count ); + + default: + return null; + } +} + +/** + * Registers Term Data source in the block bindings registry. + * + * @since 6.9.0 + * @access private + */ +function _register_block_bindings_term_data_source() { + if ( get_block_bindings_source( 'core/term-data' ) ) { + // The source is already registered. + return; + } + + register_block_bindings_source( + 'core/term-data', + array( + 'label' => _x( 'Term Data', 'block bindings source' ), + 'get_value_callback' => '_block_bindings_term_data_get_value', + 'uses_context' => array( 'termId', 'taxonomy' ), + ) + ); +} + +add_action( 'init', '_register_block_bindings_term_data_source' ); diff --git a/src/wp-settings.php b/src/wp-settings.php index b59991e92b2bb..b1f7a7cadcf4a 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -369,6 +369,7 @@ require ABSPATH . WPINC . '/block-bindings/pattern-overrides.php'; require ABSPATH . WPINC . '/block-bindings/post-data.php'; require ABSPATH . WPINC . '/block-bindings/post-meta.php'; +require ABSPATH . WPINC . '/block-bindings/term-data.php'; require ABSPATH . WPINC . '/blocks.php'; require ABSPATH . WPINC . '/blocks/index.php'; require ABSPATH . WPINC . '/block-editor.php'; diff --git a/tests/phpunit/includes/functions.php b/tests/phpunit/includes/functions.php index 516727380c109..898f1214ab15a 100644 --- a/tests/phpunit/includes/functions.php +++ b/tests/phpunit/includes/functions.php @@ -350,6 +350,7 @@ function _unhook_block_registration() { remove_action( 'init', '_register_block_bindings_pattern_overrides_source' ); remove_action( 'init', '_register_block_bindings_post_data_source' ); remove_action( 'init', '_register_block_bindings_post_meta_source' ); + remove_action( 'init', '_register_block_bindings_term_data_source' ); } tests_add_filter( 'init', '_unhook_block_registration', 1000 ); diff --git a/tests/phpunit/tests/block-bindings/register.php b/tests/phpunit/tests/block-bindings/register.php index cff3c0251cd6e..008c8b209bc35 100644 --- a/tests/phpunit/tests/block-bindings/register.php +++ b/tests/phpunit/tests/block-bindings/register.php @@ -75,6 +75,7 @@ public function test_get_all_registered() { 'core/post-data' => get_block_bindings_source( 'core/post-data' ), 'core/post-meta' => get_block_bindings_source( 'core/post-meta' ), 'core/pattern-overrides' => get_block_bindings_source( 'core/pattern-overrides' ), + 'core/term-data' => get_block_bindings_source( 'core/term-data' ), ); $registered = get_all_registered_block_bindings_sources();