Ticket #6772: 6772.08.patch
File 6772.08.patch, 58.9 KB (added by , 9 years ago) |
---|
-
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_at_name_filter' ); 30 add_filter( 'bp_activity_get_embed_excerpt', 'convert_smilies', 20 ); 31 add_filter( 'bp_activity_get_embed_excerpt', 'wpautop', 30 ); 32 } 33 add_action( 'bp_loaded', 'bp_activity_setup_oembed' ); 34 35 /** 36 * Add inline styles for BP activity embeds. 37 * 38 * This is subject to change or be removed entirely for a different system. 39 * See PHPDoc for BP_Legacy::locate_asset_in_stack(). 40 * 41 * @since 2.6.0 42 * @access private 43 */ 44 function _bp_activity_embed_add_inline_styles() { 45 if ( false === bp_is_single_activity() ) { 46 return; 47 } 48 49 $inline_css = <<<EOD 50 51 #bp-embed-header:after { 52 clear: both; 53 content: ""; 54 display: table; 55 margin-bottom: 1em; 56 } 57 58 .bp-embed-avatar { 59 float: left; 60 margin: 0 .75em 0 0; 61 } 62 63 p.bp-embed-activity-action { 64 font-size: 15px; 65 margin-bottom: 0; 66 } 67 68 p.bp-embed-activity-action a:first-child { 69 color: #32373c; 70 font-weight: bold; 71 } 72 73 p.bp-embed-activity-action img.avatar { 74 padding: 0 4px 0 3px; 75 vertical-align: text-bottom; 76 } 77 78 .bp-embed-excerpt { 79 margin-bottom: 1em; 80 } 81 82 .bp-embed-excerpt a { 83 color: #21759b; 84 display: inline-block; 85 overflow: hidden; 86 text-overflow: ellipsis; 87 vertical-align: top; 88 white-space: nowrap; 89 max-width: 250px; 90 } 91 92 .bp-activity-embed-display-media { 93 height: auto; 94 max-width: 550px; 95 } 96 97 .bp-activity-embed-display-media img { 98 height: auto; 99 max-width: 100%; 100 } 101 102 .activity-read-more { 103 margin-left: .5em; 104 } 105 106 .activity-read-more a { 107 color: #b4b9be; 108 } 109 110 .wp-embed-footer { 111 margin-top: 20px; 112 } 113 114 span.bp-embed-timestamp { 115 font-size: .9em; 116 } 117 118 video { 119 width: 100%; 120 height: auto; 121 } 122 123 EOD; 124 125 echo "<style type='text/css'>{$inline_css}</style>"; 126 } 127 add_action( 'embed_head', '_bp_activity_embed_add_inline_styles', 20 ); 128 129 /** 130 * Outputs excerpt for an activity embed item. 131 * 132 * @since 2.6.0 133 */ 134 function bp_activity_embed_excerpt( $content = '' ) { 135 echo bp_activity_get_embed_excerpt( $content = '' ); 136 } 137 138 /** 139 * Generates excerpt for an activity embed item. 140 * 141 * @since 2.6.0 142 * 143 * @param string $content The content to generate an excerpt for. 144 * @return string 145 */ 146 function bp_activity_get_embed_excerpt( $content = '' ) { 147 if ( empty( $content ) && ! empty( $GLOBALS['activities_template']->in_the_loop ) ) { 148 $content = $GLOBALS['activities_template']->activity->content; 149 } 150 151 /** 152 * bp_activity_truncate_entry() includes the 'Read More' link, which is why 153 * we're using this instead of bp_create_excerpt(). 154 */ 155 $content = html_entity_decode( $content ); 156 $content = bp_activity_truncate_entry( $content, array( 157 'html' => false, 158 'filter_shortcodes' => true, 159 'strip_tags' => true, 160 'force_truncate' => true 161 ) ); 162 163 /** 164 * Filter the activity embed excerpt. 165 * 166 * @since 2.6.0 167 * 168 * @var string $content Embed Excerpt. 169 * @var string $unmodified_content Unmodified activity content. 170 */ 171 return apply_filters( 'bp_activity_get_embed_excerpt', $content, $GLOBALS['activities_template']->activity->content ); 172 } 173 174 /** 175 * Outputs the first embedded item in the activity oEmbed template. 176 * 177 * @since 2.6.0 178 */ 179 function bp_activity_embed_media() { 180 // Bail if oEmbed request explicitly hides media. 181 if ( isset( $_REQUEST['hide_media'] ) && true == wp_validate_boolean( $_REQUEST['hide_media'] ) ) { 182 /** 183 * Do something after media is rendered for an activity oEmbed item. 184 * 185 * @since 2.6.0 186 */ 187 do_action( 'bp_activity_embed_after_media' ); 188 189 return; 190 } 191 192 $do_fluidvids = false; 193 194 /** 195 * Should we display media in the oEmbed template? 196 * 197 * @since 2.6.0 198 * 199 * @param bool $retval Defaults to true. 200 */ 201 $allow_media = apply_filters( 'bp_activity_embed_display_media', true ); 202 203 // Find oEmbeds from WP registered providers. 204 $media = bp_core_extract_media_from_content( $GLOBALS['activities_template']->activity->content, 'embeds' ); 205 206 // oEmbeds have precedence over inline video, audio and images. 207 if ( isset( $media['embeds'] ) && true === $allow_media ) { 208 // Call BP_Embed if it hasn't already loaded. 209 bp_embed_init(); 210 211 // Autoembed first URL. 212 $content = buddypress()->embed->autoembed( $media['embeds'][0]['url'] ); 213 214 // Wrap content around a custom DIV. 215 echo '<div class="bp-activity-embed-display-media">' . $content . '</div>'; 216 217 // Add fluidvids.js for responsive IFRAMEs / OBJECTs. 218 if ( false !== strpos( $content, '<iframe' ) || false !== strpos( $content, '<object' ) ) { 219 $do_fluidvids = true; 220 } 221 222 // Videos, audio and images. 223 } elseif ( true === $allow_media ) { 224 // Call BP_Embed if it hasn't already loaded. 225 bp_embed_init(); 226 227 // Run shortcode and embed routine. 228 $content = buddypress()->embed->run_shortcode( $GLOBALS['activities_template']->activity->content ); 229 $content = buddypress()->embed->autoembed( $content ); 230 231 // Try to find inline video, audio and images. 232 $media = bp_core_extract_media_from_content( $content, 100 ); 233 234 // Video takes precedence. HTML5-only. 235 if ( isset( $media['videos'] ) && 'shortcodes' === $media['videos'][0]['source'] ) { 236 echo "<video controls> 237 <source src='{$media['videos'][0]['url']}'> 238 <p>" . __( 'Your browser does not support HTML5 video', 'buddypress' ) . "</p> 239 </video>"; 240 241 // No video? Try audio. HTML5-only. 242 } elseif ( isset( $media['audio'] ) && 'shortcodes' === $media['audio'][0]['source'] ) { 243 echo "<audio controls> 244 <source src='{$media['audio'][0]['url']}'> 245 <p>" . __( 'Your browser does not support HTML5 audio', 'buddypress' ) . "</p> 246 </audio>"; 247 248 // No video or audio? Try image. 249 } elseif ( isset( $media['images'] ) && 'html' === $media['images'][0]['source'] ) { 250 echo '<div class="bp-activity-embed-display-media"><img src="' . $media['images'][0]['url'] . '" /></div>'; 251 } 252 253 } 254 255 // Hook fluidvids script to embed footer. 256 if ( true === $do_fluidvids ) { 257 add_action( 'embed_footer', '_bp_activity_embed_print_fluidvids_js' ); 258 } 259 260 /** This hook is documented in /bp-activity/bp-activity-embeds.php */ 261 do_action( 'bp_activity_embed_after_media' ); 262 } 263 264 /** 265 * Print fluidvids.js inline. 266 * 267 * Subject to be removed at any time. 268 * 269 * @since 2.6.0 270 * @access private 271 */ 272 function _bp_activity_embed_print_fluidvids_js() { 273 ?> 274 275 <script> 276 /*! fluidvids.js v2.4.1 | (c) 2014 @toddmotto | License: MIT | https://github.com/toddmotto/fluidvids */ 277 !function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t:e.fluidvids=t()}(this,function(){"use strict";function e(e){return new RegExp("^(https?:)?//(?:"+d.players.join("|")+").*$","i").test(e)}function t(e,t){return parseInt(e,10)/parseInt(t,10)*100+"%"}function i(i){if((e(i.src)||e(i.data))&&!i.getAttribute("data-fluidvids")){var n=document.createElement("div");i.parentNode.insertBefore(n,i),i.className+=(i.className?" ":"")+"fluidvids-item",i.setAttribute("data-fluidvids","loaded"),n.className+="fluidvids",n.style.paddingTop=t(i.height,i.width),n.appendChild(i)}}function n(){var e=document.createElement("div");e.innerHTML="<p>x</p><style>"+o+"</style>",r.appendChild(e.childNodes[1])}var d={selector:["iframe","object"],players:["www.youtube.com","player.vimeo.com"]},o=[".fluidvids {","width: 100%; max-width: 100%; position: relative;","}",".fluidvids-item {","position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;","}"].join(""),r=document.head||document.getElementsByTagName("head")[0];return d.render=function(){for(var e=document.querySelectorAll(d.selector.join()),t=e.length;t--;)i(e[t])},d.init=function(e){for(var t in e)d[t]=e[t];d.render(),n()},d}); 278 279 // init 280 fluidvids.init({ 281 selector: ['iframe', 'object'], 282 players: ['.'] // remove default youtube / vimeo restriction. 283 }); 284 </script> 285 286 <?php 287 } -
src/bp-activity/bp-activity-filters.php
395 395 * This method can only be used inside the Activity loop. 396 396 * 397 397 * @since 1.5.0 398 * 399 * @uses bp_is_single_activity() 400 * @uses apply_filters() To call the 'bp_activity_excerpt_append_text' hook. 401 * @uses apply_filters() To call the 'bp_activity_excerpt_length' hook. 402 * @uses bp_create_excerpt() 403 * @uses bp_get_activity_id() 404 * @uses bp_get_activity_thread_permalink() 405 * @uses apply_filters() To call the 'bp_activity_truncate_entry' hook. 398 * @since 2.6.0 Added $args parameter. 406 399 * 407 400 * @param string $text The original activity entry text. 408 401 * @return string $excerpt The truncated text. 409 402 */ 410 function bp_activity_truncate_entry( $text ) {403 function bp_activity_truncate_entry( $text, $args = array() ) { 411 404 global $activities_template; 412 405 413 406 /** … … 423 416 ); 424 417 425 418 // The full text of the activity update should always show on the single activity screen. 426 if ( ! $maybe_truncate_text || bp_is_single_activity() ) {419 if ( empty( $args['force_truncate'] ) && ( ! $maybe_truncate_text || bp_is_single_activity() ) ) { 427 420 return $text; 428 421 } 429 422 … … 445 438 */ 446 439 $excerpt_length = apply_filters( 'bp_activity_excerpt_length', 358 ); 447 440 441 $args = wp_parse_args( $args, array( 'ending' => __( '…', 'buddypress' ) ) ); 442 448 443 // Run the text through the excerpt function. If it's too short, the original text will be returned. 449 $excerpt = bp_create_excerpt( $text, $excerpt_length, array( 'ending' => __( '…', 'buddypress' ) ));444 $excerpt = bp_create_excerpt( $text, $excerpt_length, $args ); 450 445 451 446 /* 452 447 * If the text returned by bp_create_excerpt() is different from the original text (ie it's 453 448 * been truncated), add the "Read More" link. Note that bp_create_excerpt() is stripping 454 449 * shortcodes, so we have strip them from the $text before the comparison. 455 450 */ 456 if ( $excerpt != strip_shortcodes( $text) ) {451 if ( strlen( $excerpt ) > strlen( strip_shortcodes( $text ) ) ) { 457 452 $id = !empty( $activities_template->activity->current_comment->id ) ? 'acomment-read-more-' . $activities_template->activity->current_comment->id : 'activity-read-more-' . bp_get_activity_id(); 458 453 459 454 $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
3338 3338 } 3339 3339 3340 3340 // Generate a text excerpt for this activity item (and remove any oEmbeds URLs). 3341 $summary = strip_shortcodes( html_entity_decode( strip_tags( $content ) ) ); 3342 $summary = bp_create_excerpt( preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $summary ) ); 3341 $summary = bp_create_excerpt( html_entity_decode( $content ), 225, array( 3342 'html' => false, 3343 'filter_shortcodes' => true, 3344 'strip_tags' => true, 3345 'remove_links' => true 3346 ) ); 3343 3347 3344 3348 if ( $use_media_type === 'embeds' ) { 3345 3349 $summary .= PHP_EOL . PHP_EOL . $extracted_media['url']; -
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 'user_id' => $activity->user_id, 140 'content' => $activity->content, 141 'title' => __( 'Activity', 'buddypress' ), 142 'author_url' => bp_core_get_user_domain( $activity->user_id ) 143 ); 144 } 145 146 /** 147 * Sets a custom <blockquote> for our oEmbed fallback HTML. 148 * 149 * @since 2.6.0 150 * 151 * @param int $item_id The activity ID. 152 * @return string 153 */ 154 protected function set_fallback_html( $item_id ) { 155 $activity = new BP_Activity_Activity( $item_id ); 156 $mentionname = bp_activity_do_mentions() ? ' (@' . bp_activity_get_user_mentionname( $activity->user_id ) . ')' : ''; 157 $date = date_i18n( get_option( 'date_format' ), strtotime( $activity->date_recorded ) ); 158 159 // Make sure we can use some activity functions that depend on the loop. 160 $GLOBALS['activities_template'] = new stdClass; 161 $GLOBALS['activities_template']->activity = $activity; 162 163 // 'wp-embedded-content' CSS class is necessary due to how the embed JS works. 164 $blockquote = sprintf( '<blockquote class="wp-embedded-content bp-activity-item">%1$s%2$s %3$s</blockquote>', 165 bp_activity_get_embed_excerpt( $activity->content ), 166 '- ' . bp_core_get_user_displayname( $activity->user_id ) . $mentionname, 167 '<a href="' . esc_url( bp_activity_get_permalink( $item_id ) ) . '">' . $date . '</a>' 168 ); 169 170 // Clean up. 171 unset( $GLOBALS['activities_template'] ); 172 173 /** 174 * Filters the fallback HTML used when embedding a BP activity item. 175 * 176 * @since 2.6.0 177 * 178 * @param string $blockquote Current fallback HTML 179 * @param BP_Activity_Activity $activity Activity object 180 */ 181 return apply_filters( 'bp_activity_embed_fallback_html', $blockquote, $activity ); 182 } 183 184 /** 185 * Sets a custom <iframe> title for our oEmbed item. 186 * 187 * @since 2.6.0 188 * 189 * @param int $item_id The activity ID 190 * @return string 191 */ 192 protected function set_iframe_title( $item_id ) { 193 return __( 'Embedded Activity Item', 'buddypress' ); 194 } 195 196 /** 197 * Use our custom <iframe> sandbox attribute in our oEmbed response. 198 * 199 * WordPress sets the <iframe> sandbox attribute to 'allow-scripts' regardless 200 * of whatever the oEmbed response is in {@link wp_filter_oembed_result()}. We 201 * need to add back our custom sandbox value so rich media like YouTube works. 202 * 203 * @since 2.6.0 204 * 205 * @see BP_Activity_Component::modify_iframe() where our custom sandbox value is set. 206 * 207 * @param string $result The oEmbed HTML result. 208 * @param object $data A data object result from an oEmbed provider. 209 * @param string $url The URL of the content to be embedded. 210 * @return string 211 */ 212 public function use_custom_iframe_sandbox_attribute( $result, $data, $url ) { 213 // Make sure we are on our activity embed URL. If not, bail. 214 if ( false === $this->validate_url_to_item_id( $url ) ) { 215 return $result; 216 } 217 218 // Get unfiltered sandbox attribute from our own oEmbed response. 219 $sandbox_pos = strpos( $data->html, 'sandbox=' ) + 9; 220 $sandbox = substr( $data->html, $sandbox_pos, strpos( $data->html, '"', $sandbox_pos ) - $sandbox_pos ); 221 222 // Replace only if our sandbox attribute contains 'allow-same-origin'. 223 if ( false !== strpos( $sandbox, 'allow-same-origin' ) ) { 224 $result = str_replace( ' sandbox="allow-scripts"', " sandbox=\"{$sandbox}\"", $result ); 225 226 // This is for IE10-11. 227 $result = str_replace( 'security="restricted"', "", $result ); 228 229 // Add <iframe> fullscreen attribute back as well. 230 $result = str_replace( '<iframe', '<iframe allowfullscreen="true"', $result ); 231 } 232 233 return $result; 234 } 235 236 /** 237 * Modify various IFRAME-related items if embeds are allowed. 238 * 239 * IFRAME attributes added: 240 * - sandbox="allow-same-origin" - This is required for certain oEmbed items to work. 241 * - allowfullscreen="true" - Allows users to view embed content fullscreen. 242 * 243 * IFRAME attributes removed: 244 * - security="restricted" - Removed only if the oEmbed item requires it. For IE10-11 only. 245 * 246 * JS modified: 247 * - remove IFRAME height restriction of 1000px. Fixes long embed items being 248 * truncated. 249 * - delay IFRAME onload script by 550ms. Fixes resize issues with Twitter and 250 * Tumblr embeds. 251 * 252 * @since 2.6.0 253 * 254 * @param string $retval Current embed HTML. 255 * @return string 256 */ 257 public function modify_iframe( $retval ) { 258 // Bail if oEmbed request explicitly hides media. 259 if ( isset( $_REQUEST['hide_media'] ) && true == wp_validate_boolean( $_REQUEST['hide_media'] ) ) { 260 return $retval; 261 } 262 263 // Check filter-configured value. 264 if ( false == apply_filters( 'bp_activity_embed_display_media', true ) ) { 265 return $retval; 266 } 267 268 // Add 'allow-same-origin' sandbox attribute to IFRAME. 269 // This is only applicable to a select list of WP registered oEmbed providers. 270 if ( false === strpos( $retval, 'allow-same-origin' ) ) { 271 // Grab activity meta for this item. 272 $meta = bp_activity_get_meta( $this->get_item_id() ); 273 274 $allow_same_origin = array( 275 'youtube.com', 276 'vimeo.com', 277 'twitter.com', 278 'soundcloud.com', 279 'kickstarter.com', 280 'ted.com', 281 'slideshare.net', 282 'dailymotion.com' 283 ); 284 285 $checked = false; 286 287 // See if our oEmbed item is from a provider that requires the 288 // 'allow-same-origin' attribute. 289 foreach( $meta as $key => $val ) { 290 if ( $checked ) { 291 break; 292 } 293 294 // Not an oEmbed meta item. 295 if ( false === strpos( $key, '_oembed' ) ) { 296 continue; 297 } 298 299 $checked = true; 300 $allow_origin = false; 301 302 // See if the first embed needs our 'allow-same-origin' attribute. 303 foreach( $allow_same_origin as $check ) { 304 if ( false !== strpos( $val[0], $check ) ) { 305 $allow_origin = true; 306 break; 307 } 308 } 309 310 if ( false === $allow_origin ) { 311 continue; 312 } 313 314 // Add the 'allow-same-origin' sandbox attribute. 315 $retval = str_replace( 'sandbox="', 'sandbox="allow-same-origin ', $retval ); 316 317 // IE requires removing the 'security' attribute. 318 $retval = str_replace( 'security="restricted"', '', $retval ); 319 } 320 } 321 322 // Add 'allowfullscreen' attribute to IFRAME. 323 if ( false === strpos( $retval, 'allowfullscreen=' ) ) { 324 $retval = str_replace( '<iframe', '<iframe allowfullscreen="true"', $retval ); 325 } 326 327 // See /wp-includes/js/wp-embed.js. 328 if ( SCRIPT_DEBUG ) { 329 // Removes WP's hardcoded IFRAME height restriction. 330 $retval = str_replace( 'height = 1000;', 'height = height;', $retval ); 331 332 // Delay IFRAME postMessage script so Tumblr and Twitter embeds are not truncated. 333 $retval = str_replace( 334 "window.addEventListener( 'message', window.wp.receiveEmbedMessage, false );", 335 "window.addEventListener( 'message', function(e) { 336 if ( e.source.frameElement.contentDocument.querySelector('iframe.tumblr-embed') || e.source.frameElement.contentDocument.querySelector('twitterwidget') ) { 337 setTimeout( function() { 338 window.wp.receiveEmbedMessage(e); 339 }, 550 ); 340 } else { 341 window.wp.receiveEmbedMessage(e); 342 } 343 }, false );", 344 $retval 345 ); 346 347 // This is for the WP build minified version. 348 } else { 349 $retval = str_replace( 'g=1e3', 'g=g', $retval ); 350 $retval = str_replace( 351 'a.addEventListener("message",a.wp.receiveEmbedMessage,!1)', 352 'a.addEventListener("message",function(e){if(e.source.frameElement.contentDocument.querySelector("iframe.tumblr-embed")||e.source.frameElement.contentDocument.querySelector("twitterwidget")){setTimeout(function(){a.wp.receiveEmbedMessage(e);},550);}else{a.wp.receiveEmbedMessage(e);}},!1)', 353 $retval 354 ); 355 } 356 357 return $retval; 358 } 359 360 /** 361 * Do stuff when our oEmbed activity header template part is loading. 362 * 363 * Currently, removes wpautop() from the bp_activity_action() function. 364 * 365 * @since 2.6.0 366 * 367 * @param string $slug Template part slug requested. 368 * @param string $name Template part name requested. 369 */ 370 public function on_activity_header( $slug, $name ) { 371 if ( false === $this->is_page() || 'activity' !== $name ) { 372 return; 373 } 374 375 remove_filter( 'bp_get_activity_action', 'wpautop' ); 376 } 377 378 /** 379 * Prints the markup for the activity embed comments button. 380 * 381 * Basically a copy of {@link print_embed_comments_button()}, but modified for 382 * the BP activity component. 383 * 384 * @since 2.6.0 385 */ 386 public function embed_comments_button() { 387 if ( ! bp_is_single_activity() ) { 388 return; 389 } 390 391 // Make sure our custom permalink shows up in the 'WordPress Embed' block. 392 add_filter( 'the_permalink', array( $this, 'filter_embed_url' ) ); 393 394 // Only show comment bubble if we have some activity comments. 395 $count = bp_activity_get_comment_count(); 396 if ( empty( $count ) ) { 397 return; 398 } 399 ?> 400 401 <div class="wp-embed-comments"> 402 <a href="<?php bp_activity_thread_permalink(); ?>"> 403 <span class="dashicons dashicons-admin-comments"></span> 404 <?php 405 printf( 406 _n( 407 '%s <span class="screen-reader-text">Comment</span>', 408 '%s <span class="screen-reader-text">Comments</span>', 409 $count, 410 'buddypress' 411 ), 412 number_format_i18n( $count ) 413 ); 414 ?> 415 </a> 416 </div> 417 418 <?php 419 } 420 } -
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
477 477 } 478 478 479 479 /** 480 * Fires the 'bp_enqueue_embed_scripts' action in the <head> for BP oEmbeds. 481 * 482 * @since 2.6.0 483 */ 484 function bp_enqueue_embed_scripts() { 485 if ( ! is_buddypress() ) { 486 return; 487 } 488 489 /** 490 * Enqueue CSS and JS files for BuddyPress embeds. 491 * 492 * @since 2.6.0 493 */ 494 do_action ( 'bp_enqueue_embed_scripts' ); 495 } 496 497 /** 480 498 * Fire the 'bp_add_rewrite_tag' action, where BP adds its custom rewrite tags. 481 499 * 482 500 * @since 1.8.0 -
src/bp-core/bp-core-functions.php
1666 1666 return apply_filters( 'bp_use_embed_in_private_messages', !defined( 'BP_EMBED_DISABLE_PRIVATE_MESSAGES' ) || !BP_EMBED_DISABLE_PRIVATE_MESSAGES ); 1667 1667 } 1668 1668 1669 /** 1670 * Extracts media metadata from a given content. 1671 * 1672 * @since 2.6.0 1673 * 1674 * @param string $content The content to check. 1675 * @param string|int $type The type to check. Can also use a bitmask. See the class constants in the 1676 * BP_Media_Extractor class for more info. 1677 * @return array|bool If media exists, will return array of media metadata. Else, boolean false. 1678 */ 1679 function bp_core_extract_media_from_content( $content = '', $type = 'all' ) { 1680 if ( is_string( $type ) ) { 1681 $class = new ReflectionClass( 'BP_Media_Extractor' ); 1682 $bitmask = $class->getConstant( strtoupper( $type ) ); 1683 } else { 1684 $bitmask = (int) $type; 1685 } 1686 1687 // Type isn't valid, so bail. 1688 if ( empty( $bitmask ) ) { 1689 return false; 1690 } 1691 1692 $x = new BP_Media_Extractor; 1693 $media = $x->extract( $content, $bitmask ); 1694 1695 unset( $media['has'] ); 1696 $retval = array_filter( $media ); 1697 1698 return ! empty( $retval ) ? $retval : false; 1699 } 1700 1669 1701 /** Admin *********************************************************************/ 1670 1702 1671 1703 /** -
src/bp-core/bp-core-template-loader.php
66 66 } 67 67 68 68 /** 69 * Get an asset template part. 70 * 71 * Basically the same as {@link bp_get_template_part()}, but with 'assets/' 72 * prepended to the slug. 73 * 74 * @since 2.6.0 75 * 76 * @see bp_get_template_part() for full documentation. 77 */ 78 function bp_get_asset_template_part( $slug, $name = null ) { 79 return bp_get_template_part( "assets/{$slug}", $name ); 80 } 81 82 /** 69 83 * Retrieve the name of the highest priority template file that exists. 70 84 * 71 85 * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which -
src/bp-core/bp-core-template.php
784 784 * This function is borrowed from CakePHP v2.0, under the MIT license. See 785 785 * http://book.cakephp.org/view/1469/Text#truncate-1625 786 786 * 787 * @since 2.6.0 Added 'strip_tags' and 'remove_links' as $options args. 788 * 787 789 * ### Options: 788 790 * 789 791 * - `ending` Will be used as Ending and appended to the trimmed string. … … 806 808 * excerpt length. Default: true. 807 809 * @type bool $filter_shortcodes If true, shortcodes will be stripped. 808 810 * Default: true. 811 * @type bool $strip_tags If true, HTML tags will be stripped. Default: false. 812 * Only applicable if $html is set to false. 813 * @type bool $remove_links If true, URLs will be stripped. Default: false. 814 * Only applicable if $html is set to false. 809 815 * } 810 816 * @return string Trimmed string. 811 817 */ … … 818 824 'ending' => __( ' […]', 'buddypress' ), 819 825 'exact' => false, 820 826 'html' => true, 821 'filter_shortcodes' => $filter_shortcodes_default 827 'filter_shortcodes' => $filter_shortcodes_default, 828 'strip_tags' => false, 829 'remove_links' => false, 822 830 ), 'create_excerpt' ); 823 831 824 832 // Save the original text, to be passed along to the filter. … … 904 912 } 905 913 } 906 914 } else { 915 // Strip HTML tags if necessary. 916 if ( ! empty( $r['strip_tags'] ) ) { 917 $text = strip_tags( $text ); 918 } 919 920 // Remove links if necessary. 921 if ( ! empty( $r['remove_links'] ) ) { 922 $text = preg_replace( '#^\s*(https?://[^\s"]+)\s*$#im', '', $text ); 923 } 924 907 925 if ( mb_strlen( $text ) <= $length ) { 908 return $text; 926 /** 927 * Filters the final generated excerpt. 928 * 929 * @since 1.1.0 930 * 931 * @param string $truncate Generated excerpt. 932 * @param string $original_text Original text provided. 933 * @param int $length Length of returned string, including ellipsis. 934 * @param array $options Array of HTML attributes and options. 935 */ 936 return apply_filters( 'bp_create_excerpt', $text, $original_text, $length, $options ); 909 937 } else { 910 938 $truncate = mb_substr( $text, 0, $length - mb_strlen( $ending ) ); 911 939 } -
src/bp-core/bp-core-theme-compatibility.php
547 547 } 548 548 549 549 /** 550 * Create a dummy WP_Post object. 551 * 552 * @since 2.6.0 553 * 554 * @param array $args Array of optional arguments. Arguments parallel the properties 555 * of {@link WP_Post}; see that class for more details. 556 * @return WP_Post 557 */ 558 function bp_theme_compat_create_dummy_post( $args = array() ) { 559 $args = wp_parse_args( $args, array( 560 'ID' => -9999, 561 'post_status' => 'public', 562 'post_author' => 0, 563 'post_parent' => 0, 564 'post_type' => 'page', 565 'post_date' => 0, 566 'post_date_gmt' => 0, 567 'post_modified' => 0, 568 'post_modified_gmt' => 0, 569 'post_content' => '', 570 'post_title' => '', 571 'post_excerpt' => '', 572 'post_content_filtered' => '', 573 'post_mime_type' => '', 574 'post_password' => '', 575 'post_name' => '', 576 'guid' => '', 577 'menu_order' => 0, 578 'pinged' => '', 579 'to_ping' => '', 580 'ping_status' => '', 581 'comment_status' => 'closed', 582 'comment_count' => 0, 583 'filter' => 'raw', 584 585 'is_404' => false, 586 'is_page' => false, 587 'is_single' => false, 588 'is_archive' => false, 589 'is_tax' => false, 590 ) ); 591 592 // Create the dummy post. 593 $post = new WP_Post( (object) $args ); 594 595 return $post; 596 } 597 598 /** 550 599 * Populate various WordPress globals with dummy data to prevent errors. 551 600 * 552 601 * This dummy data is necessary because theme compatibility essentially fakes … … 567 616 568 617 // Switch defaults if post is set. 569 618 if ( isset( $wp_query->post ) ) { 570 $ dummy= wp_parse_args( $args, array(619 $args = wp_parse_args( $args, array( 571 620 'ID' => $wp_query->post->ID, 572 621 'post_status' => $wp_query->post->post_status, 573 622 'post_author' => $wp_query->post->post_author, … … 592 641 'comment_status' => $wp_query->post->comment_status, 593 642 'comment_count' => $wp_query->post->comment_count, 594 643 'filter' => $wp_query->post->filter, 595 596 'is_404' => false,597 'is_page' => false,598 'is_single' => false,599 'is_archive' => false,600 'is_tax' => false,601 ) );602 } else {603 $dummy = wp_parse_args( $args, array(604 'ID' => -9999,605 'post_status' => 'public',606 'post_author' => 0,607 'post_parent' => 0,608 'post_type' => 'page',609 'post_date' => 0,610 'post_date_gmt' => 0,611 'post_modified' => 0,612 'post_modified_gmt' => 0,613 'post_content' => '',614 'post_title' => '',615 'post_excerpt' => '',616 'post_content_filtered' => '',617 'post_mime_type' => '',618 'post_password' => '',619 'post_name' => '',620 'guid' => '',621 'menu_order' => 0,622 'pinged' => '',623 'to_ping' => '',624 'ping_status' => '',625 'comment_status' => 'closed',626 'comment_count' => 0,627 'filter' => 'raw',628 629 'is_404' => false,630 'is_page' => false,631 'is_single' => false,632 'is_archive' => false,633 'is_tax' => false,634 644 ) ); 635 645 } 636 646 637 647 // Bail if dummy post is empty. 638 if ( empty( $ dummy) ) {648 if ( empty( $args ) ) { 639 649 return; 640 650 } 641 651 642 652 // Set the $post global. 643 $post = new WP_Post( (object) $dummy);653 $post = bp_theme_compat_create_dummy_post( $args ); 644 654 645 655 // Copy the new post global into the main $wp_query. 646 656 $wp_query->post = $post; … … 648 658 649 659 // Prevent comments form from appearing. 650 660 $wp_query->post_count = 1; 651 $wp_query->is_404 = $dummy['is_404']; 652 $wp_query->is_page = $dummy['is_page']; 653 $wp_query->is_single = $dummy['is_single']; 654 $wp_query->is_archive = $dummy['is_archive']; 655 $wp_query->is_tax = $dummy['is_tax']; 656 657 // Clean up the dummy post. 658 unset( $dummy ); 661 $wp_query->is_404 = $post->is_404; 662 $wp_query->is_page = $post->is_page; 663 $wp_query->is_single = $post->is_single; 664 $wp_query->is_archive = $post->is_archive; 665 $wp_query->is_tax = $post->is_tax; 659 666 660 667 /** 661 668 * Force the header back to 200 status if not a deliberate 404 … … 678 685 * 679 686 * @since 1.7.0 680 687 * 681 * @uses bp_is_single_user() To check if page is single user.682 * @uses bp_get_single_user_template() To get user template.683 * @uses bp_is_single_user_edit() To check if page is single user edit.684 * @uses bp_get_single_user_edit_template() To get user edit template.685 * @uses bp_is_single_view() To check if page is single view.686 * @uses bp_get_single_view_template() To get view template.687 * @uses bp_is_forum_edit() To check if page is forum edit.688 * @uses bp_get_forum_edit_template() To get forum edit template.689 * @uses bp_is_topic_merge() To check if page is topic merge.690 * @uses bp_get_topic_merge_template() To get topic merge template.691 * @uses bp_is_topic_split() To check if page is topic split.692 * @uses bp_get_topic_split_template() To get topic split template.693 * @uses bp_is_topic_edit() To check if page is topic edit.694 * @uses bp_get_topic_edit_template() To get topic edit template.695 * @uses bp_is_reply_edit() To check if page is reply edit.696 * @uses bp_get_reply_edit_template() To get reply edit template.697 * @uses bp_set_theme_compat_template() To set the global theme compat template.698 *699 688 * @param string $template Template name. 700 689 * @return string $template Template name. 701 690 */ 702 691 function bp_template_include_theme_compat( $template = '' ) { 692 // If embed template, bail. 693 if ( true === function_exists( 'is_embed' ) && is_embed() ) { 694 return $template; 695 } 703 696 704 697 // If the current theme doesn't need theme compat, bail at this point. 705 698 if ( ! bp_use_theme_compat_with_current_theme() ) { -
src/bp-core/classes/class-bp-admin.php
836 836 <a href="https://bbpress.org">bbPress</a>, 837 837 <a href="https://github.com/ichord/Caret.js">Caret.js</a>, 838 838 <a href="http://tedgoas.github.io/Cerberus/">Cerberus</a>, 839 <a href="https://github.com/toddmotto/fluidvids">fluidvids.js</a>, 839 840 <a href="https://github.com/carhartl/jquery-cookie">jquery.cookie</a>, 840 841 <a href="https://www.mediawiki.org/wiki/MediaWiki">MediaWiki</a>, 841 842 <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 * @var string 27 */ 28 public $slug_endpoint = ''; 29 30 /** 31 * (optional) Embed template hierarchy. 32 * 33 * Define the template hierarchy for your oEmbed rendering. 34 * 35 * @var array 36 */ 37 protected $template_hierarchy = array(); 38 39 /** END PROPERTIES ******************************************************/ 40 41 /** 42 * Constructor. 43 */ 44 final public function __construct() { 45 $this->setup_properties(); 46 47 // Some rudimentary logic checking. 48 if ( empty( $this->slug_endpoint ) ) { 49 $class = get_class( $this ); 50 throw new LogicException( $class . ' class must define $slug_endpoint property' ); 51 } 52 53 $this->setup_hooks(); 54 $this->custom_hooks(); 55 } 56 57 /** REQUIRED METHODS ****************************************************/ 58 59 /** 60 * Add content for your oEmbed response here. 61 * 62 * @since 2.6.0 63 */ 64 abstract protected function content(); 65 66 /** 67 * Add a check for when you are on the page you want to oEmbed. 68 * 69 * You'll want to return a boolean here. eg. bp_is_single_activity(). 70 * 71 * @since 2.6.0 72 * 73 * @return bool 74 */ 75 abstract protected function is_page(); 76 77 /** 78 * Validate the URL to see if it matches your item ID. 79 * 80 * @since 2.6.0 81 * 82 * @return int Your item ID 83 */ 84 abstract protected function validate_url_to_item_id( $url ); 85 86 /** 87 * Set the oEmbed response data. 88 * 89 * @since 2.6.0 90 * 91 * @param int $item_id Your item ID to do checks against. 92 * @return array Should contain 'user_id', 'content', 'title', 'author_url' as array keys. 93 * 'author_url' is optional; the rest are required. 94 */ 95 abstract protected function set_oembed_response_data( $item_id ); 96 97 /** 98 * Sets the fallback HTML for the oEmbed response. 99 * 100 * In a WordPress oEmbed item, the fallback HTML is a <blockquote>. This is 101 * usually hidden after the <iframe> is loaded. 102 * 103 * @since 2.6.0 104 * 105 * @param int $item_id Your item ID to do checks against. 106 * @return string Fallback HTML you want to output. 107 */ 108 abstract protected function set_fallback_html( $item_id ); 109 110 /** OPTIONAL METHODS ****************************************************/ 111 112 /** 113 * If your oEmbed endpoint requires additional arguments, set them here. 114 * 115 * @see register_rest_route() View the $args parameter for more info. 116 * 117 * @since 2.6.0 118 * 119 * @return array 120 */ 121 protected function set_route_args() { 122 return array(); 123 } 124 125 /** 126 * Set the iframe title. 127 * 128 * If not set, this will fallback to WP's 'Embedded WordPress Post'. 129 * 130 * @param int $item_id The item ID to do checks for. 131 * @return string 132 */ 133 protected function set_iframe_title( $item_id ) {} 134 135 /** 136 * Do what you need to do here to initialize any custom hooks. 137 * 138 * @since 2.6.0 139 */ 140 protected function custom_hooks() {} 141 142 /** 143 * Set permalink for oEmbed link discovery. 144 * 145 * This method will be called on the page we want to oEmbed. In most cases, 146 * you will not need to override this method. However, if you need to, do 147 * override in your extended class. 148 * 149 * @since 2.6.0 150 */ 151 protected function set_permalink() { 152 $url = bp_get_requested_url(); 153 154 // Remove querystring from bp_get_requested_url() 155 if ( false !== strpos( bp_get_requested_url(), '?' ) ) { 156 $url = substr( bp_get_requested_url(), 0, strpos( bp_get_requested_url(), '?' ) ); 157 } 158 159 return $url; 160 } 161 162 /** HELPERS *************************************************************/ 163 164 /** 165 * Get the item ID when filtering the oEmbed HTML. 166 * 167 * Should only be used during the 'embed_html' hook. 168 * 169 * @since 2.6.0 170 */ 171 protected function get_item_id() { 172 return $this->is_page() ? $this->validate_url_to_item_id( $this->set_permalink() ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress; 173 } 174 175 /** SET UP **************************************************************/ 176 177 /** 178 * Set up properties. 179 * 180 * @since 2.6.0 181 */ 182 protected function setup_properties() { 183 $this->slug_endpoint = sanitize_title( $this->slug_endpoint ); 184 } 185 186 /** 187 * Hooks! We do the dirty work here, so you don't have to! :) 188 * 189 * More hooks are available in the setup_template_parts() method. 190 * 191 * @since 2.6.0 192 */ 193 protected function setup_hooks() { 194 add_action( 'rest_api_init', array( $this, 'register_route' ) ); 195 add_action( 'bp_embed_content', array( $this, 'inject_content' ) ); 196 197 add_filter( 'embed_template', array( $this, 'setup_template_parts' ) ); 198 add_filter( 'post_embed_url', array( $this, 'filter_embed_url' ) ); 199 add_filter( 'embed_html', array( $this, 'filter_embed_html' ) ); 200 add_filter( 'oembed_discovery_links', array( $this, 'add_oembed_discovery_links' ) ); 201 add_filter( 'rest_pre_serve_request', array( $this, 'oembed_xml_request' ), 20, 4 ); 202 } 203 204 /** HOOKS ***************************************************************/ 205 206 /** 207 * Register the oEmbed REST API route. 208 * 209 * @since 2.6.0 210 */ 211 public function register_route() { 212 /** This filter is documented in wp-includes/class-wp-oembed-controller.php */ 213 $maxwidth = apply_filters( 'oembed_default_width', 600 ); 214 215 // Required arguments. 216 $args = array( 217 'url' => array( 218 'required' => true, 219 'sanitize_callback' => 'esc_url_raw', 220 ), 221 'format' => array( 222 'default' => 'json', 223 'sanitize_callback' => 'wp_oembed_ensure_format', 224 ), 225 'maxwidth' => array( 226 'default' => $maxwidth, 227 'sanitize_callback' => 'absint', 228 ) 229 ); 230 231 // Merge custom arguments here. 232 $args = $args + (array) $this->set_route_args(); 233 234 register_rest_route( 'oembed/1.0', "/embed/{$this->slug_endpoint}", array( 235 array( 236 'methods' => WP_REST_Server::READABLE, 237 'callback' => array( $this, 'get_item' ), 238 'args' => $args 239 ), 240 ) ); 241 } 242 243 /** 244 * Set up custom embed template parts for BuddyPress use. 245 * 246 * @since 2.6.0 247 * 248 * @param string $template File path to current embed template. 249 * @return string 250 */ 251 public function setup_template_parts( $template ) { 252 // Determine if we're on our BP page. 253 if ( ! $this->is_page() || is_404() ) { 254 return $template; 255 } 256 257 // Set up some BP-specific embed template overrides. 258 add_action( 'embed_head', array( $this, 'add_base_element' ), 1 ); 259 add_action( 'get_template_part_embed', array( $this, 'content_buffer_start' ), -999, 2 ); 260 add_action( 'get_footer', array( $this, 'content_buffer_end' ), -999 ); 261 262 // Return the original WP embed template. 263 return $template; 264 } 265 266 /** 267 * Add <base> element to embed <head>. 268 * 269 * @since 2.6.0 270 */ 271 public function add_base_element() { 272 echo '<base target="_top" />'; 273 } 274 275 /** 276 * Start object buffer. 277 * 278 * We're going to override WP's get_template_part( 'embed, 'content' ) call 279 * and inject our own template for BuddyPress use. 280 * 281 * @since 2.6.0 282 */ 283 public function content_buffer_start( $slug, $name ) { 284 if ( 'embed' !== $slug || 'content' !== $name ) { 285 return; 286 } 287 288 // Start the buffer to wipe out get_template_part( 'embed, 'content' ). 289 ob_start(); 290 } 291 292 /** 293 * End object buffer. 294 * 295 * We're going to override WP's get_template_part( 'embed, 'content' ) call 296 * and inject our own template for BuddyPress use. 297 * 298 * @since 2.6.0 299 */ 300 public function content_buffer_end( $name ) { 301 if ( 'embed' !== $name || is_404() ) { 302 return; 303 } 304 305 // Wipe out get_template_part( 'embed, 'content' ). 306 ob_end_clean(); 307 308 // Start our custom BuddyPress embed template! 309 echo '<div '; 310 post_class( 'wp-embed' ); 311 echo '>'; 312 313 // Template part for our embed header. 314 bp_get_asset_template_part( 'embeds/header', bp_current_component() ); 315 316 /** 317 * Inject BuddyPress embed content on this hook. 318 * 319 * You shouldn't really need to use this if you extend the 320 * {@link BP_oEmbed_Component} class. 321 * 322 * @since 2.6.0 323 */ 324 do_action( 'bp_embed_content' ); 325 326 // Template part for our embed footer. 327 bp_get_asset_template_part( 'embeds/footer', bp_current_component() ); 328 329 echo '</div>'; 330 } 331 332 /** 333 * Adds oEmbed discovery links on single activity pages. 334 * 335 * @since 2.6.0 336 * 337 * @param string $retval Current discovery links. 338 * @return string 339 */ 340 public function add_oembed_discovery_links( $retval ) { 341 if ( ! $this->is_page() ) { 342 return $retval; 343 } 344 345 $permalink = $this->set_permalink(); 346 if ( empty( $permalink ) ) { 347 return $retval; 348 } 349 350 add_filter( 'rest_url' , array( $this, 'filter_rest_url' ) ); 351 352 $retval = '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink ) ) . '" />' . "\n"; 353 354 if ( class_exists( 'SimpleXMLElement' ) ) { 355 $retval .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( $permalink, 'xml' ) ) . '" />' . "\n"; 356 } 357 358 remove_filter( 'rest_url' , array( $this, 'filter_rest_url' ) ); 359 360 return $retval; 361 } 362 363 /** 364 * Callback for the API endpoint. 365 * 366 * Returns the JSON object for the item. 367 * 368 * @since 2.6.0 369 * 370 * @param WP_REST_Request $request Full data about the request. 371 * @return WP_Error|array oEmbed response data or WP_Error on failure. 372 */ 373 public function get_item( $request ) { 374 $url = $request['url']; 375 376 $data = false; 377 378 $item_id = (int) $this->validate_url_to_item_id( $url ); 379 380 if ( ! empty( $item_id ) ) { 381 $item = $this->set_oembed_response_data( $item_id ); 382 383 // Create dummy post to piggyback off of get_oembed_response_data() 384 $post = bp_theme_compat_create_dummy_post( array( 385 'post_author' => $item['user_id'], 386 'post_title' => $item['title'], 387 'post_content' => $item['content'], 388 389 // This passes the get_oembed_response_data() check. 390 'post_status' => 'publish' 391 ) ); 392 393 // Add markers to tell that we're embedding a single activity. 394 // This is needed for various oEmbed response data filtering. 395 if ( empty( buddypress()->{$this->slug_endpoint} ) ) { 396 buddypress()->{$this->slug_endpoint} = new stdClass; 397 } 398 buddypress()->{$this->slug_endpoint}->embedurl_in_progress = $url; 399 buddypress()->{$this->slug_endpoint}->embedid_in_progress = $item_id; 400 401 // Save custom route args as well. 402 $custom_args = array_keys( (array) $this->set_route_args() ); 403 if ( ! empty( $custom_args ) ) { 404 buddypress()->{$this->slug_endpoint}->embedargs_in_progress = array(); 405 406 foreach( $custom_args as $arg ) { 407 if ( isset( $request[ $arg ] ) ) { 408 buddypress()->{$this->slug_endpoint}->embedargs_in_progress[ $arg ] = $request[ $arg ]; 409 } 410 } 411 } 412 413 // Use WP's oEmbed response data function. 414 $data = get_oembed_response_data( $post, $request['maxwidth'] ); 415 416 // Set custom 'author_url' if we have one. 417 if ( ! empty( $item['author_url'] ) ) { 418 $data['author_url'] = $item['author_url']; 419 } 420 } 421 422 if ( ! $data ) { 423 return new WP_Error( 'oembed_invalid_url', get_status_header_desc( 404 ), array( 'status' => 404 ) ); 424 } 425 426 return $data; 427 } 428 429 /** 430 * If oEmbed request wants XML, return XML instead of JSON. 431 * 432 * Basically a copy of {@link _oembed_rest_pre_serve_request()}. Unfortunate 433 * that we have to duplicate this just for a URL check. 434 * 435 * @param bool $served Whether the request has already been served. 436 * @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response. 437 * @param WP_REST_Request $request Request used to generate the response. 438 * @param WP_REST_Server $server Server instance. 439 * @return bool 440 */ 441 public function oembed_xml_request( $served, $result, $request, $server ) { 442 $params = $request->get_params(); 443 444 if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) { 445 return $served; 446 } 447 448 // Validate URL against our oEmbed endpoint. If not valid, bail. 449 // This is our mod to _oembed_rest_pre_serve_request(). 450 $query_params = $request->get_query_params(); 451 if ( false === $this->validate_url_to_item_id( $query_params['url'] ) ) { 452 return $served; 453 } 454 455 // Embed links inside the request. 456 $data = $server->response_to_data( $result, false ); 457 458 if ( ! class_exists( 'SimpleXMLElement' ) ) { 459 status_header( 501 ); 460 die( get_status_header_desc( 501 ) ); 461 } 462 463 $result = _oembed_create_xml( $data ); 464 465 // Bail if there's no XML. 466 if ( ! $result ) { 467 status_header( 501 ); 468 return get_status_header_desc( 501 ); 469 } 470 471 if ( ! headers_sent() ) { 472 $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) ); 473 } 474 475 echo $result; 476 477 return true; 478 } 479 480 /** 481 * Pass our BuddyPress activity permalink for embedding. 482 * 483 * @since 2.6.0 484 * 485 * @see bp_activity_embed_rest_route_callback() 486 * 487 * @param string $retval Current embed URL 488 * @return string 489 */ 490 public function filter_embed_url( $retval ) { 491 if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) { 492 return $retval; 493 } 494 495 $url = $this->is_page() ? $this->set_permalink() : buddypress()->{$this->slug_endpoint}->embedurl_in_progress; 496 $url = trailingslashit( $url ); 497 498 // This is for the 'WordPress Embed' block 499 // @see bp_activity_embed_comments_button() 500 if ( 'the_permalink' !== current_filter() ) { 501 $url = add_query_arg( 'embed', 'true', trailingslashit( $url ) ); 502 503 // Add custom route args to iframe. 504 if ( ! empty( buddypress()->{$this->slug_endpoint}->embedargs_in_progress ) ) { 505 foreach( buddypress()->{$this->slug_endpoint}->embedargs_in_progress as $key => $value ) { 506 $url = add_query_arg( $key, $value, $url ); 507 } 508 } 509 } 510 511 return $url; 512 } 513 514 /** 515 * Filters the embed HTML for our BP oEmbed endpoint. 516 * 517 * @since 2.6.0 518 * 519 * @param string $retval Current embed HTML 520 * @return string 521 */ 522 public function filter_embed_html( $retval ) { 523 if ( false === isset( buddypress()->{$this->slug_endpoint}->embedurl_in_progress ) && ! $this->is_page() ) { 524 return $retval; 525 } 526 527 $url = $this->set_permalink(); 528 529 $item_id = $this->is_page() ? $this->validate_url_to_item_id( $url ) : buddypress()->{$this->slug_endpoint}->embedid_in_progress; 530 531 // Change 'Embedded WordPress Post' to custom title. 532 $custom_title = $this->set_iframe_title( $item_id ); 533 if ( ! empty( $custom_title ) ) { 534 $title_pos = strpos( $retval, 'title=' ) + 7; 535 $title_end_pos = strpos( $retval, '"', $title_pos ); 536 537 $retval = substr_replace( $retval, esc_attr( $custom_title ), $title_pos, $title_end_pos - $title_pos ); 538 } 539 540 // Add 'max-width' CSS attribute to IFRAME. 541 // This will make our oEmbeds responsive. 542 if ( false === strpos( $retval, 'style="max-width' ) ) { 543 $retval = str_replace( '<iframe', '<iframe style="max-width:100%"', $retval ); 544 } 545 546 // Remove default <blockquote> 547 $retval = substr( $retval, strpos( $retval, '</blockquote>' ) + 13 ); 548 549 // Set up new fallback HTML 550 // @todo Maybe use KSES? 551 $fallback_html = $this->set_fallback_html( $item_id ); 552 553 /** 554 * Dynamic filter to return BP oEmbed HTML. 555 * 556 * @since 2.6.0 557 * 558 * @var string $retval 559 */ 560 return apply_filters( "bp_{$this->slug_endpoint}_embed_html", $fallback_html . $retval ); 561 } 562 563 /** 564 * Append our custom slug endpoint to oEmbed endpoint URL. 565 * 566 * Meant to be used as a filter on 'rest_url' before any call to 567 * {@link get_oembed_endpoint_url()} is used. 568 * 569 * @since 2.6.0 570 * 571 * @see add_oembed_discovery_links() 572 * 573 * @param string $retval Current oEmbed endpoint URL 574 * @return string 575 */ 576 function filter_rest_url( $retval = '' ) { 577 return $retval . "/{$this->slug_endpoint}"; 578 } 579 580 /** 581 * Inject content into the embed template. 582 * 583 * @since 2.6.0 584 */ 585 public function inject_content() { 586 if ( ! $this->is_page() ) { 587 return; 588 } 589 590 $this->content(); 591 } 592 } 593 No newline at end of file -
src/bp-groups/classes/class-bp-groups-widget.php
29 29 ); 30 30 parent::__construct( false, _x( '(BuddyPress) Groups', 'widget name', 'buddypress' ), $widget_ops ); 31 31 32 if ( is_active_widget( false, false, $this->id_base ) && ! is_admin() && ! is_network_admin() ) {32 if ( is_active_widget( false, false, $this->id_base ) && ! is_admin() && ! is_network_admin() && 'embed=true' !== $_SERVER['QUERY_STRING'] ) { 33 33 $min = bp_core_get_minified_asset_suffix(); 34 34 wp_enqueue_script( 'groups_widget_groups_list-js', buddypress()->plugin_url . "bp-groups/js/widget-groups{$min}.js", array( 'jquery' ), bp_get_version() ); 35 35 } -
new file src/bp-templates/bp-legacy/buddypress/assets/embeds/activity.php
new file mode 100644
- + 1 2 <?php if ( bp_has_activities( 'display_comments=threaded&show_hidden=true&include=' . bp_current_action() ) ) : ?> 3 4 <?php while ( bp_activities() ) : bp_the_activity(); ?> 5 <div class="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/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_has_activities( 'display_comments=threaded&show_hidden=true&include=' . 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 <?php if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() ) : ?> 20 <p class="bp-embed-mentionname">@<?php bp_displayed_user_mentionname(); ?> · <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></p> 21 <?php endif; ?> 22 </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>