Completed
Push — update/grunion-personal-data ( beacb7...22114a )
by
unknown
14:02
created

Jetpack_CLI::module()   F

Complexity

Conditions 20
Paths 440

Size

Total Lines 71
Code Lines 57

Duplication

Lines 5
Ratio 7.04 %

Importance

Changes 0
Metric Value
cc 20
eloc 57
nc 440
nop 2
dl 5
loc 71
rs 3.9588
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
WP_CLI::add_command( 'jetpack', 'Jetpack_CLI' );
4
5
/**
6
 * Control your local Jetpack installation.
7
 */
8
class Jetpack_CLI extends WP_CLI_Command {
9
10
	// Aesthetics
11
	public $green_open  = "\033[32m";
12
	public $red_open    = "\033[31m";
13
	public $yellow_open = "\033[33m";
14
	public $color_close = "\033[0m";
15
16
	/**
17
	 * Get Jetpack Details
18
	 *
19
	 * ## OPTIONS
20
	 *
21
	 * empty: Leave it empty for basic stats
22
	 *
23
	 * full: View full stats.  It's the data from the heartbeat
24
	 *
25
	 * ## EXAMPLES
26
	 *
27
	 * wp jetpack status
28
	 * wp jetpack status full
29
	 *
30
	 */
31
	public function status( $args, $assoc_args ) {
32
33
		WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
34
35
		if ( ! Jetpack::is_active() ) {
36
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
37
		}
38
39 View Code Duplication
		if ( isset( $args[0] ) && 'full' !== $args[0] ) {
40
			/* translators: %s is a command like "prompt" */
41
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $args[0] ) );
42
		}
43
44
		$master_user_email = Jetpack::get_master_user_email();
45
46
		/*
47
		 * Are they asking for all data?
48
		 *
49
		 * Loop through heartbeat data and organize by priority.
50
		 */
51
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
52
		if ( $all_data ) {
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...
53
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
54
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
55
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
56
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
57
58
			// Heartbeat data
59
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
60
61
			// Get the filtered heartbeat data.
62
			// Filtered so we can color/list by severity
63
			$stats = Jetpack::jetpack_check_heartbeat_data();
64
65
			// Display red flags first
66
			foreach ( $stats['bad'] as $stat => $value ) {
67
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
68
			}
69
70
			// Display caution warnings next
71
			foreach ( $stats['caution'] as $stat => $value ) {
72
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
73
			}
74
75
			// The rest of the results are good!
76
			foreach ( $stats['good'] as $stat => $value ) {
77
78
				// Modules should get special spacing for aestetics
79
				if ( strpos( $stat, 'odule-' ) ) {
80
					printf( "%-'.30s %s\n", $stat, $value );
81
					usleep( 4000 ); // For dramatic effect lolz
82
					continue;
83
				}
84
				printf( "%-'.16s %s\n", $stat, $value );
85
				usleep( 4000 ); // For dramatic effect lolz
86
			}
87
		} else {
88
			// Just the basics
89
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
90
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
91
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
92
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
93
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
94
		}
95
	}
96
97
	/**
98
	 * Tests the active connection
99
	 *
100
	 * Does a two-way test to verify that the local site can communicate with remote Jetpack/WP.com servers and that Jetpack/WP.com servers can talk to the local site.
101
	 *
102
	 * ## EXAMPLES
103
	 *
104
	 * wp jetpack test-connection
105
	 *
106
	 * @subcommand test-connection
107
	 */
