Completed
Push — update/jpo-update-widget-on-ch... ( 55f815...bb1c2c )
by
unknown
21:39 queued 09:56
created

Jetpack_Core_API_Data   F

Complexity

Total Complexity 239

Size/Duplication

Total Lines 971
Duplicated Lines 6.39 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 62
loc 971
rs 3.5555
c 0
b 0
f 0
wmc 239
lcom 1
cbo 11

9 Methods

Rating   Name   Duplication   Size   Complexity  
A process() 0 11 3
C get_module() 0 33 7
D get_all_options() 0 83 18
F update_data() 52 497 141
F _process_onboarding() 10 134 40
D handle_business_address() 0 53 11
A has_business_address_widget() 0 12 4
A _process_post_by_email() 0 21 4
C can_request() 0 30 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Core_API_Data often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Core_API_Data, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This is the base class for every Core API endpoint Jetpack uses.
4
 *
5
 */
6
class Jetpack_Core_API_Module_Toggle_Endpoint
7
	extends Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
0 ignored issues
show
Coding Style introduced by
The extends keyword must be on the same line as the class name
Loading history...
8
9
	/**
10
	 * Check if the module requires the site to be publicly accessible from WPCOM.
11
	 * If the site meets this requirement, the module is activated. Otherwise an error is returned.
12
	 *
13
	 * @since 4.3.0
14
	 *
15
	 * @param WP_REST_Request $request {
16
	 *     Array of parameters received by request.
17
	 *
18
	 *     @type string $slug Module slug.
19
	 *     @type bool   $active should module be activated.
20
	 * }
21
	 *
22
	 * @return WP_REST_Response|WP_Error A REST response if the request was served successfully, otherwise an error.
23
	 */
24
	public function process( $request ) {
25
		if ( $request['active'] ) {
26
			return $this->activate_module( $request );
27
		} else {
28
			return $this->deactivate_module( $request );
29
		}
30
	}
31
32
	/**
33
	 * If it's a valid Jetpack module, activate it.
34
	 *
35
	 * @since 4.3.0
36
	 *
37
	 * @param string|WP_REST_Request $request It's a WP_REST_Request when called from endpoint /module/<slug>/*
38
	 *                                        and a string when called from Jetpack_Core_API_Data->update_data.
39
	 * {
40
	 *     Array of parameters received by request.
41
	 *
42
	 *     @type string $slug Module slug.
43
	 * }
44
	 *
45
	 * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
46
	 */
47
	public function activate_module( $request ) {
48
		$module_slug = '';
0 ignored issues
show
Unused Code introduced by
$module_slug 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...
49
50 View Code Duplication
		if (
51
			(
52
				is_array( $request )
53
				|| is_object( $request )
54
			)
55
			&& isset( $request['slug'] )
56
		) {
57
			$module_slug = $request['slug'];
58
		} else {
59
			$module_slug = $request;
60
		}
61
62 View Code Duplication
		if ( ! Jetpack::is_module( $module_slug ) ) {
63
			return new WP_Error(
64
				'not_found',
65
				esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
66
				array( 'status' => 404 )
67
			);
68
		}
69
70 View Code Duplication
		if ( Jetpack::activate_module( $module_slug, false, false ) ) {
71
			return rest_ensure_response( array(
72
				'code' 	  => 'success',
73
				'message' => esc_html__( 'The requested Jetpack module was activated.', 'jetpack' ),
74
			) );
75
		}
76
77
		return new WP_Error(
78
			'activation_failed',
79
			esc_html__( 'The requested Jetpack module could not be activated.', 'jetpack' ),
80
			array( 'status' => 424 )
81
		);
82
	}
83
84
	/**
85
	 * If it's a valid Jetpack module, deactivate it.
86
	 *
87
	 * @since 4.3.0
88
	 *
89
	 * @param string|WP_REST_Request $request It's a WP_REST_Request when called from endpoint /module/<slug>/*
90
	 *                                        and a string when called from Jetpack_Core_API_Data->update_data.
91
	 * {
92
	 *     Array of parameters received by request.
93
	 *
94
	 *     @type string $slug Module slug.
95
	 * }
96
	 *
97
	 * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
98
	 */
99
	public function deactivate_module( $request ) {
100
		$module_slug = '';
0 ignored issues
show
Unused Code introduced by
$module_slug 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...
101
102 View Code Duplication
		if (
103
			(
104
				is_array( $request )
105
				|| is_object( $request )
106
			)
107
			&& isset( $request['slug'] )
108
		) {
109
			$module_slug = $request['slug'];
110
		} else {
111
			$module_slug = $request;
112
		}
113
114 View Code Duplication
		if ( ! Jetpack::is_module( $module_slug ) ) {
115
			return new WP_Error(
116
				'not_found',
117
				esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
118
				array( 'status' => 404 )
119
			);
120
		}
121
122 View Code Duplication
		if ( ! Jetpack::is_module_active( $module_slug ) ) {
123
			return new WP_Error(
124
				'already_inactive',
125
				esc_html__( 'The requested Jetpack module was already inactive.', 'jetpack' ),
126
				array( 'status' => 409 )
127
			);
128
		}
129
130 View Code Duplication
		if ( Jetpack::deactivate_module( $module_slug ) ) {
131
			return rest_ensure_response( array(
132
				'code' 	  => 'success',
133
				'message' => esc_html__( 'The requested Jetpack module was deactivated.', 'jetpack' ),
134
			) );
135
		}
136
		return new WP_Error(
137
			'deactivation_failed',
138
			esc_html__( 'The requested Jetpack module could not be deactivated.', 'jetpack' ),
139
			array( 'status' => 400 )
140
		);
141
	}
142
143
	/**
144
	 * Check that the current user has permissions to manage Jetpack modules.
145
	 *
146
	 * @since 4.3.0
147
	 *
148
	 * @return bool
149
	 */
150
	public function can_request() {
151
		return current_user_can( 'jetpack_manage_modules' );
152
	}
153
}
154
155
class Jetpack_Core_API_Module_List_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
156
157
	/**
158
	 * A WordPress REST API callback method that accepts a request object and decides what to do with it.
159
	 *
160
	 * @param WP_REST_Request $request The request sent to the WP REST API.
161
	 *
162
	 * @since 4.3.0
163
	 *
164
	 * @return bool|Array|WP_Error a resulting value or object, or an error.
165
	 */
166
	public function process( $request ) {
167
		if ( 'GET' === $request->get_method() ) {
168
			return $this->get_modules( $request );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Core_API_Module_...Endpoint::get_modules() has too many arguments starting with $request.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
169
		} else {
170
			return $this->activate_modules( $request );
171
		}
172
	}
173
174
	/**
175
	 * Get a list of all Jetpack modules and their information.
176
	 *
177
	 * @since 4.3.0
178
	 *
179
	 * @return array Array of Jetpack modules.
180
	 */
181
	public function get_modules() {
182
		require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php' );
183
184
		$modules = Jetpack_Admin::init()->get_modules();
185
		foreach ( $modules as $slug => $properties ) {
186
			$modules[ $slug ]['options'] =
187
				Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $slug );
188
			if (
189
				isset( $modules[ $slug ]['requires_connection'] )
190
				&& $modules[ $slug ]['requires_connection']
191
				&& Jetpack::is_development_mode()
192
			) {
193
				$modules[ $slug ]['activated'] = false;
194
			}
195
		}
196
197
		$modules = Jetpack::get_translated_modules( $modules );
198
199
		return Jetpack_Core_Json_Api_Endpoints::prepare_modules_for_response( $modules );
200
	}
201
202
	/**
203
	 * Activate a list of valid Jetpack modules.
204
	 *
205
	 * @since 4.3.0
206
	 *
207
	 * @param WP_REST_Request $request {
208
	 *     Array of parameters received by request.
209
	 *
210
	 *     @type string $slug Module slug.
211
	 * }
212
	 *
213
	 * @return bool|WP_Error True if modules were activated. Otherwise, a WP_Error instance with the corresponding error.
214
	 */
