Completed
Push — add/theme-switch-name ( 3e6989 )
by
unknown
11:52
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
		/*
42
		 * Are they asking for all data?
43
		 *
44
		 * Loop through heartbeat data and organize by priority.
45
		 */
46
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
47
		if ( $all_data ) {
48
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
49
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
50
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
51
52
			// Heartbeat data
53
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
54
55
			// Get the filtered heartbeat data.
56
			// Filtered so we can color/list by severity
57
			$stats = Jetpack::jetpack_check_heartbeat_data();
58
59
			// Display red flags first
60
			foreach ( $stats['bad'] as $stat => $value ) {
61
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
62
			}
63
64
			// Display caution warnings next
65
			foreach ( $stats['caution'] as $stat => $value ) {
66
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
67
			}
68
69
			// The rest of the results are good!
70
			foreach ( $stats['good'] as $stat => $value ) {
71
72
				// Modules should get special spacing for aestetics
73
				if ( strpos( $stat, 'odule-' ) ) {
74
					printf( "%-'.30s %s\n", $stat, $value );
75
					usleep( 4000 ); // For dramatic effect lolz
76
					continue;
77
				}
78
				printf( "%-'.16s %s\n", $stat, $value );
79
				usleep( 4000 ); // For dramatic effect lolz
80
			}
81
		} else {
82
			// Just the basics
83
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
84
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
85
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
86
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
87
		}
88
	}
89
90
	/**
91
	 * Disconnect Jetpack Blogs or Users
92
	 *
93
	 * ## OPTIONS
94
	 *
95
	 * blog: Disconnect the entire blog.
96
	 *
97
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
98
	 *
99
	 * Please note, the primary account that the blog is connected
100
	 * to WordPress.com with cannot be disconnected without
101
	 * disconnecting the entire blog.
102
	 *
103
	 * ## EXAMPLES
104
	 *
105
	 * wp jetpack disconnect blog
106
	 * wp jetpack disconnect user 13
107
	 * wp jetpack disconnect user username
108
	 * wp jetpack disconnect user [email protected]
109
	 *
110
	 * @synopsis <blog|user> [<user_identifier>]
111
	 */
112
	public function disconnect( $args, $assoc_args ) {
113
		if ( ! Jetpack::is_active() ) {
114
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
115
		}
116
117
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
118
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
119
			/* translators: %s is a command like "prompt" */
120
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
121
		}
122
123
		if ( in_array( $action, array( 'user' ) ) ) {
124
			if ( isset( $args[1] ) ) {
125
				$user_id = $args[1];
126
				if ( ctype_digit( $user_id ) ) {
127
					$field = 'id';
128
					$user_id = (int) $user_id;
129
				} elseif ( is_email( $user_id ) ) {
130
					$field = 'email';
131
					$user_id = sanitize_user( $user_id, true );
132
				} else {
133
					$field = 'login';
134
					$user_id = sanitize_user( $user_id, true );
135
				}
136
				if ( ! $user = get_user_by( $field, $user_id ) ) {
137
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
138
				}
139
			} else {
140
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
141
			}
142
		}
143
144
		switch ( $action ) {
145
			case 'blog':
146
				Jetpack::log( 'disconnect' );
147
				Jetpack::disconnect();
148
				WP_CLI::success( __( 'Jetpack has been successfully disconnected.', 'jetpack' ) );
149
				break;
150
			case 'user':
151
				if ( Jetpack::unlink_user( $user->ID ) ) {
152
					Jetpack::log( 'unlink', $user->ID );
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...
153
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
154
				} else {
155
					/* translators: %s is a username */
156
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
157
				}
158
				break;
159
			case 'prompt':
160
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
161
				break;
162
		}
163
	}
164
165
	/**
166
	 * Reset Jetpack options and settings to default
167
	 *
168
	 * ## OPTIONS
169
	 *
170
	 * modules: Resets modules to default state ( get_default_modules() )
171
	 *
172
	 * options: Resets all Jetpack options except:
173
	 *  - All private options (Blog token, user token, etc...)
174
	 *  - id (The Client ID/WP.com Blog ID of this site)
175
	 *  - master_user
176
	 *  - version
177
	 *  - activated
178
	 *
179
	 * ## EXAMPLES
180
	 *
181
	 * wp jetpack reset options
182
	 * wp jetpack reset modules
183
	 *
184
	 * @synopsis <modules|options>
185
	 */
