Ticket #6772: 6772.11.patch
File 6772.11.patch, 58.6 KB (added by , 8 years ago) |
---|
-
Gruntfile.js
5 5 BUILD_DIR = 'build/', 6 6 7 7 BP_CSS = [ 8 '**/*.css' 8 '**/*.css', 9 '**/css-*.php' 9 10 ], 10 11 11 12 // CSS exclusions, for excluding files from certain tasks, e.g. rtlcss 12 13 BP_EXCLUDED_CSS = [ 13 '!**/*-rtl.css' 14 '!**/*-rtl.css', 15 '!**/*-rtl.php' 14 16 ], 15 17 16 18 BP_JS = [ … … 105 107 cwd: SOURCE_DIR, 106 108 dest: SOURCE_DIR, 107 109 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 } 110 118 } 111 119 }, 112 120 checktextdomain: { -
src/bp-activity/bp-activity-classes.php
13 13 require dirname( __FILE__ ) . '/classes/class-bp-activity-activity.php'; 14 14 require dirname( __FILE__ ) . '/classes/class-bp-activity-feed.php'; 15 15 require dirname( __FILE__ ) . '/classes/class-bp-activity-query.php'; 16 17 // Embeds - only applicable for WP 4.5+ 18 if ( 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. 14 defined( 'ABSPATH' ) || exit; 15 16 /** 17 * Loads our activity oEmbed component. 18 * 19 * @since 2.6.0 20 */ 21 function 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 } 34 add_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 */ 47 function 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 */ 71 function _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 } 89 add_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 */ 102 function 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 */ 131 function 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 */ 176 function 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> 269 EOD; 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
387 387 * This method can only be used inside the Activity loop. 388 388 * 389 389 * @since 1.5.0 390 * @since 2.6.0 Added $args parameter. 390 391 * 391 392 * @param string $text The original activity entry text. 392 393 * @return string $excerpt The truncated text. 393 394 */ 394 function bp_activity_truncate_entry( $text ) {395 function bp_activity_truncate_entry( $text, $args = array() ) { 395 396 global $activities_template; 396 397 397 398 /** … … 407 408 ); 408 409 409 410 // 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() ) ) { 411 412 return $text; 412 413 } 413 414 … … 429 430 */ 430 431 $excerpt_length = apply_filters( 'bp_activity_excerpt_length', 358 ); 431 432 433 $args = wp_parse_args( $args, array( 'ending' => __( '…', 'buddypress' ) ) ); 434 432 435 // 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' => __( '…', 'buddypress' ) ));436 $excerpt = bp_create_excerpt( $text, $excerpt_length, $args ); 434 437 435 438 /* 436 439 * If the text returned by bp_create_excerpt() is different from the original text (ie it's 437 440 * been truncated), add the "Read More" link. Note that bp_create_excerpt() is stripping 438 441 * shortcodes, so we have strip them from the $text before the comparison. 439 442 */ 440 if ( $excerpt != strip_shortcodes( $text) ) {443 if ( strlen( $excerpt ) > strlen( strip_shortcodes( $text ) ) ) { 441 444 $id = !empty( $activities_template->activity->current_comment->id ) ? 'acomment-read-more-' . $activities_template->activity->current_comment->id : 'activity-read-more-' . bp_get_activity_id(); 442 445 443 446 $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
3226 3226 } 3227 3227 3228 3228 // 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 ) ); 3231 3235 3232 3236 if ( $use_media_type === 'embeds' ) { 3233 3237 $summary .= PHP_EOL . PHP_EOL . $extracted_media['url']; … … 3385 3389 */ 3386 3390 function bp_activity_embed() { 3387 3391 add_filter( 'embed_post_id', 'bp_get_activity_id' ); 3392 add_filter( 'oembed_dataparse', 'bp_activity_oembed_dataparse', 10, 2 ); 3388 3393 add_filter( 'bp_embed_get_cache', 'bp_embed_activity_cache', 10, 3 ); 3389 3394 add_action( 'bp_embed_update_cache', 'bp_embed_activity_save_cache', 10, 3 ); 3390 3395 } 3391 3396 add_action( 'activity_loop_start', 'bp_activity_embed' ); 3392 3397 3393 3398 /** 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 */ 3408 function bp_activity_oembed_dataparse( $retval, $data ) { 3409 buddypress()->activity->oembed_response = $data; 3410 3411 return $retval; 3412 } 3413 3414 /** 3394 3415 * Set up activity oEmbed cache while recursing through activity comments. 3395 3416 * 3396 3417 * While crawling through an activity comment tree … … 3481 3502 */ 3482 3503 function bp_embed_activity_save_cache( $cache, $cachekey, $id ) { 3483 3504 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 } 3484 3511 } 3485 3512 3486 3513 /** -
src/bp-activity/classes/class-bp-activity-component.php
32 32 array( 33 33 'adminbar_myaccount_order' => 10, 34 34 'search_query_arg' => 'activity_search', 35 'features' => array( 'embeds' ) 35 36 ) 36 37 ); 37 38 } … … 72 73 $includes[] = 'akismet'; 73 74 } 74 75 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 75 81 if ( is_admin() ) { 76 82 $includes[] = 'admin'; 77 83 } -
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. 10 defined( 'ABSPATH' ) || exit; 11 12 require_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 */ 19 class 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
40 40 add_action( 'setup_theme', 'bp_setup_theme', 10 ); 41 41 add_action( 'after_setup_theme', 'bp_after_setup_theme', 100 ); // After WP themes. 42 42 add_action( 'wp_enqueue_scripts', 'bp_enqueue_scripts', 10 ); 43 add_action( 'enqueue_embed_scripts', 'bp_enqueue_embed_scripts', 10 ); 43 44 add_action( 'admin_bar_menu', 'bp_setup_admin_bar', 20 ); // After WP core. 44 45 add_action( 'template_redirect', 'bp_template_redirect', 10 ); 45 46 add_action( 'widgets_init', 'bp_widgets_init', 10 ); -
src/bp-core/bp-core-dependency.php
475 475 } 476 476 477 477 /** 478 * Fires the 'bp_enqueue_embed_scripts' action in the <head> for BP oEmbeds. 479 * 480 * @since 2.6.0 481 */ 482 function 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 /** 478 496 * Fire the 'bp_add_rewrite_tag' action, where BP adds its custom rewrite tags. 479 497 * 480 498 * @since 1.8.0 -
src/bp-core/bp-core-functions.php
1668 1668 return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES ); 1669 1669 } 1670 1670 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 */ 1681 function 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 1671 1703 /** Admin *********************************************************************/ 1672 1704 1673 1705 /** -
src/bp-core/bp-core-template-loader.php
62 62 } 63 63 64 64 /** 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 */ 74 function bp_get_asset_template_part( $slug, $name = null ) { 75 return bp_get_template_part( "assets/{$slug}", $name ); 76 } 77 78 /** 65 79 * Retrieve the name of the highest priority template file that exists. 66 80 * 67 81 * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which -
src/bp-core/bp-core-template.php
783 783 * This function is borrowed from CakePHP v2.0, under the MIT license. See 784 784 * http://book.cakephp.org/view/1469/Text#truncate-1625 785 785 * 786 * @since 2.6.0 Added 'strip_tags' and 'remove_links' as $options args. 787 * 786 788 * ### Options: 787 789 * 788 790 * - `ending` Will be used as Ending and appended to the trimmed string. … … 805 807 * excerpt length. Default: true. 806 808 * @type bool $filter_shortcodes If true, shortcodes will be stripped. 807 809 * 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. 808 814 * } 809 815 * @return string Trimmed string. 810 816 */ … … 817 823 'ending' => __( ' […]', 'buddypress' ), 818 824 'exact' => false, 819 825 'html' => true, 820 'filter_shortcodes' => $filter_shortcodes_default 826 'filter_shortcodes' => $filter_shortcodes_default, 827 'strip_tags' => false, 828 'remove_links' => false, 821 829 ), 'create_excerpt' ); 822 830 823 831 // Save the original text, to be passed along to the filter. … … 903 911 } 904 912 } 905 913 } 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 906 924 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 ); 908 936 } else { 909 937 $truncate = mb_substr( $text, 0, $length - mb_strlen( $ending ) ); 910 938 } -
src/bp-core/bp-core-theme-compatibility.php
670 670 * @return string $template Template name. 671 671 */ 672 672 function bp_template_include_theme_compat( $template = '' ) { 673 // If embed template, bail. 674 if ( true === function_exists( 'is_embed' ) && is_embed() ) { 675 return $template; 676 } 673 677 674 678 // If the current theme doesn't need theme compat, bail at this point. 675 679 if ( ! bp_use_theme_compat_with_current_theme() ) { -
src/bp-core/classes/class-bp-admin.php
826 826 <a href="https://bbpress.org">bbPress</a>, 827 827 <a href="https://github.com/ichord/Caret.js">Caret.js</a>, 828 828 <a href="http://tedgoas.github.io/Cerberus/">Cerberus</a>, 829 <a href="http://ionicons.com/">Ionicons</a>, 829 830 <a href="https://github.com/carhartl/jquery-cookie">jquery.cookie</a>, 830 831 <a href="https://www.mediawiki.org/wiki/MediaWiki">MediaWiki</a>, 831 832 <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. 10 defined( 'ABSPATH' ) || exit; 11 12 /** 13 * API for responding and returning a custom oEmbed request. 14 * 15 * @since 2.6.0 16 */ 17 abstract 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 13 p.bp-embed-activity-action { 14 font-size: 15px; 15 margin-bottom: 0; 16 } 17 18 p.bp-embed-activity-action a:first-child { 19 color: #32373c; 20 font-weight: bold; 21 } 22 23 p.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 54 span.bp-embed-timestamp { 55 font-size: .9em; 56 } 57 58 video { 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 102 a.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 123 a.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(); ?> · </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>