BP_Core_oEmbed_Extension
API for responding and returning a custom oEmbed request.
Description Description
Source Source
File: bp-core/classes/class-bp-core-oembed-extension.php
abstract class BP_Core_oEmbed_Extension {
/** START PROPERTIES ****************************************************/
/**
* (required) The slug endpoint.
*
* Should be your component id.
*
* @since 2.6.0
*
* @var string
*/
public $slug_endpoint = '';
/** END PROPERTIES ******************************************************/
/**
* Constructor.
*/
final public function __construct() {
$this->setup_properties();
// Some rudimentary logic checking.
if ( empty( $this->slug_endpoint ) ) {
return;
}
$this->setup_hooks();
$this->custom_hooks();
}
/** REQUIRED METHODS ****************************************************/
/**
* Add content for your oEmbed response here.
*
* @since 2.6.0
*
* @return null
*/
abstract protected function content();
/**
* Add a check for when you are on the page you want to oEmbed.
*
* You'll want to return a boolean here. eg. bp_is_single_activity().
*
* @since 2.6.0
*
* @return bool
*/
abstract protected function is_page();
/**
* Validate the URL to see if it matches your item ID.
*
* @since 2.6.0
*
* @param string $url URL to validate.
* @return int Your item ID
*/
abstract protected function validate_url_to_item_id( $url );
/**
* Set the oEmbed response data.
*
* @since 2.6.0
*
* @param int $item_id Your item ID to do checks against.
* @return array Should contain 'content', 'title', 'author_url', 'author_name' as array
* keys. 'author_url' and 'author_name' is optional; the rest are required.
*/
abstract protected function set_oembed_response_data( $item_id );
/**
* Sets the fallback HTML for the oEmbed response.
*
* In a WordPress oEmbed item, the fallback HTML is a <blockquote>. This is
* usually hidden after the <iframe> is loaded.
*
* @since 2.6.0
*
* @param int $item_id Your item ID to do checks against.
* @return string Fallback HTML you want to output.
*/
abstract protected function set_fallback_html( $item_id );
/** OPTIONAL METHODS ****************************************************/
/**
* If your oEmbed endpoint requires additional arguments, set them here.
*
* @see register_rest_route() View the $args parameter for more info.
*
* @since 2.6.0
*
* @return array
*/
protected function set_route_args() {
return array();
}
/**
* Set the iframe title.
*
* If not set, this will fallback to WP's 'Embedded WordPress Post'.
*
* @since 2.6.0
*
* @param int $item_id The item ID to do checks for.
*/
protected function set_iframe_title( $item_id ) {}
/**
* Do what you need to do here to initialize any custom hooks.
*
* @since 2.6.0
*/
protected function custom_hooks() {}
/**
* Set permalink for oEmbed link discovery.
*
* This method will be called on the page we want to oEmbed. In most cases,
* you will not need to override this method. However, if you need to, do
* override in your extended class.
*
* @since 2.6.0
*/
protected function set_permalink() {
$url = bp_get_requested_url();
// Remove querystring from bp_get_requested_url().
if ( false !== strpos( bp_get_requested_url(), '?' ) ) {
$url = substr( bp_get_requested_url(), 0, strpos( bp_get_requested_url(), '?' ) );
}
return $url;
}
/** HELPERS *************************************************************/
/**
* Get the item ID when filtering the oEmbed HTML.
*
* Should only be used during the 'embed_html' hook.
*
* @since 2.6.0
*/
protected function get_item_id() {
return $this->is_page() ? $this->validate_url_to_item_id( $this->set_permalink() ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
}
/** SET UP **************************************************************/
/**
* Set up properties.
*
* @since 2.6.0
*/
protected function setup_properties() {
$this->slug_endpoint = sanitize_title( $this->slug_endpoint );
}
/**
* Hooks! We do the dirty work here, so you don't have to! :)
*
* More hooks are available in the setup_template_parts() method.
*
* @since 2.6.0
*/
protected function setup_hooks() {
add_action( 'rest_api_init', array( $this, 'register_route' ) );
add_action( 'bp_embed_content', array( $this, 'inject_content' ) );
add_filter( 'embed_template', array( $this, 'setup_template_parts' ) );
add_filter( 'post_embed_url', array( $this, 'filter_embed_url' ) );
add_filter( 'embed_html', array( $this, 'filter_embed_html' ) );
add_filter( 'oembed_discovery_links', array( $this, 'add_oembed_discovery_links' ) );
add_filter( 'rest_pre_serve_request', array( $this, 'oembed_xml_request' ), 20, 4 );
}
/** HOOKS ***************************************************************/
/**
* Register the oEmbed REST API route.
*
* @since 2.6.0
*/
public function register_route() {
/** This filter is documented in wp-includes/class-wp-oembed-controller.php */
$maxwidth = apply_filters( 'oembed_default_width', 600 );
// Required arguments.
$args = array(
'url' => array(
'required' => true,
'sanitize_callback' => 'esc_url_raw',
),
'format' => array(
'default' => 'json',
'sanitize_callback' => 'wp_oembed_ensure_format',
),
'maxwidth' => array(
'default' => $maxwidth,
'sanitize_callback' => 'absint',
)
);
// Merge custom arguments here.
$args = $args + (array) $this->set_route_args();
register_rest_route( 'oembed/1.0', "/embed/{$this->slug_endpoint}", array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
'args' => $args
),
) );
}
/**
* Set up custom embed template parts for BuddyPress use.
*
* @since 2.6.0
*
* @param string $template File path to current embed template.
* @return string
*/
public function setup_template_parts( $template ) {
// Determine if we're on our BP page.
if ( ! $this->is_page() || is_404() ) {
return $template;
}
// Set up some BP-specific embed template overrides.
add_action( 'get_template_part_embed', array( $this, 'content_buffer_start' ), -999, 2 );
add_action( 'get_footer', array( $this, 'content_buffer_end' ), -999 );
// Return the original WP embed template.
return $template;
}
/**
* Start object buffer.
*
* We're going to override WP's get_template_part( 'embed, 'content' ) call
* and inject our own template for BuddyPress use.
*
* @since 2.6.0
*
* @param string $slug Template slug.
* @param string $name Template name.
*/
public function content_buffer_start( $slug, $name ) {
if ( 'embed' !== $slug || 'content' !== $name ) {
return;
}
// Start the buffer to wipe out get_template_part( 'embed, 'content' ).
ob_start();
}
/**
* End object buffer.
*
* We're going to override WP's get_template_part( 'embed, 'content' ) call
* and inject our own template for BuddyPress use.
*
* @since 2.6.0
*
* @param string $name Template name.
*/
public function content_buffer_end( $name ) {
if ( 'embed' !== $name || is_404() ) {
return;
}
// Wipe out get_template_part( 'embed, 'content' ).
ob_end_clean();
// Start our custom BuddyPress embed template!
echo '<div ';
post_class( 'wp-embed' );
echo '>';
// Template part for our embed header.
bp_get_asset_template_part( 'embeds/header', bp_current_component() );
/**
* Inject BuddyPress embed content on this hook.
*
* You shouldn't really need to use this if you extend the
* {@link BP_oEmbed_Component} class.
*
* @since 2.6.0
*/
do_action( 'bp_embed_content' );
// Template part for our embed footer.
bp_get_asset_template_part( 'embeds/footer', bp_current_component() );
echo '</div>';
}
/**
* Adds oEmbed discovery links on single activity pages.
*
* @since 2.6.0
*
* @param string $retval Current discovery links.
* @return string
*/
public function add_oembed_discovery_links( $retval ) {
if ( ! $this->is_page() ) {
return $retval;
}
$permalink = $this->set_permalink();
if ( empty( $permalink ) ) {
return $retval;
}
add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
$retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n";
if ( class_exists( 'SimpleXMLElement' ) ) {
$retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n";
}
remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
return $retval;
}
/**
* Fetch our oEmbed response data to return.
*
* A simplified version of {@link get_oembed_response_data()}.
*
* @since 2.6.0
*
* @link http://oembed.com/ View the 'Response parameters' section for more details.
*
* @param array $item Custom oEmbed response data.
* @param int $width The requested width.
* @return array
*/
protected function get_oembed_response_data( $item, $width ) {
$data = wp_parse_args( $item, 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' => ucfirst( $this->slug_endpoint ),
'type' => 'rich',
) );
/** This filter is documented in /wp-includes/embed.php */
$min_max_width = apply_filters( 'oembed_min_max_width', array(
'min' => 200,
'max' => 600
) );
$width = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
$height = max( ceil( $width / 16 * 9 ), 200 );
$data['width'] = absint( $width );
$data['height'] = absint( $height );
// Set 'html' parameter.
if ( 'video' === $data['type'] || 'rich' === $data['type'] ) {
// Fake a WP post so we can use get_post_embed_html().
$post = new stdClass;
$post->post_content = $data['content'];
$post->post_title = $data['title'];
$data['html'] = get_post_embed_html( $data['width'], $data['height'], $post );
}
// Remove temporary parameters.
unset( $data['content'] );
return $data;
}
/**
* Callback for the API endpoint.
*
* Returns the JSON object for the item.
*
* @since 2.6.0
*
* @param WP_REST_Request $request Full data about the request.
* @return WP_Error|array oEmbed response data or WP_Error on failure.
*/
public function get_item( $request ) {
$url = $request['url'];
$data = false;
$item_id = (int) $this->validate_url_to_item_id( $url );
if ( ! empty( $item_id ) ) {
// Add markers to tell that we're embedding a single activity.
// This is needed for various oEmbed response data filtering.
if ( ! isset( buddypress()->{$this->slug_endpoint} ) || ! buddypress()->{$this->slug_endpoint} ) {
buddypress()->{$this->slug_endpoint} = new stdClass;
}
buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url;
buddypress()->{$this->slug_endpoint}->embedid_in_progress = $item_id;
// Save custom route args as well.
$custom_args = array_keys( (array) $this->set_route_args() );
if ( ! empty( $custom_args ) ) {
buddypress()->{$this->slug_endpoint}->embedargs_in_progress = array();
foreach( $custom_args as $arg ) {
if ( isset( $request[ $arg ] ) ) {
buddypress()->{$this->slug_endpoint}->embedargs_in_progress[ $arg ] = $request[ $arg ];
}
}
}
// Grab custom oEmbed response data.
$item = $this->set_oembed_response_data( $item_id );
// Set oEmbed response data.
$data = $this->get_oembed_response_data( $item, $request['maxwidth'] );
}
if ( ! $data ) {
return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
}
return $data;
}
/**
* If oEmbed request wants XML, return XML instead of JSON.
*
* Basically a copy of {@link _oembed_rest_pre_serve_request()}. Unfortunate
* that we have to duplicate this just for a URL check.
*
* @since 2.6.0
*
* @param bool $served Whether the request has already been served.
* @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response.
* @param WP_REST_Request $request Request used to generate the response.
* @param WP_REST_Server $server Server instance.
* @return bool
*/
public function oembed_xml_request( $served, $result, $request, $server ) {
$params = $request->get_params();
if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
return $served;
}
// Validate URL against our oEmbed endpoint. If not valid, bail.
// This is our mod to _oembed_rest_pre_serve_request().
$query_params = $request->get_query_params();
if ( false === $this->validate_url_to_item_id( $query_params['url'] ) ) {
return $served;
}
// Embed links inside the request.
$data = $server->response_to_data( $result, false );
if ( ! class_exists( 'SimpleXMLElement' ) ) {
status_header( 501 );
die( get_status_header_desc( 501 ) );
}
$result = _oembed_create_xml( $data );
// Bail if there's no XML.
if ( ! $result ) {
status_header( 501 );
return get_status_header_desc( 501 );
}
if ( ! headers_sent() ) {
$server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
}
echo $result;
return true;
}
/**
* Pass our BuddyPress activity permalink for embedding.
*
* @since 2.6.0
*
* @see bp_activity_embed_rest_route_callback()
*
* @param string $retval Current embed URL.
* @return string
*/
public function filter_embed_url( $retval ) {
if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
return $retval;
}
$url = $this->is_page() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress;
$url = trailingslashit( $url );
// This is for the 'WordPress Embed' block
// @see bp_activity_embed_comments_button().
if ( 'the_permalink' !== current_filter() ) {
$url = add_query_arg( 'embed', 'true', trailingslashit( $url ) );
// Add custom route args to iframe.
if ( isset( buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) && buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) {
foreach( buddypress()->{$this->slug_endpoint}->embedargs_in_progress as $key => $value ) {
$url = add_query_arg( $key, $value, $url );
}
}
}
return $url;
}
/**
* Filters the embed HTML for our BP oEmbed endpoint.
*
* @since 2.6.0
*
* @param string $retval Current embed HTML.
* @return string
*/
public function filter_embed_html( $retval ) {
if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
return $retval;
}
$url = $this->set_permalink();
$item_id = $this->is_page() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
// Change 'Embedded WordPress Post' to custom title.
$custom_title = $this->set_iframe_title( $item_id );
if ( ! empty( $custom_title ) ) {
$title_pos = strpos( $retval, 'title=' ) + 7;
$title_end_pos = strpos( $retval, '"', $title_pos );
$retval = substr_replace( $retval, esc_attr( $custom_title ), $title_pos, $title_end_pos - $title_pos );
}
// Add 'max-width' CSS attribute to IFRAME.
// This will make our oEmbeds responsive.
if ( false === strpos( $retval, 'style="max-width' ) ) {
$retval = str_replace( '<iframe', '<iframe style="max-width:100%"', $retval );
}
// Remove default <blockquote>.
$retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
// Set up new fallback HTML
// @todo Maybe use KSES?
$fallback_html = $this->set_fallback_html( $item_id );
/**
* Dynamic filter to return BP oEmbed HTML.
*
* @since 2.6.0
*
* @var string $retval
*/
return apply_filters( "bp_{$this->slug_endpoint}_embed_html", $fallback_html . $retval );
}
/**
* Append our custom slug endpoint to oEmbed endpoint URL.
*
* Meant to be used as a filter on 'rest_url' before any call to
* {@link get_oembed_endpoint_url()} is used.
*
* @since 2.6.0
*
* @see add_oembed_discovery_links()
*
* @param string $retval Current oEmbed endpoint URL.
* @return string
*/
public function filter_rest_url( $retval = '' ) {
return $retval . "/{$this->slug_endpoint}";
}
/**
* Inject content into the embed template.
*
* @since 2.6.0
*/
public function inject_content() {
if ( ! $this->is_page() ) {
return;
}
$this->content();
}
}
Changelog Changelog
| Version | Description |
|---|---|
| 2.6.0 | Introduced. |
Methods Methods
- __construct — Constructor.
- add_oembed_discovery_links — Adds oEmbed discovery links on single activity pages.
- content — Add content for your oEmbed response here.
- content_buffer_end — End object buffer.
- content_buffer_start — Start object buffer.
- custom_hooks — Do what you need to do here to initialize any custom hooks.
- filter_embed_html — Filters the embed HTML for our BP oEmbed endpoint.
- filter_embed_url — Pass our BuddyPress activity permalink for embedding.
- filter_rest_url — Append our custom slug endpoint to oEmbed endpoint URL.
- get_item — Callback for the API endpoint.
- get_item_id — Get the item ID when filtering the oEmbed HTML.
- get_oembed_response_data — Fetch our oEmbed response data to return.
- inject_content — Inject content into the embed template.
- is_page — Add a check for when you are on the page you want to oEmbed.
- oembed_xml_request — If oEmbed request wants XML, return XML instead of JSON.
- register_route — Register the oEmbed REST API route.
- set_fallback_html — Sets the fallback HTML for the oEmbed response.
- set_iframe_title — Set the iframe title.
- set_oembed_response_data — Set the oEmbed response data.
- set_permalink — Set permalink for oEmbed link discovery.
- set_route_args — If your oEmbed endpoint requires additional arguments, set them here.
- setup_hooks — Hooks! We do the dirty work here, so you don't have to! :)
- setup_properties — Set up properties.
- setup_template_parts — Set up custom embed template parts for BuddyPress use.
- validate_url_to_item_id — Validate the URL to see if it matches your item ID.