PluginProbe
Gutenberg / 8.5.1
Gutenberg v8.5.1
24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 All 403 releases
gutenberg / lib / class-wp-rest-block-directory-controller.php

class-wp-rest-block-directory-controller.php in Gutenberg 8.5.1, at lib/class-wp-rest-block-directory-controller.php

344 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Start: Include for phase 2
4 * Block Directory REST API: WP_REST_Block_Directory_Controller class
5 *
6 * @since 5.5.0
7 * @package gutenberg
8 */
9
10 /**
11 * Controller which provides REST endpoint for the blocks.
12 *
13 * This class can be removed when plugin support requires WordPress 5.5.0+.
14 *
15 * @since 5.5.0
16 *
17 * @see WP_REST_Controller
18 */
19 class WP_REST_Block_Directory_Controller extends WP_REST_Controller {
20
21 /**
22 * Constructs the controller.
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 public function register_routes() {
33 register_rest_route(
34 $this->namespace,
35 '/' . $this->rest_base . '/search',
36 array(
37 array(
38 'methods' => WP_REST_Server::READABLE,
39 'callback' => array( $this, 'get_items' ),
40 'permission_callback' => array( $this, 'get_items_permissions_check' ),
41 'args' => $this->get_collection_params(),
42 ),
43 'schema' => array( $this, 'get_public_item_schema' ),
44 )
45 );
46 }
47
48 /**
49 * Checks whether a given request has permission to install and activate plugins.
50 *
51 * @since 5.5.0
52 *
53 * @param WP_REST_Request $request Full details about the request.
54 *
55 * @return WP_Error|bool True if the request has permission, WP_Error object otherwise.
56 */
57 public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
58 if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
59 return new WP_Error(
60 'rest_block_directory_cannot_view',
61 __( 'Sorry, you are not allowed to browse the block directory.', 'gutenberg' ),
62 array( 'status' => rest_authorization_required_code() )
63 );
64 }
65
66 return true;
67 }
68
69 /**
70 * Search and retrieve blocks metadata
71 *
72 * @since 5.5.0
73 *
74 * @param WP_REST_Request $request Full details about the request.
75 *
76 * @return WP_Error|WP_REST_Response 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 $data = $this->prepare_item_for_response( $plugin, $request );
101 $result[] = $this->prepare_response_for_collection( $data );
102 }
103
104 return rest_ensure_response( $result );
105 }
106
107 /**
108 * Parse block metadata for a block, and prepare it for an API repsonse.
109 *
110 * @since 5.5.0
111 *
112 * @param array $plugin The plugin metadata.
113 * @param WP_REST_Request $request Request object.
114 *
115 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
116 */
117 public function prepare_item_for_response( $plugin, $request ) {
118 // There might be multiple blocks in a plugin. Only the first block is mapped.
119 $block_data = reset( $plugin['blocks'] );
120
121 // A data array containing the properties we'll return.
122 $block = array(
123 'name' => $block_data['name'],
124 'title' => ( $block_data['title'] ? $block_data['title'] : $plugin['name'] ),
125 'description' => wp_trim_words( $plugin['description'], 30, '...' ),
126 'id' => $plugin['slug'],
127 'rating' => $plugin['rating'] / 20,
128 'rating_count' => intval( $plugin['num_ratings'] ),
129 'active_installs' => intval( $plugin['active_installs'] ),
130 'author_block_rating' => $plugin['author_block_rating'] / 20,
131 'author_block_count' => intval( $plugin['author_block_count'] ),
132 'author' => wp_strip_all_tags( $plugin['author'] ),
133 'icon' => ( isset( $plugin['icons']['1x'] ) ? $plugin['icons']['1x'] : 'block-default' ),
134 'assets' => array(),
135 'last_updated' => $plugin['last_updated'],
136 'humanized_updated' => sprintf(
137 /* translators: %s: Human-readable time difference. */
138 __( '%s ago', 'gutenberg' ),
139 human_time_diff( strtotime( $plugin['last_updated'] ) )
140 ),
141 );
142
143 foreach ( $plugin['block_assets'] as $asset ) {
144 // Allow for fully qualified URLs in future.
145 if ( 'https' === wp_parse_url( $asset, PHP_URL_SCHEME ) && ! empty( wp_parse_url( $asset, PHP_URL_HOST ) ) ) {
146 $block['assets'][] = esc_url_raw(
147 $asset,
148 array( 'https' )
149 );
150 } else {
151 $block['assets'][] = esc_url_raw(
152 add_query_arg( 'v', strtotime( $block['last_updated'] ), 'https://ps.w.org/' . $plugin['slug'] . $asset ),
153 array( 'https' )
154 );
155 }
156 }
157
158 $this->add_additional_fields_to_object( $block, $request );
159
160 $response = new WP_REST_Response( $block );
161 $response->add_links( $this->prepare_links( $plugin ) );
162
163 return $response;
164 }
165
166 /**
167 * Generates a list of links to include in the response for the plugin.
168 *
169 * @since 5.5.0
170 *
171 * @param array $plugin The plugin data from WordPress.org.
172 *
173 * @return array
174 */
175 protected function prepare_links( $plugin ) {
176 $links = array(
177 'https://api.w.org/install-plugin' => array(
178 'href' => add_query_arg( 'slug', urlencode( $plugin['slug'] ), rest_url( 'wp/v2/plugins' ) ),
179 ),
180 );
181
182 $plugin_file = $this->find_plugin_for_slug( $plugin['slug'] );
183
184 if ( $plugin_file ) {
185 $links['https://api.w.org/plugin'] = array(
186 'href' => rest_url( 'wp/v2/plugins/' . substr( $plugin_file, 0, - 4 ) ),
187 'embeddable' => true,
188 );
189 }
190
191 return $links;
192 }
193
194 /**
195 * Finds an installed plugin for the given slug.
196 *
197 * @since 5.5.0
198 *
199 * @param string $slug The WordPress.org directory slug for a plugin.
200 *
201 * @return string The plugin file found matching it.
202 */
203 protected function find_plugin_for_slug( $slug ) {
204 require_once ABSPATH . 'wp-admin/includes/plugin.php';
205
206 $plugin_files = get_plugins( '/' . $slug );
207
208 if ( ! $plugin_files ) {
209 return '';
210 }
211
212 $plugin_files = array_keys( $plugin_files );
213
214 return $slug . '/' . reset( $plugin_files );
215 }
216
217 /**
218 * Retrieves the theme's schema, conforming to JSON Schema.
219 *
220 * @since 5.5.0
221 *
222 * @return array Item schema data.
223 */
224 public function get_item_schema() {
225 if ( $this->schema ) {
226 return $this->add_additional_fields_schema( $this->schema );
227 }
228
229 $this->schema = array(
230 '$schema' => 'http://json-schema.org/draft-04/schema#',
231 'title' => 'block-directory-item',
232 'type' => 'object',
233 'properties' => array(
234 'name' => array(
235 'description' => __( 'The block name, in namespace/block-name format.', 'gutenberg' ),
236 'type' => 'string',
237 'context' => array( 'view' ),
238 ),
239 'title' => array(
240 'description' => __( 'The block title, in human readable format.', 'gutenberg' ),
241 'type' => 'string',
242 'context' => array( 'view' ),
243 ),
244 'description' => array(
245 'description' => __( 'A short description of the block, in human readable format.', 'gutenberg' ),
246 'type' => 'string',
247 'context' => array( 'view' ),
248 ),
249 'id' => array(
250 'description' => __( 'The block slug.', 'gutenberg' ),
251 'type' => 'string',
252 'context' => array( 'view' ),
253 ),
254 'rating' => array(
255 'description' => __( 'The star rating of the block.', 'gutenberg' ),
256 'type' => 'integer',
257 'context' => array( 'view' ),
258 ),
259 'rating_count' => array(
260 'description' => __( 'The number of ratings.', 'gutenberg' ),
261 'type' => 'integer',
262 'context' => array( 'view' ),
263 ),
264 'active_installs' => array(
265 'description' => __( 'The number sites that have activated this block.', 'gutenberg' ),
266 'type' => 'string',
267 'context' => array( 'view' ),
268 ),
269 'author_block_rating' => array(
270 'description' => __( 'The average rating of blocks published by the same author.', 'gutenberg' ),
271 'type' => 'integer',
272 'context' => array( 'view' ),
273 ),
274 'author_block_count' => array(
275 'description' => __( 'The number of blocks published by the same author.', 'gutenberg' ),
276 'type' => 'integer',
277 'context' => array( 'view' ),
278 ),
279 'author' => array(
280 'description' => __( 'The WordPress.org username of the block author.', 'gutenberg' ),
281 'type' => 'string',
282 'context' => array( 'view' ),
283 ),
284 'icon' => array(
285 'description' => __( 'The block icon.', 'gutenberg' ),
286 'type' => 'string',
287 'format' => 'uri',
288 'context' => array( 'view' ),
289 ),
290 'humanized_updated' => array(
291 'description' => __( 'The date when the block was last updated, in fuzzy human readable format.', 'gutenberg' ),
292 'type' => 'string',
293 'context' => array( 'view' ),
294 ),
295 'assets' => array(
296 'description' => __( 'An object representing the block CSS and JavaScript assets.', 'gutenberg' ),
297 'type' => 'array',
298 'context' => array( 'view' ),
299 'readonly' => true,
300 'items' => array(
301 'type' => 'string',
302 'format' => 'uri',
303 ),
304
305 ),
306
307 ),
308 );
309
310 return $this->add_additional_fields_schema( $this->schema );
311 }
312
313 /**
314 * Retrieves the search params for the blocks collection.
315 *
316 * @since 5.5.0
317 *
318 * @return array Collection parameters.
319 */
320 public function get_collection_params() {
321 $query_params = parent::get_collection_params();
322
323 $query_params['context']['default'] = 'view';
324
325 $query_params['term'] = array(
326 'description' => __( 'Limit result set to blocks matching the search term.', 'gutenberg' ),
327 'type' => 'string',
328 'required' => true,
329 'minLength' => 1,
330 );
331
332 unset( $query_params['search'] );
333
334 /**
335 * Filter collection parameters for the block directory controller.
336 *
337 * @since 5.5.0
338 *
339 * @param array $query_params JSON Schema-formatted collection parameters.
340 */
341 return apply_filters( 'rest_block_directory_collection_params', $query_params );
342 }
343 }
344