Completed
Push — fix/videopress-flash-rss ( 410f56...198f69 )
by
unknown
19:49 queued 09:08
created

_inc/lib/class.core-rest-api-endpoints.php (29 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Register WP REST API endpoints for Jetpack.
4
 *
5
 * @author Automattic
6
 */
7
8
/**
9
 * Disable direct access.
10
 */
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
// Load WP_Error for error messages.
16
require_once ABSPATH . '/wp-includes/class-wp-error.php';
17
18
// Register endpoints when WP REST API is initialized.
19
add_action( 'rest_api_init', array( 'Jetpack_Core_Json_Api_Endpoints', 'register_endpoints' ) );
20
21
/**
22
 * Class Jetpack_Core_Json_Api_Endpoints
23
 *
24
 * @since 4.1.0
25
 */
26
class Jetpack_Core_Json_Api_Endpoints {
27
28
	public static $user_permissions_error_msg;
29
30
	function __construct() {
31
		self::$user_permissions_error_msg = esc_html__(
32
			'You do not have the correct user permissions to perform this action.
33
			Please contact your site admin if you think this is a mistake.',
34
			'jetpack'
35
		);
36
	}
37
38
	/**
39
	 * Declare the Jetpack REST API endpoints.
40
	 *
41
	 * @since 4.1.0
42
	 */
43
	public static function register_endpoints() {
44
		// Get current connection status of Jetpack
45
		register_rest_route( 'jetpack/v4', '/connection-status', array(
46
			'methods' => WP_REST_Server::READABLE,
47
			'callback' => __CLASS__ . '::jetpack_connection_status',
48
		) );
49
50
		// Fetches a fresh connect URL
51
		register_rest_route( 'jetpack/v4', '/connect-url', array(
52
			'methods' => WP_REST_Server::READABLE,
53
			'callback' => __CLASS__ . '::build_connect_url',
54
			'permission_callback' => __CLASS__ . '::connect_url_permission_callback',
55
		) );
56
57
		// Get current user connection data
58
		register_rest_route( 'jetpack/v4', '/user-connection-data', array(
59
			'methods' => WP_REST_Server::READABLE,
60
			'callback' => __CLASS__ . '::get_user_connection_data',
61
			'permission_callback' => __CLASS__ . '::get_user_connection_data_permission_callback',
62
		) );
63
64
		// Disconnect site from WordPress.com servers
65
		register_rest_route( 'jetpack/v4', '/disconnect/site', array(
66
			'methods' => WP_REST_Server::EDITABLE,
67
			'callback' => __CLASS__ . '::disconnect_site',
68
			'permission_callback' => __CLASS__ . '::disconnect_site_permission_callback',
69
		) );
70
71
		// Disconnect/unlink user from WordPress.com servers
72
		register_rest_route( 'jetpack/v4', '/unlink', array(
73
			'methods' => WP_REST_Server::EDITABLE,
74
			'callback' => __CLASS__ . '::unlink_user',
75
			'permission_callback' => __CLASS__ . '::link_user_permission_callback',
76
			'args' => array(
77
				'id' => array(
78
					'default' => get_current_user_id(),
79
					'validate_callback' => __CLASS__  . '::validate_posint',
80
				),
81
			),
82
		) );
83
84
		register_rest_route( 'jetpack/v4', '/recheck-ssl', array(
85
			'methods' => WP_REST_Server::EDITABLE,
86
			'callback' => __CLASS__ . '::recheck_ssl',
87
		) );
88
89
		// Return all modules
90
		register_rest_route( 'jetpack/v4', '/modules', array(
91
			'methods' => WP_REST_Server::READABLE,
92
			'callback' => __CLASS__ . '::get_modules',
93
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
94
		) );
95
96
		// Return a single module
97
		register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', array(
98
			'methods' => WP_REST_Server::READABLE,
99
			'callback' => __CLASS__ . '::get_module',
100
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
101
		) );
102
103
		// Activate a module
104
		register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/activate', array(
105
			'methods' => WP_REST_Server::EDITABLE,
106
			'callback' => __CLASS__ . '::activate_module',
107
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
108
		) );
109
110
		// Deactivate a module
111
		register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/deactivate', array(
112
			'methods' => WP_REST_Server::EDITABLE,
113
			'callback' => __CLASS__ . '::deactivate_module',
114
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
115
		) );
116
117
		// Update a module
118
		register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/update', array(
119
			'methods' => WP_REST_Server::EDITABLE,
120
			'callback' => __CLASS__ . '::update_module',
121
			'permission_callback' => __CLASS__ . '::configure_modules_permission_check',
122
			'args' => self::get_module_updating_parameters(),
123
		) );
124
125
		// Activate many modules
126
		register_rest_route( 'jetpack/v4', '/modules/activate', array(
127
			'methods' => WP_REST_Server::EDITABLE,
128
			'callback' => __CLASS__ . '::activate_modules',
129
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
130
			'args' => array(
131
				'modules' => array(
132
					'default'           => '',
133
					'type'              => 'array',
134
					'required'          => true,
135
					'validate_callback' => __CLASS__ . '::validate_module_list',
136
				),
137
			),
138
		) );
139
140
		// Reset all Jetpack options
141
		register_rest_route( 'jetpack/v4', '/reset/(?P<options>[a-z\-]+)', array(
142
			'methods' => WP_REST_Server::EDITABLE,
143
			'callback' => __CLASS__ . '::reset_jetpack_options',
144
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
145
		) );
146
147
		// Return miscellaneous settings
148
		register_rest_route( 'jetpack/v4', '/settings', array(
149
			'methods' => WP_REST_Server::READABLE,
150
			'callback' => __CLASS__ . '::get_settings',
151
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
152
		) );
153
154
		// Update miscellaneous setting
155
		register_rest_route( 'jetpack/v4', '/setting/update', array(
156
			'methods' => WP_REST_Server::EDITABLE,
157
			'callback' => __CLASS__ . '::update_setting',
158
			'permission_callback' => __CLASS__ . '::update_settings',
159
		) );
160
161
		// Jumpstart
162
		register_rest_route( 'jetpack/v4', '/jumpstart/activate', array(
163
			'methods' => WP_REST_Server::EDITABLE,
164
			'callback' => __CLASS__ . '::jumpstart_activate',
165
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
166
		) );
167
168
		register_rest_route( 'jetpack/v4', '/jumpstart/deactivate', array(
169
			'methods' => WP_REST_Server::EDITABLE,
170
			'callback' => __CLASS__ . '::jumpstart_deactivate',
171
			'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
172
		) );
173
174
		// Protect: get blocked count
175
		register_rest_route( 'jetpack/v4', '/module/protect/count/get', array(
176
			'methods' => WP_REST_Server::READABLE,
177
			'callback' => __CLASS__ . '::protect_get_blocked_count',
178
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
179
		) );
180
181
		// Akismet: get spam count
182
		register_rest_route( 'jetpack/v4', '/akismet/stats/get', array(
183
			'methods'  => WP_REST_Server::READABLE,
184
			'callback' => __CLASS__ . '::akismet_get_stats_data',
185
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
186
		) );
187
188
		// Monitor: get last downtime
189
		register_rest_route( 'jetpack/v4', '/module/monitor/downtime/last', array(
190
			'methods' => WP_REST_Server::READABLE,
191
			'callback' => __CLASS__ . '::monitor_get_last_downtime',
192
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
193
		) );
194
195
		// Updates: get number of plugin updates available
196
		register_rest_route( 'jetpack/v4', '/updates/plugins', array(
197
			'methods' => WP_REST_Server::READABLE,
198
			'callback' => __CLASS__ . '::get_plugin_update_count',
199
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
200
		) );
201
202
		// Verification: get services that this site is verified with
203
		register_rest_route( 'jetpack/v4', '/module/verification-tools/services', array(
204
			'methods' => WP_REST_Server::READABLE,
205
			'callback' => __CLASS__ . '::get_verified_services',
206
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
207
		) );
208
209
		// VaultPress: get date last backup or status and actions for user to take
210
		register_rest_route( 'jetpack/v4', '/module/vaultpress/data', array(
211
			'methods' => WP_REST_Server::READABLE,
212
			'callback' => __CLASS__ . '::vaultpress_get_site_data',
213
			'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
214
		) );
215
	}
216
217
	/**
218
	 * Verify that the user can disconnect the site.
219
	 *
220
	 * @since 4.1.0
221
	 *
222
	 * @return bool|WP_Error True if user is able to disconnect the site.
223
	 */
224
	public static function disconnect_site_permission_callback() {
225
		if ( current_user_can( 'jetpack_disconnect' ) ) {
226
			return true;
227
		}
228
229
		return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
230
231
	}
232
233
	/**
234
	 * Verify that the user can get a connect/link URL
235
	 *
236
	 * @since 4.1.0
237
	 *
238
	 * @return bool|WP_Error True if user is able to disconnect the site.
239
	 */
240 View Code Duplication
	public static function connect_url_permission_callback() {
241
		if ( current_user_can( 'jetpack_connect_user' ) ) {
242
			return true;
243
		}
244
245
		return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
246
247
	}
248
249
	/**
250
	 * Verify that a user can use the link endpoint.
251
	 *
252
	 * @since 4.1.0
253
	 *
254
	 * @return bool|WP_Error True if user is able to link to WordPress.com
255
	 */
