diff --git a/wp-includes/class-wp-oembed-controller.php b/wp-includes/class-wp-oembed-controller.php index a613d0616d..0db5340e01 100644 --- a/wp-includes/class-wp-oembed-controller.php +++ b/wp-includes/class-wp-oembed-controller.php @@ -10,36 +10,18 @@ /** * oEmbed API endpoint controller. * - * Parses the oEmbed API requests and delivers - * XML and JSON responses. + * Registers the API route and delivers the response data. + * The output format (XML or JSON) is handled by the REST API. * * @since 4.4.0 */ final class WP_oEmbed_Controller { /** - * Hook into the query parsing to detect oEmbed requests. - * - * If an oEmbed request is made, trigger the output. + * Register the oEmbed REST API route. * * @since 4.4.0 - * - * @param WP_Query $wp_query The WP_Query instance (passed by reference). */ - public function parse_query( $wp_query ) { - if ( false === $wp_query->get( 'oembed', false ) ) { - return; - } - - if ( false === $wp_query->get( 'url', false ) ) { - status_header( 400 ); - return get_status_header_desc( 400 ); - exit; - } - - $url = esc_url_raw( get_query_var( 'url' ) ); - - $format = wp_oembed_ensure_format( get_query_var( 'format' ) ); - + public function register_routes() { /** * Filter the maxwidth oEmbed parameter. * @@ -48,30 +30,40 @@ final class WP_oEmbed_Controller { * @param int $maxwidth Maximum allowed width. Default 600. */ $maxwidth = apply_filters( 'oembed_default_width', 600 ); - $maxwidth = absint( get_query_var( 'maxwidth', $maxwidth ) ); - $callback = get_query_var( '_jsonp', false ); - - $request = array( - 'url' => $url, - 'format' => $format, - 'maxwidth' => $maxwidth, - 'callback' => $callback, - ); - - echo $this->dispatch( $request ); - exit; + register_rest_route( 'oembed/1.0/', '/embed', array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'args' => array( + 'url' => array( + 'required' => true, + 'sanitize_callback' => 'esc_url_raw', + ), + 'format' => array( + 'default' => 'json', + 'sanitize_callback' => 'wp_oembed_ensure_format', + ), + 'maxwidth' => array( + 'default' => $maxwidth, + 'sanitize_callback' => 'absint', + ), + ), + ), + ) ); } /** - * Handle the whole request and print the response. + * Callback for the API endpoint. + * + * Returns the JSON object for the post. * * @since 4.4.0 * - * @param array $request The request arguments. - * @return string The oEmbed API response. + * @param WP_REST_Request $request Full data about the request. + * @return WP_Error|array oEmbed response data or WP_Error on failure. */ - public function dispatch( $request ) { + public function get_item( $request ) { $post_id = url_to_postid( $request['url'] ); /** @@ -86,79 +78,10 @@ final class WP_oEmbed_Controller { $data = get_oembed_response_data( $post_id, $request['maxwidth'] ); - if ( false === $data ) { - status_header( 404 ); - return get_status_header_desc( 404 ); + if ( ! $data ) { + return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) ); } - if ( 'json' === $request['format'] ) { - return $this->json_response( $data, $request ); - } - - return $this->xml_response( $data ); - } - - /** - * Print the oEmbed JSON response. - * - * @since 4.4.0 - * - * @param array $data The oEmbed response data. - * @param array $request The request arguments. - * @return string The JSON response data. - */ - public function json_response( $data, $request ) { - if ( ! is_string( $request['callback'] ) || preg_match( '/[^\w\.]/', $request['callback'] ) ) { - $request['callback'] = false; - } - - $result = wp_json_encode( $data ); - - // Bail if the result couldn't be JSON encoded. - if ( ! $result || ! is_array( $data ) || empty( $data ) ) { - status_header( 501 ); - return get_status_header_desc( 501 ); - } - - if ( ! headers_sent() ) { - $content_type = $request['callback'] ? 'application/javascript' : 'application/json'; - header( 'Content-Type: ' . $content_type . '; charset=' . get_option( 'blog_charset' ) ); - header( 'X-Content-Type-Options: nosniff' ); - } - - if ( $request['callback'] ) { - return '/**/' . $request['callback'] . '(' . $result . ')'; - } - - return $result; - } - - /** - * Print the oEmbed XML response. - * - * @since 4.4.0 - * - * @param array $data The oEmbed response data. - * @return string The XML response data. - */ - public function xml_response( $data ) { - if ( ! class_exists( 'SimpleXMLElement' ) ) { - status_header( 501 ); - return get_status_header_desc( 501 ); - } - - $result = _oembed_create_xml( $data ); - - // Bail if there's no XML. - if ( ! $result ) { - status_header( 501 ); - return get_status_header_desc( 501 ); - } - - if ( ! headers_sent() ) { - header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ) ); - } - - return $result; + return $data; } } diff --git a/wp-includes/class-wp.php b/wp-includes/class-wp.php index 0f86251676..555167b1ea 100644 --- a/wp-includes/class-wp.php +++ b/wp-includes/class-wp.php @@ -15,7 +15,7 @@ class WP { * @access public * @var array */ - public $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type', 'title', 'embed', 'oembed', 'format', 'url', '_jsonp', 'maxwidth' ); + public $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type', 'title', 'embed' ); /** * Private query variables. diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php index 0bda80e1f4..70a9791ebc 100644 --- a/wp-includes/default-filters.php +++ b/wp-includes/default-filters.php @@ -439,31 +439,32 @@ add_filter( 'image_send_to_editor', 'image_add_caption', 20, 8 ); add_filter( 'media_send_to_editor', 'image_media_send_to_editor', 10, 3 ); // Embeds -add_action( 'parse_query', 'wp_oembed_parse_query' ); +add_action( 'rest_api_init', 'wp_oembed_register_route' ); +add_filter( 'rest_pre_serve_request', '_oembed_rest_pre_serve_request', 10, 4 ); -add_action( 'wp_head', 'wp_oembed_add_discovery_links' ); -add_action( 'wp_head', 'wp_oembed_add_host_js' ); +add_action( 'wp_head', 'wp_oembed_add_discovery_links' ); +add_action( 'wp_head', 'wp_oembed_add_host_js' ); -add_action( 'embed_head', 'print_emoji_detection_script' ); -add_action( 'embed_head', 'print_emoji_styles' ); -add_action( 'embed_head', 'print_embed_styles' ); -add_action( 'embed_head', 'wp_print_head_scripts', 20 ); -add_action( 'embed_head', 'wp_print_styles', 20 ); -add_action( 'embed_head', 'wp_no_robots' ); -add_action( 'embed_head', 'rel_canonical' ); -add_action( 'embed_head', 'locale_stylesheet' ); +add_action( 'embed_head', 'print_emoji_detection_script' ); +add_action( 'embed_head', 'print_emoji_styles' ); +add_action( 'embed_head', 'print_embed_styles' ); +add_action( 'embed_head', 'wp_print_head_scripts', 20 ); +add_action( 'embed_head', 'wp_print_styles', 20 ); +add_action( 'embed_head', 'wp_no_robots' ); +add_action( 'embed_head', 'rel_canonical' ); +add_action( 'embed_head', 'locale_stylesheet' ); -add_action( 'embed_footer', 'print_embed_scripts' ); -add_action( 'embed_footer', 'wp_print_footer_scripts', 20 ); +add_action( 'embed_footer', 'print_embed_scripts' ); +add_action( 'embed_footer', 'wp_print_footer_scripts', 20 ); -add_filter( 'excerpt_more', 'wp_embed_excerpt_more', 20 ); -add_filter( 'the_excerpt_embed', 'wptexturize' ); -add_filter( 'the_excerpt_embed', 'convert_chars' ); -add_filter( 'the_excerpt_embed', 'wpautop' ); -add_filter( 'the_excerpt_embed', 'shortcode_unautop' ); -add_filter( 'the_excerpt_embed', 'wp_embed_excerpt_attachment' ); +add_filter( 'excerpt_more', 'wp_embed_excerpt_more', 20 ); +add_filter( 'the_excerpt_embed', 'wptexturize' ); +add_filter( 'the_excerpt_embed', 'convert_chars' ); +add_filter( 'the_excerpt_embed', 'wpautop' ); +add_filter( 'the_excerpt_embed', 'shortcode_unautop' ); +add_filter( 'the_excerpt_embed', 'wp_embed_excerpt_attachment' ); -add_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10, 3 ); -add_filter( 'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 ); +add_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10, 3 ); +add_filter( 'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 ); unset( $filter, $action ); diff --git a/wp-includes/embed-functions.php b/wp-includes/embed-functions.php index aef67914d6..f28ed8692b 100644 --- a/wp-includes/embed-functions.php +++ b/wp-includes/embed-functions.php @@ -328,17 +328,13 @@ function wp_embed_handler_video( $matches, $attr, $url, $rawattr ) { } /** - * Parses an oEmbed API query. + * Registers the oEmbed REST API route. * * @since 4.4.0 - * - * @see WP_oEmbed_Controller::parse_query() - * - * @param WP_Query $wp_query The current WP_Query instance. */ -function wp_oembed_parse_query( $wp_query ) { +function wp_oembed_register_route() { $controller = new WP_oEmbed_Controller(); - $controller->parse_query( $wp_query ); + $controller->register_routes(); } /** @@ -421,7 +417,7 @@ function get_post_embed_url( $post = null ) { * @return string The oEmbed endpoint URL. */ function get_oembed_endpoint_url( $permalink = '', $format = 'json' ) { - $url = add_query_arg( array( 'oembed' => 'true' ), home_url( '/' ) ); + $url = rest_url( 'oembed/1.0/embed' ); if ( 'json' === $format ) { $format = false; @@ -545,17 +541,8 @@ function get_oembed_response_data( $post = null, $width ) { 'max' => 600 ) ); - if ( $width < $min_max_width['min'] ) { - $width = $min_max_width['min']; - } elseif ( $width > $min_max_width['max'] ) { - $width = $min_max_width['max']; - } - - $height = ceil( $width / 16 * 9 ); - - if ( 200 > $height ) { - $height = 200; - } + $width = min( max( $min_max_width['min'], $width ), $min_max_width['max'] ); + $height = max( ceil( $width / 16 * 9 ), 200 ); $data = array( 'version' => '1.0', @@ -646,6 +633,61 @@ function wp_oembed_ensure_format( $format ) { return $format; } +/** + * Hooks into the REST API output to print XML instead of JSON. + * + * This is only done for the oEmbed API endpoint, + * which supports both formats. + * + * @access private + * @since 4.4.0 + * + * @param bool $served Whether the request has already been served. + * @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response. + * @param WP_REST_Request $request Request used to generate the response. + * @param WP_REST_Server $server Server instance. + * @return true + */ +function _oembed_rest_pre_serve_request( $served, $result, $request, $server ) { + $params = $request->get_params(); + + if ( '/oembed/1.0/embed' !== $request->get_route() || 'GET' !== $request->get_method() ) { + return $served; + } + + if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) { + return $served; + } + + // Embed links inside the request. + $data = $server->response_to_data( $result, false ); + + if ( 404 === $result->get_status() ) { + $data = $data[0]; + } + + if ( ! class_exists( 'SimpleXMLElement' ) ) { + status_header( 501 ); + die( get_status_header_desc( 501 ) ); + } + + $result = _oembed_create_xml( $data ); + + // Bail if there's no XML. + if ( ! $result ) { + status_header( 501 ); + return get_status_header_desc( 501 ); + } + + if ( ! headers_sent() ) { + $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) ); + } + + echo $result; + + return true; +} + /** * Creates an XML string from a given array. * diff --git a/wp-includes/version.php b/wp-includes/version.php index d6390e45b2..9bed31e941 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '4.4-beta2-35435'; +$wp_version = '4.4-beta2-35436'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.