186
	public function reset( $args, $assoc_args ) {
187
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
188 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
189
			/* translators: %s is a command like "prompt" */
190
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
191
		}
192
193
		// Are you sure?
194
		jetpack_cli_are_you_sure();
195
196
		switch ( $action ) {
197
			case 'options':
198
				$options_to_reset = Jetpack::get_jetpack_options_for_reset();
199
200
				// Reset the Jetpack options
201
				_e( "Resetting Jetpack Options...\n", "jetpack" );
202
				sleep(1); // Take a breath
203
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
204
					Jetpack_Options::delete_option( $option_to_reset );
205
					usleep( 100000 );
206
					/* translators: This is the result of an action. The option named %s was reset */
207
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
208
				}
209
210
				// Reset the WP options
211
				_e( "Resetting the jetpack options stored in wp_options...\n", "jetpack" );
212
				usleep( 500000 ); // Take a breath
213
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
214
					delete_option( $option_to_reset );
215
					usleep( 100000 );
216
					/* translators: This is the result of an action. The option named %s was reset */
217
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
218
				}
219
220
				// Reset to default modules
221
				_e( "Resetting default modules...\n", "jetpack" );
222
				usleep( 500000 ); // Take a breath
223
				$default_modules = Jetpack::get_default_modules();
224
				Jetpack::update_active_modules( $default_modules );
225
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
226
227
				// Jumpstart option is special
228
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
229
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
230
				break;
231 View Code Duplication
			case 'modules':
232
				$default_modules = Jetpack::get_default_modules();
233
				Jetpack::update_active_modules( $default_modules );
234
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
235
				break;
236
			case 'prompt':
237
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
238
				break;
239
		}
240
	}
241
242
	/**
243
	 * Manage Jetpack Modules
244
	 *
245
	 * ## OPTIONS
246
	 *
247
	 * list          : View all available modules, and their status.
248
	 * activate all  : Activate all modules
249
	 * deactivate all: Deactivate all modules
250
	 *
251
	 * activate   <module_slug> : Activate a module.
252
	 * deactivate <module_slug> : Deactivate a module.
253
	 * toggle     <module_slug> : Toggle a module on or off.
254
	 *
255
	 * ## EXAMPLES
256
	 *
257
	 * wp jetpack module list
258
	 * wp jetpack module activate stats
259
	 * wp jetpack module deactivate stats
260
	 * wp jetpack module toggle stats
261
	 *
262
	 * wp jetpack module activate all
263
	 * wp jetpack module deactivate all
264
	 *
265
	 * @synopsis <list|activate|deactivate|toggle> [<module_name>]
266
	 */
267
	public function module( $args, $assoc_args ) {
268
		$action = isset( $args[0] ) ? $args[0] : 'list';
269
		if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) {
270
			/* translators: %s is a command like "prompt" */
271
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
272
		}
273
		if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) {
274
			if ( isset( $args[1] ) ) {
275
				$module_slug = $args[1];
276
				if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
277
					WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
278
				}
279
				if ( 'toggle' == $action ) {
280
					$action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate';
281
				}
282
				// Bulk actions
283
				if ( 'all' == $args[1] ) {
284
					$action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all';
285
				}
286
				// VaultPress needs to be handled elsewhere.
287
				if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) && 'vaultpress' == $args[1] ) {
288
					WP_CLI::error( sprintf( _x( 'Please visit %s to configure your VaultPress subscription.', '%s is a website', 'jetpack' ), esc_url( 'https://vaultpress.com/jetpack/' ) ) );
289
				}
290
			} else {
291
				WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
292
				$action = 'list';
293
			}
294
		}
295
		switch ( $action ) {
296
			case 'list':
297
				WP_CLI::line( __( 'Available Modules:', 'jetpack' ) );
298
				$modules = Jetpack::get_available_modules();
299
				sort( $modules );
300
				foreach( $modules as $module_slug ) {
301
					if ( 'vaultpress' == $module_slug ) {
302
						continue;
303
					}
304
					$active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' );
305
					WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active );
306
				}
307
				break;
308 View Code Duplication
			case 'activate':
309
				$module = Jetpack::get_module( $module_slug );
310
				Jetpack::log( 'activate', $module_slug );
311
				Jetpack::activate_module( $module_slug, false, false );
312
				WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
313
				break;
314 View Code Duplication
			case 'activate_all':
