PluginProbe
Gutenberg / 9.9.1
Gutenberg v9.9.1
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 9.9.1, at lib/class-wp-rest-pattern-directory-controller.php

289 lines 8.7 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_ids = $request['category'];
89 $search_term = $request['search'];
90
91 if ( $category_ids ) {
92 $query_args['pattern-categories'] = $category_ids;
93 }
94
95 if ( $search_term ) {
96 $query_args['search'] = $search_term;
97 }
98
99 $api_url = add_query_arg(
100 array_map( 'rawurlencode', $query_args ),
101 'http://api.wordpress.org/patterns/1.0/'
102 );
103
104 if ( wp_http_supports( array( 'ssl' ) ) ) {
105 $api_url = set_url_scheme( $api_url, 'https' );
106 }
107
108 $wporg_response = wp_remote_get( $api_url );
109 $raw_patterns = json_decode( wp_remote_retrieve_body( $wporg_response ) );
110
111 if ( is_wp_error( $wporg_response ) ) {
112 $wporg_response->add_data( array( 'status' => 500 ) );
113
114 return $wporg_response;
115 }
116
117 // Make sure w.org returned valid data.
118 if ( ! is_array( $raw_patterns ) ) {
119 return new WP_Error(
120 'pattern_api_failed',
121 sprintf(
122 /* translators: %s: Support forums URL. */
123 __( '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' ),
124 __( 'https://wordpress.org/support/forums/', 'gutenberg' )
125 ),
126 array(
127 'status' => 500,
128 'response' => wp_remote_retrieve_body( $wporg_response ),
129 )
130 );
131 }
132
133 $response = array();
134
135 if ( $raw_patterns ) {
136 foreach ( $raw_patterns as $pattern ) {
137 $response[] = $this->prepare_response_for_collection(
138 $this->prepare_item_for_response( $pattern, $request )
139 );
140 }
141 }
142
143 return new WP_REST_Response( $response );
144 }
145
146 /**
147 * Prepare a raw pattern before it's output in an API response.
148 *
149 * @since 5.8.0
150 *
151 * @param object $raw_pattern A pattern from api.wordpress.org, before any changes.
152 * @param WP_REST_Request $request Request object.
153 *
154 * @return WP_REST_Response
155 */
156 public function prepare_item_for_response( $raw_pattern, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Method must match signature of parent class.
157 $prepared_pattern = array(
158 'id' => absint( $raw_pattern->id ),
159 'title' => sanitize_text_field( $raw_pattern->title->rendered ),
160 'content' => wp_kses_post( $raw_pattern->content->rendered ),
161 'categories' => array_map( 'sanitize_title', $raw_pattern->category_slugs ),
162 'keywords' => array_map( 'sanitize_title', $raw_pattern->keyword_slugs ),
163 'description' => sanitize_text_field( $raw_pattern->meta->wpop_description ),
164 'viewport_width' => absint( $raw_pattern->meta->wpop_viewport_width ),
165
166 );
167
168 $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request );
169
170 $response = new WP_REST_Response( $prepared_pattern );
171
172 /**
173 * Filters the REST API response for a pattern.
174 *
175 * @since 5.8.0
176 *
177 * @param WP_REST_Response $response The response object.
178 * @param object $raw_pattern The unprepared pattern.
179 * @param WP_REST_Request $request The request object.
180 */
181 return apply_filters( 'rest_prepare_application_password', $response, $raw_pattern, $request );
182 }
183
184 /**
185 * Retrieves the pattern's schema, conforming to JSON Schema.
186 *
187 * @since 5.8.0
188 *
189 * @return array Item schema data.
190 */
191 public function get_item_schema() {
192 if ( $this->schema ) {
193 return $this->add_additional_fields_schema( $this->schema );
194 }
195
196 $this->schema = array(
197 '$schema' => 'http://json-schema.org/draft-04/schema#',
198 'title' => 'pattern-directory-item',
199 'type' => 'object',
200 'properties' => array(
201 'id' => array(
202 'description' => __( 'The pattern ID.', 'gutenberg' ),
203 'type' => 'integer',
204 'minimum' => 1,
205 'context' => array( 'view', 'embed' ),
206 ),
207
208 'title' => array(
209 'description' => __( 'The pattern title, in human readable format.', 'gutenberg' ),
210 'type' => 'string',
211 'minLength' => 1,
212 'context' => array( 'view', 'embed' ),
213 ),
214
215 'content' => array(
216 'description' => __( 'The pattern content.', 'gutenberg' ),
217 'type' => 'string',
218 'minLength' => 1,
219 'context' => array( 'view', 'embed' ),
220 ),
221
222 'categories' => array(
223 'description' => __( "The pattern's category slugs.", 'gutenberg' ),
224 'type' => 'array',
225 'uniqueItems' => true,
226 'items' => array( 'type' => 'string' ),
227 'context' => array( 'view', 'embed' ),
228 ),
229
230 'keywords' => array(
231 'description' => __( "The pattern's keyword slugs.", 'gutenberg' ),
232 'type' => 'array',
233 'uniqueItems' => true,
234 'items' => array( 'type' => 'string' ),
235 'context' => array( 'view', 'embed' ),
236 ),
237
238 'description' => array(
239 'description' => __( 'A description of the pattern.', 'gutenberg' ),
240 'type' => 'string',
241 'minLength' => 1,
242 'context' => array( 'view', 'embed' ),
243 ),
244
245 'viewport_width' => array(
246 'description' => __( 'The preferred width of the viewport when previewing a pattern, in pixels.', 'gutenberg' ),
247 'type' => 'integer',
248 'context' => array( 'view', 'embed' ),
249 ),
250 ),
251 );
252
253 return $this->add_additional_fields_schema( $this->schema );
254 }
255
256 /**
257 * Retrieves the search params for the patterns collection.
258 *
259 * @since 5.5.0
260 *
261 * @return array Collection parameters.
262 */
263 public function get_collection_params() {
264 $query_params = parent::get_collection_params();
265
266 // Pagination is not supported.
267 unset( $query_params['page'] );
268 unset( $query_params['per_page'] );
269
270 $query_params['search']['minLength'] = 1;
271 $query_params['context']['default'] = 'view';
272
273 $query_params['category'] = array(
274 'description' => __( 'Limit results to those matching a category ID.', 'gutenberg' ),
275 'type' => 'integer',
276 'minimum' => 1,
277 );
278
279 /**
280 * Filter collection parameters for the pattern directory controller.
281 *
282 * @since 5.5.0
283 *
284 * @param array $query_params JSON Schema-formatted collection parameters.
285 */
286 return apply_filters( 'rest_pattern_directory_collection_params', $query_params );
287 }
288 }
289