215
	public static function activate_modules( $request ) {
216
217 View Code Duplication
		if (
218
			! isset( $request['modules'] )
219
			|| ! is_array( $request['modules'] )
220
		) {
221
			return new WP_Error(
222
				'not_found',
223
				esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
224
				array( 'status' => 404 )
225
			);
226
		}
227
228
		$activated = array();
229
		$failed = array();
230
231
		foreach ( $request['modules'] as $module ) {
232
			if ( Jetpack::activate_module( $module, false, false ) ) {
233
				$activated[] = $module;
234
			} else {
235
				$failed[] = $module;
236
			}
237
		}
238
239 View Code Duplication
		if ( empty( $failed ) ) {
240
			return rest_ensure_response( array(
241
				'code' 	  => 'success',
242
				'message' => esc_html__( 'All modules activated.', 'jetpack' ),
243
			) );
244
		}
245
246
		$error = '';
247
248
		$activated_count = count( $activated );
249 View Code Duplication
		if ( $activated_count > 0 ) {
250
			$activated_last = array_pop( $activated );
251
			$activated_text = $activated_count > 1 ? sprintf(
252
				/* Translators: first variable is a list followed by the last item, which is the second variable. Example: dog, cat and bird. */
253
				__( '%1$s and %2$s', 'jetpack' ),
254
				join( ', ', $activated ), $activated_last ) : $activated_last;
255
256
			$error = sprintf(
257
				/* Translators: the variable is a module name. */
258
				_n( 'The module %s was activated.', 'The modules %s were activated.', $activated_count, 'jetpack' ),
259
				$activated_text ) . ' ';
260
		}
261
262
		$failed_count = count( $failed );
263 View Code Duplication
		if ( count( $failed ) > 0 ) {
264
			$failed_last = array_pop( $failed );
265
			$failed_text = $failed_count > 1 ? sprintf(
266
				/* Translators: first variable is a list followed by the last item, which is the second variable. Example: dog, cat and bird. */
267
				__( '%1$s and %2$s', 'jetpack' ),
268
				join( ', ', $failed ), $failed_last ) : $failed_last;
269
270
			$error = sprintf(
271
				/* Translators: the variable is a module name. */
272
				_n( 'The module %s failed to be activated.', 'The modules %s failed to be activated.', $failed_count, 'jetpack' ),
273
				$failed_text ) . ' ';
274
		}
275
276
		return new WP_Error(
277
			'activation_failed',
278
			esc_html( $error ),
279
			array( 'status' => 424 )
280
		);
281
	}
282
283
	/**
284
	 * A WordPress REST API permission callback method that accepts a request object and decides
285
	 * if the current user has enough privileges to act.
286
	 *
287
	 * @since 4.3.0
288
	 *
289
	 * @param WP_REST_Request $request The request sent to the WP REST API.
290
	 *
291
	 * @return bool does the current user have enough privilege.
292
	 */
293
	public function can_request( $request ) {
294
		if ( 'GET' === $request->get_method() ) {
295
			return current_user_can( 'jetpack_admin_page' );
296
		} else {
297
			return current_user_can( 'jetpack_manage_modules' );
298
		}
299
	}
300
}
301
302
/**
303
 * Class that manages updating of Jetpack module options and general Jetpack settings or retrieving module data.
304
 * If no module is specified, all module settings are retrieved/updated.
305
 *
306
 * @since 4.3.0
307
 * @since 4.4.0 Renamed Jetpack_Core_API_Module_Endpoint from to Jetpack_Core_API_Data.
308
 *
309
 * @author Automattic
310
 */
311
class Jetpack_Core_API_Data extends Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
312
313
	/**
314
	 * Process request by returning the module or updating it.
315
	 * If no module is specified, settings for all modules are assumed.
316
	 *
317
	 * @since 4.3.0
318
	 *
319
	 * @param WP_REST_Request $request
320
	 *
321
	 * @return bool|mixed|void|WP_Error
322
	 */
323
	public function process( $request ) {
324
		if ( 'GET' === $request->get_method() ) {
325
			if ( isset( $request['slug'] ) ) {
326
				return $this->get_module( $request );
327
			}
328
329
			return $this->get_all_options();
330
		} else {
331
			return $this->update_data( $request );
332
		}
333
	}
334
335
	/**
336
	 * Get information about a specific and valid Jetpack module.
337
	 *
338
	 * @since 4.3.0
339
	 *
340
	 * @param WP_REST_Request $request {
341
	 *     Array of parameters received by request.
342
	 *
343
	 *     @type string $slug Module slug.
344
	 * }
345
	 *
346
	 * @return mixed|void|WP_Error
347
	 */
348
	public function get_module( $request ) {
349
		if ( Jetpack::is_module( $request['slug'] ) ) {
350
351
			$module = Jetpack::get_module( $request['slug'] );
352
353
			$module['options'] = Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $request['slug'] );
354
355
			if (
356
				isset( $module['requires_connection'] )
357
				&& $module['requires_connection']
358
				&& Jetpack::is_development_mode()
359
			) {
360
				$module['activated'] = false;
361
			}
362
363
			$i18n = jetpack_get_module_i18n( $request['slug'] );
364
			if ( isset( $module['name'] ) ) {
365
				$module['name'] = $i18n['name'];
366
			}
367
			if ( isset( $module['description'] ) ) {
368
				$module['description'] = $i18n['description'];
369
				$module['short_description'] = $i18n['description'];
370
			}
371
372
			return Jetpack_Core_Json_Api_Endpoints::prepare_modules_for_response( $module );
373
		}
374
375
		return new WP_Error(
376
			'not_found',
377
			esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ),
378
			array( 'status' => 404 )
379
		);
380
	}
381
382
	/**
383
	 * Get information about all Jetpack module options and settings.
384
	 *
385
	 * @since 4.6.0
386
	 *
387
	 * @return WP_REST_Response $response
388
	 */
