Completed
Push — fix/domdoc-parsing-errors ( fe8099...30de94 )
by Jeremy
09:15
created

get_real_user_capabilities()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
class Jetpack_Sync_Module_Users extends Jetpack_Sync_Module {
4
	const MAX_INITIAL_SYNC_USERS = 100;
5
6
	function name() {
7
		return 'users';
8
	}
9
10
	// this is here to support the backfill API
11
	public function get_object_by_id( $object_type, $id ) {
12
		if ( $object_type === 'user' && $user = get_user_by( 'id', intval( $id ) ) ) {
13
			return $this->sanitize_user_and_expand( $user );
14
		}
15
16
		return false;
17
	}
18
19
	public function init_listeners( $callable ) {
20
		// users
21
		add_action( 'user_register', array( $this, 'save_user_handler' ) );
22
		add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
23
		add_action( 'add_user_to_blog', array( $this, 'save_user_handler' ) );
24
		add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
25
		add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
26
		add_action( 'jetpack_sync_save_user', $callable );
27
28
		//Edit user info, see https://github.com/WordPress/WordPress/blob/c05f1dc805bddcc0e76fd90c4aaf2d9ea76dc0fb/wp-admin/user-edit.php#L126
29
		add_action( 'personal_options_update', array( $this, 'edited_user_handler' ) );
30
		add_action( 'edit_user_profile_update', array( $this, 'edited_user_handler' ) );
31
		add_action( 'jetpack_user_edited', $callable );
32
33
		add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
34
		add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
35
36
		add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
37
		add_action( 'jetpack_deleted_user', $callable, 10, 3 );
38
		add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
39
		add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
40
41
		// user roles
42
		add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
43
		add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
44
		add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
45
46
		// user capabilities
47
		add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
48
		add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
49
		add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
50
51
		// user authentication
52
		add_action( 'wp_login', $callable, 10, 2 );
53
		add_action( 'wp_logout', $callable, 10, 0 );
54
		add_action( 'wp_masterbar_logout', $callable, 10, 0 );
55
	}
56
57
	public function init_full_sync_listeners( $callable ) {
58
		add_action( 'jetpack_full_sync_users', $callable );
59
	}
60
61
	public function init_before_send() {
62
		add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_user' ) );
63
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_user' ) );
64
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_user' ), 10, 2 );
65
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
66
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
67
68
		// full sync
69
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
70
	}
71
72
	public function sanitize_user_and_expand( $user ) {
73
		$user = $this->add_to_user( $user );
74
		return $this->sanitize_user( $user );
75
	}
76
77
	public function sanitize_user( $user ) {
78
		// this create a new user object and stops the passing of the object by reference.
79
		$user = unserialize( serialize( $user ) );
80
81
		if ( is_object( $user ) && is_object( $user->data ) ) {
82
			unset( $user->data->user_pass );
83
		}
84
85
		$user->allcaps = $this->get_real_user_capabilities( $user );
86
		return $user;
87
	}
88
89
	public function add_to_user( $user ) {
90
		$user->allowed_mime_types = get_allowed_mime_types( $user );
91
92
		if ( function_exists( 'get_user_locale' ) ) {
93
94
			// Only set the user locale if it is different from the site local
95
			if ( get_locale() !== get_user_locale( $user->ID ) ) {
96
				$user->locale = get_user_locale( $user->ID );
97
			}
98
		}
99
100
		return $user;
101
	}
102
103
	public function get_real_user_capabilities( $user ) {
104
		$user_capabilities = array();
105
		foreach( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
106
			if ( $user_has_capabilities = user_can( $user , $capability ) ) {
0 ignored issues
show
Unused Code introduced by
$user_has_capabilities is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
107
				$user_capabilities[ $capability ] = true;
108
			}
109
		}
110
		return $user_capabilities;
111
	}
112
113
	public function expand_user( $args ) {
114
		list( $user ) = $args;
115
116
		if ( $user ) {
117
			return array( $this->add_to_user( $user ) );
118
		}
119
120
		return false;
121
	}
122
123
	public function expand_login_username( $args ) {
124
		list( $login, $user ) = $args;
125
		$user = $this->sanitize_user( $user );
126
127
		return array( $login, $user );
128
	}
129
130
	public function expand_logout_username( $args, $user_id ) {
131
		$user  = get_userdata( $user_id );
132
		$user  = $this->sanitize_user( $user );
133
		$login = '';
134
		if ( is_object( $user ) && is_object( $user->data ) ) {
135
			$login = $user->data->user_login;
136
		}
137
138
		return array( $login, $user );
139
	}
140
141
	public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
142
		$is_multisite = is_multisite();
143
		/**
144
		 * Fires when a user is deleted on a site
145
		 *
146
		 * @since 5.4.0
147
		 *
148
		 * @param int $deleted_user_id - ID of the deleted user
149
		 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
150
		 * @param bool $is_multisite - Whether this site is a multisite installation
151
		 */
152
		do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
153
	}
