From 5a107cefc4e5a5f8389e3b0e952260570f7d6719 Mon Sep 17 00:00:00 2001 From: Igor Zinovyev Date: Mon, 22 Jan 2018 23:49:04 +0300 Subject: [PATCH] Merges changes to the GettyImages shortcode from WordPress.com. This is a merge of changes from WordPress.com related to the new format of markup returned by GettyImages oembed endpoints. D9498-wpcom is the mirror change. --- modules/shortcodes/getty.php | 119 ++++++++++++++---- .../modules/shortcodes/test_class.getty.php | 116 +++++++++++++++-- 2 files changed, 200 insertions(+), 35 deletions(-) diff --git a/modules/shortcodes/getty.php b/modules/shortcodes/getty.php index 3fa4956a46a3d..acdc18f5cd2d4 100644 --- a/modules/shortcodes/getty.php +++ b/modules/shortcodes/getty.php @@ -27,8 +27,8 @@ function jetpack_getty_enable_embeds( $site = 'wpcom' ) { : 'wordpress.com'; // Support their oEmbed Endpoint - wp_oembed_add_provider( '#https?://www\.gettyimages\.com/detail/.*#i', "https://embed.gettyimages.com/oembed/?caller=$caller", true ); - wp_oembed_add_provider( '#https?://(www\.)?gty\.im/.*#i', "https://embed.gettyimages.com/oembed/?caller=$caller", true ); + wp_oembed_add_provider( '#https?://www\.gettyimages\.com/detail/.*#i', "https://embed.gettyimages.com/oembed/", true ); + wp_oembed_add_provider( '#https?://(www\.)?gty\.im/.*#i', "https://embed.gettyimages.com/oembed/", true ); // Allow iframes to be filtered to short code (so direct copy+paste can be done) add_filter( 'pre_kses', 'wpcom_shortcodereverse_getty' ); @@ -37,6 +37,41 @@ function jetpack_getty_enable_embeds( $site = 'wpcom' ) { add_shortcode( 'getty', 'jetpack_getty_shortcode' ); } +/** + * Filters the oEmbed provider URL for Getty URLs to include site URL host as + * caller if available, falling back to "wordpress.com". Must be applied at + * time of embed in case that `init` is too early (WP.com REST API). + * + * @module shortcodes + * + * @since 5.8.0 + * + * @see WP_oEmbed::fetch + * + * @return string oEmbed provider URL + */ +add_filter( 'oembed_fetch_url', 'getty_add_oembed_endpoint_caller' ); + +function getty_add_oembed_endpoint_caller( $provider ) { + // By time filter is called, original provider URL has had url, maxwidth, + // maxheight query parameters added. + if ( 0 !== strpos( $provider, 'https://embed.gettyimages.com/oembed/' ) ) { + return $provider; + } + + // Only include caller for non-private sites + if ( ! function_exists( 'is_private_blog' ) || ! is_private_blog() ) { + $host = parse_url( get_bloginfo( 'url' ), PHP_URL_HOST ); + } + + // Fall back to WordPress.com + if ( empty( $host ) ) { + $host = 'wordpress.com'; + } + + return add_query_arg( 'caller', $host, $provider ); +} + /** * Compose shortcode based on Getty iframes. * @@ -47,39 +82,73 @@ function jetpack_getty_enable_embeds( $site = 'wpcom' ) { * @return mixed */ function wpcom_shortcodereverse_getty( $content ) { - if ( ! is_string( $content ) || false === stripos( $content, 'embed.gettyimages.com/embed' ) ) { + if ( ! is_string( $content ) || false === stripos( $content, '.gettyimages.com/' ) ) { return $content; } - $regexp = '!!i'; + $regexp = '!!i'; $regexp_ent = str_replace( '&#0*58;', '&#0*58;|�*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) ); - foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) { + /** + * Context: http://wp.me/p5j4vm-1y7 + * + * @hide-in-jetpack + */ + + // Markup pattern for 2017 embed syntax with significant differences from + // the prior pattern: + $regexp_2017 = '!!'; + $regexp_2017_ent = str_replace( '&#0*58;', '&#0*58;|�*58;', htmlspecialchars( $regexp_2017, ENT_NOQUOTES ) ); + + foreach ( array( 'regexp_2017', 'regexp_2017_ent', 'regexp', 'regexp_ent' ) as $reg ) { if ( ! preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) ) { continue; } foreach ( $matches as $match ) { - $ids = esc_html( $match[3] ); - - $params = $match[5]; - - if ( 'regexp_ent' == $reg ) { - $params = html_entity_decode( $params ); + if ( 'regexp_2017' === $reg || 'regexp_2017_ent' === $reg ) { + // Extract individual keys from the matched JavaScript object + $params = $match[2]; + if ( ! preg_match_all( '!(?P\w+)\s*:\s*([\'"](?P[^\'"]*?)(px)?[\'"])!', $params, $key_matches, PREG_SET_ORDER ) ) { + continue; + } + + foreach ( $key_matches as $key_match ) { + switch ( $key_match['key'] ) { + case 'items': $ids = $key_match['value']; break; + case 'w': $width = (int) $key_match['value']; break; + case 'h': $height = (int) $key_match['value']; break; + case 'tld': $tld = $key_match['value']; break; + } + } + } else { + $params = $match[5]; + if ( 'regexp_ent' === $reg ) { + $params = html_entity_decode( $params ); + } + $params = wp_kses_hair( $params, array( 'http' ) ); + + $ids = esc_html( $match[3] ); + $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0; + $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0; } - $params = wp_kses_hair( $params, array( 'http' ) ); - - $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0; - $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0; + if ( empty( $ids ) ) { + continue; + } $shortcode = '[getty src="' . esc_attr( $ids ) . '"'; - if ( $width ) { + if ( ! empty( $width ) ) { $shortcode .= ' width="' . esc_attr( $width ) . '"'; } - if ( $height ) { + if ( ! empty( $height ) ) { $shortcode .= ' height="' . esc_attr( $height ) . '"'; } + // While it does not appear to have any practical impact, Getty has + // requested that we include TLD in the embed request + if ( ! empty( $tld ) ) { + $shortcode .= ' tld="' . esc_attr( $tld ). '"'; + } $shortcode .= ']'; $content = str_replace( $match[0], $shortcode, $content ); @@ -89,6 +158,7 @@ function wpcom_shortcodereverse_getty( $content ) { // strip out enclosing div and any other markup $regexp = '%'; + + const GETTY_EMBED_MULTI = ''; + + const GETTY_EMBED_2017 = 'Embed from Getty Images'; + + const GETTY_EMBED_2017_SLIDESHOW = 'Embed from Getty Images'; + + const GETTY_EMBED_ALPHANUMERIC_DASHED_ID = ''; + + const GETTY_OLD_EMBED = ''; + + const GETTY_ESCAPED_EMBED = '<div class="getty embed image" style="background-color:#fff;display:inline-block;font-family:\'Helvetica Neue\',Helvetica,Arial,sans-serif;color:#a7a7a7;font-size:11px;width:100%;max-width:462px;"><div style="padding:0;margin:0;text-align:left;"><a href="http://www.gettyimages.com/detail/82278805" target="_blank" style="color:#a7a7a7;text-decoration:none;font-weight:normal !important;border:none;display:inline-block;">Embed from Getty Images</a></div><div style="overflow:hidden;position:relative;height:0;padding:80.086580% 0 0 0;width:100%;"><iframe src="//embed.gettyimages.com/embed/82278805?et=wi6iT1Wqn0yYxEh6Ocx_aA&sig=G63PuQ-eKJqGCnssk8rsSu1wcGoyUsgwqL8Jfu83wis=" width="462" height="370" scrolling="no" frameborder="0" style="display:inline-block;position:absolute;top:0;left:0;width:100%;height:100%;"></iframe></div><p style="margin:0;"></p></div>'; + + const GETTY_ESCAPED_EMBED_2017 = '<a id=\'giY-P3UyQ7NgmhoUs69FfA\' class=\'gie-single\' href=\'http://www.gettyimages.com/detail/82278805\' target=\'_blank\' style=\'color:#a7a7a7;text-decoration:none;font-weight:normal !important;border:none;display:inline-block;\'>Embed from Getty Images</a><script>window.gie=window.gie||function(c){(gie.q=gie.q||[]).push(c)};gie(function(){gie.widgets.load({id:\'giY-P3UyQ7NgmhoUs69FfA\',sig:\'7_gkXdhdHtVWWsyemkD0qPEuDZVBmfepEDjfqlTi61M=\',w:\'462px\',h:\'370px\',items:\'82278805\',caption: true ,tld:\'com\',is360: false })});</script><script src=\'//embed-cdn.gettyimages.com/widgets.js\' charset=\'utf-8\' async></script>'; + + const GETTY_EXAMPLE_PROCESSED = ''; + + static function strip_url_signature_args( $str ) { + return preg_replace( '/((id=\'[:alpha:\-]+)|[\?&]|&|&)(et=[\w-]+|sig=[\w-=]+)/', '', $str ); + } + /** * Verify that [getty] exists. * @@ -11,18 +47,74 @@ public function test_shortcodes_getty_exists() { $this->assertEquals( shortcode_exists( 'getty' ), true ); } - /** - * Verify that calling do_shortcode with the shortcode doesn't return the same content. - * - * @since 4.5.0 - */ - public function test_shortcodes_getty() { - $content = '[getty]'; + function test_getty_shortcode() { + $parsed = do_shortcode( self::GETTY_SHORTCODE ); - $shortcode_content = do_shortcode( $content ); + $doc = new DOMDocument(); + $doc->loadHTML( $parsed ); + $links = $doc->getElementsByTagName( 'a' ); + + foreach( $links as $link ) { + $this->assertTrue( $link->hasAttribute( 'href' ) ); + $this->assertContains( self::GETTY_IDENTIFIER, $link->getAttribute( 'href' ) ); + } + } + + function test_getty_reverse_shortcode() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_EMBED ); + $this->assertEquals( self::GETTY_SHORTCODE, $shortcode ); + } + + function test_getty_reverse_multi_shortcode() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_EMBED_MULTI ); + $this->assertEquals( self::GETTY_SHORTCODE_MULTI, $shortcode ); + } + + function test_getty_reverse_alphanumeric_daashed_id_shortcode() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_EMBED_ALPHANUMERIC_DASHED_ID ); + $this->assertEquals( self::GETTY_SHORTCODE_ALPHANUMERIC_DASHED_ID, $shortcode ); + } + + function test_getty_reverse_shortcode_works_on_escaped_html() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_ESCAPED_EMBED ); + $this->assertEquals( self::GETTY_SHORTCODE, $shortcode ); + } - $this->assertNotEquals( $content, $shortcode_content ); - $this->assertEquals( '', $shortcode_content ); + function test_getty_reverse_shortcode_works_on_old_embed() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_OLD_EMBED ); + $this->assertEquals( self::GETTY_SHORTCODE, $shortcode ); + } + + function test_getty_reverse_shortcode_2017() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_EMBED_2017 ); + $this->assertEquals( self::GETTY_SHORTCODE_TLD, $shortcode ); + } + + function test_getty_reverse_shortcode_2017_works_on_escaped_html() { + $shortcode = wpcom_shortcodereverse_getty( self::GETTY_ESCAPED_EMBED_2017 ); + $this->assertEquals( self::GETTY_SHORTCODE_TLD, $shortcode ); + } + + function test_getty_reverse_shortcode_doesnt_remove_too_much() { + $before = '
test
another div'; + $after = 'blah
'; + $shortcode = wpcom_shortcodereverse_getty( $before . self::GETTY_EMBED . $after ); + $expected = $before . self::GETTY_SHORTCODE . $after; + $this->assertEquals( $expected, $shortcode ); + } + + function test_getty_add_oembed_endpoint_caller_non_getty() { + $provider_url = apply_filters( + 'oembed_fetch_url', + 'https://www.youtube.com/oembed?maxwidth=471&maxheight=594&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ', + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + '' + ); + + $this->assertEquals( + $provider_url, + 'https://www.youtube.com/oembed?maxwidth=471&maxheight=594&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ' + ); } /** @@ -31,13 +123,11 @@ public function test_shortcodes_getty() { * @since 4.5.0 */ public function test_shortcodes_getty_image() { - $this->markTestSkipped(); - $image_id = '82278805'; $content = "[getty src='$image_id']"; $shortcode_content = do_shortcode( $content ); - $this->assertContains( '