389
	public function get_all_options() {
390
		$response = array();
391
392
		$modules = Jetpack::get_available_modules();
393
		if ( is_array( $modules ) && ! empty( $modules ) ) {
394
			foreach ( $modules as $module ) {
395
				// Add all module options
396
				$options = Jetpack_Core_Json_Api_Endpoints::prepare_options_for_response( $module );
397
				foreach ( $options as $option_name => $option ) {
398
					$response[ $option_name ] = $option['current_value'];
399
				}
400
401
				// Add the module activation state
402
				$response[ $module ] = Jetpack::is_module_active( $module );
403
			}
404
		}
405
406
		$settings = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( 'settings' );
407
		$holiday_snow_option_name = Jetpack_Core_Json_Api_Endpoints::holiday_snow_option_name();
408
409
		if ( ! function_exists( 'is_plugin_active' ) ) {
410
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
411
		}
412
413
		foreach ( $settings as $setting => $properties ) {
414
			switch ( $setting ) {
415
				case 'lang_id':
416
					if ( defined( 'WPLANG' ) ) {
417
						// We can't affect this setting, so warn the client
418
						$response[ $setting ] = 'error_const';
419
						break;
420
					}
421
422
					if ( ! current_user_can( 'install_languages' ) ) {
423
						// The user doesn't have caps to install language packs, so warn the client
424
						$response[ $setting ] = 'error_cap';
425
						break;
426
					}
427
428
					$value = get_option( 'WPLANG' );
429
					$response[ $setting ] = empty( $value ) ? 'en_US' : $value;
430
					break;
431
432
				case $holiday_snow_option_name:
433
					$response[ $setting ] = get_option( $holiday_snow_option_name ) === 'letitsnow';
434
					break;
435
436
				case 'wordpress_api_key':
437
					// When field is clear, return empty. Otherwise it would return "false".
438
					if ( '' === get_option( 'wordpress_api_key', '' ) ) {
439
						$response[ $setting ] = '';
440
					} else {
441
						if ( ! class_exists( 'Akismet' ) ) {
442
							if ( is_readable( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
443
								require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
444
							}
445
						}
446
						$response[ $setting ] = class_exists( 'Akismet' ) ? Akismet::get_api_key() : '';
447
					}
448
					break;
449
450
				case 'onboarding':
451
					$response[ $setting ] = array(
452
						'siteTitle' => get_option( 'blogname' ),
453
						'siteDescription' => get_option( 'blogdescription' ),
454
						'siteType' => get_option( 'jpo_site_type' ),
455
						'homepageFormat' => get_option( 'jpo_homepage_format' ),
456
						'addContactForm' => intval( get_option( 'jpo_contact_page' ) ),
457
						'businessAddress' => get_option( 'jpo_business_address' ),
458
						'installWooCommerce' => is_plugin_active( 'woocommerce/woocommerce.php' ),
459
					);
460
					break;
461
462
				default:
463
					$response[ $setting ] = Jetpack_Core_Json_Api_Endpoints::cast_value( get_option( $setting ), $settings[ $setting ] );
464
					break;
465
			}
466
		}
467
468
		$response['akismet'] = is_plugin_active( 'akismet/akismet.php' );
469
470
		return rest_ensure_response( $response );
471
	}
472
473
	/**
474
	 * If it's a valid Jetpack module and configuration parameters have been sent, update it.
475
	 *
476
	 * @since 4.3.0
477
	 *
478
	 * @param WP_REST_Request $request {
479
	 *     Array of parameters received by request.
480
	 *
481
	 *     @type string $slug Module slug.
482
	 * }
483
	 *
484
	 * @return bool|WP_Error True if module was updated. Otherwise, a WP_Error instance with the corresponding error.
485
	 */
486
	public function update_data( $request ) {
487
488
		// If it's null, we're trying to update many module options from different modules.
489
		if ( is_null( $request['slug'] ) ) {
490
491
			// Value admitted by Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list that will make it return all module options.
492
			// It will not be passed. It's just checked in this method to pass that method a string or array.
493
			$request['slug'] = 'any';
494
		} else {
495 View Code Duplication
			if ( ! Jetpack::is_module( $request['slug'] ) ) {
496
				return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
497
			}
498
499 View Code Duplication
			if ( ! Jetpack::is_module_active( $request['slug'] ) ) {
500
				return new WP_Error( 'inactive', esc_html__( 'The requested Jetpack module is inactive.', 'jetpack' ), array( 'status' => 409 ) );
501
			}
502
		}
503
504
		// Get parameters to update the module. We can not simply use $request->get_params() because when we registered
505
		// this route, we are adding the entire output of Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list() to
506
		// the current request object's params. We are interested in body of the actual request.
507
		// This may be JSON:
508
		$params = $request->get_json_params();
509
		if ( ! is_array( $params ) ) {
510
			// Or it may be standard POST key-value pairs:
511
			$params = $request->get_body_params();
512
		}
513
514
		// Exit if no parameters were passed.
515
		if ( ! is_array( $params ) ) {
516
			return new WP_Error( 'missing_options', esc_html__( 'Missing options.', 'jetpack' ), array( 'status' => 404 ) );
517
		}
518
519
		// If $params was set via `get_body_params()` there may be some additional variables in the request that can
520
		// cause validation to fail. This method verifies that each param was in fact updated and will throw a `some_updated`
521
		// error if unused variables are included in the request.
522
		foreach ( array_keys( $params ) as $key ) {
523
			if ( is_int( $key ) || 'slug' === $key || 'context' === $key ) {
524
				unset( $params[ $key ] );
525
			}
526
		}
527
528
		// Get available module options.
529
		$options = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( 'any' === $request['slug']
530
			? $params
531
			: $request['slug']
532
		);
533
534
		// Prepare to toggle module if needed
535
		$toggle_module = new Jetpack_Core_API_Module_Toggle_Endpoint( new Jetpack_IXR_Client() );
536
537
		// Options that are invalid or failed to update.
538
		$invalid = array_keys( array_diff_key( $params, $options ) );
539
		$not_updated = array();
540
541
		// Remove invalid options
542
		$params = array_intersect_key( $params, $options );
543
544
		// Used if response is successful. The message can be overwritten and additional data can be added here.
545
		$response = array(
546
			'code'	  => 'success',
547
			'message' => esc_html__( 'The requested Jetpack data updates were successful.', 'jetpack' ),
548
		);
549
550
		// If there are modules to activate, activate them first so they're ready when their options are set.
551
		foreach ( $params as $option => $value ) {
552
			if ( 'modules' === $options[ $option ]['jp_group'] ) {
553
554
				// Used if there was an error. Can be overwritten with specific error messages.
555
				$error = '';
556
557
				// Set to true if the module toggling was successful.
558
				$updated = false;
559
560
				// Check if user can toggle the module.
561
				if ( $toggle_module->can_request() ) {
562
563
					// Activate or deactivate the module according to the value passed.
564
					$toggle_result = $value
565
						? $toggle_module->activate_module( $option )
566
						: $toggle_module->deactivate_module( $option );
567
568
					if (
569
						is_wp_error( $toggle_result )
570
						&& 'already_inactive' === $toggle_result->get_error_code()
571
					) {
572
573
						// If the module is already inactive, we don't fail
574
						$updated = true;
575
					} elseif ( is_wp_error( $toggle_result ) ) {
576
						$error = $toggle_result->get_error_message();
577
					} else {
578
						$updated = true;
579
					}
580
				} else {
581
					$error = Jetpack_Core_Json_Api_Endpoints::$user_permissions_error_msg;
582
				}
583
584
				// The module was not toggled.
585
				if ( ! $updated ) {
586
					$not_updated[ $option ] = $error;
587
				}
588
589
				// Remove module from list so we don't go through it again.
590
				unset( $params[ $option ] );
591
			}
592
		}
593
594
		foreach ( $params as $option => $value ) {
595
596
			// Used if there was an error. Can be overwritten with specific error messages.
597
			$error = '';
598
599
			// Set to true if the option update was successful.
600
			$updated = false;
601
602
			// Get option attributes, including the group it belongs to.
603
			$option_attrs = $options[ $option ];
604
605
			// If this is a module option and the related module isn't active for any reason, continue with the next one.
606
			if ( 'settings' !== $option_attrs['jp_group'] ) {
607 View Code Duplication
				if ( ! Jetpack::is_module( $option_attrs['jp_group'] ) ) {
608
					$not_updated[ $option ] = esc_html__( 'The requested Jetpack module was not found.', 'jetpack' );
609
					continue;
610
				}
611
612 View Code Duplication
				if (
613
					'any' !== $request['slug']
614
					&& ! Jetpack::is_module_active( $option_attrs['jp_group'] )
615
				) {
616
617
					// We only take note of skipped options when updating one module
618
					$not_updated[ $option ] = esc_html__( 'The requested Jetpack module is inactive.', 'jetpack' );
619
					continue;
620
				}
621
			}
622
623
			// Properly cast value based on its type defined in endpoint accepted args.
624
			$value = Jetpack_Core_Json_Api_Endpoints::cast_value( $value, $option_attrs );
625
626
			switch ( $option ) {
627
				case 'lang_id':
628
					if ( defined( 'WPLANG' ) || ! current_user_can( 'install_languages' ) ) {
629
						// We can't affect this setting
630
						$updated = false;
631
						break;
632
					}
633
634
					if ( $value === 'en_US' || empty( $value ) ) {
635
						return delete_option( 'WPLANG' );
636
					}
637
638
					// `wp_download_language_pack` only tries to download packs if they're not already available
639
					$language = wp_download_language_pack( $value );
640
					if ( $language === false ) {
641
						// The language pack download failed.
642
						$updated = false;
643
						break;
644
					}
645
					$updated = get_option( 'WPLANG' ) === $language ? true : update_option( 'WPLANG', $language );
646
					break;
647
648
				case 'monitor_receive_notifications':
649
					$monitor = new Jetpack_Monitor();
650
651
					// If we got true as response, consider it done.
652
					$updated = true === $monitor->update_option_receive_jetpack_monitor_notification( $value );
653
					break;
654
655
				case 'post_by_email_address':
656
					if ( 'create' == $value ) {
657
						$result = $this->_process_post_by_email(
658
							'jetpack.createPostByEmailAddress',
659
							esc_html__( 'Unable to create the Post by Email address. Please try again later.', 'jetpack' )
660
						);
661
					} elseif ( 'regenerate' == $value ) {
662
						$result = $this->_process_post_by_email(
663
							'jetpack.regeneratePostByEmailAddress',
664
							esc_html__( 'Unable to regenerate the Post by Email address. Please try again later.', 'jetpack' )
665
						);
666
					} elseif ( 'delete' == $value ) {
667
						$result = $this->_process_post_by_email(
668
							'jetpack.deletePostByEmailAddress',
669
							esc_html__( 'Unable to delete the Post by Email address. Please try again later.', 'jetpack' )
670
						);
671
					} else {
672
						$result = false;
673
					}
674
675
					// If we got an email address (create or regenerate) or 1 (delete), consider it done.
676
					if ( is_string( $result ) && preg_match( '/[a-z0-9][email protected]/', $result ) ) {
677
						$response[$option] = $result;
678
						$updated           = true;
679
					} elseif ( 1 == $result ) {
680
						$updated = true;
681
					} elseif ( is_array( $result ) && isset( $result['message'] ) ) {
682
						$error = $result['message'];
683
					}
684
					break;
685
686
				case 'jetpack_protect_key':
687
					$protect = Jetpack_Protect_Module::instance();
688
					if ( 'create' == $value ) {
689
						$result = $protect->get_protect_key();
690
					} else {
691
						$result = false;
692
					}
693
694
					// If we got one of Protect keys, consider it done.
695
					if ( preg_match( '/[a-z0-9]{40,}/i', $result ) ) {
696
						$response[$option] = $result;
697
						$updated           = true;
698
					}
699
					break;
700
701
				case 'jetpack_protect_global_whitelist':
702
					$updated = jetpack_protect_save_whitelist( explode( PHP_EOL, str_replace( array( ' ', ',' ), array( '', "\n" ), $value ) ) );
703
					if ( is_wp_error( $updated ) ) {
704
						$error = $updated->get_error_message();
705
					}
706
					break;
707
708
				case 'show_headline':
709
				case 'show_thumbnails':
710
					$grouped_options          = $grouped_options_current = (array) Jetpack_Options::get_option( 'relatedposts' );
711
					$grouped_options[$option] = $value;
712
713
					// If option value was the same, consider it done.
714
					$updated = $grouped_options_current != $grouped_options ? Jetpack_Options::update_option( 'relatedposts', $grouped_options ) : true;
715
					break;
716
717
				case 'google':
718
				case 'bing':
719
				case 'pinterest':
720 View Code Duplication
				case 'yandex':
721
					$grouped_options          = $grouped_options_current = (array) get_option( 'verification_services_codes' );
722
					$grouped_options[$option] = $value;
723
724
					// If option value was the same, consider it done.
725
					$updated = $grouped_options_current != $grouped_options ? update_option( 'verification_services_codes', $grouped_options ) : true;
726
					break;
727
728
				case 'sharing_services':
729
					if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
730
						break;
731
					}
732
733
					$sharer = new Sharing_Service();
734
735
					// If option value was the same, consider it done.
736
					$updated = $value != $sharer->get_blog_services() ? $sharer->set_blog_services( $value['visible'], $value['hidden'] ) : true;
737
					break;
738
739
				case 'button_style':
740
				case 'sharing_label':
741
				case 'show':
742
					if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
743
						break;
744
					}
745
746
					$sharer = new Sharing_Service();
747
					$grouped_options = $sharer->get_global_options();
748
					$grouped_options[ $option ] = $value;
749
					$updated = $sharer->set_global_options( $grouped_options );
750
					break;
751
752
				case 'custom':
753
					if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
754
						break;
755
					}
756
757
					$sharer = new Sharing_Service();
758
					$updated = $sharer->new_service( stripslashes( $value['sharing_name'] ), stripslashes( $value['sharing_url'] ), stripslashes( $value['sharing_icon'] ) );
759
760
					// Return new custom service
761
					$response[$option] = $updated;
762
					break;
763
764
				case 'sharing_delete_service':
765
					if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) {
766
						break;
767
					}