108
	public function test_connection( $args, $assoc_args ) {
109
110
		WP_CLI::line( sprintf( __( 'Testing connection for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
111
112
		if ( ! Jetpack::is_active() ) {
113
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
114
		}
115
116
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
117
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
118
			Jetpack_Client::WPCOM_JSON_API_VERSION
119
		);
120
121 View Code Duplication
		if ( is_wp_error( $response ) ) {
122
			/* translators: %1$s is the error code, %2$s is the error message */
123
			WP_CLI::error( sprintf( __( 'Failed to test connection (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ) );
124
		}
125
126
		$body = wp_remote_retrieve_body( $response );
127
		if ( ! $body ) {
128
			WP_CLI::error( __( 'Failed to test connection (empty response body)', 'jetpack' ) );
129
		}
130
131
		$result = json_decode( $body );
132
		$is_connected = (bool) $result->connected;
133
		$message = $result->message;
134
135
		if ( $is_connected ) {
136
			WP_CLI::success( $message );
137
		} else {
138
			WP_CLI::error( $message );
139
		}
140
	}
141
142
	/**
143
	 * Disconnect Jetpack Blogs or Users
144
	 *
145
	 * ## OPTIONS
146
	 *
147
	 * blog: Disconnect the entire blog.
148
	 *
149
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
150
	 *
151
	 * Please note, the primary account that the blog is connected
152
	 * to WordPress.com with cannot be disconnected without
153
	 * disconnecting the entire blog.
154
	 *
155
	 * ## EXAMPLES
156
	 *
157
	 * wp jetpack disconnect blog
158
	 * wp jetpack disconnect user 13
159
	 * wp jetpack disconnect user username
160
	 * wp jetpack disconnect user [email protected]
161
	 *
162
	 * @synopsis <blog|user> [<user_identifier>]
163
	 */
164
	public function disconnect( $args, $assoc_args ) {
165
		if ( ! Jetpack::is_active() ) {
166
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
167
		}
168
169
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
170
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
171
			/* translators: %s is a command like "prompt" */
172
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
173
		}
174
175
		if ( in_array( $action, array( 'user' ) ) ) {
176
			if ( isset( $args[1] ) ) {
177
				$user_id = $args[1];
178
				if ( ctype_digit( $user_id ) ) {
179
					$field = 'id';
180
					$user_id = (int) $user_id;
181
				} elseif ( is_email( $user_id ) ) {
182
					$field = 'email';
183
					$user_id = sanitize_user( $user_id, true );
184
				} else {
185
					$field = 'login';
186
					$user_id = sanitize_user( $user_id, true );
187
				}
188
				if ( ! $user = get_user_by( $field, $user_id ) ) {
189
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
190
				}
191
			} else {
192
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
193
			}
194
		}
195
196
		switch ( $action ) {
197
			case 'blog':
198
				Jetpack::log( 'disconnect' );
199
				Jetpack::disconnect();
200
				WP_CLI::success( sprintf(
201
					__( 'Jetpack has been successfully disconnected for %s.', 'jetpack' ),
202
					esc_url( get_site_url() )
203
				) );
204
				break;
205
			case 'user':
206
				if ( Jetpack::unlink_user( $user->ID ) ) {
207
					Jetpack::log( 'unlink', $user->ID );
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...
208
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
209
				} else {
210
					/* translators: %s is a username */
211
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
212
				}
213
				break;
214
			case 'prompt':
215
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
216
				break;
217
		}
218
	}
219
220
	/**
221
	 * Reset Jetpack options and settings to default
222
	 *
223
	 * ## OPTIONS
224
	 *
225
	 * modules: Resets modules to default state ( get_default_modules() )
226
	 *
227
	 * options: Resets all Jetpack options except:
228
	 *  - All private options (Blog token, user token, etc...)
229
	 *  - id (The Client ID/WP.com Blog ID of this site)
230
	 *  - master_user
231
	 *  - version
232
	 *  - activated
233
	 *
234
	 * ## EXAMPLES
235
	 *
236
	 * wp jetpack reset options
237
	 * wp jetpack reset modules
238
	 *
239
	 * @synopsis <modules|options>
240
	 */
241
	public function reset( $args, $assoc_args ) {
242
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
243 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
244
			/* translators: %s is a command like "prompt" */
245
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
246
		}
247
248
		// Are you sure?
249
		jetpack_cli_are_you_sure();
250
251
		switch ( $action ) {
252
			case 'options':
253
				$options_to_reset = Jetpack_Options::get_options_for_reset();
254
255
				// Reset the Jetpack options
256
				WP_CLI::line( sprintf(
257
					__( "Resetting Jetpack Options for %s...\n", "jetpack" ),
258
					esc_url( get_site_url() )
259
				) );
260
				sleep(1); // Take a breath
261
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
262
					Jetpack_Options::delete_option( $option_to_reset );
263
					usleep( 100000 );
264
					/* translators: This is the result of an action. The option named %s was reset */
265
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
266
				}
267
268
				// Reset the WP options
269
				WP_CLI::line( __( "Resetting the jetpack options stored in wp_options...\n", "jetpack" ) );
270
				usleep( 500000 ); // Take a breath
271
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
272
					delete_option( $option_to_reset );
273
					usleep( 100000 );
274
					/* translators: This is the result of an action. The option named %s was reset */
275
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
276
				}
277
278
				// Reset to default modules
279
				WP_CLI::line( __( "Resetting default modules...\n", "jetpack" ) );
280
				usleep( 500000 ); // Take a breath
281
				$default_modules = Jetpack::get_default_modules();
282
				Jetpack::update_active_modules( $default_modules );
283
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
284
285
				// Jumpstart option is special
286
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
287
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
288
				break;
289 View Code Duplication
			case 'modules':
290
				$default_modules = Jetpack::get_default_modules();
291
				Jetpack::update_active_modules( $default_modules );
292
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
293
				break;
294
			case 'prompt':
295
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
296
				break;
297
		}
298
	}
299
300
	/**
301
	 * Manage Jetpack Modules
302
	 *
303
	 * ## OPTIONS
304
	 *
305
	 * <list|activate|deactivate|toggle>
306
	 * : The action to take.
307
	 * ---
308
	 * default: list
309
	 * options:
310
	 *  - list
311
	 *  - activate
312
	 *  - deactivate
313
	 *  - toggle
314
	 * ---
315
	 *
316
	 * [<module_slug>]
317
	 * : The slug of the module to perform an action on.
318
	 *
319
	 * [--format=<format>]
320
	 * : Allows overriding the output of the command when listing modules.
321
	 * ---
322
	 * default: table
323
	 * options:
324
	 *  - table
325
	 *  - json
326
	 *  - csv
327
	 *  - yaml
328
	 *  - ids
329
	 *  - count
330
	 * ---
331
	 *
332
	 * ## EXAMPLES
333
	 *
334
	 * wp jetpack module list
335
	 * wp jetpack module list --format=json
336
	 * wp jetpack module activate stats
337
	 * wp jetpack module deactivate stats
338
	 * wp jetpack module toggle stats
339
	 * wp jetpack module activate all
340
	 * wp jetpack module deactivate all
341
	 */
342
	public function module( $args, $assoc_args ) {
343
		$action = isset( $args[0] ) ? $args[0] : 'list';
344
345
		if ( isset( $args[1] ) ) {
346
			$module_slug = $args[1];
347
			if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
348
				/* translators: %s is a module slug like "stats" */
349
				WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
350
			}
351
			if ( 'toggle' === $action ) {
352
				$action = Jetpack::is_module_active( $module_slug )
353
					? 'deactivate'
354
					: 'activate';
355
			}
356
			if ( 'all' === $args[1] ) {
357
				$action = ( 'deactivate' === $action )
358
					? 'deactivate_all'
359
					: 'activate_all';
360
			}
361
		} elseif ( 'list' !== $action ) {
362
			WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
363
			$action = 'list';
364
		}
365
366
		switch ( $action ) {
367
			case 'list':
368
				$modules_list = array();
369
				$modules      = Jetpack::get_available_modules();
370
				sort( $modules );
371
				foreach ( (array) $modules as $module_slug ) {
372
					if ( 'vaultpress' === $module_slug ) {
373
						continue;
374
					}
375
					$modules_list[] = array(
376
						'slug'   => $module_slug,
377
						'status' => Jetpack::is_module_active( $module_slug )
378
							? __( 'Active', 'jetpack' )
379
							: __( 'Inactive', 'jetpack' ),
380
					);
381
				}
382
				WP_CLI\Utils\format_items( $assoc_args['format'], $modules_list, array( 'slug', 'status' ) );
383
				break;
384
			case 'activate':
385
				$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...
386
				Jetpack::log( 'activate', $module_slug );
387
				if ( Jetpack::activate_module( $module_slug, false, false ) ) {
388
					WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
389
				} else {
390
					WP_CLI::error( sprintf( __( '%s could not be activated.', 'jetpack' ), $module['name'] ) );
391
				}
392
				break;
393 View Code Duplication
			case 'activate_all':
394
				$modules = Jetpack::get_available_modules();
395
				Jetpack::update_active_modules( $modules );
396
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
397
				break;
398
			case 'deactivate':
399
				$module = Jetpack::get_module( $module_slug );
400
				Jetpack::log( 'deactivate', $module_slug );
401
				Jetpack::deactivate_module( $module_slug );
402
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
403
				break;
404
			case 'deactivate_all':
405
				Jetpack::delete_active_modules();
406
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
407
				break;
408
			case 'toggle':
409
				// Will never happen, should have been handled above and changed to activate or deactivate.
410
				break;
411
		}
412
	}
