PluginProbe
Gutenberg / 10.3.0
Gutenberg v10.3.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-pattern-directory-controller.php

class-wp-rest-pattern-directory-controller.php in Gutenberg 10.3.0, at lib/class-wp-rest-pattern-directory-controller.php

300 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 Pattern Directory REST API: WP_REST_Pattern_Directory_Controller class
5 *
6 * @since 5.8.0
7 * @package gutenberg
8 */
9
10 /**
11 * Controller which provides REST endpoint for block patterns.
12 *
13 * This simply proxies the endpoint at http://api.wordpress.org/patterns/1.0/. That isn't necessary for
14 * functionality, but is desired for privacy. It prevents api.wordpress.org from knowing the user's IP address.
15 *
16 * This class can be removed when plugin support requires WordPress 5.8.0+.
17 *
18 * @since 5.8.0
19 *
20 * @see WP_REST_Controller
21 */
22 class WP_REST_Pattern_Directory_Controller extends WP_REST_Controller {
23 /**
24 * Constructs the controller.
25 */
26 public function __construct() {
27 $this->namespace = '__experimental';
28 $this->rest_base = 'pattern-directory';
29 }
30
31 /**
32 * Registers the necessary REST API routes.
33 */
34 public function register_routes() {
35 register_rest_route(
36 $this->namespace,
37 '/' . $this->rest_base . '/patterns',
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 view the local pattern directory.
52 *
53 * @since 5.8.0
54 *
55 * @param WP_REST_Request $request Full details about the request.
56 *
57 * @return WP_Error|bool True if the request has permission, WP_Error object otherwise.
58 */
59 public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Method must match signature of parent class.
60 if ( current_user_can( 'edit_posts' ) ) {
61 return true;
62 }
63
64 foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
65 if ( current_user_can( $post_type->cap->edit_posts ) ) {
66 return true;
67 }
68 }
69
70 return new WP_Error(
71 'rest_pattern_directory_cannot_view',
72 __( 'Sorry, you are not allowed to browse the local block pattern directory.', 'gutenberg' ),
73 array( 'status' => rest_authorization_required_code() )
74 );
75 }
76
77 /**
78 * Search and retrieve block patterns metadata
79 *
80 * @since 5.8.0
81 *
82 * @param WP_REST_Request $request Full details about the request.
83 *
84 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
85 */
86 public function get_items( $request ) {
87 $query_args = array();
88 $category_id = $request['category'];
89 $keyword_id = $request['keyword'];
90 $search_term = $request['search'];
91
92 if ( $category_id ) {
93 $query_args['pattern-categories'] = $category_id;
94 }
95
96 if ( $keyword_id ) {
97 $query_args['pattern-keywords'] = $keyword_id;
98 }
99
100 if ( $search_term ) {
101 $query_args['search'] = $search_term;
102 }
103
104 $api_url = add_query_arg(
105 array_map( 'rawurlencode', $query_args ),
106 'http://api.wordpress.org/patterns/1.0/'
107 );
108
109 if ( wp_http_supports( array( 'ssl' ) ) ) {
110 $api_url = set_url_scheme( $api_url, 'https' );
111 }
112
113 $wporg_response = wp_remote_get( $api_url );
114 $raw_patterns = json_decode( wp_remote_retrieve_body( $wporg_response ) );
115
116 if ( is_wp_error( $wporg_response ) ) {
117 $wporg_response->add_data( array( 'status' => 500 ) );
118
119 return $wporg_response;
120 }
121
122 // Make sure w.org returned valid data.
123 if ( ! is_array( $raw_patterns ) ) {
124 return new WP_Error(
125 'pattern_api_failed',
126 sprintf(
127 /* translators: %s: Support forums URL. */
128 __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.', 'gutenberg' ),
129 __( 'https://wordpress.org/support/forums/', 'gutenberg' )
130 ),
131 array(
132 'status' => 500,
133 'response' => wp_remote_retrieve_body( $wporg_response ),
134 )
135 );
136 }
137
138 $response = array();
139
140 if ( $raw_patterns ) {
141 foreach ( $raw_patterns as $pattern ) {
142 $response[] = $this->prepare_response_for_collection(
143 $this->prepare_item_for_response( $pattern, $request )
144 );
145 }
146 }
147
148 return new WP_REST_Response( $response );
149 }
150
151 /**
152 * Prepare a raw pattern before it's output in an API response.
153 *
154 * @since 5.8.0
155 *
156 * @param object $raw_pattern A pattern from api.wordpress.org, before any changes.
157 * @param WP_REST_Request $request Request object.
158 *
159 * @return WP_REST_Response
160 */
161 public function prepare_item_for_response( $raw_pattern, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Method must match signature of parent class.
162 $prepared_pattern = array(
163 'id' => absint( $raw_pattern->id ),
164 'title' => sanitize_text_field( $raw_pattern->title->rendered ),
165 'content' => wp_kses_post( $raw_pattern->pattern_content ),
166 'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ),
167 'keywords' => array_map( 'sanitize_title', $raw_pattern->keyword_slugs ),
168 'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ),
169 'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ),
170
171 );
172
173 $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request );
174
175 $response = new WP_REST_Response( $prepared_pattern );
176
177 /**
178 * Filters the REST API response for a pattern.
179 *
180 * @since 5.8.0
181 *
182 * @param WP_REST_Response $response The response object.
183 * @param object $raw_pattern The unprepared pattern.
184 * @param WP_REST_Request $request The request object.
185 */
186 return apply_filters( 'rest_prepare_application_password', $response, $raw_pattern, $request );
187 }
188
189 /**
190 * Retrieves the pattern's schema, conforming to JSON Schema.
191 *
192 * @since 5.8.0
193 *
194 * @return array Item schema data.
195 */
196 public function get_item_schema() {
197 if ( $this->schema ) {
198 return $this->add_additional_fields_schema( $this->schema );
199 }
200
201 $this->schema = array(
202 '$schema' => 'http://json-schema.org/draft-04/schema#',
203 'title' => 'pattern-directory-item',
204 'type' => 'object',
205 'properties' => array(
206 'id' => array(
207 'description' => __( 'The pattern ID.', 'gutenberg' ),
208 'type' => 'integer',
209 'minimum' => 1,
210 'context' => array( 'view', 'embed' ),
211 ),
212
213 'title' => array(
214 'description' => __( 'The pattern title, in human readable format.', 'gutenberg' ),
215 'type' => 'string',
216 'minLength' => 1,
217 'context' => array( 'view', 'embed' ),
218 ),
219
220 'content' => array(
221 'description' => __( 'The pattern content.', 'gutenberg' ),
222 'type' => 'string',
223 'minLength' => 1,
224 'context' => array( 'view', 'embed' ),
225 ),
226
227 'categories' => array(
228 'description' => __( "The pattern's category slugs.", 'gutenberg' ),
229 'type' => 'array',
230 'uniqueItems' => true,
231 'items' => array( 'type' => 'string' ),
232 'context' => array( 'view', 'embed' ),
233 ),
234
235 'keywords' => array(
236 'description' => __( "The pattern's keyword slugs.", 'gutenberg' ),
237 'type' => 'array',
238 'uniqueItems' => true,
239 'items' => array( 'type' => 'string' ),
240 'context' => array( 'view', 'embed' ),
241 ),
242
243 'description' => array(
244 'description' => __( 'A description of the pattern.', 'gutenberg' ),
245 'type' => 'string',
246 'minLength' => 1,
247 'context' => array( 'view', 'embed' ),
248 ),
249
250 'viewport_width' => array(
251 'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.', 'gutenberg' ),
252 'type' => 'integer',
253 'context' => array( 'view', 'embed' ),
254 ),
255 ),
256 );
257
258 return $this->add_additional_fields_schema( $this->schema );
259 }
260
261 /**
262 * Retrieves the search params for the patterns collection.
263 *
264 * @since 5.5.0
265 *
266 * @return array Collection parameters.
267 */
268 public function get_collection_params() {
269 $query_params = parent::get_collection_params();
270
271 // Pagination is not supported.
272 unset( $query_params['page'] );
273 unset( $query_params['per_page'] );
274
275 $query_params['search']['minLength'] = 1;
276 $query_params['context']['default'] = 'view';
277
278 $query_params['category'] = array(
279 'description' => __( 'Limit results to those matching a category ID.', 'gutenberg' ),
280 'type' => 'integer',
281 'minimum' => 1,
282 );
283
284 $query_params['keyword'] = array(
285 'description' => __( 'Limit results to those matching a keyword ID.', 'gutenberg' ),
286 'type' => 'integer',
287 'minimum' => 1,
288 );
289
290 /**
291 * Filter collection parameters for the pattern directory controller.
292 *
293 * @since 5.5.0
294 *
295 * @param array $query_params JSON Schema-formatted collection parameters.
296 */
297 return apply_filters( 'rest_pattern_directory_collection_params', $query_params );
298 }
299 }
300