PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 6.4.2
Jetpack – WP Security, Backup, Speed, & Growth v6.4.2
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / class.jetpack-cli.php
jetpack Last commit date
3rd-party 7 years ago _inc 7 years ago bin 7 years ago css 7 years ago images 7 years ago json-endpoints 7 years ago languages 7 years ago logs 9 years ago modules 7 years ago sal 7 years ago scss 7 years ago sync 7 years ago views 8 years ago wpcom-test-backup 10 years ago .svnignore 12 years ago CODE-OF-CONDUCT.md 9 years ago changelog.txt 7 years ago class.frame-nonce-preview.php 9 years ago class.jetpack-admin.php 8 years ago class.jetpack-autoupdate.php 8 years ago class.jetpack-bbpress-json-api-compat.php 9 years ago class.jetpack-cli.php 7 years ago class.jetpack-client-server.php 8 years ago class.jetpack-client.php 8 years ago class.jetpack-connection-banner.php 7 years ago class.jetpack-constants.php 8 years ago class.jetpack-data.php 7 years ago class.jetpack-debugger.php 8 years ago class.jetpack-error.php 10 years ago class.jetpack-heartbeat.php 9 years ago class.jetpack-idc.php 8 years ago class.jetpack-ixr-client.php 10 years ago class.jetpack-jitm.php 8 years ago class.jetpack-modules-list-table.php 8 years ago class.jetpack-network-sites-list-table.php 9 years ago class.jetpack-network.php 8 years ago class.jetpack-options.php 7 years ago class.jetpack-post-images.php 7 years ago class.jetpack-signature.php 8 years ago class.jetpack-tracks.php 8 years ago class.jetpack-twitter-cards.php 7 years ago class.jetpack-user-agent.php 8 years ago class.jetpack-xmlrpc-server.php 7 years ago class.jetpack.php 7 years ago class.json-api-endpoints.php 7 years ago class.json-api.php 7 years ago class.photon.php 7 years ago composer.json 7 years ago functions.compat.php 7 years ago functions.gallery.php 8 years ago functions.global.php 7 years ago functions.opengraph.php 8 years ago functions.photon.php 8 years ago jetpack.php 7 years ago json-api-config.php 10 years ago json-endpoints.php 8 years ago locales.php 9 years ago phpcs.xml 7 years ago readme.txt 7 years ago require-lib.php 8 years ago uninstall.php 8 years ago wpml-config.xml 10 years ago
class.jetpack-cli.php
1462 lines
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 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 ) {
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 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@domain.com
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 );
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 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 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' ) ); // phpcs:ignore PHPCompatibility
388 break;
389 case 'activate':
390 $module = Jetpack::get_module( $module_slug );
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 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 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 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 } 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 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(
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' ) ); // phpcs:ignore PHPCompatibility
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 ),
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 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 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';
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 } 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();
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(
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( // phpcs:ignore PHPCompatibility
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 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 ) );
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 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 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 )
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 );
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 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 ); // phpcs:ignore PHPCompatibility
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 : (array) $publicize->get_all_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 ); // phpcs:ignore PHPCompatibility
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 : (array) $publicize->get_all_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( // phpcs:ignore PHPCompatibility
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 );
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