413
414
	/**
415
	 * Manage Protect Settings
416
	 *
417
	 * ## OPTIONS
418
	 *
419
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
420
	 *
421
	 *
422
	 * ## EXAMPLES
423
	 *
424
	 * wp jetpack protect whitelist <ip address>
425
	 * wp jetpack protect whitelist list
426
	 * wp jetpack protect whitelist clear
427
	 *
428
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
429
	 */
430
	public function protect( $args, $assoc_args ) {
431
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
432
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
433
			/* translators: %s is a command like "prompt" */
434
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
435
		}
436
		// Check if module is active
437
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
438
			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__ ) );
439
		}
440
		if ( in_array( $action, array( 'whitelist' ) ) ) {
441
			if ( isset( $args[1] ) ) {
442
				$action = 'whitelist';
443
			} else {
444
				$action = 'prompt';
445
			}
446
		}
447
		switch ( $action ) {
448
			case 'whitelist':
449
				$whitelist         = array();
450
				$new_ip            = $args[1];
451
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
452
453
				// Build array of IPs that are already whitelisted.
454
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
455
				// low & high range params for jetpack_protect_ip_address_is_in_range();
456
				foreach( $current_whitelist as $whitelisted ) {
457
458
					// IP ranges
459
					if ( $whitelisted->range ) {
460
461
						// Is it already whitelisted?
462
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
463
							/* translators: %s is an IP address */
464
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
465
							break;
466
						}
467
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
468
469
					} else { // Individual IPs
470
471
						// Check if the IP is already whitelisted (single IP only)
472
						if ( $new_ip == $whitelisted->ip_address ) {
473
							/* translators: %s is an IP address */
474
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
475
							break;
476
						}
477
						$whitelist[] = $whitelisted->ip_address;
478
479
					}
480
				}
481
482
				/*
483
				 * List the whitelist
484
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
485
				 */
486
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
487 View Code Duplication
					if ( ! empty( $whitelist ) ) {
488
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
489
						foreach ( $whitelist as $ip ) {
490
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
491
						}
492
					} else {
493
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
494
					}
495
					break;
496
				}
497
498
				/*
499
				 * Clear the whitelist
500
				 */
501
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
502
					if ( ! empty( $whitelist ) ) {
503
						$whitelist = array();
504
						jetpack_protect_save_whitelist( $whitelist );
505
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
506
					} else {
507
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
508
					}
509
					break;
510
				}
511
512
				// Append new IP to whitelist array
513
				array_push( $whitelist, $new_ip );
514
515
				// Save whitelist if there are no errors
516
				$result = jetpack_protect_save_whitelist( $whitelist );
517
				if ( is_wp_error( $result ) ) {
518
					WP_CLI::error( __( $result, 'jetpack' ) );
519
				}
520
521
				/* translators: %s is an IP address */
522
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
523
				break;
524
			case 'prompt':
525
				WP_CLI::error(
526
					__( 'No command found.', 'jetpack' ) . "\n" .
527
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
528
					_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" .
529
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
530
				);
531
				break;
532
		}
533
	}
534
535
	/**
536
	 * Manage Jetpack Options
537
	 *
538
	 * ## OPTIONS
539
	 *
540
	 * list   : List all jetpack options and their values
541
	 * delete : Delete an option
542
	 *          - can only delete options that are white listed.
543
	 * update : update an option
544
	 *          - can only update option strings
545
	 * get    : get the value of an option
546
	 *
547
	 * ## EXAMPLES
548
	 *
549
	 * wp jetpack options list
550
	 * wp jetpack options get    <option_name>
551
	 * wp jetpack options delete <option_name>
552
	 * wp jetpack options update <option_name> [<option_value>]
553
	 *
554
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
555
	 */
556
	public function options( $args, $assoc_args ) {
557
		$action = isset( $args[0] ) ? $args[0] : 'list';
558
		$safe_to_modify = Jetpack_Options::get_options_for_reset();
559
560
		// Jumpstart is special
561
		array_push( $safe_to_modify, 'jumpstart' );
562
563
		// Is the option flagged as unsafe?
564
		$flagged = ! in_array( $args[1], $safe_to_modify );
565
566 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
567
			/* translators: %s is a command like "prompt" */
568
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
569
		}
570
571
		if ( isset( $args[0] ) ) {
572
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
573
				$action = 'get';
574
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
575
				$action = 'delete';
576 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
577
				$action = 'update';
578
			} else {
579
				$action = 'list';
580
			}