256 View Code Duplication
	public static function link_user_permission_callback() {
257
		if ( current_user_can( 'jetpack_connect_user' ) ) {
258
			return true;
259
		}
260
261
		return new WP_Error( 'invalid_user_permission_link_user', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
262
	}
263
264
	/**
265
	 * Verify that a user can get the data about the current user.
266
	 * Only those who can connect.
267
	 *
268
	 * @since 4.1.0
269
	 *
270
	 * @uses Jetpack::is_user_connected();
271
	 *
272
	 * @return bool|WP_Error True if user is able to unlink.
273
	 */
274 View Code Duplication
	public static function get_user_connection_data_permission_callback() {
275
		if ( current_user_can( 'jetpack_connect_user' ) ) {
276
			return true;
277
		}
278
279
		return new WP_Error( 'invalid_user_permission_unlink_user', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
280
	}
281
282
	/**
283
	 * Verify that a user can use the unlink endpoint.
284
	 * Either needs to be an admin of the site, or for them to be currently linked.
285
	 *
286
	 * @since 4.1.0
287
	 *
288
	 * @uses Jetpack::is_user_connected();
289
	 *
290
	 * @return bool|WP_Error True if user is able to unlink.
291
	 */
292
	public static function unlink_user_permission_callback() {
293
		if ( current_user_can( 'jetpack_connect' ) || Jetpack::is_user_connected( get_current_user_id() ) ) {
294
			return true;
295
		}
296
297
		return new WP_Error( 'invalid_user_permission_unlink_user', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
298
	}
299
300
	/**
301
	 * Verify that user can manage Jetpack modules.
302
	 *
303
	 * @since 4.1.0
304
	 *
305
	 * @return bool Whether user has the capability 'jetpack_manage_modules'.
306
	 */
307
	public static function manage_modules_permission_check() {
308
		if ( current_user_can( 'jetpack_manage_modules' ) ) {
309
			return true;
310
		}
311
312
		return new WP_Error( 'invalid_user_permission_manage_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
313
	}
314
315
	/**
316
	 * Verify that user can update Jetpack modules.
317
	 *
318
	 * @since 4.1.0
319
	 *
320
	 * @return bool Whether user has the capability 'jetpack_configure_modules'.
321
	 */
322
	public static function configure_modules_permission_check() {
323
		if ( current_user_can( 'jetpack_configure_modules' ) ) {
324
			return true;
325
		}
326
327
		return new WP_Error( 'invalid_user_permission_configure_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
328
	}
329
330
	/**
331
	 * Verify that user can view Jetpack admin page.
332
	 *
333
	 * @since 4.1.0
334
	 *
335
	 * @return bool Whether user has the capability 'jetpack_admin_page'.
336
	 */
337
	public static function view_admin_page_permission_check() {
338
		if ( current_user_can( 'jetpack_admin_page' ) ) {
339
			return true;
340
		}
341
342
		return new WP_Error( 'invalid_user_permission_view_admin', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
343
	}
344
345
	/**
346
	 * Verify that user can update Jetpack options.
347
	 *
348
	 * @since 4.1.0
349
	 *
350
	 * @return bool Whether user has the capability 'jetpack_admin_page'.
351
	 */
352
	public static function update_settings() {
353
		if ( current_user_can( 'manage_options' ) ) {
354
			return true;
355
		}
356
357
		return new WP_Error( 'invalid_user_permission_manage_settings', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) );
358
	}
359
360
	/**
361
	 * Contextual HTTP error code for authorization failure.
362
	 *
363
	 * Taken from rest_authorization_required_code() in WP-API plugin until is added to core.
364
	 * @see https://github.com/WP-API/WP-API/commit/7ba0ae6fe4f605d5ffe4ee85b1cd5f9fb46900a6
365
	 *
366
	 * @since 4.1.0
367
	 *
368
	 * @return int
369
	 */
370
	public static function rest_authorization_required_code() {
371
		return is_user_logged_in() ? 403 : 401;
372
	}
373
374
	/**
375
	 * Get connection status for this Jetpack site.
376
	 *
377
	 * @since 4.1.0
378
	 *
379
	 * @return bool True if site is connected
380
	 */
381
	public static function jetpack_connection_status() {
382
		if ( Jetpack::is_development_mode() ) {
383
			return rest_ensure_response( 'dev' );
384
		}
385
		return Jetpack::is_active();
386
	}
387
388
	public static function recheck_ssl() {
389
		$result = Jetpack::permit_ssl( true );
390
		return array(
391
			'enabled' => $result,
392
			'message' => get_transient( 'jetpack_https_test_message' )
393
		);
394
	}
395
396
	/**
397
	 * Disconnects Jetpack from the WordPress.com Servers
398
	 *
399
	 * @uses Jetpack::disconnect();
400
	 * @since 4.1.0
401
	 * @return bool|WP_Error True if Jetpack successfully disconnected.
402
	 */
403
	public static function disconnect_site() {
404
		if ( Jetpack::is_active() ) {
405
			Jetpack::disconnect();
406
			return rest_ensure_response( array( 'code' => 'success' ) );
407
		}
408
409
		return new WP_Error( 'disconnect_failed', esc_html__( 'Was not able to disconnect the site.  Please try again.', 'jetpack' ), array( 'status' => 400 ) );
410
	}
411
412
	/**
413
	 * Gets a new connect URL with fresh nonce
414
	 *
415
	 * @uses Jetpack::disconnect();
416
	 * @since 4.1.0
417
	 * @return bool|WP_Error True if Jetpack successfully disconnected.
418
	 */
419
	public static function build_connect_url() {
420
		if ( require_once( ABSPATH . 'wp-admin/includes/plugin.php' ) ) {
421
			$url = Jetpack::init()->build_connect_url( true, true, false );
422
			return rest_ensure_response( $url );
423
		}
424
425
		return new WP_Error( 'build_connect_url_failed', esc_html__( 'Unable to build the connect URL.  Please reload the page and try again.', 'jetpack' ), array( 'status' => 400 ) );
426
	}
427
428
	/**
429
	 * Get miscellaneous settings for this Jetpack installation, like Holiday Snow.
430
	 *
431
	 * @since 4.1.0
432
	 *
433
	 * @return object $response {
434
	 *     Array of miscellaneous settings.
435
	 *
436
	 *     @type bool $holiday-snow Did Jack steal Christmas?
437
	 * }
438
	 */
439
	public static function get_settings() {
440
		$response = array(
441
			jetpack_holiday_snow_option_name() => get_option( jetpack_holiday_snow_option_name() ) == 'letitsnow',
442
		);
443
		return rest_ensure_response( $response );
444
	}
445
446
	/**
447
	 * Get miscellaneous user data related to the connection. Similar data available in old "My Jetpack".
448
	 * Information about the master/primary user.
449
	 * Information about the current user.
450
	 *
451
	 * @since 4.1.0
452
	 *
453
	 * @return object
454
	 */
455
	public static function get_user_connection_data() {
456
		require_once( JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class.jetpack-react-page.php' );
457
458
		$response = array(
459
			'othersLinked' => jetpack_get_other_linked_users(),
460
			'currentUser'  => jetpack_current_user_data(),
461
		);
462
		return rest_ensure_response( $response );
463
	}
464
465
466
467
	/**
468
	 * Update a single miscellaneous setting for this Jetpack installation, like Holiday Snow.
469
	 *
470
	 * @since 4.1.0
471
	 *
472
	 * @param WP_REST_Request $data
473
	 *
474
	 * @return object Jetpack miscellaneous settings.
475
	 */
476
	public static function update_setting( $data ) {
477
		// Get parameters to update the module.
478
		$param = $data->get_json_params();
479
480
		// Exit if no parameters were passed.
481 View Code Duplication
		if ( ! is_array( $param ) ) {
482
			return new WP_Error( 'missing_setting', esc_html__( 'Missing setting.', 'jetpack' ), array( 'status' => 404 ) );
483
		}
484
485
		// Get option name and value.
486
		$option = key( $param );
487
		$value  = current( $param );
488
489
		// Log success or not
490
		$updated = false;
491
492
		switch ( $option ) {
493
			case jetpack_holiday_snow_option_name():
494
				$updated = update_option( $option, ( true == (bool) $value ) ? 'letitsnow' : '' );
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
495
				break;
496
		}
497
498
		if ( $updated ) {
499
			return rest_ensure_response( array(
500
				'code' 	  => 'success',
501
				'message' => esc_html__( 'Setting updated.', 'jetpack' ),
502
				'value'   => $value,
503
			) );
504
		}
505
506
		return new WP_Error( 'setting_not_updated', esc_html__( 'The setting was not updated.', 'jetpack' ), array( 'status' => 400 ) );
507
	}
508
509
	/**
510
	 * Unlinks a user from the WordPress.com Servers.
511
	 * Default $data['id'] will default to current_user_id if no value is given.
512
	 *
513
	 * Example: '/unlink?id=1234'
514
	 *
515
	 * @since 4.1.0
516
	 * @uses  Jetpack::unlink_user
517
	 *
518
	 * @param WP_REST_Request $data {
519
	 *     Array of parameters received by request.
520
	 *
521
	 *     @type int $id ID of user to unlink.
522
	 * }
523
	 *
524
	 * @return bool|WP_Error True if user successfully unlinked.
525
	 */
526
	public static function unlink_user( $data ) {
527
		if ( isset( $data['id'] ) && Jetpack::unlink_user( $data['id'] ) ) {
528
			return rest_ensure_response(
529
				array(
530
					'code' => 'success'
531
				)
532
			);
533
		}
534
535
		return new WP_Error( 'unlink_user_failed', esc_html__( 'Was not able to unlink the user.  Please try again.', 'jetpack' ), array( 'status' => 400 ) );
536
	}
537
538
	/**
539
	 * Is Akismet registered and active?
540
	 *
541
	 * @since 4.1.0
542
	 *
543
	 * @return bool|WP_Error True if Akismet is active and registered. Otherwise, a WP_Error instance with the corresponding error.
544
	 */
545
	public static function akismet_is_active_and_registered() {
546
		if ( ! file_exists( WP_PLUGIN_DIR . '/akismet/class.akismet.php' ) ) {
547
			return new WP_Error( 'not_installed', esc_html__( 'Please install Akismet.', 'jetpack' ), array( 'status' => 400 ) );
548
		}
549
550
		if ( ! class_exists( 'Akismet' ) ) {
551
			return new WP_Error( 'not_active', esc_html__( 'Please activate Akismet.', 'jetpack' ), array( 'status' => 400 ) );
552
		}
553
554
		// What about if Akismet is put in a sub-directory or maybe in mu-plugins?
555
		require_once WP_PLUGIN_DIR . '/akismet/class.akismet.php';
556
		require_once WP_PLUGIN_DIR . '/akismet/class.akismet-admin.php';
557
		$akismet_key = Akismet::verify_key( Akismet::get_api_key() );
558
559
		if ( ! $akismet_key || 'invalid' === $akismet_key || 'failed' === $akismet_key ) {
560
			return new WP_Error( 'invalid_key', esc_html__( 'Invalid Akismet key. Please contact support.', 'jetpack' ), array( 'status' => 400 ) );
561
		}
562
563
		return true;
564
	}
565
566
	/**
567
	 * Get a list of all Jetpack modules and their information.
568
	 *
569
	 * @since 4.1.0
570
	 *
571
	 * @return array Array of Jetpack modules.
572
	 */
573
	public static function get_modules() {
574
		require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php' );
575
576
		$modules = Jetpack_Admin::init()->get_modules();
577
		foreach ( $modules as $slug => $properties ) {
578
			$modules[ $slug ]['options'] = self::prepare_options_for_response( $slug );
579
		}
580
581
		return $modules;
582
	}
583
584
	/**
585
	 * Get information about a specific and valid Jetpack module.
586
	 *
587
	 * @since 4.1.0
588
	 *
589
	 * @param WP_REST_Request $data {
590
	 *     Array of parameters received by request.
591
	 *
592
	 *     @type string $slug Module slug.
593
	 * }
594
	 *
595
	 * @return mixed|void|WP_Error
596
	 */
597
	public static function get_module( $data ) {
598
		if ( Jetpack::is_module( $data['slug'] ) ) {
599
600
			$module = Jetpack::get_module( $data['slug'] );
601
602
			$module['options'] = self::prepare_options_for_response( $data['slug'] );
603
604
			return $module;
605
		}
606
607
		return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
608
	}
609
610
	/**
611
	 * If it's a valid Jetpack module, activate it.
612
	 *
613
	 * @since 4.1.0
614
	 *
615
	 * @param WP_REST_Request $data {
616
	 *     Array of parameters received by request.
617
	 *
618
	 *     @type string $slug Module slug.
619
	 * }
620
	 *
621
	 * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
622
	 */
623
	public static function activate_module( $data ) {
624
		if ( Jetpack::is_module( $data['slug'] ) ) {
625 View Code Duplication
			if ( Jetpack::activate_module( $data['slug'], false, false ) ) {
626
				return rest_ensure_response( array(
627
					'code' 	  => 'success',
628
					'message' => esc_html__( 'The requested Jetpack module was activated.', 'jetpack' ),
629
				) );
630
			}
631
			return new WP_Error( 'activation_failed', esc_html__( 'The requested Jetpack module could not be activated.', 'jetpack' ), array( 'status' => 424 ) );
632
		}
633
634
		return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
635
	}
636
637
	/**
638
	 * Activate a list of valid Jetpack modules.
639
	 *
640
	 * @since 4.1.0
641
	 *
642
	 * @param WP_REST_Request $data {
643
	 *     Array of parameters received by request.
644
	 *
645
	 *     @type string $slug Module slug.
646
	 * }
647
	 *
648
	 * @return bool|WP_Error True if modules were activated. Otherwise, a WP_Error instance with the corresponding error.
649
	 */
650
	public static function activate_modules( $data ) {
651
		$params = $data->get_json_params();
652
		if ( isset( $params['modules'] ) && is_array( $params['modules'] ) ) {
653
			$activated = array();
654
			$failed = array();
655
656
			foreach ( $params['modules'] as $module ) {
657
				if ( Jetpack::activate_module( $module, false, false ) ) {
658
					$activated[] = $module;
659
				} else {
660
					$failed[] = $module;
661
				}
662
			}
663
664
			if ( empty( $failed ) ) {
665
				return rest_ensure_response( array(
666
					'code' 	  => 'success',
667
					'message' => esc_html__( 'All modules activated.', 'jetpack' ),
668
				) );
669
			} else {
670
				$error = '';
671
672
				$activated_count = count( $activated );
673 View Code Duplication
				if ( $activated_count > 0 ) {
674
					$activated_last = array_pop( $activated );
675
					$activated_text = $activated_count > 1 ? sprintf(
676
						/* Translators: first variable is a list followed by a last item. Example: dog, cat and bird. */
677
						__( '%s and %s', 'jetpack' ),
678
						join( ', ', $activated ), $activated_last ) : $activated_last;
679
680
					$error = sprintf(
681
						/* Translators: the plural variable is a list followed by a last item. Example: dog, cat and bird. */
682
						_n( 'The module %s was activated.', 'The modules %s were activated.', $activated_count, 'jetpack' ),
683
						$activated_text ) . ' ';
684
				}
685
686
				$failed_count = count( $failed );
687 View Code Duplication
				if ( count( $failed ) > 0 ) {
688
					$failed_last = array_pop( $failed );
689
					$failed_text = $failed_count > 1 ? sprintf(
690
						/* Translators: first variable is a list followed by a last item. Example: dog, cat and bird. */
691
						__( '%s and %s', 'jetpack' ),
692
						join( ', ', $failed ), $failed_last ) : $failed_last;
693
694
					$error = sprintf(
695
						/* Translators: the plural variable is a list followed by a last item. Example: dog, cat and bird. */
696
						_n( 'The module %s failed to be activated.', 'The modules %s failed to be activated.', $failed_count, 'jetpack' ),
697
						$failed_text ) . ' ';
698
				}
699
			}
700
			return new WP_Error( 'activation_failed', esc_html( $error ), array( 'status' => 424 ) );
701
		}
702
703
		return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
704
	}
705
706
	/**
707
	 * Reset Jetpack options
708
	 *
709
	 * @since 4.1.0
710
	 *
711
	 * @param WP_REST_Request $data {
712
	 *     Array of parameters received by request.
713
	 *
714
	 *     @type string $options Available options to reset are options|modules
715
	 * }
716
	 *
717
	 * @return bool|WP_Error True if options were reset. Otherwise, a WP_Error instance with the corresponding error.
718
	 */
719
	public static function reset_jetpack_options( $data ) {
720
		if ( isset( $data['options'] ) ) {
721
			$data = $data['options'];
722
723
			switch( $data ) {
724
				case ( 'options' ) :
725
					$options_to_reset = Jetpack::get_jetpack_options_for_reset();
726
727
					// Reset the Jetpack options
728
					foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
729
						Jetpack_Options::delete_option( $option_to_reset );
730
					}
731
732
					foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
733
						delete_option( $option_to_reset );
734
					}
735
736
					// Reset to default modules
737
					$default_modules = Jetpack::get_default_modules();
738
					Jetpack_Options::update_option( 'active_modules', $default_modules );
739
740
					// Jumpstart option is special
741
					Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
742
					return rest_ensure_response( array(
743
						'code' 	  => 'success',
744
						'message' => esc_html__( 'Jetpack options reset.', 'jetpack' ),
745
					) );
746
					break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
747
748
				case 'modules':
749
					$default_modules = Jetpack::get_default_modules();
750
					Jetpack_Options::update_option( 'active_modules', $default_modules );
751
752
					return rest_ensure_response( array(
753
						'code' 	  => 'success',
754
						'message' => esc_html__( 'Modules reset to default.', 'jetpack' ),
755
					) );
756
					break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
757
758
				default:
759
					return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
760
			}
761
		}
762
763
		return new WP_Error( 'required_param', esc_html__( 'Missing parameter "type".', 'jetpack' ), array( 'status' => 404 ) );
764
	}
765
766
	/**
767
	 * Activates a series of valid Jetpack modules and initializes some options.
768
	 *
769
	 * @since 4.1.0
770
	 *
771
	 * @param WP_REST_Request $data {
772
	 *     Array of parameters received by request.
773
	 * }
774
	 *
775
	 * @return bool|WP_Error True if Jumpstart succeeded. Otherwise, a WP_Error instance with the corresponding error.
776
	 */
777
	public static function jumpstart_activate( $data ) {
0 ignored issues
show
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
778
		$modules = Jetpack::get_available_modules();
779
		$activate_modules = array();
780
		foreach ( $modules as $module ) {
781
			$module_info = Jetpack::get_module( $module );
782
			if ( isset( $module_info['feature'] ) && is_array( $module_info['feature'] ) && in_array( 'Jumpstart', $module_info['feature'] ) ) {
783
				$activate_modules[] = $module;
784
			}
785
		}
786
787
		// Collect success/error messages like modules that are properly activated.
788
		$result = array(
789
			'activated_modules' => array(),
790
			'failed_modules'    => array(),
791
		);
792
793
		// Update the jumpstart option
794
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
795
			$result['jumpstart_activated'] = Jetpack_Options::update_option( 'jumpstart', 'jumpstart_activated' );
796
		}
797
798
		// Check for possible conflicting plugins
799
		$module_slugs_filtered = Jetpack::init()->filter_default_modules( $activate_modules );
800
801
		foreach ( $module_slugs_filtered as $module_slug ) {
802
			Jetpack::log( 'activate', $module_slug );
803
			if ( Jetpack::activate_module( $module_slug, false, false ) ) {
804
				$result['activated_modules'][] = $module_slug;
805
			} else {
806
				$result['failed_modules'][] = $module_slug;
807
			}
808
			Jetpack::state( 'message', 'no_message' );
809
		}
810
811
		// Set the default sharing buttons and set to display on posts if none have been set.
812
		$sharing_services = get_option( 'sharing-services' );
813
		$sharing_options  = get_option( 'sharing-options' );
814
		if ( empty( $sharing_services['visible'] ) ) {
815
			// Default buttons to set
816
			$visible = array(
817
				'twitter',
818
				'facebook',
819
				'google-plus-1',
820
			);
821
			$hidden = array();
822
823
			// Set some sharing settings
824
			$sharing = new Sharing_Service();
825
			$sharing_options['global'] = array(
826
				'button_style'  => 'icon',
827
				'sharing_label' => $sharing->default_sharing_label,
828
				'open_links'    => 'same',
829
				'show'          => array( 'post' ),
830
				'custom'        => isset( $sharing_options['global']['custom'] ) ? $sharing_options['global']['custom'] : array()
831
			);
832
833
			$result['sharing_options']  = update_option( 'sharing-options', $sharing_options );
834
			$result['sharing_services'] = update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) );
835
		}
836
837
		// If all Jumpstart modules were activated
838
		if ( empty( $result['failed_modules'] ) ) {
839
			return rest_ensure_response( array(
840
				'code' 	  => 'success',
841
				'message' => esc_html__( 'Jumpstart done.', 'jetpack' ),
842
				'data'    => $result,
843
			) );
844
		}
845
846
		return new WP_Error( 'jumpstart_failed', esc_html( sprintf( _n( 'Jumpstart failed activating this module: %s.', 'Jumpstart failed activating these modules: %s.', count( $result['failed_modules'] ), 'jetpack' ), join( ', ', $result['failed_modules'] ) ) ), array( 'status' => 400 ) );
847
	}
848
849
	/**
850
	 * Dismisses Jumpstart so user is not prompted to go through it again.
851
	 *
852
	 * @since 4.1.0
853
	 *
854
	 * @param WP_REST_Request $data {
855
	 *     Array of parameters received by request.
856
	 * }
857
	 *
858
	 * @return bool|WP_Error True if Jumpstart was disabled or was nothing to dismiss. Otherwise, a WP_Error instance with a message.
859
	 */
860
	public static function jumpstart_deactivate( $data ) {
0 ignored issues
show
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
861
862
		// If dismissed, flag the jumpstart option as such.
863
		if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) {
864
			if ( Jetpack_Options::update_option( 'jumpstart', 'jumpstart_dismissed' ) ) {
865
				return rest_ensure_response( array(
866
					'code' 	  => 'success',
867
					'message' => esc_html__( 'Jumpstart dismissed.', 'jetpack' ),
868
				) );
869
			} else {
870
				return new WP_Error( 'jumpstart_failed_dismiss', esc_html__( 'Jumpstart could not be dismissed.', 'jetpack' ), array( 'status' => 400 ) );
871
			}
872
		}
873
874
		// If this was not a new connection and there was nothing to dismiss, don't fail.
875
		return rest_ensure_response( array(
876
			'code' 	  => 'success',
877
			'message' => esc_html__( 'Nothing to dismiss. This was not a new connection.', 'jetpack' ),
878
		) );
879
	}
