Completed
Push — add/sync-user-password-changes ( fb1202 )
by
unknown
08:16
created

Jetpack_Sync_Module_Users::save_user_handler()   C

Complexity

Conditions 9
Paths 27

Size

Total Lines 65
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 25
nc 27
nop 2
dl 0
loc 65
rs 6.4766
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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, 10, 2 );
27
		add_action( 'jetpack_updated_user_password', $callable );
28
29
		//Edit user info, see https://github.com/WordPress/WordPress/blob/c05f1dc805bddcc0e76fd90c4aaf2d9ea76dc0fb/wp-admin/user-edit.php#L126
30
		add_action( 'personal_options_update', array( $this, 'edited_user_handler' ) );
31
		add_action( 'edit_user_profile_update', array( $this, 'edited_user_handler' ) );
32
		add_action( 'jetpack_user_edited', $callable );
33
34
		add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
35
		add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
36
37
		add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
38
		add_action( 'jetpack_deleted_user', $callable, 10, 3 );
39
		add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
40
		add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
41
42
		// user roles
43
		add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
44
		add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
45
		add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
46
47
		// user capabilities
48
		add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
49
		add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
50
		add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
51
52
		// user authentication
53
		add_action( 'wp_login', $callable, 10, 2 );
54
		add_action( 'wp_logout', $callable, 10, 0 );
55
		add_action( 'wp_masterbar_logout', $callable, 10, 0 );
56
	}
57
58
	public function init_full_sync_listeners( $callable ) {
59
		add_action( 'jetpack_full_sync_users', $callable );
60
	}
61
62
	public function init_before_send() {
63
		add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_user' ) );
64
		add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_user' ) );
65
		add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_user' ) );
66
		add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
67
		add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
68
69
		// full sync
70
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
71
	}
72
73
	public function sanitize_user_and_expand( $user ) {
74
		$user = $this->get_user( $user );
75
		$user = $this->add_to_user( $user );
76
		return $this->sanitize_user( $user );
77
	}
78
79
	private function get_user( $user ) {
80
		if ( $user && ! is_object( $user ) && is_numeric( $user ) ) {
81
			$user = get_user_by( 'id', $user );
82
		}
83
		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...
84
			return $user;
85
		}
86
		return null;
87
	}
88
89
	public function sanitize_user( $user ) {
90
		$user = $this->get_user( $user );
91
		// this create a new user object and stops the passing of the object by reference.
92
		$user = unserialize( serialize( $user ) );
93
94
		if ( is_object( $user ) && is_object( $user->data ) ) {
95
			unset( $user->data->user_pass );
96
		}
97
		if ( $user ) {
98
			$user->allcaps = $this->get_real_user_capabilities( $user );
99
		}
100
		return $user;
101
	}
102
103
	public function add_to_user( $user ) {
104
		if ( ! is_object( $user ) ) {
105
			return null;
106
		}
107
		$user->allowed_mime_types = get_allowed_mime_types( $user );
108
109
		if ( function_exists( 'get_user_locale' ) ) {
110
111
			// Only set the user locale if it is different from the site local
112
			if ( get_locale() !== get_user_locale( $user->ID ) ) {
113
				$user->locale = get_user_locale( $user->ID );
114
			}
115
		}
116
117
		return $user;
118
	}
119
120
	public function get_real_user_capabilities( $user ) {
121
		$user_capabilities = array();
122
		if ( is_wp_error( $user ) ) {
123
			return $user_capabilities;
124
		}
125
		foreach( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
126
			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...
127
				$user_capabilities[ $capability ] = true;
128
			}
129
		}
130
		return $user_capabilities;
131
	}
132
133
	public function expand_user( $args ) {
134
		list( $user ) = $args;
135
		if ( $user ) {
136
			if ( isset( $args[1] ) ) { // if state is available also send state.
137
				return array( $this->add_to_user( $user ), $args[1] );
138
			}
139
			return array( $this->add_to_user( $user ) );
140
		}
141
142
		return false;
143
	}
144
145
	public function expand_login_username( $args ) {
146
		list( $login, $user ) = $args;
147
		$user = $this->sanitize_user( $user );
148
149
		return array( $login, $user );
150
	}
