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