880
881
	/**
882
	 * If it's a valid Jetpack module, deactivate it.
883
	 *
884
	 * @since 4.1.0
885
	 *
886
	 * @param WP_REST_Request $data {
887
	 *     Array of parameters received by request.
888
	 *
889
	 *     @type string $slug Module slug.
890
	 * }
891
	 *
892
	 * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error.
893
	 */
894
	public static function deactivate_module( $data ) {
895
		if ( Jetpack::is_module( $data['slug'] ) ) {
896 View Code Duplication
			if ( ! Jetpack::is_module_active( $data['slug'] ) ) {
897
				return new WP_Error( 'already_inactive', esc_html__( 'The requested Jetpack module was already inactive.', 'jetpack' ), array( 'status' => 409 ) );
898
			}
899 View Code Duplication
			if ( Jetpack::deactivate_module( $data['slug'] ) ) {
900
				return rest_ensure_response( array(
901
					'code' 	  => 'success',
902
					'message' => esc_html__( 'The requested Jetpack module was deactivated.', 'jetpack' ),
903
				) );
904
			}
905
			return new WP_Error( 'deactivation_failed', esc_html__( 'The requested Jetpack module could not be deactivated.', 'jetpack' ), array( 'status' => 400 ) );
906
		}
907
908
		return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
909
	}
910
911
	/**
912
	 * If it's a valid Jetpack module and configuration parameters have been sent, update it.
913
	 *
914
	 * @since 4.1.0
915
	 *
916
	 * @param WP_REST_Request $data {
917
	 *     Array of parameters received by request.
918
	 *
919
	 *     @type string $slug Module slug.
920
	 * }
921
	 *
922
	 * @return bool|WP_Error True if module was updated. Otherwise, a WP_Error instance with the corresponding error.
923
	 */