151
152
	public function expand_logout_username( $args, $user_id ) {
153
		$user  = get_userdata( $user_id );
154
		$user  = $this->sanitize_user( $user );
155
156
		$login = '';
157
		if ( is_object( $user ) && is_object( $user->data ) ) {
158
			$login = $user->data->user_login;
159
		}
160
		// if we don't have a user here lets not send anything.
161
		if ( empty( $login ) ) {
162
			return false;
163
		}
164
165
		return array( $login, $user );
166
	}
167
168
	public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
169
		$is_multisite = is_multisite();
170
		/**
171
		 * Fires when a user is deleted on a site
172
		 *
173
		 * @since 5.4.0
174
		 *
175
		 * @param int $deleted_user_id - ID of the deleted user
176
		 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
177
		 * @param bool $is_multisite - Whether this site is a multisite installation
178
		 */
179
		do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
180
	}
181
182
	public function edited_user_handler( $user_id ) {
183
		/**
184
		 * Fires when a user is edited on a site
185
		 *
186
		 * @since 5.4.0
187
		 *
188
		 * @param int $user_id - ID of the edited user
189
		 */
190
		do_action( 'jetpack_user_edited', $user_id );
191
	}
192
193
	function save_user_handler( $user_id, $old_user_data = null ) {
194
		// ensure we only sync users who are members of the current blog
195
		if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
196
			return;
197
		}
198
		$raw_user = get_user_by( 'id', $user_id );
199
		$user = $this->sanitize_user( $raw_user );
200
		$user_password_changed = false;
201
202
		// Older versions of WP don't pass the old_user_data in ->data
203
		if ( isset( $old_user_data->data ) ) {
204
			$old_user = $old_user_data->data;
205
		} else {
206
			$old_user = $old_user_data;
207
		}
208
209
		if ( $old_user !== null ) {
210
			if ( $raw_user->user_pass !== $old_user->user_pass ) {
211
				$user_password_changed = true;
212
			}
213
			unset( $old_user->user_pass );
214
			if ( serialize( $old_user ) === serialize( $user->data ) ) {
215
				if ( $user_password_changed ) {
216
					do_action( 'jetpack_updated_user_password', $user );
217
				}
218
				return;
219
			}
220
		}
221
222
		if ( 'user_register' === current_filter() ) {
223
			/**
224
			 * Fires when a new user is registered on a site
225
			 *
226
			 * @since 4.9.0
227
			 *
228
			 * @param object The WP_User object
229
			 */
230
			do_action( 'jetpack_sync_register_user', $user );
231
232
			return;
233
		}
234
		/* MU Sites add users instead of register them to sites */
235
		if ( 'add_user_to_blog' === current_filter() ) {
236
			/**
237
			 * Fires when a new user is added to a site. (WordPress Multisite)
238
			 *
239
			 * @since 4.9.0
240
			 *
241
			 * @param object The WP_User object
242
			 */
243
			do_action( 'jetpack_sync_add_user', $user );
244
245
			return;
246
		}
247
248
		/**
249
		 * Fires when the client needs to sync an updated user
250
		 *
251
		 * @since 4.2.0
252
		 *
253
		 * @param object The WP_User object
254
		 * @param array state
255
		 */
256
		do_action( 'jetpack_sync_save_user', $user, array( 'password_changed' => $user_password_changed ) );
257
	}
258
259
	function save_user_role_handler( $user_id, $role, $old_roles = null ) {
260
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
261
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
262
			return;
263
		}
264
265
		$user = $this->sanitize_user( get_user_by( 'id', $user_id ) );
266
		/**
267
		 * Fires when the client needs to sync an updated user
268
		 *
269
		 * @since 4.2.0
270
		 *
271
		 * @param object The WP_User object
272
	 	 * @param array state
273
		 */
274
		do_action( 'jetpack_sync_save_user', $user, array( 'role_changed' => $role, 'previous_roles' => $old_roles ) );
275
	}
276
277
	function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