154
155
	public function edited_user_handler( $user_id ) {
156
		/**
157
		 * Fires when a user is edited on a site
158
		 *
159
		 * @since 5.4.0
160
		 *
161
		 * @param int $user_id - ID of the edited user
162
		 */
163
		do_action( 'jetpack_user_edited', $user_id );
164
	}
165
	
166
	function save_user_handler( $user_id, $old_user_data = null ) {
167
		// ensure we only sync users who are members of the current blog
168
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
169
			return;
170
		}
171
172
		$user = $this->sanitize_user( get_user_by( 'id', $user_id ) );
173
174
		// Older versions of WP don't pass the old_user_data in ->data
175
		if ( isset( $old_user_data->data ) ) {
176
			$old_user = $old_user_data->data;
177
		} else {
178
			$old_user = $old_user_data;
179
		}
180
181
		if ( $old_user !== null ) {
182
			unset( $old_user->user_pass );
183
			if ( serialize( $old_user ) === serialize( $user->data ) ) {
184
				return;
185
			}
186
		}
187
188
		if ( 'user_register' === current_filter() ) {
189
			/**
190
			 * Fires when a new user is registered on a site
191
			 *
192
			 * @since 4.9.0
193
			 *
194
			 * @param object The WP_User object
195
			 */
196
			do_action( 'jetpack_sync_register_user', $user );
197
198
			return;
199
		}
200
		/* MU Sites add users instead of register them to sites */
201
		if ( 'add_user_to_blog' === current_filter() ) {
202
			/**
203
			 * Fires when a new user is added to a site. (WordPress Multisite)
204
			 *
205
			 * @since 4.9.0
206
			 *
207
			 * @param object The WP_User object
208
			 */
209
			do_action( 'jetpack_sync_add_user', $user );
210
211
			return;
212
		}
213
214
		/**
215
		 * Fires when the client needs to sync an updated user
216
		 *
217
		 * @since 4.2.0
218
		 *
219
		 * @param object The WP_User object
220
		 */
221
		do_action( 'jetpack_sync_save_user', $user );
222
	}
223
224
	function save_user_role_handler( $user_id, $role, $old_roles = null ) {
225
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
226
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
227
			return;
228
		}
229
230
		$user = $this->sanitize_user( get_user_by( 'id', $user_id ) );
231
		/**
232
		 * Fires when the client needs to sync an updated user
233
		 *
234
		 * @since 4.2.0
235
		 *
236
		 * @param object The WP_User object
237
		 */
238
		do_action( 'jetpack_sync_save_user', $user );
239
	}
240
241
	function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
242
		if ( $meta_key === 'locale' ) {
243
			if ( current_filter() === 'deleted_user_meta' ) {
244
				/**
245
				 * Allow listeners to listen for user local delete changes
246
				 *
247
				 * @since 4.8.0
248
				 *
249
				 * @param int $user_id - The ID of the user whos locale is being deleted
250
				 */
251
				do_action( 'jetpack_sync_user_locale_delete', $user_id );
252
			} else {
253
				/**
254
				 * Allow listeners to listen for user local changes
255
				 *
256
				 * @since 4.8.0
257
				 *
258
				 * @param int $user_id - The ID of the user whos locale is being changed
259
				 * @param int $value - The value of the new locale
260
				 */
261
				do_action( 'jetpack_sync_user_locale', $user_id, $value );
262
			}
263
		}
