Completed
Push — branch-5.9 ( c4d4d4...51a07e )
by
unknown
17:14 queued 07:18
created

class.jetpack-cli.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
WP_CLI::add_command( 'jetpack', 'Jetpack_CLI' );
4
5
/**
6
 * Control your local Jetpack installation.
7
 */
8
class Jetpack_CLI extends WP_CLI_Command {
9
10
	// Aesthetics
11
	public $green_open  = "\033[32m";
12
	public $red_open    = "\033[31m";
13
	public $yellow_open = "\033[33m";
14
	public $color_close = "\033[0m";
15
16
	/**
17
	 * Get Jetpack Details
18
	 *
19
	 * ## OPTIONS
20
	 *
21
	 * empty: Leave it empty for basic stats
22
	 *
23
	 * full: View full stats.  It's the data from the heartbeat
24
	 *
25
	 * ## EXAMPLES
26
	 *
27
	 * wp jetpack status
28
	 * wp jetpack status full
29
	 *
30
	 */
31
	public function status( $args, $assoc_args ) {
32
33
		WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
34
35
		if ( ! Jetpack::is_active() ) {
36
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
37
		}
38
39 View Code Duplication
		if ( isset( $args[0] ) && 'full' !== $args[0] ) {
40
			/* translators: %s is a command like "prompt" */
41
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $args[0] ) );
42
		}
43
44
		$master_user_email = Jetpack::get_master_user_email();
45
46
		/*
47
		 * Are they asking for all data?
48
		 *
49
		 * Loop through heartbeat data and organize by priority.
50
		 */
51
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
52
		if ( $all_data ) {
53
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
54
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
55
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
56
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
57
58
			// Heartbeat data
59
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
60
61
			// Get the filtered heartbeat data.
62
			// Filtered so we can color/list by severity
63
			$stats = Jetpack::jetpack_check_heartbeat_data();
64
65
			// Display red flags first
66
			foreach ( $stats['bad'] as $stat => $value ) {
67
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
68
			}
69
70
			// Display caution warnings next
71
			foreach ( $stats['caution'] as $stat => $value ) {
72
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
73
			}
74
75
			// The rest of the results are good!
76
			foreach ( $stats['good'] as $stat => $value ) {
77
78
				// Modules should get special spacing for aestetics
79
				if ( strpos( $stat, 'odule-' ) ) {
80
					printf( "%-'.30s %s\n", $stat, $value );
81
					usleep( 4000 ); // For dramatic effect lolz
82
					continue;
83
				}
84
				printf( "%-'.16s %s\n", $stat, $value );
85
				usleep( 4000 ); // For dramatic effect lolz
86
			}
87
		} else {
88
			// Just the basics
89
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
90
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
91
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
92
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
93
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
94
		}
95
	}
96
97
	/**
98
	 * Tests the active connection
99
	 *
100
	 * Does a two-way test to verify that the local site can communicate with remote Jetpack/WP.com servers and that Jetpack/WP.com servers can talk to the local site.
101
	 *
102
	 * ## EXAMPLES
103
	 *
104
	 * wp jetpack test-connection
105
	 *
106
	 * @subcommand test-connection
107
	 */
