mirror of
git://develop.git.wordpress.org/
synced 2025-01-17 21:08:44 +01:00
Multisite: Initialize a user's roles correctly when setting them up for a different site.
While it has always been possible to initialize a user's roles and capabilities for another site than the current one in a multisite, the actual roles available were not switched prior to this change, possibly causing invalid roles to show up or actually valid capabilities not being available. In order to fix this bug in a clean way, relevant parts of the `WP_User` class have been refactored. The ID of the site for which capabilities are currently initialized are now stored in a private property `WP_User::$site_id`. The `WP_User::for_blog( $blog_id )` and `WP_User::_init_caps( $cap_key )` methods have been deprecated in favor of `WP_User::for_site( $site_id )`. In addition, a new method `WP_User::get_site_id()` has been introduced to retrieve the site ID for which the user's capabilities are currently initialized. Props ryanduff, jeremyfelt, flixos90. Fixes #36961. git-svn-id: https://develop.svn.wordpress.org/trunk@41624 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
0ef31b8de5
commit
8be3b4f729
@ -92,6 +92,14 @@ class WP_User {
|
||||
*/
|
||||
public $filter = null;
|
||||
|
||||
/**
|
||||
* The site ID the capabilities of this user are initialized for.
|
||||
*
|
||||
* @since 4.9.0
|
||||
* @var int
|
||||
*/
|
||||
private $site_id = 0;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @since 3.3.0
|
||||
@ -110,9 +118,9 @@ class WP_User {
|
||||
*
|
||||
* @param int|string|stdClass|WP_User $id User's ID, a WP_User object, or a user object from the DB.
|
||||
* @param string $name Optional. User's username
|
||||
* @param int $blog_id Optional Site ID, defaults to current site.
|
||||
* @param int $site_id Optional Site ID, defaults to current site.
|
||||
*/
|
||||
public function __construct( $id = 0, $name = '', $blog_id = '' ) {
|
||||
public function __construct( $id = 0, $name = '', $site_id = '' ) {
|
||||
if ( ! isset( self::$back_compat_keys ) ) {
|
||||
$prefix = $GLOBALS['wpdb']->prefix;
|
||||
self::$back_compat_keys = array(
|
||||
@ -126,10 +134,10 @@ class WP_User {
|
||||
}
|
||||
|
||||
if ( $id instanceof WP_User ) {
|
||||
$this->init( $id->data, $blog_id );
|
||||
$this->init( $id->data, $site_id );
|
||||
return;
|
||||
} elseif ( is_object( $id ) ) {
|
||||
$this->init( $id, $blog_id );
|
||||
$this->init( $id, $site_id );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -145,7 +153,7 @@ class WP_User {
|
||||
}
|
||||
|
||||
if ( $data ) {
|
||||
$this->init( $data, $blog_id );
|
||||
$this->init( $data, $site_id );
|
||||
} else {
|
||||
$this->data = new stdClass;
|
||||
}
|
||||
@ -157,13 +165,13 @@ class WP_User {
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param object $data User DB row object.
|
||||
* @param int $blog_id Optional. The site ID to initialize for.
|
||||
* @param int $site_id Optional. The site ID to initialize for.
|
||||
*/
|
||||
public function init( $data, $blog_id = '' ) {
|
||||
public function init( $data, $site_id = '' ) {
|
||||
$this->data = $data;
|
||||
$this->ID = (int) $data->ID;
|
||||
|
||||
$this->for_blog( $blog_id );
|
||||
$this->for_site( $site_id );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,22 +248,6 @@ class WP_User {
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes private/protected methods readable for backward compatibility.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @param callable $name Method to call.
|
||||
* @param array $arguments Arguments to pass when calling.
|
||||
* @return mixed|false Return value of the callback, false otherwise.
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
if ( '_init_caps' === $name ) {
|
||||
return call_user_func_array( array( $this, $name ), $arguments );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method for checking the existence of a certain custom field.
|
||||
*
|
||||
@ -424,6 +416,22 @@ class WP_User {
|
||||
return get_object_vars( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes private/protected methods readable for backward compatibility.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @param callable $name Method to call.
|
||||
* @param array $arguments Arguments to pass when calling.
|
||||
* @return mixed|false Return value of the callback, false otherwise.
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
if ( '_init_caps' === $name ) {
|
||||
return call_user_func_array( array( $this, $name ), $arguments );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up capability object properties.
|
||||
*
|
||||
@ -433,6 +441,7 @@ class WP_User {
|
||||
* used.
|
||||
*
|
||||
* @since 2.1.0
|
||||
* @deprecated 4.9.0 Use WP_User::for_site()
|
||||
*
|
||||
* @global wpdb $wpdb WordPress database abstraction object.
|
||||
*
|
||||
@ -441,15 +450,15 @@ class WP_User {
|
||||
protected function _init_caps( $cap_key = '' ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty($cap_key) )
|
||||
$this->cap_key = $wpdb->get_blog_prefix() . 'capabilities';
|
||||
else
|
||||
_deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' );
|
||||
|
||||
if ( empty( $cap_key ) ) {
|
||||
$this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
|
||||
} else {
|
||||
$this->cap_key = $cap_key;
|
||||
}
|
||||
|
||||
$this->caps = get_user_meta( $this->ID, $this->cap_key, true );
|
||||
|
||||
if ( ! is_array( $this->caps ) )
|
||||
$this->caps = array();
|
||||
$this->caps = $this->get_caps_data();
|
||||
|
||||
$this->get_role_caps();
|
||||
}
|
||||
@ -467,6 +476,13 @@ class WP_User {
|
||||
* @return array List of all capabilities for the user.
|
||||
*/
|
||||
public function get_role_caps() {
|
||||
$switch_site = false;
|
||||
if ( is_multisite() && $this->site_id != get_current_blog_id() ) {
|
||||
$switch_site = true;
|
||||
|
||||
switch_to_blog( $this->site_id );
|
||||
}
|
||||
|
||||
$wp_roles = wp_roles();
|
||||
|
||||
//Filter out caps that are not role names and assign to $this->roles
|
||||
@ -481,6 +497,10 @@ class WP_User {
|
||||
}
|
||||
$this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps );
|
||||
|
||||
if ( $switch_site ) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->allcaps;
|
||||
}
|
||||
|
||||
@ -754,17 +774,68 @@ class WP_User {
|
||||
* Set the site to operate on. Defaults to the current site.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @deprecated 4.9.0 Use WP_User::for_site()
|
||||
*
|
||||
* @global wpdb $wpdb WordPress database abstraction object.
|
||||
*
|
||||
* @param int $blog_id Optional. Site ID, defaults to current site.
|
||||
*/
|
||||
public function for_blog( $blog_id = '' ) {
|
||||
_deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' );
|
||||
|
||||
$this->for_site( $blog_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site to operate on. Defaults to the current site.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @global wpdb $wpdb WordPress database abstraction object.
|
||||
*
|
||||
* @param int $site_id Site ID to initialize user capabilities for. Default is the current site.
|
||||
*/
|
||||
public function for_site( $site_id = '' ) {
|
||||
global $wpdb;
|
||||
if ( ! empty( $blog_id ) )
|
||||
$cap_key = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
|
||||
else
|
||||
$cap_key = '';
|
||||
$this->_init_caps( $cap_key );
|
||||
|
||||
if ( ! empty( $site_id ) ) {
|
||||
$this->site_id = absint( $site_id );
|
||||
} else {
|
||||
$this->site_id = get_current_blog_id();
|
||||
}
|
||||
|
||||
$this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
|
||||
|
||||
$this->caps = $this->get_caps_data();
|
||||
|
||||
$this->get_role_caps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the site for which the user's capabilities are currently initialized.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @return int Site ID.
|
||||
*/
|
||||
public function get_site_id() {
|
||||
return $this->site_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available user capabilities data.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @return array User capabilities array.
|
||||
*/
|
||||
private function get_caps_data() {
|
||||
$caps = get_user_meta( $this->ID, $this->cap_key, true );
|
||||
|
||||
if ( ! is_array( $caps ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $caps;
|
||||
}
|
||||
}
|
||||
|
@ -850,7 +850,7 @@ function switch_to_blog( $new_blog, $deprecated = null ) {
|
||||
if ( did_action( 'init' ) ) {
|
||||
$wp_roles = new WP_Roles();
|
||||
$current_user = wp_get_current_user();
|
||||
$current_user->for_blog( $new_blog );
|
||||
$current_user->for_site( $new_blog );
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/ms-blogs.php */
|
||||
@ -924,7 +924,7 @@ function restore_current_blog() {
|
||||
if ( did_action( 'init' ) ) {
|
||||
$wp_roles = new WP_Roles();
|
||||
$current_user = wp_get_current_user();
|
||||
$current_user->for_blog( $blog );
|
||||
$current_user->for_site( $blog );
|
||||
}
|
||||
|
||||
/** This filter is documented in wp-includes/ms-blogs.php */
|
||||
|
@ -180,7 +180,7 @@ class Tests_User extends WP_UnitTestCase {
|
||||
$this->assertEquals( 'foo', $user->$key );
|
||||
$this->assertEquals( 'foo', $user->data->$key ); // This will fail with WP < 3.3
|
||||
|
||||
foreach ( (array) $user as $key => $value ) {
|
||||
foreach ( get_object_vars( $user ) as $key => $value ) {
|
||||
$this->assertEquals( $value, $user->$key );
|
||||
}
|
||||
}
|
||||
|
@ -1848,4 +1848,95 @@ class Tests_User_Capabilities extends WP_UnitTestCase {
|
||||
$this->assertFalse( user_can( self::$users['contributor']->ID, 'remove_user', self::$users['contributor']->ID ) );
|
||||
$this->assertFalse( user_can( self::$users['subscriber']->ID, 'remove_user', self::$users['subscriber']->ID ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 36961
|
||||
* @group ms-required
|
||||
*/
|
||||
function test_init_user_caps_for_different_site() {
|
||||
global $wpdb;
|
||||
|
||||
$site_id = self::factory()->blog->create( array( 'user_id' => self::$users['administrator']->ID ) );
|
||||
|
||||
switch_to_blog( $site_id );
|
||||
|
||||
$role_name = 'uploader';
|
||||
add_role( $role_name, 'Uploader', array(
|
||||
'read' => true,
|
||||
'upload_files' => true,
|
||||
) );
|
||||
add_user_to_blog( $site_id, self::$users['subscriber']->ID, $role_name );
|
||||
|
||||
restore_current_blog();
|
||||
|
||||
$user = new WP_User( self::$users['subscriber']->ID, '', $site_id );
|
||||
$this->assertTrue( $user->has_cap( 'upload_files' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 36961
|
||||
* @group ms-required
|
||||
*/
|
||||
function test_init_user_caps_for_different_site_by_user_switch() {
|
||||
global $wpdb;
|
||||
|
||||
$user = new WP_User( self::$users['subscriber']->ID );
|
||||
|
||||
$site_id = self::factory()->blog->create( array( 'user_id' => self::$users['administrator']->ID ) );
|
||||
|
||||
switch_to_blog( $site_id );
|
||||
|
||||
$role_name = 'uploader';
|
||||
add_role( $role_name, 'Uploader', array(
|
||||
'read' => true,
|
||||
'upload_files' => true,
|
||||
) );
|
||||
add_user_to_blog( $site_id, self::$users['subscriber']->ID, $role_name );
|
||||
|
||||
restore_current_blog();
|
||||
|
||||
$user->for_site( $site_id );
|
||||
$this->assertTrue( $user->has_cap( 'upload_files' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 36961
|
||||
*/
|
||||
function test_get_caps_data() {
|
||||
global $wpdb;
|
||||
|
||||
$custom_caps = array(
|
||||
'do_foo' => true,
|
||||
'do_bar' => false,
|
||||
);
|
||||
|
||||
// Test `WP_User::get_caps_data()` by manually setting capabilities metadata.
|
||||
update_user_meta( self::$users['subscriber']->ID, $wpdb->get_blog_prefix( get_current_blog_id() ) . 'capabilities', $custom_caps );
|
||||
|
||||
$user = new WP_User( self::$users['subscriber']->ID );
|
||||
$this->assertSame( $custom_caps, $user->caps );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 36961
|
||||
*/
|
||||
function test_user_get_site_id_default() {
|
||||
$user = new WP_User( self::$users['subscriber']->ID );
|
||||
$this->assertSame( get_current_blog_id(), $user->get_site_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 36961
|
||||
*/
|
||||
function test_user_get_site_id() {
|
||||
global $wpdb;
|
||||
|
||||
// Suppressing errors here allows to get around creating an actual site,
|
||||
// which is unnecessary for this test.
|
||||
$suppress = $wpdb->suppress_errors();
|
||||
$user = new WP_User( self::$users['subscriber']->ID, '', 333 );
|
||||
$wpdb->suppress_errors( $suppress );
|
||||
|
||||
$this->assertSame( 333, $user->get_site_id() );
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user