diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index e8900d1ccdc1a..71d95aff5ca8a 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -665,6 +665,8 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex } } + $editor_settings['canUpdateBlockBindings'] = current_user_can( 'edit_block_binding', $block_editor_context ); + /** * Filters the settings to pass to the block editor for all editor type. * diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php index d420717bc759b..dab0067f63858 100644 --- a/src/wp-includes/capabilities.php +++ b/src/wp-includes/capabilities.php @@ -801,6 +801,32 @@ function map_meta_cap( $cap, $user_id, ...$args ) { case 'delete_app_password': $caps = map_meta_cap( 'edit_user', $user_id, $args[0] ); break; + case 'edit_block_binding': + $block_editor_context = $args[0]; + if ( isset( $block_editor_context->post ) ) { + $object_id = $block_editor_context->post->ID; + } + /* + * If the post ID is null, check if the context is the site editor. + * Fall back to the edit_theme_options in that case. + */ + if ( ! isset( $object_id ) ) { + if ( ! isset( $block_editor_context->name ) || 'core/edit-site' !== $block_editor_context->name ) { + $caps[] = 'do_not_allow'; + break; + } + $caps = map_meta_cap( 'edit_theme_options', $user_id ); + break; + } + + $object_subtype = get_object_subtype( 'post', (int) $object_id ); + if ( empty( $object_subtype ) ) { + $caps[] = 'do_not_allow'; + break; + } + + $caps = map_meta_cap( "edit_{$object_subtype}", $user_id, $object_id ); + break; default: // Handle meta capabilities for custom post types. global $post_type_meta_caps; diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php index 473b0417ba198..afaf3d54f7e03 100644 --- a/tests/phpunit/tests/user/capabilities.php +++ b/tests/phpunit/tests/user/capabilities.php @@ -570,7 +570,8 @@ public function testMetaCapsTestsAreCorrect() { $expected['read_app_password'], $expected['edit_app_password'], $expected['delete_app_passwords'], - $expected['delete_app_password'] + $expected['delete_app_password'], + $expected['edit_block_binding'] ); $expected = array_keys( $expected ); @@ -2376,4 +2377,52 @@ public function data_block_caps() { return $data; } + + /** + * Test `edit_block_binding` meta capability is properly mapped. + * + * @ticket 61945 + */ + public function test_edit_block_binding_caps_are_mapped_correctly() { + $author = self::$users['administrator']; + $post = self::factory()->post->create_and_get( + array( + 'post_author' => $author->ID, + 'post_type' => 'post', + ) + ); + + foreach ( self::$users as $role => $user ) { + // It should map to `edit_{post_type}` if editing a post. + $this->assertSame( + user_can( $user->ID, 'edit_post', $post->ID ), + user_can( + $user->ID, + 'edit_block_binding', + new WP_Block_Editor_Context( + array( + 'post' => $post, + 'name' => 'core/edit-post', + ) + ) + ), + "Role: {$role} in post editing" + ); + // It should map to `edit_theme_options` if editing a template. + $this->assertSame( + user_can( $user->ID, 'edit_theme_options' ), + user_can( + $user->ID, + 'edit_block_binding', + new WP_Block_Editor_Context( + array( + 'post' => null, + 'name' => 'core/edit-site', + ) + ) + ), + "Role: {$role} in template editing" + ); + } + } }