108
	public function test_connection( $args, $assoc_args ) {
109
110
		WP_CLI::line( sprintf( __( 'Testing connection for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
111
112
		if ( ! Jetpack::is_active() ) {
113
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
114
		}
115
116
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
117
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
118
			Jetpack_Client::WPCOM_JSON_API_VERSION
119
		);
120
121
		if ( is_wp_error( $response ) ) {
122
			/* translators: %1$s is the error code, %2$s is the error message */
123
			WP_CLI::error( sprintf( __( 'Failed to test connection (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ) );
124
		}
125
126
		$body = wp_remote_retrieve_body( $response );
127
		if ( ! $body ) {
128
			WP_CLI::error( __( 'Failed to test connection (empty response body)', 'jetpack' ) );
129
		}
130
131
		$result = json_decode( $body );
132
		$is_connected = (bool) $result->connected;
133
		$message = $result->message;
134
135
		if ( $is_connected ) {
136
			WP_CLI::success( $message );
137
		} else {
138
			WP_CLI::error( $message );
139
		}
140
	}
141
142
	/**
143
	 * Disconnect Jetpack Blogs or Users
144
	 *
145
	 * ## OPTIONS
146
	 *
147
	 * blog: Disconnect the entire blog.
148
	 *
149
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
150
	 *
151
	 * Please note, the primary account that the blog is connected
152
	 * to WordPress.com with cannot be disconnected without
153
	 * disconnecting the entire blog.
154
	 *
155
	 * ## EXAMPLES
156
	 *
157
	 * wp jetpack disconnect blog
158
	 * wp jetpack disconnect user 13
159
	 * wp jetpack disconnect user username
160
	 * wp jetpack disconnect user [email protected]
161
	 *
162
	 * @synopsis <blog|user> [<user_identifier>]
163
	 */
164
	public function disconnect( $args, $assoc_args ) {
165
		if ( ! Jetpack::is_active() ) {
166
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
167
		}
168
169
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
170
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
171
			/* translators: %s is a command like "prompt" */
172
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
173
		}
174
175
		if ( in_array( $action, array( 'user' ) ) ) {
176
			if ( isset( $args[1] ) ) {
177
				$user_id = $args[1];
178
				if ( ctype_digit( $user_id ) ) {
179
					$field = 'id';
180
					$user_id = (int) $user_id;
181
				} elseif ( is_email( $user_id ) ) {
182
					$field = 'email';
183
					$user_id = sanitize_user( $user_id, true );
184
				} else {
185
					$field = 'login';
186
					$user_id = sanitize_user( $user_id, true );
187
				}
188
				if ( ! $user = get_user_by( $field, $user_id ) ) {
189
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
190
				}
191
			} else {
192
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
193
			}
194
		}
195
196
		switch ( $action ) {
197
			case 'blog':
198
				Jetpack::log( 'disconnect' );
199
				Jetpack::disconnect();
200
				WP_CLI::success( sprintf(
201
					__( 'Jetpack has been successfully disconnected for %s.', 'jetpack' ),
202
					esc_url( get_site_url() )
203
				) );
204
				break;
205
			case 'user':
206
				if ( Jetpack::unlink_user( $user->ID ) ) {
207
					Jetpack::log( 'unlink', $user->ID );
208
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
209
				} else {
210
					/* translators: %s is a username */
211
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
212
				}
213
				break;
214
			case 'prompt':
215
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
216
				break;
217
		}
218
	}
219
220
	/**
221
	 * Reset Jetpack options and settings to default
222
	 *
223
	 * ## OPTIONS
224
	 *
225
	 * modules: Resets modules to default state ( get_default_modules() )
226
	 *
227
	 * options: Resets all Jetpack options except:
228
	 *  - All private options (Blog token, user token, etc...)
229
	 *  - id (The Client ID/WP.com Blog ID of this site)
230
	 *  - master_user
231
	 *  - version
232
	 *  - activated
233
	 *
234
	 * ## EXAMPLES
235
	 *
236
	 * wp jetpack reset options
237
	 * wp jetpack reset modules
238
	 *
239
	 * @synopsis <modules|options>
240
	 */
241
	public function reset( $args, $assoc_args ) {
242
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
243 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
244
			/* translators: %s is a command like "prompt" */
245
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
246
		}
247
248
		// Are you sure?
249
		jetpack_cli_are_you_sure();
250
251
		switch ( $action ) {
252
			case 'options':
253
				$options_to_reset = Jetpack_Options::get_options_for_reset();
254
255
				// Reset the Jetpack options
256
				WP_CLI::line( sprintf(
257
					__( "Resetting Jetpack Options for %s...\n", "jetpack" ),
258
					esc_url( get_site_url() )
259
				) );
260
				sleep(1); // Take a breath
261
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
262
					Jetpack_Options::delete_option( $option_to_reset );
263
					usleep( 100000 );
264
					/* translators: This is the result of an action. The option named %s was reset */
265
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
266
				}
267
268
				// Reset the WP options
269
				WP_CLI::line( __( "Resetting the jetpack options stored in wp_options...\n", "jetpack" ) );
270
				usleep( 500000 ); // Take a breath
271
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
272
					delete_option( $option_to_reset );
273
					usleep( 100000 );
274
					/* translators: This is the result of an action. The option named %s was reset */
275
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
276
				}
277
278
				// Reset to default modules
279
				WP_CLI::line( __( "Resetting default modules...\n", "jetpack" ) );
280
				usleep( 500000 ); // Take a breath
281
				$default_modules = Jetpack::get_default_modules();
282
				Jetpack::update_active_modules( $default_modules );
283
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
284
285
				// Jumpstart option is special
286
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
287
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
288
				break;
289 View Code Duplication
			case 'modules':
290
				$default_modules = Jetpack::get_default_modules();
291
				Jetpack::update_active_modules( $default_modules );
292
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
293
				break;
294
			case 'prompt':
295
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
296
				break;
297
		}
298
	}
299
300
	/**
301
	 * Manage Jetpack Modules
302
	 *
303
	 * ## OPTIONS
304
	 *
305
	 * list          : View all available modules, and their status.
306
	 * activate all  : Activate all modules
307
	 * deactivate all: Deactivate all modules
308
	 *
309
	 * activate   <module_slug> : Activate a module.
310
	 * deactivate <module_slug> : Deactivate a module.
311
	 * toggle     <module_slug> : Toggle a module on or off.
312
	 *
313
	 * ## EXAMPLES
314
	 *
315
	 * wp jetpack module list
316
	 * wp jetpack module activate stats
317
	 * wp jetpack module deactivate stats
318
	 * wp jetpack module toggle stats
319
	 *
320
	 * wp jetpack module activate all
321
	 * wp jetpack module deactivate all
322
	 *
323
	 * @synopsis <list|activate|deactivate|toggle> [<module_name>]
324
	 */
325
	public function module( $args, $assoc_args ) {
326
		$action = isset( $args[0] ) ? $args[0] : 'list';
327
		if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) {
328
			/* translators: %s is a command like "prompt" */
329
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
330
		}
331
		if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) {
332
			if ( isset( $args[1] ) ) {
333
				$module_slug = $args[1];
334
				if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
335
					WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
336
				}
337
				if ( 'toggle' == $action ) {
338
					$action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate';
339
				}
340
				// Bulk actions
341
				if ( 'all' == $args[1] ) {
342
					$action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all';
343
				}
344
			} else {
345
				WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
346
				$action = 'list';
347
			}
