Skip to content

Commit dd326c9

Browse files
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 [https://mu.trac.wordpress.org/changeset/804 mu:804], [19016]. Props dd32, mukesh27. Fixes #65096. git-svn-id: https://develop.svn.wordpress.org/trunk@62413 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ac539ca commit dd326c9

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

src/wp-includes/user.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,7 @@ function get_blogs_of_user( $user_id, $all = false ) {
11521152
* Finds out whether a user is a member of a given blog.
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.
11571158
*
@@ -1201,9 +1202,24 @@ function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
12011202
} else {
12021203
$capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
12031204
}
1204-
$has_cap = get_user_meta( $user_id, $capabilities_key, true );
12051205

1206-
return is_array( $has_cap );
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

12091225
/**

tests/phpunit/tests/user/multisite.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,71 @@ public function test_is_user_member_of_blog() {
176176
wp_set_current_user( $old_current );
177177
}
178178

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.' );
242+
}
243+
179244
/**
180245
* @ticket 23192
181246
*/

0 commit comments

Comments
 (0)