278
		if ( $meta_key === 'locale' ) {
279
			if ( current_filter() === 'deleted_user_meta' ) {
280
				/**
281
				 * Allow listeners to listen for user local delete changes
282
				 *
283
				 * @since 4.8.0
284
				 *
285
				 * @param int $user_id - The ID of the user whos locale is being deleted
286
				 */
287
				do_action( 'jetpack_sync_user_locale_delete', $user_id );
288
			} else {
289
				/**
290
				 * Allow listeners to listen for user local changes
291
				 *
292
				 * @since 4.8.0
293
				 *
294
				 * @param int $user_id - The ID of the user whos locale is being changed
295
				 * @param int $value - The value of the new locale
296
				 */
297
				do_action( 'jetpack_sync_user_locale', $user_id, $value );
298
			}
299
		}
300
		$this->save_user_cap_handler( $meta_id, $user_id, $meta_key, $value );
301
	}
302
303
	function save_user_cap_handler( $meta_id, $user_id, $meta_key, $capabilities ) {
304
		//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
305
		if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
306
			return;
307
		}
308
309
		// if a user is currently being removed as a member of this blog, we don't fire the event
310
		if ( current_filter() === 'deleted_user_meta'
311
		     &&
312
		     preg_match( '/capabilities|user_level/', $meta_key )
313
		     &&
314
		     ! is_user_member_of_blog( $user_id, get_current_blog_id() )
315
		) {
316
			return;
317
		}
318
319
		$user = get_user_by( 'id', $user_id );
320
		if ( $meta_key === $user->cap_key ) {
321
			/**
322
			 * Fires when the client needs to sync an updated user
323
			 *
324
			 * @since 4.2.0
325
			 *
326
			 * @param object The Sanitized WP_User object
327
		     * @param array state Since 5.8
328
			 */
329
			do_action( 'jetpack_sync_save_user', $this->sanitize_user( $user ),
330
				array( 'capabilities_action' => current_filter(), 'capabilities' => $capabilities )
331
			);
332
333
		}
334
	}
335
336
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
337
		global $wpdb;
338
339
		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 );
340
	}
341
342
	public function estimate_full_sync_actions( $config ) {
343
		global $wpdb;
344
345
		$query = "SELECT count(*) FROM $wpdb->usermeta";
346
347
		if ( $where_sql = $this->get_where_sql( $config ) ) {
348
			$query .= ' WHERE ' . $where_sql;
349
		}
350
351
		$count = $wpdb->get_var( $query );
352
353
		return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
354
	}
355
356 View Code Duplication
	private function get_where_sql( $config ) {
357
		global $wpdb;
358
359
		$query = "meta_key = '{$wpdb->prefix}capabilities'";
360
361
		// config is a list of user IDs to sync
362
		if ( is_array( $config ) ) {
363
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
364
		}
365
366
		return $query;
367
	}
368
369
	function get_full_sync_actions() {
370
		return array( 'jetpack_full_sync_users' );
371
	}
372
373
	function get_initial_sync_user_config() {
374
		global $wpdb;
375
376
		$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 ) );
377
378
		if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
379
			return $user_ids;
380
		} else {
381
			return false;
382
		}
383
	}
384
385
	public function expand_users( $args ) {
386
		$user_ids = $args[0];
387
388
		return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
389
	}
390
391
	public function remove_user_from_blog_handler( $user_id, $blog_id ) {
392
		//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
393
		if ( $this->is_add_new_user_to_blog() ) {
394
			return;
395
		}
396
397
		$reassigned_user_id = $this->get_reassigned_network_user_id();
398
399
		//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
400
		/**
401
		 * Fires when a user is removed from a blog on a multisite installation
402
		 *
403
		 * @since 5.4.0
404
		 *
405
		 * @param int $user_id - ID of the removed user
406
		 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
407
		 */
408
		do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
409
	}
410
411
	private function is_add_new_user_to_blog() {
412
		return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
413
	}
414
415
	private function is_add_user_to_blog() {
416
		return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
417
	}
418
419
	private function is_create_user() {
420
		$functions = array(
421
			'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
422
			'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
423
			'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
424
		);
425
426
		return Jetpack::is_function_in_backtrace( $functions );
427
	}
428
429
	private function get_reassigned_network_user_id() {
430
		$backtrace = debug_backtrace( false );
431
		foreach ( $backtrace as $call ) {
432
			if (
433
				'remove_user_from_blog' === $call['function'] &&
434
				3 === count( $call['args'] )
435
			) {
436
				return $call['args'][2];
437
			}
438
		}
439
440
		return false;
441
	}
442
}
443