264
		$this->save_user_cap_handler( $meta_id, $user_id, $meta_key, $value );
265
	}
266
267
	function save_user_cap_handler( $meta_id, $user_id, $meta_key, $capabilities ) {
268
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
269
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
270
			return;
271
		}
272
273
		// if a user is currently being removed as a member of this blog, we don't fire the event
274
		if ( current_filter() === 'deleted_user_meta'
275
		     &&
276
		     preg_match( '/capabilities|user_level/', $meta_key )
277
		     &&
278
		     ! is_user_member_of_blog( $user_id, get_current_blog_id() )
279
		) {
280
			return;
281
		}
282
283
		$user = get_user_by( 'id', $user_id );
284
		if ( $meta_key === $user->cap_key ) {
285
			/**
286
			 * Fires when the client needs to sync an updated user
287
			 *
288
			 * @since 4.2.0
289
			 *
290
			 * @param object The Sanitized WP_User object
291
			 */
292
			do_action( 'jetpack_sync_save_user', $this->sanitize_user( $user ) );
293
		}
294
	}
295
296
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
297
		global $wpdb;
298
299
		return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_users', $wpdb->usermeta, 'user_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
300
	}
301
302
	public function estimate_full_sync_actions( $config ) {
303
		global $wpdb;
304
305
		$query = "SELECT count(*) FROM $wpdb->usermeta";
306
307
		if ( $where_sql = $this->get_where_sql( $config ) ) {
308
			$query .= ' WHERE ' . $where_sql;
309
		}
310
311
		$count = $wpdb->get_var( $query );
312
313
		return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
314
	}
315
316 View Code Duplication
	private function get_where_sql( $config ) {
317
		global $wpdb;
318
319
		$query = "meta_key = '{$wpdb->prefix}capabilities'";
320
321
		// config is a list of user IDs to sync
322
		if ( is_array( $config ) ) {
323
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
324
		}
325
326
		return $query;
327
	}
328
329
	function get_full_sync_actions() {
330
		return array( 'jetpack_full_sync_users' );
331
	}
332
333
	function get_initial_sync_user_config() {
334
		global $wpdb;
335
336
		$user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) );
337
338
		if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
339
			return $user_ids;
340
		} else {
341
			return false;
342
		}
343
	}
344
345
	public function expand_users( $args ) {
346
		$user_ids = $args[0];
347
348
		return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
349
	}
350
351
	public function remove_user_from_blog_handler( $user_id, $blog_id ) {
352
		//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
353
		if ( $this->is_add_new_user_to_blog() ) {
354
			return;
355
		}
356
357
		$reassigned_user_id = $this->get_reassigned_network_user_id();
358
359
		//Note that we are in the context of the blog the user is removed from, see https://github.com/WordPress/WordPress/blob/473e1ba73bc5c18c72d7f288447503713d518790/wp-includes/ms-functions.php#L233
360
		/**
361
		 * Fires when a user is removed from a blog on a multisite installation
362
		 *
363
		 * @since 5.4.0
364
		 *
365
		 * @param int $user_id - ID of the removed user
366
		 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
367
		 */
368
		do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
369
	}
370
371
	private function is_add_new_user_to_blog() {
372
		return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
373
	}
374
375
	private function is_add_user_to_blog() {
376
		return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
377
	}
378
379
	private function is_create_user() {
380
		$functions = array(
381
			'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
382
			'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
383
			'wp_insert_user', // Used to suppress jetpack_sync_save_user in save_user_cap_handler and save_user_role_handler when user registered on single site
384
		);
385
386
		return Jetpack::is_function_in_backtrace( $functions );
387
	}
388
389
	private function get_reassigned_network_user_id() {
390
		$backtrace = debug_backtrace( false );
391
		foreach ( $backtrace as $call ) {
392
			if (
393
				'remove_user_from_blog' === $call['function'] &&
394
				3 === count( $call['args'] )
395
			) {
396
				return $call['args'][2];
397
			}
398
		}
399
400
		return false;
401
	}
402
}
403