Make WordPress Core

Ticket #45098: 45098.diff

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