Changeset 40628
- Timestamp:
- 05/11/2017 06:18:00 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-oembed.php
r40574 r40628 320 320 321 321 /** 322 * Takes a URL and attempts to return the oEmbed data. 323 * 324 * @see WP_oEmbed::fetch() 325 * 326 * @since 4.8.0 327 * @access public 328 * 329 * @param string $url The URL to the content that should be attempted to be embedded. 330 * @param array|string $args Optional. Arguments, usually passed from a shortcode. Default empty. 331 * @return false|object False on failure, otherwise the result in the form of an object. 332 */ 333 public function get_data( $url, $args = '' ) { 334 $args = wp_parse_args( $args ); 335 336 $provider = $this->get_provider( $url, $args ); 337 338 if ( ! $provider ) { 339 return false; 340 } 341 342 $data = $this->fetch( $provider, $url, $args ); 343 344 if ( false === $data ) { 345 return false; 346 } 347 348 return $data; 349 } 350 351 /** 322 352 * The do-it-all function that takes a URL and attempts to return the HTML. 323 353 * … … 333 363 */ 334 364 public function get_html( $url, $args = '' ) { 335 $args = wp_parse_args( $args );336 337 365 /** 338 366 * Filters the oEmbed result before any HTTP requests are made. … … 356 384 } 357 385 358 $ provider = $this->get_provider( $url, $args );359 360 if ( ! $provider || false === $data = $this->fetch( $provider, $url, $args )) {386 $data = $this->get_data( $url, $args ); 387 388 if ( false === $data ) { 361 389 return false; 362 390 } -
trunk/src/wp-includes/class-wp-oembed-controller.php
r37568 r40628 53 53 ), 54 54 ) ); 55 56 register_rest_route( 'oembed/1.0', '/proxy', array( 57 array( 58 'methods' => WP_REST_Server::READABLE, 59 'callback' => array( $this, 'get_proxy_item' ), 60 'permission_callback' => array( $this, 'get_proxy_item_permissions_check' ), 61 'args' => array( 62 'url' => array( 63 'description' => __( 'The URL of the resource for which to fetch oEmbed data.' ), 64 'type' => 'string', 65 'required' => true, 66 'sanitize_callback' => 'esc_url_raw', 67 ), 68 'format' => array( 69 'description' => __( 'The oEmbed format to use.' ), 70 'type' => 'string', 71 'default' => 'json', 72 'enum' => array( 73 'json', 74 'xml', 75 ), 76 ), 77 'maxwidth' => array( 78 'description' => __( 'The maximum width of the embed frame in pixels.' ), 79 'type' => 'integer', 80 'default' => $maxwidth, 81 'sanitize_callback' => 'absint', 82 ), 83 'maxheight' => array( 84 'description' => __( 'The maximum height of the embed frame in pixels.' ), 85 'type' => 'integer', 86 'sanitize_callback' => 'absint', 87 ), 88 'discover' => array( 89 'description' => __( 'Whether to perform an oEmbed discovery request for non-whitelisted providers.' ), 90 'type' => 'boolean', 91 'default' => true, 92 ), 93 ), 94 ), 95 ) ); 55 96 } 56 97 57 98 /** 58 * Callback for the API endpoint.99 * Callback for the embed API endpoint. 59 100 * 60 101 * Returns the JSON object for the post. … … 87 128 return $data; 88 129 } 130 131 /** 132 * Checks if current user can make a proxy oEmbed request. 133 * 134 * @since 4.8.0 135 * @access public 136 * 137 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 138 */ 139 public function get_proxy_item_permissions_check() { 140 if ( ! current_user_can( 'edit_posts' ) ) { 141 return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to make proxied oEmbed requests.' ), array( 'status' => rest_authorization_required_code() ) ); 142 } 143 return true; 144 } 145 146 /** 147 * Callback for the proxy API endpoint. 148 * 149 * Returns the JSON object for the proxied item. 150 * 151 * @since 4.8.0 152 * @access public 153 * 154 * @see WP_oEmbed::get_html() 155 * @param WP_REST_Request $request Full data about the request. 156 * @return WP_Error|array oEmbed response data or WP_Error on failure. 157 */ 158 public function get_proxy_item( $request ) { 159 $args = $request->get_params(); 160 161 // Serve oEmbed data from cache if set. 162 $cache_key = 'oembed_' . md5( serialize( $args ) ); 163 $data = get_transient( $cache_key ); 164 if ( ! empty( $data ) ) { 165 return $data; 166 } 167 168 $url = $request['url']; 169 unset( $args['url'] ); 170 171 $data = _wp_oembed_get_object()->get_data( $url, $args ); 172 173 if ( false === $data ) { 174 return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) ); 175 } 176 177 /** 178 * Filters the oEmbed TTL value (time to live). 179 * 180 * Similar to the {@see 'oembed_ttl'} filter, but for the REST API 181 * oEmbed proxy endpoint. 182 * 183 * @since 4.8.0 184 * 185 * @param int $time Time to live (in seconds). 186 * @param string $url The attempted embed URL. 187 * @param array $args An array of embed request arguments. 188 */ 189 $ttl = apply_filters( 'rest_oembed_ttl', DAY_IN_SECONDS, $url, $args ); 190 191 set_transient( $cache_key, $data, $ttl ); 192 193 return $data; 194 } 89 195 } -
trunk/src/wp-includes/js/media-views.js
r40573 r40628 4625 4625 4626 4626 fetch: function() { 4627 var embed;4628 4627 4629 4628 // check if they haven't typed in 500 ms … … 4636 4635 } 4637 4636 4638 embed = new wp.shortcode({4639 tag: 'embed',4640 attrs: _.pick( this.model.attributes, [ 'width', 'height', 'src' ] ),4641 content: this.model.get('url')4642 });4643 4644 4637 this.dfd = $.ajax({ 4645 type: 'POST', 4646 url: wp.ajax.settings.url, 4647 context: this, 4648 data: { 4649 action: 'parse-embed', 4650 post_ID: wp.media.view.settings.post.id, 4651 shortcode: embed.string() 4652 } 4638 url: wp.media.view.settings.oEmbedProxyUrl, 4639 data: { 4640 url: this.model.get( 'url' ), 4641 maxwidth: this.model.get( 'width' ), 4642 maxheight: this.model.get( 'height' ), 4643 _wpnonce: wp.media.view.settings.nonce.wpRestApi 4644 }, 4645 type: 'GET', 4646 dataType: 'json', 4647 context: this 4653 4648 }) 4654 .done( this.renderoEmbed ) 4649 .done( function( response ) { 4650 this.renderoEmbed( { 4651 data: { 4652 body: response.html || '' 4653 } 4654 } ); 4655 } ) 4655 4656 .fail( this.renderFail ); 4656 4657 }, -
trunk/src/wp-includes/js/media/views/embed/link.js
r33337 r40628 36 36 37 37 fetch: function() { 38 var embed;39 38 40 39 // check if they haven't typed in 500 ms … … 47 46 } 48 47 49 embed = new wp.shortcode({50 tag: 'embed',51 attrs: _.pick( this.model.attributes, [ 'width', 'height', 'src' ] ),52 content: this.model.get('url')53 });54 55 48 this.dfd = $.ajax({ 56 type: 'POST', 57 url: wp.ajax.settings.url, 58 context: this, 59 data: { 60 action: 'parse-embed', 61 post_ID: wp.media.view.settings.post.id, 62 shortcode: embed.string() 63 } 49 url: wp.media.view.settings.oEmbedProxyUrl, 50 data: { 51 url: this.model.get( 'url' ), 52 maxwidth: this.model.get( 'width' ), 53 maxheight: this.model.get( 'height' ), 54 _wpnonce: wp.media.view.settings.nonce.wpRestApi 55 }, 56 type: 'GET', 57 dataType: 'json', 58 context: this 64 59 }) 65 .done( this.renderoEmbed ) 60 .done( function( response ) { 61 this.renderoEmbed( { 62 data: { 63 body: response.html || '' 64 } 65 } ); 66 } ) 66 67 .fail( this.renderFail ); 67 68 }, -
trunk/src/wp-includes/media.php
r40573 r40628 3415 3415 'nonce' => array( 3416 3416 'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ), 3417 'wpRestApi' => wp_create_nonce( 'wp_rest' ), 3417 3418 ), 3418 3419 'post' => array( … … 3424 3425 'video' => ( $show_video_playlist ) ? 1 : 0, 3425 3426 ), 3427 'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ), 3426 3428 'embedExts' => $exts, 3427 3429 'embedMimes' => $ext_mimes, -
trunk/tests/phpunit/tests/oembed/controller.php
r40564 r40628 10 10 */ 11 11 protected $server; 12 protected static $editor; 13 protected static $subscriber; 14 const YOUTUBE_VIDEO_ID = 'OQSNhk5ICTI'; 15 const INVALID_OEMBED_URL = 'https://www.notreallyanoembedprovider.com/watch?v=awesome-cat-video'; 16 17 public static function wpSetUpBeforeClass( $factory ) { 18 self::$subscriber = $factory->user->create( array( 19 'role' => 'subscriber', 20 ) ); 21 self::$editor = $factory->user->create( array( 22 'role' => 'editor', 23 'user_email' => 'editor@example.com', 24 ) ); 25 } 26 27 public static function wpTearDownAfterClass() { 28 self::delete_user( self::$subscriber ); 29 self::delete_user( self::$editor ); 30 } 12 31 13 32 public function setUp() { … … 19 38 20 39 do_action( 'rest_api_init', $this->server ); 40 add_filter( 'pre_http_request', array( $this, 'mock_embed_request' ), 10, 3 ); 41 $this->request_count = 0; 42 } 43 44 public function tearDown() { 45 parent::tearDown(); 46 47 remove_filter( 'pre_http_request', array( $this, 'mock_embed_request' ), 10, 3 ); 48 } 49 50 /** 51 * Count of the number of requests attempted. 52 * 53 * @var int 54 */ 55 public $request_count = 0; 56 57 /** 58 * Intercept oEmbed requests and mock responses. 59 * 60 * @param mixed $preempt Whether to preempt an HTTP request's return value. Default false. 61 * @param mixed $r HTTP request arguments. 62 * @param string $url The request URL. 63 * @return array Response data. 64 */ 65 public function mock_embed_request( $preempt, $r, $url ) { 66 unset( $preempt, $r ); 67 68 $this->request_count += 1; 69 70 // Mock request to YouTube Embed. 71 if ( false !== strpos( $url, self::YOUTUBE_VIDEO_ID ) ) { 72 return array( 73 'response' => array( 74 'code' => 200, 75 ), 76 'body' => wp_json_encode( 77 array( 78 'version' => '1.0', 79 'type' => 'video', 80 'provider_name' => 'YouTube', 81 'provider_url' => 'https://www.youtube.com', 82 'thumbnail_width' => 480, 83 'width' => 500, 84 'thumbnail_height' => 360, 85 'html' => '<iframe width="500" height="375" src="https://www.youtube.com/embed/' . self::YOUTUBE_VIDEO_ID . '?feature=oembed" frameborder="0" allowfullscreen></iframe>', 86 'author_name' => 'Yosemitebear62', 87 'thumbnail_url' => 'https://i.ytimg.com/vi/' . self::YOUTUBE_VIDEO_ID . '/hqdefault.jpg', 88 'title' => 'Yosemitebear Mountain Double Rainbow 1-8-10', 89 'height' => 375, 90 ) 91 ), 92 ); 93 } else { 94 return array( 95 'response' => array( 96 'code' => 404, 97 ), 98 ); 99 } 21 100 } 22 101 … … 87 166 $this->assertArrayHasKey( 'methods', $route[0] ); 88 167 $this->assertArrayHasKey( 'args', $route[0] ); 168 169 // Check proxy route registration. 170 $this->assertArrayHasKey( '/oembed/1.0/proxy', $filtered_routes ); 171 $proxy_route = $filtered_routes['/oembed/1.0/proxy']; 172 $this->assertCount( 1, $proxy_route ); 173 $this->assertArrayHasKey( 'callback', $proxy_route[0] ); 174 $this->assertArrayHasKey( 'permission_callback', $proxy_route[0] ); 175 $this->assertArrayHasKey( 'methods', $proxy_route[0] ); 176 $this->assertArrayHasKey( 'args', $proxy_route[0] ); 89 177 } 90 178 … … 349 437 update_option( 'permalink_structure', '' ); 350 438 } 439 440 public function test_proxy_without_permission() { 441 // Test without a login. 442 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 443 $response = $this->server->dispatch( $request ); 444 445 $this->assertEquals( 400, $response->get_status() ); 446 447 // Test with a user that does not have edit_posts capability. 448 wp_set_current_user( self::$subscriber ); 449 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 450 $request->set_param( 'url', self::INVALID_OEMBED_URL ); 451 $response = $this->server->dispatch( $request ); 452 453 $this->assertEquals( 403, $response->get_status() ); 454 $data = $response->get_data(); 455 $this->assertEquals( $data['code'], 'rest_forbidden' ); 456 } 457 458 public function test_proxy_with_invalid_oembed_provider() { 459 wp_set_current_user( self::$editor ); 460 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 461 $request->set_param( 'url', self::INVALID_OEMBED_URL ); 462 $response = $this->server->dispatch( $request ); 463 $this->assertEquals( 404, $response->get_status() ); 464 $data = $response->get_data(); 465 $this->assertEquals( 'oembed_invalid_url', $data['code'] ); 466 } 467 468 public function test_proxy_with_invalid_type() { 469 wp_set_current_user( self::$editor ); 470 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 471 $request->set_param( 'type', 'xml' ); 472 $response = $this->server->dispatch( $request ); 473 474 $this->assertEquals( 400, $response->get_status() ); 475 $data = $response->get_data(); 476 } 477 478 public function test_proxy_with_valid_oembed_provider() { 479 wp_set_current_user( self::$editor ); 480 481 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 482 $request->set_param( 'url', 'https://www.youtube.com/watch?v=' . self::YOUTUBE_VIDEO_ID ); 483 $response = $this->server->dispatch( $request ); 484 $this->assertEquals( 200, $response->get_status() ); 485 $this->assertEquals( 1, $this->request_count ); 486 487 // Subsequent request is cached and so it should not cause a request. 488 $response = $this->server->dispatch( $request ); 489 $this->assertEquals( 1, $this->request_count ); 490 491 // Test data object. 492 $data = $response->get_data(); 493 494 $this->assertNotEmpty( $data ); 495 $this->assertTrue( is_object( $data ) ); 496 $this->assertEquals( 'YouTube', $data->provider_name ); 497 $this->assertEquals( 'https://i.ytimg.com/vi/' . self::YOUTUBE_VIDEO_ID . '/hqdefault.jpg', $data->thumbnail_url ); 498 } 499 500 public function test_proxy_with_invalid_oembed_provider_no_discovery() { 501 wp_set_current_user( self::$editor ); 502 503 // If discover is false for an unkown provider, no discovery request should take place. 504 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 505 $request->set_param( 'url', self::INVALID_OEMBED_URL ); 506 $request->set_param( 'discover', 0 ); 507 $response = $this->server->dispatch( $request ); 508 $this->assertEquals( 404, $response->get_status() ); 509 $this->assertEquals( 0, $this->request_count ); 510 } 511 512 public function test_proxy_with_invalid_oembed_provider_with_default_discover_param() { 513 wp_set_current_user( self::$editor ); 514 515 // For an unkown provider, a discovery request should happen. 516 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 517 $request->set_param( 'url', self::INVALID_OEMBED_URL ); 518 $response = $this->server->dispatch( $request ); 519 $this->assertEquals( 404, $response->get_status() ); 520 $this->assertEquals( 1, $this->request_count ); 521 } 522 523 public function test_proxy_with_invalid_discover_param() { 524 wp_set_current_user( self::$editor ); 525 $request = new WP_REST_Request( 'GET', '/oembed/1.0/proxy' ); 526 $request->set_param( 'url', self::INVALID_OEMBED_URL ); 527 $request->set_param( 'discover', 'notaboolean' ); 528 529 $response = $this->server->dispatch( $request ); 530 531 $this->assertEquals( 400, $response->get_status() ); 532 $data = $response->get_data(); 533 $this->assertEquals( $data['code'], 'rest_invalid_param' ); 534 } 351 535 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
r40341 r40628 44 44 '/oembed/1.0', 45 45 '/oembed/1.0/embed', 46 '/oembed/1.0/proxy', 46 47 '/wp/v2', 47 48 '/wp/v2/posts', … … 165 166 'route' => '/oembed/1.0/embed', 166 167 'name' => 'oembeds', 168 ), 169 array( 170 'route' => '/oembed/1.0/proxy', 171 'name' => 'oembedProxy', 167 172 ), 168 173 array( -
trunk/tests/qunit/fixtures/wp-api-generated.js
r40605 r40628 96 96 } 97 97 }, 98 "/oembed/1.0/proxy": { 99 "namespace": "oembed/1.0", 100 "methods": [ 101 "GET" 102 ], 103 "endpoints": [ 104 { 105 "methods": [ 106 "GET" 107 ], 108 "args": { 109 "url": { 110 "required": true, 111 "description": "The URL of the resource for which to fetch oEmbed data.", 112 "type": "string" 113 }, 114 "format": { 115 "required": false, 116 "default": "json", 117 "enum": [ 118 "json", 119 "xml" 120 ], 121 "description": "The oEmbed format to use.", 122 "type": "string" 123 }, 124 "maxwidth": { 125 "required": false, 126 "default": 600, 127 "description": "The maximum width of the embed frame in pixels.", 128 "type": "integer" 129 }, 130 "maxheight": { 131 "required": false, 132 "description": "The maximum height of the embed frame in pixels.", 133 "type": "integer" 134 }, 135 "discover": { 136 "required": false, 137 "default": true, 138 "description": "Whether to perform an oEmbed discovery request for non-whitelisted providers.", 139 "type": "boolean" 140 } 141 } 142 } 143 ], 144 "_links": { 145 "self": "http://example.org/?rest_route=/oembed/1.0/proxy" 146 } 147 }, 98 148 "/wp/v2": { 99 149 "namespace": "wp/v2", … … 3397 3447 "self": "http://example.org/?rest_route=/oembed/1.0/embed" 3398 3448 } 3449 }, 3450 "/oembed/1.0/proxy": { 3451 "namespace": "oembed/1.0", 3452 "methods": [ 3453 "GET" 3454 ], 3455 "endpoints": [ 3456 { 3457 "methods": [ 3458 "GET" 3459 ], 3460 "args": { 3461 "url": { 3462 "required": true, 3463 "description": "The URL of the resource for which to fetch oEmbed data.", 3464 "type": "string" 3465 }, 3466 "format": { 3467 "required": false, 3468 "default": "json", 3469 "enum": [ 3470 "json", 3471 "xml" 3472 ], 3473 "description": "The oEmbed format to use.", 3474 "type": "string" 3475 }, 3476 "maxwidth": { 3477 "required": false, 3478 "default": 600, 3479 "description": "The maximum width of the embed frame in pixels.", 3480 "type": "integer" 3481 }, 3482 "maxheight": { 3483 "required": false, 3484 "description": "The maximum height of the embed frame in pixels.", 3485 "type": "integer" 3486 }, 3487 "discover": { 3488 "required": false, 3489 "default": true, 3490 "description": "Whether to perform an oEmbed discovery request for non-whitelisted providers.", 3491 "type": "boolean" 3492 } 3493 } 3494 } 3495 ], 3496 "_links": { 3497 "self": "http://example.org/?rest_route=/oembed/1.0/proxy" 3498 } 3399 3499 } 3400 3500 } … … 3402 3502 3403 3503 mockedApiResponse.oembeds = { 3504 "code": "rest_missing_callback_param", 3505 "message": "Missing parameter(s): url", 3506 "data": { 3507 "status": 400, 3508 "params": [ 3509 "url" 3510 ] 3511 } 3512 }; 3513 3514 mockedApiResponse.oembedProxy = { 3404 3515 "code": "rest_missing_callback_param", 3405 3516 "message": "Missing parameter(s): url",
Note: See TracChangeset
for help on using the changeset viewer.