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