PluginProbe
Gutenberg / 7.4.0
Gutenberg v7.4.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 7.4.0 All 402 releases
gutenberg / lib / class-wp-rest-block-directory-controller.php

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

312 lines 8.9 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_Blocks_Controller class
5 *
6 * @package gutenberg
7 * @since 6.5.0
8 */
9
10 /**
11 * Controller which provides REST endpoint for the blocks.
12 *
13 * @since 6.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 public function __construct() {
23 $this->namespace = '__experimental';
24 $this->rest_base = 'block-directory';
25 }
26
27 /**
28 * Registers the necessary REST API routes.
29 */
30 public function register_routes() {
31 register_rest_route(
32 $this->namespace,
33 '/' . $this->rest_base . '/search',
34 array(
35 array(
36 'methods' => WP_REST_Server::READABLE,
37 'callback' => array( $this, 'get_items' ),
38 'permission_callback' => array( $this, 'permissions_check' ),
39 ),
40 'schema' => array( $this, 'get_item_schema' ),
41 )
42 );
43 register_rest_route(
44 $this->namespace,
45 '/' . $this->rest_base . '/install',
46 array(
47 array(
48 'methods' => WP_REST_Server::CREATABLE,
49 'callback' => array( $this, 'install_block' ),
50 'permission_callback' => array( $this, 'permissions_check' ),
51 ),
52 'schema' => array( $this, 'get_item_schema' ),
53 )
54 );
55 register_rest_route(
56 $this->namespace,
57 '/' . $this->rest_base . '/uninstall',
58 array(
59 array(
60 'methods' => WP_REST_Server::DELETABLE,
61 'callback' => array( $this, 'uninstall_block' ),
62 'permission_callback' => array( $this, 'permissions_check' ),
63 ),
64 'schema' => array( $this, 'get_item_schema' ),
65 )
66 );
67 }
68
69 /**
70 * Checks whether a given request has permission to install and activate plugins.
71 *
72 * @since 6.5.0
73 *
74 * @return WP_Error|bool True if the request has permission, WP_Error object otherwise.
75 */
76 public function permissions_check() {
77 if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
78 return new WP_Error(
79 'rest_user_cannot_view',
80 __( 'Sorry, you are not allowed to install blocks.', 'gutenberg' )
81 );
82 }
83
84 return true;
85 }
86
87 /**
88 * Installs and activates a plugin
89 *
90 * @since 6.5.0
91 *
92 * @param WP_REST_Request $request Full details about the request.
93 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
94 */
95 public function install_block( $request ) {
96
97 include_once( ABSPATH . 'wp-admin/includes/file.php' );
98 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
99 include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
100 include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
101
102 $api = plugins_api(
103 'plugin_information',
104 array(
105 'slug' => $request->get_param( 'slug' ),
106 'fields' => array(
107 'sections' => false,
108 ),
109 )
110 );
111
112 if ( is_wp_error( $api ) ) {
113 return WP_Error( $api->get_error_code(), $api->get_error_message() );
114 }
115
116 $skin = new WP_Ajax_Upgrader_Skin();
117 $upgrader = new Plugin_Upgrader( $skin );
118
119 $filesystem_method = get_filesystem_method();
120
121 if ( 'direct' !== $filesystem_method ) {
122 return WP_Error( null, 'Only direct FS_METHOD is supported.' );
123 }
124
125 $result = $upgrader->install( $api->download_link );
126
127 if ( is_wp_error( $result ) ) {
128 return WP_Error( $result->get_error_code(), $result->get_error_message() );
129 }
130
131 if ( is_wp_error( $skin->result ) ) {
132 return WP_Error( $skin->$result->get_error_code(), $skin->$result->get_error_message() );
133 }
134
135 if ( $skin->get_errors()->has_errors() ) {
136 return WP_Error( $skin->$result->get_error_code(), $skin->$result->get_error_messages() );
137 }
138
139 if ( is_null( $result ) ) {
140 global $wp_filesystem;
141 // Pass through the error from WP_Filesystem if one was raised.
142 if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
143 return WP_Error( 'unable_to_connect_to_filesystem', esc_html( $wp_filesystem->errors->get_error_message() ) );
144 }
145 return WP_Error( 'unable_to_connect_to_filesystem', __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'gutenberg' ) );
146 }
147
148 $install_status = install_plugin_install_status( $api );
149
150 $activate_result = activate_plugin( $install_status['file'] );
151
152 if ( is_wp_error( $activate_result ) ) {
153 return WP_Error( $activate_result->get_error_code(), $activate_result->get_error_message() );
154 }
155
156 return rest_ensure_response( true );
157 }
158
159 /**
160 * Deactivates and deletes a plugin
161 *
162 * @since 6.5.0
163 *
164 * @param WP_REST_Request $request Full details about the request.
165 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
166 */
167 public function uninstall_block( $request ) {
168
169 include_once( ABSPATH . 'wp-admin/includes/file.php' );
170 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
171 include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
172 include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
173
174 $api = plugins_api(
175 'plugin_information',
176 array(
177 'slug' => $request->get_param( 'slug' ),
178 'fields' => array(
179 'sections' => false,
180 ),
181 )
182 );
183
184 if ( is_wp_error( $api ) ) {
185 return WP_Error( $api->get_error_code(), $api->get_error_message() );
186 }
187
188 $install_status = install_plugin_install_status( $api );
189
190 $deactivate_result = deactivate_plugins( $install_status['file'] );
191
192 if ( is_wp_error( $deactivate_result ) ) {
193 return WP_Error( $deactivate_result->get_error_code(), $deactivate_result->get_error_message() );
194 }
195
196 $delete_result = delete_plugins( array( $install_status['file'] ) );
197
198 if ( is_wp_error( $delete_result ) ) {
199 return WP_Error( $delete_result->get_error_code(), $delete_result->get_error_message() );
200 }
201
202 return rest_ensure_response( true );
203 }
204
205 /**
206 * Search and retrieve blocks metadata
207 *
208 * @since 6.5.0
209 *
210 * @param WP_REST_Request $request Full details about the request.
211 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
212 */
213 public function get_items( $request ) {
214
215 $search_string = $request->get_param( 'term' );
216
217 if ( empty( $search_string ) ) {
218 return rest_ensure_response( array() );
219 }
220
221 include( ABSPATH . WPINC . '/version.php' );
222
223 $url = 'http://api.wordpress.org/plugins/info/1.2/';
224 $url = add_query_arg(
225 array(
226 'action' => 'query_plugins',
227 'request[block]' => $search_string,
228 'request[wp_version]' => '5.3',
229 'request[per_page]' => '3',
230 ),
231 $url
232 );
233 $ssl = wp_http_supports( array( 'ssl' ) );
234 if ( $ssl ) {
235 $url = set_url_scheme( $url, 'https' );
236 }
237
238 global $wp_version;
239 $http_args = array(
240 'timeout' => 15,
241 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
242 );
243
244 $request = wp_remote_get( $url, $http_args );
245 $response = json_decode( wp_remote_retrieve_body( $request ), true );
246
247 if ( ! function_exists( 'get_plugins' ) ) {
248 require_once ABSPATH . 'wp-admin/includes/plugin.php';
249 }
250
251 $result = array();
252
253 foreach ( $response['plugins'] as $plugin ) {
254 $installed_plugins = get_plugins( '/' . $plugin['slug'] );
255
256 // Only show uninstalled blocks.
257 if ( empty( $installed_plugins ) ) {
258 $result[] = self::parse_block_metadata( $plugin );
259 }
260 }
261
262 return rest_ensure_response( $result );
263 }
264
265 /**
266 * Parse block metadata for a block
267 *
268 * @since 6.5.0
269 *
270 * @param WP_Object $plugin The plugin metadata.
271 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
272 */
273 private static function parse_block_metadata( $plugin ) {
274 $block = new stdClass();
275
276 // There might be multiple blocks in a plugin. Only the first block is mapped.
277 $block_data = reset( $plugin['blocks'] );
278 $block->name = $block_data['name'];
279 $block->title = $block_data['title'];
280
281 // Plugin's description, not description in block.json.
282 $block->description = wp_trim_words( wp_strip_all_tags( $plugin['description'] ), 30, '...' );
283
284 $block->id = $plugin['slug'];
285 $block->rating = $plugin['rating'] / 20;
286 $block->rating_count = $plugin['num_ratings'];
287 $block->active_installs = $plugin['active_installs'];
288 $block->author_block_rating = $plugin['author_block_rating'] / 20;
289 $block->author_block_count = $plugin['author_block_count'];
290
291 // Plugin's author, not author in block.json.
292 $block->author = wp_strip_all_tags( $plugin['author'] );
293
294 // Plugin's icons or icon in block.json.
295 $block->icon = isset( $plugin['icons']['1x'] ) ? $plugin['icons']['1x'] : 'block-default';
296
297 $block->assets = array();
298
299 foreach ( $plugin['block_assets'] as $asset ) {
300 $block->assets[] = 'https://plugins.svn.wordpress.org/' . $plugin['slug'] . $asset;
301 }
302
303 $block->humanized_updated = sprintf(
304 /* translators: %s: Human-readable time difference. */
305 __( '%s ago', 'gutenberg' ),
306 human_time_diff( strtotime( $plugin['last_updated'] ), current_time( 'timestamp' ) )
307 );
308
309 return $block;
310 }
311 }
312