Skip to:
Content

bbPress.org

Changeset 7475


Ignore:
Timestamp:
09/10/2026 09:21:55 PM (6 weeks ago)
Author:
johnjamesjacoby
Message:

Counts: avoid forum-wide reply recounts.

Apply a topic's stored public reply count as an atomic forum difference when the topic enters or leaves the public status set. Add a short-circuit filter for integrations that replace the complete persisted transition count operation.

Add coverage for custom hidden statuses, moderation cascades, and the replacement lifecycle.

In trunk, for 2.7.

See #3678.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/CHANGELOG.md

    r7473 r7475  
    3434- Moved built-in topic, reply, forum, and user count synchronization to
    3535  `bbp_transition_post_status`. Existing creation and moderation actions still
    36   fire, but no longer host bbPress's core count callbacks. Extensions that need
    37   finalized counts should use the transition action at priority 11 or later;
    38   permanent deletion continues through `bbp_deleted_topic` and
    39   `bbp_deleted_reply`.
     36  fire, but no longer host bbPress's core count callbacks. Topic transitions
     37  apply the stored reply-count difference to forum aggregates instead of
     38  recounting every reply. Extensions that need finalized counts should use the
     39  transition action at priority 11 or later; permanent deletion continues
     40  through `bbp_deleted_topic` and `bbp_deleted_reply`.
    4041- Kept the classic editor for forums, topics, and replies while making bbPress
    4142  blocks available in Posts and Pages.
  • trunk/src/includes/common/functions.php

    r7467 r7475  
    265265function bbp_update_counts_on_transition_post_status( $new_status = '', $old_status = '', $post = false ) {
    266266
     267        /**
     268         * Short-circuits count updates for a persisted post-status transition.
     269         *
     270         * Returning a non-null value prevents the normal topic and reply count
     271         * updates. This allows integrations with custom post-status lifecycles or
     272         * count storage to replace the complete transition operation.
     273         *
     274         * @since 2.6.16
     275         *
     276         * @param null|bool $check      Whether to short-circuit count updates.
     277         * @param string    $new_status New post status.
     278         * @param string    $old_status Old post status.
     279         * @param WP_Post   $post       Post object.
     280         */
     281        $check = apply_filters( 'bbp_pre_update_counts_on_transition_post_status', null, $new_status, $old_status, $post );
     282        if ( null !== $check ) {
     283                return (bool) $check;
     284        }
     285
    267286        // Bail if the status did not change
    268287        if ( $new_status === $old_status ) {
    … …  
    302321                        }
    303322
    304                         // Recount replies after their parent topic crosses the public boundary
     323                        // Apply every public reply when its topic crosses the public boundary
    305324                        if ( ! $is_new && ! empty( $forum_id ) && ! empty( $public_difference ) ) {
    306                                 bbp_update_forum_reply_count( $forum_id, true );
     325                                $reply_count      = bbp_get_topic_reply_count( $post->ID, true );
     326                                $reply_difference = $reply_count * $public_difference;
     327
     328                                if ( ! empty( $reply_difference ) ) {
     329                                        bbp_bump_forum_reply_count( $forum_id, $reply_difference );
     330                                }
    307331                        }
    308332                }
  • trunk/src/readme.txt

    r7471 r7475  
    4848= Count updates in 2.6.16 =
    4949
    50 bbPress now synchronizes its built-in public and hidden topic and reply counts, aggregate forum counts, and user contribution counts on `bbp_transition_post_status` at priority 10, after WordPress persists the new post status. This action receives the new status, old status, and `WP_Post` object.
     50bbPress now synchronizes its built-in public and hidden topic and reply counts, aggregate forum counts, and user contribution counts on `bbp_transition_post_status` at priority 10, after WordPress persists the new post status. When a topic crosses the public status boundary, its stored public reply count is applied to the forum aggregate instead of recounting every reply. The action receives the new status, old status, and `WP_Post` object. The `bbp_pre_update_counts_on_transition_post_status` filter can short-circuit the complete topic or reply count operation for custom status lifecycles or count storage.
    5151
    5252The existing `bbp_new_*`, `bbp_insert_*`, `bbp_trash_*`, `bbp_untrash_*`, `bbp_spam_*`, `bbp_unspam_*`, `bbp_approve_*`, and `bbp_unapprove_*` topic and reply actions continue to fire with their existing arguments and timing. Their corresponding completed actions, such as `bbp_trashed_*`, also remain available. The public count helper functions also remain callable.
  • trunk/tests/phpunit/testcases/forums/functions/counts.php

    r7467 r7475  
    300300
    301301        /**
     302         * @covers ::bbp_update_counts_on_transition_post_status
     303         * @ticket BBP3678
     304         */
     305        public function test_bbp_status_transition_counts_can_be_short_circuited() {
     306                $user_id  = $this->factory->user->create();
     307                $forum_id = $this->factory->forum->create();
     308                $topic_id = $this->factory->topic->create( array(
     309                        'post_author' => $user_id,
     310                        'post_parent' => $forum_id,
     311                        'topic_meta'  => array( 'forum_id' => $forum_id ),
     312                ) );
     313                $this->factory->reply->create( array(
     314                        'post_parent' => $topic_id,
     315                        'reply_meta'  => array(
     316                                'forum_id' => $forum_id,
     317                                'topic_id' => $topic_id,
     318                        ),
     319                ) );
     320
     321                $this->assertSame( 1, bbp_get_forum_reply_count( $forum_id, false, true ) );
     322                $short_circuit = '__return_false';
     323                add_filter( 'bbp_pre_update_counts_on_transition_post_status', $short_circuit );
     324
     325                try {
     326                        bbp_unapprove_topic( $topic_id );
     327                } finally {
     328                        remove_filter( 'bbp_pre_update_counts_on_transition_post_status', $short_circuit );
     329                }
     330
     331                $this->assertSame( 1, bbp_get_forum_topic_count( $forum_id, false, true ) );
     332                $this->assertSame( 0, bbp_get_forum_topic_count_hidden( $forum_id, false, true ) );
     333                $this->assertSame( 1, bbp_get_forum_reply_count( $forum_id, false, true ) );
     334                $this->assertSame( 1, bbp_get_user_topic_count( $user_id, true ) );
     335        }
     336
     337        /**
    302338         * Generic function to test the forum counts on a approved/unapproved topic
    303339         */
  • trunk/tests/phpunit/testcases/replies/functions/status.php

    r7449 r7475  
    129129
    130130        /**
     131         * @covers ::bbp_update_counts_on_transition_post_status
     132         * @ticket BBP3678
     133         */
     134        public function test_custom_non_public_status_updates_reply_counts() {
     135                $user_id  = $this->factory->user->create();
     136                $forum_id = $this->factory->forum->create();
     137                $topic_id = $this->factory->topic->create( array(
     138                        'post_parent' => $forum_id,
     139                        'topic_meta'  => array( 'forum_id' => $forum_id ),
     140                ) );
     141                $reply_id = $this->factory->reply->create( array(
     142                        'post_author' => $user_id,
     143                        'post_parent' => $topic_id,
     144                        'reply_meta'  => array(
     145                                'forum_id' => $forum_id,
     146                                'topic_id' => $topic_id,
     147                        ),
     148                ) );
     149                $add_archived = function( $statuses ) {
     150                        $statuses[] = 'archived';
     151
     152                        return $statuses;
     153                };
     154
     155                add_filter( 'bbp_get_non_public_reply_statuses', $add_archived );
     156
     157                try {
     158                        wp_update_post( array(
     159                                'ID'          => $reply_id,
     160                                'post_status' => 'archived',
     161                        ) );
     162
     163                        $this->assertSame( 0, bbp_get_topic_reply_count( $topic_id, true ) );
     164                        $this->assertSame( 1, bbp_get_topic_reply_count_hidden( $topic_id, true ) );
     165                        $this->assertSame( 0, bbp_get_forum_reply_count( $forum_id, false, true ) );
     166                        $this->assertSame( 1, bbp_get_forum_reply_count_hidden( $forum_id, false, true ) );
     167                        $this->assertSame( 0, bbp_get_user_reply_count( $user_id, true ) );
     168
     169                        wp_update_post( array(
     170                                'ID'          => $reply_id,
     171                                'post_status' => bbp_get_public_status_id(),
     172                        ) );
     173
     174                        $this->assertSame( 1, bbp_get_topic_reply_count( $topic_id, true ) );
     175                        $this->assertSame( 0, bbp_get_topic_reply_count_hidden( $topic_id, true ) );
     176                        $this->assertSame( 1, bbp_get_forum_reply_count( $forum_id, false, true ) );
     177                        $this->assertSame( 0, bbp_get_forum_reply_count_hidden( $forum_id, false, true ) );
     178                        $this->assertSame( 1, bbp_get_user_reply_count( $user_id, true ) );
     179                } finally {
     180                        remove_filter( 'bbp_get_non_public_reply_statuses', $add_archived );
     181                }
     182        }
     183
     184        /**
    131185         * @covers ::bbp_update_reply_walker
    132186         */
  • trunk/tests/phpunit/testcases/topics/functions/status.php

    r7449 r7475  
    7878        /**
    7979         * @covers ::bbp_spam_topic
     80         * @covers ::bbp_update_counts_on_transition_post_status
    8081         */
    8182        public function test_bbp_spam_topic() {
    … …  
    233234
    234235        /**
     236         * @covers ::bbp_update_counts_on_transition_post_status
     237         * @ticket BBP3678
     238         */
     239        public function test_custom_non_public_status_updates_topic_and_forum_counts() {
     240                $user_id  = $this->factory->user->create();
     241                $forum_id = $this->factory->forum->create();
     242                $topic_id = $this->factory->topic->create( array(
     243                        'post_author' => $user_id,
     244                        'post_parent' => $forum_id,
     245                        'topic_meta'  => array( 'forum_id' => $forum_id ),
     246                ) );
     247                $this->factory->reply->create( array(
     248                        'post_parent' => $topic_id,
     249                        'reply_meta'  => array(
     250                                'forum_id' => $forum_id,
     251                                'topic_id' => $topic_id,
     252                        ),
     253                ) );
     254                $add_archived = function( $statuses ) {
     255                        $statuses[] = 'archived';
     256
     257                        return $statuses;
     258                };
     259
     260                add_filter( 'bbp_get_non_public_topic_statuses', $add_archived );
     261
     262                try {
     263                        wp_update_post( array(
     264                                'ID'          => $topic_id,
     265                                'post_status' => 'archived',
     266                        ) );
     267
     268                        $this->assertSame( 0, bbp_get_forum_topic_count( $forum_id, false, true ) );
     269                        $this->assertSame( 1, bbp_get_forum_topic_count_hidden( $forum_id, false, true ) );
     270                        $this->assertSame( 0, bbp_get_forum_reply_count( $forum_id, false, true ) );
     271                        $this->assertSame( 0, bbp_get_user_topic_count( $user_id, true ) );
     272
     273                        wp_update_post( array(
     274                                'ID'          => $topic_id,
     275                                'post_status' => bbp_get_public_status_id(),
     276                        ) );
     277
     278                        $this->assertSame( 1, bbp_get_forum_topic_count( $forum_id, false, true ) );
     279                        $this->assertSame( 0, bbp_get_forum_topic_count_hidden( $forum_id, false, true ) );
     280                        $this->assertSame( 1, bbp_get_forum_reply_count( $forum_id, false, true ) );
     281                        $this->assertSame( 1, bbp_get_user_topic_count( $user_id, true ) );
     282                } finally {
     283                        remove_filter( 'bbp_get_non_public_topic_statuses', $add_archived );
     284                }
     285        }
     286
     287        /**
    235288         * @covers ::bbp_spam_topic_replies
    236289         */
    … …  
    306359        /**
    307360         * @covers ::bbp_unspam_topic
     361         * @covers ::bbp_update_counts_on_transition_post_status
    308362         */
    309363        public function test_bbp_unspam_topic() {
    … …  
    384438
    385439        /**
     440         * @covers ::bbp_update_counts_on_transition_post_status
     441         * @ticket BBP3678
     442         */
     443        public function test_repeated_topic_status_cascades_do_not_drift_forum_reply_count() {
     444                $forum_id = $this->factory->forum->create();
     445                $topic_id = $this->factory->topic->create( array(
     446                        'post_parent' => $forum_id,
     447                        'topic_meta'  => array( 'forum_id' => $forum_id ),
     448                ) );
     449                $this->factory->reply->create_many( 2, array(
     450                        'post_parent' => $topic_id,
     451                        'reply_meta'  => array(
     452                                'forum_id' => $forum_id,
     453                                'topic_id' => $topic_id,
     454                        ),
     455                ) );
     456
     457                for ( $i = 0; $i < 2; $i++ ) {
     458                        bbp_spam_topic( $topic_id );
     459                        $this->assertSame( 0, bbp_get_forum_reply_count( $forum_id, false, true ) );
     460
     461                        bbp_unspam_topic( $topic_id );
     462                        $this->assertSame( 2, bbp_get_forum_reply_count( $forum_id, false, true ) );
     463
     464                        wp_trash_post( $topic_id );
     465                        $this->assertSame( 0, bbp_get_forum_reply_count( $forum_id, false, true ) );
     466
     467                        wp_untrash_post( $topic_id );
     468                        $this->assertSame( 2, bbp_get_forum_reply_count( $forum_id, false, true ) );
     469                }
     470        }
     471
     472        /**
    386473         * @covers ::bbp_unspam_topic_replies
    387474         */
Note: See TracChangeset for help on using the changeset viewer.