Completed
Push — update/sync-save-user-locale ( 8cf80f...727212 )
by
unknown
26:30 queued 07:33
created

Jetpack_Sync_Module_Users::is_delete_user()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
	const DEFAULT_FLAGS = array(
7
		'password_changed' => false,
8
		'role_changed'     => false,
9
		'previous_role'    => false,
10
		'locale_changed'   => false,
11
		'capabilities_changed' => false,
12
	);
13
14
	protected $flags = array();
15
16
	function name() {
17
		return 'users';
18
	}
19
20
	// this is here to support the backfill API
21
	public function get_object_by_id( $object_type, $id ) {
22
		if ( $object_type === 'user' && $user = get_user_by( 'id', intval( $id ) ) ) {
23
			return $this->sanitize_user_and_expand( $user );
24
		}
25
26
		return false;
27
	}
28
29
	public function init_listeners( $callable ) {
30
31
		// users
32
		add_action( 'user_register', array( $this, 'user_register_handler' ) );
33
		add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
34
35
		add_action( 'add_user_to_blog', array( $this, 'add_user_to_blog_handler' ) );
36
		add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
37
38
		add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
39
		add_action( 'jetpack_sync_register_user', array( $this, 'clear_flags' ), 11 );
40
41
		add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
42
		add_action( 'jetpack_sync_save_user', array( $this, 'clear_flags' ), 11 );
43
44
		add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
45
		add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
46
47
		add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
48
		add_action( 'jetpack_deleted_user', $callable, 10, 3 );
49
		add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
50
		add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
51
52
		// user roles
53
		add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
54
		add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
55
		add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
56
57
		// user capabilities
58
		add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
59
		add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
60
		add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
61
62
		// user authentication
63
		add_action( 'wp_login', $callable, 10, 2 );
64
		add_action( 'wp_logout', $callable, 10, 0 );
65
		add_action( 'wp_masterbar_logout', $callable, 10, 0 );
66
	}
67
68
	public function init_full_sync_listeners( $callable ) {
69
		add_action( 'jetpack_full_sync_users', $callable );
70
	}
71
72
	public function init_before_send() {
73
		add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_action' ) );
74
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_action' ) );
75
76
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_action' ) );
77
78
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
79
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
80
81
		// full sync
82
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
83
	}
84
85
	private function get_user( $user ) {
86
		if ( is_numeric( $user ) ) {
87
			$user = get_user_by( 'id', $user );
88
		}
89
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

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