581
		}
582
583
		// Bail if the option isn't found
584
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
585 View Code Duplication
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
586
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
587
		}
588
589
		// Let's print_r the option if it's an array
590
		// Used in the 'get' and 'list' actions
591
		$option = is_array( $option ) ? print_r( $option ) : $option;
592
593
		switch ( $action ) {
594
			case 'get':
595
				WP_CLI::success( "\t" . $option );
596
				break;
597
			case 'delete':
598
				jetpack_cli_are_you_sure( $flagged );
599
600
				Jetpack_Options::delete_option( $args[1] );
601
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
602
				break;
603
			case 'update':
604
				jetpack_cli_are_you_sure( $flagged );
605
606
				// Updating arrays would get pretty tricky...
607
				$value = Jetpack_Options::get_option( $args[1] );
608
				if ( $value && is_array( $value ) ) {
609
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
610
				}
611
612
				Jetpack_Options::update_option( $args[1], $args[2] );
613
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
614
				break;
615
			case 'list':
616
				$options_compact     = Jetpack_Options::get_option_names();
617
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
618
				$options_private     = Jetpack_Options::get_option_names( 'private' );
619
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
620
621
				// Table headers
622
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
623
624
				// List out the options and their values
625
				// Tell them if the value is empty or not
626
				// Tell them if it's an array
627
				foreach ( $options as $option ) {
628
					$value = Jetpack_Options::get_option( $option );
629
					if ( ! $value ) {
630
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
631
						continue;
632
					}
633
634
					if ( ! is_array( $value ) ) {
635
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
636
					} else if ( is_array( $value ) ) {
637
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
638
					}
639
				}
640
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
641
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
642
643
				WP_CLI::success(
644
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
645
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
646
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
647
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
648
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
649
				);
650
				break;
651
		}
652
	}
653
654
	/**
655
	 * Get the status of or start a new Jetpack sync.
656
	 *
657
	 * ## OPTIONS
658
	 *
659
	 * status : Print the current sync status
660
	 * start  : Start a full sync from this site to WordPress.com
661
	 *
662
	 * ## EXAMPLES
663
	 *
664
	 * wp jetpack sync status
665
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
666
	 *
667
	 * @synopsis <status|start> [--<field>=<value>]
668
	 */
669
	public function sync( $args, $assoc_args ) {
670
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
671
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
672
		}
673
674
		$action = isset( $args[0] ) ? $args[0] : 'status';
675
676
		switch ( $action ) {
677
			case 'status':
678
				$status = Jetpack_Sync_Actions::get_sync_status();
679
				$collection = array();
680
				foreach ( $status as $key => $item ) {
681
					$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...
682
						'option' => $key,
683
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
684
					);
685
				}
686
687
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
688
				break;
689
			case 'start':
690
				// Get the original settings so that we can restore them later
691
				$original_settings = Jetpack_Sync_Settings::get_settings();
692
693
				// Initialize sync settigns so we can sync as quickly as possible
694
				$sync_settings = wp_parse_args(
695
					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...
696
					array(
697
						'sync_wait_time' => 0,
698
						'enqueue_wait_time' => 0,
699
						'queue_max_writes_sec' => 10000,
700
						'max_queue_size_full_sync' => 100000
701
					)
702
				);
703
				Jetpack_Sync_Settings::update_settings( $sync_settings );
704
705
				// Convert comma-delimited string of modules to an array
706 View Code Duplication
				if ( ! empty( $assoc_args['modules'] ) ) {
707
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
708
709
					// Convert the array so that the keys are the module name and the value is true to indicate
710
					// that we want to sync the module
711
					$modules = array_map( '__return_true', array_flip( $modules ) );
712
				}
713
714 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
715
					if (
716
						'users' === $module_name &&
717
						isset( $assoc_args[ $module_name ] ) &&
718
						'initial' === $assoc_args[ $module_name ]
719
					) {
720
						$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...
721
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
722
						$ids = explode( ',', $assoc_args[ $module_name ] );
723
						if ( count( $ids ) > 0 ) {
724
							$modules[ $module_name ] = $ids;
725
						}
726
					}
727
				}
728
729
				if ( empty( $modules ) ) {
730
					$modules = null;
731
				}
732
733
				// Kick off a full sync
734
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
735
					if ( $modules ) {
736
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), join( ', ', array_keys( $modules ) ) ) );
737
					} else {
738
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
739
					}
740 View Code Duplication
				} else {
741
742
					// Reset sync settings to original.
743
					Jetpack_Sync_Settings::update_settings( $original_settings );
744
745
					if ( $modules ) {
746
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
747
					} else {
748
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
749
					}
750
				}
751
752
				// Keep sending to WPCOM until there's nothing to send
753
				$i = 1;
754
				do {
755
					$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...
756
					if ( is_wp_error( $result ) ) {
757
						$queue_empty_error = ( 'empty_queue_full_sync' == $result->get_error_code() );
758
						if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 == $i ) ) ) {
759
							WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
760
						}
761
					} else {
762
						if ( 1 == $i ) {
763
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
764
						} else {
765
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
766
						}
767
					}
768
					$i++;
769
				} while ( $result && ! is_wp_error( $result ) );
770
771
				// Reset sync settings to original.
772
				Jetpack_Sync_Settings::update_settings( $original_settings );
773
774
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
775
				break;
776
		}
777
	}
778
779
	/**
780
	 * List the contents of a specific Jetpack sync queue.
781
	 *
782
	 * ## OPTIONS
783
	 *
784
	 * peek : List the 100 front-most items on the queue.
785
	 *
786
	 * ## EXAMPLES
787
	 *
788
	 * wp jetpack sync_queue full_sync peek
789
	 *
790
	 * @synopsis <incremental|full_sync> <peek>
791
	 */
792
	public function sync_queue( $args, $assoc_args ) {
793
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
794
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
795
		}