768
769
					$sharer = new Sharing_Service();
770
					$updated = $sharer->delete_service( $value );
771
					break;
772
773
				case 'jetpack-twitter-cards-site-tag':
774
					$value   = trim( ltrim( strip_tags( $value ), '@' ) );
775
					$updated = get_option( $option ) !== $value ? update_option( $option, $value ) : true;
776
					break;
777
778
				case 'onpublish':
779
				case 'onupdate':
780
				case 'Bias Language':
781
				case 'Cliches':
782
				case 'Complex Expression':
783
				case 'Diacritical Marks':
784
				case 'Double Negative':
785
				case 'Hidden Verbs':
786
				case 'Jargon Language':
787
				case 'Passive voice':
788
				case 'Phrases to Avoid':
789
				case 'Redundant Expression':
790
				case 'guess_lang':
791
					if ( in_array( $option, array( 'onpublish', 'onupdate' ) ) ) {
792
						$atd_option = 'AtD_check_when';
793
					} elseif ( 'guess_lang' == $option ) {
794
						$atd_option = 'AtD_guess_lang';
795
						$option     = 'true';
796
					} else {
797
						$atd_option = 'AtD_options';
798
					}
799
					$user_id                 = get_current_user_id();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 17 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...
800
					if ( ! function_exists( 'AtD_get_options' ) ) {
801
						include_once( JETPACK__PLUGIN_DIR . 'modules/after-the-deadline.php' );
802
					}
803
					$grouped_options_current = AtD_get_options( $user_id, $atd_option );
804
					unset( $grouped_options_current['name'] );
805
					$grouped_options = $grouped_options_current;
806
					if ( $value && ! isset( $grouped_options [$option] ) ) {
807
						$grouped_options [$option] = $value;
808
					} elseif ( ! $value && isset( $grouped_options [$option] ) ) {
809
						unset( $grouped_options [$option] );
810
					}
811
					// If option value was the same, consider it done, otherwise try to update it.
812
					$options_to_save = implode( ',', array_keys( $grouped_options ) );
813
					$updated         = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $options_to_save ) : true;
814
					break;
815
816
				case 'ignored_phrases':
817
				case 'unignore_phrase':
818
					$user_id         = get_current_user_id();
819
					$atd_option      = 'AtD_ignored_phrases';
820
					$grouped_options = $grouped_options_current = explode( ',', AtD_get_setting( $user_id, $atd_option ) );
821
					if ( 'ignored_phrases' == $option ) {
822
						$grouped_options = explode( ',', $value );
823
					} else {
824
						$index = array_search( $value, $grouped_options );
825
						if ( false !== $index ) {
826
							unset( $grouped_options[$index] );
827
							$grouped_options = array_values( $grouped_options );
828
						}
829
					}
830
					$ignored_phrases = implode( ',', array_filter( array_map( 'strip_tags', $grouped_options ) ) );
831
					$updated         = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $ignored_phrases ) : true;
832
					break;
833
834
				case 'admin_bar':
835
				case 'roles':
836
				case 'count_roles':
837
				case 'blog_id':
838
				case 'do_not_track':
839
				case 'hide_smile':
840 View Code Duplication
				case 'version':
841
					$grouped_options          = $grouped_options_current = (array) get_option( 'stats_options' );
842
					$grouped_options[$option] = $value;
843
844
					// If option value was the same, consider it done.
845
					$updated = $grouped_options_current != $grouped_options ? update_option( 'stats_options', $grouped_options ) : true;
846
					break;
847
848
				case Jetpack_Core_Json_Api_Endpoints::holiday_snow_option_name():
849
					$updated = get_option( $option ) != $value ? update_option( $option, (bool) $value ? 'letitsnow' : '' ) : true;
850
					break;
851
852
				case 'akismet_show_user_comments_approved':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
853
854
					// Save Akismet option '1' or '0' like it's done in akismet/class.akismet-admin.php
855
					$updated = get_option( $option ) != $value ? update_option( $option, (bool) $value ? '1' : '0' ) : true;
856
					break;
857
858
				case 'wordpress_api_key':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
859
860
					if ( ! file_exists( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
861
						$error = esc_html__( 'Please install Akismet.', 'jetpack' );
862
						$updated = false;
863
						break;
864
					}
865
866
					if ( ! defined( 'AKISMET_VERSION' ) ) {
867
						$error = esc_html__( 'Please activate Akismet.', 'jetpack' );
868
						$updated = false;
869
						break;
870
					}
871
872
					// Allow to clear the API key field
873
					if ( '' === $value ) {
874
						$updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
875
						break;
876
					}
877
878
					require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
879
					require_once WP_PLUGIN_DIR . '/akismet/class.akismet-admin.php';
880
881
					if ( class_exists( 'Akismet_Admin' ) && method_exists( 'Akismet_Admin', 'save_key' ) ) {
882
						if ( Akismet::verify_key( $value ) === 'valid' ) {
883
							$akismet_user = Akismet_Admin::get_akismet_user( $value );
884
							if ( $akismet_user ) {
885
								if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) ) {
886
									$updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
887
									break;
888
								} else {
889
									$error = esc_html__( "Akismet user status doesn't allow to update the key", 'jetpack' );
890
								}
891
							} else {
892
								$error = esc_html__( 'Invalid Akismet user', 'jetpack' );
893
							}
894
						} else {
895
							$error = esc_html__( 'Invalid Akismet key', 'jetpack' );
896
						}
897
					} else {
898
						$error = esc_html__( 'Akismet is not installed or active', 'jetpack' );
899
					}
