diff --git a/src/wp-includes/class-wp-oembed-controller.php b/src/wp-includes/class-wp-oembed-controller.php index a613d0616d..0db5340e01 100644 --- a/src/wp-includes/class-wp-oembed-controller.php +++ b/src/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/src/wp-includes/class-wp.php b/src/wp-includes/class-wp.php index 0f86251676..555167b1ea 100644 --- a/src/wp-includes/class-wp.php +++ b/src/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/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 0bda80e1f4..70a9791ebc 100644 --- a/src/wp-includes/default-filters.php +++ b/src/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/src/wp-includes/embed-functions.php b/src/wp-includes/embed-functions.php index 0b67ed5869..585b78e855 100644 --- a/src/wp-includes/embed-functions.php +++ b/src/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/tests/phpunit/tests/oembed/controller.php b/tests/phpunit/tests/oembed/controller.php index 51c6302c32..9410e00fa0 100644 --- a/tests/phpunit/tests/oembed/controller.php +++ b/tests/phpunit/tests/oembed/controller.php @@ -2,217 +2,22 @@ /** * @group oembed + * @group restapi */ class Test_oEmbed_Controller extends WP_UnitTestCase { - function test_request_with_bad_url() { - $request = array( - 'url' => '', - 'format' => 'json', - 'maxwidth' => 600, - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $this->assertEquals( get_status_header_desc( 404 ), $legacy_controller->dispatch( $request ) ); - } - - function test_request_json() { - $user = self::factory()->user->create_and_get( array( - 'display_name' => 'John Doe', - ) ); - $post = self::factory()->post->create_and_get( array( - 'post_author' => $user->ID, - 'post_title' => 'Hello World', - ) ); - - // WP_Query arguments. - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'json', - 'maxwidth' => 400, - 'callback' => '', - 'oembed' => true, - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $data = json_decode( $legacy_controller->dispatch( $request ), true ); - - $this->assertTrue( is_array( $data ) ); - - $this->assertArrayHasKey( 'version', $data ); - $this->assertArrayHasKey( 'provider_name', $data ); - $this->assertArrayHasKey( 'provider_url', $data ); - $this->assertArrayHasKey( 'author_name', $data ); - $this->assertArrayHasKey( 'author_url', $data ); - $this->assertArrayHasKey( 'title', $data ); - $this->assertArrayHasKey( 'type', $data ); - $this->assertArrayHasKey( 'width', $data ); - - $this->assertEquals( '1.0', $data['version'] ); - $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] ); - $this->assertEquals( get_home_url(), $data['provider_url'] ); - $this->assertEquals( $user->display_name, $data['author_name'] ); - $this->assertEquals( get_author_posts_url( $user->ID, $user->user_nicename ), $data['author_url'] ); - $this->assertEquals( $post->post_title, $data['title'] ); - $this->assertEquals( 'rich', $data['type'] ); - $this->assertTrue( $data['width'] <= $request['maxwidth'] ); - } - - function test_request_jsonp() { - $user = self::factory()->user->create_and_get( array( - 'display_name' => 'John Doe', - ) ); - $post = self::factory()->post->create_and_get( array( - 'post_author' => $user->ID, - 'post_title' => 'Hello World', - ) ); - - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'json', - 'maxwidth' => 600, - 'callback' => 'mycallback', - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $data = $legacy_controller->dispatch( $request ); - - $this->assertEquals( 0, strpos( $data, '/**/mycallback(' ) ); - } - - function test_request_jsonp_invalid_callback() { - $user = self::factory()->user->create_and_get( array( - 'display_name' => 'John Doe', - ) ); - $post = self::factory()->post->create_and_get( array( - 'post_author' => $user->ID, - 'post_title' => 'Hello World', - ) ); - - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'json', - 'maxwidth' => 600, - 'callback' => array( 'foo', 'bar' ), - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $data = $legacy_controller->dispatch( $request ); - - $this->assertFalse( strpos( $data, '/**/' ) ); - } - - function test_request_json_invalid_data() { - $request = array( - 'callback' => '', - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->json_response( null, $request ) ); - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->json_response( 123, $request ) ); - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->json_response( array(), $request ) ); - } - - function test_request_xml() { - $user = self::factory()->user->create_and_get( array( - 'display_name' => 'John Doe', - ) ); - $post = self::factory()->post->create_and_get( array( - 'post_author' => $user->ID, - 'post_title' => 'Hello World', - ) ); - - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'xml', - 'maxwidth' => 400, - 'callback' => '', - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $data = $legacy_controller->dispatch( $request ); - - $data = simplexml_load_string( $data ); - $this->assertInstanceOf( 'SimpleXMLElement', $data ); - - $data = (array) $data; - - $this->assertArrayHasKey( 'version', $data ); - $this->assertArrayHasKey( 'provider_name', $data ); - $this->assertArrayHasKey( 'provider_url', $data ); - $this->assertArrayHasKey( 'author_name', $data ); - $this->assertArrayHasKey( 'author_url', $data ); - $this->assertArrayHasKey( 'title', $data ); - $this->assertArrayHasKey( 'type', $data ); - $this->assertArrayHasKey( 'width', $data ); - - $this->assertEquals( '1.0', $data['version'] ); - $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] ); - $this->assertEquals( get_home_url(), $data['provider_url'] ); - $this->assertEquals( $user->display_name, $data['author_name'] ); - $this->assertEquals( get_author_posts_url( $user->ID, $user->user_nicename ), $data['author_url'] ); - $this->assertEquals( $post->post_title, $data['title'] ); - $this->assertEquals( 'rich', $data['type'] ); - $this->assertTrue( $data['width'] <= $request['maxwidth'] ); - } - - function test_request_xml_invalid_data() { - $legacy_controller = new WP_oEmbed_Controller(); - - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->xml_response( null ) ); - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->xml_response( 123 ) ); - $this->assertEquals( get_status_header_desc( 501 ), $legacy_controller->xml_response( array() ) ); - } - /** - * @group multisite + * @var WP_REST_Server */ - function test_request_ms_child_in_root_blog() { - if ( ! is_multisite() ) { - $this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' ); - } + protected $server; - $child = self::factory()->blog->create(); + public function setUp() { + parent::setUp(); - switch_to_blog( $child ); + /** @var WP_REST_Server $wp_rest_server */ + global $wp_rest_server; + $this->server = $wp_rest_server = new Spy_REST_Server(); - $post = self::factory()->post->create_and_get( array( - 'post_title' => 'Hello Child Blog', - ) ); - - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'json', - 'maxwidth' => 600, - 'callback' => '', - ); - - $legacy_controller = new WP_oEmbed_Controller(); - - $data = json_decode( $legacy_controller->dispatch( $request ), true ); - - $this->assertInternalType( 'array', $data ); - $this->assertNotEmpty( $data ); - - restore_current_blog(); - } - - function test_get_oembed_endpoint_url() { - $this->assertEquals( home_url() . '/?oembed=true', get_oembed_endpoint_url() ); - $this->assertEquals( home_url() . '/?oembed=true', get_oembed_endpoint_url( '', 'json' ) ); - $this->assertEquals( home_url() . '/?oembed=true', get_oembed_endpoint_url( '', 'xml' ) ); - - $post_id = self::factory()->post->create(); - $url = get_permalink( $post_id ); - $url_encoded = urlencode( $url ); - - $this->assertEquals( home_url() . '/?oembed=true&url=' . $url_encoded, get_oembed_endpoint_url( $url ) ); - $this->assertEquals( home_url() . '/?oembed=true&url=' . $url_encoded . '&format=xml', get_oembed_endpoint_url( $url, 'xml' ) ); + do_action( 'rest_api_init', $this->server ); } function test_wp_oembed_ensure_format() { @@ -271,4 +76,236 @@ class Test_oEmbed_Controller extends WP_UnitTestCase { $this->assertStringEndsWith( $expected, trim( $actual ) ); } + + public function test_route_availability() { + // Check the route was registered correctly. + $filtered_routes = $this->server->get_routes(); + $this->assertArrayHasKey( '/oembed/1.0/embed', $filtered_routes ); + $route = $filtered_routes['/oembed/1.0/embed']; + $this->assertCount( 1, $route ); + $this->assertArrayHasKey( 'callback', $route[0] ); + $this->assertArrayHasKey( 'methods', $route[0] ); + $this->assertArrayHasKey( 'args', $route[0] ); + } + + function test_request_with_wrong_method() { + $request = new WP_REST_Request( 'POST', '/oembed/1.0/embed' ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 'rest_no_route', $data[0]['code'] ); + } + + function test_request_without_url_param() { + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 'rest_missing_callback_param', $data[0]['code'] ); + $this->assertEquals( 'url', $data[0]['data']['params'][0] ); + } + + function test_request_with_bad_url() { + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', 'http://google.com/' ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 'oembed_invalid_url', $data[0]['code'] ); + } + + function test_request_invalid_format() { + $post_id = $this->factory()->post->create(); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post_id ) ); + $request->set_param( 'format', 'random' ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertInternalType( 'array', $data ); + $this->assertNotEmpty( $data ); + } + + function test_request_json() { + $user = self::factory()->user->create_and_get( array( + 'display_name' => 'John Doe', + ) ); + $post = self::factory()->post->create_and_get( array( + 'post_author' => $user->ID, + 'post_title' => 'Hello World', + ) ); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'maxwidth', 400 ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertInternalType( 'array', $data ); + $this->assertNotEmpty( $data ); + + $this->assertArrayHasKey( 'version', $data ); + $this->assertArrayHasKey( 'provider_name', $data ); + $this->assertArrayHasKey( 'provider_url', $data ); + $this->assertArrayHasKey( 'author_name', $data ); + $this->assertArrayHasKey( 'author_url', $data ); + $this->assertArrayHasKey( 'title', $data ); + $this->assertArrayHasKey( 'type', $data ); + $this->assertArrayHasKey( 'width', $data ); + + $this->assertEquals( '1.0', $data['version'] ); + $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] ); + $this->assertEquals( get_home_url(), $data['provider_url'] ); + $this->assertEquals( $user->display_name, $data['author_name'] ); + $this->assertEquals( get_author_posts_url( $user->ID, $user->user_nicename ), $data['author_url'] ); + $this->assertEquals( $post->post_title, $data['title'] ); + $this->assertEquals( 'rich', $data['type'] ); + $this->assertTrue( $data['width'] <= $request['maxwidth'] ); + } + + function test_request_xml() { + $user = self::factory()->user->create_and_get( array( + 'display_name' => 'John Doe', + ) ); + $post = self::factory()->post->create_and_get( array( + 'post_author' => $user->ID, + 'post_title' => 'Hello World', + ) ); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'format', 'xml' ); + $request->set_param( 'maxwidth', 400 ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertInternalType( 'array', $data ); + $this->assertNotEmpty( $data ); + + $this->assertArrayHasKey( 'version', $data ); + $this->assertArrayHasKey( 'provider_name', $data ); + $this->assertArrayHasKey( 'provider_url', $data ); + $this->assertArrayHasKey( 'author_name', $data ); + $this->assertArrayHasKey( 'author_url', $data ); + $this->assertArrayHasKey( 'title', $data ); + $this->assertArrayHasKey( 'type', $data ); + $this->assertArrayHasKey( 'width', $data ); + + $this->assertEquals( '1.0', $data['version'] ); + $this->assertEquals( get_bloginfo( 'name' ), $data['provider_name'] ); + $this->assertEquals( get_home_url(), $data['provider_url'] ); + $this->assertEquals( $user->display_name, $data['author_name'] ); + $this->assertEquals( get_author_posts_url( $user->ID, $user->user_nicename ), $data['author_url'] ); + $this->assertEquals( $post->post_title, $data['title'] ); + $this->assertEquals( 'rich', $data['type'] ); + $this->assertTrue( $data['width'] <= $request['maxwidth'] ); + } + + /** + * @group multisite + */ + function test_request_ms_child_in_root_blog() { + if ( ! is_multisite() ) { + $this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' ); + } + + $child = self::factory()->blog->create(); + switch_to_blog( $child ); + + $post = self::factory()->post->create_and_get( array( + 'post_title' => 'Hello Child Blog', + ) ); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'maxwidth', 400 ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertInternalType( 'array', $data ); + $this->assertNotEmpty( $data ); + + restore_current_blog(); + } + + function test_rest_pre_serve_request() { + $user = $this->factory()->user->create_and_get( array( + 'display_name' => 'John Doe', + ) ); + $post = $this->factory()->post->create_and_get( array( + 'post_author' => $user->ID, + 'post_title' => 'Hello World', + ) ); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'format', 'xml' ); + + $response = $this->server->dispatch( $request ); + $output = get_echo( '_oembed_rest_pre_serve_request', array( true, $response, $request, $this->server ) ); + + $xml = simplexml_load_string( $output ); + $this->assertInstanceOf( 'SimpleXMLElement', $xml ); + } + + function test_rest_pre_serve_request_wrong_format() { + $post = $this->factory()->post->create_and_get(); + + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'format', 'json' ); + + $response = $this->server->dispatch( $request ); + + $this->assertTrue( _oembed_rest_pre_serve_request( true, $response, $request, $this->server ) ); + } + + function test_rest_pre_serve_request_wrong_method() { + $post = $this->factory()->post->create_and_get(); + + $request = new WP_REST_Request( 'HEAD', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'format', 'xml' ); + + $response = $this->server->dispatch( $request ); + + $this->assertTrue( _oembed_rest_pre_serve_request( true, $response, $request, $this->server ) ); + } + + function test_get_oembed_endpoint_url() { + $this->assertEquals( home_url() . '/?rest_route=/oembed/1.0/embed', get_oembed_endpoint_url() ); + $this->assertEquals( home_url() . '/?rest_route=/oembed/1.0/embed', get_oembed_endpoint_url( '', 'json' ) ); + $this->assertEquals( home_url() . '/?rest_route=/oembed/1.0/embed', get_oembed_endpoint_url( '', 'xml' ) ); + + $post_id = $this->factory()->post->create(); + $url = get_permalink( $post_id ); + $url_encoded = urlencode( $url ); + + $this->assertEquals( home_url() . '/?rest_route=%2Foembed%2F1.0%2Fembed&url=' . $url_encoded, get_oembed_endpoint_url( $url ) ); + $this->assertEquals( home_url() . '/?rest_route=%2Foembed%2F1.0%2Fembed&url=' . $url_encoded . '&format=xml', get_oembed_endpoint_url( $url, 'xml' ) ); + } + + function test_get_oembed_endpoint_url_pretty_permalinks() { + update_option( 'permalink_structure', '/%postname%' ); + + $this->assertEquals( home_url() . '/wp-json/oembed/1.0/embed', get_oembed_endpoint_url() ); + $this->assertEquals( home_url() . '/wp-json/oembed/1.0/embed', get_oembed_endpoint_url( '', 'xml' ) ); + + $post_id = $this->factory()->post->create(); + $url = get_permalink( $post_id ); + $url_encoded = urlencode( $url ); + + $this->assertEquals( home_url() . '/wp-json/oembed/1.0/embed?url=' . $url_encoded, get_oembed_endpoint_url( $url ) ); + $this->assertEquals( home_url() . '/wp-json/oembed/1.0/embed?url=' . $url_encoded . '&format=xml', get_oembed_endpoint_url( $url, 'xml' ) ); + + update_option( 'permalink_structure', '' ); + } } diff --git a/tests/phpunit/tests/oembed/discovery.php b/tests/phpunit/tests/oembed/discovery.php index e7615f9056..63eb5bbf3a 100644 --- a/tests/phpunit/tests/oembed/discovery.php +++ b/tests/phpunit/tests/oembed/discovery.php @@ -5,26 +5,18 @@ */ class Tests_oEmbed_Discovery extends WP_UnitTestCase { function test_add_oembed_discovery_links_non_singular() { - ob_start(); - wp_oembed_add_discovery_links(); - $actual = ob_get_clean(); - $this->assertSame( '', $actual ); + $this->assertSame( '', get_echo( 'wp_oembed_add_discovery_links' ) ); } function test_add_oembed_discovery_links_to_post() { $post_id = self::factory()->post->create(); $this->go_to( get_permalink( $post_id ) ); - $this->assertQueryTrue( 'is_single', 'is_singular' ); - ob_start(); - wp_oembed_add_discovery_links(); - $actual = ob_get_clean(); - $expected = '' . "\n"; $expected .= '' . "\n"; - $this->assertEquals( $expected, $actual ); + $this->assertEquals( $expected, get_echo( 'wp_oembed_add_discovery_links' ) ); } function test_add_oembed_discovery_links_to_page() { @@ -32,17 +24,12 @@ class Tests_oEmbed_Discovery extends WP_UnitTestCase { 'post_type' => 'page' )); $this->go_to( get_permalink( $post_id ) ); - $this->assertQueryTrue( 'is_page', 'is_singular' ); - ob_start(); - wp_oembed_add_discovery_links(); - $actual = ob_get_clean(); - $expected = '' . "\n"; $expected .= '' . "\n"; - $this->assertEquals( $expected, $actual ); + $this->assertEquals( $expected, get_echo( 'wp_oembed_add_discovery_links' ) ); } function test_add_oembed_discovery_links_to_attachment() { @@ -53,16 +40,11 @@ class Tests_oEmbed_Discovery extends WP_UnitTestCase { ) ); $this->go_to( get_permalink( $attachment_id ) ); - $this->assertQueryTrue( 'is_attachment', 'is_singular', 'is_single' ); - ob_start(); - wp_oembed_add_discovery_links(); - $actual = ob_get_clean(); - $expected = '' . "\n"; $expected .= '' . "\n"; - $this->assertEquals( $expected, $actual ); + $this->assertEquals( $expected, get_echo( 'wp_oembed_add_discovery_links' ) ); } } diff --git a/tests/phpunit/tests/oembed/headers.php b/tests/phpunit/tests/oembed/headers.php index 3c9f80fcc6..b0bc2c89f1 100644 --- a/tests/phpunit/tests/oembed/headers.php +++ b/tests/phpunit/tests/oembed/headers.php @@ -7,57 +7,24 @@ * @group oembed-headers */ class Tests_oEmbed_HTTP_Headers extends WP_UnitTestCase { - function test_request_json_response_headers() { + function test_rest_pre_serve_request_headers() { if ( ! function_exists( 'xdebug_get_headers' ) ) { $this->markTestSkipped( 'xdebug is required for this test' ); } - $post = self::factory()->post->create_and_get( array( + $post = $this->factory()->post->create_and_get( array( 'post_title' => 'Hello World', ) ); - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'json', - 'maxwidth' => 600, - 'callback' => '', - ); + $request = new WP_REST_Request( 'GET', '/oembed/1.0/embed' ); + $request->set_param( 'url', get_permalink( $post->ID ) ); + $request->set_param( 'format', 'xml' ); - $legacy_controller = new WP_oEmbed_Controller(); - $legacy_controller->dispatch( $request ); + $server = new WP_REST_Server(); + $response = $server->dispatch( $request ); + $output = get_echo( '_oembed_rest_pre_serve_request', array( true, $response, $request, $server ) ); - $headers = xdebug_get_headers(); - - $this->assertTrue( in_array( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ), $headers ) ); - $this->assertTrue( in_array( 'X-Content-Type-Options: nosniff', $headers ) ); - - $request['callback'] = 'foobar'; - - $legacy_controller->dispatch( $request ); - - $headers = xdebug_get_headers(); - - $this->assertTrue( in_array( 'Content-Type: application/javascript; charset=' . get_option( 'blog_charset' ), $headers ) ); - $this->assertTrue( in_array( 'X-Content-Type-Options: nosniff', $headers ) ); - } - - function test_request_xml_response_headers() { - if ( ! function_exists( 'xdebug_get_headers' ) ) { - $this->markTestSkipped( 'xdebug is required for this test' ); - } - - $post = self::factory()->post->create_and_get( array( - 'post_title' => 'Hello World', - ) ); - - $request = array( - 'url' => get_permalink( $post->ID ), - 'format' => 'xml', - 'maxwidth' => 600, - ); - - $legacy_controller = new WP_oEmbed_Controller(); - $legacy_controller->dispatch( $request ); + $this->assertNotEmpty( $output ); $headers = xdebug_get_headers(); diff --git a/tests/phpunit/tests/oembed/template.php b/tests/phpunit/tests/oembed/template.php index 04a07ba984..644a6d52b6 100644 --- a/tests/phpunit/tests/oembed/template.php +++ b/tests/phpunit/tests/oembed/template.php @@ -249,9 +249,7 @@ class Tests_Embed_Template extends WP_UnitTestCase { } function test_add_host_js() { - ob_start(); wp_oembed_add_host_js(); - ob_end_clean(); $this->assertTrue( wp_script_is( 'wp-embed' ) ); }