Completed
Push — try/gutenberg-separate-jetpack... ( e8dd3e...f0efb9 )
by Bernhard
39:26 queued 23:22
created

Jetpack_CLI::test_connection()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 33

Duplication

Lines 4
Ratio 12.12 %

Importance

Changes 0
Metric Value
cc 5
nc 16
nop 2
dl 4
loc 33
rs 9.0808
c 0
b 0
f 0
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
	// Aesthetics
10
	public $green_open  = "\033[32m";
11
	public $red_open    = "\033[31m";
12
	public $yellow_open = "\033[33m";
13
	public $color_close = "\033[0m";
14
15
	/**
16
	 * Get Jetpack Details
17
	 *
18
	 * ## OPTIONS
19
	 *
20
	 * empty: Leave it empty for basic stats
21
	 *
22
	 * full: View full stats.  It's the data from the heartbeat
23
	 *
24
	 * ## EXAMPLES
25
	 *
26
	 * wp jetpack status
27
	 * wp jetpack status full
28
	 *
29
	 */
30
	public function status( $args, $assoc_args ) {
31
		require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-debugger.php' );
32
33
		WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_home_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
		$jetpack_self_test = Jetpack_Debugger::run_self_test(); // Performs the same tests as jetpack.com/support/debug/
47
48
		if ( ! $jetpack_self_test || ! wp_remote_retrieve_response_code( $jetpack_self_test ) ) {
49
			WP_CLI::error( __( 'Jetpack connection status unknown.', 'jetpack' ) );
50
		} else if ( 200 == wp_remote_retrieve_response_code( $jetpack_self_test ) ) {
51
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
52
		} else {
53
			WP_CLI::error( __( 'Jetpack connection is broken.', 'jetpack' ) );
54
		}
55
56
		WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
57
		WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
58
		WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
59
60
		/*
61
		 * Are they asking for all data?
62
		 *
63
		 * Loop through heartbeat data and organize by priority.
64
		 */
65
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
66
		if ( $all_data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $all_data of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
67
			// Heartbeat data
68
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
69
70
			// Get the filtered heartbeat data.
71
			// Filtered so we can color/list by severity
72
			$stats = Jetpack::jetpack_check_heartbeat_data();
73
74
			// Display red flags first
75
			foreach ( $stats['bad'] as $stat => $value ) {
76
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
77
			}
78
79
			// Display caution warnings next
80
			foreach ( $stats['caution'] as $stat => $value ) {
81
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
82
			}
83
84
			// The rest of the results are good!
85
			foreach ( $stats['good'] as $stat => $value ) {
86
87
				// Modules should get special spacing for aestetics
88
				if ( strpos( $stat, 'odule-' ) ) {
89
					printf( "%-'.30s %s\n", $stat, $value );
90
					usleep( 4000 ); // For dramatic effect lolz
91
					continue;
92
				}
93
				printf( "%-'.16s %s\n", $stat, $value );
94
				usleep( 4000 ); // For dramatic effect lolz
95
			}
96
		} else {
97
			// Just the basics
98
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
99
		}
100
	}
101
102
	/**
103
	 * Tests the active connection
104
	 *
105
	 * 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.
106
	 *
107
	 * ## EXAMPLES
108
	 *
109
	 * wp jetpack test-connection
110
	 *
111
	 * @subcommand test-connection
112
	 */
