[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-core/classes/ -> class-bp-core-oembed-extension.php (source)

   1  <?php
   2  /**
   3   * Core component classes.
   4   *
   5   * @package BuddyPress
   6   * @subpackage Core
   7   * @since 2.6.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * API for responding and returning a custom oEmbed request.
  15   *
  16   * @since 2.6.0
  17   */
  18  abstract class BP_Core_oEmbed_Extension {
  19  
  20      /** START PROPERTIES ****************************************************/
  21  
  22      /**
  23       * (required) The slug endpoint.
  24       *
  25       * Should be your component id.
  26       *
  27       * @since 2.6.0
  28       *
  29       * @var string
  30       */
  31      public $slug_endpoint = '';
  32  
  33      /** END PROPERTIES ******************************************************/
  34  
  35      /**
  36       * Constructor.
  37       */
  38  	final public function __construct() {
  39          $this->setup_properties();
  40  
  41          // Some rudimentary logic checking.
  42          if ( empty( $this->slug_endpoint ) ) {
  43              return;
  44          }
  45  
  46          $this->setup_hooks();
  47          $this->custom_hooks();
  48      }
  49  
  50      /** REQUIRED METHODS ****************************************************/
  51  
  52      /**
  53       * Add content for your oEmbed response here.
  54       *
  55       * @since 2.6.0
  56       *
  57       * @return null
  58       */
  59      abstract protected function content();
  60  
  61      /**
  62       * Add a check for when you are on the page you want to oEmbed.
  63       *
  64       * You'll want to return a boolean here. eg. bp_is_single_activity().
  65       *
  66       * @since 2.6.0
  67       *
  68       * @return bool
  69       */
  70      abstract protected function is_page();
  71  
  72      /**
  73       * Validate the URL to see if it matches your item ID.
  74       *
  75       * @since 2.6.0
  76       *
  77       * @param string $url URL to validate.
  78       * @return int Your item ID
  79       */
  80      abstract protected function validate_url_to_item_id( $url );
  81  
  82      /**
  83       * Set the oEmbed response data.
  84       *
  85       * @since 2.6.0
  86       *
  87       * @param int $item_id Your item ID to do checks against.
  88       * @return array Should contain 'content', 'title', 'author_url', 'author_name' as array
  89       *               keys. 'author_url' and 'author_name' is optional; the rest are required.
  90       */
  91      abstract protected function set_oembed_response_data( $item_id );
  92  
  93      /**
  94       * Sets the fallback HTML for the oEmbed response.
  95       *
  96       * In a WordPress oEmbed item, the fallback HTML is a <blockquote>.  This is
  97       * usually hidden after the <iframe> is loaded.
  98       *
  99       * @since 2.6.0
 100       *
 101       * @param int $item_id Your item ID to do checks against.
 102       * @return string Fallback HTML you want to output.
 103       */
 104      abstract protected function set_fallback_html( $item_id );
 105  
 106      /** OPTIONAL METHODS ****************************************************/
 107  
 108      /**
 109       * If your oEmbed endpoint requires additional arguments, set them here.
 110       *
 111       * @see register_rest_route() View the $args parameter for more info.
 112       *
 113       * @since 2.6.0
 114       *
 115       * @return array
 116       */
 117  	protected function set_route_args() {
 118          return array();
 119      }
 120  
 121      /**
 122       * Set the iframe title.
 123       *
 124       * If not set, this will fallback to WP's 'Embedded WordPress Post'.
 125       *
 126       * @since 2.6.0
 127       *
 128       * @param int $item_id The item ID to do checks for.
 129       */
 130  	protected function set_iframe_title( $item_id ) {}
 131  
 132      /**
 133       * Do what you need to do here to initialize any custom hooks.
 134       *
 135       * @since 2.6.0
 136       */
 137  	protected function custom_hooks() {}
 138  
 139      /**
 140       * Set permalink for oEmbed link discovery.
 141       *
 142       * This method will be called on the page we want to oEmbed.  In most cases,
 143       * you will not need to override this method.  However, if you need to, do
 144       * override in your extended class.
 145       *
 146       * @since 2.6.0
 147       */
 148  	protected function set_permalink() {
 149          $url = bp_get_requested_url();
 150  
 151          // Remove querystring from bp_get_requested_url().
 152          if ( false !== strpos( bp_get_requested_url(), '?' ) ) {
 153              $url = substr( bp_get_requested_url(), 0, strpos( bp_get_requested_url(), '?' ) );
 154          }
 155  
 156          return $url;
 157      }
 158  
 159      /** HELPERS *************************************************************/
 160  
 161      /**
 162       * Get the item ID when filtering the oEmbed HTML.
 163       *
 164       * Should only be used during the 'embed_html' hook.
 165       *
 166       * @since 2.6.0
 167       */
 168  	protected function get_item_id() {
 169          return $this->is_page() ? $this->validate_url_to_item_id( $this->set_permalink() ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
 170      }
 171  
 172      /** SET UP **************************************************************/
 173  
 174      /**
 175       * Set up properties.
 176       *
 177       * @since 2.6.0
 178       */
 179  	protected function setup_properties() {
 180          $this->slug_endpoint = sanitize_title( $this->slug_endpoint );
 181      }
 182  
 183      /**
 184       * Hooks! We do the dirty work here, so you don't have to! :)
 185       *
 186       * More hooks are available in the setup_template_parts() method.
 187       *
 188       * @since 2.6.0
 189       */
 190  	protected function setup_hooks() {
 191          add_action( 'rest_api_init',    array( $this, 'register_route' ) );
 192          add_action( 'bp_embed_content', array( $this, 'inject_content' ) );
 193  
 194          add_filter( 'embed_template', array( $this, 'setup_template_parts' ) );
 195          add_filter( 'post_embed_url', array( $this, 'filter_embed_url' ) );
 196          add_filter( 'embed_html',     array( $this, 'filter_embed_html' ) );
 197          add_filter( 'oembed_discovery_links', array( $this, 'add_oembed_discovery_links' ) );
 198          add_filter( 'rest_pre_serve_request', array( $this, 'oembed_xml_request' ), 20, 4 );
 199      }
 200  
 201      /** HOOKS ***************************************************************/
 202  
 203      /**
 204       * Register the oEmbed REST API route.
 205       *
 206       * @since 2.6.0
 207       */
 208  	public function register_route() {
 209          /** This filter is documented in wp-includes/class-wp-oembed-controller.php */
 210          $maxwidth = apply_filters( 'oembed_default_width', 600 );
 211  
 212          // Required arguments.
 213          $args = array(
 214              'url'      => array(
 215                  'required'          => true,
 216                  'sanitize_callback' => 'esc_url_raw',
 217              ),
 218              'format'   => array(
 219                  'default'           => 'json',
 220                  'sanitize_callback' => 'wp_oembed_ensure_format',
 221              ),
 222              'maxwidth' => array(
 223                  'default'           => $maxwidth,
 224                  'sanitize_callback' => 'absint',
 225              )
 226          );
 227  
 228          // Merge custom arguments here.
 229          $args = $args + (array) $this->set_route_args();
 230  
 231          register_rest_route( 'oembed/1.0', "/embed/{$this->slug_endpoint}", array(
 232              array(
 233                  'methods'  => WP_REST_Server::READABLE,
 234                  'callback' => array( $this, 'get_item' ),
 235                  'args'     => $args
 236              ),
 237          ) );
 238      }
 239  
 240      /**
 241       * Set up custom embed template parts for BuddyPress use.
 242       *
 243       * @since 2.6.0
 244       *
 245       * @param string $template File path to current embed template.
 246       * @return string
 247       */
 248  	public function setup_template_parts( $template ) {
 249          // Determine if we're on our BP page.
 250          if ( ! $this->is_page() || is_404() ) {
 251              return $template;
 252          }
 253  
 254          // Set up some BP-specific embed template overrides.
 255          add_action( 'get_template_part_embed', array( $this, 'content_buffer_start' ), -999, 2 );
 256          add_action( 'get_footer',              array( $this, 'content_buffer_end' ), -999 );
 257  
 258          // Return the original WP embed template.
 259          return $template;
 260      }
 261  
 262      /**
 263       * Start object buffer.
 264       *
 265       * We're going to override WP's get_template_part( 'embed, 'content' ) call
 266       * and inject our own template for BuddyPress use.
 267       *
 268       * @since 2.6.0
 269       *
 270       * @param string $slug Template slug.
 271       * @param string $name Template name.
 272       */
 273  	public function content_buffer_start( $slug, $name ) {
 274          if ( 'embed' !== $slug || 'content' !== $name ) {
 275              return;
 276          }
 277  
 278          // Start the buffer to wipe out get_template_part( 'embed, 'content' ).
 279          ob_start();
 280      }
 281  
 282      /**
 283       * End object buffer.
 284       *
 285       * We're going to override WP's get_template_part( 'embed, 'content' ) call
 286       * and inject our own template for BuddyPress use.
 287       *
 288       * @since 2.6.0
 289       *
 290       * @param string $name Template name.
 291       */
 292  	public function content_buffer_end( $name ) {
 293          if ( 'embed' !== $name || is_404() ) {
 294              return;
 295          }
 296  
 297          // Wipe out get_template_part( 'embed, 'content' ).
 298          ob_end_clean();
 299  
 300          // Start our custom BuddyPress embed template!
 301          echo '<div ';
 302          post_class( 'wp-embed' );
 303          echo '>';
 304  
 305          // Template part for our embed header.
 306          bp_get_asset_template_part( 'embeds/header', bp_current_component() );
 307  
 308          /**
 309           * Inject BuddyPress embed content on this hook.
 310           *
 311           * You shouldn't really need to use this if you extend the
 312           * {@link BP_oEmbed_Component} class.
 313           *
 314           * @since 2.6.0
 315           */
 316          do_action( 'bp_embed_content' );
 317  
 318          // Template part for our embed footer.
 319          bp_get_asset_template_part( 'embeds/footer', bp_current_component() );
 320  
 321          echo '</div>';
 322      }
 323  
 324      /**
 325       * Adds oEmbed discovery links on single activity pages.
 326       *
 327       * @since 2.6.0
 328       *
 329       * @param string $retval Current discovery links.
 330       * @return string
 331       */
 332  	public function add_oembed_discovery_links( $retval ) {
 333          if ( ! $this->is_page() ) {
 334              return $retval;
 335          }
 336  
 337          $permalink = $this->set_permalink();
 338          if ( empty( $permalink ) ) {
 339              return $retval;
 340          }
 341  
 342          add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
 343  
 344          $retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n";
 345  
 346          if ( class_exists( 'SimpleXMLElement' ) ) {
 347              $retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n";
 348          }
 349  
 350          remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
 351  
 352          return $retval;
 353      }
 354  
 355      /**
 356       * Fetch our oEmbed response data to return.
 357       *
 358       * A simplified version of {@link get_oembed_response_data()}.
 359       *
 360       * @since 2.6.0
 361       *
 362       * @link http://oembed.com/ View the 'Response parameters' section for more details.
 363       *
 364       * @param array $item  Custom oEmbed response data.
 365       * @param int   $width The requested width.
 366       * @return array
 367       */
 368  	protected function get_oembed_response_data( $item, $width ) {
 369          $data = wp_parse_args( $item, array(
 370              'version'       => '1.0',
 371              'provider_name' => get_bloginfo( 'name' ),
 372              'provider_url'  => get_home_url(),
 373              'author_name'   => get_bloginfo( 'name' ),
 374              'author_url'    => get_home_url(),
 375              'title'         => ucfirst( $this->slug_endpoint ),
 376              'type'          => 'rich',
 377          ) );
 378  
 379          /** This filter is documented in /wp-includes/embed.php */
 380          $min_max_width = apply_filters( 'oembed_min_max_width', array(
 381              'min' => 200,
 382              'max' => 600
 383          ) );
 384  
 385          $width  = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
 386          $height = max( ceil( $width / 16 * 9 ), 200 );
 387  
 388          $data['width']  = absint( $width );
 389          $data['height'] = absint( $height );
 390  
 391          // Set 'html' parameter.
 392          if ( 'video' === $data['type'] || 'rich' === $data['type'] ) {
 393              // Fake a WP post so we can use get_post_embed_html().
 394              $post = new stdClass;
 395              $post->post_content = $data['content'];
 396              $post->post_title   = $data['title'];
 397  
 398              $data['html'] = get_post_embed_html( $data['width'], $data['height'], $post );
 399          }
 400  
 401          // Remove temporary parameters.
 402          unset( $data['content'] );
 403  
 404          return $data;
 405      }
 406  
 407      /**
 408       * Callback for the API endpoint.
 409       *
 410       * Returns the JSON object for the item.
 411       *
 412       * @since 2.6.0
 413       *
 414       * @param WP_REST_Request $request Full data about the request.
 415       * @return WP_Error|array oEmbed response data or WP_Error on failure.
 416       */
 417  	public function get_item( $request ) {
 418          $url = $request['url'];
 419  
 420          $data = false;
 421  
 422          $item_id = (int) $this->validate_url_to_item_id( $url );
 423  
 424          if ( ! empty( $item_id ) ) {
 425              // Add markers to tell that we're embedding a single activity.
 426              // This is needed for various oEmbed response data filtering.
 427              if ( ! isset( buddypress()->{$this->slug_endpoint} ) || ! buddypress()->{$this->slug_endpoint} ) {
 428                  buddypress()->{$this->slug_endpoint} = new stdClass;
 429              }
 430              buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url;
 431              buddypress()->{$this->slug_endpoint}->embedid_in_progress  = $item_id;
 432  
 433              // Save custom route args as well.
 434              $custom_args = array_keys( (array) $this->set_route_args() );
 435              if ( ! empty( $custom_args ) ) {
 436                  buddypress()->{$this->slug_endpoint}->embedargs_in_progress = array();
 437  
 438                  foreach( $custom_args as $arg ) {
 439                      if ( isset( $request[ $arg ] ) ) {
 440                          buddypress()->{$this->slug_endpoint}->embedargs_in_progress[ $arg ] = $request[ $arg ];
 441                      }
 442                  }
 443              }
 444  
 445              // Grab custom oEmbed response data.
 446              $item = $this->set_oembed_response_data( $item_id );
 447  
 448              // Set oEmbed response data.
 449              $data = $this->get_oembed_response_data( $item, $request['maxwidth'] );
 450          }
 451  
 452          if ( ! $data ) {
 453              return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
 454          }
 455  
 456          return $data;
 457      }
 458  
 459      /**
 460       * If oEmbed request wants XML, return XML instead of JSON.
 461       *
 462       * Basically a copy of {@link _oembed_rest_pre_serve_request()}. Unfortunate
 463       * that we have to duplicate this just for a URL check.
 464       *
 465       * @since 2.6.0
 466       *
 467       * @param bool                      $served  Whether the request has already been served.
 468       * @param WP_HTTP_ResponseInterface $result  Result to send to the client. Usually a WP_REST_Response.
 469       * @param WP_REST_Request           $request Request used to generate the response.
 470       * @param WP_REST_Server            $server  Server instance.
 471       * @return bool
 472       */
 473  	public function oembed_xml_request( $served, $result, $request, $server ) {
 474          $params = $request->get_params();
 475  
 476          if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
 477              return $served;
 478          }
 479  
 480          // Validate URL against our oEmbed endpoint. If not valid, bail.
 481          // This is our mod to _oembed_rest_pre_serve_request().
 482          $query_params = $request->get_query_params();
 483          if ( false === $this->validate_url_to_item_id( $query_params['url'] ) ) {
 484              return $served;
 485          }
 486  
 487          // Embed links inside the request.
 488          $data = $server->response_to_data( $result, false );
 489  
 490          if ( ! class_exists( 'SimpleXMLElement' ) ) {
 491              status_header( 501 );
 492              die( get_status_header_desc( 501 ) );
 493          }
 494  
 495          $result = _oembed_create_xml( $data );
 496  
 497          // Bail if there's no XML.
 498          if ( ! $result ) {
 499              status_header( 501 );
 500              return get_status_header_desc( 501 );
 501          }
 502  
 503          if ( ! headers_sent() ) {
 504              $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
 505          }
 506  
 507          echo $result;
 508  
 509          return true;
 510      }
 511  
 512      /**
 513       * Pass our BuddyPress activity permalink for embedding.
 514       *
 515       * @since 2.6.0
 516       *
 517       * @see bp_activity_embed_rest_route_callback()
 518       *
 519       * @param string $retval Current embed URL.
 520       * @return string
 521       */
 522  	public function filter_embed_url( $retval ) {
 523          if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
 524              return $retval;
 525          }
 526  
 527          $url = $this->is_page() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress;
 528          $url = trailingslashit( $url );
 529  
 530          // This is for the 'WordPress Embed' block
 531          // @see bp_activity_embed_comments_button().
 532          if ( 'the_permalink' !== current_filter() ) {
 533              $url = add_query_arg( 'embed', 'true', trailingslashit( $url ) );
 534  
 535              // Add custom route args to iframe.
 536              if ( isset( buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) && buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) {
 537                  foreach( buddypress()->{$this->slug_endpoint}->embedargs_in_progress as $key => $value ) {
 538                      $url = add_query_arg( $key, $value, $url );
 539                  }
 540              }
 541          }
 542  
 543          return $url;
 544      }
 545  
 546      /**
 547       * Filters the embed HTML for our BP oEmbed endpoint.
 548       *
 549       * @since 2.6.0
 550       *
 551       * @param string $retval Current embed HTML.
 552       * @return string
 553       */
 554  	public function filter_embed_html( $retval ) {
 555          if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
 556              return $retval;
 557          }
 558  
 559          $url = $this->set_permalink();
 560  
 561          $item_id = $this->is_page() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
 562  
 563          // Change 'Embedded WordPress Post' to custom title.
 564          $custom_title = $this->set_iframe_title( $item_id );
 565          if ( ! empty( $custom_title ) ) {
 566              $title_pos = strpos( $retval, 'title=' ) + 7;
 567              $title_end_pos = strpos( $retval, '"', $title_pos );
 568  
 569              $retval = substr_replace( $retval, esc_attr( $custom_title ), $title_pos, $title_end_pos - $title_pos );
 570          }
 571  
 572          // Add 'max-width' CSS attribute to IFRAME.
 573          // This will make our oEmbeds responsive.
 574          if ( false === strpos( $retval, 'style="max-width' ) ) {
 575              $retval = str_replace( '<iframe', '<iframe style="max-width:100%"', $retval );
 576          }
 577  
 578          // Remove default <blockquote>.
 579          $retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
 580  
 581          // Set up new fallback HTML
 582          // @todo Maybe use KSES?
 583          $fallback_html = $this->set_fallback_html( $item_id );
 584  
 585          /**
 586           * Dynamic filter to return BP oEmbed HTML.
 587           *
 588           * @since 2.6.0
 589           *
 590           * @var string $retval
 591           */
 592          return apply_filters( "bp_{$this->slug_endpoint}_embed_html", $fallback_html . $retval );
 593      }
 594  
 595      /**
 596       * Append our custom slug endpoint to oEmbed endpoint URL.
 597       *
 598       * Meant to be used as a filter on 'rest_url' before any call to
 599       * {@link get_oembed_endpoint_url()} is used.
 600       *
 601       * @since 2.6.0
 602       *
 603       * @see add_oembed_discovery_links()
 604       *
 605       * @param string $retval Current oEmbed endpoint URL.
 606       * @return string
 607       */
 608  	public function filter_rest_url( $retval = '' ) {
 609          return $retval . "/{$this->slug_endpoint}";
 610      }
 611  
 612      /**
 613       * Inject content into the embed template.
 614       *
 615       * @since 2.6.0
 616       */
 617  	public function inject_content() {
 618          if ( ! $this->is_page() ) {
 619              return;
 620          }
 621  
 622          $this->content();
 623      }
 624  }


Generated: Tue Dec 10 01:01:39 2019 Cross-referenced by PHPXref 0.7.1