From 4832d8d9330f8fde2599c95f664af37ee6c0d42e Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Thu, 29 Oct 2015 22:50:13 +0000 Subject: [PATCH] Embeds: Who put this REST API infrastructure in my WordPress? Well, while it's here, we probably should make use of it. The oEmbed endpoint now uses the REST API infrastructure, instead of providing its own. Props swissspidy. Fixes #34207. git-svn-id: https://develop.svn.wordpress.org/trunk@35436 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-oembed-controller.php | 143 ++---- src/wp-includes/class-wp.php | 2 +- src/wp-includes/default-filters.php | 43 +- src/wp-includes/embed-functions.php | 80 +++- tests/phpunit/tests/oembed/controller.php | 445 ++++++++++-------- tests/phpunit/tests/oembed/discovery.php | 26 +- tests/phpunit/tests/oembed/headers.php | 51 +- tests/phpunit/tests/oembed/template.php | 2 - 8 files changed, 371 insertions(+), 421 deletions(-) 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' ) ); }