Index: src/bp-activity/bp-activity-embeds.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-activity/bp-activity-embeds.php
@@ -0,0 +1,359 @@
+<?php
+/**
+ * Functions related to embedding single activity items externally.
+ *
+ * Relies on WordPress 4.4.
+ *
+ * @since 2.5.0
+ *
+ * @package BuddyPress
+ * @subpackage ActivityEmbeds
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Register REST route for embedding single activity items.
+ *
+ * Almost exactly the same as WP_oEmbed_Controller::register_routes(), but
+ * with our '/activity' slug appended to the endpoint.
+ *
+ * @since 2.5.0
+ */
+function bp_activity_embed_register_rest_route() {
+	/**
+	 * Filter the maxwidth oEmbed parameter.
+	 *
+	 * @since 4.4.0
+	 *
+	 * @param int $maxwidth Maximum allowed width. Default 600.
+	 */
+	$maxwidth = apply_filters( 'oembed_default_width', 600 );
+
+	register_rest_route( 'oembed/1.0', '/embed/activity', array(
+		array(
+			'methods'  => WP_REST_Server::READABLE,
+			'callback' => 'bp_activity_embed_rest_route_callback',
+			'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',
+				),
+			),
+		),
+	) );
+
+}
+add_action( 'rest_api_init', 'bp_activity_embed_register_rest_route' );
+
+/**
+ * Callback for the API endpoint.
+ *
+ * Returns the JSON object for the post.
+ *
+ * @since 2.5.0
+ *
+ * @param  WP_REST_Request $request Full data about the request.
+ * @return WP_Error|array oEmbed response data or WP_Error on failure.
+ */
+function bp_activity_embed_rest_route_callback( $request ) {
+	$url = $request['url'];
+
+	$invalid = $data = false;
+
+	if ( bp_core_enable_root_profiles() ) {
+		$domain = bp_get_root_domain();
+	} else {
+		$domain = bp_get_members_directory_permalink();
+	}
+
+	// Check the URL to see if this is a single activity URL.
+	if ( 0 !== strpos( $url, $domain ) ) {
+		$invalid = true;
+	}
+
+	// Check for activity slug.
+	if ( false === strpos( $url, '/' . bp_get_activity_slug() . '/' ) ) {
+		$invalid = true;
+	}
+
+	// Do more checks.
+	if ( false === $invalid ) {
+		$url = trim( untrailingslashit( $url ) );
+
+		// Grab the activity ID.
+		$activity_id = (int) substr(
+			$url,
+			strrpos( $url, '/' ) + 1
+		);
+
+		if ( ! empty( $activity_id ) ) {
+			// Check if activity item still exists.
+			$activity = new BP_Activity_Activity( $activity_id );
+
+			// Okay, we're good to go!
+			if ( ! empty( $activity->component ) ) {
+				// Create dummy post to piggyback off of get_oembed_response_data()
+				$post = bp_theme_compat_create_dummy_post( array(
+					'post_author'  => $activity->user_id,
+					'post_title'   => __( 'Activity', 'buddypress' ),
+					'post_content' => $activity->content,
+
+					// This passes the get_oembed_response_data() check.
+					'post_status'  => 'publish'
+				) );
+
+				// Add markers to tell that we're embedding a single activity.
+				// This needed for various oEmbed response data filtering.
+				buddypress()->activity->embedurl_in_progress = $url;
+				buddypress()->activity->embedid_in_progress  = $activity_id;
+
+				// Use WP's oEmbed response data function.
+				$data = get_oembed_response_data( $post, $request['maxwidth'] );
+
+				// Make one change here to reference our BP user profile
+				$data['author_url'] = bp_core_get_user_domain( $activity->user_id );
+			}
+		}
+	}
+
+	if ( ! $data ) {
+		return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
+	}
+
+	return $data;
+}
+
+/**
+ * Register oEmbed provider on BuddyPress pages.
+ *
+ * This allows single activity items to be embedded.  WP's TinyMCE doesn't
+ * require this somehow, which is why we only do this on BuddyPress pages.
+ *
+ * @since 2.5.0
+ *
+ * @todo Fix up issues with javascript not firing to hide the fallback <blockquote>
+ */
+function bp_activity_embed_add_oembed_provider() {
+	// Only register our provider on BuddyPress pages.
+	if ( false === is_buddypress() ) {
+		return;
+	}
+
+	if ( bp_core_enable_root_profiles() ) {
+		$domain = bp_get_root_domain();
+	} else {
+		$domain = bp_get_members_directory_permalink();
+	}
+
+	add_filter( 'rest_url' , 'bp_activity_embed_filter_rest_url' );
+	wp_oembed_add_provider( $domain . '*/activity/*', get_oembed_endpoint_url() );
+	remove_filter( 'rest_url' , 'bp_activity_embed_filter_rest_url' );
+}
+add_action( 'bp_init', 'bp_activity_embed_add_oembed_provider' );
+
+/**
+ * Use our custom embed template for activity items.
+ *
+ * @since 2.5.0
+ *
+ * @param  string $retval Current embed template
+ * @return string
+ */
+function bp_activity_embed_filter_template( $retval ) {
+	if ( ! bp_is_single_activity() ) {
+		return $retval;
+	}
+
+	// Embed template hierarchy!
+	return bp_locate_template( array(
+		'embeds/template-single-activity.php',
+		'embeds/template.php'
+	) );
+}
+add_filter( 'embed_template', 'bp_activity_embed_filter_template' );
+
+/**
+ * Inject activity content into the embed template.
+ *
+ * @since 2.5.0
+ */
+function bp_activity_embed_inject_content() {
+	if ( ! bp_is_single_activity() ) {
+		return;
+	}
+
+	bp_get_template_part( 'embeds/activity' );
+}
+add_action( 'embed_content', 'bp_activity_embed_inject_content' );
+
+/**
+ * Adds oEmbed discovery links on single activity pages.
+ *
+ * @since 2.5.0
+ *
+ * @param  string $retval Current discovery links.
+ * @return string
+ */
+function bp_activity_embed_oembed_discovery_links( $retval ) {
+	if ( ! bp_is_single_activity() ) {
+		return $retval;
+	}
+
+	$permalink = bp_displayed_user_domain() . bp_get_activity_slug() . '/' . bp_current_action() . '/';
+
+	add_filter( 'rest_url' , 'bp_activity_embed_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' , 'bp_activity_embed_filter_rest_url' );
+
+	return $retval;
+}
+add_filter( 'oembed_discovery_links', 'bp_activity_embed_oembed_discovery_links' );
+
+/**
+ * Pass our BuddyPress activity permalink for embedding.
+ *
+ * @since 2.5.0
+ *
+ * @see bp_activity_embed_rest_route_callback()
+ *
+ * @param  string $retval Current embed URL
+ * @return string
+ */
+function bp_activity_embed_filter_post_embed_url( $retval ) {
+	if ( false === isset( buddypress()->activity->embedurl_in_progress ) && ! bp_is_single_activity() ) {
+		return $retval;
+	}
+
+	$url = bp_is_single_activity() ? bp_displayed_user_domain() . bp_get_activity_slug() . '/' . bp_current_action() . '/' : buddypress()->activity->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 ) );
+	}
+
+	return $url;
+}
+add_filter( 'post_embed_url', 'bp_activity_embed_filter_post_embed_url' );
+
+/**
+ * Filters the embed HTML for the default oEmbed fallback HTML.
+ *
+ * @since 2.5.0
+ *
+ * @param  string $retval Current embed HTML
+ * @return string
+ */
+function bp_activity_embed_filter_html( $retval ) {
+	if ( false === isset( buddypress()->activity->embedurl_in_progress ) && ! bp_is_single_activity() ) {
+		return $retval;
+	}
+
+	// Change 'Embedded WordPress Post' to 'Embedded Activity Item'
+	$retval = str_replace( __( 'Embedded WordPress Post' ), __( 'Embedded Activity Item', 'buddypress' ), $retval );
+
+	// Remove default <blockquote>
+	$retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
+
+	// Set up new <blockquote>
+	$activity_id = bp_is_single_activity() ? bp_current_action() : buddypress()->activity->embedid_in_progress;
+	$activity    = new BP_Activity_Activity( $activity_id );
+	$mentionname = bp_activity_do_mentions() ? ' (@' . bp_activity_get_user_mentionname( $activity->user_id ) . ')' : '';
+	$date        = date_i18n( get_option( 'date_format' ), strtotime( $activity->date_recorded ) );
+
+	// 'wp-embedded-content' CSS class is necessary due to how the embed JS works.
+	$blockquote = sprintf( '<blockquote class="wp-embedded-content bp-activity-item">%1$s%2$s %3$s</blockquote>',
+		apply_filters( 'bp_get_activity_content_body', $activity->content ),
+		'- ' . bp_core_get_user_displayname( $activity->user_id ) . $mentionname,
+		'<a href="' . esc_url( bp_activity_get_permalink( $activity_id ) ) . '">' . $date . '</a>'
+	);
+
+	/**
+	 * Filters the fallback HTML used when embedding a BP activity item.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param string               $blockquote Current fallback HTML
+	 * @param BP_Activity_Activity $activity   Activity object
+	 */
+	$blockquote = apply_filters( 'bp_activity_embed_fallback_html', $blockquote, $activity );
+
+	// Add our custom <blockquote>
+	return $blockquote . $retval;
+}
+add_filter( 'embed_html', 'bp_activity_embed_filter_html' );
+
+/**
+ * Prints the markup for the activity embed comments button.
+ *
+ * @since 2.5.0
+ */
+function bp_activity_embed_comments_button() {
+	if ( ! bp_is_single_activity() ) {
+		return;
+	}
+
+	// Make sure our custom permalink shows up in the 'WordPress Embed' block.
+	add_filter( 'the_permalink', 'bp_activity_embed_filter_post_embed_url' );
+
+	// Only show comment bubble if we have some activity comments.
+	$count = bp_activity_get_comment_count();
+	if ( empty( $count ) ) {
+		return;
+	}
+?>
+
+	<div class="wp-embed-comments">
+		<a href="<?php bp_activity_thread_permalink(); ?>">
+			<span class="dashicons dashicons-admin-comments"></span>
+			<?php
+			printf(
+				_n(
+					'%s <span class="screen-reader-text">Comment</span>',
+					'%s <span class="screen-reader-text">Comments</span>',
+					$count
+				),
+				number_format_i18n( $count )
+			);
+			?>
+		</a>
+	</div>
+
+<?php
+}
+add_action( 'embed_content_meta', 'bp_activity_embed_comments_button', 5 );
+
+/**
+ * Append '/activity' 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.5.0
+ *
+ * @see bp_activity_embed_oembed_discovery_links()
+ * @see bp_activity_embed_add_oembed_provider()
+ *
+ * @param  string $retval Current oEmbed endpoint URL
+ * @return string
+ */
+function bp_activity_embed_filter_rest_url( $retval = '' ) {
+	return $retval . '/activity';
+}
\ No newline at end of file
Index: src/bp-activity/bp-activity-loader.php
===================================================================
--- src/bp-activity/bp-activity-loader.php
+++ src/bp-activity/bp-activity-loader.php
@@ -31,6 +31,7 @@
 			array(
 				'adminbar_myaccount_order' => 10,
 				'search_query_arg' => 'activity_search',
+				'features' => array( 'embeds' )
 			)
 		);
 	}