315
				$modules = Jetpack::get_available_modules();
316
				Jetpack::update_active_modules( $modules );
317
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
318
				break;
319 View Code Duplication
			case 'deactivate':
320
				$module = Jetpack::get_module( $module_slug );
321
				Jetpack::log( 'deactivate', $module_slug );
322
				Jetpack::deactivate_module( $module_slug );
323
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
324
				break;
325
			case 'deactivate_all':
326
				Jetpack::delete_active_modules();
327
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
328
				break;
329
			case 'toggle':
330
				// Will never happen, should have been handled above and changed to activate or deactivate.
331
				break;
332
		}
333
	}
334
335
	/**
336
	 * Manage Protect Settings
337
	 *
338
	 * ## OPTIONS
339
	 *
340
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
341
	 *
342
	 *
343
	 * ## EXAMPLES
344
	 *
345
	 * wp jetpack protect whitelist <ip address>
346
	 * wp jetpack protect whitelist list
347
	 * wp jetpack protect whitelist clear
348
	 *
349
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
350
	 */
351
	public function protect( $args, $assoc_args ) {
352
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
353
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
354
			/* translators: %s is a command like "prompt" */
355
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
356
		}
357
		// Check if module is active
358
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
359
			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__ ) );
360
		}
361
		if ( in_array( $action, array( 'whitelist' ) ) ) {
362
			if ( isset( $args[1] ) ) {
363
				$action = 'whitelist';
364
			} else {
365
				$action = 'prompt';
366
			}
367
		}
368
		switch ( $action ) {
369
			case 'whitelist':
370
				$whitelist         = array();
371
				$new_ip            = $args[1];
372
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist' );
373
374
				// Build array of IPs that are already whitelisted.
375
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
376
				// low & high range params for jetpack_protect_ip_address_is_in_range();
377
				foreach( $current_whitelist as $whitelisted ) {
378
379
					// IP ranges
380
					if ( $whitelisted->range ) {
381
382
						// Is it already whitelisted?
383
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
384
							/* translators: %s is an IP address */
385
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
386
							break;
387
						}
388
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
389
390
					} else { // Individual IPs
391
392
						// Check if the IP is already whitelisted (single IP only)
393
						if ( $new_ip == $whitelisted->ip_address ) {
394
							/* translators: %s is an IP address */
395
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
396
							break;
397
						}
398
						$whitelist[] = $whitelisted->ip_address;
399
400
					}
401
				}
402
403
				/*
404
				 * List the whitelist
405
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
406
				 */
407
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
408
					if ( ! empty( $whitelist ) ) {
409
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
410
						foreach ( $whitelist as $ip ) {
411
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
412
						}
413
					} else {
414
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
415
					}
416
					break;
417
				}
418
419
				/*
420
				 * Clear the whitelist
421
				 */
422
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
423 View Code Duplication
					if ( ! empty( $whitelist ) ) {
424
						$whitelist = array();
425
						jetpack_protect_save_whitelist( $whitelist );
426
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
427
					} else {
428
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
429
					}
430
					break;
431
				}
432
433
				// Append new IP to whitelist array
434
				array_push( $whitelist, $new_ip );
435
436
				// Save whitelist if there are no errors
437
				$result = jetpack_protect_save_whitelist( $whitelist );
438
				if ( is_wp_error( $result ) ) {
439
					WP_CLI::error( __( $result, 'jetpack' ) );
440
				}
441
442
				/* translators: %s is an IP address */
443
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
444
				break;
445
			case 'prompt':
446
				WP_CLI::error(
447
					__( 'No command found.', 'jetpack' ) . "\n" .
448
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
449
					_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" .
450
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
451
				);
452
				break;
453
		}
454
	}
455
456
	/**
457
	 * Manage Jetpack Options
458
	 *
459
	 * ## OPTIONS
460
	 *
461
	 * list   : List all jetpack options and their values
462
	 * delete : Delete an option
463
	 *          - can only delete options that are white listed.
464
	 * update : update an option
465
	 *          - can only update option strings
466
	 * get    : get the value of an option
467
	 *
468
	 * ## EXAMPLES
469
	 *
470
	 * wp jetpack options list
471
	 * wp jetpack options get    <option_name>
472
	 * wp jetpack options delete <option_name>
473
	 * wp jetpack options update <option_name> [<option_value>]
474
	 *
475
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
476
	 */
