PluginProbe
Gutenberg / 4.6.1
Gutenberg v4.6.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 / rest-api.php

rest-api.php in Gutenberg 4.6.1, at lib/rest-api.php

353 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHP and WordPress configuration compatibility functions for the Gutenberg
4 * editor plugin changes related to REST API.
5 *
6 * @package gutenberg
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 die( 'Silence is golden.' );
11 }
12
13 /**
14 * Registers the REST API routes needed by the Gutenberg editor.
15 *
16 * @since 2.8.0
17 */
18 function gutenberg_register_rest_routes() {
19 $controller = new WP_REST_Block_Renderer_Controller();
20 $controller->register_routes();
21
22 /**
23 * Filters the search handlers to use in the REST search controller.
24 *
25 * @since 3.3.0
26 *
27 * @param array $search_handlers List of search handlers to use in the controller. Each search
28 * handler instance must extend the `WP_REST_Search_Handler` class.
29 * Default is only a handler for posts.
30 */
31 $search_handlers = apply_filters( 'wp_rest_search_handlers', array( new WP_REST_Post_Search_Handler() ) );
32
33 $controller = new WP_REST_Search_Controller( $search_handlers );
34 $controller->register_routes();
35
36 foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
37 $class = ! empty( $post_type->rest_controller_class ) ? $post_type->rest_controller_class : 'WP_REST_Posts_Controller';
38
39 // Check if the class exists and is a subclass of WP_REST_Controller.
40 if ( ! is_subclass_of( $class, 'WP_REST_Controller' ) ) {
41 continue;
42 }
43
44 // Initialize the Autosaves controller.
45 $autosaves_controller = new WP_REST_Autosaves_Controller( $post_type->name );
46 $autosaves_controller->register_routes();
47 }
48
49 $themes_controller = new WP_REST_Themes_Controller();
50 $themes_controller->register_routes();
51 }
52 add_action( 'rest_api_init', 'gutenberg_register_rest_routes' );
53
54 /**
55 * Make sure oEmbed REST Requests apply the WP Embed security mechanism for WordPress embeds.
56 *
57 * @see https://core.trac.wordpress.org/ticket/32522
58 *
59 * TODO: This is a temporary solution. Next step would be to edit the WP_oEmbed_Controller,
60 * once merged into Core.
61 *
62 * @since 2.3.0
63 *
64 * @param WP_HTTP_Response|WP_Error $response The REST Request response.
65 * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
66 * @param WP_REST_Request $request Request used to generate the response.
67 * @return WP_HTTP_Response|object|WP_Error The REST Request response.
68 */
69 function gutenberg_filter_oembed_result( $response, $handler, $request ) {
70 if ( 'GET' !== $request->get_method() ) {
71 return $response;
72 }
73
74 if ( is_wp_error( $response ) && 'oembed_invalid_url' !== $response->get_error_code() ) {
75 return $response;
76 }
77
78 // External embeds.
79 if ( '/oembed/1.0/proxy' === $request->get_route() ) {
80 if ( is_wp_error( $response ) ) {
81 // It's possibly a local post, so lets try and retrieve it that way.
82 $post_id = url_to_postid( $_GET['url'] );
83 $data = get_oembed_response_data( $post_id, apply_filters( 'oembed_default_width', 600 ) );
84
85 if ( $data ) {
86 // It's a local post!
87 $response = (object) $data;
88 } else {
89 // Try using a classic embed, instead.
90 global $wp_embed;
91 $html = $wp_embed->shortcode( array(), $_GET['url'] );
92 if ( $html ) {
93 global $wp_scripts;
94 // Check if any scripts were enqueued by the shortcode, and
95 // include them in the response.
96 $enqueued_scripts = array();
97 foreach ( $wp_scripts->queue as $script ) {
98 $enqueued_scripts[] = $wp_scripts->registered[ $script ]->src;
99 }
100 return array(
101 'provider_name' => __( 'Embed Handler', 'gutenberg' ),
102 'html' => $html,
103 'scripts' => $enqueued_scripts,
104 );
105 }
106 }
107 }
108
109 // Make sure the HTML is run through the oembed sanitisation routines.
110 $response->html = wp_oembed_get( $_GET['url'], $_GET );
111 }
112
113 return $response;
114 }
115 add_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result', 10, 3 );
116
117 /**
118 * Add additional 'visibility' rest api field to taxonomies.
119 *
120 * Used so private taxonomies are not displayed in the UI.
121 *
122 * @see https://core.trac.wordpress.org/ticket/42707
123 */
124 function gutenberg_add_taxonomy_visibility_field() {
125 register_rest_field(
126 'taxonomy',
127 'visibility',
128 array(
129 'get_callback' => 'gutenberg_get_taxonomy_visibility_data',
130 'schema' => array(
131 'description' => __( 'The visibility settings for the taxonomy.', 'gutenberg' ),
132 'type' => 'object',
133 'context' => array( 'edit' ),
134 'readonly' => true,
135 'properties' => array(
136 'public' => array(
137 'description' => __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.', 'gutenberg' ),
138 'type' => 'boolean',
139 ),
140 'publicly_queryable' => array(
141 'description' => __( 'Whether the taxonomy is publicly queryable.', 'gutenberg' ),
142 'type' => 'boolean',
143 ),
144 'show_ui' => array(
145 'description' => __( 'Whether to generate a default UI for managing this taxonomy.', 'gutenberg' ),
146 'type' => 'boolean',
147 ),
148 'show_admin_column' => array(
149 'description' => __( 'Whether to allow automatic creation of taxonomy columns on associated post-types table.', 'gutenberg' ),
150 'type' => 'boolean',
151 ),
152 'show_in_nav_menus' => array(
153 'description' => __( 'Whether to make the taxonomy available for selection in navigation menus.', 'gutenberg' ),
154 'type' => 'boolean',
155 ),
156 'show_in_quick_edit' => array(
157 'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'gutenberg' ),
158 'type' => 'boolean',
159 ),
160 ),
161 ),
162 )
163 );
164 }
165
166 /**
167 * Gets taxonomy visibility property data.
168 *
169 * @see https://core.trac.wordpress.org/ticket/42707
170 *
171 * @param array $object Taxonomy data from REST API.
172 * @return array Array of taxonomy visibility data.
173 */
174 function gutenberg_get_taxonomy_visibility_data( $object ) {
175 // Just return existing data for up-to-date Core.
176 if ( isset( $object['visibility'] ) ) {
177 return $object['visibility'];
178 }
179
180 $taxonomy = get_taxonomy( $object['slug'] );
181
182 return array(
183 'public' => $taxonomy->public,
184 'publicly_queryable' => $taxonomy->publicly_queryable,
185 'show_ui' => $taxonomy->show_ui,
186 'show_admin_column' => $taxonomy->show_admin_column,
187 'show_in_nav_menus' => $taxonomy->show_in_nav_menus,
188 'show_in_quick_edit' => $taxonomy->show_ui,
189 );
190 }
191
192 add_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' );
193
194 /**
195 * Add a permalink template to posts in the post REST API response.
196 *
197 * @see https://core.trac.wordpress.org/ticket/45017
198 *
199 * @param WP_REST_Response $response WP REST API response of a post.
200 * @param WP_Post $post The post being returned.
201 * @param WP_REST_Request $request WP REST API request.
202 * @return WP_REST_Response Response containing the permalink_template.
203 */
204 function gutenberg_add_permalink_template_to_posts( $response, $post, $request ) {
205 if ( 'edit' !== $request['context'] ) {
206 return $response;
207 }
208
209 $post_type_obj = get_post_type_object( $post->post_type );
210 if ( ! is_post_type_viewable( $post_type_obj ) || ! $post_type_obj->public ) {
211 return $response;
212 }
213
214 if ( ! function_exists( 'get_sample_permalink' ) ) {
215 require_once ABSPATH . '/wp-admin/includes/post.php';
216 }
217
218 $sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' );
219
220 $response->data['permalink_template'] = $sample_permalink[0];
221 $response->data['generated_slug'] = $sample_permalink[1];
222
223 return $response;
224 }
225
226 /**
227 * Add the block format version to post content in the post REST API response.
228 *
229 * @todo This will need to be registered to the schema too.
230 *
231 * @see https://core.trac.wordpress.org/ticket/43887
232 *
233 * @param WP_REST_Response $response WP REST API response of a post.
234 * @param WP_Post $post The post being returned.
235 * @param WP_REST_Request $request WP REST API request.
236 * @return WP_REST_Response Response containing the block_format.
237 */
238 function gutenberg_add_block_format_to_post_content( $response, $post, $request ) {
239 if ( 'edit' !== $request['context'] ) {
240 return $response;
241 }
242
243 $response_data = $response->get_data();
244 if ( isset( $response_data['content'] ) && is_array( $response_data['content'] ) && isset( $response_data['content']['raw'] ) ) {
245 $response_data['content']['block_format'] = gutenberg_content_block_version( $response_data['content']['raw'] );
246 $response->set_data( $response_data );
247 }
248
249 return $response;
250 }
251
252 /**
253 * Include target schema attributes to links, based on whether the user can.
254 *
255 * @see https://core.trac.wordpress.org/ticket/45014
256 *
257 * @param WP_REST_Response $response WP REST API response of a post.
258 * @param WP_Post $post The post being returned.
259 * @param WP_REST_Request $request WP REST API request.
260 * @return WP_REST_Response Response containing the new links.
261 */
262 function gutenberg_add_target_schema_to_links( $response, $post, $request ) {
263 $new_links = array();
264 $orig_links = $response->get_links();
265 $post_type = get_post_type_object( $post->post_type );
266 $orig_href = ! empty( $orig_links['self'][0]['href'] ) ? $orig_links['self'][0]['href'] : null;
267 if ( 'edit' === $request['context'] && current_user_can( 'unfiltered_html' ) ) {
268 $new_links['https://api.w.org/action-unfiltered-html'] = array(
269 array(
270 'title' => __( 'The current user can post HTML markup and JavaScript.', 'gutenberg' ),
271 'href' => $orig_href,
272 'targetSchema' => array(
273 'type' => 'object',
274 'properties' => array(
275 'unfiltered_html' => array(
276 'type' => 'boolean',
277 ),
278 ),
279 ),
280 ),
281 );
282 }
283
284 $response->add_links( $new_links );
285 return $response;
286 }
287
288 /**
289 * Whenever a post type is registered, ensure we're hooked into it's WP REST API response.
290 *
291 * @param string $post_type The newly registered post type.
292 * @return string That same post type.
293 */
294 function gutenberg_register_post_prepare_functions( $post_type ) {
295 add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_permalink_template_to_posts', 10, 3 );
296 add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_block_format_to_post_content', 10, 3 );
297 add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_target_schema_to_links', 10, 3 );
298 return $post_type;
299 }
300 add_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' );
301
302
303 /**
304 * Silence PHP Warnings and Errors in JSON requests
305 *
306 * @todo This is a temporary measure until errors are properly silenced in REST API responses in core
307 *
308 * @see https://core.trac.wordpress.org/ticket/44534
309 */
310 function gutenberg_silence_rest_errors() {
311
312 if ( ( isset( $_SERVER['CONTENT_TYPE'] ) && 'application/json' === $_SERVER['CONTENT_TYPE'] ) ||
313 ( isset( $_SERVER['HTTP_ACCEPT'] ) && strpos( $_SERVER['HTTP_ACCEPT'], 'application/json' ) !== false ) ) {
314 // @codingStandardsIgnoreStart
315 @ini_set( 'display_errors', 0 );
316 // @codingStandardsIgnoreEnd
317 }
318
319 }
320
321 /**
322 * Include additional labels for registered post types
323 *
324 * @see https://core.trac.wordpress.org/ticket/45101
325 *
326 * @param array $args Arguments supplied to register_post_type().
327 * @param string $post_type Post type key.
328 * @return array Arguments supplied to register_post_type()
329 */
330 function gutenberg_filter_post_type_labels( $args, $post_type ) {
331 $registered_labels = ( empty( $args['labels'] ) ) ? array() : $args['labels'];
332 if ( is_post_type_hierarchical( $post_type ) ) {
333 $labels = array(
334 'item_published' => __( 'Page published.', 'gutenberg' ),
335 'item_published_privately' => __( 'Page published privately.', 'gutenberg' ),
336 'item_reverted_to_draft' => __( 'Page reverted to draft.', 'gutenberg' ),
337 'item_scheduled' => __( 'Page scheduled.', 'gutenberg' ),
338 'item_updated' => __( 'Page updated.', 'gutenberg' ),
339 );
340 } else {
341 $labels = array(
342 'item_published' => __( 'Post published.', 'gutenberg' ),
343 'item_published_privately' => __( 'Post published privately.', 'gutenberg' ),
344 'item_reverted_to_draft' => __( 'Post reverted to draft.', 'gutenberg' ),
345 'item_scheduled' => __( 'Post scheduled.', 'gutenberg' ),
346 'item_updated' => __( 'Post updated.', 'gutenberg' ),
347 );
348 }
349 $args['labels'] = array_merge( $labels, $registered_labels );
350 return $args;
351 }
352 add_filter( 'register_post_type_args', 'gutenberg_filter_post_type_labels', 10, 2 );
353