113
	public function test_connection( $args, $assoc_args ) {
114
115
		WP_CLI::line( sprintf( __( 'Testing connection for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
116
117
		if ( ! Jetpack::is_active() ) {
118
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
119
		}
120
121
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
122
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
123
			Jetpack_Client::WPCOM_JSON_API_VERSION
124
		);
125
126 View Code Duplication
		if ( is_wp_error( $response ) ) {
127
			/* translators: %1$s is the error code, %2$s is the error message */
128
			WP_CLI::error( sprintf( __( 'Failed to test connection (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ) );
129
		}
130
131
		$body = wp_remote_retrieve_body( $response );
132
		if ( ! $body ) {
133
			WP_CLI::error( __( 'Failed to test connection (empty response body)', 'jetpack' ) );
134
		}
135
136
		$result = json_decode( $body );
137
		$is_connected = (bool) $result->connected;
138
		$message = $result->message;
139
140
		if ( $is_connected ) {
141
			WP_CLI::success( $message );
142
		} else {
143
			WP_CLI::error( $message );
144
		}
145
	}
146
147
	/**
148
	 * Disconnect Jetpack Blogs or Users
149
	 *
150
	 * ## OPTIONS
151
	 *
152
	 * blog: Disconnect the entire blog.
153
	 *
154
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
155
	 *
156
	 * Please note, the primary account that the blog is connected
157
	 * to WordPress.com with cannot be disconnected without
158
	 * disconnecting the entire blog.
159
	 *
160
	 * ## EXAMPLES
161
	 *
162
	 * wp jetpack disconnect blog
163
	 * wp jetpack disconnect user 13
164
	 * wp jetpack disconnect user username
165
	 * wp jetpack disconnect user [email protected]
166
	 *
167
	 * @synopsis <blog|user> [<user_identifier>]
168
	 */
169
	public function disconnect( $args, $assoc_args ) {
170
		if ( ! Jetpack::is_active() ) {
171
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
172
		}
173
174
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
175
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
176
			/* translators: %s is a command like "prompt" */
177
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
178
		}
179
180
		if ( in_array( $action, array( 'user' ) ) ) {
181
			if ( isset( $args[1] ) ) {
182
				$user_id = $args[1];
183
				if ( ctype_digit( $user_id ) ) {
184
					$field = 'id';
185
					$user_id = (int) $user_id;
186
				} elseif ( is_email( $user_id ) ) {
187
					$field = 'email';
188
					$user_id = sanitize_user( $user_id, true );
189
				} else {
190
					$field = 'login';
191
					$user_id = sanitize_user( $user_id, true );
192
				}
193
				if ( ! $user = get_user_by( $field, $user_id ) ) {
194
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
195
				}
196
			} else {
197
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
198
			}
199
		}
200
201
		switch ( $action ) {
202
			case 'blog':
203
				Jetpack::log( 'disconnect' );
204
				Jetpack::disconnect();
205
				WP_CLI::success( sprintf(
206
					__( 'Jetpack has been successfully disconnected for %s.', 'jetpack' ),
207
					esc_url( get_site_url() )
208
				) );
209
				break;
210
			case 'user':
211
				if ( Jetpack::unlink_user( $user->ID ) ) {
212
					Jetpack::log( 'unlink', $user->ID );
0 ignored issues
show
Bug introduced by
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...
213
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
214
				} else {
215
					/* translators: %s is a username */
216
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
217
				}
218
				break;
219
			case 'prompt':
220
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
221
				break;
222
		}
223
	}
224
225
	/**
226
	 * Reset Jetpack options and settings to default
227
	 *
228
	 * ## OPTIONS
229
	 *
230
	 * modules: Resets modules to default state ( get_default_modules() )
231
	 *
232
	 * options: Resets all Jetpack options except:
233
	 *  - All private options (Blog token, user token, etc...)
234
	 *  - id (The Client ID/WP.com Blog ID of this site)
235
	 *  - master_user
236
	 *  - version
237
	 *  - activated
238
	 *
239
	 * ## EXAMPLES
240
	 *
241
	 * wp jetpack reset options
242
	 * wp jetpack reset modules
243
	 *
244
	 * @synopsis <modules|options>
245
	 */
246
	public function reset( $args, $assoc_args ) {
247
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
248 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
249
			/* translators: %s is a command like "prompt" */
250
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
251
		}
252
253
		// Are you sure?
254
		jetpack_cli_are_you_sure();
255
256
		switch ( $action ) {
257
			case 'options':
258
				$options_to_reset = Jetpack_Options::get_options_for_reset();
259
260
				// Reset the Jetpack options
261
				WP_CLI::line( sprintf(
262
					__( "Resetting Jetpack Options for %s...\n", "jetpack" ),
263
					esc_url( get_site_url() )
264
				) );
265
				sleep(1); // Take a breath
266
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
267
					Jetpack_Options::delete_option( $option_to_reset );
268
					usleep( 100000 );
269
					/* translators: This is the result of an action. The option named %s was reset */
270
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
271
				}
272
273
				// Reset the WP options
274
				WP_CLI::line( __( "Resetting the jetpack options stored in wp_options...\n", "jetpack" ) );
275
				usleep( 500000 ); // Take a breath
276
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
277
					delete_option( $option_to_reset );
278
					usleep( 100000 );
279
					/* translators: This is the result of an action. The option named %s was reset */
280
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
281
				}
282
283
				// Reset to default modules
284
				WP_CLI::line( __( "Resetting default modules...\n", "jetpack" ) );
285
				usleep( 500000 ); // Take a breath
286
				$default_modules = Jetpack::get_default_modules();
287
				Jetpack::update_active_modules( $default_modules );
288
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
289
290
				// Jumpstart option is special
291
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
292
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
293
				break;
294 View Code Duplication
			case 'modules':
295
				$default_modules = Jetpack::get_default_modules();
296
				Jetpack::update_active_modules( $default_modules );
297
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
298
				break;
299
			case 'prompt':
300
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
301
				break;
302
		}
303
	}
304
305
	/**
306
	 * Manage Jetpack Modules
307
	 *
308
	 * ## OPTIONS
309
	 *
310
	 * <list|activate|deactivate|toggle>
311
	 * : The action to take.
312
	 * ---
313
	 * default: list
314
	 * options:
315
	 *  - list
316
	 *  - activate
317
	 *  - deactivate
318
	 *  - toggle
319
	 * ---
320
	 *
321
	 * [<module_slug>]
322
	 * : The slug of the module to perform an action on.
323
	 *
324
	 * [--format=<format>]
325
	 * : Allows overriding the output of the command when listing modules.
326
	 * ---
327
	 * default: table
328
	 * options:
329
	 *  - table
330
	 *  - json
331
	 *  - csv
332
	 *  - yaml
333
	 *  - ids
334
	 *  - count
335
	 * ---
336
	 *
337
	 * ## EXAMPLES
338
	 *
339
	 * wp jetpack module list
340
	 * wp jetpack module list --format=json
341
	 * wp jetpack module activate stats
342
	 * wp jetpack module deactivate stats
343
	 * wp jetpack module toggle stats
344
	 * wp jetpack module activate all
345
	 * wp jetpack module deactivate all
346
	 */
347
	public function module( $args, $assoc_args ) {
348
		$action = isset( $args[0] ) ? $args[0] : 'list';
349
350
		if ( isset( $args[1] ) ) {
351
			$module_slug = $args[1];
352
			if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
353
				/* translators: %s is a module slug like "stats" */
354
				WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
355
			}
356
			if ( 'toggle' === $action ) {
357
				$action = Jetpack::is_module_active( $module_slug )
358
					? 'deactivate'
359
					: 'activate';
360
			}
361
			if ( 'all' === $args[1] ) {
362
				$action = ( 'deactivate' === $action )
363
					? 'deactivate_all'
364
					: 'activate_all';
365
			}
366
		} elseif ( 'list' !== $action ) {
367
			WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
368
			$action = 'list';
369
		}
370
371
		switch ( $action ) {
372
			case 'list':
373
				$modules_list = array();
374
				$modules      = Jetpack::get_available_modules();
375
				sort( $modules );
376
				foreach ( (array) $modules as $module_slug ) {
377
					if ( 'vaultpress' === $module_slug ) {
378
						continue;
379
					}
380
					$modules_list[] = array(
381
						'slug'   => $module_slug,
382
						'status' => Jetpack::is_module_active( $module_slug )
383
							? __( 'Active', 'jetpack' )
384
							: __( 'Inactive', 'jetpack' ),
385
					);
386
				}
387
				WP_CLI\Utils\format_items( $assoc_args['format'], $modules_list, array( 'slug', 'status' ) );
388
				break;
389
			case 'activate':
390
				$module = Jetpack::get_module( $module_slug );
0 ignored issues
show
Bug introduced by
The variable $module_slug 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...
391
				Jetpack::log( 'activate', $module_slug );
392
				if ( Jetpack::activate_module( $module_slug, false, false ) ) {
393
					WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
394
				} else {
395
					WP_CLI::error( sprintf( __( '%s could not be activated.', 'jetpack' ), $module['name'] ) );
396
				}
397
				break;
398 View Code Duplication
			case 'activate_all':
399
				$modules = Jetpack::get_available_modules();
400
				Jetpack::update_active_modules( $modules );
401
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
402
				break;
403
			case 'deactivate':
404
				$module = Jetpack::get_module( $module_slug );
405
				Jetpack::log( 'deactivate', $module_slug );
406
				Jetpack::deactivate_module( $module_slug );
407
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
408
				break;
409
			case 'deactivate_all':
410
				Jetpack::delete_active_modules();
411
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
412
				break;
413
			case 'toggle':
414
				// Will never happen, should have been handled above and changed to activate or deactivate.
415
				break;
416
		}
417
	}
