Make WordPress Core

Changeset 62413 for trunk


Ignore:
Timestamp:
05/23/2026 06:16:45 PM (5 months ago)
Author:
SergeyBiryukov
Message:

Role/Capability: Add is_user_member_of_blog filter.

This aims to make it easier to dynamically grant or revoke a user access to the site without giving them a role directly.

Follow-up to ​mu:804, [19016].

Props dd32, mukesh27.
Fixes #65096.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/user.php

    r61735 r62413  
    11531153 *
    11541154 * @since MU (3.0.0)
     1155 * @since 7.1.0 Introduced the {@see 'is_user_member_of_blog'} filter.
    11551156 *
    11561157 * @global wpdb $wpdb WordPress database abstraction object.
    … …  
    12021203                $capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
    12031204        }
    1204         $has_cap = get_user_meta( $user_id, $capabilities_key, true );
    1205 
    1206         return is_array( $has_cap );
     1205
     1206        $has_cap   = get_user_meta( $user_id, $capabilities_key, true );
     1207        $is_member = is_array( $has_cap );
     1208
     1209        /**
     1210         * Filters whether the user is a member of a given blog.
     1211         *
     1212         * This filter only runs when the user and blog have both been resolved
     1213         * to valid records on a multisite installation; it is not invoked for
     1214         * logged-out requests, unknown users, or archived/spammed/deleted sites.
     1215         *
     1216         * @since 7.1.0
     1217         *
     1218         * @param bool $is_member Whether the user is a member of the blog.
     1219         * @param int  $user_id   The user ID being checked.
     1220         * @param int  $blog_id   The blog ID being checked.
     1221         */
     1222        return (bool) apply_filters( 'is_user_member_of_blog', $is_member, $user_id, $blog_id );
    12071223}
    12081224
  • trunk/tests/phpunit/tests/user/multisite.php

    r61037 r62413  
    175175
    176176                wp_set_current_user( $old_current );
     177        }
     178
     179        /**
     180         * Ensures the `is_user_member_of_blog` filter can override the return value
     181         * and receives the resolved user ID and blog ID.
     182         *
     183         * @ticket 65096
     184         *
     185         * @covers ::is_user_member_of_blog
     186         */
     187        public function test_is_user_member_of_blog_filter() {
     188                $user_id = self::factory()->user->create();
     189                $blog_id = self::factory()->blog->create();
     190
     191                // Sanity check: the user is not a member of the blog by default.
     192                $this->assertFalse( is_user_member_of_blog( $user_id, $blog_id ) );
     193
     194                $filter_args = array();
     195                $filter      = function ( $is_member, $filtered_user_id, $filtered_blog_id ) use ( &$filter_args ) {
     196                        $filter_args[] = array( $is_member, $filtered_user_id, $filtered_blog_id );
     197                        return true;
     198                };
     199
     200                add_filter( 'is_user_member_of_blog', $filter, 10, 3 );
     201                $result = is_user_member_of_blog( $user_id, $blog_id );
     202
     203                $this->assertTrue( $result, 'Filter should be able to force a truthy return value.' );
     204                $this->assertCount( 1, $filter_args, 'Filter should run exactly once per call on a valid multisite blog.' );
     205                $this->assertSame( array( false, $user_id, $blog_id ), $filter_args[0], 'Filter should receive the computed membership, user ID, and blog ID.' );
     206        }
     207
     208        /**
     209         * Ensures the `is_user_member_of_blog` filter is not invoked for requests
     210         * that short-circuit before the membership is computed.
     211         *
     212         * @ticket 65096
     213         *
     214         * @covers ::is_user_member_of_blog
     215         */
     216        public function test_is_user_member_of_blog_filter_not_called_for_invalid_input() {
     217                $filter_calls = 0;
     218                $filter       = function ( $is_member ) use ( &$filter_calls ) {
     219                        ++$filter_calls;
     220                        return $is_member;
     221                };
     222
     223                add_filter( 'is_user_member_of_blog', $filter );
     224
     225                // No current user, and no user ID provided.
     226                $old_current = get_current_user_id();
     227                wp_set_current_user( 0 );
     228                $is_member = is_user_member_of_blog();
     229                wp_set_current_user( $old_current );
     230                $this->assertFalse( $is_member, 'Filter should not run when no user ID was provided.' );
     231
     232                // Unknown user ID.
     233                $this->assertFalse( is_user_member_of_blog( PHP_INT_MAX ), 'Filter should not run without a valid user ID.' );
     234
     235                // Known user, but an archived/deleted/spam site short-circuits.
     236                $user_id = self::factory()->user->create();
     237                $blog_id = self::factory()->blog->create();
     238                update_blog_details( $blog_id, array( 'archived' => 1 ) );
     239                $this->assertFalse( is_user_member_of_blog( $user_id, $blog_id ) );
     240
     241                $this->assertSame( 0, $filter_calls, 'Filter should not run when the function short-circuits before computing membership.' );
    177242        }
    178243
Note: See TracChangeset for help on using the changeset viewer.