Embeds: Add oEmbed provider support.
For the past 6 years, WordPress has operated as an oEmbed consumer, allowing users to easily embed content from other sites. By adding oEmbed provider support, this allows any oEmbed consumer to embed posts from WordPress sites. In addition to creating an oEmbed provider, WordPress' oEmbed consumer code has been enhanced to work with any site that provides oEmbed data (as long as it matches some strict security rules), and provides a preview from within the post editor. For security, embeds appear within a sandboxed iframe - the iframe content is a template that can be styled or replaced entirely by the theme on the provider site. Props swissspidy, pento, melchoyce, netweb, pfefferle, johnbillion, extendwings, davidbinda, danielbachhuber, SergeyBiryukov, afercia Fixes #32522. Built from https://develop.svn.wordpress.org/trunk@34903 git-svn-id: http://core.svn.wordpress.org/trunk@34868 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
@@ -326,3 +326,529 @@ function wp_embed_handler_video( $matches, $attr, $url, $rawattr ) {
|
||||
*/
|
||||
return apply_filters( 'wp_embed_handler_video', $video, $attr, $url, $rawattr );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an oEmbed API query.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function wp_oembed_parse_query( $wp_query ) {
|
||||
$controller = new WP_oEmbed_Controller;
|
||||
$controller->parse_query( $wp_query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds oEmbed discovery links in the website <head>.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function wp_oembed_add_discovery_links() {
|
||||
$output = '';
|
||||
|
||||
if ( is_singular() ) {
|
||||
$output .= '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink() ) ) . '" />' . "\n";
|
||||
$output .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink(), 'xml' ) ) . '" />' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the oEmbed discovery links.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $output HTML of the discovery links.
|
||||
*/
|
||||
echo apply_filters( 'oembed_discovery_links', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the necessary JavaScript to communicate with the embedded iframes.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function wp_oembed_add_host_js() {
|
||||
wp_enqueue_script( 'wp-oembed' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the URL to embed a specific post in an iframe.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param int|WP_Post $post Optional. Post ID or object. Defaults to the current post.
|
||||
* @return string|false The post embed URL on success, false if the post doesn't exist.
|
||||
*/
|
||||
function get_post_embed_url( $post = null ) {
|
||||
$post = get_post( $post );
|
||||
|
||||
if ( ! $post ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( get_option( 'permalink_structure' ) ) {
|
||||
$embed_url = trailingslashit( get_permalink( $post ) ) . user_trailingslashit( 'embed' );
|
||||
} else {
|
||||
$embed_url = add_query_arg( array( 'embed' => 'true' ), get_permalink( $post ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the URL to embed a specific post.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $embed_url The post embed URL.
|
||||
* @param WP_Post $post The corresponding post object.
|
||||
*/
|
||||
return esc_url_raw( apply_filters( 'post_embed_url', $embed_url, $post ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the oEmbed endpoint URL for a given permalink.
|
||||
*
|
||||
* Pass an empty string as the first argument
|
||||
* to get the endpoint base URL.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $permalink Optional. The permalink used for the `url` query arg. Default empty.
|
||||
* @param string $format Optional. The requested response format. Default 'json'.
|
||||
* @return string The oEmbed endpoint URL.
|
||||
*/
|
||||
function get_oembed_endpoint_url( $permalink = '', $format = 'json' ) {
|
||||
$url = add_query_arg( array( 'oembed' => 'true' ), home_url( '/' ) );
|
||||
|
||||
if ( 'json' === $format ) {
|
||||
$format = false;
|
||||
}
|
||||
|
||||
if ( '' !== $permalink ) {
|
||||
$url = add_query_arg( array(
|
||||
'url' => $permalink,
|
||||
'format' => $format,
|
||||
), $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the oEmbed endpoint URL.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $url The URL to the oEmbed endpoint.
|
||||
* @param string $permalink The permalink used for the `url` query arg.
|
||||
* @param string $format The requested response format.
|
||||
*/
|
||||
return apply_filters( 'oembed_endpoint_url', $url, $permalink, $format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the embed code for a specific post.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param int|WP_Post $post Optional. Post ID or object. Default is global `$post`.
|
||||
* @param int $width The width for the response.
|
||||
* @param int $height The height for the response.
|
||||
* @return string|false Embed code on success, false if post doesn't exist.
|
||||
*/
|
||||
function get_post_embed_html( $post = null, $width, $height ) {
|
||||
$post = get_post( $post );
|
||||
|
||||
if ( ! $post ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$embed_url = get_post_embed_url( $post );
|
||||
|
||||
$output = "<script type='text/javascript'>\n";
|
||||
if ( SCRIPT_DEBUG ) {
|
||||
$output .= file_get_contents( ABSPATH . WPINC . '/js/wp-oembed.js' );
|
||||
} else {
|
||||
/*
|
||||
* If you're looking at a src version of this file, you'll see an "include"
|
||||
* statement below. This is used by the `grunt build` process to directly
|
||||
* include a minified version of wp-oembed.js, instead of using the
|
||||
* file_get_contents() method from above.
|
||||
*
|
||||
* If you're looking at a build version of this file, you'll see a string of
|
||||
* minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
|
||||
* and edit wp-oembed.js directly.
|
||||
*/
|
||||
$output .=<<<JS
|
||||
include "js/wp-oembed.min.js"
|
||||
JS;
|
||||
}
|
||||
$output .= "\n</script>";
|
||||
|
||||
$output .= sprintf(
|
||||
'<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
|
||||
esc_url( $embed_url ),
|
||||
absint( $width ),
|
||||
absint( $height ),
|
||||
esc_attr__( 'Embedded WordPress Post', 'oembed-api' )
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the oEmbed HTML output.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $output The default HTML.
|
||||
* @param WP_Post $post Current post object.
|
||||
* @param int $width Width of the response.
|
||||
* @param int $height Height of the response.
|
||||
*/
|
||||
return apply_filters( 'oembed_html', $output, $post, $width, $height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the oEmbed response data for a given post.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param WP_Post|int $post Optional. Post object or ID. Default is global `$post`.
|
||||
* @param int $width The requested width.
|
||||
* @return array|false Response data on success, false if post doesn't exist.
|
||||
*/
|
||||
function get_oembed_response_data( $post = null, $width ) {
|
||||
$post = get_post( $post );
|
||||
|
||||
if ( ! $post ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'publish' !== get_post_status( $post ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the allowed minimum width for the oEmbed response.
|
||||
*
|
||||
* @param int $width The minimum width. Defaults to 200.
|
||||
*/
|
||||
$minwidth = apply_filters( 'oembed_minwidth', 200 );
|
||||
|
||||
/**
|
||||
* Filter the allowed maximum width for the oEmbed response.
|
||||
*
|
||||
* @param int $width The maximum width. Defaults to 600.
|
||||
*/
|
||||
$maxwidth = apply_filters( 'oembed_maxwidth', 600 );
|
||||
|
||||
if ( $width < $minwidth ) {
|
||||
$width = $minwidth;
|
||||
} else if ( $width > $maxwidth ) {
|
||||
$width = $maxwidth;
|
||||
}
|
||||
|
||||
$height = ceil( $width / 16 * 9 );
|
||||
|
||||
if ( 200 > $height ) {
|
||||
$height = 200;
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'version' => '1.0',
|
||||
'provider_name' => get_bloginfo( 'name' ),
|
||||
'provider_url' => get_home_url(),
|
||||
'author_name' => get_bloginfo( 'name' ),
|
||||
'author_url' => get_home_url(),
|
||||
'title' => $post->post_title,
|
||||
'type' => 'link',
|
||||
);
|
||||
|
||||
$author = get_userdata( $post->post_author );
|
||||
|
||||
if ( $author ) {
|
||||
$data['author_name'] = $author->display_name;
|
||||
$data['author_url'] = get_author_posts_url( $author->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the oEmbed response data.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param array $data The response data.
|
||||
* @param WP_Post $post The post object.
|
||||
* @param int $width The requested width.
|
||||
* @param int $height The calculated height.
|
||||
*/
|
||||
return apply_filters( 'oembed_response_data', $data, $post, $width, $height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the oEmbed response data to return an iframe embed code.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param array $data The response data.
|
||||
* @param WP_Post $post The post object.
|
||||
* @param int $width The requested width.
|
||||
* @param int $height The calculated height.
|
||||
* @return array The modified response data.
|
||||
*/
|
||||
function get_oembed_response_data_rich( $data, $post, $width, $height ) {
|
||||
$data['width'] = absint( $width );
|
||||
$data['height'] = absint( $height );
|
||||
$data['type'] = 'rich';
|
||||
$data['html'] = get_post_embed_html( $post, $width, $height );
|
||||
|
||||
// Add post thumbnail to response if available.
|
||||
$thumbnail_id = false;
|
||||
|
||||
if ( has_post_thumbnail( $post->ID ) ) {
|
||||
$thumbnail_id = get_post_thumbnail_id( $post->ID );
|
||||
}
|
||||
|
||||
if ( 'attachment' === get_post_type( $post ) ) {
|
||||
if ( wp_attachment_is_image( $post ) ) {
|
||||
$thumbnail_id = $post->ID;
|
||||
} else if ( wp_attachment_is( 'video', $post ) ) {
|
||||
$thumbnail_id = get_post_thumbnail_id( $post );
|
||||
$data['type'] = 'video';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $thumbnail_id ) {
|
||||
list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
|
||||
$data['thumbnail_url'] = $thumbnail_url;
|
||||
$data['thumbnail_width'] = $thumbnail_width;
|
||||
$data['thumbnail_height'] = $thumbnail_height;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the specified format is either 'json' or 'xml'.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $format The oEmbed response format. Accepts 'json', 'xml'.
|
||||
* @return string The format, either 'xml' or 'json'. Default 'json'.
|
||||
*/
|
||||
function wp_oembed_ensure_format( $format ) {
|
||||
if ( ! in_array( $format, array( 'json', 'xml' ), true ) ) {
|
||||
return 'json';
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an XML string from a given array.
|
||||
*
|
||||
* @since 4.4.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $data The original oEmbed response data.
|
||||
* @param SimpleXMLElement $node Optional. XML node to append the result to recursively.
|
||||
* @return string|false XML string on success, false on error.
|
||||
*/
|
||||
function _oembed_create_xml( $data, $node = null ) {
|
||||
if ( ! is_array( $data ) || empty( $data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( null === $node ) {
|
||||
$node = new SimpleXMLElement( '<oembed></oembed>' );
|
||||
}
|
||||
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( is_numeric( $key ) ) {
|
||||
$key = 'oembed';
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
$item = $node->addChild( $key );
|
||||
_oembed_create_xml( $value, $item );
|
||||
} else {
|
||||
$node->addChild( $key, esc_html( $value ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $node->asXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the returned oEmbed HTML.
|
||||
*
|
||||
* If the $url isn't on the trusted providers list,
|
||||
* we need to filter the HTML heavily for security.
|
||||
*
|
||||
* Only filters 'rich' and 'html' response types.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $return The returned oEmbed HTML.
|
||||
* @param object $data A data object result from an oEmbed provider.
|
||||
* @param string $url The URL of the content to be embedded.
|
||||
* @return string The filtered and sanitized oEmbed result.
|
||||
*/
|
||||
function wp_filter_oembed_result( $return, $data, $url ) {
|
||||
if ( false === $return || ! in_array( $data->type, array( 'rich', 'video' ) ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
require_once( ABSPATH . WPINC . '/class-oembed.php' );
|
||||
$wp_oembed = _wp_oembed_get_object();
|
||||
|
||||
// Don't modify the HTML for trusted providers.
|
||||
if ( false !== $wp_oembed->get_provider( $url, array( 'discover' => false ) ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$allowed_html = array(
|
||||
'iframe' => array(
|
||||
'src' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'frameborder' => true,
|
||||
'marginwidth' => true,
|
||||
'marginheight' => true,
|
||||
'scrolling' => true,
|
||||
'title' => true,
|
||||
'class' => true,
|
||||
),
|
||||
);
|
||||
|
||||
$html = wp_kses( $return, $allowed_html );
|
||||
preg_match( '|^.*(<iframe.*?></iframe>).*$|m', $html, $iframes );
|
||||
|
||||
if ( empty( $iframes ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$html = str_replace( '<iframe', '<iframe sandbox="allow-scripts" security="restricted"', $iframes[1] );
|
||||
|
||||
preg_match( '/ src=[\'"]([^\'"]*)[\'"]/', $html, $results );
|
||||
|
||||
if ( ! empty( $results ) ) {
|
||||
$secret = wp_generate_password( 10, false );
|
||||
|
||||
$url = esc_url( "{$results[1]}#?secret=$secret" );
|
||||
|
||||
$html = str_replace( $results[0], " src=\"$url\" data-secret=\"$secret\"", $html );
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the string in the "more" link displayed after a trimmed excerpt.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $more_string The string shown within the more link.
|
||||
* @return string The modified excerpt.
|
||||
*/
|
||||
function wp_oembed_excerpt_more( $more_string ) {
|
||||
if ( ! is_embed() ) {
|
||||
return $more_string;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
_x( '… %s', 'read more link', 'oembed-api' ),
|
||||
sprintf(
|
||||
'<a class="wp-embed-more" href="%s" target="_top">%s</a>',
|
||||
get_the_permalink(),
|
||||
__( 'Read more', 'oembed-api' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the post excerpt for the embed template.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function the_excerpt_embed() {
|
||||
$output = get_the_excerpt();
|
||||
|
||||
/**
|
||||
* Filter the post excerpt for the embed template.
|
||||
*
|
||||
* @param string $output The current post excerpt.
|
||||
*/
|
||||
echo apply_filters( 'the_excerpt_embed', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the post excerpt for the embed template.
|
||||
*
|
||||
* Shows players for video and audio attachments.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*
|
||||
* @param string $content The current post excerpt.
|
||||
* @return string The modified post excerpt.
|
||||
*/
|
||||
function wp_oembed_excerpt_attachment( $content ) {
|
||||
if ( is_attachment() ) {
|
||||
return prepend_attachment( '' );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the CSS in the embed iframe header.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function print_oembed_embed_styles() {
|
||||
?>
|
||||
<style type="text/css">
|
||||
<?php
|
||||
if ( WP_DEBUG ) {
|
||||
readfile( ABSPATH . WPINC . "/css/wp-oembed-embed.css" );
|
||||
} else {
|
||||
/*
|
||||
* If you're looking at a src version of this file, you'll see an "include"
|
||||
* statement below. This is used by the `grunt build` process to directly
|
||||
* include a minified version of wp-oembed-embed.css, instead of using the
|
||||
* readfile() method from above.
|
||||
*
|
||||
* If you're looking at a build version of this file, you'll see a string of
|
||||
* minified CSS. If you need to debug it, please turn on WP_DEBUG
|
||||
* and edit wp-oembed-embed.css directly.
|
||||
*/
|
||||
?>
|
||||
include "css/wp-oembed-embed.min.css"
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the CSS in the embed iframe header.
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
function print_oembed_embed_scripts() {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
<?php
|
||||
if ( SCRIPT_DEBUG ) {
|
||||
readfile( ABSPATH . WPINC . "/js/wp-oembed-embed.js" );
|
||||
} else {
|
||||
/*
|
||||
* If you're looking at a src version of this file, you'll see an "include"
|
||||
* statement below. This is used by the `grunt build` process to directly
|
||||
* include a minified version of wp-oembed-embed.js, instead of using the
|
||||
* readfile() method from above.
|
||||
*
|
||||
* If you're looking at a build version of this file, you'll see a string of
|
||||
* minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
|
||||
* and edit wp-oembed-embed.js directly.
|
||||
*/
|
||||
?>
|
||||
include "js/wp-oembed-embed.min.js"
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
Reference in New Issue
Block a user