Skip to:
Content

BuddyPress.org

Ticket #6772: 6772.05.patch

File 6772.05.patch, 39.9 KB (added by r-a-y, 9 years ago)
  • new file src/bp-activity/bp-activity-embeds.php

    diff --git src/bp-activity/bp-activity-embeds.php src/bp-activity/bp-activity-embeds.php
    new file mode 100644
    index 0000000..31a4e02
    - +  
     1<?php
     2/**
     3 * Functions related to embedding single activity items externally.
     4 *
     5 * Relies on WordPress 4.4.
     6 *
     7 * @since 2.5.0
     8 *
     9 * @package BuddyPress
     10 * @subpackage ActivityEmbeds
     11 */
     12
     13// Exit if accessed directly.
     14defined( 'ABSPATH' ) || exit;
     15
     16/**
     17 * Loads our activity oEmbed component.
     18 *
     19 * @since 2.5.0
     20 */
     21function bp_activity_setup_oembed() {
     22        if ( bp_get_major_wp_version() >= 4.4 && bp_is_active( 'activity', 'embeds' ) ) {
     23                buddypress()->activity->oembed = new BP_Activity_oEmbed_Component;
     24        }
     25}
     26
     27/**
     28 * Outputs a text excerpt for an activity item (and remove any oEmbeds URLs).
     29 *
     30 * @since 2.5.0
     31 */
     32function bp_activity_embed_excerpt( $content = '' ) {
     33        echo bp_activity_get_embed_excerpt( $content = '' );
     34}
     35
     36        /**
     37         * Generate a text excerpt for an activity item (and remove any oEmbeds URLs).
     38         *
     39         * @since 2.5.0
     40         *
     41         * @param  string $content The content to generate an excerpt for.
     42         * @return string
     43         */
     44        function bp_activity_get_embed_excerpt( $content = '' ) {
     45                if ( empty( $content ) && ! empty( $GLOBALS['activities_template']->in_the_loop ) ) {
     46                        $content = $GLOBALS['activities_template']->activity->content;
     47                }
     48
     49                /**
     50                 * bp_activity_truncate_entry() includes the 'Read More' link, which is why
     51                 * we're using this instead of bp_create_excerpt().
     52                 */
     53                $content = html_entity_decode( $content );
     54                $content = bp_activity_truncate_entry( $content, array(
     55                        'html' => false,
     56                        'filter_shortcodes' => true,
     57                        'strip_tags'        => true,
     58                        'force_truncate'    => true
     59                ) );
     60
     61                // Remove links from content.
     62                $content = preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $content );
     63
     64                return $content;
     65        }
     66
     67/**
     68 * Outputs the first embedded item in the activity oEmbed template.
     69 *
     70 * @since 2.5.0
     71 */
     72function bp_activity_embed_response_display_media() {
     73        // Bail if oEmbed request explicitly hides media.
     74        if ( isset( $_REQUEST['hide_media'] ) && true == wp_validate_boolean( $_REQUEST['hide_media'] ) ) {
     75                return;
     76        }
     77
     78        // Find all embeds for the activity item.
     79        $embeds = bp_core_extract_media_from_content( $GLOBALS['activities_template']->activity->content, 'embeds' );
     80
     81        /**
     82         * Should we display media in the oEmbed template?
     83         *
     84         * @since 2.5.0
     85         *
     86         * @param bool $retval Defaults to true.
     87         */
     88        $allow_embeds = apply_filters( 'bp_activity_embed_display_media', true );
     89
     90        // Only embed the first embedded item.
     91        if ( $embeds && $allow_embeds ) {
     92                $content = apply_filters( 'bp_get_activity_content_body', $embeds['embeds'][0]['url'] );
     93                echo $content;
     94
     95                if ( false !== strpos( $content, '<iframe' ) ) {
     96        ?>
     97
     98<script>
     99/*! fluidvids.js v1.2.0 | (c) 2013 @toddmotto | https://github.com/toddmotto/fluidvids */
     100window.fluidvids=function(a,b){"use strict";var c=function(a){this.elem=a};c.prototype={init:function(){var a=100*(this.elem.height/this.elem.width);this.elem.style.position="absolute",this.elem.style.top="0",this.elem.style.left="0",this.elem.width="100%",this.elem.height="100%";var c=b.createElement("div");c.className="fluidvids",c.style.width="100%",c.style.position="relative",c.style.paddingTop=a+"%";var d=this.elem.parentNode;d.insertBefore(c,this.elem),c.appendChild(this.elem)}};for(var d=b.getElementsByTagName("iframe"),e=0;e<d.length;e++){d[e].src&&new c(d[e]).init()}}(window,document);
     101</script>
     102
     103        <?php
     104                }
     105        }
     106}
     107add_action( 'bp_activity_embed_after_content', 'bp_activity_embed_response_display_media' );
  • src/bp-activity/bp-activity-filters.php

    diff --git src/bp-activity/bp-activity-filters.php src/bp-activity/bp-activity-filters.php
    index bc61bdc..1ff8ade 100644
    function bp_activity_make_nofollow_filter( $text ) { 
    387387 * This method can only be used inside the Activity loop.
    388388 *
    389389 * @since 1.5.0
    390  *
    391  * @uses bp_is_single_activity()
    392  * @uses apply_filters() To call the 'bp_activity_excerpt_append_text' hook.
    393  * @uses apply_filters() To call the 'bp_activity_excerpt_length' hook.
    394  * @uses bp_create_excerpt()
    395  * @uses bp_get_activity_id()
    396  * @uses bp_get_activity_thread_permalink()
    397  * @uses apply_filters() To call the 'bp_activity_truncate_entry' hook.
     390 * @since 2.5.0 Added $args parameter.
    398391 *
    399392 * @param string $text The original activity entry text.
    400393 * @return string $excerpt The truncated text.
    401394 */
    402 function bp_activity_truncate_entry( $text ) {
     395function bp_activity_truncate_entry( $text, $args = array() ) {
    403396        global $activities_template;
    404397
    405398        /**
    function bp_activity_truncate_entry( $text ) { 
    415408        );
    416409
    417410        // The full text of the activity update should always show on the single activity screen.
    418         if ( ! $maybe_truncate_text || bp_is_single_activity() ) {
     411        if ( empty( $args['force_truncate'] ) && ( ! $maybe_truncate_text || bp_is_single_activity() ) ) {
    419412                return $text;
    420413        }
    421414
    function bp_activity_truncate_entry( $text ) { 
    437430         */
    438431        $excerpt_length = apply_filters( 'bp_activity_excerpt_length', 358 );
    439432
     433        $args = wp_parse_args( $args, array( 'ending' => __( '&hellip;', 'buddypress' ) ) );
     434
    440435        // Run the text through the excerpt function. If it's too short, the original text will be returned.
    441         $excerpt        = bp_create_excerpt( $text, $excerpt_length, array( 'ending' => __( '&hellip;', 'buddypress' ) ) );
     436        $excerpt        = bp_create_excerpt( $text, $excerpt_length, $args );
    442437
    443438        /*
    444439         * If the text returned by bp_create_excerpt() is different from the original text (ie it's
  • src/bp-activity/bp-activity-functions.php

    diff --git src/bp-activity/bp-activity-functions.php src/bp-activity/bp-activity-functions.php
    index e6c101d..6f474a7 100644
    function bp_activity_create_summary( $content, $activity ) { 
    27892789        }
    27902790
    27912791        // Generate a text excerpt for this activity item (and remove any oEmbeds URLs).
    2792         $summary = strip_shortcodes( html_entity_decode( strip_tags( $content ) ) );
    2793         $summary = bp_create_excerpt( preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $summary ) );
     2792        $summary = bp_create_excerpt( html_entity_decode( $content ), 225, array(
     2793                'html' => false,
     2794                'filter_shortcodes' => true,
     2795                'strip_tags'        => true,
     2796                'remove_links'      => true
     2797        ) );
    27942798
    27952799        if ( $use_media_type === 'embeds' ) {
    27962800                $summary .= PHP_EOL . PHP_EOL . $extracted_media['url'];
  • src/bp-activity/bp-activity-loader.php

    diff --git src/bp-activity/bp-activity-loader.php src/bp-activity/bp-activity-loader.php
    index dfdf325..f41630e 100644
    class BP_Activity_Component extends BP_Component { 
    3131                        array(
    3232                                'adminbar_myaccount_order' => 10,
    3333                                'search_query_arg' => 'activity_search',
     34                                'features' => array( 'embeds' )
    3435                        )
    3536                );
    3637        }
    class BP_Activity_Component extends BP_Component { 
    6768                        $includes[] = 'akismet';
    6869                }
    6970
     71                // Embeds - only applicable for WP 4.4+
     72                if ( bp_get_major_wp_version() >= 4.4 && bp_is_active( $this->id, 'embeds' ) ) {
     73                        $includes[] = 'classes/class-bp-activity-oembed-component';
     74                        $includes[] = 'embeds';
     75                }
     76
    7077                if ( is_admin() ) {
    7178                        $includes[] = 'admin';
    7279                }
    class BP_Activity_Component extends BP_Component { 
    365372                // Spam prevention.
    366373                add_action( 'bp_include', 'bp_activity_setup_akismet' );
    367374
     375                // oEmbed handler.
     376                add_action( 'bp_loaded', 'bp_activity_setup_oembed' );
     377
    368378                parent::setup_actions();
    369379        }
    370380
  • new file src/bp-activity/classes/class-bp-activity-oembed-component.php

    diff --git src/bp-activity/classes/class-bp-activity-oembed-component.php src/bp-activity/classes/class-bp-activity-oembed-component.php
    new file mode 100644
    index 0000000..8deaa8b
    - +  
     1<?php
     2/**
     3 * BuddyPress Activity Classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Embeds
     7 */
     8
     9// Exit if accessed directly.
     10defined( 'ABSPATH' ) || exit;
     11
     12require_once( buddypress()->plugin_dir . '/bp-core/classes/class-bp-oembed-component.php' );
     13
     14/**
     15 * oEmbed handler to respond and render single activity items.
     16 *
     17 * @since 2.5.0
     18 */
     19class BP_Activity_oEmbed_Component extends BP_oEmbed_Component {
     20        /**
     21         * Custom oEmbed slug endpoint.
     22         *
     23         * @since 2.5.0
     24         *
     25         * @var string
     26         */
     27        public $slug_endpoint = 'activity';
     28
     29        /**
     30         * Custom hooks.
     31         *
     32         * @since 2.5.0
     33         */
     34        protected function custom_hooks() {
     35                add_action( 'embed_content_meta', array( $this, 'embed_comments_button' ), 5 );
     36        }
     37
     38        /**
     39         * Add custom endpoint arguments.
     40         *
     41         * Currently, includes 'hide_media'.
     42         *
     43         * @since 2.5.0
     44         *
     45         * @return array
     46         */
     47        protected function set_route_args() {
     48                return array(
     49                        'hide_media' => array(
     50                                'default' => false,
     51                                'sanitize_callback' => 'wp_validate_boolean'
     52                        )
     53                );
     54        }
     55
     56        /**
     57         * Output our custom embed template part.
     58         *
     59         * @since 2.5.0
     60         */
     61        protected function content() {
     62                bp_get_asset_template_part( 'embeds/activity' );
     63        }
     64
     65        /**
     66         * Check if we're on our single activity page.
     67         *
     68         * @since 2.5.0
     69         *
     70         * @return bool
     71         */
     72        protected function is_page() {
     73                return bp_is_single_activity();
     74        }
     75
     76        /**
     77         * Validates the URL to determine if the activity item is valid.
     78         *
     79         * @since 2.5.0
     80         *
     81         * @param  string   $url The URL to check.
     82         * @return int|bool Activity ID on success; boolean false on failure.
     83         */
     84        protected function validate_url_to_item_id( $url ) {
     85                if ( bp_core_enable_root_profiles() ) {
     86                        $domain = bp_get_root_domain();
     87                } else {
     88                        $domain = bp_get_members_directory_permalink();
     89                }
     90
     91                // Check the URL to see if this is a single activity URL.
     92                if ( 0 !== strpos( $url, $domain ) ) {
     93                        return false;
     94                }
     95
     96                // Check for activity slug.
     97                if ( false === strpos( $url, '/' . bp_get_activity_slug() . '/' ) ) {
     98                        return false;
     99                }
     100
     101                // Do more checks.
     102                $url = trim( untrailingslashit( $url ) );
     103
     104                // Grab the activity ID.
     105                $activity_id = (int) substr(
     106                        $url,
     107                        strrpos( $url, '/' ) + 1
     108                );
     109
     110                if ( ! empty( $activity_id ) ) {
     111                        // Check if activity item still exists.
     112                        $activity = new BP_Activity_Activity( $activity_id );
     113
     114                        // Okay, we're good to go!
     115                        if ( ! empty( $activity->component ) && 0 === (int) $activity->is_spam ) {
     116                                return $activity_id;
     117                        }
     118                }
     119
     120                return false;
     121        }
     122
     123        /**
     124         * Sets the oEmbed response data for our activity item.
     125         *
     126         * @since 2.5.0
     127         *
     128         * @param  int $item_id The activity ID.
     129         * @return array
     130         */
     131        protected function set_oembed_response_data( $item_id ) {
     132                $activity = new BP_Activity_Activity( $item_id );
     133
     134                return array(
     135                        'user_id'    => $activity->user_id,
     136                        'content'    => $activity->content,
     137                        'title'      => __( 'Activity', 'buddypress' ),
     138                        'author_url' => bp_core_get_user_domain( $activity->user_id )
     139                );
     140        }
     141
     142        /**
     143         * Sets a custom <blockquote> for our oEmbed fallback HTML.
     144         *
     145         * @since 2.5.0
     146         *
     147         * @param  int $item_id The activity ID.
     148         * @return string
     149         */
     150        protected function set_fallback_html( $item_id ) {
     151                $activity    = new BP_Activity_Activity( $item_id );
     152                $mentionname = bp_activity_do_mentions() ? ' (@' . bp_activity_get_user_mentionname( $activity->user_id ) . ')' : '';
     153                $date        = date_i18n( get_option( 'date_format' ), strtotime( $activity->date_recorded ) );
     154
     155                // Make sure we can use some activity functions that depend on the loop.
     156                $GLOBALS['activities_template'] = new stdClass;
     157                $GLOBALS['activities_template']->activity = $activity;
     158
     159                // 'wp-embedded-content' CSS class is necessary due to how the embed JS works.
     160                $blockquote = sprintf( '<blockquote class="wp-embedded-content bp-activity-item">%1$s%2$s %3$s</blockquote>',
     161                        '<p>' . bp_activity_get_embed_excerpt( $activity->content ) . '</p>',
     162                        '- ' . bp_core_get_user_displayname( $activity->user_id ) . $mentionname,
     163                        '<a href="' . esc_url( bp_activity_get_permalink( $item_id ) ) . '">' . $date . '</a>'
     164                );
     165
     166                // Clean up.
     167                unset( $GLOBALS['activities_template'] );
     168
     169                /**
     170                 * Filters the fallback HTML used when embedding a BP activity item.
     171                 *
     172                 * @since 2.5.0
     173                 *
     174                 * @param string               $blockquote Current fallback HTML
     175                 * @param BP_Activity_Activity $activity   Activity object
     176                 */
     177                return apply_filters( 'bp_activity_embed_fallback_html', $blockquote, $activity );
     178        }
     179
     180        /**
     181         * Sets a custom <iframe> title for our oEmbed item.
     182         *
     183         * @since 2.5.0
     184         *
     185         * @param  int $item_id The activity ID
     186         * @return string
     187         */
     188        protected function set_iframe_title( $item_id ) {
     189                return __( 'Embedded Activity Item', 'buddypress' );
     190        }
     191
     192        /**
     193         * Prints the markup for the activity embed comments button.
     194         *
     195         * @since 2.5.0
     196         */
     197        public function embed_comments_button() {
     198                if ( ! bp_is_single_activity() ) {
     199                        return;
     200                }
     201
     202                // Make sure our custom permalink shows up in the 'WordPress Embed' block.
     203                add_filter( 'the_permalink', array( $this, 'filter_embed_url' ) );
     204
     205                // Only show comment bubble if we have some activity comments.
     206                $count = bp_activity_get_comment_count();
     207                if ( empty( $count ) ) {
     208                        return;
     209                }
     210        ?>
     211
     212                <div class="wp-embed-comments">
     213                        <a href="<?php bp_activity_thread_permalink(); ?>">
     214                                <span class="dashicons dashicons-admin-comments"></span>
     215                                <?php
     216                                printf(
     217                                        _n(
     218                                                '%s <span class="screen-reader-text">Comment</span>',
     219                                                '%s <span class="screen-reader-text">Comments</span>',
     220                                                $count
     221                                        ),
     222                                        number_format_i18n( $count )
     223                                );
     224                                ?>
     225                        </a>
     226                </div>
     227
     228        <?php
     229        }
     230}
  • src/bp-core/bp-core-functions.php

    diff --git src/bp-core/bp-core-functions.php src/bp-core/bp-core-functions.php
    index f865d7c..da8540d 100644
    function bp_use_embed_in_private_messages() { 
    16661666        return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES );
    16671667}
    16681668
     1669/**
     1670 * Extracts media metadata from a given content.
     1671 *
     1672 * @since 2.5.0
     1673 *
     1674 * @param  string     $content The content to check.
     1675 * @param  string|int $type    The type to check. Can also use a bitmask. See the class constants in the
     1676 *                             BP_Media_Extractor class for more info.
     1677 * @return array|bool          If media exists, will return array of media metadata. Else, boolean false.
     1678 */
     1679function bp_core_extract_media_from_content( $content = '', $type = 'all' ) {
     1680        if ( is_string( $type ) ) {
     1681                $class = new ReflectionClass( 'BP_Media_Extractor' );
     1682                $bitmask = $class->getConstant( strtoupper( $type ) );
     1683        } else {
     1684                $bitmask = (int) $type;
     1685        }
     1686
     1687        // Type isn't valid, so bail.
     1688        if ( empty( $bitmask ) ) {
     1689                return false;
     1690        }
     1691
     1692        $x = new BP_Media_Extractor;
     1693        $media = $x->extract( $content, $bitmask );
     1694
     1695        unset( $media['has'] );
     1696        $retval = array_filter( $media );
     1697
     1698        return ! empty( $retval ) ? $retval : false;
     1699}
     1700
    16691701/** Admin *********************************************************************/
    16701702
    16711703/**
  • src/bp-core/bp-core-template-loader.php

    diff --git src/bp-core/bp-core-template-loader.php src/bp-core/bp-core-template-loader.php
    index 91c67ef..8f4b03c 100644
    function bp_get_template_part( $slug, $name = null ) { 
    6565}
    6666
    6767/**
     68 * Get an asset template part.
     69 *
     70 * Basically the same as {@link bp_get_template_part()}, but with 'assets/'
     71 * prepended to the slug.
     72 *
     73 * @since 2.5.0
     74 *
     75 * @see bp_get_template_part() for full documentation.
     76 */
     77function bp_get_asset_template_part( $slug, $name = null ) {
     78        return bp_get_template_part( "assets/{$slug}", $name );
     79}
     80
     81/**
    6882 * Retrieve the name of the highest priority template file that exists.
    6983 *
    7084 * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
  • src/bp-core/bp-core-template.php

    diff --git src/bp-core/bp-core-template.php src/bp-core/bp-core-template.php
    index 7a3a2d4..ad02fda 100644
    function bp_button( $args = '' ) { 
    742742 * This function is borrowed from CakePHP v2.0, under the MIT license. See
    743743 * http://book.cakephp.org/view/1469/Text#truncate-1625
    744744 *
     745 * @since 2.5.0 Added 'strip_tags' and 'remove_links' as $options args.
     746 *
    745747 * ### Options:
    746748 *
    747749 * - `ending` Will be used as Ending and appended to the trimmed string.
    function bp_button( $args = '' ) { 
    762764 *                                     excerpt length. Default: true.
    763765 *     @type bool   $filter_shortcodes If true, shortcodes will be stripped.
    764766 *                                     Default: true.
     767 *     @type bool   $strip_tags        If true, HTML tags will be stripped. Default: false.
     768 *                                     Only applicable if $html is set to false.
     769 *     @type bool   $remove_links      If true, URLs will be stripped. Default: false.
     770 *                                     Only applicable if $html is set to false.
    765771 * }
    766772 * @return string Trimmed string.
    767773 */
    function bp_create_excerpt( $text, $length = 225, $options = array() ) { 
    774780                'ending'            => __( ' [&hellip;]', 'buddypress' ),
    775781                'exact'             => false,
    776782                'html'              => true,
    777                 'filter_shortcodes' => $filter_shortcodes_default
     783                'filter_shortcodes' => $filter_shortcodes_default,
     784                'strip_tags'        => false,
     785                'remove_links'      => false,
    778786        ), 'create_excerpt' );
    779787
    780788        // Save the original text, to be passed along to the filter.
    function bp_create_excerpt( $text, $length = 225, $options = array() ) { 
    860868                        }
    861869                }
    862870        } else {
     871                // Strip HTML tags if necessary.
     872                if ( ! empty( $r['strip_tags'] ) ) {
     873                        $text = strip_tags( $text );
     874                }
     875
     876                // Remove links if necessary.
     877                if ( ! empty( $r['remove_links'] ) ) {
     878                        $text = preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $text );
     879                }
     880
    863881                if ( mb_strlen( $text ) <= $length ) {
    864882                        return $text;
    865883                } else {
  • src/bp-core/bp-core-theme-compatibility.php

    diff --git src/bp-core/bp-core-theme-compatibility.php src/bp-core/bp-core-theme-compatibility.php
    index da54dce..0de2895 100644
    function bp_register_theme_package( $theme = array(), $override = true ) { 
    650650}
    651651
    652652/**
     653 * Create a dummy WP_Post object.
     654 *
     655 * @since 2.5.0
     656 *
     657 * @param  array $args Array of optional arguments. Arguments parallel the properties
     658 *                    of {@link WP_Post}; see that class for more details.
     659 * @return WP_Post
     660 */
     661function bp_theme_compat_create_dummy_post( $args = array() ) {
     662        $args = wp_parse_args( $args, array(
     663                'ID'                    => -9999,
     664                'post_status'           => 'public',
     665                'post_author'           => 0,
     666                'post_parent'           => 0,
     667                'post_type'             => 'page',
     668                'post_date'             => 0,
     669                'post_date_gmt'         => 0,
     670                'post_modified'         => 0,
     671                'post_modified_gmt'     => 0,
     672                'post_content'          => '',
     673                'post_title'            => '',
     674                'post_excerpt'          => '',
     675                'post_content_filtered' => '',
     676                'post_mime_type'        => '',
     677                'post_password'         => '',
     678                'post_name'             => '',
     679                'guid'                  => '',
     680                'menu_order'            => 0,
     681                'pinged'                => '',
     682                'to_ping'               => '',
     683                'ping_status'           => '',
     684                'comment_status'        => 'closed',
     685                'comment_count'         => 0,
     686                'filter'                => 'raw',
     687
     688                'is_404'                => false,
     689                'is_page'               => false,
     690                'is_single'             => false,
     691                'is_archive'            => false,
     692                'is_tax'                => false,
     693        ) );
     694
     695        // Create the dummy post.
     696        $post = new WP_Post( (object) $args );
     697
     698        return $post;
     699}
     700
     701/**
    653702 * Populate various WordPress globals with dummy data to prevent errors.
    654703 *
    655704 * This dummy data is necessary because theme compatibility essentially fakes
    function bp_theme_compat_reset_post( $args = array() ) { 
    670719
    671720        // Switch defaults if post is set.
    672721        if ( isset( $wp_query->post ) ) {
    673                 $dummy = wp_parse_args( $args, array(
     722                $args = wp_parse_args( $args, array(
    674723                        'ID'                    => $wp_query->post->ID,
    675724                        'post_status'           => $wp_query->post->post_status,
    676725                        'post_author'           => $wp_query->post->post_author,
    function bp_theme_compat_reset_post( $args = array() ) { 
    695744                        'comment_status'        => $wp_query->post->comment_status,
    696745                        'comment_count'         => $wp_query->post->comment_count,
    697746                        'filter'                => $wp_query->post->filter,
    698 
    699                         'is_404'                => false,
    700                         'is_page'               => false,
    701                         'is_single'             => false,
    702                         'is_archive'            => false,
    703                         'is_tax'                => false,
    704                 ) );
    705         } else {
    706                 $dummy = wp_parse_args( $args, array(
    707                         'ID'                    => -9999,
    708                         'post_status'           => 'public',
    709                         'post_author'           => 0,
    710                         'post_parent'           => 0,
    711                         'post_type'             => 'page',
    712                         'post_date'             => 0,
    713                         'post_date_gmt'         => 0,
    714                         'post_modified'         => 0,
    715                         'post_modified_gmt'     => 0,
    716                         'post_content'          => '',
    717                         'post_title'            => '',
    718                         'post_excerpt'          => '',
    719                         'post_content_filtered' => '',
    720                         'post_mime_type'        => '',
    721                         'post_password'         => '',
    722                         'post_name'             => '',
    723                         'guid'                  => '',
    724                         'menu_order'            => 0,
    725                         'pinged'                => '',
    726                         'to_ping'               => '',
    727                         'ping_status'           => '',
    728                         'comment_status'        => 'closed',
    729                         'comment_count'         => 0,
    730                         'filter'                => 'raw',
    731 
    732                         'is_404'                => false,
    733                         'is_page'               => false,
    734                         'is_single'             => false,
    735                         'is_archive'            => false,
    736                         'is_tax'                => false,
    737747                ) );
    738748        }
    739749
    740750        // Bail if dummy post is empty.
    741         if ( empty( $dummy ) ) {
     751        if ( empty( $args ) ) {
    742752                return;
    743753        }
    744754
    745755        // Set the $post global.
    746         $post = new WP_Post( (object) $dummy );
     756        $post = bp_theme_compat_create_dummy_post( $args );
    747757
    748758        // Copy the new post global into the main $wp_query.
    749759        $wp_query->post       = $post;
    function bp_theme_compat_reset_post( $args = array() ) { 
    751761
    752762        // Prevent comments form from appearing.
    753763        $wp_query->post_count = 1;
    754         $wp_query->is_404     = $dummy['is_404'];
    755         $wp_query->is_page    = $dummy['is_page'];
    756         $wp_query->is_single  = $dummy['is_single'];
    757         $wp_query->is_archive = $dummy['is_archive'];
    758         $wp_query->is_tax     = $dummy['is_tax'];
    759 
    760         // Clean up the dummy post.
    761         unset( $dummy );
     764        $wp_query->is_404     = $post->is_404;
     765        $wp_query->is_page    = $post->is_page;
     766        $wp_query->is_single  = $post->is_single;
     767        $wp_query->is_archive = $post->is_archive;
     768        $wp_query->is_tax     = $post->is_tax;
    762769
    763770        /**
    764771         * Force the header back to 200 status if not a deliberate 404
  • new file src/bp-core/classes/class-bp-oembed-component.php

    diff --git src/bp-core/classes/class-bp-oembed-component.php src/bp-core/classes/class-bp-oembed-component.php
    new file mode 100644
    index 0000000..4e0b9f9
    - +  
     1<?php
     2/**
     3 * Core component classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 */
     8
     9// Exit if accessed directly.
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * API for responding and returning a custom oEmbed request.
     14 *
     15 * @since 2.5.0
     16 */
     17abstract class BP_oEmbed_Component {
     18
     19        /** START PROPERTIES ****************************************************/
     20
     21        /**
     22         * (required) The slug endpoint.
     23         *
     24         * Should be your component id.
     25         *
     26         * @var string
     27         */
     28        public $slug_endpoint = '';
     29
     30        /**
     31         * (optional) Embed template hierarchy.
     32         *
     33         * Define the template hierarchy for your oEmbed rendering.
     34         *
     35         * @var array
     36         */
     37        protected $template_hierarchy = array();
     38
     39        /** END PROPERTIES ******************************************************/
     40
     41        /**
     42         * Constructor.
     43         */
     44        final public function __construct() {
     45                $this->setup_properties();
     46
     47                // Some rudimentary logic checking.
     48                if ( empty( $this->slug_endpoint ) ) {
     49                        $class = get_class( $this );
     50                        throw new \LogicException( $class . ' class must define $slug_endpoint property' );
     51                }
     52
     53                $this->setup_hooks();
     54                $this->custom_hooks();
     55        }
     56
     57        /** REQUIRED METHODS ****************************************************/
     58
     59        /**
     60         * Add content for your oEmbed response here.
     61         *
     62         * @since 2.5.0
     63         */
     64        abstract protected function content();
     65
     66        /**
     67         * Add a check for when you are on the page you want to oEmbed.
     68         *
     69         * You'll want to return a boolean here. eg. bp_is_single_activity().
     70         *
     71         * @since 2.5.0
     72         *
     73         * @return bool
     74         */
     75        abstract protected function is_page();
     76
     77        /**
     78         * Validate the URL to see if it matches your item ID.
     79         *
     80         * @since 2.5.0
     81         *
     82         * @return int Your item ID
     83         */
     84        abstract protected function validate_url_to_item_id( $url );
     85
     86        /**
     87         * Set the oEmbed response data.
     88         *
     89         * @since 2.5.0
     90         *
     91         * @param  int   $item_id Your item ID to do checks against.
     92         * @return array Should contain 'user_id', 'content', 'title', 'author_url' as array keys.
     93         *               'author_url' is optional; the rest are required.
     94         */
     95        abstract protected function set_oembed_response_data( $item_id );
     96
     97        /**
     98         * Sets the fallback HTML for the oEmbed response.
     99         *
     100         * In a WordPress oEmbed item, the fallback HTML is a <blockquote>.  This is
     101         * usually hidden after the <iframe> is loaded.
     102         *
     103         * @since 2.5.0
     104         *
     105         * @param  int    $item_id Your item ID to do checks against.
     106         * @return string Fallback HTML you want to output.
     107         */
     108        abstract protected function set_fallback_html( $item_id );
     109
     110        /** OPTIONAL METHODS ****************************************************/
     111
     112        /**
     113         * If your oEmbed endpoint requires additional arguments, set them here.
     114         *
     115         * @see register_rest_route() View the $args parameter for more info.
     116         *
     117         * @since 2.5.0
     118         *
     119         * @return array
     120         */
     121        protected function set_route_args() {
     122                return array();
     123        }
     124
     125        /**
     126         * Set the iframe title.
     127         *
     128         * If not set, this will fallback to WP's 'Embedded WordPress Post'.
     129         *
     130         * @param int $item_id The item ID to do checks for.
     131         * @return string
     132         */
     133        protected function set_iframe_title( $item_id ) {}
     134
     135        /**
     136         * Do what you need to do here to initialize any custom hooks.
     137         *
     138         * @since 2.5.0
     139         */
     140        protected function custom_hooks() {}
     141
     142        /**
     143         * Set permalink for oEmbed link discovery.
     144         *
     145         * This method will be called on the page we want to oEmbed.  In most cases,
     146         * you will not need to override this method.  However, if you need to, do
     147         * override in your extended class.
     148         *
     149         * @since 2.5.0
     150         */
     151        protected function set_permalink() {
     152                $url = bp_get_requested_url();
     153
     154                // Remove querystring from bp_get_requested_url()
     155                if ( false !== strpos( bp_get_requested_url(), '?' ) ) {
     156                        $url = substr( bp_get_requested_url(), 0, strpos( bp_get_requested_url(), '?' ) );
     157                }
     158
     159                return $url;
     160        }
     161
     162        /** SET UP **************************************************************/
     163
     164        /**
     165         * Set up properties.
     166         *
     167         * @since 2.5.0
     168         */
     169        protected function setup_properties() {
     170                $this->slug_endpoint = sanitize_title( $this->slug_endpoint );
     171        }
     172
     173        /**
     174         * Hooks! We do the dirty work here, so you don't have to! :)
     175         *
     176         * @since 2.5.0
     177         */
     178        protected function setup_hooks() {
     179                add_action( 'rest_api_init',  array( $this, 'register_route' ) );
     180                add_action( 'embed_content',  array( $this, 'inject_content' ) );
     181
     182                add_filter( 'embed_template', array( $this, 'filter_template' ) );
     183                add_filter( 'post_embed_url', array( $this, 'filter_embed_url' ) );
     184                add_filter( 'embed_html',     array( $this, 'filter_embed_html' ) );
     185                add_filter( 'oembed_discovery_links', array( $this, 'add_oembed_discovery_links' ) );
     186        }
     187
     188        /** HOOKS ***************************************************************/
     189
     190        /**
     191         * Register the oEmbed REST API route.
     192         *
     193         * @since 2.5.0
     194         */
     195        public function register_route() {
     196                /** This filter is documented in wp-includes/class-wp-oembed-controller.php */
     197                $maxwidth = apply_filters( 'oembed_default_width', 600 );
     198
     199                // Required arguments.
     200                $args = array(
     201                        'url'      => array(
     202                                'required'          => true,
     203                                'sanitize_callback' => 'esc_url_raw',
     204                        ),
     205                        'format'   => array(
     206                                'default'           => 'json',
     207                                'sanitize_callback' => 'wp_oembed_ensure_format',
     208                        ),
     209                        'maxwidth' => array(
     210                                'default'           => $maxwidth,
     211                                'sanitize_callback' => 'absint',
     212                        )
     213                );
     214
     215                // Merge custom arguments here.
     216                $args = $args + (array) $this->set_route_args();
     217
     218                register_rest_route( 'oembed/1.0', "/embed/{$this->slug_endpoint}", array(
     219                        array(
     220                                'methods'  => WP_REST_Server::READABLE,
     221                                'callback' => array( $this, 'get_item' ),
     222                                'args'     => $args
     223                        ),
     224                ) );
     225        }
     226
     227        /**
     228         * Filters the embed template.
     229         *
     230         * Override if needed.
     231         *
     232         * @since 2.5.0
     233         *
     234         * @param  string $template File path to current embed template.
     235         * @return string
     236         */
     237        public function filter_template( $template ) {
     238                if ( ! $this->is_page() || is_404() ) {
     239                        return $template;
     240                }
     241
     242                // Embed template hierarchy!
     243                return bp_locate_template( array(
     244                        'assets/embeds/template.php'
     245                ) );
     246        }
     247
     248        /**
     249         * Adds oEmbed discovery links on single activity pages.
     250         *
     251         * @since 2.5.0
     252         *
     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
     261                $permalink = $this->set_permalink();
     262                if ( empty( $permalink ) ) {
     263                        return $retval;
     264                }
     265
     266                add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
     267
     268                $retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n";
     269
     270                if ( class_exists( 'SimpleXMLElement' ) ) {
     271                        $retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n";
     272                }
     273
     274                remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) );
     275
     276                return $retval;
     277        }
     278
     279        /**
     280         * Callback for the API endpoint.
     281         *
     282         * Returns the JSON object for the item.
     283         *
     284         * @since 2.5.0
     285         *
     286         * @param  WP_REST_Request $request Full data about the request.
     287         * @return WP_Error|array oEmbed response data or WP_Error on failure.
     288         */
     289        public function get_item( $request ) {
     290                $url = $request['url'];
     291
     292                $data = false;
     293
     294                $item_id = (int) $this->validate_url_to_item_id( $url );
     295
     296                if ( ! empty( $item_id ) ) {
     297                        $item = $this->set_oembed_response_data( $item_id );
     298
     299                        // Create dummy post to piggyback off of get_oembed_response_data()
     300                        $post = bp_theme_compat_create_dummy_post( array(
     301                                'post_author'  => $item['user_id'],
     302                                'post_title'   => $item['title'],
     303                                'post_content' => $item['content'],
     304
     305                                // This passes the get_oembed_response_data() check.
     306                                'post_status'  => 'publish'
     307                        ) );
     308
     309                        // Add markers to tell that we're embedding a single activity.
     310                        // This is needed for various oEmbed response data filtering.
     311                        if ( empty( buddypress()->{$this->slug_endpoint} ) ) {
     312                                buddypress()->{$this->slug_endpoint} = new stdClass;
     313                        }
     314                        buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url;
     315                        buddypress()->{$this->slug_endpoint}->embedid_in_progress  = $item_id;
     316
     317                        // Save custom route args as well.
     318                        $custom_args = array_keys( (array) $this->set_route_args() );
     319                        if ( ! empty( $custom_args ) ) {
     320                                buddypress()->{$this->slug_endpoint}->embedargs_in_progress = array();
     321
     322                                foreach( $custom_args as $arg ) {
     323                                        if ( isset( $request[ $arg ] ) ) {
     324                                                buddypress()->{$this->slug_endpoint}->embedargs_in_progress[ $arg ] = $request[ $arg ];
     325                                        }
     326                                }
     327                        }
     328
     329                        // Use WP's oEmbed response data function.
     330                        $data = get_oembed_response_data( $post, $request['maxwidth'] );
     331
     332                        // Set custom 'author_url' if we have one.
     333                        if ( ! empty( $item['author_url'] ) ) {
     334                                $data['author_url'] = $item['author_url'];
     335                        }
     336                }
     337
     338                if ( ! $data ) {
     339                        return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) );
     340                }
     341
     342                return $data;
     343        }
     344
     345        /**
     346         * Pass our BuddyPress activity permalink for embedding.
     347         *
     348         * @since 2.5.0
     349         *
     350         * @see bp_activity_embed_rest_route_callback()
     351         *
     352         * @param  string $retval Current embed URL
     353         * @return string
     354         */
     355        public function filter_embed_url( $retval ) {
     356                if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
     357                        return $retval;
     358                }
     359
     360                $url = $this->is_page() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress;
     361                $url = trailingslashit( $url );
     362
     363                // This is for the 'WordPress Embed' block
     364                // @see bp_activity_embed_comments_button()
     365                if ( 'the_permalink' !== current_filter() ) {
     366                        $url = add_query_arg( 'embed', 'true', trailingslashit( $url ) );
     367
     368                        // Add custom route args to iframe.
     369                        if ( ! empty( buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) ) {
     370                                foreach( buddypress()->{$this->slug_endpoint}->embedargs_in_progress as $key => $value ) {
     371                                        $url = add_query_arg( $key, $value, $url );
     372                                }
     373                        }
     374                }
     375
     376                return $url;
     377        }
     378
     379        /**
     380         * Filters the embed HTML for the default oEmbed fallback HTML.
     381         *
     382         * @since 2.5.0
     383         *
     384         * @param  string $retval Current embed HTML
     385         * @return string
     386         */
     387        public function filter_embed_html( $retval ) {
     388                if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) {
     389                        return $retval;
     390                }
     391
     392                $url = $this->set_permalink();
     393
     394                $item_id = $this->is_page() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress;
     395
     396                // Change 'Embedded WordPress Post' to custom title.
     397                $custom_title = $this->set_iframe_title( $item_id );
     398                if ( ! empty( $custom_title ) ) {
     399                        $retval = str_replace( __( 'Embedded WordPress Post' ), esc_attr( $custom_title ), $retval );
     400                }
     401
     402                // Remove default <blockquote>
     403                $retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 );
     404
     405                // Set up new fallback HTML
     406                // @todo Maybe use KSES?
     407                $fallback_html = $this->set_fallback_html( $item_id );
     408
     409                // Add our custom <blockquote>
     410                return $fallback_html . $retval;
     411        }
     412
     413        /**
     414         * Append our custom slug endpoint to oEmbed endpoint URL.
     415         *
     416         * Meant to be used as a filter on 'rest_url' before any call to
     417         * {@link get_oembed_endpoint_url()} is used.
     418         *
     419         * @since 2.5.0
     420         *
     421         * @see add_oembed_discovery_links()
     422         *
     423         * @param  string $retval Current oEmbed endpoint URL
     424         * @return string
     425         */
     426        function filter_rest_url( $retval = '' ) {
     427                return $retval . "/{$this->slug_endpoint}";
     428        }
     429
     430        /**
     431         * Inject activity content into the embed template.
     432         *
     433         * @since 2.5.0
     434         */
     435        public function inject_content() {
     436                if ( ! $this->is_page() ) {
     437                        return;
     438                }
     439
     440                $this->content();
     441        }
     442}
     443 No newline at end of file
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/activity.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/embeds/activity.php src/bp-templates/bp-legacy/buddypress/assets/embeds/activity.php
    new file mode 100644
    index 0000000..1fdae1a
    - +  
     1
     2                <?php if ( bp_has_activities( 'display_comments=threaded&show_hidden=true&include=' . bp_current_action() ) ) : ?>
     3
     4                        <?php while ( bp_activities() ) : bp_the_activity(); ?>
     5                                <div class="wp-embed-excerpt"><p><?php bp_activity_embed_excerpt(); ?></p></div>
     6
     7                                <p class="wp-embed-timestamp"><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>
     8
     9                                <?php do_action( 'bp_activity_embed_after_content' ); ?>
     10
     11                        <?php endwhile; ?>
     12
     13                <?php endif; ?>
     14
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/css.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/embeds/css.php src/bp-templates/bp-legacy/buddypress/assets/embeds/css.php
    new file mode 100644
    index 0000000..beaa782
    - +  
     1<style type="text/css">
     2#wp-embed-header:after {
     3        clear: both;
     4        content: "";
     5        display: table;
     6        margin-bottom: 1em;
     7}
     8
     9.wp-embed-avatar {
     10        float: left;
     11        margin: 0 .75em 0 0;
     12}
     13
     14p.wp-embed-heading {
     15        font-size: 16px;
     16        margin-bottom: 0;
     17}
     18
     19.wp-embed-excerpt, p.wp-embed-timestamp {
     20        margin-bottom: .5em;
     21}
     22
     23.activity-read-more {
     24        margin-left: .5em;
     25}
     26
     27.activity-read-more a {
     28        color: #b4b9be;
     29}
     30
     31.wp-embed-footer {
     32        margin-top: 20px;
     33}
     34</style>
     35 No newline at end of file
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/footer.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/embeds/footer.php src/bp-templates/bp-legacy/buddypress/assets/embeds/footer.php
    new file mode 100644
    index 0000000..22de696
    - +  
     1                        <div class="wp-embed-footer">
     2                                <div class="wp-embed-site-title">
     3                                        <?php
     4                                        $site_title = sprintf(
     5                                                '<a href="%s"><img src="%s" srcset="%s 2x" width="32" height="32" alt="" class="wp-embed-site-icon"/><span>%s</span></a>',
     6                                                esc_url( home_url() ),
     7                                                esc_url( get_site_icon_url( 32, admin_url( 'images/w-logo-blue.png' ) ) ),
     8                                                esc_url( get_site_icon_url( 64, admin_url( 'images/w-logo-blue.png' ) ) ),
     9                                                esc_html( get_bloginfo( 'name' ) )
     10                                        );
     11
     12                                        /**
     13                                         * Filter the site title HTML in the embed footer.
     14                                         *
     15                                         * @since 4.4.0
     16                                         *
     17                                         * @param string $site_title The site title HTML.
     18                                         */
     19                                        echo apply_filters( 'embed_site_title_html', $site_title );
     20                                        ?>
     21                                </div>
     22
     23                                <div class="wp-embed-meta">
     24                                        <?php
     25                                        /**
     26                                         * Print additional meta content in the embed template.
     27                                         *
     28                                         * @since 4.4.0
     29                                         */
     30                                        do_action( 'embed_content_meta');
     31                                        ?>
     32                                </div>
     33                        </div>
     34 No newline at end of file
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/header.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/embeds/header.php src/bp-templates/bp-legacy/buddypress/assets/embeds/header.php
    new file mode 100644
    index 0000000..7a4bc8a
    - +  
     1
     2                <div id="wp-embed-header">
     3                        <div class="wp-embed-avatar">
     4                                <a href="<?php bp_displayed_user_link(); ?>">
     5                                        <?php bp_displayed_user_avatar( 'type=thumb&width=36&height=36' ); ?>
     6                                </a>
     7                        </div>
     8
     9                        <p class="wp-embed-heading">
     10                                <a href="<?php bp_displayed_user_link(); ?>">
     11                                        <?php bp_displayed_user_fullname(); ?>
     12                                </a>
     13                        </p>
     14
     15                        <?php if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() ) : ?>
     16                                <p class="wp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?></p>
     17                        <?php endif; ?>
     18                </div>
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/template.php

    diff --git src/bp-templates/bp-legacy/buddypress/assets/embeds/template.php src/bp-templates/bp-legacy/buddypress/assets/embeds/template.php
    new file mode 100644
    index 0000000..599c283
    - +  
     1<?php
     2/**
     3 * Generic embed template used by BuddyPress.
     4 *
     5 * When a BuddyPress item is embedded in an iframe, this file is used to
     6 * create the output.
     7 *
     8 * This is a modified version of the bundled WordPress embed template
     9 * included in WordPress 4.4+.
     10 *
     11 * @since 2.5.0
     12 *
     13 * @package BuddyPress
     14 * @subpackage Embeds
     15 */
     16
     17if ( ! headers_sent() ) {
     18        header( 'X-WP-embed: true' );
     19}
     20
     21?>
     22<!DOCTYPE html>
     23<html <?php language_attributes(); ?> class="no-js">
     24<head>
     25        <title><?php echo wp_get_document_title(); ?></title>
     26        <meta http-equiv="X-UA-Compatible" content="IE=edge">
     27        <base target="_top" />
     28        <?php
     29        /** This action is documented in wp-includes/embed-template.php */
     30        do_action( 'embed_head' );
     31
     32        // This is used by r-a-y for testing purposes at the moment.
     33        bp_get_asset_template_part( 'embeds/css' );
     34        ?>
     35</head>
     36<body <?php body_class(); ?>>
     37<div <?php post_class( 'wp-embed' ); ?>>
     38<?php
     39bp_get_asset_template_part( 'embeds/header', bp_current_component() );
     40
     41/** This action is documented in wp-includes/embed-template.php */
     42do_action( 'embed_content' );
     43
     44bp_get_asset_template_part( 'embeds/footer', bp_current_component() );
     45?>
     46
     47</div>
     48
     49<?php
     50/** This action is documented in wp-includes/embed-template.php */
     51do_action( 'embed_footer' );
     52?>
     53</body>
     54</html>
     55 No newline at end of file