900
					$updated = false;
901
					break;
902
903 View Code Duplication
				case 'google_analytics_tracking_id':
904
					$grouped_options = $grouped_options_current = (array) get_option( 'jetpack_wga' );
905
					$grouped_options[ 'code' ] = $value;
906
907
					// If option value was the same, consider it done.
908
					$updated = $grouped_options_current != $grouped_options ? update_option( 'jetpack_wga', $grouped_options ) : true;
909
					break;
910
911
				case 'dismiss_dash_app_card':
912 View Code Duplication
				case 'dismiss_empty_stats_card':
913
					// If option value was the same, consider it done.
914
					$updated = get_option( $option ) != $value ? update_option( $option, (bool) $value ) : true;
915
					break;
916
917
				case 'onboarding':
918
					jetpack_require_lib( 'widgets' );
919
					// Break apart and set Jetpack onboarding options.
920
					$result = $this->_process_onboarding( (array) $value );
921
					if ( empty( $result ) ) {
922
						$updated = true;
923
					} else {
924
						$error = sprintf( esc_html__( 'Onboarding failed to process: %s', 'jetpack' ), $result );
925
						$updated = false;
926
					}
927
					break;
928
929 View Code Duplication
				case 'show_welcome_for_new_plan':
930
					// If option value was the same, consider it done.
931
					$updated = get_option( $option ) !== $value ? update_option( $option, (bool) $value ) : true;
932
					break;
933
934 View Code Duplication
				default:
935
					// If option value was the same, consider it done.
936
					$updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
937
					break;
938
			}
939
940
			// The option was not updated.
941
			if ( ! $updated ) {
942
				$not_updated[ $option ] = $error;
943
			}
944
		}
945
946
		if ( empty( $invalid ) && empty( $not_updated ) ) {
947
			// The option was updated.
948
			return rest_ensure_response( $response );
949
		} else {
950
			$invalid_count = count( $invalid );
951
			$not_updated_count = count( $not_updated );
952
			$error = '';
953
			if ( $invalid_count > 0 ) {
954
				$error = sprintf(
955
				/* Translators: the plural variable is a comma-separated list. Example: dog, cat, bird. */
956
					_n( 'Invalid option: %s.', 'Invalid options: %s.', $invalid_count, 'jetpack' ),
957
					join( ', ', $invalid )
958
				);
959
			}
960
			if ( $not_updated_count > 0 ) {
961
				$not_updated_messages = array();
962
				foreach ( $not_updated as $not_updated_option => $not_updated_message ) {
963
					if ( ! empty( $not_updated_message ) ) {
964
						$not_updated_messages[] = sprintf(
965
							/* Translators: the first variable is a module option or slug, or setting. The second is the error message . */
966
							__( '%1$s: %2$s', 'jetpack' ),
967
							$not_updated_option, $not_updated_message );
968
					}
969
				}
970
				if ( ! empty( $error ) ) {
971
					$error .= ' ';
972
				}
973
				if ( ! empty( $not_updated_messages ) ) {
974
					$error .= ' ' . join( '. ', $not_updated_messages );
975
				}
976
977
			}
978
			// There was an error because some options were updated but others were invalid or failed to update.
979
			return new WP_Error( 'some_updated', esc_html( $error ), array( 'status' => 400 ) );
980
		}
981
982
	}
983
984
	/**
985
	 * Perform tasks in the site based on onboarding choices.
986
	 *
987
	 * @since 5.4.0
988
	 *
989
	 * @param array $data Onboarding choices made by user.
990
	 *
991
	 * @return string Result of onboarding processing and, if there is one, an error message.
992
	 */
993
	private function _process_onboarding( $data ) {
994
		if ( isset( $data['end'] ) && $data['end'] ) {
995
			return Jetpack::invalidate_onboarding_token()
996
				? ''
997
				: esc_html__( "The onboarding token couldn't be deleted.", 'jetpack' );
998
		}
999
1000
		$error = array();
1001
1002
		if ( ! empty( $data['siteTitle'] ) ) {
1003
			// If option value was the same, consider it done.
1004
			if ( ! ( update_option( 'blogname', $data['siteTitle'] ) || get_option( 'blogname' ) == $data['siteTitle'] ) ) {
1005
				$error[] = 'siteTitle';
1006
			}
1007
		}
1008
1009
		if ( ! empty( $data['siteDescription'] ) ) {
1010
			// If option value was the same, consider it done.
1011
			if ( ! ( update_option( 'blogdescription', $data['siteDescription'] ) || get_option( 'blogdescription' ) == $data['siteDescription'] ) ) {
1012
				$error[] = 'siteDescription';
1013
			}
1014
		}
1015
1016
		$site_title = get_option( 'blogname' );
1017
		$author = get_current_user_id() || 1;
1018
1019
		if ( ! empty( $data['siteType'] ) ) {
1020
			if ( ! ( update_option( 'jpo_site_type', $data['siteType'] ) || get_option( 'jpo_site_type' ) == $data['siteType'] ) ) {
1021
				$error[] = 'siteType';
1022
			}
1023
		}
1024
1025
		if ( isset( $data['homepageFormat'] ) ) {
1026
			// If $data['homepageFormat'] is 'posts', we have nothing to do since it's WordPress' default
1027
			if ( 'page' === $data['homepageFormat'] ) {
1028
				if ( ! ( update_option( 'show_on_front', 'page' ) || get_option( 'show_on_front' ) == 'page' ) ) {
1029
					$error[] = 'homepageFormat';
1030
				}
1031
1032
				$home = wp_insert_post( array(
1033
					'post_type'     => 'page',
1034
					/* translators: this references the home page of a site, also called front page. */
1035
					'post_title'    => esc_html_x( 'Home Page', 'The home page of a website.', 'jetpack' ),
1036
					'post_content'  => sprintf( esc_html__( 'Welcome to %s.', 'jetpack' ), $site_title ),
1037
					'post_status'   => 'publish',
1038
					'post_author'   => $author,
1039
				) );
1040 View Code Duplication
				if ( 0 == $home ) {
1041
					$error[] = 'home insert: 0';
1042
				} elseif ( is_wp_error( $home ) ) {
1043
					$error[] = 'home creation: '. $home->get_error_message();
1044
				}
1045
				if ( ! ( update_option( 'page_on_front', $home ) || get_option( 'page_on_front' ) == $home ) ) {
1046
					$error[] = 'home set';
1047
				}
1048
1049
				$blog = wp_insert_post( array(
1050
					'post_type'     => 'page',
1051
					/* translators: this references the page where blog posts are listed. */
1052
					'post_title'    => esc_html_x( 'Blog', 'The blog of a website.', 'jetpack' ),
1053
					'post_content'  => sprintf( esc_html__( 'These are the latest posts in %s.', 'jetpack' ), $site_title ),
1054
					'post_status'   => 'publish',
1055
					'post_author'   => $author,
1056
				) );
1057 View Code Duplication
				if ( 0 == $blog ) {
1058
					$error[] = 'blog insert: 0';
1059
				} elseif ( is_wp_error( $blog ) ) {
1060
					$error[] = 'blog creation: '. $blog->get_error_message();
1061
				}
1062
				if ( ! ( update_option( 'page_for_posts', $blog ) || get_option( 'page_for_posts' ) == $blog ) ) {
1063
					$error[] = 'blog set';
1064
				}
1065
			}
1066
1067
			update_option( 'jpo_homepage_format', $data['homepageFormat'] );
1068
		}
1069
1070
		// Setup contact page and add a form and/or business info
1071
		$contact_page = '';
1072
1073
		if ( isset( $data['addContactForm'] ) && $data['addContactForm'] ) {
1074
			$contact_form_module_active = Jetpack::is_module_active( 'contact-form' );
1075
			if ( ! $contact_form_module_active ) {
1076
				$contact_form_module_active = Jetpack::activate_module( 'contact-form', false, false );
1077
			}
1078
1079
			if ( $contact_form_module_active ) {
1080
				$contact_page = '[contact-form][contact-field label="' . esc_html__( 'Name', 'jetpack' ) . '" type="name" required="true" /][contact-field label="' . esc_html__( 'Email', 'jetpack' ) . '" type="email" required="true" /][contact-field label="' . esc_html__( 'Website', 'jetpack' ) . '" type="url" /][contact-field label="' . esc_html__( 'Message', 'jetpack' ) . '" type="textarea" /][/contact-form]';
1081
			} else {
1082
				$error[] = 'contact-form activate';
1083
			}
1084
		}
1085
1086
		if ( isset( $data['businessPersonal'] ) && 'business' === $data['businessPersonal'] ) {
1087
			$contact_page .= "\n" . join( "\n", $data['businessInfo'] );
1088
		}
1089
1090
		if ( ! empty( $contact_page ) ) {
1091
			$form = wp_insert_post( array(
1092
				'post_type'     => 'page',
1093
				/* translators: this references a page with contact details and possibly a form. */
1094
				'post_title'    => esc_html_x( 'Contact us', 'Contact page for your website.', 'jetpack' ),
1095
				'post_content'  => esc_html__( 'Send us a message!', 'jetpack' ) . "\n" . $contact_page,
1096
				'post_status'   => 'publish',
1097
				'post_author'   => $author,
1098
			) );
1099
			if ( 0 == $form ) {
1100
				$error[] = 'form insert: 0';
1101
			} elseif ( is_wp_error( $form ) ) {
1102
				$error[] = 'form creation: '. $form->get_error_message();
1103
			} else {
1104
				update_option( 'jpo_contact_page', $form );
1105
			}
1106
		}
1107
1108
		if ( isset( $data['businessAddress'] ) ) {
1109
			$handled_business_address = self::handle_business_address( $data['businessAddress'] );
1110
			if ( is_wp_error( $handled_business_address ) ) {
1111
				$error[] = 'BusinessAddress';
1112
			}
1113
		}
1114
1115
		if ( ! empty( $data['installWooCommerce'] ) ) {
1116
			jetpack_require_lib( 'plugins' );
1117
			$wc_install_result = Jetpack_Plugins::install_and_activate_plugin( 'woocommerce' );
1118
			if ( is_wp_error( $wc_install_result ) ) {
1119
				$error[] = 'woocommerce installation';
1120
			}
1121
		}
1122
1123
		return empty( $error )
1124
			? ''
1125
			: join( ', ', $error );
1126
	}
