| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API: WP_REST_Block_Directory_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 5.5.0 8 */ 9 10 /** 11 * Controller which provides REST endpoint for the blocks. 12 * 13 * @since 5.5.0 14 * 15 * @see WP_REST_Controller 16 */ 17 class WP_REST_Block_Directory_Controller extends WP_REST_Controller { 18 19 /** 20 * Constructs the controller. 21 * 22 * @since 5.5.0 23 */ 24 public function __construct() { 25 $this->namespace = 'wp/v2'; 26 $this->rest_base = 'block-directory'; 27 } 28 29 /** 30 * Registers the necessary REST API routes. 31 * 32 * @since 5.5.0 33 */ 34 public function register_routes() { 35 register_rest_route( 36 $this->namespace, 37 '/' . $this->rest_base . '/search', 38 array( 39 array( 40 'methods' => WP_REST_Server::READABLE, 41 'callback' => array( $this, 'get_items' ), 42 'permission_callback' => array( $this, 'get_items_permissions_check' ), 43 'args' => $this->get_collection_params(), 44 ), 45 'schema' => array( $this, 'get_public_item_schema' ), 46 ) 47 ); 48 } 49 50 /** 51 * Checks whether a given request has permission to install and activate plugins. 52 * 53 * @since 5.5.0 54 * 55 * @param WP_REST_Request $request Full details about the request. 56 * @return true|WP_Error True if the request has permission, WP_Error object otherwise. 57 */ 58 public function get_items_permissions_check( $request ) { 59 if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) { 60 return new WP_Error( 61 'rest_block_directory_cannot_view', 62 __( 'Sorry, you are not allowed to browse the block directory.' ), 63 array( 'status' => rest_authorization_required_code() ) 64 ); 65 } 66 67 return true; 68 } 69 70 /** 71 * Search and retrieve blocks metadata 72 * 73 * @since 5.5.0 74 * 75 * @param WP_REST_Request $request Full details about the request. 76 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 77 */ 78 public function get_items( $request ) { 79 require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; 80 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 81 82 $response = plugins_api( 83 'query_plugins', 84 array( 85 'block' => $request['term'], 86 'per_page' => $request['per_page'], 87 'page' => $request['page'], 88 ) 89 ); 90 91 if ( is_wp_error( $response ) ) { 92 $response->add_data( array( 'status' => 500 ) ); 93 94 return $response; 95 } 96 97 $result = array(); 98 99 foreach ( $response->plugins as $plugin ) { 100 // If the API returned a plugin with empty data for 'blocks', skip it. 101 if ( empty( $plugin['blocks'] ) ) { 102 continue; 103 } 104 105 $data = $this->prepare_item_for_response( $plugin, $request ); 106 $result[] = $this->prepare_response_for_collection( $data ); 107 } 108 109 return rest_ensure_response( $result ); 110 } 111 112 /** 113 * Parse block metadata for a block, and prepare it for an API response. 114 * 115 * @since 5.5.0 116 * @since 5.9.0 Renamed `$plugin` to `$item` to match parent class for PHP 8 named parameter support. 117 * 118 * @param array $item The plugin metadata. 119 * @param WP_REST_Request $request Request object. 120 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 121 */ 122 public function prepare_item_for_response( $item, $request ) { 123 // Restores the more descriptive, specific name for use within this method. 124 $plugin = $item; 125 126 $fields = $this->get_fields_for_response( $request ); 127 128 // There might be multiple blocks in a plugin. Only the first block is mapped. 129 $block_data = reset( $plugin['blocks'] ); 130 131 // A data array containing the properties we'll return. 132 $block = array( 133 'name' => $block_data['name'], 134 'title' => ( $block_data['title'] ? $block_data['title'] : $plugin['name'] ), 135 'description' => wp_trim_words( $plugin['short_description'], 30, '...' ), 136 'id' => $plugin['slug'], 137 'rating' => $plugin['rating'] / 20, 138 'rating_count' => (int) $plugin['num_ratings'], 139 'active_installs' => (int) $plugin['active_installs'], 140 'author_block_rating' => $plugin['author_block_rating'] / 20, 141 'author_block_count' => (int) $plugin['author_block_count'], 142 'author' => wp_strip_all_tags( $plugin['author'] ), 143 'icon' => $plugin['icons']['1x'] ?? 'block-default', 144 'last_updated' => gmdate( 'Y-m-d\TH:i:s', strtotime( $plugin['last_updated'] ) ), 145 'humanized_updated' => sprintf( 146 /* translators: %s: Human-readable time difference. */ 147 __( '%s ago' ), 148 human_time_diff( strtotime( $plugin['last_updated'] ) ) 149 ), 150 ); 151 152 $this->add_additional_fields_to_object( $block, $request ); 153 154 $response = new WP_REST_Response( $block ); 155 156 if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { 157 $response->add_links( $this->prepare_links( $plugin ) ); 158 } 159 160 return $response; 161 } 162 163 /** 164 * Generates a list of links to include in the response for the plugin. 165 * 166 * @since 5.5.0 167 * 168 * @param array $plugin The plugin data from WordPress.org. 169 * @return array 170 */ 171 protected function prepare_links( $plugin ) { 172 $links = array( 173 'https://api.w.org/install-plugin' => array( 174 'href' => add_query_arg( 'slug', urlencode( $plugin['slug'] ), rest_url( 'wp/v2/plugins' ) ), 175 ), 176 ); 177 178 $plugin_file = $this->find_plugin_for_slug( $plugin['slug'] ); 179 180 if ( $plugin_file ) { 181 $links['https://api.w.org/plugin'] = array( 182 'href' => rest_url( 'wp/v2/plugins/' . substr( $plugin_file, 0, - 4 ) ), 183 'embeddable' => true, 184 ); 185 } 186 187 return $links; 188 } 189 190 /** 191 * Finds an installed plugin for the given slug. 192 * 193 * @since 5.5.0 194 * 195 * @param string $slug The WordPress.org directory slug for a plugin. 196 * @return string The plugin file found matching it. 197 */ 198 protected function find_plugin_for_slug( $slug ) { 199 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 200 201 $plugin_files = get_plugins( '/' . $slug ); 202 203 if ( ! $plugin_files ) { 204 return ''; 205 } 206 207 return $slug . '/' . array_key_first( $plugin_files ); 208 } 209 210 /** 211 * Retrieves the theme's schema, conforming to JSON Schema. 212 * 213 * @since 5.5.0 214 * 215 * @return array Item schema data. 216 */ 217 public function get_item_schema() { 218 if ( $this->schema ) { 219 return $this->add_additional_fields_schema( $this->schema ); 220 } 221 222 $this->schema = array( 223 '$schema' => 'http://json-schema.org/draft-04/schema#', 224 'title' => 'block-directory-item', 225 'type' => 'object', 226 'properties' => array( 227 'name' => array( 228 'description' => __( 'The block name, in namespace/block-name format.' ), 229 'type' => 'string', 230 'context' => array( 'view' ), 231 ), 232 'title' => array( 233 'description' => __( 'The block title, in human readable format.' ), 234 'type' => 'string', 235 'context' => array( 'view' ), 236 ), 237 'description' => array( 238 'description' => __( 'A short description of the block, in human readable format.' ), 239 'type' => 'string', 240 'context' => array( 'view' ), 241 ), 242 'id' => array( 243 'description' => __( 'The block slug.' ), 244 'type' => 'string', 245 'context' => array( 'view' ), 246 ), 247 'rating' => array( 248 'description' => __( 'The star rating of the block.' ), 249 'type' => 'number', 250 'context' => array( 'view' ), 251 ), 252 'rating_count' => array( 253 'description' => __( 'The number of ratings.' ), 254 'type' => 'integer', 255 'context' => array( 'view' ), 256 ), 257 'active_installs' => array( 258 'description' => __( 'The number sites that have activated this block.' ), 259 'type' => 'integer', 260 'context' => array( 'view' ), 261 ), 262 'author_block_rating' => array( 263 'description' => __( 'The average rating of blocks published by the same author.' ), 264 'type' => 'number', 265 'context' => array( 'view' ), 266 ), 267 'author_block_count' => array( 268 'description' => __( 'The number of blocks published by the same author.' ), 269 'type' => 'integer', 270 'context' => array( 'view' ), 271 ), 272 'author' => array( 273 'description' => __( 'The WordPress.org username of the block author.' ), 274 'type' => 'string', 275 'context' => array( 'view' ), 276 ), 277 'icon' => array( 278 'description' => __( 'The block icon.' ), 279 'type' => 'string', 280 'format' => 'uri', 281 'context' => array( 'view' ), 282 ), 283 'last_updated' => array( 284 'description' => __( 'The date when the block was last updated.' ), 285 'type' => 'string', 286 'format' => 'date-time', 287 'context' => array( 'view' ), 288 ), 289 'humanized_updated' => array( 290 'description' => __( 'The date when the block was last updated, in human readable format.' ), 291 'type' => 'string', 292 'context' => array( 'view' ), 293 ), 294 ), 295 ); 296 297 return $this->add_additional_fields_schema( $this->schema ); 298 } 299 300 /** 301 * Retrieves the search params for the blocks collection. 302 * 303 * @since 5.5.0 304 * 305 * @return array Collection parameters. 306 */ 307 public function get_collection_params() { 308 $query_params = parent::get_collection_params(); 309 310 $query_params['context']['default'] = 'view'; 311 312 $query_params['term'] = array( 313 'description' => __( 'Limit result set to blocks matching the search term.' ), 314 'type' => 'string', 315 'required' => true, 316 'minLength' => 1, 317 ); 318 319 unset( $query_params['search'] ); 320 321 /** 322 * Filters REST API collection parameters for the block directory controller. 323 * 324 * @since 5.5.0 325 * 326 * @param array $query_params JSON Schema-formatted collection parameters. 327 */ 328 return apply_filters( 'rest_block_directory_collection_params', $query_params ); 329 } 330 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sun Sep 27 08:20:30 2026 | Cross-referenced by PHPXref |