796
797
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
798
		$action = isset( $args[1] ) ? $args[1] : 'peek';
799
800
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
801
		// the queue name that the code expects.
802
		$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...
803
			'incremental' => 'sync',
804
			'full'        => 'full_sync',
805
		);
806
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
807
808
		switch( $action ) {
809
			case 'peek':
810
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
811
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
812
				$items = $queue->peek( 100 );
813
814
				if ( empty( $items ) ) {
815
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
816
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
817
				} else {
818
					$collection = array();
819
					foreach ( $items as $item ) {
820
						$collection[] = array(
821
							'action'          => $item[0],
822
							'args'            => json_encode( $item[1] ),
823
							'current_user_id' => $item[2],
824
							'microtime'       => $item[3],
825
							'importing'       => (string) $item[4],
826
						);
827
					}
828
					WP_CLI\Utils\format_items(
829
						'table',
830
						$collection,
831
						array(
832
							'action',
833
							'args',
834
							'current_user_id',
835
							'microtime',
836
							'importing',
837
						)
838
					);
839
				}
840
				break;
841
		}
842
	}
843
844
	/**
845
	 * Cancel's the current Jetpack plan granted by this partner, if applicable
846
	 *
847
	 * Returns success or error JSON
848
	 *
849
	 * <token_json>
850
	 * : JSON blob of WPCOM API token
851
	 *  [--partner_tracking_id=<partner_tracking_id>]
852
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
853
	 *
854
	 *  * @synopsis <token_json> [--partner_tracking_id=<partner_tracking_id>]
855
	 */
856
	public function partner_cancel( $args, $named_args ) {
857
		list( $token_json ) = $args;
858
859 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
860
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
861
		}
862
863
		if ( isset( $token->error ) ) {
864
			$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...
865
		}
866
867
		if ( ! isset( $token->access_token ) ) {
868
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
869
		}
870
871
		if ( Jetpack::validate_sync_error_idc_option() ) {
872
			$this->partner_provision_error( new WP_Error(
873
				'site_in_safe_mode',
874
				esc_html__( 'Can not cancel a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
875
			) );
876
		}
877
878
		$site_identifier = Jetpack_Options::get_option( 'id' );
879
880
		if ( ! $site_identifier ) {
881
			$site_identifier = Jetpack::build_raw_urls( get_home_url() );
882
		}
883
884
		$request = array(
885
			'headers' => array(
886
				'Authorization' => "Bearer " . $token->access_token,
887
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
888
			),
889
			'timeout' => 60,
890
			'method'  => 'POST',
891
		);
892
893
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%s/partner-cancel', $this->get_api_host(), $site_identifier );
894 View Code Duplication
		if ( ! empty( $named_args ) && ! empty( $named_args['partner_tracking_id'] ) ) {
895
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner_tracking_id'], $url ) );
896
		}
897
898
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
899
900
		Jetpack_Options::delete_option( 'onboarding' );
901
902
		if ( is_wp_error( $result ) ) {
903
			$this->partner_provision_error( $result );
904
		}
905
906
		WP_CLI::log( wp_remote_retrieve_body( $result ) );
907
	}
908
909
	/**
910
	 * Provision a site using a Jetpack Partner license
911
	 *
912
	 * Returns JSON blob
913
	 *
914
	 * ## OPTIONS
915
	 *
916
	 * <token_json>
917
	 * : JSON blob of WPCOM API token
918
	 * [--plan=<plan_name>]
919
	 * : Slug of the requested plan, e.g. premium
920
	 * [--wpcom_user_id=<user_id>]
921
	 * : WordPress.com ID of user to connect as (must be whitelisted against partner key)
922
	 * [--wpcom_user_email=<wpcom_user_email>]
923
	 * : Override the email we send to WordPress.com for registration
924
	 * [--onboarding=<onboarding>]
925
	 * : Guide the user through an onboarding wizard
926
	 * [--force_register=<register>]
927
	 * : Whether to force a site to register
928
	 * [--force_connect=<force_connect>]
929
	 * : Force JPS to not reuse existing credentials
930
	 * [--home_url=<home_url>]
931
	 * : Overrides the home option via the home_url filter, or the WP_HOME constant
932
	 * [--site_url=<site_url>]
933
	 * : Overrides the siteurl option via the site_url filter, or the WP_SITEURL constant
934
	 * [--partner_tracking_id=<partner_tracking_id>]
935
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
936
	 *
937
	 * ## EXAMPLES
938
	 *
939
	 *     $ wp jetpack partner_provision '{ some: "json" }' premium 1
940
	 *     { success: true }
941
	 *
942
	 * @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>]
943
	 */
944
	public function partner_provision( $args, $named_args ) {
945
		list( $token_json ) = $args;
946
947 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
948
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
949
		}
950
951
		if ( isset( $token->error ) ) {
952
			$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...
953
				? $token->message
954
				: '';
955
			$this->partner_provision_error( new WP_Error( $token->error, $message ) );
956
		}
957
958
		if ( ! isset( $token->access_token ) ) {
959
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
960
		}
961
962
		require_once JETPACK__PLUGIN_DIR . '_inc/class.jetpack-provision.php';
963
964
		$body_json = Jetpack_Provision::partner_provision( $token->access_token, $named_args );
965
966
		if ( is_wp_error( $body_json ) ) {
967
			error_log( json_encode( array(
968
				'success'       => false,
969
				'error_code'    => $body_json->get_error_code(),
970
				'error_message' => $body_json->get_error_message()
971
			) ) );
972
			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...
973
		}
974
975
		WP_CLI::log( json_encode( $body_json ) );
976
	}
977
978
	/**
979
	 * Manages your Jetpack sitemap
980
	 *
981
	 * ## OPTIONS
982
	 *
983
	 * rebuild : Rebuild all sitemaps
984
	 * --purge : if set, will remove all existing sitemap data before rebuilding
985
	 *
986
	 * ## EXAMPLES
987
	 *
988
	 * wp jetpack sitemap rebuild
989
	 *
990
	 * @subcommand sitemap
991
	 * @synopsis <rebuild> [--purge]
992
	 */