924
	public static function update_module( $data ) {
925
		if ( ! Jetpack::is_module( $data['slug'] ) ) {
926
			return new WP_Error( 'not_found', esc_html__( 'The requested Jetpack module was not found.', 'jetpack' ), array( 'status' => 404 ) );
927
		}
928
929 View Code Duplication
		if ( ! Jetpack::is_module_active( $data['slug'] ) ) {
930
			return new WP_Error( 'inactive', esc_html__( 'The requested Jetpack module is inactive.', 'jetpack' ), array( 'status' => 409 ) );
931
		}
932
933
		// Get parameters to update the module.
934
		$param = $data->get_json_params();
935
936
		// Exit if no parameters were passed.
937 View Code Duplication
		if ( ! is_array( $param ) ) {
938
			return new WP_Error( 'missing_option', esc_html__( 'Missing option.', 'jetpack' ), array( 'status' => 404 ) );
939
		}
940
941
		// Get option name and value.
942
		$option = key( $param );
943
		$value  = current( $param );
944
945
		// Get available module options.
946
		$options = self::get_module_available_options();
947
948
		// If option is invalid, don't go any further.
949
		if ( ! in_array( $option, array_keys( $options ) ) ) {
950
			return new WP_Error( 'invalid_param', esc_html(	sprintf( __( 'The option %s is invalid for this module.', 'jetpack' ), $option ) ), array( 'status' => 404 ) );
951
		}
952
953
		// Used if response is successful. The message can be overwritten and additional data can be added here.
954
		$response = array(
955
			'code' 	  => 'success',
956
			'message' => esc_html__( 'The requested Jetpack module was updated.', 'jetpack' ),
957
		);
958
959
		// Used if there was an error. Can be overwritten with specific error messages.
960
		$error = sprintf( __( 'The option %s was not updated.', 'jetpack' ), $option );
961
962
		// Set to true if the option update was successful.
963
		$updated = false;
964
965
		// Properly cast value based on its type defined in endpoint accepted args.
966
		$value = self::cast_value( $value, $options[ $option ] );
967
968
		switch ( $option ) {
969
			case 'monitor_receive_notifications':
970
				$monitor = new Jetpack_Monitor();
971
972
				// If we got true as response, consider it done.
973
				$updated = true === $monitor->update_option_receive_jetpack_monitor_notification( $value );
974
				break;
975
976
			case 'post_by_email_address':
977
				$post_by_email = new Jetpack_Post_By_Email();
978
				if ( 'create' == $value ) {
979
					$result = $post_by_email->create_post_by_email_address();
0 ignored issues
show
Are you sure the assignment to $result is correct as $post_by_email->create_post_by_email_address() (which targets Jetpack_Post_By_Email::c...post_by_email_address()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
980
				} elseif ( 'regenerate' == $value ) {
981
					$result = $post_by_email->regenerate_post_by_email_address();
0 ignored issues
show
Are you sure the assignment to $result is correct as $post_by_email->regenera...post_by_email_address() (which targets Jetpack_Post_By_Email::r...post_by_email_address()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
982
				} elseif ( 'delete' == $value ) {
983
					$result = $post_by_email->delete_post_by_email_address();
0 ignored issues
show
Are you sure the assignment to $result is correct as $post_by_email->delete_post_by_email_address() (which targets Jetpack_Post_By_Email::d...post_by_email_address()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
984
				} else {
985
					$result = false;
986
				}
987
988
				// If we got an email address (create or regenerate) or 1 (delete), consider it done.
989
				if ( preg_match( '/[a-z0-9][email protected]/', $result ) ) {
990
					$response[ $option ] = $result;
991
					$updated = true;
992
				} elseif ( 1 == $result ) {
993
					$updated = true;
994
				} elseif ( is_array( $result ) && isset( $result['message'] ) ) {
995
					$error = $result['message'];
996
				}
997
				break;
998
999
			case 'jetpack_protect_key':
1000
				$protect = Jetpack_Protect_Module::instance();
1001
				if ( 'create' == $value ) {
1002
					$result = $protect->get_protect_key();
1003
				} else {
1004
					$result = false;
1005
				}
1006
1007
				// If we got one of Protect keys, consider it done.
1008
				if ( preg_match( '/[a-z0-9]{40,}/i', $result ) ) {
1009
					$response[ $option ] = $result;
1010
					$updated = true;
1011
				}
1012
				break;
1013
1014
			case 'jetpack_protect_global_whitelist':
1015
				$updated = jetpack_protect_save_whitelist( explode( PHP_EOL, str_replace( ' ', '', $value ) ) );
1016
				if ( is_wp_error( $updated ) ) {
1017
					$error = $updated->get_error_message();
1018
				}
1019
				break;
1020
1021
			case 'show_headline':
1022
			case 'show_thumbnails':
1023
				$grouped_options = $grouped_options_current = Jetpack_Options::get_option( 'relatedposts' );
1024
				$grouped_options[ $option ] = $value;
1025
1026
				// If option value was the same, consider it done.
1027
				$updated = $grouped_options_current != $grouped_options ? Jetpack_Options::update_option( 'relatedposts', $grouped_options ) : true;
1028
				break;
1029
1030
			case 'google':
1031
			case 'bing':
1032
			case 'pinterest':
1033
				$grouped_options = $grouped_options_current = get_option( 'verification_services_codes' );
1034
				$grouped_options[ $option ] = $value;
1035
1036
				// If option value was the same, consider it done.
1037
				$updated = $grouped_options_current != $grouped_options ? update_option( 'verification_services_codes', $grouped_options ) : true;
1038
				break;
1039
1040
			case 'sharing_services':
1041
				$sharer = new Sharing_Service();
1042
1043
				// If option value was the same, consider it done.
1044
				$updated = $value != $sharer->get_blog_services() ? $sharer->set_blog_services( $value['visible'], $value['hidden'] ) : true;
1045
				break;
1046
1047
			case 'button_style':
1048
			case 'sharing_label':
1049
			case 'show':
1050
				$sharer = new Sharing_Service();
1051
				$grouped_options = $sharer->get_global_options();
1052
				$grouped_options[ $option ] = $value;
1053
				$updated = $sharer->set_global_options( $grouped_options );
1054
				break;
1055
1056
			case 'custom':
1057
				$sharer = new Sharing_Service();
1058
				$updated = $sharer->new_service( stripslashes( $value['sharing_name'] ), stripslashes( $value['sharing_url'] ), stripslashes( $value['sharing_icon'] ) );
1059
1060
				// Return new custom service
1061
				$response[ $option ] = $updated;
1062
				break;
1063
1064
			case 'sharing_delete_service':
1065
				$sharer = new Sharing_Service();
1066
				$updated = $sharer->delete_service( $value );
1067
				break;
1068
1069
			case 'jetpack-twitter-cards-site-tag':
1070
				$value = trim( ltrim( strip_tags( $value ), '@' ) );
1071
				$updated = get_option( $option ) !== $value ? update_option( $option, $value ) : true;
1072
				break;
1073
1074
			case 'onpublish':
1075
			case 'onupdate':
1076
			case 'Bias Language':
1077
			case 'Cliches':
1078
			case 'Complex Expression':
1079
			case 'Diacritical Marks':
1080
			case 'Double Negative':
1081
			case 'Hidden Verbs':
1082
			case 'Jargon Language':
1083
			case 'Passive voice':
1084
			case 'Phrases to Avoid':
1085
			case 'Redundant Expression':
1086
			case 'guess_lang':
1087
				if ( in_array( $option, array( 'onpublish', 'onupdate' ) ) ) {
1088
					$atd_option = 'AtD_check_when';
1089
				} elseif ( 'guess_lang' == $option ) {
1090
					$atd_option = 'AtD_guess_lang';
1091
					$option = 'true';
1092
				} else {
1093
					$atd_option = 'AtD_options';
1094
				}
1095
				$user_id = get_current_user_id();
1096
				$grouped_options_current = AtD_get_options( $user_id, $atd_option );
1097
				unset( $grouped_options_current['name'] );
1098
				$grouped_options = $grouped_options_current;
1099
				if ( $value && ! isset( $grouped_options [ $option ] ) ) {
1100
					$grouped_options [ $option ] = $value;
1101
				} elseif ( ! $value && isset( $grouped_options [ $option ] ) ) {
1102
					unset( $grouped_options [ $option ] );
1103
				}
1104
				// If option value was the same, consider it done, otherwise try to update it.
1105
				$options_to_save = implode( ',', array_keys( $grouped_options ) );
1106
				$updated = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $options_to_save ) : true;
1107
				break;
1108
1109
			case 'ignored_phrases':
1110
			case 'unignore_phrase':
1111
				$user_id = get_current_user_id();
1112
				$atd_option = 'AtD_ignored_phrases';
1113
				$grouped_options = $grouped_options_current = explode( ',', AtD_get_setting( $user_id, $atd_option ) );
1114
				if ( 'ignored_phrases' == $option ) {
1115
					$grouped_options[] = $value;
1116
				} else {
1117
					$index = array_search( $value, $grouped_options );
1118
					if ( false !== $index ) {
1119
						unset( $grouped_options[ $index ] );
1120
						$grouped_options = array_values( $grouped_options );
1121
					}
1122
				}
1123
				$ignored_phrases = implode( ',', array_filter( array_map( 'strip_tags', $grouped_options ) ) );
1124
				$updated = $grouped_options != $grouped_options_current ? AtD_update_setting( $user_id, $atd_option, $ignored_phrases ) : true;
1125
				break;
1126
1127
			default:
1128
				// If option value was the same, consider it done.
1129
				$updated = get_option( $option ) != $value ? update_option( $option, $value ) : true;
1130
				break;
1131
		}
1132
1133
		// The option was not updated.
1134
		if ( ! $updated ) {
1135
			return new WP_Error( 'module_option_not_updated', esc_html( $error ), array( 'status' => 400 ) );
1136
		}
1137
1138
		// The option was updated.
1139
		return rest_ensure_response( $response );
1140
	}
1141
1142
	/**
1143
	 * Get the query parameters for module updating.
1144
	 *
1145
	 * @since 4.1.0
1146
	 *
1147
	 * @return array
1148
	 */
1149
	public static function get_module_updating_parameters() {
1150
		$parameters = array(
1151
			'context'     => array(
1152
				'default' => 'edit',
1153
			),
1154
		);
1155
1156
		return array_merge( $parameters, self::get_module_available_options() );
1157
	}
1158
1159
	/**
1160
	 * Returns a list of module options that can be updated.
1161
	 *
1162
	 * @since 4.1.0
1163
	 *
1164
	 * @param string $module Module slug. If empty, it's assumed we're updating a module and we'll try to get its slug.
1165
	 * @param bool $cache Whether to cache the options or return always fresh.
1166
	 *
1167
	 * @return array
1168
	 */
1169
	public static function get_module_available_options( $module = '', $cache = true ) {
1170
		if ( $cache ) {
1171
			static $options;
1172
		} else {
1173
			$options = null;
1174
		}
1175
1176
		if ( isset( $options ) ) {
1177
			return $options;
1178
		}
1179
1180
		if ( empty( $module ) ) {
1181
			$module = self::get_module_requested( '/module/(?P<slug>[a-z\-]+)/update' );
1182
			if ( empty( $module ) ) {
1183
				return array();
1184
			}
1185
		}
1186
1187
		switch ( $module ) {
1188
1189
			// Carousel
1190
			case 'carousel':
1191
				$options = array(
1192
					'carousel_background_color' => array(
1193
						'description'        => esc_html__( 'Background color.', 'jetpack' ),
1194
						'type'               => 'string',
1195
						'default'            => 'black',
1196
						'enum'				 => array(
1197
							'black' => esc_html__( 'Black', 'jetpack' ),
1198
							'white' => esc_html__( 'White', 'jetpack' ),
1199
						),
1200
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1201
					),
1202
					'carousel_display_exif' => array(
1203
						'description'        => wp_kses( sprintf( __( 'Show photo metadata (<a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format" target="_blank">Exif</a>) in carousel, when available.', 'jetpack' ) ), array( 'a' => array( 'href' => true, 'target' => true ) )  ),
1204
						'type'               => 'boolean',
1205
						'default'            => 0,
1206
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1207
					),
1208
				);
1209
				break;
1210
1211
			// Comments
1212
			case 'comments':
1213
				$options = array(
1214
					'highlander_comment_form_prompt' => array(
1215
						'description'        => esc_html__( 'Greeting Text', 'jetpack' ),
1216
						'type'               => 'string',
1217
						'default'            => esc_html__( 'Leave a Reply', 'jetpack' ),
1218
						'sanitize_callback'  => 'sanitize_text_field',
1219
					),
1220
					'jetpack_comment_form_color_scheme' => array(
1221
						'description'        => esc_html__( "Color Scheme", 'jetpack' ),
1222
						'type'               => 'string',
1223
						'default'            => 'light',
1224
						'enum'				 => array(
1225
							'light'       => esc_html__( 'Light', 'jetpack' ),
1226
							'dark'        => esc_html__( 'Dark', 'jetpack' ),
1227
							'transparent' => esc_html__( 'Transparent', 'jetpack' ),
1228
						),
1229
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1230
					),
1231
				);
1232
				break;
1233
1234
			// Custom Content Types
1235
			case 'custom-content-types':
1236
				$options = array(
1237
					'jetpack_portfolio' => array(
1238
						'description'        => esc_html__( 'Enable or disable Jetpack portfolio post type.', 'jetpack' ),
1239
						'type'               => 'boolean',
1240
						'default'            => 0,
1241
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1242
					),
1243
					'jetpack_portfolio_posts_per_page' => array(
1244
						'description'        => esc_html__( 'Number of entries to show at most in Portfolio pages.', 'jetpack' ),
1245
						'type'               => 'integer',
1246
						'default'            => 10,
1247
						'validate_callback'  => __CLASS__ . '::validate_posint',
1248
					),
1249
					'jetpack_testimonial' => array(
1250
						'description'        => esc_html__( 'Enable or disable Jetpack testimonial post type.', 'jetpack' ),
1251
						'type'               => 'boolean',
1252
						'default'            => 0,
1253
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1254
					),
1255
					'jetpack_testimonial_posts_per_page' => array(
1256
						'description'        => esc_html__( 'Number of entries to show at most in Testimonial pages.', 'jetpack' ),
1257
						'type'               => 'integer',
1258
						'default'            => 10,
1259
						'validate_callback'  => __CLASS__ . '::validate_posint',
1260
					),
1261
				);
1262
				break;
1263
1264
			// Galleries
1265 View Code Duplication
			case 'tiled-gallery':
1266
				$options = array(
1267
					'tiled_galleries' => array(
1268
						'description'        => esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ),
1269
						'type'               => 'boolean',
1270
						'default'            => 0,
1271
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1272
					),
1273
				);
1274
				break;
1275
1276
			// Gravatar Hovercards
1277
			case 'gravatar-hovercards':
1278
				$options = array(
1279
					'gravatar_disable_hovercards' => array(
1280
						'description'        => esc_html__( "View people's profiles when you mouse over their Gravatars", 'jetpack' ),
1281
						'type'               => 'string',
1282
						'default'            => 'enabled',
1283
						// Not visible. This is used as the checkbox value.
1284
						'enum'				 => array( 'enabled', 'disabled' ),
1285
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1286
					),
1287
				);
1288
				break;
1289
1290
			// Infinite Scroll
1291
			case 'infinite-scroll':
1292
				$options = array(
1293
					'infinite_scroll' => array(
1294
						'description'        => esc_html__( 'To infinity and beyond', 'jetpack' ),
1295
						'type'               => 'boolean',
1296
						'default'            => 1,
1297
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1298
					),
1299
					'infinite_scroll_google_analytics' => array(
1300
						'description'        => esc_html__( 'Use Google Analytics with Infinite Scroll', 'jetpack' ),
1301
						'type'               => 'boolean',
1302
						'default'            => 0,
1303
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1304
					),
1305
				);
1306
				break;
1307
1308
			// Likes
1309
			case 'likes':
1310
				$options = array(
1311
					'wpl_default' => array(
1312
						'description'        => esc_html__( 'WordPress.com Likes are', 'jetpack' ),
1313
						'type'               => 'string',
1314
						'default'            => 'on',
1315
						'enum'				 => array(
1316
							'on'  => esc_html__( 'On for all posts', 'jetpack' ),
1317
							'off' => esc_html__( 'Turned on per post', 'jetpack' ),
1318
						),
1319
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1320
					),
1321
					'social_notifications_like' => array(
1322
						'description'        => esc_html__( 'Send email notification when someone likes a posts', 'jetpack' ),
1323
						'type'               => 'boolean',
1324
						'default'            => 1,
1325
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1326
					),
1327
				);
1328
				break;
1329
1330
			// Markdown
1331 View Code Duplication
			case 'markdown':
1332
				$options = array(
1333
					'wpcom_publish_comments_with_markdown' => array(
1334
						'description'        => esc_html__( 'Use Markdown for comments.', 'jetpack' ),
1335
						'type'               => 'boolean',
1336
						'default'            => 0,
1337
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1338
					),
1339
				);
1340
				break;
1341
1342
			// Mobile Theme
1343
			case 'minileven':
1344
				$options = array(
1345
					'wp_mobile_excerpt' => array(
1346
						'description'        => esc_html__( 'Excerpts', 'jetpack' ),
1347
						'type'               => 'string',
1348
						'default'            => '0',
1349
						'enum'				 => array(
1350
							'1'  => esc_html__( 'Enable excerpts on front page and on archive pages', 'jetpack' ),
1351
							'0' => esc_html__( 'Show full posts on front page and on archive pages', 'jetpack' ),
1352
						),
1353
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1354
					),
1355
					'wp_mobile_featured_images' => array(
1356
						'description'        => esc_html__( 'Featured Images', 'jetpack' ),
1357
						'type'               => 'string',
1358
						'default'            => '0',
1359
						'enum'				 => array(
1360
							'0'  => esc_html__( 'Hide all featured images', 'jetpack' ),
1361
							'1' => esc_html__( 'Display featured images', 'jetpack' ),
1362
						),
1363
						'validate_callback'  => __CLASS__ . '::validate_list_item',
1364
					),
1365
					'wp_mobile_app_promos' => array(
1366
						'description'        => esc_html__( 'Show a promo for the WordPress mobile apps in the footer of the mobile theme.', 'jetpack' ),
1367
						'type'               => 'boolean',
1368
						'default'            => 0,
1369
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1370
					),
1371
				);
1372
				break;
1373
1374
			// Monitor
1375 View Code Duplication
			case 'monitor':
1376
				$options = array(
1377
					'monitor_receive_notifications' => array(
1378
						'description'        => esc_html__( 'Receive Monitor Email Notifications.', 'jetpack' ),
1379
						'type'               => 'boolean',
1380
						'default'            => 0,
1381
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1382
					),
1383
				);
1384
				break;
1385
1386
			// Post by Email
1387 View Code Duplication
			case 'post-by-email':
1388
				$options = array(
1389
					'post_by_email_address' => array(
1390
						'description'       => esc_html__( 'Email Address', 'jetpack' ),
1391
						'type'              => 'string',
1392
						'default'           => '',
1393
						'enum'              => array(
1394
							'create'     => esc_html__( 'Create Post by Email address', 'jetpack' ),
1395
							'regenerate' => esc_html__( 'Regenerate Post by Email address', 'jetpack' ),
1396
							'delete'     => esc_html__( 'Delete Post by Email address', 'jetpack' ),
1397
						),
1398
						'validate_callback' => __CLASS__ . '::validate_list_item',
1399
					),
1400
				);
1401
				break;
1402
1403
			// Protect
1404 View Code Duplication
			case 'protect':
1405
				$options = array(
1406
					'jetpack_protect_key' => array(
1407
						'description'        => esc_html__( 'Protect API key', 'jetpack' ),
1408
						'type'               => 'string',
1409
						'default'            => '',
1410
						'validate_callback'  => __CLASS__ . '::validate_alphanum',
1411
					),
1412
					'jetpack_protect_global_whitelist' => array(
1413
						'description'        => esc_html__( 'Protect global whitelist', 'jetpack' ),
1414
						'type'               => 'string',
1415
						'default'            => '',
1416
						'validate_callback'  => __CLASS__ . '::validate_string',
1417
						'sanitize_callback'  => 'esc_textarea',
1418
					),
1419
				);
1420
				break;
1421
1422
			// Sharing
1423
			case 'sharedaddy':
1424
				$options = array(
1425
					'sharing_services' => array(
1426
						'description'        => esc_html__( 'Enabled Services and those hidden behind a button', 'jetpack' ),
1427
						'type'               => 'array',
1428
						'default'            => array(
1429
							'visible' => array( 'twitter', 'facebook', 'google-plus-1' ),
1430
							'hidden'  => array(),
1431
						),
1432
						'validate_callback'  => __CLASS__ . '::validate_services',
1433
					),
1434
					'button_style' => array(
1435
						'description'       => esc_html__( 'Button Style', 'jetpack' ),
1436
						'type'              => 'string',
1437
						'default'           => 'icon',
1438
						'enum'              => array(
1439
							'icon-text' => esc_html__( 'Icon + text', 'jetpack' ),
1440
							'icon'      => esc_html__( 'Icon only', 'jetpack' ),
1441
							'text'      => esc_html__( 'Text only', 'jetpack' ),
1442
							'official'  => esc_html__( 'Official buttons', 'jetpack' ),
1443
						),
1444
						'validate_callback' => __CLASS__ . '::validate_list_item',
1445
					),
1446
					'sharing_label' => array(
1447
						'description'        => esc_html__( 'Sharing Label', 'jetpack' ),
1448
						'type'               => 'string',
1449
						'default'            => '',
1450
						'validate_callback'  => __CLASS__ . '::validate_string',
1451
						'sanitize_callback'  => 'esc_html',
1452
					),
1453
					'show' => array(
1454
						'description'        => esc_html__( 'Views where buttons are shown', 'jetpack' ),
1455
						'type'               => 'array',
1456
						'default'            => array( 'post' ),
1457
						'validate_callback'  => __CLASS__ . '::validate_sharing_show',
1458
					),
1459
					'jetpack-twitter-cards-site-tag' => array(
1460
						'description'        => esc_html__( "The Twitter username of the owner of this site's domain.", 'jetpack' ),
1461
						'type'               => 'string',
1462
						'default'            => '',
1463
						'validate_callback'  => __CLASS__ . '::validate_twitter_username',
1464
						'sanitize_callback'  => 'esc_html',
1465
					),
1466
					'sharedaddy_disable_resources' => array(
1467
						'description'        => esc_html__( 'Disable CSS and JS', 'jetpack' ),
1468
						'type'               => 'boolean',
1469
						'default'            => 0,
1470
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1471
					),
1472
					'custom' => array(
1473
						'description'        => esc_html__( 'Custom sharing services added by user.', 'jetpack' ),
1474
						'type'               => 'array',
1475
						'default'            => array(
1476
							'sharing_name' => '',
1477
							'sharing_url'  => '',
1478
							'sharing_icon' => '',
1479
						),
1480
						'validate_callback'  => __CLASS__ . '::validate_custom_service',
1481
					),
1482
					// Not an option, but an action that can be perfomed on the list of custom services passing the service ID.
1483
					'sharing_delete_service' => array(
1484
						'description'        => esc_html__( 'Delete custom sharing service.', 'jetpack' ),
1485
						'type'               => 'string',
1486
						'default'            => '',
1487
						'validate_callback'  => __CLASS__ . '::validate_custom_service_id',
1488
					),
1489
				);
1490
				break;
1491
1492
			// SSO
1493 View Code Duplication
			case 'sso':
1494
				$options = array(
1495
					'jetpack_sso_require_two_step' => array(
1496
						'description'        => esc_html__( 'Require Two-Step Authentication', 'jetpack' ),
1497
						'type'               => 'boolean',
1498
						'default'            => 0,
1499
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1500
					),
1501
					'jetpack_sso_match_by_email' => array(
1502
						'description'        => esc_html__( 'Match by Email', 'jetpack' ),
1503
						'type'               => 'boolean',
1504
						'default'            => 0,
1505
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1506
					),
1507
				);
1508
				break;
1509
1510
			// Site Icon
1511 View Code Duplication
			case 'site-icon':
1512
				$options = array(
1513
					'site_icon_id' => array(
1514
						'description'        => esc_html__( 'Site Icon ID', 'jetpack' ),
1515
						'type'               => 'integer',
1516
						'default'            => 0,
1517
						'validate_callback'  => __CLASS__ . '::validate_posint',
1518
					),
1519
					'site_icon_url' => array(
1520
						'description'        => esc_html__( 'Site Icon URL', 'jetpack' ),
1521
						'type'               => 'string',
1522
						'default'            => '',
1523
						'sanitize_callback'  => 'esc_url',
1524
					),
1525
				);
1526
				break;
1527
1528
			// Subscriptions
1529
			case 'subscriptions':
1530
				$options = array(
1531
					'stb_enabled' => array(
1532
						'description'        => esc_html__( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ),
1533
						'type'               => 'boolean',
1534
						'default'            => 1,
1535
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1536
					),
1537
					'stc_enabled' => array(
1538
						'description'        => esc_html__( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ),
1539
						'type'               => 'boolean',
1540
						'default'            => 1,
1541
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1542
					),
1543
				);
1544
				break;
1545
1546
			// Related Posts
1547
			case 'related-posts':
1548
				$options = array(
1549
					'show_headline' => array(
1550
						'description'        => esc_html__( 'Show a "Related" header to more clearly separate the related section from posts', 'jetpack' ),
1551
						'type'               => 'boolean',
1552
						'default'            => 1,
1553
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1554
					),
1555
					'show_thumbnails' => array(
1556
						'description'        => esc_html__( 'Use a large and visually striking layout', 'jetpack' ),
1557
						'type'               => 'boolean',
1558
						'default'            => 0,
1559
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1560
					),
1561
				);
1562
				break;
1563
1564
			// Spelling and Grammar - After the Deadline
1565
			case 'after-the-deadline':
1566
				$options = array(
1567
					'onpublish' => array(
1568
						'description'        => esc_html__( 'Proofread when a post or page is first published.', 'jetpack' ),
1569
						'type'               => 'boolean',
1570
						'default'            => 0,
1571
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1572
					),
1573
					'onupdate' => array(
1574
						'description'        => esc_html__( 'Proofread when a post or page is updated.', 'jetpack' ),
1575
						'type'               => 'boolean',
1576
						'default'            => 0,
1577
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1578
					),
1579
					'Bias Language' => array(
1580
						'description'        => esc_html__( 'Bias Language', 'jetpack' ),
1581
						'type'               => 'boolean',
1582
						'default'            => 0,
1583
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1584
					),
1585
					'Cliches' => array(
1586
						'description'        => esc_html__( 'Clichés', 'jetpack' ),
1587
						'type'               => 'boolean',
1588
						'default'            => 0,
1589
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1590
					),
1591
					'Complex Phrases' => array(
1592
						'description'        => esc_html__( 'Complex Phrases', 'jetpack' ),
1593
						'type'               => 'boolean',
1594
						'default'            => 0,
1595
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1596
					),
1597
					'Diacritical Marks' => array(
1598
						'description'        => esc_html__( 'Diacritical Marks', 'jetpack' ),
1599
						'type'               => 'boolean',
1600
						'default'            => 0,
1601
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1602
					),
1603
					'Double Negative' => array(
1604
						'description'        => esc_html__( 'Double Negatives', 'jetpack' ),
1605
						'type'               => 'boolean',
1606
						'default'            => 0,
1607
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1608
					),
1609
					'Hidden Verbs' => array(
1610
						'description'        => esc_html__( 'Hidden Verbs', 'jetpack' ),
1611
						'type'               => 'boolean',
1612
						'default'            => 0,
1613
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1614
					),
1615
					'Jargon Language' => array(
1616
						'description'        => esc_html__( 'Jargon', 'jetpack' ),
1617
						'type'               => 'boolean',
1618
						'default'            => 0,
1619
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1620
					),
1621
					'Passive voice' => array(
1622
						'description'        => esc_html__( 'Passive Voice', 'jetpack' ),
1623
						'type'               => 'boolean',
1624
						'default'            => 0,
1625
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1626
					),
1627
					'Phrases to Avoid' => array(
1628
						'description'        => esc_html__( 'Phrases to Avoid', 'jetpack' ),
1629
						'type'               => 'boolean',
1630
						'default'            => 0,
1631
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1632
					),
1633
					'Redundant Expression' => array(
1634
						'description'        => esc_html__( 'Redundant Phrases', 'jetpack' ),
1635
						'type'               => 'boolean',
1636
						'default'            => 0,
1637
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1638
					),
1639
					'guess_lang' => array(
1640
						'description'        => esc_html__( 'Use automatically detected language to proofread posts and pages', 'jetpack' ),
1641
						'type'               => 'boolean',
1642
						'default'            => 0,
1643
						'validate_callback'  => __CLASS__ . '::validate_boolean',
1644
					),
1645
					'ignored_phrases' => array(
1646
						'description'        => esc_html__( 'Add Phrase to be ignored', 'jetpack' ),
1647
						'type'               => 'string',
1648
						'default'            => '',
1649
						'sanitize_callback'  => 'esc_html',
1650
					),
1651
					'unignore_phrase' => array(
1652
						'description'        => esc_html__( 'Remove Phrase from being ignored', 'jetpack' ),
1653
						'type'               => 'string',
1654
						'default'            => '',
1655
						'sanitize_callback'  => 'esc_html',
1656
					),
1657
				);
1658
				break;
1659
1660
			// Verification Tools
1661
			case 'verification-tools':
1662
				$options = array(
1663
					'google' => array(
1664
						'description'        => esc_html__( 'Google Search Console', 'jetpack' ),
1665
						'type'               => 'string',
1666
						'default'            => '',
1667
						'validate_callback'  => __CLASS__ . '::validate_alphanum',
1668
					),
1669
					'bing' => array(
1670
						'description'        => esc_html__( 'Bing Webmaster Center', 'jetpack' ),
1671
						'type'               => 'string',
1672
						'default'            => '',
1673
						'validate_callback'  => __CLASS__ . '::validate_alphanum',
1674
					),
1675
					'pinterest' => array(
1676
						'description'        => esc_html__( 'Pinterest Site Verification', 'jetpack' ),
1677
						'type'               => 'string',
1678
						'default'            => '',
1679
						'validate_callback'  => __CLASS__ . '::validate_alphanum',
1680
					),
1681
				);
1682
				break;
1683
		}
1684
1685
		return $options;
1686
	}
1687
1688
	/**
1689
	 * Validates that the parameter is either a pure boolean or a numeric string that can be mapped to a boolean.
1690
	 *
1691
	 * @since 4.1.0
1692
	 *
1693
	 * @param string|bool $value Value to check.
1694
	 * @param WP_REST_Request $request
1695
	 * @param string $param
1696
	 *
1697
	 * @return bool
1698
	 */
1699
	public static function validate_boolean( $value, $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1700
		if ( ! is_bool( $value ) && ! in_array( $value, array( 0, 1 ) ) ) {
1701
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be true, false, 0 or 1.', 'jetpack' ), $param ) );
1702
		}
1703
		return true;
1704
	}
1705
1706
	/**
1707
	 * Validates that the parameter is a positive integer.
1708
	 *
1709
	 * @since 4.1.0
1710
	 *
1711
	 * @param int $value Value to check.
1712
	 * @param WP_REST_Request $request
1713
	 * @param string $param
1714
	 *
1715
	 * @return bool
1716
	 */
1717
	public static function validate_posint( $value = 0, $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1718
		if ( ! is_numeric( $value ) || $value <= 0 ) {
1719
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a positive integer.', 'jetpack' ), $param ) );
1720
		}
1721
		return true;
1722
	}
1723
1724
	/**
1725
	 * Validates that the parameter belongs to a list of admitted values.
1726
	 *
1727
	 * @since 4.1.0
1728
	 *
1729
	 * @param string $value Value to check.
1730
	 * @param WP_REST_Request $request
1731
	 * @param string $param
1732
	 *
1733
	 * @return bool
1734
	 */
1735
	public static function validate_list_item( $value = '', $request, $param ) {
0 ignored issues
show
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1736
		$attributes = $request->get_attributes();
1737
		if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
1738
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s not recognized', 'jetpack' ), $param ) );
1739
		}
1740
		$args = $attributes['args'][ $param ];
1741
		if ( ! empty( $args['enum'] ) ) {
1742
1743
			// If it's an associative array, use the keys to check that the value is among those admitted.
1744
			$enum = ( count( array_filter( array_keys( $args['enum'] ), 'is_string' ) ) > 0 ) ? array_keys( $args['enum'] ) : $args['enum'];
1745 View Code Duplication
			if ( ! in_array( $value, $enum ) ) {
1746
				return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be one of %s', 'jetpack' ), $param, implode( ', ', $enum ) ) );
1747
			}
1748
		}
1749
		return true;
1750
	}
