111 |
113 |
112 if ( $search_term ) { |
114 if ( $search_term ) { |
113 $query_args['search'] = $search_term; |
115 $query_args['search'] = $search_term; |
114 } |
116 } |
115 |
117 |
116 /* |
118 if ( $slug ) { |
117 * Include a hash of the query args, so that different requests are stored in |
119 $query_args['slug'] = $slug; |
118 * separate caches. |
120 } |
119 * |
121 |
120 * MD5 is chosen for its speed, low-collision rate, universal availability, and to stay |
122 $transient_key = $this->get_transient_key( $query_args ); |
121 * under the character limit for `_site_transient_timeout_{...}` keys. |
|
122 * |
|
123 * @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses |
|
124 */ |
|
125 $transient_key = 'wp_remote_block_patterns_' . md5( implode( '-', $query_args ) ); |
|
126 |
123 |
127 /* |
124 /* |
128 * Use network-wide transient to improve performance. The locale is the only site |
125 * Use network-wide transient to improve performance. The locale is the only site |
129 * configuration that affects the response, and it's included in the transient key. |
126 * configuration that affects the response, and it's included in the transient key. |
130 */ |
127 */ |
131 $raw_patterns = get_site_transient( $transient_key ); |
128 $raw_patterns = get_site_transient( $transient_key ); |
132 |
129 |
133 if ( ! $raw_patterns ) { |
130 if ( ! $raw_patterns ) { |
134 $api_url = add_query_arg( |
131 $api_url = 'http://api.wordpress.org/patterns/1.0/?' . build_query( $query_args ); |
135 array_map( 'rawurlencode', $query_args ), |
|
136 'http://api.wordpress.org/patterns/1.0/' |
|
137 ); |
|
138 |
|
139 if ( wp_http_supports( array( 'ssl' ) ) ) { |
132 if ( wp_http_supports( array( 'ssl' ) ) ) { |
140 $api_url = set_url_scheme( $api_url, 'https' ); |
133 $api_url = set_url_scheme( $api_url, 'https' ); |
141 } |
134 } |
142 |
135 |
143 /* |
136 /* |
157 } elseif ( ! is_array( $raw_patterns ) ) { |
150 } elseif ( ! is_array( $raw_patterns ) ) { |
158 // HTTP request succeeded, but response data is invalid. |
151 // HTTP request succeeded, but response data is invalid. |
159 $raw_patterns = new WP_Error( |
152 $raw_patterns = new WP_Error( |
160 'pattern_api_failed', |
153 'pattern_api_failed', |
161 sprintf( |
154 sprintf( |
162 /* translators: %s: Support forums URL. */ |
155 /* translators: %s: Support forums URL. */ |
163 __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ), |
156 __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ), |
164 __( 'https://wordpress.org/support/forums/' ) |
157 __( 'https://wordpress.org/support/forums/' ) |
165 ), |
158 ), |
166 array( |
159 array( |
167 'response' => wp_remote_retrieve_body( $wporg_response ), |
160 'response' => wp_remote_retrieve_body( $wporg_response ), |
194 |
187 |
195 return new WP_REST_Response( $response ); |
188 return new WP_REST_Response( $response ); |
196 } |
189 } |
197 |
190 |
198 /** |
191 /** |
199 * Prepare a raw pattern before it's output in an API response. |
192 * Prepare a raw block pattern before it gets output in a REST API response. |
200 * |
193 * |
201 * @since 5.8.0 |
194 * @since 5.8.0 |
202 * |
195 * @since 5.9.0 Renamed `$raw_pattern` to `$item` to match parent class for PHP 8 named parameter support. |
203 * @param object $raw_pattern A pattern from api.wordpress.org, before any changes. |
196 * |
204 * @param WP_REST_Request $request Request object. |
197 * @param object $item Raw pattern from api.wordpress.org, before any changes. |
|
198 * @param WP_REST_Request $request Request object. |
205 * @return WP_REST_Response |
199 * @return WP_REST_Response |
206 */ |
200 */ |
207 public function prepare_item_for_response( $raw_pattern, $request ) { |
201 public function prepare_item_for_response( $item, $request ) { |
|
202 // Restores the more descriptive, specific name for use within this method. |
|
203 $raw_pattern = $item; |
208 $prepared_pattern = array( |
204 $prepared_pattern = array( |
209 'id' => absint( $raw_pattern->id ), |
205 'id' => absint( $raw_pattern->id ), |
210 'title' => sanitize_text_field( $raw_pattern->title->rendered ), |
206 'title' => sanitize_text_field( $raw_pattern->title->rendered ), |
211 'content' => wp_kses_post( $raw_pattern->pattern_content ), |
207 'content' => wp_kses_post( $raw_pattern->pattern_content ), |
212 'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ), |
208 'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ), |
213 'keywords' => array_map( 'sanitize_title', $raw_pattern->keyword_slugs ), |
209 'keywords' => array_map( 'sanitize_text_field', explode( ',', $raw_pattern->meta->wpop_keywords ) ), |
214 'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ), |
210 'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ), |
215 'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ), |
211 'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ), |
216 ); |
212 ); |
217 |
213 |
218 $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request ); |
214 $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request ); |
219 |
215 |
220 $response = new WP_REST_Response( $prepared_pattern ); |
216 $response = new WP_REST_Response( $prepared_pattern ); |
221 |
217 |
222 /** |
218 /** |
223 * Filters the REST API response for a pattern. |
219 * Filters the REST API response for a block pattern. |
224 * |
220 * |
225 * @since 5.8.0 |
221 * @since 5.8.0 |
226 * |
222 * |
227 * @param WP_REST_Response $response The response object. |
223 * @param WP_REST_Response $response The response object. |
228 * @param object $raw_pattern The unprepared pattern. |
224 * @param object $raw_pattern The unprepared block pattern. |
229 * @param WP_REST_Request $request The request object. |
225 * @param WP_REST_Request $request The request object. |
230 */ |
226 */ |
231 return apply_filters( 'rest_prepare_block_pattern', $response, $raw_pattern, $request ); |
227 return apply_filters( 'rest_prepare_block_pattern', $response, $raw_pattern, $request ); |
232 } |
228 } |
233 |
229 |
234 /** |
230 /** |
235 * Retrieves the pattern's schema, conforming to JSON Schema. |
231 * Retrieves the block pattern's schema, conforming to JSON Schema. |
236 * |
232 * |
237 * @since 5.8.0 |
233 * @since 5.8.0 |
238 * |
234 * |
239 * @return array Item schema data. |
235 * @return array Item schema data. |
240 */ |
236 */ |
250 'properties' => array( |
246 'properties' => array( |
251 'id' => array( |
247 'id' => array( |
252 'description' => __( 'The pattern ID.' ), |
248 'description' => __( 'The pattern ID.' ), |
253 'type' => 'integer', |
249 'type' => 'integer', |
254 'minimum' => 1, |
250 'minimum' => 1, |
255 'context' => array( 'view', 'embed' ), |
251 'context' => array( 'view', 'edit', 'embed' ), |
256 ), |
252 ), |
257 |
253 |
258 'title' => array( |
254 'title' => array( |
259 'description' => __( 'The pattern title, in human readable format.' ), |
255 'description' => __( 'The pattern title, in human readable format.' ), |
260 'type' => 'string', |
256 'type' => 'string', |
261 'minLength' => 1, |
257 'minLength' => 1, |
262 'context' => array( 'view', 'embed' ), |
258 'context' => array( 'view', 'edit', 'embed' ), |
263 ), |
259 ), |
264 |
260 |
265 'content' => array( |
261 'content' => array( |
266 'description' => __( 'The pattern content.' ), |
262 'description' => __( 'The pattern content.' ), |
267 'type' => 'string', |
263 'type' => 'string', |
268 'minLength' => 1, |
264 'minLength' => 1, |
269 'context' => array( 'view', 'embed' ), |
265 'context' => array( 'view', 'edit', 'embed' ), |
270 ), |
266 ), |
271 |
267 |
272 'categories' => array( |
268 'categories' => array( |
273 'description' => __( "The pattern's category slugs." ), |
269 'description' => __( "The pattern's category slugs." ), |
274 'type' => 'array', |
270 'type' => 'array', |
275 'uniqueItems' => true, |
271 'uniqueItems' => true, |
276 'items' => array( 'type' => 'string' ), |
272 'items' => array( 'type' => 'string' ), |
277 'context' => array( 'view', 'embed' ), |
273 'context' => array( 'view', 'edit', 'embed' ), |
278 ), |
274 ), |
279 |
275 |
280 'keywords' => array( |
276 'keywords' => array( |
281 'description' => __( "The pattern's keyword slugs." ), |
277 'description' => __( "The pattern's keywords." ), |
282 'type' => 'array', |
278 'type' => 'array', |
283 'uniqueItems' => true, |
279 'uniqueItems' => true, |
284 'items' => array( 'type' => 'string' ), |
280 'items' => array( 'type' => 'string' ), |
285 'context' => array( 'view', 'embed' ), |
281 'context' => array( 'view', 'edit', 'embed' ), |
286 ), |
282 ), |
287 |
283 |
288 'description' => array( |
284 'description' => array( |
289 'description' => __( 'A description of the pattern.' ), |
285 'description' => __( 'A description of the pattern.' ), |
290 'type' => 'string', |
286 'type' => 'string', |
291 'minLength' => 1, |
287 'minLength' => 1, |
292 'context' => array( 'view', 'embed' ), |
288 'context' => array( 'view', 'edit', 'embed' ), |
293 ), |
289 ), |
294 |
290 |
295 'viewport_width' => array( |
291 'viewport_width' => array( |
296 'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.' ), |
292 'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.' ), |
297 'type' => 'integer', |
293 'type' => 'integer', |
298 'context' => array( 'view', 'embed' ), |
294 'context' => array( 'view', 'edit', 'embed' ), |
299 ), |
295 ), |
300 ), |
296 ), |
301 ); |
297 ); |
302 |
298 |
303 return $this->add_additional_fields_schema( $this->schema ); |
299 return $this->add_additional_fields_schema( $this->schema ); |
304 } |
300 } |
305 |
301 |
306 /** |
302 /** |
307 * Retrieves the search params for the patterns collection. |
303 * Retrieves the search parameters for the block pattern's collection. |
308 * |
304 * |
309 * @since 5.8.0 |
305 * @since 5.8.0 |
310 * |
306 * |
311 * @return array Collection parameters. |
307 * @return array Collection parameters. |
312 */ |
308 */ |
330 'description' => __( 'Limit results to those matching a keyword ID.' ), |
326 'description' => __( 'Limit results to those matching a keyword ID.' ), |
331 'type' => 'integer', |
327 'type' => 'integer', |
332 'minimum' => 1, |
328 'minimum' => 1, |
333 ); |
329 ); |
334 |
330 |
|
331 $query_params['slug'] = array( |
|
332 'description' => __( 'Limit results to those matching a pattern (slug).' ), |
|
333 'type' => 'array', |
|
334 ); |
|
335 |
335 /** |
336 /** |
336 * Filter collection parameters for the pattern directory controller. |
337 * Filter collection parameters for the block pattern directory controller. |
337 * |
338 * |
338 * @since 5.8.0 |
339 * @since 5.8.0 |
339 * |
340 * |
340 * @param array $query_params JSON Schema-formatted collection parameters. |
341 * @param array $query_params JSON Schema-formatted collection parameters. |
341 */ |
342 */ |
342 return apply_filters( 'rest_pattern_directory_collection_params', $query_params ); |
343 return apply_filters( 'rest_pattern_directory_collection_params', $query_params ); |
343 } |
344 } |
|
345 |
|
346 /* |
|
347 * Include a hash of the query args, so that different requests are stored in |
|
348 * separate caches. |
|
349 * |
|
350 * MD5 is chosen for its speed, low-collision rate, universal availability, and to stay |
|
351 * under the character limit for `_site_transient_timeout_{...}` keys. |
|
352 * |
|
353 * @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses |
|
354 * |
|
355 * @since 6.0.0 |
|
356 * |
|
357 * @param array $query_args Query arguments to generate a transient key from. |
|
358 * @return string Transient key. |
|
359 */ |
|
360 protected function get_transient_key( $query_args ) { |
|
361 |
|
362 if ( isset( $query_args['slug'] ) ) { |
|
363 // This is an additional precaution because the "sort" function expects an array. |
|
364 $query_args['slug'] = wp_parse_list( $query_args['slug'] ); |
|
365 |
|
366 // Empty arrays should not affect the transient key. |
|
367 if ( empty( $query_args['slug'] ) ) { |
|
368 unset( $query_args['slug'] ); |
|
369 } else { |
|
370 // Sort the array so that the transient key doesn't depend on the order of slugs. |
|
371 sort( $query_args['slug'] ); |
|
372 } |
|
373 } |
|
374 |
|
375 return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) ); |
|
376 } |
344 } |
377 } |