348
		}
349
		switch ( $action ) {
350
			case 'list':
351
				WP_CLI::line( __( 'Available Modules:', 'jetpack' ) );
352
				$modules = Jetpack::get_available_modules();
353
				sort( $modules );
354
				foreach( $modules as $module_slug ) {
355
					if ( 'vaultpress' == $module_slug ) {
356
						continue;
357
					}
358
					$active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' );
359
					WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active );
360
				}
361
				break;
362 View Code Duplication
			case 'activate':
363
				$module = Jetpack::get_module( $module_slug );
364
				Jetpack::log( 'activate', $module_slug );
365
				Jetpack::activate_module( $module_slug, false, false );
366
				WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
367
				break;
368 View Code Duplication
			case 'activate_all':
369
				$modules = Jetpack::get_available_modules();
370
				Jetpack::update_active_modules( $modules );
371
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
372
				break;
373 View Code Duplication
			case 'deactivate':
374
				$module = Jetpack::get_module( $module_slug );
375
				Jetpack::log( 'deactivate', $module_slug );
376
				Jetpack::deactivate_module( $module_slug );
377
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
378
				break;
379
			case 'deactivate_all':
380
				Jetpack::delete_active_modules();
381
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
382
				break;
383
			case 'toggle':
384
				// Will never happen, should have been handled above and changed to activate or deactivate.
385
				break;
386
		}
387
	}
388
389
	/**
390
	 * Manage Protect Settings
391
	 *
392
	 * ## OPTIONS
393
	 *
394
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
395
	 *
396
	 *
397
	 * ## EXAMPLES
398
	 *
399
	 * wp jetpack protect whitelist <ip address>
400
	 * wp jetpack protect whitelist list
401
	 * wp jetpack protect whitelist clear
402
	 *
403
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
404
	 */
405
	public function protect( $args, $assoc_args ) {
406
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
407
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
408
			/* translators: %s is a command like "prompt" */
409
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
410
		}
411
		// Check if module is active
412
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
413
			WP_CLI::error( sprintf( _x( '%s is not active. You can activate it with "wp jetpack module activate %s"', '"wp jetpack module activate" is a command - do not translate', 'jetpack' ), __FUNCTION__, __FUNCTION__ ) );
414
		}
415
		if ( in_array( $action, array( 'whitelist' ) ) ) {
416
			if ( isset( $args[1] ) ) {
417
				$action = 'whitelist';
418
			} else {
419
				$action = 'prompt';
420
			}
421
		}
422
		switch ( $action ) {
423
			case 'whitelist':
424
				$whitelist         = array();
425
				$new_ip            = $args[1];
426
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
427
428
				// Build array of IPs that are already whitelisted.
429
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
430
				// low & high range params for jetpack_protect_ip_address_is_in_range();
431
				foreach( $current_whitelist as $whitelisted ) {
432
433
					// IP ranges
434
					if ( $whitelisted->range ) {
435
436
						// Is it already whitelisted?
437
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
438
							/* translators: %s is an IP address */
439
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
440
							break;
441
						}
442
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
443
444
					} else { // Individual IPs
445
446
						// Check if the IP is already whitelisted (single IP only)
447
						if ( $new_ip == $whitelisted->ip_address ) {
448
							/* translators: %s is an IP address */
449
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
450
							break;
451
						}
452
						$whitelist[] = $whitelisted->ip_address;
453
454
					}
455
				}
456
457
				/*
458
				 * List the whitelist
459
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
460
				 */
461
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
462
					if ( ! empty( $whitelist ) ) {
463
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
464
						foreach ( $whitelist as $ip ) {
465
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
466
						}
467
					} else {
468
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
469
					}
470
					break;
471
				}
472
473
				/*
474
				 * Clear the whitelist
475
				 */
476
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
477 View Code Duplication
					if ( ! empty( $whitelist ) ) {
478
						$whitelist = array();
479
						jetpack_protect_save_whitelist( $whitelist );
480
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
481
					} else {
482
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
483
					}
484
					break;
485
				}
486
487
				// Append new IP to whitelist array
488
				array_push( $whitelist, $new_ip );
489
490
				// Save whitelist if there are no errors
491
				$result = jetpack_protect_save_whitelist( $whitelist );
492
				if ( is_wp_error( $result ) ) {
493
					WP_CLI::error( __( $result, 'jetpack' ) );
494
				}
495
496
				/* translators: %s is an IP address */
497
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
498
				break;
499
			case 'prompt':
