Completed
Push — update/search/migrate-to-wpes-... ( 9fd1a1...2610ca )
by Alex
08:55
created

Jetpack_Sync_Module_Users::expand_action()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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