418
419
	/**
420
	 * Manage Protect Settings
421
	 *
422
	 * ## OPTIONS
423
	 *
424
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
425
	 *
426
	 *
427
	 * ## EXAMPLES
428
	 *
429
	 * wp jetpack protect whitelist <ip address>
430
	 * wp jetpack protect whitelist list
431
	 * wp jetpack protect whitelist clear
432
	 *
433
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
434
	 */
435
	public function protect( $args, $assoc_args ) {
436
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
437
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
438
			/* translators: %s is a command like "prompt" */
439
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
440
		}
441
		// Check if module is active
442
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
443
			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__ ) );
444
		}
445
		if ( in_array( $action, array( 'whitelist' ) ) ) {
446
			if ( isset( $args[1] ) ) {
447
				$action = 'whitelist';
448
			} else {
449
				$action = 'prompt';
450
			}
451
		}
452
		switch ( $action ) {
453
			case 'whitelist':
454
				$whitelist         = array();
455
				$new_ip            = $args[1];
456
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
457
458
				// Build array of IPs that are already whitelisted.
459
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
460
				// low & high range params for jetpack_protect_ip_address_is_in_range();
461
				foreach( $current_whitelist as $whitelisted ) {
462
463
					// IP ranges
464
					if ( $whitelisted->range ) {
465
466
						// Is it already whitelisted?
467
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
468
							/* translators: %s is an IP address */
469
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
470
							break;
471
						}
472
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
473
474
					} else { // Individual IPs
475
476
						// Check if the IP is already whitelisted (single IP only)
477
						if ( $new_ip == $whitelisted->ip_address ) {
478
							/* translators: %s is an IP address */
479
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
480
							break;
481
						}
482
						$whitelist[] = $whitelisted->ip_address;
483
484
					}
485
				}
486
487
				/*
488
				 * List the whitelist
489
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
490
				 */
491
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
492 View Code Duplication
					if ( ! empty( $whitelist ) ) {
493
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
494
						foreach ( $whitelist as $ip ) {
495
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
496
						}
497
					} else {
498
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
499
					}
500
					break;
501
				}
502
503
				/*
504
				 * Clear the whitelist
505
				 */
506
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
507
					if ( ! empty( $whitelist ) ) {
508
						$whitelist = array();
509
						jetpack_protect_save_whitelist( $whitelist );
510
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
511
					} else {
512
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
513
					}
514
					break;
515
				}
516
517
				// Append new IP to whitelist array
518
				array_push( $whitelist, $new_ip );
519
520
				// Save whitelist if there are no errors
521
				$result = jetpack_protect_save_whitelist( $whitelist );
522
				if ( is_wp_error( $result ) ) {
523
					WP_CLI::error( __( $result, 'jetpack' ) );
524
				}
525
526
				/* translators: %s is an IP address */
527
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
528
				break;
529
			case 'prompt':
530
				WP_CLI::error(
531
					__( 'No command found.', 'jetpack' ) . "\n" .
532
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
533
					_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" .
534
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
535
				);
536
				break;
537
		}
538
	}
539
540
	/**
541
	 * Manage Jetpack Options
542
	 *
543
	 * ## OPTIONS
544
	 *
545
	 * list   : List all jetpack options and their values
546
	 * delete : Delete an option
547
	 *          - can only delete options that are white listed.
548
	 * update : update an option
549
	 *          - can only update option strings
550
	 * get    : get the value of an option
551
	 *
552
	 * ## EXAMPLES
553
	 *
554
	 * wp jetpack options list
555
	 * wp jetpack options get    <option_name>
556
	 * wp jetpack options delete <option_name>
557
	 * wp jetpack options update <option_name> [<option_value>]
558
	 *
559
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
560
	 */
561
	public function options( $args, $assoc_args ) {
562
		$action = isset( $args[0] ) ? $args[0] : 'list';
563
		$safe_to_modify = Jetpack_Options::get_options_for_reset();
564
565
		// Jumpstart is special
566
		array_push( $safe_to_modify, 'jumpstart' );
567
568
		// Is the option flagged as unsafe?
569
		$flagged = ! in_array( $args[1], $safe_to_modify );
570
571 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
572
			/* translators: %s is a command like "prompt" */
573
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
574
		}
575
576
		if ( isset( $args[0] ) ) {
577
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
578
				$action = 'get';
579
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
580
				$action = 'delete';
581 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
582
				$action = 'update';
583
			} else {
584
				$action = 'list';
585
			}
586
		}
587
588
		// Bail if the option isn't found
589
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
590 View Code Duplication
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
591
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
592
		}
593
594
		// Let's print_r the option if it's an array
595
		// Used in the 'get' and 'list' actions
596
		$option = is_array( $option ) ? print_r( $option ) : $option;