500
				WP_CLI::error(
501
					__( 'No command found.', 'jetpack' ) . "\n" .
502
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
503
					_x( 'You can save a range of IPs {low_range}-{high_range}. No spaces allowed.  (example: 1.1.1.1-2.2.2.2)', 'Instructions on how to whitelist IP ranges - low_range/high_range should be translated.', 'jetpack' ) . "\n" .
504
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
505
				);
506
				break;
507
		}
508
	}
509
510
	/**
511
	 * Manage Jetpack Options
512
	 *
513
	 * ## OPTIONS
514
	 *
515
	 * list   : List all jetpack options and their values
516
	 * delete : Delete an option
517
	 *          - can only delete options that are white listed.
518
	 * update : update an option
519
	 *          - can only update option strings
520
	 * get    : get the value of an option
521
	 *
522
	 * ## EXAMPLES
523
	 *
524
	 * wp jetpack options list
525
	 * wp jetpack options get    <option_name>
526
	 * wp jetpack options delete <option_name>
527
	 * wp jetpack options update <option_name> [<option_value>]
528
	 *
529
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
530
	 */
531
	public function options( $args, $assoc_args ) {
532
		$action = isset( $args[0] ) ? $args[0] : 'list';
533
		$safe_to_modify = Jetpack_Options::get_options_for_reset();
534
535
		// Jumpstart is special
536
		array_push( $safe_to_modify, 'jumpstart' );
537
538
		// Is the option flagged as unsafe?
539
		$flagged = ! in_array( $args[1], $safe_to_modify );
540
541 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
542
			/* translators: %s is a command like "prompt" */
543
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
544
		}
545
546
		if ( isset( $args[0] ) ) {
547
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
548
				$action = 'get';
549
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
550
				$action = 'delete';
551 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
552
				$action = 'update';
553
			} else {
554
				$action = 'list';
555
			}
556
		}
557
558
		// Bail if the option isn't found
559
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
560 View Code Duplication
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
561
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
562
		}
563
564
		// Let's print_r the option if it's an array
565
		// Used in the 'get' and 'list' actions
566
		$option = is_array( $option ) ? print_r( $option ) : $option;
567
568
		switch ( $action ) {
569
			case 'get':
570
				WP_CLI::success( "\t" . $option );
571
				break;
572
			case 'delete':
573
				jetpack_cli_are_you_sure( $flagged );
574
575
				Jetpack_Options::delete_option( $args[1] );
576
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
577
				break;
578
			case 'update':
579
				jetpack_cli_are_you_sure( $flagged );
580
581
				// Updating arrays would get pretty tricky...
582
				$value = Jetpack_Options::get_option( $args[1] );
583
				if ( $value && is_array( $value ) ) {
584
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
585
				}
586
587
				Jetpack_Options::update_option( $args[1], $args[2] );
588
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
589
				break;
590
			case 'list':
591
				$options_compact     = Jetpack_Options::get_option_names();
592
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
593
				$options_private     = Jetpack_Options::get_option_names( 'private' );
594
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
595
596
				// Table headers
597
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
598
599
				// List out the options and their values
600
				// Tell them if the value is empty or not
601
				// Tell them if it's an array
602
				foreach ( $options as $option ) {
603
					$value = Jetpack_Options::get_option( $option );
604
					if ( ! $value ) {
605
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
606
						continue;
607
					}
608
609
					if ( ! is_array( $value ) ) {
610
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
611
					} else if ( is_array( $value ) ) {
612
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
613
					}
614
				}
615
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
616
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
617
618
				WP_CLI::success(
619
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
620
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
621
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
622
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
623
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
624
				);
625
				break;
626
		}
627
	}
628
629
	/**
630
	 * Get the status of or start a new Jetpack sync.
631
	 *
632
	 * ## OPTIONS
633
	 *
634
	 * status : Print the current sync status
635
	 * start  : Start a full sync from this site to WordPress.com
636
	 *
637
	 * ## EXAMPLES
638
	 *
639
	 * wp jetpack sync status
640
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
641
	 *
642
	 * @synopsis <status|start> [--<field>=<value>]
643
	 */
644
	public function sync( $args, $assoc_args ) {
645
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
646
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
647
		}
648
649
		$action = isset( $args[0] ) ? $args[0] : 'status';