1751
1752
	/**
1753
	 * Validates that the parameter belongs to a list of admitted values.
1754
	 *
1755
	 * @since 4.1.0
1756
	 *
1757
	 * @param string $value Value to check.
1758
	 * @param WP_REST_Request $request
1759
	 * @param string $param
1760
	 *
1761
	 * @return bool
1762
	 */
1763
	public static function validate_module_list( $value = '', $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1764
		if ( ! is_array( $value ) ) {
1765
			return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be an array', 'jetpack' ), $param ) );
1766
		}
1767
1768
		$modules = Jetpack::get_available_modules();
1769
1770
		if ( count( array_intersect( $value, $modules ) ) != count( $value ) ) {
1771
			return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be a list of valid modules', 'jetpack' ), $param ) );
1772
		}
1773
1774
		return true;
1775
	}
1776
1777
	/**
1778
	 * Validates that the parameter is an alphanumeric or empty string (to be able to clear the field).
1779
	 *
1780
	 * @since 4.1.0
1781
	 *
1782
	 * @param string $value Value to check.
1783
	 * @param WP_REST_Request $request
1784
	 * @param string $param
1785
	 *
1786
	 * @return bool
1787
	 */
1788
	public static function validate_alphanum( $value = '', $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1789 View Code Duplication
		if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/[a-z0-9]+/i', $value ) ) ) {
1790
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), $param ) );
1791
		}