597
598
		switch ( $action ) {
599
			case 'get':
600
				WP_CLI::success( "\t" . $option );
601
				break;
602
			case 'delete':
603
				jetpack_cli_are_you_sure( $flagged );
604
605
				Jetpack_Options::delete_option( $args[1] );
606
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
607
				break;
608
			case 'update':
609
				jetpack_cli_are_you_sure( $flagged );
610
611
				// Updating arrays would get pretty tricky...
612
				$value = Jetpack_Options::get_option( $args[1] );
613
				if ( $value && is_array( $value ) ) {
614
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
615
				}
616
617
				Jetpack_Options::update_option( $args[1], $args[2] );
618
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
619
				break;
620
			case 'list':
621
				$options_compact     = Jetpack_Options::get_option_names();
622
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
623
				$options_private     = Jetpack_Options::get_option_names( 'private' );
624
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
625
626
				// Table headers
627
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
628
629
				// List out the options and their values
630
				// Tell them if the value is empty or not
631
				// Tell them if it's an array
632
				foreach ( $options as $option ) {
633
					$value = Jetpack_Options::get_option( $option );
634
					if ( ! $value ) {
635
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
636
						continue;
637
					}
638
639
					if ( ! is_array( $value ) ) {
640
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
641
					} else if ( is_array( $value ) ) {
642
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
643
					}
644
				}
645
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
646
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
647
648
				WP_CLI::success(
649
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
650
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
651
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
652
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
653
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
654
				);
655
				break;
656
		}
657
	}
658
659
	/**
660
	 * Get the status of or start a new Jetpack sync.
661
	 *
662
	 * ## OPTIONS
663
	 *
664
	 * status : Print the current sync status
665
	 * start  : Start a full sync from this site to WordPress.com
666
	 *
667
	 * ## EXAMPLES
668
	 *
669
	 * wp jetpack sync status
670
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
671
	 *
672
	 * @synopsis <status|start> [--<field>=<value>]
673
	 */
674
	public function sync( $args, $assoc_args ) {
675
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
676
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
677
		}
678
679
		$action = isset( $args[0] ) ? $args[0] : 'status';
680
681
		switch ( $action ) {
682
			case 'status':
683
				$status = Jetpack_Sync_Actions::get_sync_status();
684
				$collection = array();
685
				foreach ( $status as $key => $item ) {
686
					$collection[]  = array(
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
687
						'option' => $key,
688
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
689
					);
690
				}
691
692
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
693
				break;
694
			case 'start':
695
				// Get the original settings so that we can restore them later
696
				$original_settings = Jetpack_Sync_Settings::get_settings();
697
698
				// Initialize sync settigns so we can sync as quickly as possible
699
				$sync_settings = wp_parse_args(
700
					array_intersect_key( $assoc_args, Jetpack_Sync_Settings::$valid_settings ),
0 ignored issues
show
Bug introduced by
The property valid_settings cannot be accessed from this context as it is declared private in class Jetpack_Sync_Settings.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
701
					array(
702
						'sync_wait_time' => 0,
703
						'enqueue_wait_time' => 0,
704
						'queue_max_writes_sec' => 10000,
705
						'max_queue_size_full_sync' => 100000
706
					)
707
				);
708
				Jetpack_Sync_Settings::update_settings( $sync_settings );
709
710
				// Convert comma-delimited string of modules to an array
711 View Code Duplication
				if ( ! empty( $assoc_args['modules'] ) ) {
712
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
713
714
					// Convert the array so that the keys are the module name and the value is true to indicate
715
					// that we want to sync the module
716
					$modules = array_map( '__return_true', array_flip( $modules ) );
717
				}
718
719 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
720
					if (
721
						'users' === $module_name &&
722
						isset( $assoc_args[ $module_name ] ) &&
723
						'initial' === $assoc_args[ $module_name ]
724
					) {
725
						$modules[ 'users' ] = 'initial';
0 ignored issues
show
Bug introduced by
The variable $modules 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...
726
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
727
						$ids = explode( ',', $assoc_args[ $module_name ] );
728
						if ( count( $ids ) > 0 ) {
729
							$modules[ $module_name ] = $ids;
730
						}
731
					}
732
				}
733
734
				if ( empty( $modules ) ) {
735
					$modules = null;
736
				}
737
738
				// Kick off a full sync
739
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
740
					if ( $modules ) {
741
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), join( ', ', array_keys( $modules ) ) ) );
742
					} else {
743
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
744
					}
745 View Code Duplication
				} else {
746
747
					// Reset sync settings to original.
748
					Jetpack_Sync_Settings::update_settings( $original_settings );
749
750
					if ( $modules ) {
751
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
752
					} else {
753
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
754
					}
755
				}
756
757
				// Keep sending to WPCOM until there's nothing to send
758
				$i = 1;
759
				do {
760
					$result = Jetpack_Sync_Actions::$sender->do_full_sync();
0 ignored issues
show
Bug introduced by
The property sender cannot be accessed from this context as it is declared private in class Jetpack_Sync_Actions.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
761
					if ( is_wp_error( $result ) ) {
762
						$queue_empty_error = ( 'empty_queue_full_sync' == $result->get_error_code() );
763
						if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 == $i ) ) ) {
764
							WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
765
						}
766
					} else {
767
						if ( 1 == $i ) {
768
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
769
						} else {
770
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
771
						}
772
					}
773
					$i++;
774
				} while ( $result && ! is_wp_error( $result ) );
775
776
				// Reset sync settings to original.
777
				Jetpack_Sync_Settings::update_settings( $original_settings );
778
779
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
780
				break;
781
		}
782
	}
783
784
	/**
785
	 * List the contents of a specific Jetpack sync queue.
786
	 *
787
	 * ## OPTIONS
788
	 *
789
	 * peek : List the 100 front-most items on the queue.
790
	 *
791
	 * ## EXAMPLES
792
	 *
793
	 * wp jetpack sync_queue full_sync peek
794
	 *
795
	 * @synopsis <incremental|full_sync> <peek>
796
	 */