1127
1128
	/**
1129
	 * Add or update Business Address widget.
1130
	 *
1131
	 * @param array $address Array of business address fields.
1132
	 *
1133
	 * @return WP_Error|true True if the data was saved correctly.
1134
	*/
1135
	static function handle_business_address( $address ) {
1136
		$first_sidebar = Jetpack_Widgets::get_first_sidebar();
1137
1138
		$widgets_module_active = Jetpack::is_module_active( 'widgets' );
1139
		if ( ! $widgets_module_active ) {
1140
			$widgets_module_active = Jetpack::activate_module( 'widgets', false, false );
1141
		}
1142
		if ( ! $widgets_module_active ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $widgets_module_active of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1143
			return new WP_Error( 'module_activation_failed', 'Failed to activate the widgets module.', 400 );
1144
		}
1145
1146
		if ( $first_sidebar ) {
1147
			$title = isset( $address['name'] ) ? sanitize_text_field( $address['name'] ) : '';
1148
			$street = isset( $address['street'] ) ? sanitize_text_field( $address['street'] ) : '';
1149
			$city = isset( $address['city'] ) ? sanitize_text_field( $address['city'] ) : '';
1150
			$state = isset( $address['state'] ) ? sanitize_text_field( $address['state'] ) : '';
1151
			$zip = isset( $address['zip'] ) ? sanitize_text_field( $address['zip'] ) : '';
1152
1153
			$full_address = implode( ' ', array_filter( array( $street, $city, $state, $zip ) ) );
1154
1155
			$widget_options = array(
1156
				'title'   => $title,
1157
				'address' => $full_address,
1158
				'phone'   => '',
1159
				'hours'   => '',
1160
				'showmap' => false,
1161
				'email' => ''
1162
			);
1163
1164
			$widget_updated = '';
0 ignored issues
show
Unused Code introduced by
$widget_updated 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...
1165
			if ( ! self::has_business_address_widget( $first_sidebar ) ) {
1166
				$widget_updated  = Jetpack_Widgets::insert_widget_in_sidebar( 'widget_contact_info', $widget_options, $first_sidebar );
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...
1167
			} else {
1168
				$widget_updated = Jetpack_Widgets::update_widget_in_sidebar( 'widget_contact_info', $widget_options, $first_sidebar );
1169
			}
1170
			if ( is_wp_error( $widget_updated ) ) {
1171
				return new WP_Error( 'widget_update_failed', 'Widget could not be updated.', 400 );
1172
			}
1173
1174
			$address_save = array(
1175
				'name' => $title,
1176
				'street' => $street,
1177
				'city' => $city,
1178
				'state' => $state,
1179
				'zip' => $zip
1180
			);
1181
			update_option( 'jpo_business_address', $address_save );
1182
			return true;
1183
		}
1184
1185
		// No sidebar to place the widget
1186
		return new WP_Error( 'sidebar_not_found', 'No sidebar.', 400 );
1187
	}
1188
1189
	/**
1190
	 * Check whether "Contact Info & Map" widget is present in a given sidebar.
1191
	 *
1192
	 * @param string  $sidebar ID of the sidebar to which the widget will be added.
1193
	 *
1194
	 * @return bool Whether the widget is present in a given sidebar.
1195
	*/
1196
	static function has_business_address_widget( $sidebar ) {
1197
		$sidebars_widgets = get_option( 'sidebars_widgets', array() );
1198
		if ( ! isset( $sidebars_widgets[ $sidebar ] ) ) {
1199
			return false;
1200
		}
1201
		foreach ( $sidebars_widgets[ $sidebar ] as $widget ) {
1202
			if ( strpos( $widget, 'widget_contact_info' ) !== false ) {
1203
				return true;
1204
			}
1205
		}
1206
		return false;
1207
	}
1208
1209
	/**
1210
	 * Calls WPCOM through authenticated request to create, regenerate or delete the Post by Email address.
1211
	 * @todo: When all settings are updated to use endpoints, move this to the Post by Email module and replace __process_ajax_proxy_request.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
1212
	 *
1213
	 * @since 4.3.0
1214
	 *
1215
	 * @param string $endpoint Process to call on WPCOM to create, regenerate or delete the Post by Email address.
1216
	 * @param string $error	   Error message to return.
1217
	 *
1218
	 * @return array
1219
	 */
1220
	private function _process_post_by_email( $endpoint, $error ) {
1221
		if ( ! current_user_can( 'edit_posts' ) ) {
1222
			return array( 'message' => $error );
1223
		}
1224
1225
		$this->xmlrpc->query( $endpoint );
1226
1227
		if ( $this->xmlrpc->isError() ) {
1228
			return array( 'message' => $error );
1229
		}
1230
1231
		$response = $this->xmlrpc->getResponse();
1232
		if ( empty( $response ) ) {
1233
			return array( 'message' => $error );
1234
		}
1235
1236
		// Used only in Jetpack_Core_Json_Api_Endpoints::get_remote_value.
1237
		update_option( 'post_by_email_address' . get_current_user_id(), $response );
1238
1239
		return $response;
1240
	}
1241
1242
	/**
1243
	 * Check if user is allowed to perform the update.
1244
	 *
1245
	 * @since 4.3.0
1246
	 *
1247
	 * @param WP_REST_Request $request The request sent to the WP REST API.
1248
	 *
1249
	 * @return bool
1250
	 */
1251
	public function can_request( $request ) {
1252
		$req_params = $request->get_params();
1253
		if ( ! empty( $req_params['onboarding']['token'] ) && isset( $req_params['rest_route'] ) ) {
1254
			return Jetpack::validate_onboarding_token_action( $req_params['onboarding']['token'], $req_params['rest_route'] );
1255
		}
1256
1257
		if ( 'GET' === $request->get_method() ) {
1258
			return current_user_can( 'jetpack_admin_page' );
1259
		} else {
1260
			$module = Jetpack_Core_Json_Api_Endpoints::get_module_requested();
1261
			if ( empty( $module ) ) {
1262
				$params = $request->get_json_params();
1263
				if ( ! is_array( $params ) ) {
1264
					$params = $request->get_body_params();
1265
				}
1266
				$options = Jetpack_Core_Json_Api_Endpoints::get_updateable_data_list( $params );
1267
				foreach ( $options as $option => $definition ) {
1268
					if ( in_array( $options[ $option ]['jp_group'], array( 'after-the-deadline', 'post-by-email' ) ) ) {
1269
						$module = $options[ $option ]['jp_group'];
1270
						break;
1271
					}
1272
				}
1273
			}
1274
			// User is trying to create, regenerate or delete its PbE || ATD settings.
1275
			if ( 'post-by-email' === $module || 'after-the-deadline' === $module ) {
1276
				return current_user_can( 'edit_posts' ) && current_user_can( 'jetpack_admin_page' );
1277
			}
1278
			return current_user_can( 'jetpack_configure_modules' );
1279
		}
1280
	}
