[ 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                  'permission_callback' => '__return_true',
 236                  'args'                => $args
 237              ),
 238          ) );
 239      }
 240  
 241      /**
 242       * Set up custom embed template parts for BuddyPress use.
 243       *
 244       * @since 2.6.0
 245       *
 246       * @param string $template File path to current embed template.
 247       * @return string
 248       */
 249  	public function setup_template_parts( $template ) {
 250          // Determine if we're on our BP page.
 251          if ( ! $this->is_page() || is_404() ) {
 252              return $template;
 253          }
 254  
 255          // Set up some BP-specific embed template overrides.
 256          add_action( 'get_template_part_embed', array( $this, 'content_buffer_start' ), -999, 2 );
 257          add_action( 'get_footer',              array( $this, 'content_buffer_end' ), -999 );
 258  
 259          // Return the original WP embed template.
 260          return $template;
 261      }
 262  
 263      /**
 264       * Start object buffer.
 265       *
 266       * We're going to override WP's get_template_part( 'embed, 'content' ) call
 267       * and inject our own template for BuddyPress use.
 268       *
 269       * @since 2.6.0
 270       *
 271       * @param string $slug Template slug.
 272       * @param string $name Template name.
 273       */
 274  	public function content_buffer_start( $slug, $name ) {
 275          if ( 'embed' !== $slug || 'content' !== $name ) {
 276              return;
 277          }
 278  
 279          // Start the buffer to wipe out get_template_part( 'embed, 'content' ).
 280          ob_start();
 281      }
 282  
 283      /**
 284       * End object buffer.
 285       *
 286       * We're going to override WP's get_template_part( 'embed, 'content' ) call
 287       * and inject our own template for BuddyPress use.
 288       *
 289       * @since 2.6.0
 290       *
 291       * @param string $name Template name.
 292       */
 293  	public function content_buffer_end( $name ) {
 294          if ( 'embed' !== $name || is_404() ) {
 295              return;
 296          }
 297  
 298          // Wipe out get_template_part( 'embed, 'content' ).
 299          ob_end_clean();
 300  
 301          // Start our custom BuddyPress embed template!
 302          echo '<div ';
 303          post_class( 'wp-embed' );
 304          echo '>';
 305  
 306          // Template part for our embed header.
 307          bp_get_asset_template_part( 'embeds/header', bp_current_component() );
 308  
 309          /**
 310           * Inject BuddyPress embed content on this hook.
 311           *
 312           * You shouldn't really need to use this if you extend the
 313           * {@link BP_oEmbed_Component} class.
 314           *
 315           * @since 2.6.0
 316           */
 317          do_action( 'bp_embed_content' );
 318  
 319          // Template part for our embed footer.
 320          bp_get_asset_template_part( 'embeds/footer', bp_current_component() );
 321  
 322          echo '</div>';
 323      }
 324  
 325      /**
 326       * Adds oEmbed discovery links on single activity pages.
 327       *
 328       * @since 2.6.0
 329       *
 330       * @param string $retval Current discovery links.
 331       * @return string
 332       */
 333  	public function add_oembed_discovery_links( $retval ) {
 334          if ( ! $this->is_page() ) {
 335              return $retval;
 336          }
 337  
 338          $permalink = $this->set_permalink();
 339          if ( empty( $permalink ) ) {
 340              return $retval;
 341          }
 342  
 343          add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
 344  
 345          $retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n";
 346  
 347          if ( class_exists( 'SimpleXMLElement' ) ) {
 348              $retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n";
 349          }
 350  
 351          remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
 352  
 353          return $retval;
 354      }
 355  
 356      /**
 357       * Fetch our oEmbed response data to return.
 358       *
 359       * A simplified version of {@link get_oembed_response_data()}.
 360       *
 361       * @since 2.6.0
 362       *
 363       * @link http://oembed.com/ View the 'Response parameters' section for more details.
 364       *
 365       * @param array $item  Custom oEmbed response data.
 366       * @param int   $width The requested width.
 367       * @return array
 368       */
 369  	protected function get_oembed_response_data( $item, $width ) {
 370          $data = bp_parse_args(
 371              $item,
 372              array(
 373                  'version'       => '1.0',
 374                  'provider_name' => get_bloginfo( 'name' ),
 375                  'provider_url'  => get_home_url(),
 376                  'author_name'   => get_bloginfo( 'name' ),
 377                  'author_url'    => get_home_url(),
 378                  'title'         => ucfirst( $this->slug_endpoint ),
 379                  'type'          => 'rich',
 380              )
 381          );
 382  
 383          /** This filter is documented in /wp-includes/embed.php */
 384          $min_max_width = apply_filters( 'oembed_min_max_width', array(
 385              'min' => 200,
 386              'max' => 600
 387          ) );
 388  
 389          $width  = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
 390          $height = max( ceil( $width / 16 * 9 ), 200 );
 391  
 392          $data['width']  = absint( $width );
 393          $data['height'] = absint( $height );
 394  
 395          // Set 'html' parameter.
 396          if ( 'video' === $data['type'] || 'rich' === $data['type'] ) {
 397              // Fake a WP post so we can use get_post_embed_html().
 398              $post = new stdClass;
 399              $post->post_content = $data['content'];
 400              $post->post_title   = $data['title'];
 401  
 402              $data['html'] = get_post_embed_html( $data['width'], $data['height'], $post );
 403          }
 404  
 405          // Remove temporary parameters.
 406          unset( $data['content'] );
 407  
 408          return $data;
 409      }
 410  
 411      /**
 412       * Callback for the API endpoint.
 413       *
 414       * Returns the JSON object for the item.
 415       *
 416       * @since 2.6.0
 417       *
 418       * @param WP_REST_Request $request Full data about the request.
 419       * @return WP_Error|array oEmbed response data or WP_Error on failure.
 420       */
 421  	public function get_item( $request ) {
 422          $url = $request['url'];
 423  
 424          $data = false;
 425  
 426          $item_id = (int) $this->validate_url_to_item_id( $url );
 427  
 428          if ( ! empty( $item_id ) ) {
 429              // Add markers to tell that we're embedding a single activity.
 430              // This is needed for various oEmbed response data filtering.
 431              if ( ! isset( buddypress()->{$this->slug_endpoint} ) || ! buddypress()->{$this->slug_endpoint} ) {
 432                  buddypress()->{$this->slug_endpoint} = new stdClass;
 433              }
 434              buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url;
 435              buddypress()->{$this->slug_endpoint}->embedid_in_progress  = $item_id;
 436  
 437              // Save custom route args as well.
 438              $custom_args = array_keys( (array) $this->set_route_args() );
 439              if ( ! empty( $custom_args ) ) {
 440                  buddypress()->{$this->slug_endpoint}->embedargs_in_progress = array();
 441  
 442                  foreach( $custom_args as $arg ) {
 443                      if ( isset( $request[ $arg ] ) ) {
 444                          buddypress()->{$this->slug_endpoint}->embedargs_in_progress[ $arg ] = $request[ $arg ];
 445                      }
 446                  }
 447              }
 448  
 449              // Grab custom oEmbed response data.
 450              $item = $this->set_oembed_response_data( $item_id );
 451  
 452              // Set oEmbed response data.
 453              $data = $this->get_oembed_response_data( $item, $request['maxwidth'] );
 454          }
 455  
 456          if ( ! $data ) {
 457              return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
 458          }
 459  
 460          return $data;
 461      }
 462  
 463      /**
 464       * If oEmbed request wants XML, return XML instead of JSON.
 465       *
 466       * Basically a copy of {@link _oembed_rest_pre_serve_request()}. Unfortunate
 467       * that we have to duplicate this just for a URL check.
 468       *
 469       * @since 2.6.0
 470       *
 471       * @param bool                      $served  Whether the request has already been served.
 472       * @param WP_HTTP_ResponseInterface $result  Result to send to the client. Usually a WP_REST_Response.
 473       * @param WP_REST_Request           $request Request used to generate the response.
 474       * @param WP_REST_Server            $server  Server instance.
 475       * @return bool
 476       */
 477  	public function oembed_xml_request( $served, $result, $request, $server ) {
 478          $params = $request->get_params();
 479  
 480          if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
 481              return $served;
 482          }
 483  
 484          // Validate URL against our oEmbed endpoint. If not valid, bail.
 485          // This is our mod to _oembed_rest_pre_serve_request().
 486          $query_params = $request->get_query_params();
 487          if ( ! isset( $query_params['url'] ) || false === $this->validate_url_to_item_id( $query_params['url'] ) ) {
 488              return $served;
 489          }
 490  
 491          // Embed links inside the request.
 492          $data = $server->response_to_data( $result, false );
 493  
 494          if ( ! class_exists( 'SimpleXMLElement' ) ) {
 495              status_header( 501 );
 496              die( get_status_header_desc( 501 ) );
 497          }
 498  
 499          $result = _oembed_create_xml( $data );
 500  
 501          // Bail if there's no XML.
 502          if ( ! $result ) {
 503              status_header( 501 );
 504              return get_status_header_desc( 501 );
 505          }
 506  
 507          if ( ! headers_sent() ) {
 508              $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
 509          }
 510  
 511          echo $result;
 512  
 513          return true;
 514      }
 515  
 516      /**
 517       * Pass our BuddyPress activity permalink for embedding.
 518       *
 519       * @since 2.6.0
 520       *
 521       * @see bp_activity_embed_rest_route_callback()
 522       *
 523       * @param string $retval Current embed URL.
 524       * @return string
 525       */
 526  	public function filter_embed_url( $retval ) {
 527          if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
 528              return $retval;
 529          }
 530  
 531          $url = $this->is_page() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress;
 532          $url = trailingslashit( $url );
 533  
 534          // This is for the 'WordPress Embed' block
 535          // @see bp_activity_embed_comments_button().
 536          if ( 'the_permalink' !== current_filter() ) {
 537              $url = add_query_arg( 'embed', 'true', trailingslashit( $url ) );
 538  
 539              // Add custom route args to iframe.
 540              if ( isset( buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) && buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) {
 541                  foreach( buddypress()->{$this->slug_endpoint}->embedargs_in_progress as $key => $value ) {
 542                      $url = add_query_arg( $key, $value, $url );
 543                  }
 544              }
 545          }
 546  
 547          return $url;
 548      }
 549  
 550      /**
 551       * Filters the embed HTML for our BP oEmbed endpoint.
 552       *
 553       * @since 2.6.0
 554       *
 555       * @param string $retval Current embed HTML.
 556       * @return string
 557       */
 558  	public function filter_embed_html( $retval ) {
 559          if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
 560              return $retval;
 561          }
 562  
 563          $url = $this->set_permalink();
 564  
 565          $item_id = $this->is_page() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
 566  
 567          // Change 'Embedded WordPress Post' to custom title.
 568          $custom_title = $this->set_iframe_title( $item_id );
 569          if ( ! empty( $custom_title ) ) {
 570              $title_pos = strpos( $retval, 'title=' ) + 7;
 571              $title_end_pos = strpos( $retval, '"', $title_pos );
 572  
 573              $retval = substr_replace( $retval, esc_attr( $custom_title ), $title_pos, $title_end_pos - $title_pos );
 574          }
 575  
 576          // Add 'max-width' CSS attribute to IFRAME.
 577          // This will make our oEmbeds responsive.
 578          if ( false === strpos( $retval, 'style="max-width' ) ) {
 579              $retval = str_replace( '<iframe', '<iframe style="max-width:100%"', $retval );
 580          }
 581  
 582          // Remove default <blockquote>.
 583          $retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
 584  
 585          // Set up new fallback HTML
 586          // @todo Maybe use KSES?
 587          $fallback_html = $this->set_fallback_html( $item_id );
 588  
 589          /**
 590           * Dynamic filter to return BP oEmbed HTML.
 591           *
 592           * @since 2.6.0
 593           *
 594           * @var string $retval
 595           */
 596          return apply_filters( "bp_{$this->slug_endpoint}_embed_html", $fallback_html . $retval );
 597      }
 598  
 599      /**
 600       * Append our custom slug endpoint to oEmbed endpoint URL.
 601       *
 602       * Meant to be used as a filter on 'rest_url' before any call to
 603       * {@link get_oembed_endpoint_url()} is used.
 604       *
 605       * @since 2.6.0
 606       *
 607       * @see add_oembed_discovery_links()
 608       *
 609       * @param string $retval Current oEmbed endpoint URL.
 610       * @return string
 611       */
 612  	public function filter_rest_url( $retval = '' ) {
 613          return $retval . "/{$this->slug_endpoint}";
 614      }
 615  
 616      /**
 617       * Inject content into the embed template.
 618       *
 619       * @since 2.6.0
 620       */
 621  	public function inject_content() {
 622          if ( ! $this->is_page() ) {
 623              return;
 624          }
 625  
 626          $this->content();
 627      }
 628  }


Generated: Tue Nov 5 01:00:58 2024 Cross-referenced by PHPXref 0.7.1