797
	public function sync_queue( $args, $assoc_args ) {
798
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
799
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
800
		}
801
802
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
803
		$action = isset( $args[1] ) ? $args[1] : 'peek';
804
805
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
806
		// the queue name that the code expects.
807
		$queue_name_map = $allowed_queues = array(
0 ignored issues
show
Unused Code introduced by
$allowed_queues is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
808
			'incremental' => 'sync',
809
			'full'        => 'full_sync',
810
		);
811
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
812
813
		switch( $action ) {
814
			case 'peek':
815
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
816
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
817
				$items = $queue->peek( 100 );
818
819
				if ( empty( $items ) ) {
820
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
821
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
822
				} else {
823
					$collection = array();
824
					foreach ( $items as $item ) {
825
						$collection[] = array(
826
							'action'          => $item[0],
827
							'args'            => json_encode( $item[1] ),
828
							'current_user_id' => $item[2],
829
							'microtime'       => $item[3],
830
							'importing'       => (string) $item[4],
831
						);
832
					}
833
					WP_CLI\Utils\format_items(
834
						'table',
835
						$collection,
836
						array(
837
							'action',
838
							'args',
839
							'current_user_id',
840
							'microtime',
841
							'importing',
842
						)
843
					);
844
				}
845
				break;
846
		}
847
	}
848
849
	/**
850
	 * Cancel's the current Jetpack plan granted by this partner, if applicable
851
	 *
852
	 * Returns success or error JSON
853
	 *
854
	 * <token_json>
855
	 * : JSON blob of WPCOM API token
856
	 *  [--partner_tracking_id=<partner_tracking_id>]
857
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
858
	 *
859
	 *  * @synopsis <token_json> [--partner_tracking_id=<partner_tracking_id>]
860
	 */
861
	public function partner_cancel( $args, $named_args ) {
862
		list( $token_json ) = $args;
863
864 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
865
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
866
		}
867
868
		if ( isset( $token->error ) ) {
869
			$this->partner_provision_error( new WP_Error( $token->error, $token->message ) );
0 ignored issues
show
Bug introduced by
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...
870
		}
871
872
		if ( ! isset( $token->access_token ) ) {
873
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
874
		}
875
876
		if ( Jetpack::validate_sync_error_idc_option() ) {
877
			$this->partner_provision_error( new WP_Error(
878
				'site_in_safe_mode',
879
				esc_html__( 'Can not cancel a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
880
			) );
881
		}
882
883
		$site_identifier = Jetpack_Options::get_option( 'id' );
884
885
		if ( ! $site_identifier ) {
886
			$site_identifier = Jetpack::build_raw_urls( get_home_url() );
887
		}
888
889
		$request = array(
890
			'headers' => array(
891
				'Authorization' => "Bearer " . $token->access_token,
892
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
893
			),
894
			'timeout' => 60,
895
			'method'  => 'POST',
896
		);
897
898
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%s/partner-cancel', $this->get_api_host(), $site_identifier );
899 View Code Duplication
		if ( ! empty( $named_args ) && ! empty( $named_args['partner_tracking_id'] ) ) {
900
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner_tracking_id'], $url ) );
901
		}
902
903
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
904
905
		Jetpack_Options::delete_option( 'onboarding' );
906
907
		if ( is_wp_error( $result ) ) {
908
			$this->partner_provision_error( $result );
909
		}
910
911
		WP_CLI::log( wp_remote_retrieve_body( $result ) );
912
	}
913
914
	/**
915
	 * Provision a site using a Jetpack Partner license
916
	 *
917
	 * Returns JSON blob
918
	 *
919
	 * ## OPTIONS
920
	 *
921
	 * <token_json>
922
	 * : JSON blob of WPCOM API token
923
	 * [--plan=<plan_name>]
924
	 * : Slug of the requested plan, e.g. premium
925
	 * [--wpcom_user_id=<user_id>]
926
	 * : WordPress.com ID of user to connect as (must be whitelisted against partner key)
927
	 * [--wpcom_user_email=<wpcom_user_email>]
928
	 * : Override the email we send to WordPress.com for registration
929
	 * [--onboarding=<onboarding>]
930
	 * : Guide the user through an onboarding wizard
931
	 * [--force_register=<register>]
932
	 * : Whether to force a site to register
933
	 * [--force_connect=<force_connect>]
934
	 * : Force JPS to not reuse existing credentials
935
	 * [--home_url=<home_url>]
936
	 * : Overrides the home option via the home_url filter, or the WP_HOME constant
937
	 * [--site_url=<site_url>]
938
	 * : Overrides the siteurl option via the site_url filter, or the WP_SITEURL constant
939
	 * [--partner_tracking_id=<partner_tracking_id>]
940
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
941
	 *
942
	 * ## EXAMPLES
943
	 *
944
	 *     $ wp jetpack partner_provision '{ some: "json" }' premium 1
945
	 *     { success: true }
946
	 *
947
	 * @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>]
948
	 */
949
	public function partner_provision( $args, $named_args ) {
950
		list( $token_json ) = $args;
951
952 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
953
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
954
		}
955
956
		if ( isset( $token->error ) ) {
957
			$message = isset( $token->message )
0 ignored issues
show
Bug introduced by
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...
958
				? $token->message
959
				: '';
960
			$this->partner_provision_error( new WP_Error( $token->error, $message ) );
961
		}
962
963
		if ( ! isset( $token->access_token ) ) {
964
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
965
		}
966
967
		require_once JETPACK__PLUGIN_DIR . '_inc/class.jetpack-provision.php';
968
969
		$body_json = Jetpack_Provision::partner_provision( $token->access_token, $named_args );
970
971
		if ( is_wp_error( $body_json ) ) {
972
			error_log( json_encode( array(
973
				'success'       => false,
974
				'error_code'    => $body_json->get_error_code(),
975
				'error_message' => $body_json->get_error_message()
976
			) ) );
977
			exit( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method partner_provision() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
978
		}
979
980
		WP_CLI::log( json_encode( $body_json ) );
981
	}