1281
}
1282
1283
class Jetpack_Core_API_Module_Data_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1284
1285
	public function process( $request ) {
1286
		switch( $request['slug'] ) {
1287
			case 'protect':
1288
				return $this->get_protect_data();
1289
			case 'stats':
1290
				return $this->get_stats_data( $request );
1291
			case 'akismet':
1292
				return $this->get_akismet_data();
1293
			case 'monitor':
1294
				return $this->get_monitor_data();
1295
			case 'verification-tools':
1296
				return $this->get_verification_tools_data();
1297
			case 'vaultpress':
1298
				return $this->get_vaultpress_data();
1299
		}
1300
	}
1301
1302
	/**
1303
	 * Decide against which service to check the key.
1304
	 *
1305
	 * @since 4.8.0
1306
	 *
1307
	 * @param WP_REST_Request $request
1308
	 *
1309
	 * @return bool
1310
	 */
1311
	public function key_check( $request ) {
1312
		switch( $request['service'] ) {
1313
			case 'akismet':
1314
				$params = $request->get_json_params();
1315
				if ( isset( $params['api_key'] ) && ! empty( $params['api_key'] ) ) {
1316
					return $this->check_akismet_key( $params['api_key'] );
1317
				}
1318
				return $this->check_akismet_key();
1319
		}
1320
		return false;
1321
	}
1322
1323
	/**
1324
	 * Get number of blocked intrusion attempts.
1325
	 *
1326
	 * @since 4.3.0
1327
	 *
1328
	 * @return mixed|WP_Error Number of blocked attempts if protection is enabled. Otherwise, a WP_Error instance with the corresponding error.
1329
	 */
1330
	public function get_protect_data() {
1331
		if ( Jetpack::is_module_active( 'protect' ) ) {
1332
			return get_site_option( 'jetpack_protect_blocked_attempts' );
1333
		}
1334
1335
		return new WP_Error(
1336
			'not_active',
1337
			esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
1338
			array( 'status' => 404 )
1339
		);
1340
	}
1341
1342
	/**
1343
	 * Get number of spam messages blocked by Akismet.
1344
	 *
1345
	 * @since 4.3.0
1346
	 *
1347
	 * @return int|string Number of spam blocked by Akismet. Otherwise, an error message.
1348
	 */
1349
	public function get_akismet_data() {
1350
		if ( ! is_wp_error( $status = $this->akismet_is_active_and_registered() ) ) {
1351
			return rest_ensure_response( Akismet_Admin::get_stats( Akismet::get_api_key() ) );
1352
		} else {
1353
			return $status->get_error_code();
1354
		}
1355
	}
1356
1357
	/**
1358
	 * Verify the Akismet API key.
1359
	 *
1360
	 * @since 4.8.0
1361
	 *
1362
	 * @param string $api_key Optional API key to check.
1363
	 *
1364
	 * @return array Information about the key. 'validKey' is true if key is valid, false otherwise.
1365
	 */
1366
	public function check_akismet_key( $api_key = '' ) {
1367
		$akismet_status = $this->akismet_class_exists();
1368
		if ( is_wp_error( $akismet_status ) ) {
1369
			return rest_ensure_response( array(
1370
				'validKey'          => false,
1371
				'invalidKeyCode'    => $akismet_status->get_error_code(),
1372
				'invalidKeyMessage' => $akismet_status->get_error_message(),
1373
			) );
1374
		}
1375
1376
		$key_status = Akismet::check_key_status( empty( $api_key ) ? Akismet::get_api_key() : $api_key );
1377
1378 View Code Duplication
		if ( ! $key_status || 'invalid' === $key_status || 'failed' === $key_status ) {
1379
			return rest_ensure_response( array(
1380
				'validKey'          => false,
1381
				'invalidKeyCode'    => 'invalid_key',
1382
				'invalidKeyMessage' => esc_html__( 'Invalid Akismet key. Please contact support.', 'jetpack' ),
1383
			) );
1384
		}
1385
1386
		return rest_ensure_response( array(
1387
			'validKey' => isset( $key_status[1] ) && 'valid' === $key_status[1]
1388
		) );
1389
	}
1390
1391
	/**
1392
	 * Check if Akismet class file exists and if class is loaded.
1393
	 *
1394
	 * @since 4.8.0
1395
	 *
1396
	 * @return bool|WP_Error Returns true if class file exists and class is loaded, WP_Error otherwise.
1397
	 */