477
	public function options( $args, $assoc_args ) {
478
		$action = isset( $args[0] ) ? $args[0] : 'list';
479
		$safe_to_modify = Jetpack::get_jetpack_options_for_reset();
480
481
		// Jumpstart is special
482
		array_push( $safe_to_modify, 'jumpstart' );
483
484
		// Is the option flagged as unsafe?
485
		$flagged = ! in_array( $args[1], $safe_to_modify );
486
487 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
488
			/* translators: %s is a command like "prompt" */
489
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
490
		}
491
492
		if ( isset( $args[0] ) ) {
493
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
494
				$action = 'get';
495
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
496
				$action = 'delete';
497 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
498
				$action = 'update';
499
			} else {
500
				$action = 'list';
501
			}
502
		}
503
504
		// Bail if the option isn't found
505
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
506
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
507
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
508
		}
509
510
		// Let's print_r the option if it's an array
511
		// Used in the 'get' and 'list' actions
512
		$option = is_array( $option ) ? print_r( $option ) : $option;
513
514
		switch ( $action ) {
515
			case 'get':
516
				WP_CLI::success( "\t" . $option );
517
				break;
518
			case 'delete':
519
				jetpack_cli_are_you_sure( $flagged );
520
521
				Jetpack_Options::delete_option( $args[1] );
522
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
523
				break;
524
			case 'update':
525
				jetpack_cli_are_you_sure( $flagged );
526
527
				// Updating arrays would get pretty tricky...
528
				$value = Jetpack_Options::get_option( $args[1] );
529
				if ( $value && is_array( $value ) ) {
530
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
531
				}
532
533
				Jetpack_Options::update_option( $args[1], $args[2] );
534
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
535
				break;
536
			case 'list':
537
				$options_compact     = Jetpack_Options::get_option_names();
538
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
539
				$options_private     = Jetpack_Options::get_option_names( 'private' );
540
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
541
542
				// Table headers
543
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
544
545
				// List out the options and their values
546
				// Tell them if the value is empty or not
547
				// Tell them if it's an array
548
				foreach ( $options as $option ) {
549
					$value = Jetpack_Options::get_option( $option );
550
					if ( ! $value ) {
551
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
552
						continue;
553
					}
554
555
					if ( ! is_array( $value ) ) {
556
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
557
					} else if ( is_array( $value ) ) {
558
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
559
					}
560
				}
561
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
562
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
563
564
				WP_CLI::success(
565
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
566
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
567
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
568
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
569
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
570
				);
571
				break;
572
		}
573
	}
574
575
	/**
576
	 * Get the status of or start a new Jetpack sync.
577
	 *
578
	 * ## OPTIONS
579
	 *
580
	 * status : Print the current sync status
581
	 * start  : Start a full sync from this site to WordPress.com
582
	 *
583
	 * ## EXAMPLES
584
	 *
585
	 * wp jetpack sync status
586
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
587
	 *
588
	 * @synopsis <status|start> [--<field>=<value>]
589
	 */
590
	public function sync( $args, $assoc_args ) {
591
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
592
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
593
		}
594
595
		$action = isset( $args[0] ) ? $args[0] : 'status';
596
597
		switch ( $action ) {
598
			case 'status':
599
				$status = Jetpack_Sync_Actions::get_sync_status();
600
				$collection = array();
601
				foreach ( $status as $key => $item ) {
602
					$collection[]  = array(
603
						'option' => $key,
604
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
605
					);
606
				}
607
608
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
609
				break;
610
			case 'start':
611
				// Get the original settings so that we can restore them later
612
				$original_settings = Jetpack_Sync_Settings::get_settings();
613
614
				// Initialize sync settigns so we can sync as quickly as possible
615
				$sync_settings = wp_parse_args(
616
					array_intersect_key( $assoc_args, Jetpack_Sync_Settings::$valid_settings ),
617
					array(
618
						'sync_wait_time' => 0,
619
						'enqueue_wait_time' => 0,
620
						'queue_max_writes_sec' => 10000,
621
						'max_queue_size_full_sync' => 100000
622
					)
623
				);
624
				Jetpack_Sync_Settings::update_settings( $sync_settings );
625
626
				// Convert comma-delimited string of modules to an array
627
				if ( ! empty( $assoc_args['modules'] ) ) {
628
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
629
630
					// Convert the array so that the keys are the module name and the value is true to indicate
631
					// that we want to sync the module
632
					$modules = array_map( '__return_true', array_flip( $modules ) );
633
				}
634
635 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
636
					if (
637
						'users' === $module_name &&
638
						isset( $assoc_args[ $module_name ] ) &&
639
						'initial' === $assoc_args[ $module_name ]
640
					) {
641
						$modules[ 'users' ] = 'initial';
642
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
643
						$ids = explode( ',', $assoc_args[ $module_name ] );
644
						if ( count( $ids ) > 0 ) {
645
							$modules[ $module_name ] = $ids;
646
						}
647
					}
648
				}