650
651
		switch ( $action ) {
652
			case 'status':
653
				$status = Jetpack_Sync_Actions::get_sync_status();
654
				$collection = array();
655
				foreach ( $status as $key => $item ) {
656
					$collection[]  = array(
657
						'option' => $key,
658
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
659
					);
660
				}
661
662
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
663
				break;
664
			case 'start':
665
				// Get the original settings so that we can restore them later
666
				$original_settings = Jetpack_Sync_Settings::get_settings();
667
668
				// Initialize sync settigns so we can sync as quickly as possible
669
				$sync_settings = wp_parse_args(
670
					array_intersect_key( $assoc_args, Jetpack_Sync_Settings::$valid_settings ),
671
					array(
672
						'sync_wait_time' => 0,
673
						'enqueue_wait_time' => 0,
674
						'queue_max_writes_sec' => 10000,
675
						'max_queue_size_full_sync' => 100000
676
					)
677
				);
678
				Jetpack_Sync_Settings::update_settings( $sync_settings );
679
680
				// Convert comma-delimited string of modules to an array
681 View Code Duplication
				if ( ! empty( $assoc_args['modules'] ) ) {
682
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
683
684
					// Convert the array so that the keys are the module name and the value is true to indicate
685
					// that we want to sync the module
686
					$modules = array_map( '__return_true', array_flip( $modules ) );
687
				}
688
689 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
690
					if (
691
						'users' === $module_name &&
692
						isset( $assoc_args[ $module_name ] ) &&
693
						'initial' === $assoc_args[ $module_name ]
694
					) {
695
						$modules[ 'users' ] = 'initial';
696
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
697
						$ids = explode( ',', $assoc_args[ $module_name ] );
698
						if ( count( $ids ) > 0 ) {
699
							$modules[ $module_name ] = $ids;
700
						}
701
					}
702
				}
703
704
				if ( empty( $modules ) ) {
705
					$modules = null;
706
				}
707
708
				// Kick off a full sync
709
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
710 View Code Duplication
					if ( $modules ) {
711
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), join( ', ', array_keys( $modules ) ) ) );
712
					} else {
713
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
714
					}
715 View Code Duplication
				} else {
716
717
					// Reset sync settings to original.
718
					Jetpack_Sync_Settings::update_settings( $original_settings );
719
720
					if ( $modules ) {
721
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
722
					} else {
723
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
724
					}
725
				}
726
727
				// Keep sending to WPCOM until there's nothing to send
728
				$i = 1;
729
				do {
730
					$result = Jetpack_Sync_Actions::$sender->do_full_sync();
731
					if ( is_wp_error( $result ) ) {
732
						$queue_empty_error = ( 'empty_queue_full_sync' == $result->get_error_code() );
733
						if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 == $i ) ) ) {
734
							WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
735
						}
736
					} else {
737
						if ( 1 == $i ) {
738
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
739
						} else {
740
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
741
						}
742
					}
743
					$i++;
744
				} while ( $result && ! is_wp_error( $result ) );
745
746
				// Reset sync settings to original.
747
				Jetpack_Sync_Settings::update_settings( $original_settings );
748
749
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
750
				break;
751
		}
752
	}
753
754
	/**
755
	 * List the contents of a specific Jetpack sync queue.
756
	 *
757
	 * ## OPTIONS
758
	 *
759
	 * peek : List the 100 front-most items on the queue.
760
	 *
761
	 * ## EXAMPLES
762
	 *
763
	 * wp jetpack sync_queue full_sync peek
764
	 *
765
	 * @synopsis <incremental|full_sync> <peek>
766
	 */
767
	public function sync_queue( $args, $assoc_args ) {
768
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
769
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
770
		}
771
772
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
773
		$action = isset( $args[1] ) ? $args[1] : 'peek';
774
775
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
776
		// the queue name that the code expects.
777
		$queue_name_map = $allowed_queues = array(
778
			'incremental' => 'sync',
779
			'full'        => 'full_sync',
780
		);
781
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
782
783
		switch( $action ) {
784
			case 'peek':
785
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
786
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
787
				$items = $queue->peek( 100 );
788
789
				if ( empty( $items ) ) {
790
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
791
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
792
				} else {
793
					$collection = array();
794
					foreach ( $items as $item ) {
795
						$collection[] = array(
796
							'action'          => $item[0],
797
							'args'            => json_encode( $item[1] ),
798
							'current_user_id' => $item[2],
799
							'microtime'       => $item[3],
800
							'importing'       => (string) $item[4],
801
						);
802
					}
803
					WP_CLI\Utils\format_items(
804
						'table',
805
						$collection,
806
						array(
807
							'action',
808
							'args',
809
							'current_user_id',
810
							'microtime',
811
							'importing',
812
						)
813
					);
814
				}
815
				break;
816
		}
817
	}
818
819
	/**
820
	 * Cancel's the current Jetpack plan granted by this partner, if applicable
821
	 *
822
	 * Returns success or error JSON
823
	 *
824
	 * <token_json>
825
	 * : JSON blob of WPCOM API token
826
	 *  [--partner-tracking-id=<partner_tracking_id>]
827
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
828
	 *
829
	 *  * @synopsis <token_json> [--partner-tracking-id=<partner_tracking_id>]
830
	 */
831
	public function partner_cancel( $args, $named_args ) {
832
		list( $token_json ) = $args;
833
834 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
835
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
836
		}
837
838
		if ( isset( $token->error ) ) {
839
			$this->partner_provision_error( new WP_Error( $token->error, $token->message ) );
840
		}
841
842
		if ( ! isset( $token->access_token ) ) {
843
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
844
		}
