Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embeds: Replace Facebook embeds handler with oEmbeds #17849

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 132 additions & 67 deletions modules/shortcodes/facebook.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,94 +5,160 @@
* @package Jetpack
*/

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Constants;

define( 'JETPACK_FACEBOOK_EMBED_REGEX', '#^https?://(www.)?facebook\.com/([^/]+)/(posts|photos)/([^/]+)?#' );
define( 'JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX', '#^https?://(www.)?facebook\.com/permalink.php\?([^\s]+)#' );
define( 'JETPACK_FACEBOOK_PHOTO_EMBED_REGEX', '#^https?://(www.)?facebook\.com/photo.php\?([^\s]+)#' );
define( 'JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX', '#^https?://(www.)?facebook\.com/([^/]+)/photos/([^/]+)?#' );
define( 'JETPACK_FACEBOOK_VIDEO_EMBED_REGEX', '#^https?://(www.)?facebook\.com/(?:video.php|watch\/?)\?([^\s]+)#' );
define( 'JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX', '#^https?://(www.)?facebook\.com/([^/]+)/videos/([^/]+)?#' );

/*
* Example URL: https://www.facebook.com/VenusWilliams/posts/10151647007373076
*/
wp_embed_register_handler( 'facebook', JETPACK_FACEBOOK_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
add_action( 'init', 'jetpack_facebook_enable_embeds' );
} else {
jetpack_facebook_enable_embeds();
}

/*
* Example URL: https://www.facebook.com/permalink.php?id=222622504529111&story_fbid=559431180743788
/**
* Register Facebook as oembed provider, and add required filters for the API request.
* Register [facebook] shortcode.
*
* @since 9.2.0
*/
wp_embed_register_handler( 'facebook-alternate', JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
function jetpack_facebook_enable_embeds() {
$facebook_oembeds = array(
'#https?://www\.facebook\.com/.*/posts/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/.*/activity/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/.*/photos/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/photo(s/|\.php).*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/permalink\.php.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/media/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/questions/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/notes/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_post', true ),
'#https?://www\.facebook\.com/.*/videos/.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_video', true ),
'#https?://www\.facebook\.com/video\.php.*#i' => array( 'https://graph.facebook.com/v9.0/oembed_video', true ),
);

foreach ( $facebook_oembeds as $pattern => $embed_data ) {
wp_oembed_remove_provider( $pattern ); // Remove Core's oEmbed handler, if present (WP <= 5.5.2).
wp_oembed_add_provider( $pattern, $embed_data[0], $embed_data[1] );
}