649
650
				if ( empty( $modules ) ) {
651
					$modules = null;
652
				}
653
654
				// Kick off a full sync
655
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
656 View Code Duplication
					if ( $modules ) {
657
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: ', 'jetpack' ), join( ', ', $modules ) ) );
658
					} else {
659
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
660
					}
661 View Code Duplication
				} else {
662
663
					// Reset sync settings to original.
664
					Jetpack_Sync_Settings::update_settings( $original_settings );
665
666
					if ( $modules ) {
667
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
668
					} else {
669
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
670
					}
671
				}
672
673
				// Keep sending to WPCOM until there's nothing to send
674
				$i = 1;
675
				do {
676
					$result = Jetpack_Sync_Actions::$sender->do_full_sync();
677
					if ( $result ) {
678
						if ( 1 == $i++ ) {
679
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
680
						} else {
681
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
682
						}
683
					}
684
				} while ( $result );
685
686
				// Reset sync settings to original.
687
				Jetpack_Sync_Settings::update_settings( $original_settings );
688
689
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
690
				break;
691
		}
692
	}
693
694
	/**
695
	 * List the contents of a specific Jetpack sync queue.
696
	 *
697
	 * ## OPTIONS
698
	 *
699
	 * peek : List the 100 front-most items on the queue.
700
	 *
701
	 * ## EXAMPLES
702
	 *
703
	 * wp jetpack sync_queue full_sync peek
704
	 *
705
	 * @synopsis <incremental|full_sync> <peek>
706
	 */
707
	public function sync_queue( $args, $assoc_args ) {
708
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
709
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
710
		}
711
712
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
713
		$action = isset( $args[1] ) ? $args[1] : 'peek';
714
715
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
716
		// the queue name that the code expects.
717
		$queue_name_map = $allowed_queues = array(
718
			'incremental' => 'sync',
719
			'full'        => 'full_sync',
720
		);
721
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
722
723
		switch( $action ) {
724
			case 'peek':
725
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
726
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
727
				$items = $queue->peek( 100 );
728
729
				if ( empty( $items ) ) {
730
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
731
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
732
				} else {
733
					$collection = array();
734
					foreach ( $items as $item ) {
735
						$collection[] = array(
736
							'action'          => $item[0],
737
							'args'            => json_encode( $item[1] ),
738
							'current_user_id' => $item[2],
739
							'microtime'       => $item[3],
740
							'importing'       => (string) $item[4],
741
						);
742
					}
743
					WP_CLI\Utils\format_items(
744
						'table',
745
						$collection,
746
						array(
747
							'action',
748
							'args',
749
							'current_user_id',
750
							'microtime',
751
							'importing',
752
						)
753
					);
754
				}
755
				break;
756
		}
757
	}
758
}
759
760
/*
761
 * Standard "ask for permission to continue" function.
762
 * If action cancelled, ask if they need help.
763
 *
764
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
765
 *
766
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
767
 * @param $error_msg string (optional)
768
 */
769
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
770
	$cli = new Jetpack_CLI();
771
772
	// Default cancellation message
773
	if ( ! $error_msg ) {
774
		$error_msg =
775
			__( 'Action cancelled. Have a question?', 'jetpack' )
776
			. ' '
777
			. $cli->green_open
778
			. 'jetpack.com/support'
779
			.  $cli->color_close;
780
	}
781
782
	if ( ! $flagged ) {
783
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
784
	} else {
785
		/* translators: Don't translate the word yes here. */
786
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
787
	}
788
789
	WP_CLI::line( $prompt_message );
790
	$handle = fopen( "php://stdin", "r" );
791
	$line = fgets( $handle );
792
	if ( 'yes' != trim( $line ) ){
793
		WP_CLI::error( $error_msg );
794
	}
795
}
796