993
	public function sitemap( $args, $assoc_args ) {
994
		if ( ! Jetpack::is_active() ) {
995
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
996
		}
997
		if ( ! Jetpack::is_module_active( 'sitemaps' ) ) {
998
			WP_CLI::error( __( 'Jetpack Sitemaps module is not currently active. Activate it first if you want to work with sitemaps.', 'jetpack' ) );
999
		}
1000
		if ( ! class_exists( 'Jetpack_Sitemap_Builder' ) ) {
1001
			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' ) );
1002
		}
1003
1004
		if ( isset( $assoc_args['purge'] ) && $assoc_args['purge'] ) {
1005
			$librarian = new Jetpack_Sitemap_Librarian();
1006
			$librarian->delete_all_stored_sitemap_data();
1007
		}
1008
1009
		$sitemap_builder = new Jetpack_Sitemap_Builder();
1010
		$sitemap_builder->update_sitemap();
1011
	}
1012
1013
	/**
1014
	 * Allows authorizing a user via the command line and will activate
1015
	 *
1016
	 * ## EXAMPLES
1017
	 *
1018
	 * wp jetpack authorize_user --token=123456789abcdef
1019
	 *
1020
	 * @synopsis --token=<value>
1021
	 */
1022
	public function authorize_user( $args, $named_args ) {
1023
		if ( ! is_user_logged_in() ) {
1024
			WP_CLI::error( __( 'Please select a user to authorize via the --user global argument.', 'jetpack' ) );
1025
		}
1026
1027
		if ( empty( $named_args['token'] ) ) {
1028
			WP_CLI::error( __( 'A non-empty token argument must be passed.', 'jetpack' ) );
1029
		}
1030
1031
		$token = sanitize_text_field( $named_args['token'] );
1032
1033
		$is_master_user  = ! Jetpack::is_active();
1034
		$current_user_id = get_current_user_id();
1035
1036
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
1037
1038
		WP_CLI::log( wp_json_encode( $named_args ) );
1039
1040
		if ( $is_master_user ) {
1041
			/**
1042
			 * Auto-enable SSO module for new Jetpack Start connections
1043
			*
1044
			* @since 5.0.0
1045
			*
1046
			* @param bool $enable_sso Whether to enable the SSO module. Default to true.
1047
			*/
1048
			$enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
1049
			Jetpack::handle_post_authorization_actions( $enable_sso, false );
1050
1051
			/* translators: %d is a user ID */
1052
			WP_CLI::success( sprintf( __( 'Authorized %d and activated default modules.', 'jetpack' ), $current_user_id ) );
1053
		} else {
1054
			/* translators: %d is a user ID */
1055
			WP_CLI::success( sprintf( __( 'Authorized %d.', 'jetpack' ), $current_user_id ) );
1056
		}
1057
	}
1058
1059
	/**
1060
	 * Allows calling a WordPress.com API endpoint using the current blog's token.
1061
	 *
1062
	 * ## OPTIONS
1063
	 * --resource=<resource>
1064
	 * : The resource to call with the current blog's token, where `%d` represents the current blog's ID.
1065
	 *
1066
	 * [--api_version=<api_version>]
1067
	 * : The API version to query against.
1068
	 *
1069
	 * [--base_api_path=<base_api_path>]
1070
	 * : The base API path to query.
1071
	 * ---
1072
	 * default: rest
1073
	 * ---
1074
	 *
1075
	 * [--body=<body>]
1076
	 * : A JSON encoded string representing arguments to send in the body.
1077
	 *
1078
	 * [--field=<value>]
1079
	 * : Any number of arguments that should be passed to the resource.
1080
	 *
1081
	 * [--pretty]
1082
	 * : Will pretty print the results of a successful API call.
1083
	 *
1084
	 * [--strip-success]
1085
	 * : Will remove the green success label from successful API calls.
1086
	 *
1087
	 * ## EXAMPLES
1088
	 *
1089
	 * wp jetpack call_api --resource='/sites/%d'
1090
	 */
1091
	public function call_api( $args, $named_args ) {
1092
		if ( ! Jetpack::is_active() ) {
1093
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1094
		}
1095
1096
		$consumed_args = array(
1097
			'resource',
1098
			'api_version',
1099
			'base_api_path',
1100
			'body',
1101
			'pretty',
1102
		);
1103
1104
		// Get args that should be passed to resource.
1105
		$other_args = array_diff_key( $named_args, array_flip( $consumed_args ) );
1106
1107
		$decoded_body = ! empty( $named_args['body'] )
1108
			? json_decode( $named_args['body'] )
1109
			: false;
1110
1111
		$resource_url = ( false === strpos( $named_args['resource'], '%d' ) )
1112
			? $named_args['resource']
1113
			: sprintf( $named_args['resource'], Jetpack_Options::get_option( 'id' ) );
1114
1115
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
1116
			$resource_url,
1117
			empty( $named_args['api_version'] ) ? Jetpack_Client::WPCOM_JSON_API_VERSION : $named_args['api_version'],
1118
			$other_args,
1119
			empty( $decoded_body ) ? null : $decoded_body,
1120
			$named_args['base_api_path']
1121
		);
1122
1123 View Code Duplication
		if ( is_wp_error( $response ) ) {
1124
			WP_CLI::error( sprintf(
1125
				/* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an error code, %3$s is an error message. */
1126
				__( 'Request to %1$s returned an error: (%2$d) %3$s.', 'jetpack' ),
1127
				$resource_url,
1128
				$response->get_error_code(),
1129
				$response->get_error_message()
1130
			) );
1131
		}