/*
* Photos are handled on a different endpoint; e.g. https://www.facebook.com/photo.php?fbid=10151609960150073&set=a.398410140072.163165.106666030072&type=1
*/
wp_embed_register_handler( 'facebook-photo', JETPACK_FACEBOOK_PHOTO_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
/**
* Add auth token required by Facebook's oEmbed REST API, or proxy through WP.com.
*/
add_filter( 'oembed_fetch_url', 'jetpack_facebook_oembed_fetch_url', 10, 2 );

/*
* Photos (from pages for example) can be at
*/
wp_embed_register_handler( 'facebook-alternate-photo', JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
/**
* Add JP auth headers if we're proxying through WP.com.
*/
add_filter( 'oembed_remote_get_args', 'jetpack_facebook_oembed_remote_get_args', 10, 2 );

/**
* Add the shortcode.
*/
add_shortcode( 'facebook', 'jetpack_facebook_shortcode_handler' );
}

/*
* Videos
/**
* Add auth token required by Facebook's oEmbed REST API, or proxy through WP.com.
*
* @since 9.2.0
*
* Formats:
* https://www.facebook.com/video.php?v=2836814009877992
* https://www.facebook.com/watch/?v=2836814009877992
* @param string $provider URL of the oEmbed provider.
* @param string $url URL of the content to be embedded.
*
* @return string
*/
wp_embed_register_handler( 'facebook-video', JETPACK_FACEBOOK_VIDEO_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
function jetpack_facebook_oembed_fetch_url( $provider, $url ) {
if ( ! wp_startswith( $provider, 'https://graph.facebook.com/v9.0/oembed_' ) ) {
return $provider;
}

/*
* Videos https://www.facebook.com/WhiteHouse/videos/10153398464269238/
*/
wp_embed_register_handler( 'facebook-alternate-video', JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
// Get a set of URL and parameters supported by Facebook.
$clean_parameters = array(); // jetpack_facebook_get_allowed_parameters( $url ); // FIXME!

// Replace existing URL by our clean version.
if ( ! empty( $clean_parameters['url'] ) ) {
$provider = add_query_arg( 'url', rawurlencode( $clean_parameters['url'] ), $provider );
}

// Our shortcode supports the width param, but the API expects maxwidth.
if ( ! empty( $clean_parameters['width'] ) ) {
$provider = add_query_arg( 'maxwidth', $clean_parameters['width'], $provider );
}

if ( ! empty( $clean_parameters['hidecaption'] ) ) {
$provider = add_query_arg( 'hidecaption', true, $provider );
}

$access_token = jetpack_facebook_get_access_token();

if ( ! empty( $access_token ) ) {
return add_query_arg( 'access_token', $access_token, $provider );
}

// If we don't have an access token, we go through the WP.com proxy instead.
// To that end, we need to make sure that we're connected to WP.com.
if ( ! Jetpack::is_active_and_not_offline_mode() ) {
return $provider;
}

$site_id = \Jetpack_Options::get_option( 'id' );
$wpcom_oembed_proxy = Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ) . "/oembed/1.0/sites/$site_id/proxy";
return str_replace(
array(
'https://graph.facebook.com/v9.0/oembed_page',
'https://graph.facebook.com/v9.0/oembed_post',
'https://graph.facebook.com/v9.0/oembed_video',
),
$wpcom_oembed_proxy,
$provider
);
}

/**
* Callback to modify output of embedded Facebook posts.
* Add JP auth headers if we're proxying through WP.com.
*
* @since 9.2.0
*
* @param array $matches Regex partial matches against the URL passed.
* @param array $attr Attributes received in embed response.
* @param string $url Requested URL to be embedded.
* @return string Facebook embed markup.
* @param array $args oEmbed remote get arguments.
* @param string $url URL to be inspected.
*/
function jetpack_facebook_embed_handler( $matches, $attr, $url ) {
if (
false !== strpos( $url, 'video.php' )
|| false !== strpos( $url, '/videos/' )
|| false !== strpos( $url, '/watch' )
) {
$embed = sprintf( '<div class="fb-video" data-allowfullscreen="true" data-href="%s"></div>', esc_url( $url ) );
} else {
$width = 552; // As of 01/2017, the default width of Facebook embeds when no width attribute provided.

global $content_width;
if ( isset( $content_width ) ) {
$width = min( $width, $content_width );
}

$embed = sprintf( '<div class="fb-post" data-href="%s" data-width="%s"></div>', esc_url( $url ), esc_attr( $width ) );
function jetpack_facebook_oembed_remote_get_args( $args, $url ) {
if ( ! wp_startswith( $url, Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ) . '/oembed/1.0/' ) ) {
return $args;
}

// Skip rendering scripts in an AMP context.
if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
return $embed;
}
$method = 'GET';
$signed_request = Client::build_signed_request(
compact( 'url', 'method' )
);

// since Facebook is a faux embed, we need to load the JS SDK in the wpview embed iframe.
if (
defined( 'DOING_AJAX' )
&& DOING_AJAX
// No need to check for a nonce here, that's already handled by Core further up.
&& ! empty( $_POST['action'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
&& 'parse-embed' === $_POST['action'] // phpcs:ignore WordPress.Security.NonceVerification.Missing
) {
ob_start();
wp_scripts()->do_items( array( 'jetpack-facebook-embed' ) );
$scripts = ob_get_clean();
return $embed . $scripts;
} else {
wp_enqueue_script( 'jetpack-facebook-embed' );
return $embed;
}
return $signed_request['request'];
}

/**
* Fetches a Facebook API access token used for query for Facebook embed information, if one is set.
*
* @return string The access token or ''
*/
function jetpack_facebook_get_access_token() {
/**
* Filters the Facebook embed token that is used for querying the Facebook API.
*
* When this token is set, requests are not proxied through the WordPress.com API. Instead, a request is made directly to the
* Facebook API to query for information about the embed which should provide a performance benefit.
*
* @module shortcodes
*
* @since 9.2.0
*
* @param string string The access token set via the JETPACK_FACEBOOK_EMBED_TOKEN constant.
*/
return (string) apply_filters( 'jetpack_facebook_embed_token', (string) Constants::get_constant( 'JETPACK_FACEBOOK_EMBED_TOKEN' ) );
}

/**
Expand All @@ -116,4 +182,3 @@ function jetpack_facebook_shortcode_handler( $atts ) {

return $wp_embed->shortcode( $atts, $atts['url'] );
}
add_shortcode( 'facebook', 'jetpack_facebook_shortcode_handler' );