| | 1 | <?php |
| | 2 | /** |
| | 3 | * REST API: WP_REST_Block_Types_Controller class |
| | 4 | * |
| | 5 | * @since 5.4.0 |
| | 6 | * @subpackage REST_API |
| | 7 | * @package WordPress |
| | 8 | */ |
| | 9 | |
| | 10 | /** |
| | 11 | * Core class used to access block types via the REST API. |
| | 12 | * |
| | 13 | * @since 5.4.0 |
| | 14 | * |
| | 15 | * @see WP_REST_Controller |
| | 16 | */ |
| | 17 | class WP_REST_Block_Types_Controller extends WP_REST_Controller { |
| | 18 | |
| | 19 | /** |
| | 20 | * Constructor. |
| | 21 | * |
| | 22 | * @since 5.4.0 |
| | 23 | */ |
| | 24 | public function __construct() { |
| | 25 | $this->namespace = 'wp/v2'; |
| | 26 | $this->rest_base = 'block-types'; |
| | 27 | } |
| | 28 | |
| | 29 | /** |
| | 30 | * Registers the routes for the objects of the controller. |
| | 31 | * |
| | 32 | * @since 5.4.0 |
| | 33 | * |
| | 34 | * @see register_rest_route() |
| | 35 | */ |
| | 36 | public function register_routes() { |
| | 37 | |
| | 38 | register_rest_route( |
| | 39 | $this->namespace, |
| | 40 | '/' . $this->rest_base, |
| | 41 | array( |
| | 42 | array( |
| | 43 | 'methods' => WP_REST_Server::READABLE, |
| | 44 | 'callback' => array( $this, 'get_items' ), |
| | 45 | 'permission_callback' => array( $this, 'get_items_permissions_check' ), |
| | 46 | 'args' => $this->get_collection_params(), |
| | 47 | ), |
| | 48 | 'schema' => array( $this, 'get_public_item_schema' ), |
| | 49 | ) |
| | 50 | ); |
| | 51 | |
| | 52 | $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); |
| | 53 | |
| | 54 | foreach ( $block_types as $block_type ) { |
| | 55 | |
| | 56 | register_rest_route( |
| | 57 | $this->namespace, |
| | 58 | '/' . $this->rest_base . '/(?P<block_type>' . $block_type->name . ')', |
| | 59 | array( |
| | 60 | 'args' => array( |
| | 61 | 'block_type' => array( |
| | 62 | 'description' => __( '' ), |
| | 63 | 'type' => 'string', |
| | 64 | ), |
| | 65 | ), |
| | 66 | array( |
| | 67 | 'methods' => WP_REST_Server::READABLE, |
| | 68 | 'callback' => array( $this, 'get_item' ), |
| | 69 | 'permission_callback' => array( $this, 'get_item_permissions_check' ), |
| | 70 | 'args' => array( |
| | 71 | 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
| | 72 | ), |
| | 73 | ), |
| | 74 | 'schema' => array( $this, 'get_public_item_schema' ), |
| | 75 | ) |
| | 76 | ); |
| | 77 | } |
| | 78 | } |
| | 79 | |
| | 80 | /** |
| | 81 | * Checks whether a given request has permission to read post block types. |
| | 82 | * |
| | 83 | * @param WP_REST_Request $request Full details about the request. |
| | 84 | * |
| | 85 | * @since 5.4.0 |
| | 86 | * |
| | 87 | * @return WP_Error|bool True if the request has read access, WP_Error object otherwise. |
| | 88 | */ |
| | 89 | public function get_items_permissions_check( $request) { |
| | 90 | if ( ! current_user_can( 'edit_posts' ) ) { |
| | 91 | return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to manage block types.' ), array( 'status' => rest_authorization_required_code() ) ); |
| | 92 | } |
| | 93 | |
| | 94 | return true; |
| | 95 | } |
| | 96 | |
| | 97 | /** |
| | 98 | * Retrieves all post block types, depending on user context. |
| | 99 | * |
| | 100 | * @param WP_REST_Request $request Full details about the request. |
| | 101 | * |
| | 102 | * @since 5.4.0 |
| | 103 | * |
| | 104 | * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. |
| | 105 | */ |
| | 106 | public function get_items( $request ) { |
| | 107 | $data = array(); |
| | 108 | $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); |
| | 109 | |
| | 110 | // Retrieve the list of registered collection query parameters. |
| | 111 | $registered = $this->get_collection_params(); |
| | 112 | $namespace = ''; |
| | 113 | if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) { |
| | 114 | $namespace = $request['namespace']; |
| | 115 | } |
| | 116 | |
| | 117 | foreach ( $block_types as $slug => $obj ) { |
| | 118 | $ret = $this->check_read_permission( $obj ); |
| | 119 | |
| | 120 | if ( ! $ret ) { |
| | 121 | continue; |
| | 122 | } |
| | 123 | |
| | 124 | $block_type = $this->prepare_item_for_response( $obj, $request ); |
| | 125 | |
| | 126 | if ( $namespace ){ |
| | 127 | $pieces = explode('/', $obj->name); |
| | 128 | $block_namespace = array_shift($pieces); |
| | 129 | if( $namespace !== $block_namespace ){ |
| | 130 | continue; |
| | 131 | } |
| | 132 | } |
| | 133 | |
| | 134 | $data[ $obj->name ] = $this->prepare_response_for_collection( $block_type ); |
| | 135 | } |
| | 136 | |
| | 137 | return rest_ensure_response( $data ); |
| | 138 | } |
| | 139 | |
| | 140 | /** |
| | 141 | * Checks if a given request has access to read a block type. |
| | 142 | * |
| | 143 | * @param WP_REST_Request $request Full details about the request. |
| | 144 | * |
| | 145 | * @since 5.4.0 |
| | 146 | * |
| | 147 | * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise. |
| | 148 | */ |
| | 149 | public function get_item_permissions_check( $request ) { |
| | 150 | $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $request['block_type'] ); |
| | 151 | |
| | 152 | if ( empty( $block_type ) ) { |
| | 153 | return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) ); |
| | 154 | } |
| | 155 | |
| | 156 | $check = $this->check_read_permission( $block_type ); |
| | 157 | |
| | 158 | if ( ! $check ) { |
| | 159 | return new WP_Error( 'rest_cannot_read_block_type', __( 'Cannot view block type.' ), array( 'status' => rest_authorization_required_code() ) ); |
| | 160 | } |
| | 161 | |
| | 162 | return true; |
| | 163 | } |
| | 164 | |
| | 165 | /** |
| | 166 | * Checks whether a given block type should be visible. |
| | 167 | * |
| | 168 | * @param object $block_type block type. |
| | 169 | * |
| | 170 | * @since 5.4.0 |
| | 171 | * |
| | 172 | * @return bool True if the block type is visible, otherwise false. |
| | 173 | */ |
| | 174 | protected function check_read_permission( $block_type ) { |
| | 175 | if ( ! current_user_can( 'edit_posts' ) ) { |
| | 176 | return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to manage block types.' ), array( 'status' => rest_authorization_required_code() ) ); |
| | 177 | } |
| | 178 | |
| | 179 | return true; |
| | 180 | } |
| | 181 | |
| | 182 | /** |
| | 183 | * Retrieves a specific block type. |
| | 184 | * |
| | 185 | * @param WP_REST_Request $request Full details about the request. |
| | 186 | * |
| | 187 | * @since 5.4.0 |
| | 188 | * |
| | 189 | * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. |
| | 190 | */ |
| | 191 | public function get_item( $request ) { |
| | 192 | $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $request['block_type'] ); |
| | 193 | |
| | 194 | if ( empty( $block_type ) ) { |
| | 195 | return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) ); |
| | 196 | } |
| | 197 | |
| | 198 | $data = $this->prepare_item_for_response( $block_type, $request ); |
| | 199 | |
| | 200 | return rest_ensure_response( $data ); |
| | 201 | } |
| | 202 | |
| | 203 | /** |
| | 204 | * Prepares a block type object for serialization. |
| | 205 | * |
| | 206 | * @param stdClass $block_type block type data. |
| | 207 | * @param WP_REST_Request $request Full details about the request. |
| | 208 | * |
| | 209 | * @since 5.4.0 |
| | 210 | * |
| | 211 | * @return WP_REST_Response block type data. |
| | 212 | */ |
| | 213 | public function prepare_item_for_response( $block_type, $request ) { |
| | 214 | |
| | 215 | $fields = $this->get_fields_for_response( $request ); |
| | 216 | $data = array(); |
| | 217 | if ( in_array( 'name', $fields, true ) ) { |
| | 218 | $data['name'] = $block_type->name; |
| | 219 | } |
| | 220 | |
| | 221 | if ( in_array( 'attributes', $fields, true ) ) { |
| | 222 | $data['attributes'] = $block_type->get_attributes(); |
| | 223 | } |
| | 224 | |
| | 225 | if ( in_array( 'is_dynamic', $fields, true ) ) { |
| | 226 | $data['is_dynamic'] = $block_type->is_dynamic(); |
| | 227 | } |
| | 228 | |
| | 229 | $extra_fields = array( 'editor_script', 'script', 'editor_style', 'style' ); |
| | 230 | foreach ( $extra_fields as $extra_field ) { |
| | 231 | if ( in_array( $extra_field, $fields, true ) ) { |
| | 232 | $data[ $extra_field ] = (string) $block_type->$extra_field; |
| | 233 | } |
| | 234 | } |
| | 235 | |
| | 236 | |
| | 237 | $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
| | 238 | $data = $this->add_additional_fields_to_object( $data, $request ); |
| | 239 | $data = $this->filter_response_by_context( $data, $context ); |
| | 240 | |
| | 241 | $response = rest_ensure_response( $data ); |
| | 242 | |
| | 243 | $response->add_links( |
| | 244 | array( |
| | 245 | 'collection' => array( |
| | 246 | 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), |
| | 247 | ), |
| | 248 | 'https://api.w.org/items' => array( |
| | 249 | 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $block_type->name ) ), |
| | 250 | ), |
| | 251 | ) |
| | 252 | ); |
| | 253 | |
| | 254 | |
| | 255 | /** |
| | 256 | * Filters a block type returned from the REST API. |
| | 257 | * |
| | 258 | * Allows modification of the block type data right before it is returned. |
| | 259 | * |
| | 260 | * @param WP_REST_Response $response The response object. |
| | 261 | * @param object $block_type The original block type object. |
| | 262 | * @param WP_REST_Request $request Request used to generate the response. |
| | 263 | * |
| | 264 | * @since 5.4.0 |
| | 265 | * |
| | 266 | */ |
| | 267 | return apply_filters( 'rest_prepare_block_type', $response, $block_type, $request ); |
| | 268 | } |
| | 269 | |
| | 270 | /** |
| | 271 | * Retrieves the block type' schema, conforming to JSON Schema. |
| | 272 | * |
| | 273 | * @since 5.4.0 |
| | 274 | * |
| | 275 | * @return array Item schema data. |
| | 276 | */ |
| | 277 | public function get_item_schema() { |
| | 278 | if ( $this->schema ) { |
| | 279 | return $this->add_additional_fields_schema( $this->schema ); |
| | 280 | } |
| | 281 | |
| | 282 | $schema = array( |
| | 283 | '$schema' => 'http://json-schema.org/draft-04/schema#', |
| | 284 | 'title' => 'block_type', |
| | 285 | 'type' => 'object', |
| | 286 | 'properties' => array( |
| | 287 | 'name' => array( |
| | 288 | 'description' => __( 'The title for the block type.' ), |
| | 289 | 'type' => 'string', |
| | 290 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 291 | 'readonly' => true, |
| | 292 | ), |
| | 293 | 'attributes' => array( |
| | 294 | 'description' => __( '' ), |
| | 295 | 'type' => 'object', |
| | 296 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 297 | 'readonly' => true, |
| | 298 | ), |
| | 299 | 'is_dynamic' => array( |
| | 300 | 'description' => __( '' ), |
| | 301 | 'type' => 'boolean', |
| | 302 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 303 | 'readonly' => true, |
| | 304 | ), |
| | 305 | 'editor_script' => array( |
| | 306 | 'description' => __( '' ), |
| | 307 | 'type' => 'string', |
| | 308 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 309 | 'readonly' => true, |
| | 310 | ), |
| | 311 | 'script' => array( |
| | 312 | 'description' => __( '' ), |
| | 313 | 'type' => 'string', |
| | 314 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 315 | 'readonly' => true, |
| | 316 | ), |
| | 317 | 'editor_style' => array( |
| | 318 | 'description' => __( '' ), |
| | 319 | 'type' => 'string', |
| | 320 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 321 | 'readonly' => true, |
| | 322 | ), |
| | 323 | 'style' => array( |
| | 324 | 'description' => __( '' ), |
| | 325 | 'type' => 'string', |
| | 326 | 'context' => array( 'embed', 'view', 'edit' ), |
| | 327 | 'readonly' => true, |
| | 328 | ), |
| | 329 | ), |
| | 330 | ); |
| | 331 | |
| | 332 | $this->schema = $schema; |
| | 333 | |
| | 334 | return $this->add_additional_fields_schema( $this->schema ); |
| | 335 | } |
| | 336 | |
| | 337 | /** |
| | 338 | * Retrieves the query params for collections. |
| | 339 | * |
| | 340 | * @since 5.4.0 |
| | 341 | * |
| | 342 | * @return array Collection parameters. |
| | 343 | */ |
| | 344 | public function get_collection_params() { |
| | 345 | $new_params = array(); |
| | 346 | $new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); |
| | 347 | $new_params['namespace'] = array( |
| | 348 | 'description' => __( '' ), |
| | 349 | 'type' => 'string', |
| | 350 | ); |
| | 351 | return $new_params; |
| | 352 | } |
| | 353 | |
| | 354 | } |