1132
1133
		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1134
			WP_CLI::error( sprintf(
1135
				/* translators: %1$s is an endpoint route (ex. /sites/123456), %2$d is an HTTP status code. */
1136
				__( 'Request to %1$s returned a non-200 response code: %2$d.', 'jetpack' ),
1137
				$resource_url,
1138
				wp_remote_retrieve_response_code( $response )
1139
			) );
1140
		}
1141
1142
		$output = wp_remote_retrieve_body( $response );
1143
		if ( isset( $named_args['pretty'] ) ) {
1144
			$decoded_output = json_decode( $output );
1145
			if ( $decoded_output ) {
1146
				$output = wp_json_encode( $decoded_output, JSON_PRETTY_PRINT );
1147
			}
1148
		}
1149
1150
		if ( isset( $named_args['strip-success'] ) ) {
1151
			WP_CLI::log( $output );
1152
			WP_CLI::halt( 0 );
1153
		}
1154
1155
		WP_CLI::success( $output );
1156
	}
1157
1158
	/**
1159
	 * API wrapper for getting stats from the WordPress.com API for the current site.
1160
	 *
1161
	 * ## OPTIONS
1162
	 *
1163
	 * [--quantity=<quantity>]
1164
	 * : The number of units to include.
1165
	 * ---
1166
	 * default: 30
1167
	 * ---
1168
	 *
1169
	 * [--period=<period>]
1170
	 * : The unit of time to query stats for.
1171
	 * ---
1172
	 * default: day
1173
	 * options:
1174
	 *  - day
1175
	 *  - week
1176
	 *  - month
1177
	 *  - year
1178
	 * ---
1179
	 *
1180
	 * [--date=<date>]
1181
	 * : The latest date to return stats for. Ex. - 2018-01-01.
1182
	 *
1183
	 * [--pretty]
1184
	 * : Will pretty print the results of a successful API call.
1185
	 *
1186
	 * [--strip-success]
1187
	 * : Will remove the green success label from successful API calls.
1188
	 *
1189
	 * ## EXAMPLES
1190
	 *
1191
	 * wp jetpack get_stats
1192
	 */
1193
	public function get_stats( $args, $named_args ) {
1194
		$selected_args = array_intersect_key(
1195
			$named_args,
1196
			array_flip( array(
1197
				'quantity',
1198
				'date',
1199
			) )
1200
		);
1201
1202
		// The API expects unit, but period seems to be more correct.
1203
		$selected_args['unit'] = $named_args['period'];
1204
1205
		$command = sprintf(
1206
			'jetpack call_api --resource=/sites/%d/stats/%s',
1207
			Jetpack_Options::get_option( 'id' ),
1208
			add_query_arg( $selected_args, 'visits' )
1209
		);
1210
1211
		if ( isset( $named_args['pretty'] ) ) {
1212
			$command .= ' --pretty';
1213
		}
1214
1215
		if ( isset( $named_args['strip-success'] ) ) {
1216
			$command .= ' --strip-success';
1217
		}
1218
1219
		WP_CLI::runcommand(
1220
			$command,
1221
			array(
1222
				'launch' => false, // Use the current process.
1223
			)
1224
		);
1225
	}
1226
1227
	/*
1228
	 * Allows management of publicize connections.
1229
	 *
1230
	 * ## OPTIONS
1231
	 *
1232
	 * <list|disconnect>
1233
	 * : The action to perform.
1234
	 * ---
1235
	 * options:
1236
	 *  - list
1237
	 *  - disconnect
1238
	 * ---
1239
	 *
1240
	 * [<identifier>]
1241
	 * : The connection ID or service to perform an action on.
1242
	 *
1243
	 * [--format=<format>]
1244
	 * : Allows overriding the output of the command when listing connections.
1245
	 * ---
1246
	 * default: table
1247
	 * options:
1248
	 *  - table
1249
	 *  - json
1250
	 *  - csv
1251
	 *  - yaml
1252
	 *  - ids
1253
	 *  - count
1254
	 * ---
1255
	 *
1256
	 * ## EXAMPLES
1257
	 *
1258
	 * wp jetpack publicize list
1259
	 * wp jetpack publicize list twitter
1260
	 * wp --user=1 jetpack publicize list
1261
	 * wp --user=1 jetpack publicize list twitter
1262
	 * wp jetpack publicize list 123456
1263
	 * wp jetpack publicize disconnect 123456
1264
	 * wp jetpack publicize disconnect all
1265
	 * wp jetpack publicize disconnect twitter
1266
	 */