1792
		return true;
1793
	}
1794
1795
	/**
1796
	 * Validates that the parameter is among the views where the Sharing can be displayed.
1797
	 *
1798
	 * @since 4.1.0
1799
	 *
1800
	 * @param string|bool $value Value to check.
1801
	 * @param WP_REST_Request $request
1802
	 * @param string $param
1803
	 *
1804
	 * @return bool
1805
	 */
1806
	public static function validate_sharing_show( $value, $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1807
		$views = array( 'index', 'post', 'page', 'attachment', 'jetpack-portfolio' );
1808 View Code Duplication
		if ( ! array_intersect( $views, $value ) ) {
1809
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be %s.', 'jetpack' ), $param, join( ', ', $views ) ) );
1810
		}
1811
		return true;
1812
	}
1813
1814
	/**
1815
	 * Validates that the parameter is among the views where the Sharing can be displayed.
1816
	 *
1817
	 * @since 4.1.0
1818
	 *
1819
	 * @param string|bool $value Value to check.
1820
	 * @param WP_REST_Request $request
1821
	 * @param string $param
1822
	 *
1823
	 * @return bool
1824
	 */
1825
	public static function validate_services( $value, $request, $param ) {
1826 View Code Duplication
		if ( ! is_array( $value ) || ! isset( $value['visible'] ) || ! isset( $value['hidden'] ) ) {
1827
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with visible and hidden items.', 'jetpack' ), $param ) );
1828
		}
1829
1830
		// Allow to clear everything.
1831
		if ( empty( $value['visible'] ) && empty( $value['hidden'] ) ) {
1832
			return true;
1833
		}
1834
1835 View Code Duplication
		if ( ! class_exists( 'Sharing_Service' ) && ! @include( JETPACK__PLUGIN_DIR . 'modules/sharing/sharing-service.php' ) ) {
1836
			return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
1837
		}
1838
		$sharer = new Sharing_Service();
1839
		$services = array_keys( $sharer->get_all_services() );
1840
1841
		if (
1842
			( ! empty( $value['visible'] ) && ! array_intersect( $value['visible'], $services ) )
1843
			||
1844
			( ! empty( $value['hidden'] ) && ! array_intersect( $value['hidden'], $services ) ) )
1845
		{
1846
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s visible and hidden items must be a list of %s.', 'jetpack' ), $param, join( ', ', $services ) ) );
1847
		}
1848
		return true;
1849
	}