982
983
	/**
984
	 * Manages your Jetpack sitemap
985
	 *
986
	 * ## OPTIONS
987
	 *
988
	 * rebuild : Rebuild all sitemaps
989
	 * --purge : if set, will remove all existing sitemap data before rebuilding
990
	 *
991
	 * ## EXAMPLES
992
	 *
993
	 * wp jetpack sitemap rebuild
994
	 *
995
	 * @subcommand sitemap
996
	 * @synopsis <rebuild> [--purge]
997
	 */
998
	public function sitemap( $args, $assoc_args ) {
999
		if ( ! Jetpack::is_active() ) {
1000
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1001
		}
1002
		if ( ! Jetpack::is_module_active( 'sitemaps' ) ) {
1003
			WP_CLI::error( __( 'Jetpack Sitemaps module is not currently active. Activate it first if you want to work with sitemaps.', 'jetpack' ) );
1004
		}
1005
		if ( ! class_exists( 'Jetpack_Sitemap_Builder' ) ) {
1006
			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' ) );
1007
		}
1008
1009
		if ( isset( $assoc_args['purge'] ) && $assoc_args['purge'] ) {
1010
			$librarian = new Jetpack_Sitemap_Librarian();
1011
			$librarian->delete_all_stored_sitemap_data();
1012
		}
1013
1014
		$sitemap_builder = new Jetpack_Sitemap_Builder();
1015
		$sitemap_builder->update_sitemap();
1016
	}
1017
1018
	/**
1019
	 * Allows authorizing a user via the command line and will activate
1020
	 *
1021
	 * ## EXAMPLES
1022
	 *
1023
	 * wp jetpack authorize_user --token=123456789abcdef
1024
	 *
1025
	 * @synopsis --token=<value>
1026
	 */
1027
	public function authorize_user( $args, $named_args ) {
1028
		if ( ! is_user_logged_in() ) {
1029
			WP_CLI::error( __( 'Please select a user to authorize via the --user global argument.', 'jetpack' ) );
1030
		}
1031
1032
		if ( empty( $named_args['token'] ) ) {
1033
			WP_CLI::error( __( 'A non-empty token argument must be passed.', 'jetpack' ) );
1034
		}
1035
1036
		$token = sanitize_text_field( $named_args['token'] );
1037
1038
		$is_master_user  = ! Jetpack::is_active();
1039
		$current_user_id = get_current_user_id();
1040
1041
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
1042
1043
		WP_CLI::log( wp_json_encode( $named_args ) );
1044
1045
		if ( $is_master_user ) {
1046
			/**
1047
			 * Auto-enable SSO module for new Jetpack Start connections
1048
			*
1049
			* @since 5.0.0
1050
			*
1051
			* @param bool $enable_sso Whether to enable the SSO module. Default to true.
1052
			*/
1053
			$enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
1054
			Jetpack::handle_post_authorization_actions( $enable_sso, false );
1055
1056
			/* translators: %d is a user ID */
1057
			WP_CLI::success( sprintf( __( 'Authorized %d and activated default modules.', 'jetpack' ), $current_user_id ) );
1058
		} else {
1059
			/* translators: %d is a user ID */
1060
			WP_CLI::success( sprintf( __( 'Authorized %d.', 'jetpack' ), $current_user_id ) );
1061
		}
1062
	}
1063
1064
	/**
1065
	 * Allows calling a WordPress.com API endpoint using the current blog's token.
1066
	 *
1067
	 * ## OPTIONS
1068
	 * --resource=<resource>
1069
	 * : The resource to call with the current blog's token, where `%d` represents the current blog's ID.
1070
	 *
1071
	 * [--api_version=<api_version>]
1072
	 * : The API version to query against.
1073
	 *
1074
	 * [--base_api_path=<base_api_path>]
1075
	 * : The base API path to query.
1076
	 * ---
1077
	 * default: rest
1078
	 * ---
1079
	 *
1080
	 * [--body=<body>]
1081
	 * : A JSON encoded string representing arguments to send in the body.
1082
	 *
1083
	 * [--field=<value>]
1084
	 * : Any number of arguments that should be passed to the resource.
1085
	 *
1086
	 * [--pretty]
1087
	 * : Will pretty print the results of a successful API call.
1088
	 *
1089
	 * [--strip-success]
1090
	 * : Will remove the green success label from successful API calls.
1091
	 *
1092
	 * ## EXAMPLES
1093
	 *
1094
	 * wp jetpack call_api --resource='/sites/%d'
1095
	 */
1096
	public function call_api( $args, $named_args ) {
1097
		if ( ! Jetpack::is_active() ) {
1098
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1099
		}
1100
1101
		$consumed_args = array(
1102
			'resource',
1103
			'api_version',
1104
			'base_api_path',
1105
			'body',
1106
			'pretty',
1107
		);
1108
1109
		// Get args that should be passed to resource.
1110
		$other_args = array_diff_key( $named_args, array_flip( $consumed_args ) );
1111
1112
		$decoded_body = ! empty( $named_args['body'] )
1113
			? json_decode( $named_args['body'] )
1114
			: false;
1115
1116
		$resource_url = ( false === strpos( $named_args['resource'], '%d' ) )
1117
			? $named_args['resource']
1118
			: sprintf( $named_args['resource'], Jetpack_Options::get_option( 'id' ) );
1119
1120
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
1121
			$resource_url,
1122
			empty( $named_args['api_version'] ) ? Jetpack_Client::WPCOM_JSON_API_VERSION : $named_args['api_version'],
1123
			$other_args,
1124
			empty( $decoded_body ) ? null : $decoded_body,
1125
			$named_args['base_api_path']
1126
		);
1127
1128 View Code Duplication
		if ( is_wp_error( $response ) ) {
1129
			WP_CLI::error( sprintf(
1130
				/* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an error code, %3$s is an error message. */
1131
				__( 'Request to %1$s returned an error: (%2$d) %3$s.', 'jetpack' ),
1132
				$resource_url,
1133
				$response->get_error_code(),
1134
				$response->get_error_message()
1135
			) );
1136
		}
1137
1138
		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1139
			WP_CLI::error( sprintf(
1140
				/* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an HTTP status code. */
1141
				__( 'Request to %1$s returned a non-200 response code: %2$d.', 'jetpack' ),
1142
				$resource_url,
1143
				wp_remote_retrieve_response_code( $response )
1144
			) );
1145
		}
1146
1147
		$output = wp_remote_retrieve_body( $response );
1148
		if ( isset( $named_args['pretty'] ) ) {
1149
			$decoded_output = json_decode( $output );
1150
			if ( $decoded_output ) {
1151
				$output = wp_json_encode( $decoded_output, JSON_PRETTY_PRINT );
1152
			}
1153
		}
1154
1155
		if ( isset( $named_args['strip-success'] ) ) {
1156
			WP_CLI::log( $output );
1157
			WP_CLI::halt( 0 );
1158
		}
1159
1160
		WP_CLI::success( $output );
1161
	}
1162
1163
	/**
1164
	 * API wrapper for getting stats from the WordPress.com API for the current site.
1165
	 *
1166
	 * ## OPTIONS
1167
	 *
1168
	 * [--quantity=<quantity>]
1169
	 * : The number of units to include.
1170
	 * ---
1171
	 * default: 30
1172
	 * ---
1173
	 *
1174
	 * [--period=<period>]
1175
	 * : The unit of time to query stats for.
1176
	 * ---
1177
	 * default: day
1178
	 * options:
1179
	 *  - day
1180
	 *  - week
1181
	 *  - month
1182
	 *  - year
1183
	 * ---
1184
	 *
1185
	 * [--date=<date>]
1186
	 * : The latest date to return stats for. Ex. - 2018-01-01.
1187
	 *
1188
	 * [--pretty]
1189
	 * : Will pretty print the results of a successful API call.
1190
	 *
1191
	 * [--strip-success]
1192
	 * : Will remove the green success label from successful API calls.
1193
	 *
1194
	 * ## EXAMPLES
1195
	 *
1196
	 * wp jetpack get_stats
1197
	 */
1198
	public function get_stats( $args, $named_args ) {
1199
		$selected_args = array_intersect_key(
1200
			$named_args,
1201
			array_flip( array(
1202
				'quantity',
1203
				'date',
1204
			) )
1205
		);
1206
1207
		// The API expects unit, but period seems to be more correct.
1208
		$selected_args['unit'] = $named_args['period'];
1209
1210
		$command = sprintf(
1211
			'jetpack call_api --resource=/sites/%d/stats/%s',
1212
			Jetpack_Options::get_option( 'id' ),
1213
			add_query_arg( $selected_args, 'visits' )
1214
		);
1215
1216
		if ( isset( $named_args['pretty'] ) ) {
1217
			$command .= ' --pretty';
1218
		}
1219
1220
		if ( isset( $named_args['strip-success'] ) ) {
1221
			$command .= ' --strip-success';
1222
		}
1223
1224
		WP_CLI::runcommand(
1225
			$command,
1226
			array(
1227
				'launch' => false, // Use the current process.
1228
			)
1229
		);
1230
	}
1231
1232
	/*
1233
	 * Allows management of publicize connections.
1234
	 *
1235
	 * ## OPTIONS
1236
	 *
1237
	 * <list|disconnect>
1238
	 * : The action to perform.
1239
	 * ---
1240
	 * options:
1241
	 *  - list
1242
	 *  - disconnect
1243
	 * ---
1244
	 *
1245
	 * [<identifier>]
1246
	 * : The connection ID or service to perform an action on.
1247
	 *
1248
	 * [--format=<format>]
1249
	 * : Allows overriding the output of the command when listing connections.
1250
	 * ---
1251
	 * default: table
1252
	 * options:
1253
	 *  - table
1254
	 *  - json
1255
	 *  - csv
1256
	 *  - yaml
1257
	 *  - ids
1258
	 *  - count
1259
	 * ---
1260
	 *
1261
	 * ## EXAMPLES
1262
	 *
1263
	 * wp jetpack publicize list
1264
	 * wp jetpack publicize list twitter
1265
	 * wp --user=1 jetpack publicize list
1266
	 * wp --user=1 jetpack publicize list twitter
1267
	 * wp jetpack publicize list 123456
1268
	 * wp jetpack publicize disconnect 123456
1269
	 * wp jetpack publicize disconnect all
1270
	 * wp jetpack publicize disconnect twitter
1271
	 */
