By default running content through the_content
filter automatically checks for oEmbed content. However when returning data through heartbeat_received
the filter for oEmbed doesn’t seem to work.
For example –
function test_heartbeat_received( $response, $data ) {
if( $data['test_heartbeat'] == 'test' ) {
$content = "https://twitter.com/WordPress/status/456502643738030080";
// Doesn't work
global $wp_embed;
$content = $wp_embed->autoembed( $content );
$response['test_heartbeat'] = $content;
// Also doesn't work
$response['test_heartbeat'] = apply_filters( 'the_content', $content );
}
return $response;
}
add_filter( 'heartbeat_received', 'test_heartbeat_received', 10, 2 );
add_filter( 'heartbeat_nopriv_received', 'test_heartbeat_received', 10, 2 );
Doing the exact same without using Heartbeat seems to work. The content filter is being applied as the formatting exists, just without oEmbed.
Any suggestions?
Thanks!
2 Answers
The problem:
The reason why this isn’t working, is this part of the WP_Embed::shortcode()
method:
if( $post_ID ) {
.... cut ...
// Use oEmbed to get the HTML
$html = wp_oembed_get( $url, $attr );
... cut ...
}
When trying to autoembed with the Heartbeat API the $post_ID
is null
, so wp_oembed_get()
is never activated.
No Caching:
When you autoembed the Twitter link in the post editor, for a given $post_ID
, the oembed HTML is cached in the post meta table under a key like this: _oembed_7bc759c5dcea2e4b77c939fc109996fb
and a value like this:
<blockquote class="twitter-tweet" width="550">
<p>
WordPress 3.9 “Smith” is now available with a smoother media editing experience,
live widget previews, and more:
<a href="http://t.co/mEbgUFdpyG">http://t.co/mEbgUFdpyG</a>
</p>
— WordPress (@WordPress)
<a href="https://twitter.com/WordPress/statuses/456502643738030080">April 16, 2014</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Each oembed link gets it’s own row in the post meta table, well if it’s not part of the default oembed handlers.
This caching mechanism is skipped when the $post_ID
is missing, so you might want to consider some caching for your setup.
Possible workarounds:
i) You need to take all the matches of '|^\s*(https?://[^\s"]+)\s*$|im'
, within your string, and run it through the wp_oembed_get()
function and replace the links from the original string.
ii) We could also associate it with a given post, within the test_heartbeat_received
callback:
global $wp_embed, $post;
$post = get_post( 3147 ); // Post ID: 3147
$content = $wp_embed->autoembed( $content );
$response['test_heartbeat'] = $content;
to get around the missing $post_ID
part and use the default caching for that post. You just have to remember that the oembed cache clears when you update that post.
If you try for example two Twitter links, with the above method:
$content = "
<div>
WordPress 3.9 “Smith” is now available with a smoother media editing experience, live widget previews, and more: http://t.co/mEbgUFdpyG
— WordPress (@WordPress) April 16, 2014
</div>
<div>
Contributors, what has been your best/worst experience so far? http://t.co/gaYNsuIBXs
— WordPress (@WordPress) April 24, 2014
</div>
";
then after the autoembed process, you get two rows in the post meta table, assigned to post_id: 3147
: