Skip to content

Commit

Permalink
Fix rendering of SoundCloud embeds and correct parameters for soundcl…
Browse files Browse the repository at this point in the history
…oud shortcode

* Hook into oEmbed filters for rewriting SoundCloud embeds as AMP components.
* Ensure Jetpack is active while unit tests are run.
* Explicitly install and activate Jetpack during unit tests.
* Fix error message in create-embed-test-post.php
* Fix phpcs for Soundcloud code.
  • Loading branch information
westonruter committed Dec 14, 2017
1 parent b8ba836 commit 0b729b1
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .dev-lib
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
SYNC_README_MD=0
PATH_EXCLUDES_PATTERN=includes/lib/

function after_wp_install {
echo "Installing REST API..."
svn export -q https://plugins.svn.wordpress.org/jetpack/trunk/ "$WP_CORE_DIR/src/wp-content/plugins/jetpack"
}
8 changes: 6 additions & 2 deletions bin/create-embed-test-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,13 @@
'content' => 'https://stuckincustoms.smugmug.com/Portfolio/i-GnwtS8R/A',
),
array(
'heading' => 'Soundcloud Embed',
'heading' => 'SoundCloud Embed',
'content' => 'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor',
),
array(
'heading' => 'SoundCloud Shortcode',
'content' => '[soundcloud url="https://api.soundcloud.com/tracks/89299804"]',
),
array(
'heading' => 'Speaker Deck Embed',
'content' => 'https://speakerdeck.com/caitiem20/distributed-sagas-a-protocol-for-coordinating-microservices',
Expand Down Expand Up @@ -209,7 +213,7 @@ function amp_get_media_items_ids( $type, $image_count = 3 ) {
$posts_count = count( $posts );
if ( $posts_count < $image_count ) {
WP_CLI::error( sprintf(
'%1$s "2$s" expected, %3$s found. Please make sure at least %1$s "2$s" are accessible and run this script again.',
'Please make sure at least %1$s "%2$s" attachments are accessible and run this script again.',
$image_count,
$type,
$posts_count
Expand Down
165 changes: 138 additions & 27 deletions includes/embeds/class-amp-soundcloud-embed.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,49 @@
* Class AMP_SoundCloud_Embed_Handler
*/
class AMP_SoundCloud_Embed_Handler extends AMP_Base_Embed_Handler {
const URL_PATTERN = '#https?://api\.soundcloud\.com/tracks/.*#i';

/**
* Default height.
*
* @var int
*/
protected $DEFAULT_HEIGHT = 200;

/**
* Script slug.
*
* @var string
*/
private static $script_slug = 'amp-soundcloud';

/**
* Script source.
*
* @var string
*/
private static $script_src = 'https://cdn.ampproject.org/v0/amp-soundcloud-0.1.js';

/**
* Register embed.
*/
public function register_embed() {
wp_embed_register_handler( 'amp-soundcloud', self::URL_PATTERN, array( $this, 'oembed' ), -1 );
add_shortcode( 'soundcloud', array( $this, 'shortcode' ) );
add_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10, 2 );
}

/**
* Unregister embed.
*/
public function unregister_embed() {
wp_embed_unregister_handler( 'amp-soundcloud', -1 );
remove_shortcode( 'soundcloud' );
remove_filter( 'embed_oembed_html', array( $this, 'filter_embed_oembed_html' ), 10 );
}

/**
* Get scripts needed by component.
*
* @return array Scripts.
*/
public function get_scripts() {
if ( ! $this->did_convert_elements ) {
return array();
Expand All @@ -33,21 +60,81 @@ public function get_scripts() {
return array( self::$script_slug => self::$script_src );
}

public function oembed( $matches, $attr, $url, $rawattr ) {
/**
* Render oEmbed.
*
* @see \WP_Embed::shortcode()
*
* @deprecated Core's oEmbed handler is now used instead, with embed_oembed_html filter used to convert to AMP.
* @param array $matches URL pattern matches.
* @param array $attr Shortcode attribues.
* @param string $url URL.
* @return string Rendered oEmbed.
*/
public function oembed( $matches, $attr, $url ) {
_deprecated_function( __METHOD__, '0.6.0' );
unset( $matches, $attr );
$track_id = $this->get_track_id_from_url( $url );
return $this->render( array(
'track_id' => $track_id,
) );
return $this->render( compact( 'track_id' ) );
}

public function shortcode( $attr ) {
/**
* Filter oEmbed HTML for SoundCloud to convert to AMP.
*
* @param string $cache Cache for oEmbed.
* @param string $url Embed URL. Optional.
* @return string Embed.
*/
public function filter_embed_oembed_html( $cache, $url = null ) {
$parsed_url = wp_parse_url( $url );
if ( $url && false === strpos( $parsed_url['host'], 'soundcloud.com' ) ) {
return $cache;
}
return $this->parse_amp_component_from_iframe( $cache );
}

$track_id = false;
/**
* Parse AMP component from iframe.
*
* @param string $html HTML.
* @return string AMP component or empty if unable to determine SoundCloud ID.
*/
private function parse_amp_component_from_iframe( $html ) {
$embed = '';
if ( preg_match( '#<iframe.+?src="(?P<url>.+?)".*>#', $html, $matches ) ) {
$src = html_entity_decode( $matches['url'], ENT_QUOTES );
$query = array();
parse_str( wp_parse_url( $src, PHP_URL_QUERY ), $query );
if ( ! empty( $query['url'] ) ) {
$embed = $this->render( array(
'track_id' => $this->get_track_id_from_url( $query['url'] ),
) );
}
}
return $embed;
}

if ( isset( $attr['id'] ) ) {
$track_id = $attr['id'];
/**
* Render shortcode.
*
* @param array $attr Shortcode attributes.
* @param string $content Shortcode content.
* @return string Rendered shortcode.
*/
public function shortcode( $attr, $content = null ) {
$output = '';
if ( function_exists( 'soundcloud_shortcode' ) ) {
if ( empty( $attr['url'] ) && ! empty( $attr['id'] ) ) {
$attr['url'] = 'https://api.soundcloud.com/tracks/' . intval( $attr['id'] );
}
$output = soundcloud_shortcode( $attr, $content );
$output = $this->parse_amp_component_from_iframe( $output );
} else {
$url = false;
$url = null;
if ( isset( $attr['id'] ) ) {
$url = 'https://w.soundcloud.com/player/?url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F' . intval( $attr['id'] );
}

if ( isset( $attr['url'] ) ) {
$url = $attr['url'];
} elseif ( isset( $attr[0] ) ) {
Expand All @@ -56,27 +143,28 @@ public function shortcode( $attr ) {
$url = shortcode_new_to_old_params( $attr );
}

if ( ! empty( $url ) ) {
$track_id = $this->get_track_id_from_url( $url );
if ( $url ) {
$output = $this->render_embed_fallback( $url );
}
}

if ( empty( $track_id ) ) {
return '';
}

return $this->render( array(
'track_id' => $track_id,
) );
return $output;
}

/**
* Render embed.
*
* @param array $args Args.
* @return string Rendered embed.
* @global WP_Embed $wp_embed
*/
public function render( $args ) {
$args = wp_parse_args( $args, array(
'track_id' => false,
'url' => null,
) );

if ( empty( $args['track_id'] ) ) {
return AMP_HTML_Utils::build_tag( 'a', array( 'href' => esc_url( $args['url'] ), 'class' => 'amp-wp-embed-fallback' ), esc_html( $args['url'] ) );
return $this->render_embed_fallback( $args['url'] );
}

$this->did_convert_elements = true;
Expand All @@ -91,11 +179,34 @@ public function render( $args ) {
);
}

/**
* Render embed fallback.
*
* @param string $url URL.
* @returns string
*/
private function render_embed_fallback( $url ) {
return AMP_HTML_Utils::build_tag( 'a',
array(
'href' => esc_url( $url ),
'class' => 'amp-wp-embed-fallback',
),
esc_html( $url )
);
}

/**
* Get track_id from URL.
*
* @param string $url URL.
*
* @return string Track ID or empty string if none matched.
*/
private function get_track_id_from_url( $url ) {
$parsed_url = AMP_WP_Utils::parse_url( $url );
$tok = explode( '/', $parsed_url['path'] );
$track_id = $tok[2];

return $track_id;
if ( ! preg_match( '#tracks/(?P<track_id>[^/]+)#', $parsed_url['path'], $matches ) ) {
return '';
}
return $matches['track_id'];
}
}
3 changes: 3 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<php>
<const name="WP_TEST_ACTIVATED_PLUGINS" value="jetpack/jetpack.php" />
</php>
<testsuites>
<testsuite>
<directory prefix="test-" suffix=".php">./tests/</directory>
Expand Down
89 changes: 72 additions & 17 deletions tests/test-amp-soundcloud-embed.php
Original file line number Diff line number Diff line change
@@ -1,38 +1,82 @@
<?php
/**
* Class AMP_SoundCloud_Embed_Test
*
* @package AMP
*/

/**
* Class AMP_SoundCloud_Embed_Test
*
* @covers AMP_SoundCloud_Embed_Handler
*/
class AMP_SoundCloud_Embed_Test extends WP_UnitTestCase {

/**
* Set up.
*/
public function setUp() {
parent::setUp();

if ( function_exists( 'soundcloud_shortcode' ) ) {
add_shortcode( 'soundcloud', 'soundcloud_shortcode' );
}
}

/**
* Get conversion data.
*
* @return array
*/
public function get_conversion_data() {
return array(
$data = array(
'no_embed' => array(
'<p>Hello world.</p>',
'<p>Hello world.</p>' . PHP_EOL,
),

'url_simple' => array(
'https://api.soundcloud.com/tracks/89299804' . PHP_EOL,
'<p><amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud></p>' . PHP_EOL,
'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor' . PHP_EOL,
'<p><amp-soundcloud data-trackid="90097394" layout="fixed-height" height="200"></amp-soundcloud></p>' . PHP_EOL,
),
);

'shortcode_unnamed_attr_as_url' => array(
'[soundcloud https://api.soundcloud.com/tracks/89299804]' . PHP_EOL,
'<amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud>' . PHP_EOL,
),
if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
require_once JETPACK__PLUGIN_DIR . 'modules/shortcodes/soundcloud.php';
}

'shortcode_named_attr_url' => array(
'[soundcloud url=https://api.soundcloud.com/tracks/89299804]' . PHP_EOL,
'<amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud>' . PHP_EOL,
),
if ( function_exists( 'soundcloud_shortcode' ) ) {
$data = array_merge(
$data,
array(
// This is supported by Jetpack.
'shortcode_unnamed_attr_as_url' => array(
'[soundcloud url=https://api.soundcloud.com/tracks/89299804]' . PHP_EOL,
'<amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud>' . PHP_EOL,
),

'shortcode_named_attr_url' => array(
'[soundcloud id=89299804]' . PHP_EOL,
'<amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud>' . PHP_EOL,
),
// This apparently only works on WordPress.com.
'shortcode_with_id' => array(
'[soundcloud id=89299804]' . PHP_EOL,
'<amp-soundcloud data-trackid="89299804" layout="fixed-height" height="200"></amp-soundcloud>' . PHP_EOL,
),
)
);
}

);
return $data;
}

/**
* Test conversion.
*
* @covers AMP_SoundCloud_Embed_Handler::filter_embed_oembed_html()
* @covers AMP_SoundCloud_Embed_Handler::shortcode()
* @covers AMP_SoundCloud_Embed_Handler::render()
* @dataProvider get_conversion_data
*
* @param string $source Source.
* @param string $expected Expected.
*/
public function test__conversion( $source, $expected ) {
$embed = new AMP_SoundCloud_Embed_Handler();
Expand All @@ -42,21 +86,32 @@ public function test__conversion( $source, $expected ) {
$this->assertEquals( $expected, $filtered_content );
}

/**
* Get scripts data.
*
* @return array Scripts data.
*/
public function get_scripts_data() {
return array(
'not_converted' => array(
'<p>Hello World.</p>',
array(),
),
'converted' => array(
'https://api.soundcloud.com/tracks/89299804' . PHP_EOL,
'https://soundcloud.com/jack-villano-villano/mozart-requiem-in-d-minor' . PHP_EOL,
array( 'amp-soundcloud' => 'https://cdn.ampproject.org/v0/amp-soundcloud-0.1.js' ),
),
);
}

/**
* Test get scripts.
*
* @covers AMP_SoundCloud_Embed_Handler::get_scripts()
* @dataProvider get_scripts_data
*
* @param string $source Source.
* @param string $expected Expected.
*/
public function test__get_scripts( $source, $expected ) {
$embed = new AMP_SoundCloud_Embed_Handler();
Expand Down

1 comment on commit 0b729b1

@amedina
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing the SoundCloud embed @westonruter !

Please sign in to comment.