1398
	private function akismet_class_exists() {
1399
		if ( ! file_exists( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
1400
			return new WP_Error( 'not_installed', esc_html__( 'Please install Akismet.', 'jetpack' ), array( 'status' => 400 ) );
1401
		}
1402
1403 View Code Duplication
		if ( ! class_exists( 'Akismet' ) ) {
1404
			return new WP_Error( 'not_active', esc_html__( 'Please activate Akismet.', 'jetpack' ), array( 'status' => 400 ) );
1405
		}
1406
1407
		return true;
1408
	}
1409
1410
	/**
1411
	 * Is Akismet registered and active?
1412
	 *
1413
	 * @since 4.3.0
1414
	 *
1415
	 * @return bool|WP_Error True if Akismet is active and registered. Otherwise, a WP_Error instance with the corresponding error.
1416
	 */
1417
	private function akismet_is_active_and_registered() {
1418
		if ( is_wp_error( $akismet_exists = $this->akismet_class_exists() ) ) {
1419
			return $akismet_exists;
1420
		}
1421
1422
		// What about if Akismet is put in a sub-directory or maybe in mu-plugins?
1423
		require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
1424
		require_once WP_PLUGIN_DIR . '/akismet/class.akismet-admin.php';
1425
		$akismet_key = Akismet::verify_key( Akismet::get_api_key() );
1426
1427 View Code Duplication
		if ( ! $akismet_key || 'invalid' === $akismet_key || 'failed' === $akismet_key ) {
1428
			return new WP_Error( 'invalid_key', esc_html__( 'Invalid Akismet key. Please contact support.', 'jetpack' ), array( 'status' => 400 ) );
1429
		}
1430
1431
		return true;
1432
	}
1433
1434
	/**
1435
	 * Get stats data for this site
1436
	 *
1437
	 * @since 4.1.0
1438
	 *
1439
	 * @param WP_REST_Request $request {
1440
	 *     Array of parameters received by request.
1441
	 *
1442
	 *     @type string $date Date range to restrict results to.
1443
	 * }
1444
	 *
1445
	 * @return int|string Number of spam blocked by Akismet. Otherwise, an error message.
1446
	 */
1447
	public function get_stats_data( WP_REST_Request $request ) {
1448
		// Get parameters to fetch Stats data.
1449
		$range = $request->get_param( 'range' );
1450
1451
		// If no parameters were passed.
1452
		if (
1453
			empty ( $range )
1454
			|| ! in_array( $range, array( 'day', 'week', 'month' ), true )
1455
		) {
1456
			$range = 'day';
1457
		}
1458
1459
		if ( ! function_exists( 'stats_get_from_restapi' ) ) {
1460
			require_once( JETPACK__PLUGIN_DIR . 'modules/stats.php' );
1461
		}
1462
1463
		switch ( $range ) {
1464
1465
			// This is always called first on page load
1466
			case 'day':
1467
				$initial_stats = stats_get_from_restapi();
1468
				return rest_ensure_response( array(
1469
					'general' => $initial_stats,
1470
1471
					// Build data for 'day' as if it was stats_get_from_restapi( array(), 'visits?unit=day&quantity=30' );
1472
					'day' => isset( $initial_stats->visits )
1473
						? $initial_stats->visits
1474
						: array(),
1475
				) );
1476
			case 'week':
1477
				return rest_ensure_response( array(
1478
					'week' => stats_get_from_restapi( array(), 'visits?unit=week&quantity=14' ),
1479
				) );
1480
			case 'month':
1481
				return rest_ensure_response( array(
1482
					'month' => stats_get_from_restapi( array(), 'visits?unit=month&quantity=12&' ),
1483
				) );
1484
		}
1485
	}
1486
1487
	/**
1488
	 * Get date of last downtime.
1489
	 *
1490
	 * @since 4.3.0
1491
	 *
1492
	 * @return mixed|WP_Error Number of days since last downtime. Otherwise, a WP_Error instance with the corresponding error.
1493
	 */
1494
	public function get_monitor_data() {
1495 View Code Duplication
		if ( ! Jetpack::is_module_active( 'monitor' ) ) {
1496
			return new WP_Error(
1497
				'not_active',
1498
				esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
1499
				array( 'status' => 404 )
1500
			);
1501
		}
1502
1503
		$monitor       = new Jetpack_Monitor();
1504
		$last_downtime = $monitor->monitor_get_last_downtime();
1505
		if ( is_wp_error( $last_downtime ) ) {
1506
			return $last_downtime;
1507
		} else if ( false === strtotime( $last_downtime ) ) {
1508
			return rest_ensure_response( array(
1509
				'code' => 'success',
1510
				'date' => null,
1511
			) );
1512
		} else {
1513
			return rest_ensure_response( array(
1514
				'code' => 'success',
1515
				'date' => human_time_diff( strtotime( $last_downtime ), strtotime( 'now' ) ),
1516
			) );
1517
		}
1518
	}
1519
1520
	/**
1521
	 * Get services that this site is verified with.
1522
	 *
1523
	 * @since 4.3.0
1524
	 *
1525
	 * @return mixed|WP_Error List of services that verified this site. Otherwise, a WP_Error instance with the corresponding error.
1526
	 */
1527
	public function get_verification_tools_data() {
1528 View Code Duplication
		if ( ! Jetpack::is_module_active( 'verification-tools' ) ) {
1529
			return new WP_Error(
1530
				'not_active',
1531
				esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
1532
				array( 'status' => 404 )
1533
			);
1534
		}
1535
1536
		$verification_services_codes = get_option( 'verification_services_codes' );
1537 View Code Duplication
		if (
1538
			! is_array( $verification_services_codes )
1539
			|| empty( $verification_services_codes )
1540
		) {
1541
			return new WP_Error(
1542
				'empty',
1543
				esc_html__( 'Site not verified with any service.', 'jetpack' ),
1544
				array( 'status' => 404 )
1545
			);
1546
		}
1547
1548
		$services = array();
1549
		foreach ( jetpack_verification_services() as $name => $service ) {
1550
			if ( is_array( $service ) && ! empty( $verification_services_codes[ $name ] ) ) {
1551
				switch ( $name ) {
1552
					case 'google':
1553
						$services[] = 'Google';
1554
						break;
1555
					case 'bing':
1556
						$services[] = 'Bing';
1557
						break;
1558
					case 'pinterest':
1559
						$services[] = 'Pinterest';
1560
						break;
1561
					case 'yandex':
1562
						$services[] = 'Yandex';
1563
						break;
1564
				}
1565
			}
1566
		}
1567
1568
		if ( empty( $services ) ) {
1569
			return new WP_Error(
1570
				'empty',
1571
				esc_html__( 'Site not verified with any service.', 'jetpack' ),
1572
				array( 'status' => 404 )
1573
			);
1574
		}
1575
1576
		if ( 2 > count( $services ) ) {
1577
			$message = esc_html(
1578
				sprintf(
1579
					/* translators: %s is a service name like Google, Bing, Pinterest, etc. */
1580
					__( 'Your site is verified with %s.', 'jetpack' ),
1581
					$services[0]
1582
				)
1583
			);
1584
		} else {
1585
			$copy_services = $services;
1586
			$last = count( $copy_services ) - 1;
1587
			$last_service = $copy_services[ $last ];
1588
			unset( $copy_services[ $last ] );
1589
			$message = esc_html(
1590
				sprintf(
1591
					/* translators: %1$s is a comma separated list of services, and %2$s is a single service name like Google, Bing, Pinterest, etc. */
1592
					__( 'Your site is verified with %1$s and %2$s.', 'jetpack' ),
1593
					join( ', ', $copy_services ),
1594
					$last_service
1595
				)
1596
			);
1597
		}
1598
1599
		return rest_ensure_response( array(
1600
			'code'     => 'success',
1601
			'message'  => $message,
1602
			'services' => $services,
1603
		) );
1604
	}
1605
1606
	/**
1607
	 * Get VaultPress site data including, among other things, the date of the last backup if it was completed.
1608
	 *
1609
	 * @since 4.3.0
1610
	 *
1611
	 * @return mixed|WP_Error VaultPress site data. Otherwise, a WP_Error instance with the corresponding error.
1612
	 */
1613
	public function get_vaultpress_data() {
1614 View Code Duplication
		if ( ! class_exists( 'VaultPress' ) ) {
1615
			return new WP_Error(
1616
				'not_active',
1617
				esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ),
1618
				array( 'status' => 404 )
1619
			);
1620
		}
1621
1622
		$vaultpress = new VaultPress();
1623
		if ( ! $vaultpress->is_registered() ) {
1624
			return rest_ensure_response( array(
1625
				'code'    => 'not_registered',
1626
				'message' => esc_html__( 'You need to register for VaultPress.', 'jetpack' )
1627
			) );
1628
		}
1629
1630
		$data = json_decode( base64_decode( $vaultpress->contact_service( 'plugin_data' ) ) );
1631
		if ( false == $data ) {
1632
			return rest_ensure_response( array(
1633
				'code'    => 'not_registered',
1634
				'message' => esc_html__( 'Could not connect to VaultPress.', 'jetpack' )
1635
			) );
1636
		} else if ( is_wp_error( $data ) || ! isset( $data->backups->last_backup ) ) {
1637
			return $data;
1638
		} else if ( empty( $data->backups->last_backup ) ) {
1639
			return rest_ensure_response( array(
1640
				'code'    => 'success',
1641
				'message' => esc_html__( 'VaultPress is active and will back up your site soon.', 'jetpack' ),
1642
				'data'    => $data,
1643
			) );
1644
		} else {
1645
			return rest_ensure_response( array(
1646
				'code'    => 'success',
1647
				'message' => esc_html(
1648
					sprintf(
1649
						__( 'Your site was successfully backed-up %s ago.', 'jetpack' ),
1650
						human_time_diff(
1651
							$data->backups->last_backup,
1652
							current_time( 'timestamp' )
1653
						)
1654
					)
1655
				),
1656
				'data'    => $data,
1657
			) );
1658
		}
1659
	}
1660
1661
	/**
1662
	 * A WordPress REST API permission callback method that accepts a request object and
1663
	 * decides if the current user has enough privileges to act.
1664
	 *
1665
	 * @since 4.3.0
1666
	 *
1667
	 * @return bool does a current user have enough privileges.
1668
	 */
1669
	public function can_request() {
1670
		return current_user_can( 'jetpack_admin_page' );
1671
	}
1672
}
1673
1674
/**
1675
 * Actions performed only when Gravatar Hovercards is activated through the endpoint call.
1676
 *
1677
 * @since 4.3.1
1678
 */
1679
function jetpack_do_after_gravatar_hovercards_activation() {
1680
1681
	// When Gravatar Hovercards is activated, enable them automatically.
1682
	update_option( 'gravatar_disable_hovercards', 'enabled' );
1683
}
1684
add_action( 'jetpack_activate_module_gravatar-hovercards', 'jetpack_do_after_gravatar_hovercards_activation' );
1685
1686
/**
1687
 * Actions performed only when Gravatar Hovercards is activated through the endpoint call.
1688
 *
1689
 * @since 4.3.1
1690
 */
1691
function jetpack_do_after_gravatar_hovercards_deactivation() {
1692
1693
	// When Gravatar Hovercards is deactivated, disable them automatically.
1694
	update_option( 'gravatar_disable_hovercards', 'disabled' );
1695
}
1696
add_action( 'jetpack_deactivate_module_gravatar-hovercards', 'jetpack_do_after_gravatar_hovercards_deactivation' );
1697
1698
/**
1699
 * Actions performed only when Markdown is activated through the endpoint call.
1700
 *
1701
 * @since 4.7.0
1702
 */
1703
function jetpack_do_after_markdown_activation() {
1704
1705
	// When Markdown is activated, enable support for post editing automatically.
1706
	update_option( 'wpcom_publish_posts_with_markdown', true );
1707
}
1708
add_action( 'jetpack_activate_module_markdown', 'jetpack_do_after_markdown_activation' );
1709