@@ -67,6 +68,12 @@
 			$includes[] = 'akismet';
 		}
 
+		// Embeds - only applicable for WP 4.4+
+		if ( bp_get_major_wp_version() >= 4.4 && bp_is_active( $this->id, 'embeds' ) ) {
+			$includes[] = 'embeds';
+		}
+
+		// Admin-specific
 		if ( is_admin() ) {
 			$includes[] = 'admin';
 		}
Index: src/bp-core/bp-core-theme-compatibility.php
===================================================================
--- src/bp-core/bp-core-theme-compatibility.php
+++ src/bp-core/bp-core-theme-compatibility.php
@@ -650,6 +650,55 @@
 }
 
 /**
+ * Create a dummy WP_Post object.
+ *
+ * @since 2.5.0
+ *
+ * @param  array $args Array of optional arguments. Arguments parallel the properties
+ *                    of {@link WP_Post}; see that class for more details.
+ * @return WP_Post
+ */
+function bp_theme_compat_create_dummy_post( $args = array() ) {
+	$dummy = wp_parse_args( $args, array(
+		'ID'                    => -9999,
+		'post_status'           => 'public',
+		'post_author'           => 0,
+		'post_parent'           => 0,
+		'post_type'             => 'page',
+		'post_date'             => 0,
+		'post_date_gmt'         => 0,
+		'post_modified'         => 0,
+		'post_modified_gmt'     => 0,
+		'post_content'          => '',
+		'post_title'            => '',
+		'post_excerpt'          => '',
+		'post_content_filtered' => '',
+		'post_mime_type'        => '',
+		'post_password'         => '',
+		'post_name'             => '',
+		'guid'                  => '',
+		'menu_order'            => 0,
+		'pinged'                => '',
+		'to_ping'               => '',
+		'ping_status'           => '',
+		'comment_status'        => 'closed',
+		'comment_count'         => 0,
+		'filter'                => 'raw',
+
+		'is_404'                => false,
+		'is_page'               => false,
+		'is_single'             => false,
+		'is_archive'            => false,
+		'is_tax'                => false,
+	) );
+
+	// Create the dummy post.
+	$post = new WP_Post( (object) $dummy );
+
+	return $post;
+}
+
+/**
  * Populate various WordPress globals with dummy data to prevent errors.
  *
  * This dummy data is necessary because theme compatibility essentially fakes
@@ -670,7 +719,7 @@
 
 	// Switch defaults if post is set.
 	if ( isset( $wp_query->post ) ) {
-		$dummy = wp_parse_args( $args, array(
+		$args = wp_parse_args( $args, array(
 			'ID'                    => $wp_query->post->ID,
 			'post_status'           => $wp_query->post->post_status,
 			'post_author'           => $wp_query->post->post_author,
@@ -695,55 +744,16 @@
 			'comment_status'        => $wp_query->post->comment_status,
 			'comment_count'         => $wp_query->post->comment_count,
 			'filter'                => $wp_query->post->filter,
-
-			'is_404'                => false,
-			'is_page'               => false,
-			'is_single'             => false,
-			'is_archive'            => false,
-			'is_tax'                => false,
-		) );
-	} else {
-		$dummy = wp_parse_args( $args, array(
-			'ID'                    => -9999,
-			'post_status'           => 'public',
-			'post_author'           => 0,
-			'post_parent'           => 0,
-			'post_type'             => 'page',
-			'post_date'             => 0,
-			'post_date_gmt'         => 0,
-			'post_modified'         => 0,
-			'post_modified_gmt'     => 0,
-			'post_content'          => '',
-			'post_title'            => '',
-			'post_excerpt'          => '',
-			'post_content_filtered' => '',
-			'post_mime_type'        => '',
-			'post_password'         => '',
-			'post_name'             => '',
-			'guid'                  => '',
-			'menu_order'            => 0,
-			'pinged'                => '',
-			'to_ping'               => '',
-			'ping_status'           => '',
-			'comment_status'        => 'closed',
-			'comment_count'         => 0,
-			'filter'                => 'raw',
-
-			'is_404'                => false,
-			'is_page'               => false,
-			'is_single'             => false,
-			'is_archive'            => false,
-			'is_tax'                => false,
 		) );
 	}
 
 	// Bail if dummy post is empty.
-	if ( empty( $dummy ) ) {
+	if ( empty( $args ) ) {
 		return;
 	}
 
 	// Set the $post global.
-	$post = new WP_Post( (object) $dummy );
+	$post = bp_theme_compat_create_dummy_post( $args );
 
 	// Copy the new post global into the main $wp_query.
 	$wp_query->post       = $post;
@@ -751,14 +761,11 @@
 
 	// Prevent comments form from appearing.
 	$wp_query->post_count = 1;
-	$wp_query->is_404     = $dummy['is_404'];
-	$wp_query->is_page    = $dummy['is_page'];
-	$wp_query->is_single  = $dummy['is_single'];
-	$wp_query->is_archive = $dummy['is_archive'];
-	$wp_query->is_tax     = $dummy['is_tax'];
-
-	// Clean up the dummy post.
-	unset( $dummy );
+	$wp_query->is_404     = $post->is_404;
+	$wp_query->is_page    = $post->is_page;
+	$wp_query->is_single  = $post->is_single;
+	$wp_query->is_archive = $post->is_archive;
+	$wp_query->is_tax     = $post->is_tax;
 
 	/**
 	 * Force the header back to 200 status if not a deliberate 404
Index: src/bp-templates/bp-legacy/buddypress/embeds/activity.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/activity.php
@@ -0,0 +1,15 @@
+
+		<?php if ( bp_has_activities( 'display_comments=threaded&show_hidden=true&include=' . bp_current_action() ) ) : ?>
+
+			<?php while ( bp_activities() ) : bp_the_activity(); ?>
+				<div class="wp-embed-excerpt"><?php bp_activity_content_body(); ?></div>
+
+				<p><a href="<?php bp_activity_thread_permalink(); ?>"><?php echo date_i18n( get_option( 'time_format' ) . ' - ' . get_option( 'date_format' ), strtotime( bp_get_activity_date_recorded() ) ); ?></a></p>
+			<?php endwhile; ?>
+
+		<?php else : ?>
+
+			<?php bp_get_template_part( 'embeds/not-found' ); ?>
+
+		<?php endif; ?>
+
Index: src/bp-templates/bp-legacy/buddypress/embeds/css.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/css.php
@@ -0,0 +1,26 @@
+<style type="text/css">
+#wp-embed-header:after {
+	clear: both;
+	content: "";
+	display: table;
+	margin-bottom: 1em;
+}
+
+.wp-embed-avatar {
+	float: left;
+	margin: 0 .75em 0 0;
+}
+
+p.wp-embed-heading {
+	font-size: 16px;
+	margin-bottom: 0;
+}
+
+.wp-embed-excerpt {
+	margin-bottom: .5em;
+}
+
+.wp-embed-footer {
+	margin-top: 20px;
+}
+</style>
\ No newline at end of file
Index: src/bp-templates/bp-legacy/buddypress/embeds/footer.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/footer.php
@@ -0,0 +1,33 @@
+			<div class="wp-embed-footer">
+				<div class="wp-embed-site-title">
+					<?php
+					$site_title = sprintf(
+						'<a href="%s"><img src="%s" srcset="%s 2x" width="32" height="32" alt="" class="wp-embed-site-icon"/><span>%s</span></a>',
+						esc_url( home_url() ),
+						esc_url( get_site_icon_url( 32, admin_url( 'images/w-logo-blue.png' ) ) ),
+						esc_url( get_site_icon_url( 64, admin_url( 'images/w-logo-blue.png' ) ) ),
+						esc_html( get_bloginfo( 'name' ) )
+					);
+
+					/**
+					 * Filter the site title HTML in the embed footer.
+					 *
+					 * @since 4.4.0
+					 *
+					 * @param string $site_title The site title HTML.
+					 */
+					echo apply_filters( 'embed_site_title_html', $site_title );
+					?>
+				</div>
+
+				<div class="wp-embed-meta">
+					<?php
+					/**
+					 * Print additional meta content in the embed template.
+					 *
+					 * @since 4.4.0
+					 */
+					do_action( 'embed_content_meta');
+					?>
+				</div>
+			</div>
\ No newline at end of file
Index: src/bp-templates/bp-legacy/buddypress/embeds/header.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/header.php
@@ -0,0 +1,18 @@
+
+		<div id="wp-embed-header">
+			<div class="wp-embed-avatar">
+				<a href="<?php bp_displayed_user_link(); ?>">
+					<?php bp_displayed_user_avatar( 'type=thumb&width=36&height=36' ); ?>
+				</a>
+			</div>
+
+			<p class="wp-embed-heading">
+				<a href="<?php bp_displayed_user_link(); ?>">
+					<?php bp_displayed_user_fullname(); ?>
+				</a>
+			</p>
+
+			<?php if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() ) : ?>
+				<p class="wp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?></p>
+			<?php endif; ?>
+		</div>
Index: src/bp-templates/bp-legacy/buddypress/embeds/template.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-templates/bp-legacy/buddypress/embeds/template.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Generic embed template used by BuddyPress.
+ *
+ * When a BuddyPress item is embedded in an iframe, this file is used to
+ * create the output.
+ *
+ * This is a modified version of the bundled WordPress embed template
+ * included in WordPress 4.4+.
+ *
+ * @since 2.5.0
+ *
+ * @package BuddyPress
+ * @subpackage Embeds
+ */
+
+if ( ! headers_sent() ) {
+	header( 'X-WP-embed: true' );
+}
+
+?>
+<!DOCTYPE html>
+<html <?php language_attributes(); ?> class="no-js">
+<head>
+	<title><?php echo wp_get_document_title(); ?></title>
+	<meta http-equiv="X-UA-Compatible" content="IE=edge">
+	<base target="_top" />
+	<?php
+	/** This action is documented in wp-includes/embed-template.php */
+	do_action( 'embed_head' );
+
+	// This is used by r-a-y for testing purposes at the moment.
+	bp_get_template_part( 'embeds/css' );
+	?>
+</head>
+<body <?php body_class(); ?>>
+<div <?php post_class( 'wp-embed' ); ?>>
+<?php
+bp_get_template_part( 'embeds/header', bp_current_component() );
+
+/** This action is documented in wp-includes/embed-template.php */
+do_action( 'embed_content' );
+
+bp_get_template_part( 'embeds/footer', bp_current_component() );
+?>
+
+</div>
+
+<?php
+/** This action is documented in wp-includes/embed-template.php */
+do_action( 'embed_footer' );
+?>
+</body>
+</html>
\ No newline at end of file
