|
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( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 ); |
|
34
|
|
|
add_action( 'jetpack_deleted_user', $callable, 10, 3 ); |
|
35
|
|
|
add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 ); |
|
36
|
|
|
add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 ); |
|
37
|
|
|
|
|
38
|
|
|
// user authentication |
|
39
|
|
|
add_action( 'wp_login', $callable, 10, 2 ); |
|
40
|
|
|
add_action( 'wp_login_failed', $callable, 10, 2 ); |
|
41
|
|
|
add_action( 'wp_logout', $callable, 10, 0 ); |
|
42
|
|
|
add_action( 'wp_masterbar_logout', $callable, 10, 0 ); |
|
43
|
|
|
|
|
44
|
|
|
// listen for meta changes ( locale, roles and capabilities ) |
|
45
|
|
|
$this->init_listeners_for_meta_type( 'user', $callable ); |
|
46
|
|
|
$this->init_meta_whitelist_handler( 'user', array( $this, 'filter_meta' ) ); |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
public function filter_meta( $args ) { |
|
50
|
|
|
if ( $this->is_whitelisted_user_meta( $args[2] ) ) { |
|
51
|
|
|
return $args; |
|
52
|
|
|
} |
|
53
|
|
|
return false; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
public function is_whitelisted_user_meta( $meta_key ) { |
|
57
|
|
|
$user_meta_keys = (array) Jetpack_Sync_Settings::get_setting( 'user_meta_whitelist' ); |
|
58
|
|
|
return in_array( $meta_key, $user_meta_keys ); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
|
|
62
|
|
|
|
|
63
|
|
|
public function init_full_sync_listeners( $callable ) { |
|
64
|
|
|
add_action( 'jetpack_full_sync_users', $callable ); |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
public function init_before_send() { |
|
68
|
|
|
add_filter( 'jetpack_sync_before_send_jetpack_sync_add_user', array( $this, 'expand_user' ) ); |
|
69
|
|
|
add_filter( 'jetpack_sync_before_send_jetpack_sync_register_user', array( $this, 'expand_user' ) ); |
|
70
|
|
|
add_filter( 'jetpack_sync_before_send_jetpack_sync_save_user', array( $this, 'expand_user' ), 10, 2 ); |
|
71
|
|
|
add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 ); |
|
72
|
|
|
add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 ); |
|
73
|
|
|
|
|
74
|
|
|
// full sync |
|
75
|
|
|
add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) ); |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
public function sanitize_user_and_expand( $user ) { |
|
79
|
|
|
$user = $this->sanitize_user( $user ); |
|
80
|
|
|
|
|
81
|
|
|
return $this->add_to_user( $user ); |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
public function sanitize_user( $user ) { |
|
85
|
|
|
// this create a new user object and stops the passing of the object by reference. |
|
86
|
|
|
$user = unserialize( serialize( $user ) ); |
|
87
|
|
|
|
|
88
|
|
|
if ( is_object( $user ) && is_object( $user->data ) ) { |
|
89
|
|
|
unset( $user->data->user_pass ); |
|
90
|
|
|
unset( $user->cap_key ); // |
|
91
|
|
|
unset( $user->allcaps ); // We should be able to constuct this from the roles data that we have. |
|
92
|
|
|
|
|
93
|
|
|
if ( isset( $user->filter ) ) { |
|
94
|
|
|
unset( $user->filter ); |
|
95
|
|
|
} |
|
96
|
|
|
} |
|
97
|
|
|
return $user; |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
|
public function add_to_user( $user ) { |
|
101
|
|
|
$user->allowed_mime_types = get_allowed_mime_types( $user ); |
|
102
|
|
|
return $user; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
public function expand_user( $args ) { |
|
106
|
|
|
list( $user ) = $args; |
|
107
|
|
|
|
|
108
|
|
|
if ( $user ) { |
|
109
|
|
|
|
|
110
|
|
|
return array( $this->sanitize_user_and_expand( $user ) ); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
return false; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
public function expand_login_username( $args ) { |
|
117
|
|
|
list( $login, $user ) = $args; |
|
118
|
|
|
$user = $this->sanitize_user( $user ); |
|
119
|
|
|
|
|
120
|
|
|
return array( $login, $user ); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
public function expand_logout_username( $args, $user_id ) { |
|
124
|
|
|
$user = get_userdata( $user_id ); |
|
125
|
|
|
$user = $this->sanitize_user( $user ); |
|
126
|
|
|
$login = ''; |
|
127
|
|
|
if ( is_object( $user ) && is_object( $user->data ) ) { |
|
128
|
|
|
$login = $user->data->user_login; |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
return array( $login, $user ); |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) { |
|
135
|
|
|
$is_multisite = is_multisite(); |
|
136
|
|
|
/** |
|
137
|
|
|
* Fires when a user is deleted on a site |
|
138
|
|
|
* |
|
139
|
|
|
* @since 5.4.0 |
|
140
|
|
|
* |
|
141
|
|
|
* @param int $deleted_user_id - ID of the deleted user |
|
142
|
|
|
* @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any) |
|
143
|
|
|
* @param bool $is_multisite - Whether this site is a multisite installation |
|
144
|
|
|
*/ |
|
145
|
|
|
do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite ); |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
public function edited_user_handler( $user_id ) { |
|
149
|
|
|
/** |
|
150
|
|
|
* Fires when a user is edited on a site |
|
151
|
|
|
* |
|
152
|
|
|
* @since 5.4.0 |
|
153
|
|
|
* |
|
154
|
|
|
* @param int $user_id - ID of the edited user |
|
155
|
|
|
*/ |
|
156
|
|
|
do_action( 'jetpack_user_edited', $user_id ); |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
function save_user_handler( $user_id, $old_user_data = null ) { |
|
160
|
|
|
// ensure we only sync users who are members of the current blog |
|
161
|
|
|
if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) { |
|
162
|
|
|
return; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
$user = $this->sanitize_user( get_user_by( 'id', $user_id ) ); |
|
166
|
|
|
|
|
167
|
|
|
// Older versions of WP don't pass the old_user_data in ->data |
|
168
|
|
|
if ( isset( $old_user_data->data ) ) { |
|
169
|
|
|
$old_user = $old_user_data->data; |
|
170
|
|
|
} else { |
|
171
|
|
|
$old_user = $old_user_data; |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
if ( $old_user !== null ) { |
|
175
|
|
|
unset( $old_user->user_pass ); |
|
176
|
|
|
if ( serialize( $old_user ) === serialize( $user->data ) ) { |
|
177
|
|
|
return; |
|
178
|
|
|
} |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
if ( 'user_register' === current_filter() ) { |
|
182
|
|
|
/** |
|
183
|
|
|
* Fires when a new user is registered on a site |
|
184
|
|
|
* |
|
185
|
|
|
* @since 4.9.0 |
|
186
|
|
|
* |
|
187
|
|
|
* @param object The WP_User object |
|
188
|
|
|
*/ |
|
189
|
|
|
do_action( 'jetpack_sync_register_user', $user ); |
|
190
|
|
|
|
|
191
|
|
|
return; |
|
192
|
|
|
} |
|
193
|
|
|
/* MU Sites add users instead of register them to sites */ |
|
194
|
|
|
if ( 'add_user_to_blog' === current_filter() ) { |
|
195
|
|
|
/** |
|
196
|
|
|
* Fires when a new user is added to a site. (WordPress Multisite) |
|
197
|
|
|
* |
|
198
|
|
|
* @since 4.9.0 |
|
199
|
|
|
* |
|
200
|
|
|
* @param object The WP_User object |
|
201
|
|
|
*/ |
|
202
|
|
|
do_action( 'jetpack_sync_add_user', $user ); |
|
203
|
|
|
|
|
204
|
|
|
return; |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
/** |
|
208
|
|
|
* Fires when the client needs to sync an updated user |
|
209
|
|
|
* |
|
210
|
|
|
* @since 4.2.0 |
|
211
|
|
|
* |
|
212
|
|
|
* @param object The WP_User object |
|
213
|
|
|
*/ |
|
214
|
|
|
do_action( 'jetpack_sync_save_user', $user ); |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
|
|
function save_user_role_handler( $user_id, $role, $old_roles = null ) { |
|
218
|
|
|
//The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both |
|
219
|
|
|
if ( $this->is_create_user() || $this->is_add_user_to_blog() ) { |
|
220
|
|
|
return; |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
$user = $this->sanitize_user( get_user_by( 'id', $user_id ) ); |
|
224
|
|
|
/** |
|
225
|
|
|
* Fires when the client needs to sync an updated user |
|
226
|
|
|
* |
|
227
|
|
|
* @since 4.2.0 |
|
228
|
|
|
* |
|
229
|
|
|
* @param object The WP_User object |
|
230
|
|
|
*/ |
|
231
|
|
|
do_action( 'jetpack_sync_save_user', $user ); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { |
|
235
|
|
|
global $wpdb; |
|
236
|
|
|
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 ); |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
public function estimate_full_sync_actions( $config ) { |
|
240
|
|
|
global $wpdb; |
|
241
|
|
|
|
|
242
|
|
|
$query = "SELECT count(*) FROM $wpdb->usermeta"; |
|
243
|
|
|
|
|
244
|
|
|
if ( $where_sql = $this->get_where_sql( $config ) ) { |
|
245
|
|
|
$query .= ' WHERE ' . $where_sql; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
$count = $wpdb->get_var( $query ); |
|
249
|
|
|
return (int) ceil( $count / self::ARRAY_CHUNK_SIZE ); |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
private function get_where_sql( $config ) { |
|
253
|
|
|
global $wpdb; |
|
254
|
|
|
$prefix = $wpdb->get_blog_prefix(); |
|
255
|
|
|
$query = "meta_key = '{$prefix}capabilities'"; |
|
256
|
|
|
|
|
257
|
|
|
// config is a list of user IDs to sync |
|
258
|
|
|
if ( is_array( $config ) ) { |
|
259
|
|
|
$query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')'; |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
return $query; |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
function get_full_sync_actions() { |
|
266
|
|
|
return array( 'jetpack_full_sync_users' ); |
|
267
|
|
|
} |
|
268
|
|
|
|
|
269
|
|
|
function get_initial_sync_user_config() { |
|
270
|
|
|
global $wpdb; |
|
271
|
|
|
$prefix = $wpdb->get_blog_prefix(); |
|
272
|
|
|
$user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) ); |
|
273
|
|
|
|
|
274
|
|
|
if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) { |
|
275
|
|
|
return $user_ids; |
|
276
|
|
|
} else { |
|
277
|
|
|
return false; |
|
278
|
|
|
} |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
public function expand_users( $args ) { |
|
282
|
|
|
$user_ids = $args[0]; |
|
283
|
|
|
return array( |
|
284
|
|
|
'users' => array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) ), |
|
285
|
|
|
'meta' => $this->get_metadata( $user_ids, 'user', Jetpack_Sync_Settings::get_setting( 'user_meta_whitelist' ) ), |
|
286
|
|
|
); |
|
287
|
|
|
return ; |
|
|
|
|
|
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
public function remove_user_from_blog_handler( $user_id, $blog_id ) { |
|
291
|
|
|
//User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114 |
|
292
|
|
|
if ( $this->is_add_new_user_to_blog() ) { |
|
293
|
|
|
return; |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
$reassigned_user_id = $this->get_reassigned_network_user_id(); |
|
297
|
|
|
|
|
298
|
|
|
//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 |
|
299
|
|
|
/** |
|
300
|
|
|
* Fires when a user is removed from a blog on a multisite installation |
|
301
|
|
|
* |
|
302
|
|
|
* @since 5.4.0 |
|
303
|
|
|
* |
|
304
|
|
|
* @param int $user_id - ID of the removed user |
|
305
|
|
|
* @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any) |
|
306
|
|
|
*/ |
|
307
|
|
|
do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id ); |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
private function is_add_new_user_to_blog() { |
|
311
|
|
|
return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' ); |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
private function is_add_user_to_blog() { |
|
315
|
|
|
return Jetpack::is_function_in_backtrace( 'add_user_to_blog' ); |
|
316
|
|
|
} |
|
317
|
|
|
|
|
318
|
|
|
private function is_create_user() { |
|
319
|
|
|
$functions = array( |
|
320
|
|
|
'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site |
|
321
|
|
|
'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site |
|
322
|
|
|
'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 |
|
323
|
|
|
); |
|
324
|
|
|
|
|
325
|
|
|
return Jetpack::is_function_in_backtrace( $functions ); |
|
326
|
|
|
} |
|
327
|
|
|
|
|
328
|
|
|
private function get_reassigned_network_user_id() { |
|
329
|
|
|
$backtrace = debug_backtrace( false ); |
|
330
|
|
|
foreach ( $backtrace as $call ) { |
|
331
|
|
|
if ( |
|
332
|
|
|
'remove_user_from_blog' === $call['function'] && |
|
333
|
|
|
3 === count( $call['args'] ) |
|
334
|
|
|
) { |
|
335
|
|
|
return $call['args'][2]; |
|
336
|
|
|
} |
|
337
|
|
|
} |
|
338
|
|
|
|
|
339
|
|
|
return false; |
|
340
|
|
|
} |
|
341
|
|
|
} |
|
342
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return,dieorexitstatements that have been added for debug purposes.In the above example, the last
return falsewill never be executed, because a return statement has already been met in every possible execution path.