Completed
Push — feature/determine_email_change ( 336b63 )
by
unknown
10:55
created

deleted_user_meta_handler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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