Changeset 7847
- Timestamp:
- 10/06/2026 09:55:16 AM (11 hours ago)
- Location:
- trunk
- Files:
-
- 1 added
- 5 edited
-
CHANGELOG.md (modified) (1 diff)
-
src/includes/common/formatting.php (modified) (1 diff)
-
src/includes/core/filters.php (modified) (1 diff)
-
src/includes/core/theme-compat.php (modified) (3 diffs)
-
tests/phpunit/testcases/common/content-filters.php (added)
-
tests/phpunit/testcases/core/theme-compat.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/CHANGELOG.md
r7845 r7847 39 39 ### Security 40 40 41 - Kept bbPress post shortcodes literal in WordPress content loops. 41 42 - Strengthened password checks for topic and reply submissions. 42 43 - Limited block editor topic-tag choices to public discussions in accessible forums. -
trunk/src/includes/common/formatting.php
r7652 r7847 10 10 // Exit if accessed directly 11 11 defined( 'ABSPATH' ) || exit; 12 13 /** WordPress Content *********************************************************/ 14 15 /** 16 * Filter shortcodes in bbPress post content in WordPress content loops. 17 * 18 * bbPress renders its post content without running shortcodes, but a theme can 19 * pass the raw content through WordPress's the_content filter. Offer a bbPress 20 * sub-filter for this case without affecting shortcodes in other post types. 21 * 22 * @since 2.6.20 bbPress (r7847) 23 * 24 * @param false|string $output Short-circuit value from an earlier filter. 25 * @param string $tag Shortcode name. 26 * @param array $attr Shortcode attributes. 27 * @param array $matches Shortcode regular expression match. 28 * @return false|string Filtered short-circuit value. 29 */ 30 function bbp_pre_do_shortcode_tag( $output, $tag, $attr, $matches ) { 31 if ( ! doing_filter( 'the_content' ) ) { 32 return $output; 33 } 34 35 if ( ! in_array( get_post_type(), bbp_get_post_types(), true ) ) { 36 return $output; 37 } 38 39 return apply_filters( 'bbp_pre_do_shortcode_tag', $output, $tag, $attr, $matches ); 40 } 41 42 /** 43 * Prevent shortcodes in bbPress post content unless explicitly allowed. 44 * 45 * The allow-list is empty by default because bbPress does not execute 46 * shortcodes in forum, topic, or reply content on its own content paths. 47 * 48 * @since 2.6.20 bbPress (r7847) 49 * 50 * @param false|string $output Short-circuit value from an earlier filter. 51 * @param string $tag Shortcode name. 52 * @param array $attr Shortcode attributes. 53 * @param array $matches Shortcode regular expression match. 54 * @return false|string Short-circuit value or the literal shortcode. 55 */ 56 function bbp_prevent_content_shortcodes( $output, $tag, $attr, $matches ) { 57 /** 58 * Filter shortcode tags allowed to run in bbPress post content. 59 * 60 * @since 2.6.20 bbPress (r7847) 61 * 62 * @param array $allowed Allowed shortcode tags. Empty by default. 63 * @param string|false $post_type Current post type. 64 */ 65 $allowed = (array) apply_filters( 'bbp_allowed_content_shortcodes', array(), get_post_type() ); 66 67 if ( ! in_array( $tag, $allowed, true ) ) { 68 $output = $matches[0]; 69 } 70 71 return $output; 72 } 12 73 13 74 /** Kses **********************************************************************/ -
trunk/src/includes/core/filters.php
r7709 r7847 49 49 add_filter( 'logout_url', 'bbp_logout_url', 2, 2 ); 50 50 add_filter( 'plugin_locale', 'bbp_plugin_locale', 10, 2 ); 51 52 // Apply bbPress shortcode policy to forum, topic, and reply content. 53 add_filter( 'pre_do_shortcode_tag', 'bbp_pre_do_shortcode_tag', 10, 4 ); 54 add_filter( 'bbp_pre_do_shortcode_tag', 'bbp_prevent_content_shortcodes', 10, 4 ); 51 55 52 56 // Keep WordPress author discovery limited to non-forum posts. -
trunk/src/includes/core/theme-compat.php
r7653 r7847 478 478 479 479 /** 480 * Filter the content of a forum or topic archive root page. 481 * 482 * Root pages are ordinary WordPress pages, even though an archive post may be 483 * current while their content is filtered. Temporarily use the root page as 484 * the current query post so shortcodes restore the correct post data. 485 * 486 * @since 2.6.20 bbPress (r7847) 487 * 488 * @param WP_Post $page Root page. 489 * @return string Filtered page content. 490 */ 491 function bbp_get_theme_compat_page_content( $page ) { 492 global $post; 493 494 $wp_query = bbp_get_wp_query(); 495 $content = ( $page instanceof WP_Post ) ? $page->post_content : ''; 496 497 // Fall back to the previous behavior without a valid post and main query. 498 if ( ! ( $page instanceof WP_Post ) || ! ( $wp_query instanceof WP_Query ) ) { 499 return apply_filters( 'the_content', $content ); 500 } 501 502 // The main query post is also the current global post in this call path. 503 $original_query_post = $wp_query->post; 504 505 try { 506 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited 507 $post = $page; 508 $wp_query->post = $page; 509 $wp_query->setup_postdata( $page ); 510 511 return apply_filters( 'the_content', $content ); 512 } finally { 513 $wp_query->post = $original_query_post; 514 wp_reset_postdata(); 515 } 516 } 517 518 /** 480 519 * Reset main query vars and filter 'the_content' to output a bbPress 481 520 * template part as needed. … … 562 601 // ...or use the existing page content? 563 602 } else { 564 $new_content = apply_filters( 'the_content', $page->post_content);603 $new_content = bbp_get_theme_compat_page_content( $page ); 565 604 } 566 605 … … 649 688 // ...or use the existing page content? 650 689 } else { 651 $new_content = apply_filters( 'the_content', $page->post_content);690 $new_content = bbp_get_theme_compat_page_content( $page ); 652 691 } 653 692 -
trunk/tests/phpunit/testcases/core/theme-compat.php
r7424 r7847 77 77 78 78 /** 79 * A root page's shortcode must run even when a bbPress post is ambient. 80 * 81 * @dataProvider get_root_page_content_cases 82 * 83 * @param string $post_type Ambient bbPress post type. 84 * @param string $archive_filter Archive condition to enable. 85 */ 86 public function test_root_page_content_runs_shortcodes( $post_type, $archive_filter ) { 87 $old_permalinks = get_option( 'permalink_structure' ); 88 $old_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; 89 $old_query_post = isset( $GLOBALS['wp_query']->post ) ? $GLOBALS['wp_query']->post : null; 90 $old_query_posts = isset( $GLOBALS['wp_query']->posts ) ? $GLOBALS['wp_query']->posts : array(); 91 $root_slug = ( bbp_get_forum_post_type() === $post_type ) ? bbp_get_root_slug() : bbp_get_topic_archive_slug(); 92 $page_id = $this->factory->post->create( 93 array( 94 'post_type' => 'page', 95 'post_name' => $root_slug, 96 'post_content' => '[bbp-stats] [bbp_content_probe]', 97 ) 98 ); 99 $ambient_id = $this->factory->post->create( array( 'post_type' => $post_type ) ); 100 $render = function( $output ) { 101 return $output . 'root-page-bbpress-ran'; 102 }; 103 104 update_option( 'permalink_structure', '/%postname%/' ); 105 add_filter( $archive_filter, '__return_true' ); 106 add_filter( 'bbp_display_shortcode', $render ); 107 add_shortcode( 'bbp_content_probe', function() { 108 return sprintf( 'root-page-shortcode-ran-%d-%d', get_the_ID(), $GLOBALS['id'] ); 109 } ); 110 111 try { 112 $GLOBALS['post'] = get_post( $ambient_id ); 113 $GLOBALS['wp_query']->post = $GLOBALS['post']; 114 $GLOBALS['wp_query']->posts = array( $GLOBALS['post'] ); 115 $GLOBALS['wp_query']->setup_postdata( $GLOBALS['post'] ); 116 $content = bbp_get_theme_compat_page_content( get_post( $page_id ) ); 117 118 $this->assertSame( $ambient_id, $GLOBALS['post']->ID ); 119 $this->assertSame( $ambient_id, $GLOBALS['wp_query']->post->ID ); 120 $this->assertSame( $ambient_id, $GLOBALS['id'] ); 121 $this->assertStringContainsString( 'root-page-bbpress-ran', $content ); 122 $this->assertStringContainsString( "root-page-shortcode-ran-{$page_id}-{$page_id}", $content ); 123 124 bbp_template_include_theme_compat( '/original-template.php' ); 125 126 $this->assertSame( $page_id, $GLOBALS['post']->ID ); 127 $this->assertStringContainsString( "root-page-shortcode-ran-{$page_id}-{$page_id}", $GLOBALS['post']->post_content ); 128 } finally { 129 remove_shortcode( 'bbp_content_probe' ); 130 remove_filter( 'bbp_display_shortcode', $render ); 131 remove_filter( $archive_filter, '__return_true' ); 132 update_option( 'permalink_structure', $old_permalinks ); 133 $GLOBALS['post'] = $old_post; 134 $GLOBALS['wp_query']->post = $old_query_post; 135 $GLOBALS['wp_query']->posts = $old_query_posts; 136 } 137 } 138 139 /** 140 * Root page cases for forum and topic archives. 141 * 142 * @return array[] Root page cases. 143 */ 144 public function get_root_page_content_cases() { 145 return array( 146 'forum archive' => array( bbp_get_forum_post_type(), 'bbp_is_forum_archive' ), 147 'topic archive' => array( bbp_get_topic_post_type(), 'bbp_is_topic_archive' ), 148 ); 149 } 150 151 /** 152 * A root page's bbPress block and following shortcode must both run. 153 * 154 * @dataProvider get_root_page_content_cases 155 * 156 * @param string $post_type Ambient bbPress post type. 157 * @param string $archive_filter Archive condition to enable. 158 */ 159 public function test_root_page_content_runs_bbpress_blocks( $post_type, $archive_filter ) { 160 $old_permalinks = get_option( 'permalink_structure' ); 161 $old_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; 162 $old_query_post = isset( $GLOBALS['wp_query']->post ) ? $GLOBALS['wp_query']->post : null; 163 $old_query_posts = isset( $GLOBALS['wp_query']->posts ) ? $GLOBALS['wp_query']->posts : array(); 164 $root_slug = ( bbp_get_forum_post_type() === $post_type ) ? bbp_get_root_slug() : bbp_get_topic_archive_slug(); 165 $page_id = $this->factory->post->create( 166 array( 167 'post_type' => 'page', 168 'post_name' => $root_slug, 169 'post_content' => '<!-- wp:bbpress/stats /--> [bbp_content_probe]', 170 ) 171 ); 172 $ambient_id = $this->factory->post->create( array( 'post_type' => $post_type ) ); 173 $render = function( $output ) { 174 return $output . 'root-block-bbpress-ran'; 175 }; 176 177 update_option( 'permalink_structure', '/%postname%/' ); 178 add_filter( $archive_filter, '__return_true' ); 179 add_filter( 'bbp_display_shortcode', $render ); 180 add_shortcode( 'bbp_content_probe', function() { 181 return sprintf( 'root-block-shortcode-ran-%d-%d', get_the_ID(), $GLOBALS['id'] ); 182 } ); 183 184 try { 185 $GLOBALS['post'] = get_post( $ambient_id ); 186 $GLOBALS['wp_query']->post = $GLOBALS['post']; 187 $GLOBALS['wp_query']->posts = array( $GLOBALS['post'] ); 188 $GLOBALS['wp_query']->setup_postdata( $GLOBALS['post'] ); 189 $content = bbp_get_theme_compat_page_content( get_post( $page_id ) ); 190 191 $this->assertSame( $ambient_id, $GLOBALS['post']->ID ); 192 $this->assertSame( $ambient_id, $GLOBALS['wp_query']->post->ID ); 193 $this->assertSame( $ambient_id, $GLOBALS['id'] ); 194 $this->assertStringContainsString( 'root-block-bbpress-ran', $content ); 195 $this->assertStringContainsString( "root-block-shortcode-ran-{$page_id}-{$page_id}", $content ); 196 } finally { 197 remove_shortcode( 'bbp_content_probe' ); 198 remove_filter( 'bbp_display_shortcode', $render ); 199 remove_filter( $archive_filter, '__return_true' ); 200 update_option( 'permalink_structure', $old_permalinks ); 201 $GLOBALS['post'] = $old_post; 202 $GLOBALS['wp_query']->post = $old_query_post; 203 $GLOBALS['wp_query']->posts = $old_query_posts; 204 } 205 } 206 207 /** 208 * Root page filtering must restore post data when a content filter throws. 209 */ 210 public function test_root_page_content_restores_post_data_after_exception() { 211 $page_id = $this->factory->post->create( array( 'post_type' => 'page' ) ); 212 $ambient_id = $this->factory->forum->create(); 213 $old_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; 214 $old_query = $GLOBALS['wp_query']->post; 215 $throw = function() { 216 throw new RuntimeException( 'Stop filtering root page content.' ); 217 }; 218 219 $GLOBALS['post'] = get_post( $ambient_id ); 220 $GLOBALS['wp_query']->post = $GLOBALS['post']; 221 $GLOBALS['wp_query']->setup_postdata( $GLOBALS['post'] ); 222 add_filter( 'the_content', $throw, 1 ); 223 224 try { 225 bbp_get_theme_compat_page_content( get_post( $page_id ) ); 226 $this->fail( 'The content filter did not throw.' ); 227 } catch ( RuntimeException $exception ) { 228 $this->assertSame( 'Stop filtering root page content.', $exception->getMessage() ); 229 $this->assertSame( $ambient_id, $GLOBALS['post']->ID ); 230 $this->assertSame( $ambient_id, $GLOBALS['wp_query']->post->ID ); 231 $this->assertSame( $ambient_id, $GLOBALS['id'] ); 232 } finally { 233 remove_filter( 'the_content', $throw, 1 ); 234 $GLOBALS['post'] = $old_post; 235 $GLOBALS['wp_query']->post = $old_query; 236 } 237 } 238 239 /** 79 240 * Theme compatibility template selection cases. 80 241 *
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)