1272
	public function publicize( $args, $named_args ) {
1273
		if ( ! Jetpack::is_active() ) {
1274
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1275
		}
1276
1277
		$action        = $args[0];
1278
		$publicize     = new Publicize();
1279
		$identifier    = ! empty( $args[1] ) ? $args[1] : false;
1280
		$services      = array_keys( $publicize->get_services() );
1281
		$id_is_service = in_array( $identifier, $services, true );
1282
1283
		switch ( $action ) {
1284
			case 'list':
1285
				$connections_to_return = array();
1286
1287
				// For the CLI command, let's return all connections when a user isn't specified. This
1288
				// differs from the logic in the Publicize class.
1289
				$option_connections = is_user_logged_in()
1290
					? (array) $publicize->get_all_connections_for_user()
1291
					: Jetpack_Options::get_option( 'publicize_connections' );
1292
1293
				foreach ( $option_connections as $service_name => $connections ) {
1294
					foreach ( (array) $connections as $id => $connection ) {
1295
						$connection['id']        = $id;
1296
						$connection['service']   = $service_name;
1297
						$connections_to_return[] = $connection;
1298
					}
1299
				}
1300
1301
				if ( $id_is_service && ! empty( $identifier ) && ! empty( $connections_to_return ) ) {
1302
					$temp_connections      = $connections_to_return;
1303
					$connections_to_return = array();
1304
1305
					foreach ( $temp_connections as $connection ) {
1306
						if ( $identifier === $connection['service'] ) {
1307
							$connections_to_return[] = $connection;
1308
						}
1309
					}
1310
				}
1311
1312
				if ( $identifier && ! $id_is_service && ! empty( $connections_to_return ) ) {
1313
					$connections_to_return = wp_list_filter( $connections_to_return, array( 'id' => $identifier ) );
1314
				}
1315
1316
				if ( empty( $connections_to_return ) ) {
1317
					return false;
1318
				}
1319
1320
				$expected_keys = array(
1321
					'id',
1322
					'service',
1323
					'user_id',
1324
					'provider',
1325
					'issued',
1326
					'expires',
1327
					'external_id',
1328
					'external_name',
1329
					'external_display',
1330
					'type',
1331
					'connection_data',
1332
				);
1333
1334
				WP_CLI\Utils\format_items( $named_args['format'], $connections_to_return, $expected_keys );
1335
				break; // list.
1336
			case 'disconnect':
1337
				if ( ! $identifier ) {
1338
					WP_CLI::error( __( 'A connection ID must be passed in order to disconnect.', 'jetpack' ) );
1339
				}
1340
1341
				// If the connection ID is 'all' then delete all connections. If the connection ID
1342
				// matches a service, delete all connections for that service.
1343
				if ( 'all' === $identifier || $id_is_service ) {
1344
					if ( 'all' === $identifier ) {
1345
						WP_CLI::log( __( "You're about to delete all publicize connections.", 'jetpack' ) );
1346
					} else {
1347
						/* translators: %s is a lowercase string for a social network. */
1348
						WP_CLI::log( sprintf( __( "You're about to delete all publicize connections to %s.", 'jetpack' ), $identifier ) );
1349
					}
1350
1351
					jetpack_cli_are_you_sure();
1352
1353
					$connections = array();
1354
					$service     = $identifier;
1355
1356
					$option_connections = is_user_logged_in()
1357
						? (array) $publicize->get_all_connections_for_user()
1358
						: Jetpack_Options::get_option( 'publicize_connections' );
1359
1360
					if ( 'all' === $service ) {
1361
						foreach ( (array) $option_connections as $service_name => $service_connections ) {
1362
							foreach ( $service_connections as $id => $connection ) {
1363
								$connections[ $id ] = $connection;
1364
							}
1365
						}
1366
					} elseif ( ! empty( $option_connections[ $service ] ) ) {
1367
						$connections = $option_connections[ $service ];
1368
					}
1369
1370
					if ( ! empty( $connections ) ) {
1371
						$count    = count( $connections );
1372
						$progress = \WP_CLI\Utils\make_progress_bar(
1373
							/* translators: %s is a lowercase string for a social network. */
1374
							sprintf( __( 'Disconnecting all connections to %s.', 'jetpack' ), $service ),
1375
							$count
1376
						);
1377
1378
						foreach ( $connections as $id => $connection ) {
1379
							if ( false === $publicize->disconnect( false, $id ) ) {
1380
								WP_CLI::error( sprintf(
1381
									/* translators: %1$d is a numeric ID and %2$s is a lowercase string for a social network. */
1382
									__( 'Publicize connection %d could not be disconnected', 'jetpack' ),
1383
									$id
1384
								) );
1385
							}
1386
1387
							$progress->tick();
1388
						}
1389
1390
						$progress->finish();
1391
1392
						if ( 'all' === $service ) {
1393
							WP_CLI::success( __( 'All publicize connections were successfully disconnected.', 'jetpack' ) );
1394
						} else {
1395
							/* translators: %s is a lowercase string for a social network. */
1396
							WP_CLI::success( __( 'All publicize connections to %s were successfully disconnected.', 'jetpack' ), $service );
1397
						}
1398
					}
1399
				} else {
1400
					if ( false !== $publicize->disconnect( false, $identifier ) ) {
1401
						/* translators: %d is a numeric ID. Example: 1234. */
1402
						WP_CLI::success( sprintf( __( 'Publicize connection %d has been disconnected.', 'jetpack' ), $identifier ) );
1403
					} else {
1404
						/* translators: %d is a numeric ID. Example: 1234. */
1405
						WP_CLI::error( sprintf( __( 'Publicize connection %d could not be disconnected.', 'jetpack' ), $identifier ) );
1406
					}
1407
				}
1408
				break; // disconnect.
1409
		}
1410
	}
1411
1412
	private function get_api_host() {
1413
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true );
1414
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
1415
	}
1416
1417
	private function partner_provision_error( $error ) {
1418
		WP_CLI::log( json_encode( array(
1419
			'success'       => false,
1420
			'error_code'    => $error->get_error_code(),
1421
			'error_message' => $error->get_error_message()
1422
		) ) );
1423
		exit( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method partner_provision_error() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1424
	}
1425
}
1426
1427
/*
1428
 * Standard "ask for permission to continue" function.
1429
 * If action cancelled, ask if they need help.
1430
 *
1431
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
1432
 *
1433
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
1434
 * @param $error_msg string (optional)
1435
 */
1436
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
1437
	$cli = new Jetpack_CLI();
1438
1439
	// Default cancellation message
1440
	if ( ! $error_msg ) {
1441
		$error_msg =
1442
			__( 'Action cancelled. Have a question?', 'jetpack' )
1443
			. ' '
1444
			. $cli->green_open
1445
			. 'jetpack.com/support'
1446
			.  $cli->color_close;
1447
	}
1448
1449
	if ( ! $flagged ) {
1450
		$prompt_message = _x( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command - do not translate.', 'jetpack' );
1451
	} else {
1452
		$prompt_message = _x( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', '"yes" is a command - do not translate.', 'jetpack' );
1453
	}
1454
1455
	WP_CLI::line( $prompt_message );
1456
	$handle = fopen( "php://stdin", "r" );
1457
	$line = fgets( $handle );
1458
	if ( 'yes' != trim( $line ) ){
1459
		WP_CLI::error( $error_msg );
1460
	}
1461
}
1462