845
846
		if ( Jetpack::validate_sync_error_idc_option() ) {
847
			$this->partner_provision_error( new WP_Error(
848
				'site_in_safe_mode',
849
				esc_html__( 'Can not cancel a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
850
			) );
851
		}
852
853
		$site_identifier = Jetpack_Options::get_option( 'id' );
854
855
		if ( ! $site_identifier ) {
856
			$site_identifier = Jetpack::build_raw_urls( get_home_url() );
857
		}
858
859
		$request = array(
860
			'headers' => array(
861
				'Authorization' => "Bearer " . $token->access_token,
862
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
863
			),
864
			'timeout' => 60,
865
			'method'  => 'POST',
866
		);
867
868
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%s/partner-cancel', $this->get_api_host(), $site_identifier );
869 View Code Duplication
		if ( ! empty( $named_args ) && ! empty( $named_args['partner-tracking-id'] ) ) {
870
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner-tracking-id'], $url ) );
871
		}
872
873
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
874
875
		Jetpack_Options::delete_option( 'onboarding' );
876
877
		if ( is_wp_error( $result ) ) {
878
			$this->partner_provision_error( $result );
879
		}
880
881
		WP_CLI::log( wp_remote_retrieve_body( $result ) );
882
	}
883
884
	/**
885
	 * Provision a site using a Jetpack Partner license
886
	 *
887
	 * Returns JSON blob
888
	 *
889
	 * ## OPTIONS
890
	 *
891
	 * <token_json>
892
	 * : JSON blob of WPCOM API token
893
	 * [--plan=<plan_name>]
894
	 * : Slug of the requested plan, e.g. premium
895
	 * [--wpcom_user_id=<user_id>]
896
	 * : WordPress.com ID of user to connect as (must be whitelisted against partner key)
897
	 * [--wpcom_user_email=<wpcom_user_email>]
898
	 * : Override the email we send to WordPress.com for registration
899
	 * [--onboarding=<onboarding>]
900
	 * : Guide the user through an onboarding wizard
901
	 * [--force_register=<register>]
902
	 * : Whether to force a site to register
903
	 * [--force_connect=<force_connect>]
904
	 * : Force JPS to not reuse existing credentials
905
	 * [--home_url=<home_url>]
906
	 * : Overrides the home option via the home_url filter, or the WP_HOME constant
907
	 * [--site_url=<site_url>]
908
	 * : Overrides the siteurl option via the site_url filter, or the WP_SITEURL constant
909
	 * [--partner-tracking-id=<partner_tracking_id>]
910
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
911
	 *
912
	 * ## EXAMPLES
913
	 *
914
	 *     $ wp jetpack partner_provision '{ some: "json" }' premium 1
915
	 *     { success: true }
916
	 *
917
	 * @synopsis <token_json> [--wpcom_user_id=<user_id>] [--plan=<plan_name>] [--onboarding=<onboarding>] [--force_register=<register>] [--force_connect=<force_connect>] [--home_url=<home_url>] [--site_url=<site_url>] [--wpcom_user_email=<wpcom_user_email>] [--partner-tracking-id=<partner_tracking_id>]
918
	 */
919
	public function partner_provision( $args, $named_args ) {
920
		list( $token_json ) = $args;
921
922 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
923
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
924
		}
925
926
		if ( isset( $token->error ) ) {
927
			$message = isset( $token->message )
928
				? $token->message
929
				: '';
930
			$this->partner_provision_error( new WP_Error( $token->error, $message ) );
931
		}
932
933
		if ( ! isset( $token->access_token ) ) {
934
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
935
		}
936
937
		$url_args = array(
938
			'home_url' => 'WP_HOME',
939
			'site_url' => 'WP_SITEURL',
940
		);
941
942
		foreach ( $url_args as $url_arg => $constant_name ) {
943
			// Anonymous functions were introduced in 5.3.0. So, if we're running on
944
			// >= 5.3.0, use an anonymous function to set the home/siteurl values.
945
			//
946
			// Otherwise, fallback to setting the home/siteurl value via the WP_HOME and
947
			// WP_SITEURL constants if the constant hasn't already been defined.
948
			if ( isset( $named_args[ $url_arg ] ) ) {
949
				if ( version_compare( phpversion(), '5.3.0', '>=') ) {
950
					add_filter( $url_arg, function( $url ) use ( $url_arg, $named_args ) {
951
						return $named_args[ $url_arg ];
952
					}, 11 );
953
				} else if ( ! defined( $constant_name ) ) {
954
					define( $constant_name, $named_args[ $url_arg ] );
955
				}
956
			}
957
		}
958
959
		// If Jetpack is currently connected, and is not in Safe Mode already, kick off a sync of the current
960
		// functions/callables so that we can test if this site is in IDC.
961
		if ( Jetpack::is_active() && ! Jetpack::validate_sync_error_idc_option() ) {
962
			WP_CLI::runcommand( 'jetpack sync start --modules=functions --sync_wait_time=0', array(
963
				'return'     => true, // Capture output so that we don't output errors or successes.
964
				'exit_error' => false,
965
			) );
966
		}