1267
	public function publicize( $args, $named_args ) {
1268
		if ( ! Jetpack::is_active() ) {
1269
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1270
		}
1271
1272
		$action        = $args[0];
1273
		$publicize     = new Publicize();
1274
		$identifier    = ! empty( $args[1] ) ? $args[1] : false;
1275
		$services      = array_keys( $publicize->get_services() );
1276
		$id_is_service = in_array( $identifier, $services, true );
1277
1278
		switch ( $action ) {
1279
			case 'list':
1280
				$connections_to_return = array();
1281
1282
				// For the CLI command, let's return all connections when a user isn't specified. This
1283
				// differs from the logic in the Publicize class.
1284
				$option_connections = is_user_logged_in()
1285
					? (array) $publicize->get_all_connections_for_user()
1286
					: Jetpack_Options::get_option( 'publicize_connections' );
1287
1288
				foreach ( $option_connections as $service_name => $connections ) {
1289
					foreach ( (array) $connections as $id => $connection ) {
1290
						$connection['id']        = $id;
1291
						$connection['service']   = $service_name;
1292
						$connections_to_return[] = $connection;
1293
					}
1294
				}
1295
1296
				if ( $id_is_service && ! empty( $identifier ) && ! empty( $connections_to_return ) ) {
1297
					$temp_connections      = $connections_to_return;
1298
					$connections_to_return = array();
1299
1300
					foreach ( $temp_connections as $connection ) {
1301
						if ( $identifier === $connection['service'] ) {
1302
							$connections_to_return[] = $connection;
1303
						}
1304
					}
1305
				}
1306
1307
				if ( $identifier && ! $id_is_service && ! empty( $connections_to_return ) ) {
1308
					$connections_to_return = wp_list_filter( $connections_to_return, array( 'id' => $identifier ) );
1309
				}
1310
1311
				if ( empty( $connections_to_return ) ) {
1312
					return false;
1313
				}
1314
1315
				$expected_keys = array(
1316
					'id',
1317
					'service',
1318
					'user_id',
1319
					'provider',
1320
					'issued',
1321
					'expires',
1322
					'external_id',
1323
					'external_name',
1324
					'external_display',
1325
					'type',
1326
					'connection_data',
1327
				);
1328
1329
				WP_CLI\Utils\format_items( $named_args['format'], $connections_to_return, $expected_keys );
1330
				break; // list.
1331
			case 'disconnect':
1332
				if ( ! $identifier ) {
1333
					WP_CLI::error( __( 'A connection ID must be passed in order to disconnect.', 'jetpack' ) );
1334
				}
1335
1336
				// If the connection ID is 'all' then delete all connections. If the connection ID
1337
				// matches a service, delete all connections for that service.
1338
				if ( 'all' === $identifier || $id_is_service ) {
1339
					if ( 'all' === $identifier ) {
1340
						WP_CLI::log( __( "You're about to delete all publicize connections.", 'jetpack' ) );
1341
					} else {
1342
						/* translators: %s is a lowercase string for a social network. */
1343
						WP_CLI::log( sprintf( __( "You're about to delete all publicize connections to %s.", 'jetpack' ), $identifier ) );
1344
					}
1345
1346
					jetpack_cli_are_you_sure();
1347
1348
					$connections = array();
1349
					$service     = $identifier;
1350
1351
					$option_connections = is_user_logged_in()
1352
						? (array) $publicize->get_all_connections_for_user()
1353
						: Jetpack_Options::get_option( 'publicize_connections' );
1354
1355
					if ( 'all' === $service ) {
1356
						foreach ( (array) $option_connections as $service_name => $service_connections ) {
1357
							foreach ( $service_connections as $id => $connection ) {
1358
								$connections[ $id ] = $connection;
1359
							}
1360
						}
1361
					} elseif ( ! empty( $option_connections[ $service ] ) ) {
1362
						$connections = $option_connections[ $service ];
1363
					}
1364
1365
					if ( ! empty( $connections ) ) {
1366
						$count    = count( $connections );
1367
						$progress = \WP_CLI\Utils\make_progress_bar(
1368
							/* translators: %s is a lowercase string for a social network. */
1369
							sprintf( __( 'Disconnecting all connections to %s.', 'jetpack' ), $service ),
1370
							$count
1371
						);
1372
1373
						foreach ( $connections as $id => $connection ) {
1374
							if ( false === $publicize->disconnect( false, $id ) ) {
1375
								WP_CLI::error( sprintf(
1376
									/* translators: %1$d is a numeric ID and %2$s is a lowercase string for a social network. */
1377
									__( 'Publicize connection %d could not be disconnected', 'jetpack' ),
1378
									$id
1379
								) );
1380
							}
1381
1382
							$progress->tick();
1383
						}
1384
1385
						$progress->finish();
1386
1387
						if ( 'all' === $service ) {
1388
							WP_CLI::success( __( 'All publicize connections were successfully disconnected.', 'jetpack' ) );
1389
						} else {
1390
							/* translators: %s is a lowercase string for a social network. */
1391
							WP_CLI::success( __( 'All publicize connections to %s were successfully disconnected.', 'jetpack' ), $service );
1392
						}
1393
					}
1394
				} else {
1395
					if ( false !== $publicize->disconnect( false, $identifier ) ) {
1396
						/* translators: %d is a numeric ID. Example: 1234. */
1397
						WP_CLI::success( sprintf( __( 'Publicize connection %d has been disconnected.', 'jetpack' ), $identifier ) );
1398
					} else {
1399
						/* translators: %d is a numeric ID. Example: 1234. */
1400
						WP_CLI::error( sprintf( __( 'Publicize connection %d could not be disconnected.', 'jetpack' ), $identifier ) );
1401
					}
1402
				}
1403
				break; // disconnect.
1404
		}
1405
	}
1406
1407
	private function get_api_host() {
1408
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true );
1409
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
1410
	}
1411
1412
	private function partner_provision_error( $error ) {
1413
		WP_CLI::log( json_encode( array(
1414
			'success'       => false,
1415
			'error_code'    => $error->get_error_code(),
1416
			'error_message' => $error->get_error_message()
1417
		) ) );
1418
		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...
1419
	}
1420
}
1421
1422
/*
1423
 * Standard "ask for permission to continue" function.
1424
 * If action cancelled, ask if they need help.
1425
 *
1426
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
1427
 *
1428
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
1429
 * @param $error_msg string (optional)
1430
 */
1431
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
1432
	$cli = new Jetpack_CLI();
1433
1434
	// Default cancellation message
1435
	if ( ! $error_msg ) {
1436
		$error_msg =
1437
			__( 'Action cancelled. Have a question?', 'jetpack' )
1438
			. ' '
1439
			. $cli->green_open
1440
			. 'jetpack.com/support'
1441
			.  $cli->color_close;
1442
	}
1443
1444
	if ( ! $flagged ) {
1445
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
1446
	} else {
1447
		/* translators: Don't translate the word yes here. */
1448
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
1449
	}
1450
1451
	WP_CLI::line( $prompt_message );
1452
	$handle = fopen( "php://stdin", "r" );
1453
	$line = fgets( $handle );
1454
	if ( 'yes' != trim( $line ) ){
1455
		WP_CLI::error( $error_msg );
1456
	}
1457
}
1458