Make WordPress Core

Ticket #45098: 45098.2.diff

File 45098.2.diff, 38.3 KB (added by desrosj, 7 years ago)
  • src/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php

     
     1<?php
     2/**
     3 * Block Renderer REST API: WP_REST_Block_Renderer_Controller class
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Controller which provides REST endpoint for rendering a block.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see WP_REST_Controller
     16 */
     17class WP_REST_Block_Renderer_Controller extends WP_REST_Controller {
     18
     19        /**
     20         * Constructs the controller.
     21         *
     22         * @since 5.0.0
     23         */
     24        public function __construct() {
     25                $this->namespace = 'wp/v2';
     26                $this->rest_base = 'block-renderer';
     27        }
     28
     29        /**
     30         * Registers the necessary REST API routes, one for each dynamic block.
     31         *
     32         * @since 5.0.0
     33         */
     34        public function register_routes() {
     35                $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
     36
     37                foreach ( $block_types as $block_type ) {
     38                        if ( ! $block_type->is_dynamic() ) {
     39                                continue;
     40                        }
     41
     42                        register_rest_route(
     43                                $this->namespace,
     44                                '/' . $this->rest_base . '/(?P<name>' . $block_type->name . ')',
     45                                array(
     46                                        'args'   => array(
     47                                                'name' => array(
     48                                                        'description' => __( 'Unique registered name for the block.' ),
     49                                                        'type'        => 'string',
     50                                                ),
     51                                        ),
     52                                        array(
     53                                                'methods'             => WP_REST_Server::READABLE,
     54                                                'callback'            => array( $this, 'get_item' ),
     55                                                'permission_callback' => array( $this, 'get_item_permissions_check' ),
     56                                                'args'                => array(
     57                                                        'context'    => $this->get_context_param( array( 'default' => 'view' ) ),
     58                                                        'attributes' => array(
     59                                                                /* translators: %s is the name of the block */
     60                                                                'description'          => sprintf( __( 'Attributes for %s block' ), $block_type->name ),
     61                                                                'type'                 => 'object',
     62                                                                'additionalProperties' => false,
     63                                                                'properties'           => $block_type->get_attributes(),
     64                                                        ),
     65                                                        'post_id'    => array(
     66                                                                'description' => __( 'ID of the post context.' ),
     67                                                                'type'        => 'integer',
     68                                                        ),
     69                                                ),
     70                                        ),
     71                                        'schema' => array( $this, 'get_public_item_schema' ),
     72                                )
     73                        );
     74                }
     75        }
     76
     77        /**
     78         * Checks if a given request has access to read blocks.
     79         *
     80         * @since 5.0.0
     81         *
     82         * @param WP_REST_Request $request Request.
     83         * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
     84         */
     85        public function get_item_permissions_check( $request ) {
     86                global $post;
     87
     88                $post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
     89
     90                if ( 0 < $post_id ) {
     91                        $post = get_post( $post_id );
     92
     93                        if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
     94                                return new WP_Error(
     95                                        'block_cannot_read',
     96                                        __( 'Sorry, you are not allowed to read blocks of this post' ),
     97                                        array(
     98                                                'status' => rest_authorization_required_code(),
     99                                        )
     100                                );
     101                        }
     102                } else {
     103                        if ( ! current_user_can( 'edit_posts' ) ) {
     104                                return new WP_Error(
     105                                        'block_cannot_read',
     106                                        __( 'Sorry, you are not allowed to read blocks as this user.' ),
     107                                        array(
     108                                                'status' => rest_authorization_required_code(),
     109                                        )
     110                                );
     111                        }
     112                }
     113
     114                return true;
     115        }
     116
     117        /**
     118         * Returns block output from block's registered render_callback.
     119         *
     120         * @since 5.0.0
     121         *
     122         * @param WP_REST_Request $request Full details about the request.
     123         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     124         */
     125        public function get_item( $request ) {
     126                global $post;
     127
     128                $post_id = isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0;
     129
     130                if ( 0 < $post_id ) {
     131                        $post = get_post( $post_id );
     132
     133                        // Set up postdata since this will be needed if post_id was set.
     134                        setup_postdata( $post );
     135                }
     136                $registry = WP_Block_Type_Registry::get_instance();
     137                $block    = $registry->get_registered( $request['name'] );
     138
     139                if ( null === $block ) {
     140                        return new WP_Error(
     141                                'block_invalid',
     142                                __( 'Invalid block.' ),
     143                                array(
     144                                        'status' => 404,
     145                                )
     146                        );
     147                }
     148
     149                $data = array(
     150                        'rendered' => $block->render( $request->get_param( 'attributes' ) ),
     151                );
     152                return rest_ensure_response( $data );
     153        }
     154
     155        /**
     156         * Retrieves block's output schema, conforming to JSON Schema.
     157         *
     158         * @since 5.0.0
     159         *
     160         * @return array Item schema data.
     161         */
     162        public function get_item_schema() {
     163                return array(
     164                        '$schema'    => 'http://json-schema.org/schema#',
     165                        'title'      => 'rendered-block',
     166                        'type'       => 'object',
     167                        'properties' => array(
     168                                'rendered' => array(
     169                                        'description' => __( 'The rendered block.' ),
     170                                        'type'        => 'string',
     171                                        'required'    => true,
     172                                        'context'     => array( 'edit' ),
     173                                ),
     174                        ),
     175                );
     176        }
     177}
  • src/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php

     
     1<?php
     2/**
     3 * Reusable blocks REST API: WP_REST_Blocks_Controller class
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Controller which provides a REST endpoint for the editor to read, create,
     12 * edit and delete reusable blocks. Blocks are stored as posts with the wp_block
     13 * post type.
     14 *
     15 * @since 5.0.0
     16 *
     17 * @see WP_REST_Posts_Controller
     18 * @see WP_REST_Controller
     19 */
     20class WP_REST_Blocks_Controller extends WP_REST_Posts_Controller {
     21        /**
     22         * Checks if a block can be read.
     23         *
     24         * @since 5.0.0
     25         *
     26         * @param object $post Post object that backs the block.
     27         * @return bool Whether the block can be read.
     28         */
     29        public function check_read_permission( $post ) {
     30                // Ensure that the user is logged in and has the read_blocks capability.
     31                $post_type = get_post_type_object( $post->post_type );
     32                if ( ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
     33                        return false;
     34                }
     35
     36                return parent::check_read_permission( $post );
     37        }
     38
     39        /**
     40         * Handle a DELETE request.
     41         *
     42         * @since 5.0.0
     43         *
     44         * @param WP_REST_Request $request Full details about the request.
     45         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     46         */
     47        public function delete_item( $request ) {
     48                // Always hard-delete a block.
     49                $request->set_param( 'force', true );
     50
     51                return parent::delete_item( $request );
     52        }
     53
     54        /**
     55         * Given an update or create request, build the post object that is saved to
     56         * the database.
     57         *
     58         * @since 5.0.0
     59         *
     60         * @param WP_REST_Request $request Request object.
     61         * @return stdClass|WP_Error Post object or WP_Error.
     62         */
     63        public function prepare_item_for_database( $request ) {
     64                $prepared_post = parent::prepare_item_for_database( $request );
     65
     66                // Force blocks to always be published.
     67                $prepared_post->post_status = 'publish';
     68
     69                return $prepared_post;
     70        }
     71
     72        /**
     73         * Given a block from the database, build the array that is returned from an
     74         * API response.
     75         *
     76         * @since 5.0.0
     77         *
     78         * @param WP_Post         $post    Post object that backs the block.
     79         * @param WP_REST_Request $request Request object.
     80         * @return WP_REST_Response Response object.
     81         */
     82        public function prepare_item_for_response( $post, $request ) {
     83                $data = array(
     84                        'id'      => $post->ID,
     85                        'title'   => $post->post_title,
     86                        'content' => $post->post_content,
     87                );
     88
     89                $response = rest_ensure_response( $data );
     90
     91                return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
     92        }
     93
     94        /**
     95         * Builds the block's schema, conforming to JSON Schema.
     96         *
     97         * @since 5.0.0
     98         *
     99         * @return array Item schema data.
     100         */
     101        public function get_item_schema() {
     102                return array(
     103                        '$schema'    => 'http://json-schema.org/schema#',
     104                        'title'      => $this->post_type,
     105                        'type'       => 'object',
     106                        'properties' => array(
     107                                'id'      => array(
     108                                        'description' => __( 'Unique identifier for the block.' ),
     109                                        'type'        => 'integer',
     110                                        'readonly'    => true,
     111                                ),
     112                                'title'   => array(
     113                                        'description' => __( 'The block’s title.' ),
     114                                        'type'        => 'string',
     115                                        'required'    => true,
     116                                ),
     117                                'content' => array(
     118                                        'description' => __( 'The block’s HTML content.' ),
     119                                        'type'        => 'string',
     120                                        'required'    => true,
     121                                ),
     122                        ),
     123                );
     124        }
     125}
  • src/wp-includes/rest-api.php

     
    233233        // Settings.
    234234        $controller = new WP_REST_Settings_Controller;
    235235        $controller->register_routes();
     236
     237        // Blocks.
     238        $controller = new WP_REST_Block_Renderer_Controller;
     239        $controller->register_routes();
    236240}
    237241
    238242/**
  • src/wp-settings.php

     
    235235require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
    236236require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' );
    237237require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' );
     238require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-blocks-controller.php' );
     239require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-block-renderer-controller.php' );
    238240require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' );
    239241require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php' );
    240242require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' );
  • tests/phpunit/tests/rest-api/rest-block-renderer-controller.php

     
     1<?php
     2/**
     3 * WP_REST_Block_Renderer_Controller tests.
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Tests for WP_REST_Block_Renderer_Controller.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @covers WP_REST_Block_Renderer_Controller
     16 *
     17 * @group restapi-blocks
     18 * @group restapi
     19 */
     20class REST_Block_Renderer_Controller_Test extends WP_Test_REST_Controller_Testcase {
     21        /**
     22         * The REST API route for the block renderer.
     23         *
     24         * @since 5.0.0
     25         *
     26         * @var string
     27         */
     28        protected static $rest_api_route = '/wp/v2/block-renderer/';
     29
     30        /**
     31         * Test block's name.
     32         *
     33         * @since 5.0.0
     34         *
     35         * @var string
     36         */
     37        protected static $block_name = 'core/test-block';
     38
     39        /**
     40         * Test post context block's name.
     41         *
     42         * @since 5.0.0
     43         *
     44         * @var string
     45         */
     46        protected static $context_block_name = 'core/context-test-block';
     47
     48        /**
     49         * Test API user's ID.
     50         *
     51         * @since 5.0.0
     52         *
     53         * @var int
     54         */
     55        protected static $user_id;
     56
     57        /**
     58         * Test post ID.
     59         *
     60         * @since 5.0.0
     61         *
     62         * @var int
     63         */
     64        protected static $post_id;
     65
     66        /**
     67         * Author test user ID.
     68         *
     69         * @since 5.0.0
     70         *
     71         * @var int
     72         */
     73        protected static $author_id;
     74
     75        /**
     76         * Create test data before the tests run.
     77         *
     78         * @since 5.0.0
     79         *
     80         * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
     81         */
     82        public static function wpSetUpBeforeClass( $factory ) {
     83                self::$user_id = $factory->user->create(
     84                        array(
     85                                'role' => 'editor',
     86                        )
     87                );
     88
     89                self::$author_id = $factory->user->create(
     90                        array(
     91                                'role' => 'author',
     92                        )
     93                );
     94
     95                self::$post_id = $factory->post->create(
     96                        array(
     97                                'post_title' => 'Test Post',
     98                        )
     99                );
     100        }
     101
     102        /**
     103         * Delete test data after our tests run.
     104         *
     105         * @since 5.0.0
     106         */
     107        public static function wpTearDownAfterClass() {
     108                self::delete_user( self::$user_id );
     109        }
     110
     111        /**
     112         * Set up each test method.
     113         *
     114         * @since 5.0.0
     115         */
     116        public function setUp() {
     117                $this->register_test_block();
     118                $this->register_post_context_test_block();
     119                parent::setUp();
     120        }
     121
     122        /**
     123         * Tear down each test method.
     124         *
     125         * @since 5.0.0
     126         */
     127        public function tearDown() {
     128                WP_Block_Type_Registry::get_instance()->unregister( self::$block_name );
     129                WP_Block_Type_Registry::get_instance()->unregister( self::$context_block_name );
     130                parent::tearDown();
     131        }
     132
     133        /**
     134         * Register test block.
     135         *
     136         * @since 5.0.0
     137         */
     138        public function register_test_block() {
     139                register_block_type(
     140                        self::$block_name,
     141                        array(
     142                                'attributes'      => array(
     143                                        'some_string' => array(
     144                                                'type'    => 'string',
     145                                                'default' => 'some_default',
     146                                        ),
     147                                        'some_int'    => array(
     148                                                'type' => 'integer',
     149                                        ),
     150                                        'some_array'  => array(
     151                                                'type'  => 'array',
     152                                                'items' => array(
     153                                                        'type' => 'integer',
     154                                                ),
     155                                        ),
     156                                ),
     157                                'render_callback' => array( $this, 'render_test_block' ),
     158                        )
     159                );
     160        }
     161
     162        /**
     163         * Register test block with post_id as attribute for post context test.
     164         *
     165         * @since 5.0.0
     166         */
     167        public function register_post_context_test_block() {
     168                register_block_type(
     169                        self::$context_block_name,
     170                        array(
     171                                'attributes'      => array(),
     172                                'render_callback' => array( $this, 'render_post_context_test_block' ),
     173                        )
     174                );
     175        }
     176
     177        /**
     178         * Test render callback.
     179         *
     180         * @since 5.0.0
     181         *
     182         * @param array $attributes Props.
     183         * @return string Rendered attributes, which is here just JSON.
     184         */
     185        public function render_test_block( $attributes ) {
     186                return wp_json_encode( $attributes );
     187        }
     188
     189        /**
     190         * Test render callback for testing post context.
     191         *
     192         * @since 5.0.0
     193         *
     194         * @return string
     195         */
     196        public function render_post_context_test_block() {
     197                return get_the_title();
     198        }
     199
     200        /**
     201         * Check that the route was registered properly.
     202         *
     203         * @ticket 45098
     204         *
     205         * @covers WP_REST_Block_Renderer_Controller::register_routes()
     206         */
     207        public function test_register_routes() {
     208                $dynamic_block_names = get_dynamic_block_names();
     209                $this->assertContains( self::$block_name, $dynamic_block_names );
     210
     211                $routes = rest_get_server()->get_routes();
     212                foreach ( $dynamic_block_names as $dynamic_block_name ) {
     213                        $this->assertArrayHasKey( self::$rest_api_route . "(?P<name>$dynamic_block_name)", $routes );
     214                }
     215        }
     216
     217        /**
     218         * Test getting item without permissions.
     219         *
     220         * @ticket 45098
     221         *
     222         * @covers WP_REST_Block_Renderer_Controller::get_item()
     223         */
     224        public function test_get_item_without_permissions() {
     225                wp_set_current_user( 0 );
     226
     227                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     228                $request->set_param( 'context', 'edit' );
     229
     230                $response = rest_get_server()->dispatch( $request );
     231
     232                $this->assertErrorResponse( 'block_cannot_read', $response, rest_authorization_required_code() );
     233        }
     234
     235        /**
     236         * Test getting item without 'edit' context.
     237         *
     238         * @ticket 45098
     239         */
     240        public function test_get_item_with_invalid_context() {
     241                wp_set_current_user( self::$user_id );
     242
     243                $request  = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     244                $response = rest_get_server()->dispatch( $request );
     245
     246                $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
     247        }
     248
     249        /**
     250         * Test getting item with invalid block name.
     251         *
     252         * @ticket 45098
     253         *
     254         * @covers WP_REST_Block_Renderer_Controller::get_item()
     255         */
     256        public function test_get_item_invalid_block_name() {
     257                wp_set_current_user( self::$user_id );
     258                $request = new WP_REST_Request( 'GET', self::$rest_api_route . 'core/123' );
     259
     260                $request->set_param( 'context', 'edit' );
     261                $response = rest_get_server()->dispatch( $request );
     262
     263                $this->assertErrorResponse( 'rest_no_route', $response, 404 );
     264        }
     265
     266        /**
     267         * Check getting item with an invalid param provided.
     268         *
     269         * @ticket 45098
     270         *
     271         * @covers WP_REST_Block_Renderer_Controller::get_item()
     272         */
     273        public function test_get_item_invalid_attribute() {
     274                wp_set_current_user( self::$user_id );
     275                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     276                $request->set_param( 'context', 'edit' );
     277                $request->set_param(
     278                        'attributes',
     279                        array(
     280                                'some_string' => array( 'no!' ),
     281                        )
     282                );
     283                $response = rest_get_server()->dispatch( $request );
     284                $this->assertEquals( 400, $response->get_status() );
     285        }
     286
     287        /**
     288         * Check getting item with an invalid param provided.
     289         *
     290         * @ticket 45098
     291         *
     292         * @covers WP_REST_Block_Renderer_Controller::get_item()
     293         */
     294        public function test_get_item_unrecognized_attribute() {
     295                wp_set_current_user( self::$user_id );
     296                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     297                $request->set_param( 'context', 'edit' );
     298                $request->set_param(
     299                        'attributes',
     300                        array(
     301                                'unrecognized' => 'yes',
     302                        )
     303                );
     304                $response = rest_get_server()->dispatch( $request );
     305                $this->assertEquals( 400, $response->get_status() );
     306        }
     307
     308        /**
     309         * Check getting item with default attributes provided.
     310         *
     311         * @ticket 45098
     312         *
     313         * @covers WP_REST_Block_Renderer_Controller::get_item()
     314         */
     315        public function test_get_item_default_attributes() {
     316                wp_set_current_user( self::$user_id );
     317
     318                $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name );
     319                $defaults   = array();
     320                foreach ( $block_type->attributes as $key => $attribute ) {
     321                        $defaults[ $key ] = isset( $attribute['default'] ) ? $attribute['default'] : null;
     322                }
     323
     324                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     325                $request->set_param( 'context', 'edit' );
     326                $request->set_param( 'attributes', array() );
     327                $response = rest_get_server()->dispatch( $request );
     328                $this->assertEquals( 200, $response->get_status() );
     329                $data = $response->get_data();
     330
     331                $this->assertEquals( $defaults, json_decode( $data['rendered'], true ) );
     332                $this->assertEquals(
     333                        json_decode( $block_type->render( $defaults ) ),
     334                        json_decode( $data['rendered'] )
     335                );
     336        }
     337
     338        /**
     339         * Check getting item with attributes provided.
     340         *
     341         * @ticket 45098
     342         *
     343         * @covers WP_REST_Block_Renderer_Controller::get_item()
     344         */
     345        public function test_get_item() {
     346                wp_set_current_user( self::$user_id );
     347
     348                $block_type = WP_Block_Type_Registry::get_instance()->get_registered( self::$block_name );
     349                $attributes = array(
     350                        'some_int'    => '123',
     351                        'some_string' => 'foo',
     352                        'some_array'  => array( 1, '2', 3 ),
     353                );
     354
     355                $expected_attributes               = $attributes;
     356                $expected_attributes['some_int']   = (int) $expected_attributes['some_int'];
     357                $expected_attributes['some_array'] = array_map( 'intval', $expected_attributes['some_array'] );
     358
     359                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     360                $request->set_param( 'context', 'edit' );
     361                $request->set_param( 'attributes', $attributes );
     362                $response = rest_get_server()->dispatch( $request );
     363                $this->assertEquals( 200, $response->get_status() );
     364                $data = $response->get_data();
     365
     366                $this->assertEquals( $expected_attributes, json_decode( $data['rendered'], true ) );
     367                $this->assertEquals(
     368                        json_decode( $block_type->render( $attributes ), true ),
     369                        json_decode( $data['rendered'], true )
     370                );
     371        }
     372
     373
     374
     375        /**
     376         * Check success response for getting item with layout attribute provided.
     377         *
     378         * @ticket 45098
     379         */
     380        public function test_get_item_with_layout() {
     381                wp_set_current_user( self::$user_id );
     382
     383                $attributes = array(
     384                        'layout' => 'foo',
     385                );
     386
     387                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$block_name );
     388                $request->set_param( 'context', 'edit' );
     389                $request->set_param( 'attributes', $attributes );
     390                $response = rest_get_server()->dispatch( $request );
     391                $this->assertEquals( 200, $response->get_status() );
     392        }
     393
     394        /**
     395         * Test getting item with post context.
     396         *
     397         * @ticket 45098
     398         */
     399        public function test_get_item_with_post_context() {
     400                wp_set_current_user( self::$user_id );
     401
     402                $expected_title = 'Test Post';
     403                $request        = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
     404                $request->set_param( 'context', 'edit' );
     405
     406                // Test without post ID.
     407                $response = rest_get_server()->dispatch( $request );
     408
     409                $this->assertEquals( 200, $response->get_status() );
     410                $data = $response->get_data();
     411
     412                $this->assertTrue( empty( $data['rendered'] ) );
     413
     414                // Now test with post ID.
     415                $request->set_param( 'post_id', self::$post_id );
     416                $response = rest_get_server()->dispatch( $request );
     417
     418                $this->assertEquals( 200, $response->get_status() );
     419                $data = $response->get_data();
     420
     421                $this->assertEquals( $expected_title, $data['rendered'] );
     422        }
     423
     424        /**
     425         * Test getting item with invalid post ID.
     426         *
     427         * @ticket 45098
     428         */
     429        public function test_get_item_without_permissions_invalid_post() {
     430                wp_set_current_user( self::$user_id );
     431
     432                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
     433                $request->set_param( 'context', 'edit' );
     434
     435                // Test with invalid post ID.
     436                $request->set_param( 'post_id', PHP_INT_MAX );
     437                $response = rest_get_server()->dispatch( $request );
     438
     439                $this->assertErrorResponse( 'block_cannot_read', $response, 403 );
     440        }
     441
     442        /**
     443         * Test getting item without permissions to edit context post.
     444         *
     445         * @ticket 45098
     446         */
     447        public function test_get_item_without_permissions_cannot_edit_post() {
     448                wp_set_current_user( self::$author_id );
     449
     450                $request = new WP_REST_Request( 'GET', self::$rest_api_route . self::$context_block_name );
     451                $request->set_param( 'context', 'edit' );
     452
     453                // Test with private post ID.
     454                $request->set_param( 'post_id', self::$post_id );
     455                $response = rest_get_server()->dispatch( $request );
     456
     457                $this->assertErrorResponse( 'block_cannot_read', $response, 403 );
     458        }
     459
     460        /**
     461         * Get item schema.
     462         *
     463         * @ticket 45098
     464         *
     465         * @covers WP_REST_Block_Renderer_Controller::get_item_schema()
     466         */
     467        public function test_get_item_schema() {
     468                $request  = new WP_REST_Request( 'OPTIONS', self::$rest_api_route . self::$block_name );
     469                $response = rest_get_server()->dispatch( $request );
     470                $data     = $response->get_data();
     471
     472                $this->assertEqualSets( array( 'GET' ), $data['endpoints'][0]['methods'] );
     473                $this->assertEqualSets(
     474                        array( 'name', 'context', 'attributes', 'post_id' ),
     475                        array_keys( $data['endpoints'][0]['args'] )
     476                );
     477                $this->assertEquals( 'object', $data['endpoints'][0]['args']['attributes']['type'] );
     478
     479                $this->assertArrayHasKey( 'schema', $data );
     480                $this->assertEquals( 'rendered-block', $data['schema']['title'] );
     481                $this->assertEquals( 'object', $data['schema']['type'] );
     482                $this->arrayHasKey( 'rendered', $data['schema']['properties'] );
     483                $this->arrayHasKey( 'string', $data['schema']['properties']['rendered']['type'] );
     484                $this->assertEquals( array( 'edit' ), $data['schema']['properties']['rendered']['context'] );
     485        }
     486
     487        /**
     488         * The update_item() method does not exist for block rendering.
     489         */
     490        public function test_update_item() {
     491                $this->markTestSkipped( 'Controller does not implement update_item().' );
     492        }
     493
     494        /**
     495         * The create_item() method does not exist for block rendering.
     496         */
     497        public function test_create_item() {
     498                $this->markTestSkipped( 'Controller does not implement create_item().' );
     499        }
     500
     501        /**
     502         * The delete_item() method does not exist for block rendering.
     503         */
     504        public function test_delete_item() {
     505                $this->markTestSkipped( 'Controller does not implement delete_item().' );
     506        }
     507
     508        /**
     509         * The get_items() method does not exist for block rendering.
     510         */
     511        public function test_get_items() {
     512                $this->markTestSkipped( 'Controller does not implement get_items().' );
     513        }
     514
     515        /**
     516         * The context_param() method does not exist for block rendering.
     517         */
     518        public function test_context_param() {
     519                $this->markTestSkipped( 'Controller does not implement context_param().' );
     520        }
     521
     522        /**
     523         * The prepare_item() method does not exist for block rendering.
     524         */
     525        public function test_prepare_item() {
     526                $this->markTestSkipped( 'Controller does not implement prepare_item().' );
     527        }
     528}
  • tests/phpunit/tests/rest-api/rest-blocks-controller.php

     
     1<?php
     2/**
     3 * WP_REST_Blocks_Controller tests
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Tests for WP_REST_Blocks_Controller.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see WP_Test_REST_Controller_Testcase
     16 *
     17 * @group restapi-blocks
     18 * @group restapi
     19 */
     20class REST_Blocks_Controller_Test extends WP_Test_REST_Controller_Testcase {
     21        /**
     22         * Our fake block's post ID.
     23         *
     24         * @since 5.0.0
     25         *
     26         * @var int
     27         */
     28        protected static $post_id;
     29
     30        /**
     31         * Our fake user's ID.
     32         *
     33         * @since 5.0.0
     34         *
     35         * @var int
     36         */
     37        protected static $user_id;
     38
     39        /**
     40         * Create fake data before our tests run.
     41         *
     42         * @since 5.0.0
     43         *
     44         * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
     45         */
     46        public static function wpSetUpBeforeClass( $factory ) {
     47                self::$post_id = wp_insert_post(
     48                        array(
     49                                'post_type'    => 'wp_block',
     50                                'post_status'  => 'publish',
     51                                'post_title'   => 'My cool block',
     52                                'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
     53                        )
     54                );
     55
     56                self::$user_id = $factory->user->create(
     57                        array(
     58                                'role' => 'editor',
     59                        )
     60                );
     61        }
     62
     63        /**
     64         * Delete our fake data after our tests run.
     65         *
     66         * @since 5.0.0
     67         */
     68        public static function wpTearDownAfterClass() {
     69                wp_delete_post( self::$post_id );
     70
     71                self::delete_user( self::$user_id );
     72        }
     73
     74        /**
     75         * Check that our routes get set up properly.
     76         *
     77         * @ticket 45098
     78         */
     79        public function test_register_routes() {
     80                $routes = rest_get_server()->get_routes();
     81
     82                $this->assertArrayHasKey( '/wp/v2/blocks', $routes );
     83                $this->assertCount( 2, $routes['/wp/v2/blocks'] );
     84                $this->assertArrayHasKey( '/wp/v2/blocks/(?P<id>[\d]+)', $routes );
     85                $this->assertCount( 3, $routes['/wp/v2/blocks/(?P<id>[\d]+)'] );
     86        }
     87
     88        /**
     89         * Check that we can GET a collection of blocks.
     90         *
     91         * @ticket 45098
     92         */
     93        public function test_get_items() {
     94                wp_set_current_user( self::$user_id );
     95
     96                $request  = new WP_REST_Request( 'GET', '/wp/v2/blocks' );
     97                $response = rest_get_server()->dispatch( $request );
     98
     99                $this->assertEquals( 200, $response->get_status() );
     100                $this->assertEquals(
     101                        array(
     102                                array(
     103                                        'id'      => self::$post_id,
     104                                        'title'   => 'My cool block',
     105                                        'content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
     106                                ),
     107                        ),
     108                        $response->get_data()
     109                );
     110        }
     111
     112        /**
     113         * Check that we can GET a single block.
     114         *
     115         * @ticket 45098
     116         */
     117        public function test_get_item() {
     118                wp_set_current_user( self::$user_id );
     119
     120                $request  = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
     121                $response = rest_get_server()->dispatch( $request );
     122
     123                $this->assertEquals( 200, $response->get_status() );
     124                $this->assertEquals(
     125                        array(
     126                                'id'      => self::$post_id,
     127                                'title'   => 'My cool block',
     128                                'content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
     129                        ),
     130                        $response->get_data()
     131                );
     132        }
     133
     134        /**
     135         * Check that we can POST to create a new block.
     136         *
     137         * @ticket 45098
     138         */
     139        public function test_create_item() {
     140                wp_set_current_user( self::$user_id );
     141
     142                $request = new WP_REST_Request( 'POST', '/wp/v2/blocks/' . self::$post_id );
     143                $request->set_body_params(
     144                        array(
     145                                'title'   => 'New cool block',
     146                                'content' => '<!-- wp:core/paragraph --><p>Wow!</p><!-- /wp:core/paragraph -->',
     147                        )
     148                );
     149
     150                $response = rest_get_server()->dispatch( $request );
     151
     152                $this->assertEquals( 200, $response->get_status() );
     153
     154                $data = $response->get_data();
     155
     156                $this->assertArrayHasKey( 'id', $data );
     157                $this->assertArrayHasKey( 'title', $data );
     158                $this->assertArrayHasKey( 'content', $data );
     159
     160                $this->assertEquals( self::$post_id, $data['id'] );
     161                $this->assertEquals( 'New cool block', $data['title'] );
     162                $this->assertEquals( '<!-- wp:core/paragraph --><p>Wow!</p><!-- /wp:core/paragraph -->', $data['content'] );
     163        }
     164
     165        /**
     166         * Check that we can PUT to update a block.
     167         *
     168         * @ticket 45098
     169         */
     170        public function test_update_item() {
     171                wp_set_current_user( self::$user_id );
     172
     173                $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
     174                $request->set_body_params(
     175                        array(
     176                                'title'   => 'Updated cool block',
     177                                'content' => '<!-- wp:core/paragraph --><p>Nice!</p><!-- /wp:core/paragraph -->',
     178                        )
     179                );
     180
     181                $response = rest_get_server()->dispatch( $request );
     182
     183                $this->assertEquals( 200, $response->get_status() );
     184
     185                $data = $response->get_data();
     186
     187                $this->assertArrayHasKey( 'id', $data );
     188                $this->assertArrayHasKey( 'title', $data );
     189                $this->assertArrayHasKey( 'content', $data );
     190
     191                $this->assertEquals( self::$post_id, $data['id'] );
     192                $this->assertEquals( 'Updated cool block', $data['title'] );
     193                $this->assertEquals( '<!-- wp:core/paragraph --><p>Nice!</p><!-- /wp:core/paragraph -->', $data['content'] );
     194        }
     195
     196        /**
     197         * Check that we can DELETE a block.
     198         *
     199         * @ticket 45098
     200         */
     201        public function test_delete_item() {
     202                wp_set_current_user( self::$user_id );
     203
     204                $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );
     205
     206                $response = rest_get_server()->dispatch( $request );
     207
     208                $this->assertEquals( 200, $response->get_status() );
     209
     210                $data = $response->get_data();
     211
     212                $this->assertArrayHasKey( 'deleted', $data );
     213                $this->assertArrayHasKey( 'previous', $data );
     214
     215                $this->assertTrue( $data['deleted'] );
     216
     217                $this->assertArrayHasKey( 'id', $data['previous'] );
     218                $this->assertArrayHasKey( 'title', $data['previous'] );
     219                $this->assertArrayHasKey( 'content', $data['previous'] );
     220
     221                $this->assertEquals( self::$post_id, $data['previous']['id'] );
     222                $this->assertEquals( 'My cool block', $data['previous']['title'] );
     223                $this->assertEquals( '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->', $data['previous']['content'] );
     224        }
     225
     226        /**
     227         * Check that we have defined a JSON schema.
     228         *
     229         * @ticket 45098
     230         */
     231        public function test_get_item_schema() {
     232                $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/blocks' );
     233                $response   = rest_get_server()->dispatch( $request );
     234                $data       = $response->get_data();
     235                $properties = $data['schema']['properties'];
     236
     237                $this->assertEquals( 3, count( $properties ) );
     238                $this->assertArrayHasKey( 'id', $properties );
     239                $this->assertArrayHasKey( 'title', $properties );
     240                $this->assertArrayHasKey( 'content', $properties );
     241        }
     242
     243        /**
     244         * Test cases for test_capabilities().
     245         *
     246         * @since 5.0.0
     247         */
     248        public function data_capabilities() {
     249                return array(
     250                        array( 'create', 'editor', 201 ),
     251                        array( 'create', 'author', 201 ),
     252                        array( 'create', 'contributor', 403 ),
     253                        array( 'create', null, 401 ),
     254
     255                        array( 'read', 'editor', 200 ),
     256                        array( 'read', 'author', 200 ),
     257                        array( 'read', 'contributor', 200 ),
     258                        array( 'read', null, 401 ),
     259
     260                        array( 'update_delete_own', 'editor', 200 ),
     261                        array( 'update_delete_own', 'author', 200 ),
     262                        array( 'update_delete_own', 'contributor', 403 ),
     263
     264                        array( 'update_delete_others', 'editor', 200 ),
     265                        array( 'update_delete_others', 'author', 403 ),
     266                        array( 'update_delete_others', 'contributor', 403 ),
     267                        array( 'update_delete_others', null, 401 ),
     268                );
     269        }
     270
     271        /**
     272         * Exhaustively check that each role either can or cannot create, edit,
     273         * update, and delete reusable blocks.
     274         *
     275         * @ticket 45098
     276         *
     277         * @dataProvider data_capabilities
     278         *
     279         * @param string $action          Action to perform in the test.
     280         * @param string $role            User role to test.
     281         * @param int    $expected_status Expected HTTP response status.
     282         */
     283        public function test_capabilities( $action, $role, $expected_status ) {
     284                if ( $role ) {
     285                        $user_id = $this->factory->user->create( array( 'role' => $role ) );
     286                        wp_set_current_user( $user_id );
     287                } else {
     288                        wp_set_current_user( 0 );
     289                }
     290
     291                switch ( $action ) {
     292                        case 'create':
     293                                $request = new WP_REST_Request( 'POST', '/wp/v2/blocks' );
     294                                $request->set_body_params(
     295                                        array(
     296                                                'title'   => 'Test',
     297                                                'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
     298                                        )
     299                                );
     300
     301                                $response = rest_get_server()->dispatch( $request );
     302                                $this->assertEquals( $expected_status, $response->get_status() );
     303
     304                                break;
     305
     306                        case 'read':
     307                                $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
     308
     309                                $response = rest_get_server()->dispatch( $request );
     310                                $this->assertEquals( $expected_status, $response->get_status() );
     311
     312                                break;
     313
     314                        case 'update_delete_own':
     315                                $post_id = wp_insert_post(
     316                                        array(
     317                                                'post_type'    => 'wp_block',
     318                                                'post_status'  => 'publish',
     319                                                'post_title'   => 'My cool block',
     320                                                'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
     321                                                'post_author'  => $user_id,
     322                                        )
     323                                );
     324
     325                                $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . $post_id );
     326                                $request->set_body_params(
     327                                        array(
     328                                                'title'   => 'Test',
     329                                                'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
     330                                        )
     331                                );
     332
     333                                $response = rest_get_server()->dispatch( $request );
     334                                $this->assertEquals( $expected_status, $response->get_status() );
     335
     336                                $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . $post_id );
     337
     338                                $response = rest_get_server()->dispatch( $request );
     339                                $this->assertEquals( $expected_status, $response->get_status() );
     340
     341                                wp_delete_post( $post_id );
     342
     343                                break;
     344
     345                        case 'update_delete_others':
     346                                $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
     347                                $request->set_body_params(
     348                                        array(
     349                                                'title'   => 'Test',
     350                                                'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
     351                                        )
     352                                );
     353
     354                                $response = rest_get_server()->dispatch( $request );
     355                                $this->assertEquals( $expected_status, $response->get_status() );
     356
     357                                $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );
     358
     359                                $response = rest_get_server()->dispatch( $request );
     360                                $this->assertEquals( $expected_status, $response->get_status() );
     361
     362                                break;
     363
     364                        default:
     365                                $this->fail( "'$action' is not a valid action." );
     366                }
     367
     368                if ( isset( $user_id ) ) {
     369                        self::delete_user( $user_id );
     370                }
     371        }
     372
     373        /**
     374         * The context_param() method does not exist for blocks.
     375         */
     376        public function test_context_param() {
     377                $this->markTestSkipped( 'Controller does not implement get_context_param().' );
     378        }
     379
     380        /**
     381         * The prepare_item() method does not exist for blocks.
     382         */
     383        public function test_prepare_item() {
     384                $this->markTestSkipped( 'Controller does not implement prepare_item().' );
     385        }
     386}
  • tests/qunit/fixtures/wp-api-generated.js

     
    35253525            "_links": {
    35263526                "self": "http://example.org/index.php?rest_route=/wp/v2/settings"
    35273527            }
     3528        },
     3529        "/wp/v2/block-renderer/(?P<name>core/block)": {
     3530            "namespace": "wp/v2",
     3531            "methods": [
     3532                "GET"
     3533            ],
     3534            "endpoints": [
     3535                {
     3536                    "methods": [
     3537                        "GET"
     3538                    ],
     3539                    "args": {
     3540                        "name": {
     3541                            "required": false,
     3542                            "description": "Unique registered name for the block.",
     3543                            "type": "string"
     3544                        },
     3545                        "context": {
     3546                            "required": false,
     3547                            "default": "view",
     3548                            "enum": [
     3549                                "edit"
     3550                            ],
     3551                            "description": "Scope under which the request is made; determines fields present in response.",
     3552                            "type": "string"
     3553                        },
     3554                        "attributes": {
     3555                            "required": false,
     3556                            "description": "Attributes for core/block block",
     3557                            "type": "object"
     3558                        },
     3559                        "post_id": {
     3560                            "required": false,
     3561                            "description": "ID of the post context.",
     3562                            "type": "integer"
     3563                        }
     3564                    }
     3565                }
     3566            ]
     3567        },
     3568        "/wp/v2/block-renderer/(?P<name>core/latest-comments)": {
     3569            "namespace": "wp/v2",
     3570            "methods": [
     3571                "GET"
     3572            ],
     3573            "endpoints": [
     3574                {
     3575                    "methods": [
     3576                        "GET"
     3577                    ],
     3578                    "args": {
     3579                        "name": {
     3580                            "required": false,
     3581                            "description": "Unique registered name for the block.",
     3582                            "type": "string"
     3583                        },
     3584                        "context": {
     3585                            "required": false,
     3586                            "default": "view",
     3587                            "enum": [
     3588                                "edit"
     3589                            ],
     3590                            "description": "Scope under which the request is made; determines fields present in response.",
     3591                            "type": "string"
     3592                        },
     3593                        "attributes": {
     3594                            "required": false,
     3595                            "description": "Attributes for core/latest-comments block",
     3596                            "type": "object"
     3597                        },
     3598                        "post_id": {
     3599                            "required": false,
     3600                            "description": "ID of the post context.",
     3601                            "type": "integer"
     3602                        }
     3603                    }
     3604                }
     3605            ]
    35283606        }
    35293607    }
    35303608};