1850
1851
	/**
1852
	 * Validates that the parameter has enough information to build a custom sharing button.
1853
	 *
1854
	 * @since 4.1.0
1855
	 *
1856
	 * @param string|bool $value Value to check.
1857
	 * @param WP_REST_Request $request
1858
	 * @param string $param
1859
	 *
1860
	 * @return bool
1861
	 */
1862
	public static function validate_custom_service( $value, $request, $param ) {
1863 View Code Duplication
		if ( ! is_array( $value ) || ! isset( $value['sharing_name'] ) || ! isset( $value['sharing_url'] ) || ! isset( $value['sharing_icon'] ) ) {
1864
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with sharing name, url and icon.', 'jetpack' ), $param ) );
1865
		}
1866
1867
		// Allow to clear everything.
1868
		if ( empty( $value['sharing_name'] ) && empty( $value['sharing_url'] ) && empty( $value['sharing_icon'] ) ) {
1869
			return true;
1870
		}
1871
1872 View Code Duplication
		if ( ! class_exists( 'Sharing_Service' ) && ! @include( JETPACK__PLUGIN_DIR . 'modules/sharing/sharing-service.php' ) ) {
1873
			return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
1874
		}
1875
1876
		if ( ( ! empty( $value['sharing_name'] ) && ! is_string( $value['sharing_name'] ) )
1877
		|| ( ! empty( $value['sharing_url'] ) && ! is_string( $value['sharing_url'] ) )
1878
		|| ( ! empty( $value['sharing_icon'] ) && ! is_string( $value['sharing_icon'] ) ) ) {
1879
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s needs sharing name, url and icon.', 'jetpack' ), $param ) );
1880
		}
1881
		return true;
1882
	}
1883
1884
	/**
1885
	 * Validates that the parameter is a custom sharing service ID like 'custom-1461976264'.
1886
	 *
1887
	 * @since 4.1.0
1888
	 *
1889
	 * @param string $value Value to check.
1890
	 * @param WP_REST_Request $request
1891
	 * @param string $param
1892
	 *
1893
	 * @return bool
1894
	 */
1895
	public static function validate_custom_service_id( $value = '', $request, $param ) {
0 ignored issues
show
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1896 View Code Duplication
		if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/custom\-[0-1]+/i', $value ) ) ) {
1897
			return new WP_Error( 'invalid_param', sprintf( esc_html__( "%s must be a string prefixed with 'custom-' and followed by a numeric ID.", 'jetpack' ), $param ) );
1898
		}
1899
1900 View Code Duplication
		if ( ! class_exists( 'Sharing_Service' ) && ! @include( JETPACK__PLUGIN_DIR . 'modules/sharing/sharing-service.php' ) ) {
1901
			return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) );
1902
		}
1903
		$sharer = new Sharing_Service();
1904
		$services = array_keys( $sharer->get_all_services() );
1905
1906 View Code Duplication
		if ( ! empty( $value ) && ! in_array( $value, $services ) ) {
1907
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s is not a registered custom sharing service.', 'jetpack' ), $param ) );
1908
		}
1909
1910
		return true;
1911
	}
1912
1913
	/**
1914
	 * Validates that the parameter is a Twitter username or empty string (to be able to clear the field).
1915
	 *
1916
	 * @since 4.1.0
1917
	 *
1918
	 * @param string $value Value to check.
1919
	 * @param WP_REST_Request $request
1920
	 * @param string $param
1921
	 *
1922
	 * @return bool
1923
	 */
1924
	public static function validate_twitter_username( $value = '', $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1925 View Code Duplication
		if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^@?\w{1,15}$/i', $value ) ) ) {
1926
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a Twitter username.', 'jetpack' ), $param ) );
1927
		}
1928
		return true;
1929
	}
1930
1931
	/**
1932
	 * Validates that the parameter is a string.
1933
	 *
1934
	 * @since 4.1.0
1935
	 *
1936
	 * @param string $value Value to check.
1937
	 * @param WP_REST_Request $request
1938
	 * @param string $param
1939
	 *
1940
	 * @return bool
1941
	 */
1942
	public static function validate_string( $value = '', $request, $param ) {
0 ignored issues
show
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1943
		if ( ! is_string( $value ) ) {
1944
			return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a string.', 'jetpack' ), $param ) );
1945
		}
1946
		return true;
1947
	}
1948
1949
	/**
1950
	 * Get the currently accessed route and return the module slug in it.
1951
	 *
1952
	 * @since 4.1.0
1953
	 *
1954
	 * @param string $route Regular expression for the endpoint with the module slug to return.
1955
	 *
1956
	 * @return array
1957
	 */
1958
	public static function get_module_requested( $route ) {
1959
1960
		if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
1961
			return '';
1962
		}
1963
1964
		preg_match( "#$route#", $GLOBALS['wp']->query_vars['rest_route'], $module );
1965
1966
		if ( empty( $module['slug'] ) ) {
1967
			return '';
1968
		}
1969
1970
		return $module['slug'];
1971
	}
1972
1973
	/**
1974
	 * Remove 'validate_callback' item from options available for module.
1975
	 * Fetch current option value and add to array of module options.
1976
	 * Prepare values of module options that need special handling, like those saved in wpcom.
1977
	 *
1978
	 * @since 4.1.0
1979
	 *
1980
	 * @param string $module Module slug.
1981
	 * @return array
1982
	 */
1983
	public static function prepare_options_for_response( $module = '' ) {
1984
		$options = self::get_module_available_options( $module, false );
1985
1986
		if ( ! is_array( $options ) || empty( $options ) ) {
1987
			return $options;
1988
		}
1989
1990
		foreach ( $options as $key => $value ) {
1991
1992
			if ( isset( $options[ $key ]['validate_callback'] ) ) {
1993
				unset( $options[ $key ]['validate_callback'] );
1994
			}
1995
1996
			$default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
1997
1998
			$current_value = get_option( $key, $default_value );
1999
2000
			$options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] );
2001
		}
2002
2003
		// Some modules need special treatment.