967
968
		if ( Jetpack::validate_sync_error_idc_option() ) {
969
			$this->partner_provision_error( new WP_Error(
970
				'site_in_safe_mode',
971
				esc_html__( 'Can not provision a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
972
			) );
973
		}
974
975
		$blog_id    = Jetpack_Options::get_option( 'id' );
976
		$blog_token = Jetpack_Options::get_option( 'blog_token' );
977
978
		if ( ! $blog_id || ! $blog_token || ( isset( $named_args['force_register'] ) && intval( $named_args['force_register'] ) ) ) {
979
			// this code mostly copied from Jetpack::admin_page_load
980
			Jetpack::maybe_set_version_option();
981
			$registered = Jetpack::try_registration();
982
			if ( is_wp_error( $registered ) ) {
983
				$this->partner_provision_error( $registered );
984
			} elseif ( ! $registered ) {
985
				$this->partner_provision_error( new WP_Error( 'registration_error', __( 'There was an unspecified error registering the site', 'jetpack' ) ) );
986
			}
987
988
			$blog_id    = Jetpack_Options::get_option( 'id' );
989
			$blog_token = Jetpack_Options::get_option( 'blog_token' );
990
		}
991
992
		// if the user isn't specified, but we have a current master user, then set that to current user
993
		if ( ! get_current_user_id() && $master_user_id = Jetpack_Options::get_option( 'master_user' ) ) {
994
			wp_set_current_user( $master_user_id );
995
		}
996
997
		$site_icon = ( function_exists( 'has_site_icon') && has_site_icon() )
998
			? get_site_icon_url()
999
			: false;
1000
1001
		$auto_enable_sso = ( ! Jetpack::is_active() || Jetpack::is_module_active( 'sso' ) );
1002
1003
		/** This filter is documented in class.jetpack-cli.php */
1004
		if ( apply_filters( 'jetpack_start_enable_sso', $auto_enable_sso ) ) {
1005
			$redirect_uri = add_query_arg(
1006
				array( 'action' => 'jetpack-sso', 'redirect_to' => urlencode( admin_url() ) ),
1007
				wp_login_url() // TODO: come back to Jetpack dashboard?
1008
			);
1009
		} else {
1010
			$redirect_uri = admin_url();
1011
		}
1012
1013
		$request_body = array(
1014
			'jp_version'    => JETPACK__VERSION,
1015
			'redirect_uri'  => $redirect_uri
1016
		);
1017
1018
		if ( $site_icon ) {
1019
			$request_body['site_icon'] = $site_icon;
1020
		}
1021
1022
		if ( get_current_user_id() ) {
1023
			$user = wp_get_current_user();
1024
1025
			// role
1026
			$role = Jetpack::translate_current_user_to_role();
1027
			$signed_role = Jetpack::sign_role( $role );
1028
1029
			$secrets = Jetpack::init()->generate_secrets( 'authorize' );
1030
1031
			// Jetpack auth stuff
1032
			$request_body['scope']  = $signed_role;
1033
			$request_body['secret'] = $secrets['secret_1'];
1034
1035
			// User stuff
1036
			$request_body['user_id']    = $user->ID;
1037
			$request_body['user_email'] = $user->user_email;
1038
			$request_body['user_login'] = $user->user_login;
1039
		}
1040
1041
		// optional additional params
1042 View Code Duplication
		if ( isset( $named_args['wpcom_user_id'] ) && ! empty( $named_args['wpcom_user_id'] ) ) {
1043
			$request_body['wpcom_user_id'] = $named_args['wpcom_user_id'];
1044
		}
1045
1046
		// override email of selected user
1047 View Code Duplication
		if ( isset( $named_args['wpcom_user_email'] ) && ! empty( $named_args['wpcom_user_email'] ) ) {
1048
			$request_body['user_email'] = $named_args['wpcom_user_email'];
1049
		}
1050
1051 View Code Duplication
		if ( isset( $named_args['plan'] ) && ! empty( $named_args['plan'] ) ) {
1052
			$request_body['plan'] = $named_args['plan'];
1053
		}
1054
1055 View Code Duplication
		if ( isset( $named_args['onboarding'] ) && ! empty( $named_args['onboarding'] ) ) {
1056
			$request_body['onboarding'] = intval( $named_args['onboarding'] );
1057
		}
1058
1059 View Code Duplication
		if ( isset( $named_args['force_connect'] ) && ! empty( $named_args['force_connect'] ) ) {
1060
			$request_body['force_connect'] = intval( $named_args['force_connect'] );
1061
		}
1062
1063
		if ( isset( $request_body['onboarding'] ) && (bool) $request_body['onboarding'] ) {
1064
			Jetpack::create_onboarding_token();
1065
		}
1066
1067
		$request = array(
1068
			'headers' => array(
1069
				'Authorization' => "Bearer " . $token->access_token,
1070
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
1071
			),
1072
			'timeout' => 60,
1073
			'method'  => 'POST',
1074
			'body'    => json_encode( $request_body )
1075
		);
1076
1077
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%d/partner-provision', $this->get_api_host(), $blog_id );
1078 View Code Duplication
		if ( ! empty( $named_args['partner-tracking-id'] ) ) {
1079
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner-tracking-id'], $url ) );
1080
		}
1081
1082
		// add calypso env if set
1083
		if ( getenv( 'CALYPSO_ENV' ) ) {
1084
			$url = add_query_arg( array( 'calypso_env' => getenv( 'CALYPSO_ENV' ) ), $url );
1085
		}
1086
1087
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
1088
1089
		if ( is_wp_error( $result ) ) {
1090
			$this->partner_provision_error( $result );
1091
		}
1092
1093
		$response_code = wp_remote_retrieve_response_code( $result );
1094
		$body_json     = json_decode( wp_remote_retrieve_body( $result ) );
1095
1096
		if( 200 !== $response_code ) {
1097
			if ( isset( $body_json->error ) ) {
1098
				$this->partner_provision_error( new WP_Error( $body_json->error, $body_json->message ) );
1099
			} else {
1100
				$this->partner_provision_error( new WP_Error( 'server_error', sprintf( __( "Request failed with code %s" ), $response_code ) ) );
1101
			}
1102
		}
1103
1104
		if ( isset( $body_json->access_token ) ) {
1105
			// authorize user and enable SSO
1106
			Jetpack::update_user_token( $user->ID, sprintf( '%s.%d', $body_json->access_token, $user->ID ), true );
0 ignored issues
show
The variable $user does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1107
1108
			/**
1109
			 * Auto-enable SSO module for new Jetpack Start connections
1110
			 *
1111
			 * @since 5.0.0
1112
			 *
1113
			 * @param bool $enable_sso Whether to enable the SSO module. Default to true.
1114
			 */
1115
			$other_modules = apply_filters( 'jetpack_start_enable_sso', true )
1116
				? array( 'sso' )
1117
				: array();
1118
1119 View Code Duplication
			if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
1120
				Jetpack::delete_active_modules();
1121
				Jetpack::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), false );
