Skip to:
Content

BuddyPress.org

Ticket #6772: 6772.11.patch

File 6772.11.patch, 58.6 KB (added by r-a-y, 8 years ago)

Refreshed after r10825

  • Gruntfile.js

     
    55                BUILD_DIR = 'build/',
    66
    77                BP_CSS = [
    8                         '**/*.css'
     8                        '**/*.css',
     9                        '**/css-*.php'
    910                ],
    1011
    1112                // CSS exclusions, for excluding files from certain tasks, e.g. rtlcss
    1213                BP_EXCLUDED_CSS = [
    13                         '!**/*-rtl.css'
     14                        '!**/*-rtl.css',
     15                        '!**/*-rtl.php'
    1416                ],
    1517
    1618                BP_JS = [
     
    105107                                cwd: SOURCE_DIR,
    106108                                dest: SOURCE_DIR,
    107109                                extDot: 'last',
    108                                 ext: '-rtl.css',
    109                                 src: BP_CSS.concat( BP_EXCLUDED_CSS, BP_EXCLUDED_MISC )
     110                                src: BP_CSS.concat( BP_EXCLUDED_CSS, BP_EXCLUDED_MISC ),
     111                                rename: function ( dest, src ) {
     112                                        if ( src.endsWith( '.php' ) ) {
     113                                                return dest + src.replace( '.php', '-rtl.php' );
     114                                        } else {
     115                                                return dest + src.replace( '.css', '-rtl.css' );
     116                                        }
     117                                }
    110118                        }
    111119                },
    112120                checktextdomain: {
  • src/bp-activity/bp-activity-classes.php

     
    1313require dirname( __FILE__ ) . '/classes/class-bp-activity-activity.php';
    1414require dirname( __FILE__ ) . '/classes/class-bp-activity-feed.php';
    1515require dirname( __FILE__ ) . '/classes/class-bp-activity-query.php';
     16
     17// Embeds - only applicable for WP 4.5+
     18if ( bp_get_major_wp_version() >= 4.5 && bp_is_active( 'activity', 'embeds' ) ) {
     19        require dirname( __FILE__ ) . '/classes/class-bp-activity-oembed-component.php';
     20}
     21 No newline at end of file
  • new file src/bp-activity/bp-activity-embeds.php

    new file mode 100644
    - +  
     1<?php
     2/**
     3 * Functions related to embedding single activity items externally.
     4 *
     5 * Relies on WordPress 4.5.
     6 *
     7 * @since 2.6.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.6.0
     20 */
     21function bp_activity_setup_oembed() {
     22        if ( bp_get_major_wp_version() >= 4.5 && bp_is_active( 'activity', 'embeds' ) ) {
     23                buddypress()->activity->oembed = new BP_Activity_oEmbed_Component;
     24        }
     25
     26        add_filter( 'bp_activity_get_embed_excerpt', 'wptexturize' );
     27        add_filter( 'bp_activity_get_embed_excerpt', 'convert_chars' );
     28        add_filter( 'bp_activity_get_embed_excerpt', 'make_clickable', 9 );
     29        add_filter( 'bp_activity_get_embed_excerpt', 'bp_activity_embed_excerpt_onclick_location_filter' );
     30        add_filter( 'bp_activity_get_embed_excerpt', 'bp_activity_at_name_filter' );
     31        add_filter( 'bp_activity_get_embed_excerpt', 'convert_smilies', 20 );
     32        add_filter( 'bp_activity_get_embed_excerpt', 'wpautop', 30 );
     33}
     34add_action( 'bp_loaded', 'bp_activity_setup_oembed' );
     35
     36/**
     37 * Catch links in embed excerpt so top.location.href can be added.
     38 *
     39 * Due to <iframe sandbox="allow-top-navigation">, links in embeds can only be
     40 * clicked if invoked with top.location.href via JS.
     41 *
     42 * @since 2.6.0
     43 *
     44 * @param  string $text Embed excerpt
     45 * @return string
     46 */
     47function bp_activity_embed_excerpt_onclick_location_filter( $text ) {
     48        return preg_replace_callback( '/<a href=\"([^\"]*)\"/iU', 'bp_activity_embed_excerpt_onclick_location_filter_callback', $text );
     49}
     50        /**
     51         * Add onclick="top.location.href" to a link.
     52         *
     53         * @since 2.6.0
     54         *
     55         * @param  array $matches Items matched by bp_activity_embed_excerpt_onclick_location_filter().
     56         * @return string
     57         */
     58        function bp_activity_embed_excerpt_onclick_location_filter_callback( $matches ) {
     59                return sprintf( '<a href="%1$s" onclick="top.location.href=\'%1$s\'"', $matches[1] );
     60        }
     61
     62/**
     63 * Add inline styles for BP activity embeds.
     64 *
     65 * This is subject to change or be removed entirely for a different system.
     66 * Potentially for BP_Legacy::locate_asset_in_stack().
     67 *
     68 * @since  2.6.0
     69 * @access private
     70 */
     71function _bp_activity_embed_add_inline_styles() {
     72        if ( false === bp_is_single_activity() ) {
     73                return;
     74        }
     75
     76        ob_start();
     77        if ( is_rtl() ) {
     78                bp_get_asset_template_part( 'embeds/css-activity', 'rtl' );
     79        } else {
     80                bp_get_asset_template_part( 'embeds/css-activity' );
     81        }
     82        $css = ob_get_clean();
     83
     84        // Rudimentary CSS protection.
     85        $css = wp_kses( $css, array( "\'", '\"' ) );
     86
     87        printf( '<style type="text/css">%s</style>', $css );
     88}
     89add_action( 'embed_head', '_bp_activity_embed_add_inline_styles', 20 );
     90
     91/**
     92 * Query for the activity item on the activity embed template.
     93 *
     94 * Basically a wrapper for {@link bp_has_activities()}, but allows us to
     95 * use the activity loop without requerying for it again.
     96 *
     97 * @since 2.6.0
     98 *
     99 * @param  int $activity_id The activity ID.
     100 * @return bool
     101 */
     102function bp_activity_embed_has_activity( $activity_id = 0 ) {
     103        global $activities_template;
     104
     105        if ( empty( $activity_id ) ) {
     106                return false;
     107        }
     108
     109        if ( ! empty( $activities_template->activities ) ) {
     110                $activity = (array) $activities_template->activities;
     111                $activity = reset( $activity );
     112
     113                // No need to requery if we already got the embed activity
     114                if ( (int) $activity_id === (int) $activity->id ) {
     115                        return $activities_template->has_activities();
     116                }
     117        }
     118
     119        return bp_has_activities( array(
     120                'display_comments' => 'threaded',
     121                'show_hidden'      => true,
     122                'include'          => (int) $activity_id,
     123        ) );
     124}
     125
     126/**
     127 * Outputs excerpt for an activity embed item.
     128 *
     129 * @since 2.6.0
     130 */
     131function bp_activity_embed_excerpt( $content = '' ) {
     132        echo bp_activity_get_embed_excerpt( $content = '' );
     133}
     134
     135        /**
     136         * Generates excerpt for an activity embed item.
     137         *
     138         * @since 2.6.0
     139         *
     140         * @param  string $content The content to generate an excerpt for.
     141         * @return string
     142         */
     143        function bp_activity_get_embed_excerpt( $content = '' ) {
     144                if ( empty( $content ) && ! empty( $GLOBALS['activities_template']->in_the_loop ) ) {
     145                        $content = $GLOBALS['activities_template']->activity->content;
     146                }
     147
     148                /**
     149                 * bp_activity_truncate_entry() includes the 'Read More' link, which is why
     150                 * we're using this instead of bp_create_excerpt().
     151                 */
     152                $content = html_entity_decode( $content );
     153                $content = bp_activity_truncate_entry( $content, array(
     154                        'html' => false,
     155                        'filter_shortcodes' => true,
     156                        'strip_tags'        => true,
     157                        'force_truncate'    => true
     158                ) );
     159
     160                /**
     161                 * Filter the activity embed excerpt.
     162                 *
     163                 * @since 2.6.0
     164                 *
     165                 * @var string $content Embed Excerpt.
     166                 * @var string $unmodified_content Unmodified activity content.
     167                 */
     168                return apply_filters( 'bp_activity_get_embed_excerpt', $content, $GLOBALS['activities_template']->activity->content );
     169        }
     170
     171/**
     172 * Outputs the first embedded item in the activity oEmbed template.
     173 *
     174 * @since 2.6.0
     175 */
     176function bp_activity_embed_media() {
     177        // Bail if oEmbed request explicitly hides media.
     178        if ( isset( $_REQUEST['hide_media'] ) && true == wp_validate_boolean( $_REQUEST['hide_media'] ) ) {
     179                /**
     180                 * Do something after media is rendered for an activity oEmbed item.
     181                 *
     182                 * @since 2.6.0
     183                 */
     184                do_action( 'bp_activity_embed_after_media' );
     185
     186                return;
     187        }
     188
     189        /**
     190         * Should we display media in the oEmbed template?
     191         *
     192         * @since 2.6.0
     193         *
     194         * @param bool $retval Defaults to true.
     195         */
     196        $allow_media = apply_filters( 'bp_activity_embed_display_media', true );
     197
     198        // Find oEmbeds from only WP registered providers.
     199        bp_remove_all_filters( 'oembed_providers' );
     200        $media = bp_core_extract_media_from_content( $GLOBALS['activities_template']->activity->content, 'embeds' );
     201        bp_restore_all_filters( 'oembed_providers' );
     202
     203        // oEmbeds have precedence over inline video / audio.
     204        if ( isset( $media['embeds'] ) && true === $allow_media ) {
     205                // Autoembed first URL.
     206                $oembed_defaults = wp_embed_defaults();
     207                $oembed_args = array(
     208                        'width'    => $oembed_defaults['width'],
     209                        'height'   => $oembed_defaults['height'],
     210                        'discover' => true
     211                );
     212                $url      = $media['embeds'][0]['url'];
     213                $cachekey = '_oembed_response_' . md5( $url . serialize( $oembed_args ) );
     214
     215                // Try to fetch oEmbed response from meta.
     216                $oembed = bp_activity_get_meta( bp_get_activity_id(), $cachekey );
     217
     218                // No cache, so fetch full oEmbed response now!
     219                if ( '' === $oembed ) {
     220                        $o = _wp_oembed_get_object();
     221                        $oembed = $o->fetch( $o->get_provider( $url, $oembed_args ), $url, $oembed_args );
     222
     223                        // Cache oEmbed response.
     224                        bp_activity_update_meta( bp_get_activity_id(), $cachekey, $oembed );
     225                }
     226
     227                $content = '';
     228
     229                /**
     230                 * Filters the default embed display max width.
     231                 *
     232                 * This is used if the oEmbed response does not return a thumbnail width.
     233                 *
     234                 * @since 2.6.0
     235                 *
     236                 * @param int $width.
     237                 */
     238                $width = (int) apply_filters( 'bp_activity_embed_display_media_width', 550 );
     239
     240                // Set thumbnail.
     241                if ( 'photo' === $oembed->type ) {
     242                        $thumbnail = $oembed->url;
     243                } elseif ( isset( $oembed->thumbnail_url ) ) {
     244                        $thumbnail = $oembed->thumbnail_url;
     245
     246                /* Non-oEmbed standard attributes */
     247                // Mixcloud
     248                } elseif ( isset( $oembed->image ) ) {
     249                        $thumbnail = $oembed->image;
     250                // ReverbNation
     251                } elseif ( isset( $oembed->{'thumbnail-url'} ) ) {
     252                        $thumbnail = $oembed->{'thumbnail-url'};
     253                }
     254
     255                // Display thumb and related oEmbed meta.
     256                if ( true === isset ( $thumbnail ) ) {
     257                        $play_icon = $caption = '';
     258
     259                        // Add play icon for non-photos.
     260                        if ( 'photo' !== $oembed->type ) {
     261                                /**
     262                                 * ion-play icon from Ionicons.
     263                                 *
     264                                 * @link    http://ionicons.com/
     265                                 * @license MIT
     266                                 */
     267                                $play_icon = <<<EOD
     268<svg id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M405.2,232.9L126.8,67.2c-3.4-2-6.9-3.2-10.9-3.2c-10.9,0-19.8,9-19.8,20H96v344h0.1c0,11,8.9,20,19.8,20  c4.1,0,7.5-1.4,11.2-3.4l278.1-165.5c6.6-5.5,10.8-13.8,10.8-23.1C416,246.7,411.8,238.5,405.2,232.9z"/></svg>
     269EOD;
     270
     271                                $play_icon = sprintf( '<a rel="nofollow" class="play-btn" href="%1$s" onclick="top.location.href=\'%1$s\'">%2$s</a>', esc_url( $url ), $play_icon );
     272                        }
     273
     274                        // Thumb width
     275                        $thumb_width = isset( $oembed->thumbnail_width ) && 'photo' !== $oembed->type && (int) $oembed->thumbnail_width < 550 ? (int) $oembed->thumbnail_width : $width;
     276
     277                        $float_width = 350;
     278
     279                        // Set up thumb.
     280                        $content = sprintf( '<div class="thumb" style="max-width:%1$spx">%2$s<a href="%3$s" rel="nofollow" onclick="top.location.href=\'%3$s\'"><img src="%4$s" /></a></div>', $thumb_width, $play_icon, esc_url( $url ), esc_url( $thumbnail ) );
     281
     282                        // Show title.
     283                        if ( isset( $oembed->title ) ) {
     284                                $caption .= sprintf( '<p class="caption-title"><strong>%s</strong></p>', apply_filters( 'single_post_title', $oembed->title ) );
     285                        }
     286
     287                        // Show description (non-oEmbed standard)
     288                        if ( isset( $oembed->description ) ) {
     289                                $caption .= sprintf( '<div class="caption-description">%s</div>', apply_filters( 'bp_activity_get_embed_excerpt', $oembed->description ) );
     290                        }
     291
     292                        // Show author info.
     293                        if ( isset( $oembed->provider_name ) && isset( $oembed->author_name ) ) {
     294                                /* translators: By [oEmbed author] on [oEmbed provider]. eg. By BuddyPress on YouTube. */
     295                                $anchor_text = sprintf( __( 'By %1$s on %2$s', 'buddypress' ), $oembed->author_name, $oembed->provider_name );
     296
     297                        } elseif ( isset( $oembed->provider_name ) ) {
     298                                $anchor_text = sprintf( __( 'View on %s', 'buddypress' ), $oembed->provider_name );
     299                        }
     300
     301                        if ( true === isset( $anchor_text ) )  {
     302                                $caption .= sprintf( '<a rel="nofollow" href="%1$s" onclick="top.location.href=\'%1$s\'">%2$s</a>', esc_url( $url ), apply_filters( 'the_title', $anchor_text ) );
     303                        }
     304
     305                        // Set up caption.
     306                        if ( '' !== $caption ) {
     307                                $css_class = isset( $oembed->provider_name ) ? sprintf( ' provider-%s', sanitize_html_class( strtolower( $oembed->provider_name ) ) ) : '';
     308                                $caption = sprintf( '<div class="caption%1$s" style="width:%2$s">%3$s</div>',
     309                                        $css_class,
     310                                        $thumb_width > $float_width ? 100 . '%' : round( ( $width - (int) $thumb_width ) / $width * 100 ) . '%',
     311                                        $caption
     312                                );
     313
     314                                $content .= $caption;
     315                        }
     316                }
     317
     318                // Print rich content.
     319                if ( '' !== $content ) {
     320                        printf( '<div class="bp-activity-embed-display-media %s" style="max-width:%spx">%s</div>',
     321                                $thumb_width < $float_width ? 'two-col' : 'one-col',
     322                                $thumb_width < $float_width ? $width : $thumb_width,
     323                                $content
     324                        );
     325                }
     326
     327        // Video / audio.
     328        } elseif ( true === $allow_media ) {
     329                // Call BP_Embed if it hasn't already loaded.
     330                bp_embed_init();
     331
     332                // Run shortcode and embed routine.
     333                $content = buddypress()->embed->run_shortcode( $GLOBALS['activities_template']->activity->content );
     334                $content = buddypress()->embed->autoembed( $content );
     335
     336                // Try to find inline video / audio.
     337                $media = bp_core_extract_media_from_content( $content, 96 );
     338
     339                // Video takes precedence. HTML5-only.
     340                if ( isset( $media['videos'] ) && 'shortcodes' === $media['videos'][0]['source'] ) {
     341                        printf( '<video controls preload="metadata"><source src="%1$s"><p>%2$s</p></video>',
     342                                esc_url( $media['videos'][0]['url'] ),
     343                                esc_html__( 'Your browser does not support HTML5 video', 'buddypress' )
     344                        );
     345
     346                // No video? Try audio. HTML5-only.
     347                } elseif ( isset( $media['audio'] ) && 'shortcodes' === $media['audio'][0]['source'] ) {
     348                        printf( '<audio controls preload="metadata"><source src="%1$s"><p>%2$s</p></audio>',
     349                                esc_url( $media['audio'][0]['url'] ),
     350                                esc_html__( 'Your browser does not support HTML5 audio', 'buddypress' )
     351                        );
     352                }
     353
     354        }
     355
     356        /** This hook is documented in /bp-activity/bp-activity-embeds.php */
     357        do_action( 'bp_activity_embed_after_media' );
     358}
  • src/bp-activity/bp-activity-filters.php

     
    387387 * This method can only be used inside the Activity loop.
    388388 *
    389389 * @since 1.5.0
     390 * @since 2.6.0 Added $args parameter.
    390391 *
    391392 * @param string $text The original activity entry text.
    392393 * @return string $excerpt The truncated text.
    393394 */
    394 function bp_activity_truncate_entry( $text ) {
     395function bp_activity_truncate_entry( $text, $args = array() ) {
    395396        global $activities_template;
    396397
    397398        /**
     
    407408        );
    408409
    409410        // The full text of the activity update should always show on the single activity screen.
    410         if ( ! $maybe_truncate_text || bp_is_single_activity() ) {
     411        if ( empty( $args['force_truncate'] ) && ( ! $maybe_truncate_text || bp_is_single_activity() ) ) {
    411412                return $text;
    412413        }
    413414
     
    429430         */
    430431        $excerpt_length = apply_filters( 'bp_activity_excerpt_length', 358 );
    431432
     433        $args = wp_parse_args( $args, array( 'ending' => __( '&hellip;', 'buddypress' ) ) );
     434
    432435        // Run the text through the excerpt function. If it's too short, the original text will be returned.
    433         $excerpt        = bp_create_excerpt( $text, $excerpt_length, array( 'ending' => __( '&hellip;', 'buddypress' ) ) );
     436        $excerpt        = bp_create_excerpt( $text, $excerpt_length, $args );
    434437
    435438        /*
    436439         * If the text returned by bp_create_excerpt() is different from the original text (ie it's
    437440         * been truncated), add the "Read More" link. Note that bp_create_excerpt() is stripping
    438441         * shortcodes, so we have strip them from the $text before the comparison.
    439442         */
    440         if ( $excerpt != strip_shortcodes( $text ) ) {
     443        if ( strlen( $excerpt ) > strlen( strip_shortcodes( $text ) ) ) {
    441444                $id = !empty( $activities_template->activity->current_comment->id ) ? 'acomment-read-more-' . $activities_template->activity->current_comment->id : 'activity-read-more-' . bp_get_activity_id();
    442445
    443446                $excerpt = sprintf( '%1$s<span class="activity-read-more" id="%2$s"><a href="%3$s" rel="nofollow">%4$s</a></span>', $excerpt, $id, bp_get_activity_thread_permalink(), $append_text );
  • src/bp-activity/bp-activity-functions.php

     
    32263226        }
    32273227
    32283228        // Generate a text excerpt for this activity item (and remove any oEmbeds URLs).
    3229         $summary = strip_shortcodes( html_entity_decode( strip_tags( $content ) ) );
    3230         $summary = bp_create_excerpt( preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $summary ) );
     3229        $summary = bp_create_excerpt( html_entity_decode( $content ), 225, array(
     3230                'html' => false,
     3231                'filter_shortcodes' => true,
     3232                'strip_tags'        => true,
     3233                'remove_links'      => true
     3234        ) );
    32313235
    32323236        if ( $use_media_type === 'embeds' ) {
    32333237                $summary .= PHP_EOL . PHP_EOL . $extracted_media['url'];
     
    33853389 */
    33863390function bp_activity_embed() {
    33873391        add_filter( 'embed_post_id',         'bp_get_activity_id'                  );
     3392        add_filter( 'oembed_dataparse',      'bp_activity_oembed_dataparse', 10, 2 );
    33883393        add_filter( 'bp_embed_get_cache',    'bp_embed_activity_cache',      10, 3 );
    33893394        add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 );
    33903395}
    33913396add_action( 'activity_loop_start', 'bp_activity_embed' );
    33923397
    33933398/**
     3399 * Cache full oEmbed response from oEmbed.
     3400 *
     3401 * @since 2.6.0
     3402 *
     3403 * @param string $retval Current oEmbed result.
     3404 * @param object $data   Full oEmbed response.
     3405 * @param string $url    URL used for the oEmbed request.
     3406 * @return string
     3407 */
     3408function bp_activity_oembed_dataparse( $retval, $data ) {
     3409        buddypress()->activity->oembed_response = $data;
     3410
     3411        return $retval;
     3412}
     3413
     3414/**
    33943415 * Set up activity oEmbed cache while recursing through activity comments.
    33953416 *
    33963417 * While crawling through an activity comment tree
     
    34813502 */
    34823503function bp_embed_activity_save_cache( $cache, $cachekey, $id ) {
    34833504        bp_activity_update_meta( $id, $cachekey, $cache );
     3505
     3506        // Cache full oEmbed response.
     3507        if ( true === isset( buddypress()->activity->oembed_response ) ) {
     3508                $cachekey = str_replace( '_oembed', '_oembed_response', $cachekey );
     3509                bp_activity_update_meta( $id, $cachekey, buddypress()->activity->oembed_response );
     3510        }
    34843511}
    34853512
    34863513/**
  • src/bp-activity/classes/class-bp-activity-component.php

     
    3232                        array(
    3333                                'adminbar_myaccount_order' => 10,
    3434                                'search_query_arg' => 'activity_search',
     35                                'features' => array( 'embeds' )
    3536                        )
    3637                );
    3738        }
     
    7273                        $includes[] = 'akismet';
    7374                }
    7475
     76                // Embeds - only applicable for WP 4.5+
     77                if ( bp_get_major_wp_version() >= 4.5 && bp_is_active( $this->id, 'embeds' ) ) {
     78                        $includes[] = 'embeds';
     79                }
     80
    7581                if ( is_admin() ) {
    7682                        $includes[] = 'admin';
    7783                }
  • new file src/bp-activity/classes/class-bp-activity-oembed-component.php

    new file mode 100644
    - +  
     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.6.0
     18 */
     19class BP_Activity_oEmbed_Component extends BP_oEmbed_Component {
     20        /**
     21         * Custom oEmbed slug endpoint.
     22         *
     23         * @since 2.6.0
     24         *
     25         * @var string
     26         */
     27        public $slug_endpoint = 'activity';
     28
     29        /**
     30         * Custom hooks.
     31         *
     32         * @since 2.6.0
     33         */
     34        protected function custom_hooks() {
     35                add_action( 'oembed_dataparse',   array( $this, 'use_custom_iframe_sandbox_attribute' ), 20, 3 );
     36                add_action( 'embed_content_meta', array( $this, 'embed_comments_button' ), 5 );
     37                add_action( 'get_template_part_assets/embeds/header', array( $this, 'on_activity_header' ), 10, 2 );
     38
     39                add_filter( 'bp_activity_embed_html', array( $this, 'modify_iframe' ) );
     40        }
     41
     42        /**
     43         * Add custom endpoint arguments.
     44         *
     45         * Currently, includes 'hide_media'.
     46         *
     47         * @since 2.6.0
     48         *
     49         * @return array
     50         */
     51        protected function set_route_args() {
     52                return array(
     53                        'hide_media' => array(
     54                                'default' => false,
     55                                'sanitize_callback' => 'wp_validate_boolean'
     56                        )
     57                );
     58        }
     59
     60        /**
     61         * Output our custom embed template part.
     62         *
     63         * @since 2.6.0
     64         */
     65        protected function content() {
     66                bp_get_asset_template_part( 'embeds/activity' );
     67        }
     68
     69        /**
     70         * Check if we're on our single activity page.
     71         *
     72         * @since 2.6.0
     73         *
     74         * @return bool
     75         */
     76        protected function is_page() {
     77                return bp_is_single_activity();
     78        }
     79
     80        /**
     81         * Validates the URL to determine if the activity item is valid.
     82         *
     83         * @since 2.6.0
     84         *
     85         * @param  string   $url The URL to check.
     86         * @return int|bool Activity ID on success; boolean false on failure.
     87         */
     88        protected function validate_url_to_item_id( $url ) {
     89                if ( bp_core_enable_root_profiles() ) {
     90                        $domain = bp_get_root_domain();
     91                } else {
     92                        $domain = bp_get_members_directory_permalink();
     93                }
     94
     95                // Check the URL to see if this is a single activity URL.
     96                if ( 0 !== strpos( $url, $domain ) ) {
     97                        return false;
     98                }
     99
     100                // Check for activity slug.
     101                if ( false === strpos( $url, '/' . bp_get_activity_slug() . '/' ) ) {
     102                        return false;
     103                }
     104
     105                // Do more checks.
     106                $url = trim( untrailingslashit( $url ) );
     107
     108                // Grab the activity ID.
     109                $activity_id = (int) substr(
     110                        $url,
     111                        strrpos( $url, '/' ) + 1
     112                );
     113
     114                if ( ! empty( $activity_id ) ) {
     115                        // Check if activity item still exists.
     116                        $activity = new BP_Activity_Activity( $activity_id );
     117
     118                        // Okay, we're good to go!
     119                        if ( ! empty( $activity->component ) && 0 === (int) $activity->is_spam ) {
     120                                return $activity_id;
     121                        }
     122                }
     123
     124                return false;
     125        }
     126
     127        /**
     128         * Sets the oEmbed response data for our activity item.
     129         *
     130         * @since 2.6.0
     131         *
     132         * @param  int $item_id The activity ID.
     133         * @return array
     134         */
     135        protected function set_oembed_response_data( $item_id ) {
     136                $activity = new BP_Activity_Activity( $item_id );
     137
     138                return array(
     139                        'content'      => $activity->content,
     140                        'title'        => __( 'Activity', 'buddypress' ),
     141                        'author_name'  => bp_core_get_user_displayname( $activity->user_id ),
     142                        'author_url'   => bp_core_get_user_domain( $activity->user_id ),
     143
     144                        // Custom identifier.
     145                        'x_buddypress' => 'activity'
     146                );
     147        }
     148
     149        /**
     150         * Sets a custom <blockquote> for our oEmbed fallback HTML.
     151         *
     152         * @since 2.6.0
     153         *
     154         * @param  int $item_id The activity ID.
     155         * @return string
     156         */
     157        protected function set_fallback_html( $item_id ) {
     158                $activity    = new BP_Activity_Activity( $item_id );
     159                $mentionname = bp_activity_do_mentions() ? ' (@' . bp_activity_get_user_mentionname( $activity->user_id ) . ')' : '';
     160                $date        = date_i18n( get_option( 'date_format' ), strtotime( $activity->date_recorded ) );
     161
     162                // Make sure we can use some activity functions that depend on the loop.
     163                $GLOBALS['activities_template'] = new stdClass;
     164                $GLOBALS['activities_template']->activity = $activity;
     165
     166                // 'wp-embedded-content' CSS class is necessary due to how the embed JS works.
     167                $blockquote = sprintf( '<blockquote class="wp-embedded-content bp-activity-item">%1$s%2$s %3$s</blockquote>',
     168                        bp_activity_get_embed_excerpt( $activity->content ),
     169                        '- ' . bp_core_get_user_displayname( $activity->user_id ) . $mentionname,
     170                        '<a href="' . esc_url( bp_activity_get_permalink( $item_id ) ) . '">' . $date . '</a>'
     171                );
     172
     173                // Clean up.
     174                unset( $GLOBALS['activities_template'] );
     175
     176                /**
     177                 * Filters the fallback HTML used when embedding a BP activity item.
     178                 *
     179                 * @since 2.6.0
     180                 *
     181                 * @param string               $blockquote Current fallback HTML
     182                 * @param BP_Activity_Activity $activity   Activity object
     183                 */
     184                return apply_filters( 'bp_activity_embed_fallback_html', $blockquote, $activity );
     185        }
     186
     187        /**
     188         * Sets a custom <iframe> title for our oEmbed item.
     189         *
     190         * @since 2.6.0
     191         *
     192         * @param  int $item_id The activity ID
     193         * @return string
     194         */
     195        protected function set_iframe_title( $item_id ) {
     196                return __( 'Embedded Activity Item', 'buddypress' );
     197        }
     198
     199        /**
     200         * Use our custom <iframe> sandbox attribute in our oEmbed response.
     201         *
     202         * WordPress sets the <iframe> sandbox attribute to 'allow-scripts' regardless
     203         * of whatever the oEmbed response is in {@link wp_filter_oembed_result()}. We
     204         * need to add back our custom sandbox value so links will work.
     205         *
     206         * @since 2.6.0
     207         *
     208         * @see BP_Activity_Component::modify_iframe() where our custom sandbox value is set.
     209         *
     210         * @param string $result The oEmbed HTML result.
     211         * @param object $data   A data object result from an oEmbed provider.
     212         * @param string $url    The URL of the content to be embedded.
     213         * @return string
     214         */
     215        public function use_custom_iframe_sandbox_attribute( $result, $data, $url ) {
     216                // Make sure we are on a BuddyPress activity oEmbed request.
     217                if ( false === isset( $data->x_buddypress ) || 'activity' !== $data->x_buddypress ) {
     218                        return $result;
     219                }
     220
     221                // Get unfiltered sandbox attribute from our own oEmbed response.
     222                $sandbox_pos = strpos( $data->html, 'sandbox=' ) + 9;
     223                $sandbox = substr( $data->html, $sandbox_pos, strpos( $data->html, '"', $sandbox_pos ) - $sandbox_pos );
     224
     225                // Replace only if our sandbox attribute contains 'allow-top-navigation'.
     226                if ( false !== strpos( $sandbox, 'allow-top-navigation' ) ) {
     227                        $result = str_replace( ' sandbox="allow-scripts"', " sandbox=\"{$sandbox}\"", $result );
     228
     229                        // Also remove 'security' attribute; this is only used for IE < 10.
     230                        $result = str_replace( 'security="restricted"', "", $result );
     231                }
     232
     233                return $result;
     234        }
     235
     236        /**
     237         * Modify various IFRAME-related items if embeds are allowed.
     238         *
     239         * HTML modified:
     240         *  - Add sandbox="allow-top-navigation" attribute. This allows links to work
     241         *    within the iframe sandbox attribute.
     242         *
     243         * JS modified:
     244         *  - Remove IFRAME height restriction of 1000px. Fixes long embed items being
     245         *    truncated.
     246         *
     247         * @since 2.6.0
     248         *
     249         * @param  string $retval Current embed HTML.
     250         * @return string
     251         */
     252        public function modify_iframe( $retval ) {
     253                // Add 'allow-top-navigation' to allow links to be clicked.
     254                $retval = str_replace( 'sandbox="', 'sandbox="allow-top-navigation ', $retval );
     255
     256                // See /wp-includes/js/wp-embed.js.
     257                if ( SCRIPT_DEBUG ) {
     258                        // Removes WP's hardcoded IFRAME height restriction.
     259                        $retval = str_replace( 'height = 1000;', 'height = height;', $retval );
     260
     261                // This is for the WP build minified version.
     262                } else {
     263                        $retval = str_replace( 'g=1e3', 'g=g', $retval );
     264                }
     265
     266                return $retval;
     267        }
     268
     269        /**
     270         * Do stuff when our oEmbed activity header template part is loading.
     271         *
     272         * Currently, removes wpautop() from the bp_activity_action() function.
     273         *
     274         * @since 2.6.0
     275         *
     276         * @param string $slug Template part slug requested.
     277         * @param string $name Template part name requested.
     278         */
     279        public function on_activity_header( $slug, $name ) {
     280                if ( false === $this->is_page() || 'activity' !== $name ) {
     281                        return;
     282                }
     283
     284                remove_filter( 'bp_get_activity_action', 'wpautop' );
     285        }
     286
     287        /**
     288         * Prints the markup for the activity embed comments button.
     289         *
     290         * Basically a copy of {@link print_embed_comments_button()}, but modified for
     291         * the BP activity component.
     292         *
     293         * @since 2.6.0
     294         */
     295        public function embed_comments_button() {
     296                if ( ! did_action( 'bp_embed_content' ) || ! bp_is_single_activity() ) {
     297                        return;
     298                }
     299
     300                // Make sure our custom permalink shows up in the 'WordPress Embed' block.
     301                add_filter( 'the_permalink', array( $this, 'filter_embed_url' ) );
     302
     303                // Only show comment bubble if we have some activity comments.
     304                $count = bp_activity_get_comment_count();
     305                if ( empty( $count ) ) {
     306                        return;
     307                }
     308        ?>
     309
     310                <div class="wp-embed-comments">
     311                        <a href="<?php bp_activity_thread_permalink(); ?>">
     312                                <span class="dashicons dashicons-admin-comments"></span>
     313                                <?php
     314                                printf(
     315                                        _n(
     316                                                '%s <span class="screen-reader-text">Comment</span>',
     317                                                '%s <span class="screen-reader-text">Comments</span>',
     318                                                $count,
     319                                                'buddypress'
     320                                        ),
     321                                        number_format_i18n( $count )
     322                                );
     323                                ?>
     324                        </a>
     325                </div>
     326
     327        <?php
     328        }
     329}
  • src/bp-core/bp-core-actions.php

     
    4040add_action( 'setup_theme',             'bp_setup_theme',            10    );
    4141add_action( 'after_setup_theme',       'bp_after_setup_theme',      100   ); // After WP themes.
    4242add_action( 'wp_enqueue_scripts',      'bp_enqueue_scripts',        10    );
     43add_action( 'enqueue_embed_scripts',   'bp_enqueue_embed_scripts',  10    );
    4344add_action( 'admin_bar_menu',          'bp_setup_admin_bar',        20    ); // After WP core.
    4445add_action( 'template_redirect',       'bp_template_redirect',      10    );
    4546add_action( 'widgets_init',            'bp_widgets_init',           10    );
  • src/bp-core/bp-core-dependency.php

     
    475475}
    476476
    477477/**
     478 * Fires the 'bp_enqueue_embed_scripts' action in the <head> for BP oEmbeds.
     479 *
     480 * @since 2.6.0
     481 */
     482function bp_enqueue_embed_scripts() {
     483        if ( ! is_buddypress() ) {
     484                return;
     485        }
     486
     487        /**
     488         * Enqueue CSS and JS files for BuddyPress embeds.
     489         *
     490         * @since 2.6.0
     491         */
     492        do_action ( 'bp_enqueue_embed_scripts' );
     493}
     494
     495/**
    478496 * Fire the 'bp_add_rewrite_tag' action, where BP adds its custom rewrite tags.
    479497 *
    480498 * @since 1.8.0
  • src/bp-core/bp-core-functions.php

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

     
    6262}
    6363
    6464/**
     65 * Get an asset template part.
     66 *
     67 * Basically the same as {@link bp_get_template_part()}, but with 'assets/'
     68 * prepended to the slug.
     69 *
     70 * @since 2.6.0
     71 *
     72 * @see bp_get_template_part() for full documentation.
     73 */
     74function bp_get_asset_template_part( $slug, $name = null ) {
     75        return bp_get_template_part( "assets/{$slug}", $name );
     76}
     77
     78/**
    6579 * Retrieve the name of the highest priority template file that exists.
    6680 *
    6781 * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
  • src/bp-core/bp-core-template.php

     
    783783 * This function is borrowed from CakePHP v2.0, under the MIT license. See
    784784 * http://book.cakephp.org/view/1469/Text#truncate-1625
    785785 *
     786 * @since 2.6.0 Added 'strip_tags' and 'remove_links' as $options args.
     787 *
    786788 * ### Options:
    787789 *
    788790 * - `ending` Will be used as Ending and appended to the trimmed string.
     
    805807 *                                     excerpt length. Default: true.
    806808 *     @type bool   $filter_shortcodes If true, shortcodes will be stripped.
    807809 *                                     Default: true.
     810 *     @type bool   $strip_tags        If true, HTML tags will be stripped. Default: false.
     811 *                                     Only applicable if $html is set to false.
     812 *     @type bool   $remove_links      If true, URLs will be stripped. Default: false.
     813 *                                     Only applicable if $html is set to false.
    808814 * }
    809815 * @return string Trimmed string.
    810816 */
     
    817823                'ending'            => __( ' [&hellip;]', 'buddypress' ),
    818824                'exact'             => false,
    819825                'html'              => true,
    820                 'filter_shortcodes' => $filter_shortcodes_default
     826                'filter_shortcodes' => $filter_shortcodes_default,
     827                'strip_tags'        => false,
     828                'remove_links'      => false,
    821829        ), 'create_excerpt' );
    822830
    823831        // Save the original text, to be passed along to the filter.
     
    903911                        }
    904912                }
    905913        } else {
     914                // Strip HTML tags if necessary.
     915                if ( ! empty( $r['strip_tags'] ) ) {
     916                        $text = strip_tags( $text );
     917                }
     918
     919                // Remove links if necessary.
     920                if ( ! empty( $r['remove_links'] ) ) {
     921                        $text = preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $text );
     922                }
     923
    906924                if ( mb_strlen( $text ) <= $length ) {
    907                         return $text;
     925                        /**
     926                         * Filters the final generated excerpt.
     927                         *
     928                         * @since 1.1.0
     929                         *
     930                         * @param string $truncate      Generated excerpt.
     931                         * @param string $original_text Original text provided.
     932                         * @param int    $length        Length of returned string, including ellipsis.
     933                         * @param array  $options       Array of HTML attributes and options.
     934                         */
     935                        return apply_filters( 'bp_create_excerpt', $text, $original_text, $length, $options );
    908936                } else {
    909937                        $truncate = mb_substr( $text, 0, $length - mb_strlen( $ending ) );
    910938                }
  • src/bp-core/bp-core-theme-compatibility.php

     
    670670 * @return string $template Template name.
    671671 */
    672672function bp_template_include_theme_compat( $template = '' ) {
     673        // If embed template, bail.
     674        if ( true === function_exists( 'is_embed' ) && is_embed() ) {
     675                return $template;
     676        }
    673677
    674678        // If the current theme doesn't need theme compat, bail at this point.
    675679        if ( ! bp_use_theme_compat_with_current_theme() ) {
  • src/bp-core/classes/class-bp-admin.php

     
    826826                                <a href="https://bbpress.org">bbPress</a>,
    827827                                <a href="https://github.com/ichord/Caret.js">Caret.js</a>,
    828828                                <a href="http://tedgoas.github.io/Cerberus/">Cerberus</a>,
     829                                <a href="http://ionicons.com/">Ionicons</a>,
    829830                                <a href="https://github.com/carhartl/jquery-cookie">jquery.cookie</a>,
    830831                                <a href="https://www.mediawiki.org/wiki/MediaWiki">MediaWiki</a>,
    831832                                <a href="https://wordpress.org">WordPress</a>.
  • new file src/bp-core/classes/class-bp-oembed-component.php

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

    new file mode 100644
    - +  
     1
     2                <?php if ( bp_activity_embed_has_activity( bp_current_action() ) ) : ?>
     3
     4                        <?php while ( bp_activities() ) : bp_the_activity(); ?>
     5                                <div class="bp-embed-excerpt"><?php bp_activity_embed_excerpt(); ?></div>
     6
     7                                <?php bp_activity_embed_media(); ?>
     8
     9                        <?php endwhile; ?>
     10
     11                <?php endif; ?>
     12
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/css-activity.php

    new file mode 100644
    - +  
     1#bp-embed-header:after {
     2        clear: both;
     3        content: "";
     4        display: table;
     5        margin-bottom: 1em;
     6}
     7
     8.bp-embed-avatar {
     9        float: left;
     10        margin: 0 .75em 0 0;
     11}
     12
     13p.bp-embed-activity-action {
     14        font-size: 15px;
     15        margin-bottom: 0;
     16}
     17
     18p.bp-embed-activity-action a:first-child {
     19        color: #32373c;
     20        font-weight: bold;
     21}
     22
     23p.bp-embed-activity-action img.avatar {
     24        padding: 0 4px 0 3px;
     25        vertical-align: text-bottom;
     26}
     27
     28.bp-embed-excerpt {
     29        margin-bottom: 1em;
     30}
     31
     32.bp-embed-excerpt a {
     33        color: #21759b;
     34        display: inline-block;
     35        overflow: hidden;
     36        text-overflow: ellipsis;
     37        vertical-align: top;
     38        white-space: nowrap;
     39        max-width: 250px;
     40}
     41
     42.activity-read-more {
     43        margin-left: .5em;
     44}
     45
     46.activity-read-more a {
     47        color: #b4b9be;
     48}
     49
     50.wp-embed-footer {
     51        margin-top: 20px;
     52}
     53
     54span.bp-embed-timestamp {
     55        font-size: .9em;
     56}
     57
     58video {
     59        width: 100%;
     60        height: auto;
     61}
     62
     63.bp-activity-embed-display-media {
     64        border: 1px solid #ccc;
     65        border-radius: 6px;
     66}
     67
     68.bp-activity-embed-display-media.one-col,
     69.bp-activity-embed-display-media.one-col .thumb,
     70.bp-activity-embed-display-media.one-col .thumb img {
     71        width: 100%;
     72}
     73
     74.bp-activity-embed-display-media.two-col .thumb,
     75.bp-activity-embed-display-media.two-col .caption {
     76        display: table-cell;
     77}
     78
     79.bp-activity-embed-display-media.two-col .thumb {
     80        background: #000;
     81        vertical-align: middle;
     82}
     83
     84.bp-activity-embed-display-media.two-col .caption {
     85        vertical-align: top;
     86}
     87
     88.bp-activity-embed-display-media.two-col .thumb img {
     89        border-right: 1px solid #ccc;
     90        display: block;
     91        width: 100%;
     92}
     93
     94.bp-activity-embed-display-media .thumb {
     95        position: relative;
     96}
     97
     98.bp-activity-embed-display-media .caption {
     99        padding: .2em .5em .5em .5em;
     100}
     101
     102a.play-btn {
     103        background: rgba(0, 0, 0, 0.75);
     104        border-radius: 50%;
     105        height: 50px;
     106        left: 50%;
     107        margin: 0;
     108        padding: 1em;
     109        position: absolute;
     110        text-indent: 0.25em;
     111        top: 50%;
     112        transform: translateY(-50%) translateX(-50%);
     113        -webkit-transform: translateY(-50%) translateX(-50%);
     114        transition: all 0.2s ease-out;
     115        width: 50px;
     116}
     117
     118.bp-activity-embed-display-media.two-col a.play-btn {
     119        height: 35px;
     120        width: 35px;
     121}
     122
     123a.play-btn:hover {
     124        background: rgba(0, 0, 0, 0.95);
     125        transform: translateY(-50%) translateX(-50%) scale(1.05);
     126        -webkit-transform: translateY(-50%) translateX(-50%) scale(1.05);
     127        transition: all 0.2s ease-out;
     128}
     129
     130.bp-activity-embed-display-media .thumb svg {
     131        fill: #fff;
     132        overflow: hidden;
     133}
     134
     135.bp-activity-embed-display-media .caption-description {
     136        font-size: 90%;
     137        margin: .4em 0;
     138}
     139
     140@media only screen and (max-width: 480px) {
     141        .bp-activity-embed-display-media.two-col .thumb {
     142                border-bottom: 1px solid #ccc;
     143                border-right: 0;
     144                display: block;
     145                max-width: none !important;
     146        }
     147
     148        a.play-btn {
     149                height: 35px;
     150                width: 35px;
     151        }
     152}
     153 No newline at end of file
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/footer.php

    new file mode 100644
    - +  
     1                        <div class="wp-embed-footer">
     2                                <?php the_embed_site_title() ?>
     3
     4                                <div class="wp-embed-meta">
     5                                        <?php
     6                                        /** This action is documented in wp-includes/theme-compat/embed-content.php */
     7                                        do_action( 'embed_content_meta'); ?>
     8                                </div>
     9                        </div>
     10 No newline at end of file
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/header-activity.php

    new file mode 100644
    - +  
     1
     2                <div id="bp-embed-header">
     3                        <div class="bp-embed-avatar">
     4                                <a href="<?php bp_displayed_user_link(); ?>">
     5                                        <?php bp_displayed_user_avatar( 'type=thumb&width=45&height=45' ); ?>
     6                                </a>
     7                        </div>
     8
     9                        <?php if ( bp_activity_embed_has_activity( bp_current_action() ) ) : ?>
     10
     11                                <?php while ( bp_activities() ) : bp_the_activity(); ?>
     12                                        <p class="bp-embed-activity-action">
     13                                                <?php bp_activity_action( array( 'no_timestamp' => true ) ); ?>
     14                                        </p>
     15                                <?php endwhile; ?>
     16
     17                        <?php endif; ?>
     18
     19                        <p class="bp-embed-header-meta">
     20                                <?php if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() ) : ?>
     21                                        <span class="bp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?> &middot; </span>
     22                                <?php endif; ?>
     23
     24                                <span class="bp-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></span>
     25                        </p>
     26                </div>
  • new file src/bp-templates/bp-legacy/buddypress/assets/embeds/header.php

    new file mode 100644
    - +  
     1
     2                <div id="bp-embed-header">
     3                        <div class="bp-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="bp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?></p>
     17                        <?php endif; ?>
     18                </div>