2004
		switch ( $module ) {
2005
2006
			case 'monitor':
2007
				// Status of user notifications
2008
				$options['monitor_receive_notifications']['current_value'] = self::cast_value( self::get_remote_value( 'monitor', 'monitor_receive_notifications' ), $options['monitor_receive_notifications'] );
2009
				break;
2010
2011
			case 'post-by-email':
2012
				// Email address
2013
				$options['post_by_email_address']['current_value'] = self::cast_value( self::get_remote_value( 'post-by-email', 'post_by_email_address' ), $options['post_by_email_address'] );
2014
				break;
2015
2016
			case 'protect':
2017
				// Protect
2018
				$options['jetpack_protect_key']['current_value'] = get_site_option( 'jetpack_protect_key', false );
2019
				if ( ! function_exists( 'jetpack_protect_format_whitelist' ) ) {
2020
					@include( JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2021
				}
2022
				$options['jetpack_protect_global_whitelist']['current_value'] = jetpack_protect_format_whitelist();
2023
				break;
2024
2025
			case 'related-posts':
2026
				// It's local, but it must be broken apart since it's saved as an array.
2027
				$options = self::split_options( $options, Jetpack_Options::get_option( 'relatedposts' ) );
2028
				break;
2029
2030
			case 'verification-tools':
2031
				// It's local, but it must be broken apart since it's saved as an array.
2032
				$options = self::split_options( $options, get_option( 'verification_services_codes' ) );
2033
				break;
2034
2035
			case 'sharedaddy':
2036
				// It's local, but it must be broken apart since it's saved as an array.
2037
				if ( ! class_exists( 'Sharing_Service' ) && ! @include( JETPACK__PLUGIN_DIR . 'modules/sharing/sharing-service.php' ) ) {
2038
					break;
2039
				}
2040
				$sharer = new Sharing_Service();
2041
				$options = self::split_options( $options, $sharer->get_global_options() );
2042
				$options['sharing_services']['current_value'] = $sharer->get_blog_services();
2043
				break;
2044
2045
			case 'site-icon':
2046
				// Return site icon ID and URL to make it more complete.
2047
				$options['site_icon_id']['current_value'] = Jetpack_Options::get_option( 'site_icon_id' );
2048
				if ( ! function_exists( 'jetpack_site_icon_url' ) ) {
2049
					@include( JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2050
				}
2051
				$options['site_icon_url']['current_value'] = jetpack_site_icon_url();
2052
				break;
2053
2054
			case 'after-the-deadline':
2055
				if ( ! function_exists( 'AtD_get_options' ) ) {
2056
					@include( JETPACK__PLUGIN_DIR . 'modules/after-the-deadline.php' );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2057
				}
2058
				$atd_options = array_merge( AtD_get_options( get_current_user_id(), 'AtD_options' ), AtD_get_options( get_current_user_id(), 'AtD_check_when' ) );
2059
				unset( $atd_options['name'] );
2060
				foreach ( $atd_options as $key => $value ) {
2061
					$options[ $key ]['current_value'] = self::cast_value( $value, $options[ $key ] );
2062
				}
2063
				$atd_options = AtD_get_options( get_current_user_id(), 'AtD_guess_lang' );
2064
				$options['guess_lang']['current_value'] = self::cast_value( isset( $atd_options['true'] ), $options[ 'guess_lang' ] );
2065
				$options['ignored_phrases']['current_value'] = AtD_get_setting( get_current_user_id(), 'AtD_ignored_phrases' );
2066
				unset( $options['unignore_phrase'] );
2067
				break;
2068
		}
2069
2070
		return $options;
2071
	}
2072
2073
	/**
2074
	 * Splits module options saved as arrays like relatedposts or verification_services_codes into separate options to be returned in the response.
2075
	 *
2076
	 * @since 4.1.0
2077
	 *
2078
	 * @param array  $separate_options Array of options admitted by the module.
2079
	 * @param array  $grouped_options Option saved as array to be splitted.
2080
	 * @param string $prefix Optional prefix for the separate option keys.
2081
	 *
2082
	 * @return array
2083
	 */
2084
	public static function split_options( $separate_options, $grouped_options, $prefix = '' ) {
2085
		if ( is_array( $grouped_options ) ) {
2086
			foreach ( $grouped_options as $key => $value ) {
2087
				$option_key = $prefix . $key;
2088
				if ( isset( $separate_options[ $option_key ] ) ) {
2089
					$separate_options[ $option_key ]['current_value'] = self::cast_value( $grouped_options[ $key ], $separate_options[ $option_key ] );
2090
				}
2091
			}
2092
		}
2093
		return $separate_options;
2094
	}
2095
2096
	/**
2097
	 * Perform a casting to the value specified in the option definition.
2098
	 *
2099
	 * @since 4.1.0
2100
	 *
2101
	 * @param mixed $value Value to cast to the proper type.
2102
	 * @param array $definition Type to cast the value to.
2103
	 *
2104
	 * @return bool|float|int|string
2105
	 */
2106
	public static function cast_value( $value, $definition ) {
2107
		if ( isset( $definition['type'] ) ) {
2108
			switch ( $definition['type'] ) {
2109
				case 'boolean':
2110
					if ( 'true' === $value ) {
2111
						return true;
2112
					} elseif ( 'false' === $value ) {
2113
						return false;
2114
					}
2115
					return (bool) $value;
2116
					break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2117
2118
				case 'integer':
2119
					return (int) $value;
2120
					break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2121
2122
				case 'float':
2123
					return (float) $value;
2124
					break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2125
			}
2126
		}
2127
		return $value;
2128
	}
2129
2130
	/**
2131
	 * Get a value not saved locally.
2132
	 *
2133
	 * @since 4.1.0
2134
	 *
2135
	 * @param string $module Module slug.
2136
	 * @param string $option Option name.
2137
	 *
2138
	 * @return bool Whether user is receiving notifications or not.
2139
	 */
2140
	public static function get_remote_value( $module, $option ) {
2141
2142
		// If option doesn't exist, 'does_not_exist' will be returned.
2143
		$value = get_option( $option, 'does_not_exist' );
2144
2145
		// If option exists, just return it.
2146
		if ( 'does_not_exist' !== $value ) {
2147
			return $value;
2148
		}
2149
2150
		// Only check a remote option if Jetpack is connected.
2151
		if ( ! Jetpack::is_active() ) {
2152
			return false;
2153
		}
2154
2155
		// If the module is inactive, load the class to use the method.
2156
		if ( ! Jetpack::is_module_active( $module ) ) {
2157
			// Class can't be found so do nothing.
2158
			if ( ! @include( Jetpack::get_module_path( $module ) ) ) {
2159
				return false;
2160
			}
2161
		}
2162
2163
		// Do what is necessary for each module.
2164
		switch ( $module ) {
2165
			case 'monitor':
2166
				$monitor = new Jetpack_Monitor();
2167
				$value = $monitor->user_receives_notifications( false );
2168
				break;
2169
2170
			case 'post-by-email':
2171
				$post_by_email = new Jetpack_Post_By_Email();
2172
				$value = $post_by_email->get_post_by_email_address();
2173
				break;
2174
		}
2175
2176
		// Normalize value to boolean.
2177
		if ( is_wp_error( $value ) || is_null( $value ) ) {
2178
			$value = false;
2179
		}
2180
2181
		// Save option to use it next time.
2182
		update_option( $option, $value );
2183
2184
		return $value;
2185
	}
2186
2187
	/**
2188
	 * Get number of blocked intrusion attempts.
2189
	 *
2190
	 * @since 4.1.0
2191
	 *
2192
	 * @return mixed|WP_Error Number of blocked attempts if protection is enabled. Otherwise, a WP_Error instance with the corresponding error.
2193
	 */
2194
	public static function protect_get_blocked_count() {
2195
		if ( Jetpack::is_module_active( 'protect' ) ) {
2196
			return get_site_option( 'jetpack_protect_blocked_attempts' );
2197
		}
2198
2199
		return new WP_Error( 'not_active', esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ), array( 'status' => 404 ) );
2200
	}
2201
2202
	/**
2203
	 * Get number of spam messages blocked by Akismet.
2204
	 *
2205
	 * @since 4.1.0
2206
	 *
2207
	 * @param WP_REST_Request $data {
2208
	 *     Array of parameters received by request.
2209
	 *
2210
	 *     @type string $date Date range to restrict results to.
2211
	 * }
2212
	 *
2213
	 * @return int|string Number of spam blocked by Akismet. Otherwise, an error message.
2214
	 */
2215
	public static function akismet_get_stats_data( WP_REST_Request $data ) {
0 ignored issues
show
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2216
		if ( ! is_wp_error( $status = self::akismet_is_active_and_registered() ) ) {
2217
			return rest_ensure_response( Akismet_Admin::get_stats( Akismet::get_api_key() ) );
2218
		} else {
2219
			return $status->get_error_code();
2220
		}
2221
	}
2222
2223
	/**
2224
	 * Get date of last downtime.
2225
	 *
2226
	 * @since 4.1.0
2227
	 *
2228
	 * @return mixed|WP_Error Number of days since last downtime. Otherwise, a WP_Error instance with the corresponding error.
2229
	 */
2230
	public static function monitor_get_last_downtime() {
2231
		if ( Jetpack::is_module_active( 'monitor' ) ) {
2232
			$monitor       = new Jetpack_Monitor();
2233
			$last_downtime = $monitor->monitor_get_last_downtime();
2234
			if ( is_wp_error( $last_downtime ) ) {
2235
				return $last_downtime;
2236
			} else {
2237
				return rest_ensure_response( array(
2238
					'code' => 'success',
2239
					'date' => human_time_diff( strtotime( $last_downtime ), strtotime( 'now' ) ),
2240
				) );
2241
			}
2242
		}
2243
2244
		return new WP_Error( 'not_active', esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ), array( 'status' => 404 ) );
2245
	}
2246
2247
	/**
2248
	 * Get number of plugin updates available.
2249
	 *
2250
	 * @since 4.1.0
2251
	 *
2252
	 * @return mixed|WP_Error Number of plugin updates available. Otherwise, a WP_Error instance with the corresponding error.
2253
	 */
2254
	public static function get_plugin_update_count() {
2255
		$updates = wp_get_update_data();
2256
		if ( isset( $updates['counts'] ) && isset( $updates['counts']['plugins'] ) ) {
2257
			$count = $updates['counts']['plugins'];
2258
			if ( 0 == $count ) {
2259
				$response = array(
2260
					'code'    => 'success',
2261
					'message' => esc_html__( 'All plugins are up-to-date. Keep up the good work!', 'jetpack' ),
2262
					'count'   => 0,
2263
				);
2264
			} else {
2265
				$response = array(
2266
					'code'    => 'updates-available',
2267
					'message' => esc_html( sprintf( _n( '%s plugin need updating.', '%s plugins need updating.', $count, 'jetpack' ), $count ) ),
2268
					'count'   => $count,
2269
				);
2270
			}
2271
			return rest_ensure_response( $response );
2272
		}
2273
2274
		return new WP_Error( 'not_found', esc_html__( 'Could not check updates for plugins on this site.', 'jetpack' ), array( 'status' => 404 ) );
2275
	}
2276
2277
	/**
2278
	 * Get services that this site is verified with.
2279
	 *
2280
	 * @since 4.1.0
2281
	 *
2282
	 * @return mixed|WP_Error List of services that verified this site. Otherwise, a WP_Error instance with the corresponding error.
2283
	 */
2284
	public static function get_verified_services() {
2285
		if ( Jetpack::is_module_active( 'verification-tools' ) ) {
2286
			$verification_services_codes = get_option( 'verification_services_codes' );
2287
			if ( is_array( $verification_services_codes ) && ! empty( $verification_services_codes ) ) {
2288
				$services = array();
2289
				foreach ( jetpack_verification_services() as $name => $service ) {
2290
					if ( is_array( $service ) && ! empty( $verification_services_codes[ $name ] ) ) {
2291
						switch ( $name ) {
2292
							case 'google':
2293
								$services[] = 'Google';
2294
								break;
2295
							case 'bing':
2296
								$services[] = 'Bing';
2297
								break;
2298
							case 'pinterest':
2299
								$services[] = 'Pinterest';
2300
								break;
2301
						}
2302
					}
2303
				}
2304
				if ( ! empty( $services ) ) {
2305
					if ( 2 > count( $services ) ) {
2306
						$message = esc_html( sprintf( __( 'Your site is verified with %s.', 'jetpack' ), $services[0] ) );
2307
					} else {
2308
						$copy_services = $services;
2309
						$last = count( $copy_services ) - 1;
2310
						$last_service = $copy_services[ $last ];
2311
						unset( $copy_services[ $last ] );
2312
						$message = esc_html( sprintf( __( 'Your site is verified with %s and %s.', 'jetpack' ), join( ', ', $copy_services ), $last_service ) );
2313
					}
2314
					return rest_ensure_response( array(
2315
						'code'     => 'success',
2316
						'message'  => $message,
2317
						'services' => $services,
2318
					) );
2319
				}
2320
			}
2321
			return new WP_Error( 'empty', esc_html__( 'Site not verified with any service.', 'jetpack' ), array( 'status' => 404 ) );
2322
		}
2323
2324
		return new WP_Error( 'not_active', esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ), array( 'status' => 404 ) );
2325
	}
2326
2327
	/**
2328
	 * Get VaultPress site data including, among other things, the date of tge last backup if it was completed.
2329
	 *
2330
	 * @since 4.1.0
2331
	 *
2332
	 * @return mixed|WP_Error VaultPress site data. Otherwise, a WP_Error instance with the corresponding error.
2333
	 */
2334
	public static function vaultpress_get_site_data() {
2335
		if ( class_exists( 'VaultPress' ) ) {
2336
			$vaultpress = new VaultPress();
2337
			if ( ! $vaultpress->is_registered() ) {
2338
				return rest_ensure_response( array(
2339
					'code'    => 'not_registered',
2340
					'message' => esc_html( __( 'You need to register for VaultPress.', 'jetpack' ) )
2341
				) );
2342
			}
2343
			$data = json_decode( base64_decode( $vaultpress->contact_service( 'plugin_data' ) ) );
2344
			if ( is_wp_error( $data ) ) {
2345
				return $data;
2346
			} else {
2347
				return rest_ensure_response( array(
2348
					'code'    => 'success',
2349
					'message' => esc_html( sprintf( __( 'Your site was successfully backed-up %s ago.', 'jetpack' ), human_time_diff( $data->backups->last_backup, current_time( 'timestamp' ) ) ) ),
2350
					'data'    => $data,
2351
				) );
2352
			}
2353
		}
2354
2355
		return new WP_Error( 'not_active', esc_html__( 'The requested Jetpack module is not active.', 'jetpack' ), array( 'status' => 404 ) );
2356
	}
2357
2358
} // class end
2359