BP_Core_oEmbed_Extension

API for responding and returning a custom oEmbed request.

Defined (1)

The class is defined in the following location(s).

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