1122
			} else {
1123
				Jetpack::activate_default_modules( false, false, $other_modules, false );
1124
			}
1125
		}
1126
1127
		WP_CLI::log( json_encode( $body_json ) );
1128
	}
1129
1130
	/**
1131
	 * Manages your Jetpack sitemap
1132
	 *
1133
	 * ## OPTIONS
1134
	 *
1135
	 * rebuild : Rebuild all sitemaps
1136
	 * --purge : if set, will remove all existing sitemap data before rebuilding
1137
	 *
1138
	 * ## EXAMPLES
1139
	 *
1140
	 * wp jetpack sitemap rebuild
1141
	 *
1142
	 * @subcommand sitemap
1143
	 * @synopsis <rebuild> [--purge]
1144
	 */
1145
	public function sitemap( $args, $assoc_args ) {
1146
		if ( ! Jetpack::is_active() ) {
1147
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1148
		}
1149
		if ( ! Jetpack::is_module_active( 'sitemaps' ) ) {
1150
			WP_CLI::error( __( 'Jetpack Sitemaps module is not currently active. Activate it first if you want to work with sitemaps.', 'jetpack' ) );
1151
		}
1152
		if ( ! class_exists( 'Jetpack_Sitemap_Builder' ) ) {
1153
			WP_CLI::error( __( 'Jetpack Sitemaps module is active, but unavailable. This can happen if your site is set to discourage search engine indexing. Please enable search engine indexing to allow sitemap generation.', 'jetpack' ) );
1154
		}
1155
1156
		if ( isset( $assoc_args['purge'] ) && $assoc_args['purge'] ) {
1157
			$librarian = new Jetpack_Sitemap_Librarian();
1158
			$librarian->delete_all_stored_sitemap_data();
1159
		}
1160
1161
		$sitemap_builder = new Jetpack_Sitemap_Builder();
1162
		$sitemap_builder->update_sitemap();
1163
	}
1164
1165
	private function get_api_host() {
1166
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true );
1167
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
1168
	}
1169
1170
	private function partner_provision_error( $error ) {
1171
		WP_CLI::log( json_encode( array(
1172
			'success'       => false,
1173
			'error_code'    => $error->get_error_code(),
1174
			'error_message' => $error->get_error_message()
1175
		) ) );
1176
		exit( 1 );
1177
	}
1178
}
1179
1180
/*
1181
 * Standard "ask for permission to continue" function.
1182
 * If action cancelled, ask if they need help.
1183
 *
1184
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
1185
 *
1186
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
1187
 * @param $error_msg string (optional)
1188
 */
1189
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
1190
	$cli = new Jetpack_CLI();
1191
1192
	// Default cancellation message
1193
	if ( ! $error_msg ) {
1194
		$error_msg =
1195
			__( 'Action cancelled. Have a question?', 'jetpack' )
1196
			. ' '
1197
			. $cli->green_open
1198
			. 'jetpack.com/support'
1199
			.  $cli->color_close;
1200
	}
1201
1202
	if ( ! $flagged ) {
1203
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
1204
	} else {
1205
		/* translators: Don't translate the word yes here. */
1206
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
1207
	}
1208
1209
	WP_CLI::line( $prompt_message );
1210
	$handle = fopen( "php://stdin", "r" );
1211
	$line = fgets( $handle );
1212
	if ( 'yes' != trim( $line ) ){
1213
		WP_CLI::error( $error_msg );
1214
	}
1215
}
1216