<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][10837] trunk/src/bp-core/classes/class-bp-oembed-component.php: Core: Introduce `BP_oEmbed_Component` class.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://buddypress.trac.wordpress.org/changeset/10837">10837</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/10837","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>r-a-y</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-05-31 07:11:36 +0000 (Tue, 31 May 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Core: Introduce `BP_oEmbed_Component` class.

This class interacts with various aspects of the Embeds functionality
introduced in WordPress 4.4 and is meant to be extended by BuddyPress
components to easily add oEmbed support.

The Activity component will be the first one to utilize this class.

See <a href="http://buddypress.trac.wordpress.org/ticket/6772">#6772</a>.</pre>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcbpcoreclassesclassbpoembedcomponentphp">trunk/src/bp-core/classes/class-bp-oembed-component.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpcoreclassesclassbpoembedcomponentphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/bp-core/classes/class-bp-oembed-component.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-core/classes/class-bp-oembed-component.php                           (rev 0)
+++ trunk/src/bp-core/classes/class-bp-oembed-component.php     2016-05-31 07:11:36 UTC (rev 10837)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,617 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core component classes.
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * API for responding and returning a custom oEmbed request.
+ *
+ * @since 2.6.0
+ */
+abstract class BP_oEmbed_Component {
+
+       /** 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 ) ) {
+                       $class = get_class( $this );
+                       throw new LogicException( $class . ' class must define $slug_endpoint property' );
+               }
+
+               $this->setup_hooks();
+               $this->custom_hooks();
+       }
+
+       /** REQUIRED METHODS ****************************************************/
+
+       /**
+        * Add content for your oEmbed response here.
+        *
+        * @since 2.6.0
+        */
+       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
+        *
+        * @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.
+        * @return string
+        */
+       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
+        */
+       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
+        */
+       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 ( empty( 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 ( ! empty( 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();
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>