diff options
Diffstat (limited to 'plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php')
-rw-r--r-- | plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php | 977 |
1 files changed, 536 insertions, 441 deletions
diff --git a/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php b/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php index 342c85b2..35d91c3b 100644 --- a/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php +++ b/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php @@ -1,19 +1,18 @@ -<?php +<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName +/** + * Register WP REST API endpoints for Jetpack. + * + * @package automattic/jetpack + */ use Automattic\Jetpack\Connection\Client; use Automattic\Jetpack\Connection\Manager as Connection_Manager; use Automattic\Jetpack\Connection\Rest_Authentication; use Automattic\Jetpack\Connection\REST_Connector; use Automattic\Jetpack\Jetpack_CRM_Data; -use Automattic\Jetpack\Licensing; -use Automattic\Jetpack\Search\REST_Controller as Search_REST_Controller; +use Automattic\Jetpack\Plugins_Installer; use Automattic\Jetpack\Status\Host; - -/** - * Register WP REST API endpoints for Jetpack. - * - * @author Automattic - */ +use Automattic\Jetpack\Status\Visitor; /** * Disable direct access. @@ -31,25 +30,16 @@ add_action( 'rest_api_init', array( 'Jetpack_Core_Json_Api_Endpoints', 'register // Each of these is a class that will register its own routes on 'rest_api_init'. require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/load-wpcom-endpoints.php'; -// Load Search endpoints when WP REST API is initialized. -add_action( 'rest_api_init', array( new Search_REST_Controller(), 'register_rest_routes' ) ); - /** * Class Jetpack_Core_Json_Api_Endpoints * * @since 4.3.0 */ class Jetpack_Core_Json_Api_Endpoints { - /** - * @var string Generic error message when user is not allowed to perform an action. + * Roles that can access Stats once they're granted access. * - * @deprecated 8.8.0 Use `REST_Connector::get_user_permissions_error_msg()` instead. - */ - public static $user_permissions_error_msg; - - /** - * @var array Roles that can access Stats once they're granted access. + * @var array */ public static $stats_roles; @@ -60,16 +50,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function register_endpoints() { - // Load API endpoint base classes + // Load API endpoint base classes. require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php'; - // Load API endpoints + // Load API endpoints. require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php'; require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php'; require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-widgets-endpoints.php'; - self::$user_permissions_error_msg = REST_Connector::get_user_permissions_error_msg(); - self::$stats_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); $ixr_client = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id() ) ); @@ -110,7 +98,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Test current connection status of Jetpack + // Test current connection status of Jetpack. register_rest_route( 'jetpack/v4', '/connection/test', @@ -152,7 +140,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Fetches a fresh connect URL + // Fetches a fresh connect URL. register_rest_route( 'jetpack/v4', '/connection/url', @@ -188,7 +176,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Disconnect/unlink user from WordPress.com servers + // Disconnect/unlink user from WordPress.com servers. register_rest_route( 'jetpack/v4', '/connection/user', @@ -199,7 +187,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Get current site data + // Get current site data. register_rest_route( 'jetpack/v4', '/site', @@ -210,7 +198,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Get current site data + // Get current site data. register_rest_route( 'jetpack/v4', '/site/features', @@ -242,7 +230,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Get current site benefits + // Get current site benefits. register_rest_route( 'jetpack/v4', '/site/benefits', @@ -264,7 +252,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Return all modules + // Return all modules. register_rest_route( 'jetpack/v4', '/module/all', @@ -275,7 +263,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Activate many modules + // Activate many modules. register_rest_route( 'jetpack/v4', '/module/all/active', @@ -303,7 +291,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Return a single module and update it when needed + // Return a single module and update it when needed. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', @@ -314,7 +302,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Activate and deactivate a module + // Activate and deactivate a module. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/active', @@ -333,7 +321,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Update a module + // Update a module. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', @@ -365,7 +353,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Check if the API key for a specific service is valid or not + // Check if the API key for a specific service is valid or not. register_rest_route( 'jetpack/v4', '/module/(?P<service>[a-z\-]+)/key/check', @@ -395,7 +383,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Update any Jetpack module option or setting + // Update any Jetpack module option or setting. register_rest_route( 'jetpack/v4', '/settings', @@ -407,7 +395,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Update a module + // Update a module. register_rest_route( 'jetpack/v4', '/settings/(?P<slug>[a-z\-]+)', @@ -419,7 +407,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Return all module settings + // Return all module settings. register_rest_route( 'jetpack/v4', '/settings/', @@ -430,7 +418,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Reset all Jetpack options + // Reset all Jetpack options. register_rest_route( 'jetpack/v4', '/options/(?P<options>[a-z\-]+)', @@ -441,7 +429,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Updates: get number of plugin updates available + // Updates: get number of plugin updates available. register_rest_route( 'jetpack/v4', '/updates/plugins', @@ -452,7 +440,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Dismiss Jetpack Notices + // Dismiss Jetpack Notices. register_rest_route( 'jetpack/v4', '/notice/(?P<notice>[a-z\-_]+)', @@ -559,7 +547,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Site Verify: check if the site is verified, and a get verification token if not + // Site Verify: check if the site is verified, and a get verification token if not. register_rest_route( 'jetpack/v4', '/verify-site/(?P<service>[a-z\-_]+)', @@ -580,7 +568,7 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - // Site Verify: tell a service to verify the site + // Site Verify: tell a service to verify the site. register_rest_route( 'jetpack/v4', '/verify-site/(?P<service>[a-z\-_]+)', @@ -680,105 +668,26 @@ class Jetpack_Core_Json_Api_Endpoints { ) ); - /* - * Get and update the last licensing error message. - */ register_rest_route( 'jetpack/v4', - '/licensing/error', + '/recommendations/conditional', array( array( 'methods' => WP_REST_Server::READABLE, - 'callback' => __CLASS__ . '::get_licensing_error', - 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check', - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => __CLASS__ . '::update_licensing_error', + 'callback' => __CLASS__ . '::get_conditional_recommendations', 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check', - 'args' => array( - 'error' => array( - 'required' => true, - 'type' => 'string', - 'validate_callback' => __CLASS__ . '::validate_string', - 'sanitize_callback' => 'sanitize_text_field', - ), - ), ), ) ); - // Return all module settings. + // Get site discount. register_rest_route( 'jetpack/v4', - '/licensing/set-license', - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => __CLASS__ . '::set_jetpack_license', - 'permission_callback' => __CLASS__ . '::set_jetpack_license_key_permission_check', - 'args' => array( - 'license' => array( - 'required' => true, - 'type' => 'string', - 'validate_callback' => __CLASS__ . '::validate_string', - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - ) - ); - - /** - * Get Jetpack user license counts. - */ - register_rest_route( - 'jetpack/v4', - 'licensing/user/counts', + '/site/discount', array( 'methods' => WP_REST_Server::READABLE, - 'callback' => __CLASS__ . '::get_user_license_counts', - 'permission_callback' => __CLASS__ . '::user_licensing_permission_check', - ) - ); - - /** - * Update user-licensing activation notice dismiss info. - */ - register_rest_route( - 'jetpack/v4', - 'licensing/user/activation-notice-dismiss', - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => __CLASS__ . '::update_licensing_activation_notice_dismiss', - 'permission_callback' => __CLASS__ . '::user_licensing_permission_check', - 'args' => array( - 'last_detached_count' => array( - 'required' => true, - 'type' => 'integer', - 'validate_callback' => __CLASS__ . '::validate_non_neg_int', - ), - ), - ) - ); - - /** - * Attach licenses to user account - */ - register_rest_route( - 'jetpack/v4', - '/licensing/attach-licenses', - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => __CLASS__ . '::attach_jetpack_licenses', - 'permission_callback' => __CLASS__ . '::user_licensing_permission_check', - 'args' => array( - 'licenses' => array( - 'required' => true, - 'type' => 'array', - 'items' => array( - 'type' => 'string', - ), - ), - ), + 'callback' => __CLASS__ . '::get_site_discount', + 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check', ) ); @@ -837,6 +746,17 @@ class Jetpack_Core_Json_Api_Endpoints { 'permission_callback' => __CLASS__ . '::manage_modules_permission_check', ) ); + + // Get Jetpack introduction offers + register_rest_route( + 'jetpack/v4', + '/intro-offers', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => __CLASS__ . '::get_intro_offers', + 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check', + ) + ); } /** @@ -908,7 +828,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ) ); @@ -952,7 +872,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ) ); @@ -970,6 +890,15 @@ class Jetpack_Core_Json_Api_Endpoints { } /** + * Get conditional recommendations data. + * + * @return array Conditional recommendations data. + */ + public static function get_conditional_recommendations() { + return Jetpack_Recommendations::get_conditional_recommendations(); + } + + /** * Validate the recommendations data * * @param array $value Value to check received by request. @@ -1029,6 +958,11 @@ class Jetpack_Core_Json_Api_Endpoints { return Jetpack_Options::delete_option( 'purchase_token' ); } + /** + * Get list of Jetpack Plans. + * + * @param WP_REST_Request $request The request. + */ public static function get_plans( $request ) { $request = Client::wpcom_json_api_request_as_user( '/plans?_locale=' . get_user_locale(), @@ -1036,7 +970,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ) ); @@ -1045,7 +979,7 @@ class Jetpack_Core_Json_Api_Endpoints { if ( 200 === wp_remote_retrieve_response_code( $request ) ) { $data = $body; } else { - // something went wrong so we'll just return the response without caching + // something went wrong so we'll just return the response without caching. return $body; } @@ -1060,14 +994,14 @@ class Jetpack_Core_Json_Api_Endpoints { * * @return string|WP_Error A JSON object of wpcom products if the request was successful, or a WP_Error otherwise. */ - public static function get_products( $request ) { + public static function get_products( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $wpcom_request = Client::wpcom_json_api_request_as_user( '/products?_locale=' . get_user_locale() . '&type=jetpack', '2', array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ) ); @@ -1086,76 +1020,11 @@ class Jetpack_Core_Json_Api_Endpoints { } /** - * Gets the users licenses counts. - * - * @since 10.4.0 - * - * @return string|WP_Error A JSON object of user license counts if the request was successful, or a WP_Error otherwise. - */ - public static function get_user_license_counts() { - $wpcom_request = Client::wpcom_json_api_request_as_user( - '/jetpack-licensing/user/licenses/counts', - '2', - array( - 'method' => 'GET', - 'headers' => array( - 'Content-Type' => 'application/json', - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), - ), - ) - ); - - $response_code = wp_remote_retrieve_response_code( $wpcom_request ); - if ( 200 === $response_code ) { - $license_counts = json_decode( wp_remote_retrieve_body( $wpcom_request ) ); - return $license_counts; - } else { - return new WP_Error( - 'failed_to_fetch_data', - esc_html__( 'Unable to fetch the requested data.', 'jetpack' ), - array( 'status' => $response_code ) - ); - } - } - - /** - * Update the user-licenses activation notice dismissal data. - * - * @since 10.4.0 - * - * @param WP_REST_Request $request The request sent to the WP REST API. + * Send Survey details to WordPress.com. * - * @return array|WP_Error + * @param WP_REST_Request $request The request. */ - public static function update_licensing_activation_notice_dismiss( $request ) { - - if ( ! isset( $request['last_detached_count'] ) ) { - return new WP_Error( 'invalid_param', esc_html__( 'Missing parameter "last_detached_count".', 'jetpack' ), array( 'status' => 404 ) ); - } - - $default = array( - 'last_detached_count' => null, - 'last_dismissed_time' => null, - ); - $last_detached_count = ( '' === $request['last_detached_count'] ) - ? $default['last_detached_count'] - : $request['last_detached_count']; - $last_dismissed_time = ( '' === $request['last_detached_count'] ) - ? $default['last_dismissed_time'] - // Use UTC timezone and convert to ISO8601 format(DateTime::W3C) for best compatibility with JavaScript Date in all browsers. - : ( new DateTime( 'NOW', new DateTimeZone( 'UTC' ) ) )->format( DateTime::W3C ); - - $notice_data = array( - 'last_detached_count' => $last_detached_count, - 'last_dismissed_time' => $last_dismissed_time, - ); - - Jetpack_Options::update_option( 'licensing_activation_notice_dismiss', $notice_data, true ); - return rest_ensure_response( $notice_data ); - } - public static function submit_survey( $request ) { - $wpcom_request = Client::wpcom_json_api_request_as_user( '/marketing/survey', 'v2', @@ -1163,7 +1032,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'method' => 'POST', 'headers' => array( 'Content-Type' => 'application/json', - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ), $request->get_json_params() @@ -1173,7 +1042,7 @@ class Jetpack_Core_Json_Api_Endpoints { if ( 200 === wp_remote_retrieve_response_code( $wpcom_request ) ) { $data = $wpcom_request_body; } else { - // something went wrong so we'll just return the response without caching + // something went wrong so we'll just return the response without caching. return $wpcom_request_body; } @@ -1214,10 +1083,10 @@ class Jetpack_Core_Json_Api_Endpoints { $csp4 = get_option( 'seed_csp4_settings_content', array() ); if ( ( Jetpack::is_plugin_active( 'mojo-marketplace-wp-plugin/mojo-marketplace.php' ) && 'true' === $mm_coming_soon ) - || Jetpack::is_plugin_active( 'mojo-under-construction/mojo-contruction.php' ) && 1 == $under_construction_activation_status // WPCS: loose comparison ok. - || ( Jetpack::is_plugin_active( 'under-construction-page/under-construction.php' ) && isset( $ucp_options['status'] ) && 1 == $ucp_options['status'] ) // WPCS: loose comparison ok. - || ( Jetpack::is_plugin_active( 'ultimate-under-construction/ultimate-under-construction.php' ) && isset( $uuc_settings['enable'] ) && 1 == $uuc_settings['enable'] ) // WPCS: loose comparison ok. - || ( Jetpack::is_plugin_active( 'coming-soon/coming-soon.php' ) && isset( $csp4['status'] ) && ( 1 == $csp4['status'] || 2 == $csp4['status'] ) ) // WPCS: loose comparison ok. + || Jetpack::is_plugin_active( 'mojo-under-construction/mojo-contruction.php' ) && 1 == $under_construction_activation_status // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + || ( Jetpack::is_plugin_active( 'under-construction-page/under-construction.php' ) && isset( $ucp_options['status'] ) && 1 == $ucp_options['status'] ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + || ( Jetpack::is_plugin_active( 'ultimate-under-construction/ultimate-under-construction.php' ) && isset( $uuc_settings['enable'] ) && 1 == $uuc_settings['enable'] ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + || ( Jetpack::is_plugin_active( 'coming-soon/coming-soon.php' ) && isset( $csp4['status'] ) && ( 1 == $csp4['status'] || 2 == $csp4['status'] ) ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual /** * Allow plugins to mark a site as "under construction". * @@ -1254,6 +1123,11 @@ class Jetpack_Core_Json_Api_Endpoints { } } + /** + * Verify site with external service. + * + * @param WP_REST_Request $request The request. + */ public static function verify_site( $request ) { $xml = new Jetpack_IXR_Client( array( @@ -1299,7 +1173,7 @@ class Jetpack_Core_Json_Api_Endpoints { public static function dismiss_notice( $request ) { $notice = $request['notice']; - if ( ! isset( $request['dismissed'] ) || $request['dismissed'] !== true ) { + if ( ! isset( $request['dismissed'] ) || true !== $request['dismissed'] ) { return new WP_Error( 'invalid_param', esc_html__( 'Invalid parameter "dismissed".', 'jetpack' ), array( 'status' => 404 ) ); } @@ -1332,7 +1206,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_jetpack_disconnect', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } @@ -1348,7 +1226,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_jetpack_connect', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_jetpack_connect', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } @@ -1366,7 +1248,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_unlink_user', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_unlink_user', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1381,7 +1267,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_manage_modules', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_manage_modules', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1396,7 +1286,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_configure_modules', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_configure_modules', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1411,7 +1305,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_view_admin', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_view_admin', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1426,7 +1324,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_manage_settings', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_manage_settings', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1441,7 +1343,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_activate_plugins', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_activate_plugins', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1454,7 +1360,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_user_permission_edit_others_posts', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_user_permission_edit_others_posts', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1471,22 +1381,11 @@ class Jetpack_Core_Json_Api_Endpoints { return true; } - return new WP_Error( 'invalid_permission_manage_purchase_token', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); - } - - /** - * Verify that user can view and update user-licensing data. - * - * @return bool Whether the user is currently connected and they are the connection owner. - */ - public static function user_licensing_permission_check() { - $connection_manager = new Connection_Manager( 'jetpack' ); - - if ( $connection_manager->is_user_connected() && $connection_manager->is_connection_owner() ) { - return true; - } - - return new WP_Error( 'invalid_permission_manage_user_licenses', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( + 'invalid_permission_manage_purchase_token', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); } /** @@ -1520,16 +1419,17 @@ class Jetpack_Core_Json_Api_Endpoints { * @return bool */ public static function view_jetpack_connection_test_check() { + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- This is verifying the trusted caller via a shared private key and timestamp. if ( ! isset( $_GET['signature'], $_GET['timestamp'], $_GET['url'] ) ) { return false; } - $signature = base64_decode( $_GET['signature'] ); + $signature = base64_decode( wp_unslash( $_GET['signature'] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $signature_data = wp_json_encode( array( - 'rest_route' => $_GET['rest_route'], - 'timestamp' => (int) $_GET['timestamp'], - 'url' => wp_unslash( $_GET['url'] ), + 'rest_route' => isset( $_GET['rest_route'] ) ? filter_var( wp_unslash( $_GET['rest_route'] ) ) : null, + 'timestamp' => (int) $_GET['timestamp'], + 'url' => esc_url_raw( wp_unslash( $_GET['url'] ) ), ) ); @@ -1544,11 +1444,13 @@ class Jetpack_Core_Json_Api_Endpoints { return false; } - // signature timestamp must be within 5min of current time + // signature timestamp must be within 5min of current time. if ( abs( time() - (int) $_GET['timestamp'] ) > 300 ) { return false; } + // phpcs:enable WordPress.Security.NonceVerification.Recommended + return true; } @@ -1617,6 +1519,9 @@ class Jetpack_Core_Json_Api_Endpoints { ); } + /** + * Fetch information about the Rewind status of the site. + */ public static function rewind_data() { $site_id = Jetpack_Options::get_option( 'id' ); @@ -1778,7 +1683,7 @@ class Jetpack_Core_Json_Api_Endpoints { public static function disconnect_site( $request ) { _deprecated_function( __METHOD__, 'jetpack-10.0.0', '\Automattic\Jetpack\Connection\REST_Connector::disconnect_site' ); - if ( ! isset( $request['isActive'] ) || $request['isActive'] !== false ) { + if ( ! isset( $request['isActive'] ) || false !== $request['isActive'] ) { return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); } @@ -1818,7 +1723,7 @@ class Jetpack_Core_Json_Api_Endpoints { return rest_ensure_response( array( - 'authorizeUrl' => Jetpack::build_authorize_url( false, true ), + 'authorizeUrl' => Jetpack::build_authorize_url( false ), ) ); } @@ -1884,7 +1789,7 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function unlink_user( $request ) { - if ( ! isset( $request['linked'] ) || $request['linked'] !== false ) { + if ( ! isset( $request['linked'] ) || false !== $request['linked'] ) { return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); } @@ -1908,7 +1813,7 @@ class Jetpack_Core_Json_Api_Endpoints { * * @return WP_REST_Response|WP_Error Response, else error. */ - public static function get_user_tracking_settings( $request ) { + public static function get_user_tracking_settings( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected() ) { $response = array( 'tracks_opt_out' => true, // Default to opt-out if not connected to wp.com. @@ -1920,7 +1825,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ) ); @@ -1954,7 +1859,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'method' => 'PUT', 'headers' => array( 'Content-Type' => 'application/json', - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ), wp_json_encode( $request->get_params() ) @@ -1985,7 +1890,7 @@ class Jetpack_Core_Json_Api_Endpoints { // Allow use a store sandbox. Internal ref: PCYsg-IA-p2. if ( isset( $_COOKIE ) && isset( $_COOKIE['store_sandbox'] ) ) { - $secret = $_COOKIE['store_sandbox']; + $secret = filter_var( wp_unslash( $_COOKIE['store_sandbox'] ) ); $args['headers']['Cookie'] = "store_sandbox=$secret;"; } @@ -2032,7 +1937,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'code' => 'success', 'message' => esc_html__( 'Site data correctly received.', 'jetpack' ), - 'data' => json_encode( $site_data ), + 'data' => wp_json_encode( $site_data ), ) ); } @@ -2081,7 +1986,7 @@ class Jetpack_Core_Json_Api_Endpoints { array( 'method' => 'GET', 'headers' => array( - 'X-Forwarded-For' => Jetpack::current_user_ip( true ), + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), ), ), null, @@ -2103,7 +2008,7 @@ class Jetpack_Core_Json_Api_Endpoints { return new WP_Error( 'activity_not_found', esc_html__( 'No activity found', 'jetpack' ), - array( 'status' => 204 ) // no content + array( 'status' => 204 ) // no content. ); } @@ -2116,6 +2021,62 @@ class Jetpack_Core_Json_Api_Endpoints { } /** + * Fetch the discount for this site and return it. + * + * @since 10.8 + * + * @return array|WP_Error + */ + public static function get_site_discount() { + $site_id = Jetpack_Options::get_option( 'id' ); + + if ( ! $site_id ) { + return new WP_Error( + 'site_id_missing', + esc_html__( 'Site ID is missing.', 'jetpack' ), + array( 'status' => 400 ) + ); + } + + $response = Client::wpcom_json_api_request_as_user( + "/sites/$site_id/discount", + '2', + array( + 'method' => 'GET', + 'headers' => array( + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), + ), + ) + ); + + $response_code = wp_remote_retrieve_response_code( $response ); + $data = json_decode( wp_remote_retrieve_body( $response ) ); + + if ( 200 !== $response_code ) { + return new WP_Error( + 'discount_fetch_failed', + is_object( $data ) && property_exists( $data, 'error' ) ? $data->error : esc_html__( 'Could not retrieve site discount.', 'jetpack' ), + array( 'status' => $response_code ) + ); + } + + if ( ! isset( $data ) ) { + return new WP_Error( + 'discount_parse_error', + esc_html__( 'Could not parse discount', 'jetpack' ), + array( 'status' => 204 ) // no content. + ); + } + + return rest_ensure_response( + array( + 'code' => 'success', + 'data' => $data, + ) + ); + } + + /** * Reset Jetpack options * * @since 4.3.0 @@ -2130,18 +2091,19 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function reset_jetpack_options( $request ) { - if ( ! isset( $request['reset'] ) || $request['reset'] !== true ) { + if ( ! isset( $request['reset'] ) || true !== $request['reset'] ) { return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); } if ( isset( $request['options'] ) ) { - $data = $request['options']; + $data = $request['options']; + $message = ''; switch ( $data ) { case ( 'options' ): $options_to_reset = Jetpack::get_jetpack_options_for_reset(); - // Reset the Jetpack options + // Reset the Jetpack options. foreach ( $options_to_reset['jp_options'] as $option_to_reset ) { Jetpack_Options::delete_option( $option_to_reset ); } @@ -2150,32 +2112,28 @@ class Jetpack_Core_Json_Api_Endpoints { delete_option( $option_to_reset ); } - // Reset to default modules + // Reset to default modules. $default_modules = Jetpack::get_default_modules(); Jetpack::update_active_modules( $default_modules ); + $message = esc_html__( 'Jetpack options reset.', 'jetpack' ); - return rest_ensure_response( - array( - 'code' => 'success', - 'message' => esc_html__( 'Jetpack options reset.', 'jetpack' ), - ) - ); break; - case 'modules': $default_modules = Jetpack::get_default_modules(); Jetpack::update_active_modules( $default_modules ); - return rest_ensure_response( - array( - 'code' => 'success', - 'message' => esc_html__( 'Modules reset to default.', 'jetpack' ), - ) - ); - break; + $message = esc_html__( 'Modules reset to default.', 'jetpack' ); + break; default: return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); } + + return rest_ensure_response( + array( + 'code' => 'success', + 'message' => $message, + ) + ); } return new WP_Error( 'required_param', esc_html__( 'Missing parameter "type".', 'jetpack' ), array( 'status' => 404 ) ); @@ -2217,6 +2175,14 @@ class Jetpack_Core_Json_Api_Endpoints { public static function get_updateable_data_list( $selector = '' ) { $options = array( + // Blocks. + 'jetpack_blocks_disabled' => array( + 'description' => esc_html__( 'Jetpack Blocks disabled.', 'jetpack' ), + 'type' => 'boolean', + 'default' => false, + 'validate_callback' => __CLASS__ . '::validate_boolean', + 'jp_group' => 'settings', + ), // Carousel 'carousel_background_color' => array( @@ -2257,7 +2223,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'carousel', ), - // Comments + // Comments. 'highlander_comment_form_prompt' => array( 'description' => esc_html__( 'Greeting Text', 'jetpack' ), 'type' => 'string', @@ -2283,7 +2249,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'comments', ), - // Custom Content Types + // Custom Content Types. 'jetpack_portfolio' => array( 'description' => esc_html__( 'Enable or disable Jetpack portfolio post type.', 'jetpack' ), 'type' => 'boolean', @@ -2313,7 +2279,38 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'custom-content-types', ), - // Galleries + // WAF. + 'jetpack_waf_ip_list' => array( + 'description' => esc_html__( 'Allow / Block list - Block or allow a specific request IP.', 'jetpack' ), + 'type' => 'boolean', + 'default' => 0, + 'validate_callback' => __CLASS__ . '::validate_boolean', + 'jp_group' => 'waf', + ), + 'jetpack_waf_ip_block_list' => array( + 'description' => esc_html__( 'Blocked IP addresses', 'jetpack' ), + 'type' => 'string', + 'default' => '', + 'validate_callback' => __CLASS__ . '::validate_string', + 'sanitize_callback' => 'esc_textarea', + 'jp_group' => 'waf', + ), + 'jetpack_waf_ip_allow_list' => array( + 'description' => esc_html__( 'Always allowed IP addresses', 'jetpack' ), + 'type' => 'string', + 'default' => '', + 'validate_callback' => __CLASS__ . '::validate_string', + 'sanitize_callback' => 'esc_textarea', + 'jp_group' => 'waf', + ), + 'jetpack_waf_share_data' => array( + 'description' => esc_html__( 'Share data with Jetpack.', 'jetpack' ), + 'type' => 'boolean', + 'default' => 0, + 'validate_callback' => __CLASS__ . '::validate_boolean', + 'jp_group' => 'waf', + ), + // Galleries. 'tiled_galleries' => array( 'description' => esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ), 'type' => 'boolean', @@ -2339,7 +2336,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'gravatar-hovercards', ), - // Infinite Scroll + // Infinite Scroll. 'infinite_scroll' => array( 'description' => esc_html__( 'To infinity and beyond', 'jetpack' ), 'type' => 'boolean', @@ -2355,7 +2352,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'infinite-scroll', ), - // Likes + // Likes. 'wpl_default' => array( 'description' => esc_html__( 'WordPress.com Likes are', 'jetpack' ), 'type' => 'string', @@ -2379,7 +2376,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'likes', ), - // Markdown + // Markdown. 'wpcom_publish_comments_with_markdown' => array( 'description' => esc_html__( 'Use Markdown for comments.', 'jetpack' ), 'type' => 'boolean', @@ -2395,7 +2392,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'markdown', ), - // Monitor + // Monitor. 'monitor_receive_notifications' => array( 'description' => esc_html__( 'Receive Monitor Email Notifications.', 'jetpack' ), 'type' => 'boolean', @@ -2404,7 +2401,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'monitor', ), - // Post by Email + // Post by Email. 'post_by_email_address' => array( 'description' => esc_html__( 'Email Address', 'jetpack' ), 'type' => 'string', @@ -2425,7 +2422,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'post-by-email', ), - // Protect + // Protect. 'jetpack_protect_key' => array( 'description' => esc_html__( 'Protect API key', 'jetpack' ), 'type' => 'string', @@ -2442,7 +2439,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'protect', ), - // Sharing + // Sharing. 'sharing_services' => array( 'description' => esc_html__( 'Enabled Services and those hidden behind a button', 'jetpack' ), 'type' => 'object', @@ -2525,7 +2522,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'sharedaddy', ), - // SSO + // SSO. 'jetpack_sso_require_two_step' => array( 'description' => esc_html__( 'Require Two-Step Authentication', 'jetpack' ), 'type' => 'boolean', @@ -2541,7 +2538,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'sso', ), - // Subscriptions + // Subscriptions. 'stb_enabled' => array( 'description' => esc_html__( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ), 'type' => 'boolean', @@ -2564,7 +2561,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'subscriptions', ), - // Related Posts + // Related Posts. 'show_headline' => array( 'description' => esc_html__( 'Highlight related content with a heading', 'jetpack' ), 'type' => 'boolean', @@ -2605,7 +2602,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'search', ), - // Verification Tools + // Verification Tools. 'google' => array( 'description' => esc_html__( 'Google Search Console', 'jetpack' ), 'type' => 'string', @@ -2723,7 +2720,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'wordads', ), - // Google Analytics + // Google Analytics. 'google_analytics_tracking_id' => array( 'description' => esc_html__( 'Google Analytics', 'jetpack' ), 'type' => 'string', @@ -2732,7 +2729,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'google-analytics', ), - // Stats + // Stats. 'admin_bar' => array( 'description' => esc_html__( 'Include a small chart in your admin bar with a 48-hour traffic snapshot.', 'jetpack' ), 'type' => 'boolean', @@ -2807,7 +2804,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'settings', ), - // Apps card on dashboard + // Apps card on dashboard. 'dismiss_dash_app_card' => array( 'description' => '', 'type' => 'boolean', @@ -2816,7 +2813,7 @@ class Jetpack_Core_Json_Api_Endpoints { 'jp_group' => 'settings', ), - // Empty stats card dismiss + // Empty stats card dismiss. 'dismiss_empty_stats_card' => array( 'description' => '', 'type' => 'boolean', @@ -2878,9 +2875,18 @@ class Jetpack_Core_Json_Api_Endpoints { 'sanitize_callback' => 'Jetpack_SEO_Titles::sanitize_title_formats', ), + // VideoPress. + 'videopress_private_enabled_for_site' => array( + 'description' => esc_html__( 'Video Privacy: Restrict views to members of this site', 'jetpack' ), + 'type' => 'boolean', + 'default' => 0, + 'validate_callback' => __CLASS__ . '::validate_boolean', + 'jp_group' => 'videopress', + ), + ); - // Add modules to list so they can be toggled + // Add modules to list so they can be toggled. $modules = Jetpack::get_available_modules(); if ( is_array( $modules ) && ! empty( $modules ) ) { $module_args = array( @@ -2897,13 +2903,13 @@ class Jetpack_Core_Json_Api_Endpoints { if ( is_array( $selector ) ) { - // Return only those options whose keys match $selector keys + // Return only those options whose keys match $selector keys. return array_intersect_key( $options, $selector ); } if ( 'any' === $selector ) { - // Toggle module or update any module option or any general setting + // Toggle module or update any module option or any general setting. return $options; } @@ -2966,7 +2972,14 @@ class Jetpack_Core_Json_Api_Endpoints { public static function validate_boolean( $value, $request, $param ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict -- Other code depends on loose comparison here. if ( ! is_bool( $value ) && ! ( ctype_digit( (string) $value ) && in_array( $value, array( 0, 1 ) ) ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be true, false, 0 or 1.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be true, false, 0 or 1.', 'jetpack' ), + $param + ) + ); } return true; } @@ -2984,7 +2997,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_posint( $value, $request, $param ) { if ( ! is_numeric( $value ) || $value <= 0 ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a positive integer.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be a positive integer.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3025,14 +3045,23 @@ class Jetpack_Core_Json_Api_Endpoints { public static function validate_list_item( $value, $request, $param ) { $attributes = $request->get_attributes(); if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s not recognized', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s not recognized', 'jetpack' ), + $param + ) + ); } $args = $attributes['args'][ $param ]; if ( ! empty( $args['enum'] ) ) { - // If it's an associative array, use the keys to check that the value is among those admitted. - $enum = ( count( array_filter( array_keys( $args['enum'] ), 'is_string' ) ) > 0 ) ? array_keys( $args['enum'] ) : $args['enum']; - if ( ! in_array( $value, $enum ) ) { + $enum = ( count( array_filter( array_keys( $args['enum'] ), 'is_string' ) ) > 0 ) + ? array_keys( $args['enum'] ) + : $args['enum']; + $enum = array_map( 'strval', $enum ); + if ( ! in_array( $value, $enum, true ) ) { return new WP_Error( 'invalid_param_value', sprintf( @@ -3060,13 +3089,27 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_module_list( $value, $request, $param ) { if ( ! is_array( $value ) ) { - return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be an array', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param_value', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be an array', 'jetpack' ), + $param + ) + ); } $modules = Jetpack::get_available_modules(); - if ( count( array_intersect( $value, $modules ) ) != count( $value ) ) { - return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be a list of valid modules', 'jetpack' ), $param ) ); + if ( count( array_intersect( $value, $modules ) ) !== count( $value ) ) { + return new WP_Error( + 'invalid_param_value', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be a list of valid modules', 'jetpack' ), + $param + ) + ); } return true; @@ -3085,7 +3128,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_alphanum( $value, $request, $param ) { if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^[a-z0-9]+$/i', $value ) ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3095,15 +3145,22 @@ class Jetpack_Core_Json_Api_Endpoints { * * @since 4.6.0 * - * @param string $value Value to check. - * @param WP_REST_Request $request - * @param string $param Name of the parameter passed to endpoint holding $value. + * @param string $value Value to check. + * @param WP_REST_Request $request The request sent to the WP REST API. + * @param string $param Name of the parameter passed to endpoint holding $value. * * @return bool|WP_Error */ public static function validate_verification_service( $value, $request, $param ) { if ( ! empty( $value ) && ! ( is_string( $value ) && ( preg_match( '/^[a-z0-9_-]+$/i', $value ) || jetpack_verification_get_code( $value ) !== false ) ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string or a verification tag.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a verification string used to verify a service like Google Webmaster Console. */ + esc_html__( '%s must be an alphanumeric string or a verification tag.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3124,7 +3181,7 @@ class Jetpack_Core_Json_Api_Endpoints { return new WP_Error( 'invalid_param', sprintf( - /* Translators: first variable is the name of a parameter passed to endpoint holding the role that will be checked, the second is a list of roles allowed to see stats. The parameter is checked against this list. */ + /* Translators: first variable is the name of a parameter passed to endpoint holding the role that will be checked, the second is a list of roles allowed to see stats. The parameter is checked against this list. */ esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', self::$stats_roles ) @@ -3148,13 +3205,20 @@ class Jetpack_Core_Json_Api_Endpoints { public static function validate_sharing_show( $value, $request, $param ) { $views = array( 'index', 'post', 'page', 'attachment', 'jetpack-portfolio' ); if ( ! is_array( $value ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array of post types.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be an array of post types.', 'jetpack' ), + $param + ) + ); } if ( ! array_intersect( $views, $value ) ) { return new WP_Error( 'invalid_param', sprintf( - /* Translators: first variable is the name of a parameter passed to endpoint holding the post type where Sharing will be displayed, the second is a list of post types where Sharing can be displayed */ + /* Translators: first variable is the name of a parameter passed to endpoint holding the post type where Sharing will be displayed, the second is a list of post types where Sharing can be displayed */ esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', $views ) @@ -3182,7 +3246,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_services( $value, $request, $param ) { if ( ! is_array( $value ) || ! isset( $value['visible'] ) || ! isset( $value['hidden'] ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with visible and hidden items.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be an array with visible and hidden items.', 'jetpack' ), + $param + ) + ); } // Allow to clear everything. @@ -3203,7 +3274,7 @@ class Jetpack_Core_Json_Api_Endpoints { return new WP_Error( 'invalid_param', sprintf( - /* Translators: placeholder 1 is a parameter holding the services passed to endpoint, placeholder 2 is a list of all Jetpack Sharing services */ + /* Translators: placeholder 1 is a parameter holding the services passed to endpoint, placeholder 2 is a list of all Jetpack Sharing services */ esc_html__( '%1$s visible and hidden items must be a list of %2$s.', 'jetpack' ), $param, join( ', ', $services ) @@ -3226,7 +3297,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_custom_service( $value, $request, $param ) { if ( ! is_array( $value ) || ! isset( $value['sharing_name'] ) || ! isset( $value['sharing_url'] ) || ! isset( $value['sharing_icon'] ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with sharing name, url and icon.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be an array with sharing name, url and icon.', 'jetpack' ), + $param + ) + ); } // Allow to clear everything. @@ -3241,7 +3319,14 @@ class Jetpack_Core_Json_Api_Endpoints { if ( ( ! empty( $value['sharing_name'] ) && ! is_string( $value['sharing_name'] ) ) || ( ! empty( $value['sharing_url'] ) && ! is_string( $value['sharing_url'] ) ) || ( ! empty( $value['sharing_icon'] ) && ! is_string( $value['sharing_icon'] ) ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s needs sharing name, url and icon.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s needs sharing name, url and icon.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3259,17 +3344,31 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_custom_service_id( $value, $request, $param ) { if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/custom\-[0-1]+/i', $value ) ) ) { - 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 ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( "%s must be a string prefixed with 'custom-' and followed by a numeric ID.", 'jetpack' ), + $param + ) + ); } if ( ! class_exists( 'Sharing_Service' ) && ! include_once JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) { return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) ); } $sharer = new Sharing_Service(); - $services = array_keys( $sharer->get_all_services() ); + $services = $sharer->get_all_services(); - if ( ! empty( $value ) && ! in_array( $value, $services ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s is not a registered custom sharing service.', 'jetpack' ), $param ) ); + if ( ! empty( $value ) && ! isset( $services[ $value ] ) ) { + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s is not a registered custom sharing service.', 'jetpack' ), + $param + ) + ); } return true; @@ -3280,15 +3379,22 @@ class Jetpack_Core_Json_Api_Endpoints { * * @since 4.3.0 * - * @param string $value Value to check. - * @param WP_REST_Request $request - * @param string $param Name of the parameter passed to endpoint holding $value. + * @param string $value Value to check. + * @param WP_REST_Request $request The request sent to the WP REST API. + * @param string $param Name of the parameter passed to endpoint holding $value. * * @return bool|WP_Error */ public static function validate_twitter_username( $value, $request, $param ) { if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^@?\w{1,15}$/i', $value ) ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a Twitter username.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a twitter name. */ + esc_html__( '%s must be a Twitter username.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3306,7 +3412,14 @@ class Jetpack_Core_Json_Api_Endpoints { */ public static function validate_string( $value, $request, $param ) { if ( ! is_string( $value ) ) { - return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a string.', 'jetpack' ), $param ) ); + return new WP_Error( + 'invalid_param', + sprintf( + /* Translators: Placeholder is a parameter name. */ + esc_html__( '%s must be a string.', 'jetpack' ), + $param + ) + ); } return true; } @@ -3399,12 +3512,12 @@ class Jetpack_Core_Json_Api_Endpoints { $news_sitemap_url = home_url( $location . '/?jetpack-sitemap=news-sitemap.xml' ); } - if ( is_null( $slug ) && isset( $modules['sitemaps'] ) ) { - // Is a list of modules + if ( $slug === null && isset( $modules['sitemaps'] ) ) { + // Is a list of modules. $modules['sitemaps']['extra']['sitemap_url'] = $sitemap_url; $modules['sitemaps']['extra']['news_sitemap_url'] = $news_sitemap_url; - } elseif ( 'sitemaps' == $slug ) { - // It's a single module + } elseif ( 'sitemaps' === $slug ) { + // It's a single module. $modules['extra']['sitemap_url'] = $sitemap_url; $modules['extra']['news_sitemap_url'] = $news_sitemap_url; } @@ -3432,17 +3545,17 @@ class Jetpack_Core_Json_Api_Endpoints { switch ( $module ) { case 'monitor': - // Status of user notifications + // Status of user notifications. $options['monitor_receive_notifications']['current_value'] = self::cast_value( self::get_remote_value( 'monitor', 'monitor_receive_notifications' ), $options['monitor_receive_notifications'] ); break; case 'post-by-email': - // Email address + // Email address. $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'] ); break; case 'protect': - // Protect + // Protect. $options['jetpack_protect_key']['current_value'] = get_site_option( 'jetpack_protect_key', false ); if ( ! function_exists( 'jetpack_protect_format_whitelist' ) ) { include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php'; @@ -3464,7 +3577,7 @@ class Jetpack_Core_Json_Api_Endpoints { $wga = get_option( 'jetpack_wga' ); $code = ''; if ( is_array( $wga ) && array_key_exists( 'code', $wga ) ) { - $code = $wga[ 'code' ]; + $code = $wga['code']; } $options['google_analytics_tracking_id']['current_value'] = $code; break; @@ -3503,7 +3616,7 @@ class Jetpack_Core_Json_Api_Endpoints { // At this point some options have current_value not set because they're options // that only get written on update, so we set current_value to the default one. foreach ( $options as $key => $value ) { - // We don't need validate_callback in the response + // We don't need validate_callback in the response. if ( isset( $options[ $key ]['validate_callback'] ) ) { unset( $options[ $key ]['validate_callback'] ); } @@ -3549,7 +3662,7 @@ class Jetpack_Core_Json_Api_Endpoints { * @return bool|float|int|string */ public static function cast_value( $value, $definition ) { - if ( $value === 'NULL' ) { + if ( 'NULL' === $value ) { return null; } @@ -3561,19 +3674,19 @@ class Jetpack_Core_Json_Api_Endpoints { } elseif ( 'false' === $value || 'off' === $value ) { return false; } - return (bool) $value; + $value = (bool) $value; break; case 'integer': - return (int) $value; + $value = (int) $value; break; case 'float': - return (float) $value; + $value = (float) $value; break; case 'string': - return (string) $value; + $value = (string) $value; break; } } @@ -3625,14 +3738,14 @@ class Jetpack_Core_Json_Api_Endpoints { return false; } $value = Jetpack_Post_By_Email::init()->get_post_by_email_address(); - if ( $value === null ) { - $value = 'NULL'; // sentinel value so it actually gets set + if ( null === $value ) { + $value = 'NULL'; // sentinel value so it actually gets set. } break; } // Normalize value to boolean. - if ( is_wp_error( $value ) || is_null( $value ) ) { + if ( is_wp_error( $value ) || $value === null ) { $value = false; } @@ -3653,7 +3766,7 @@ class Jetpack_Core_Json_Api_Endpoints { $updates = wp_get_update_data(); if ( isset( $updates['counts'] ) && isset( $updates['counts']['plugins'] ) ) { $count = $updates['counts']['plugins']; - if ( 0 == $count ) { + if ( 0 === $count ) { $response = array( 'code' => 'success', 'message' => esc_html__( 'All plugins are up-to-date. Keep up the good work!', 'jetpack' ), @@ -3662,7 +3775,13 @@ class Jetpack_Core_Json_Api_Endpoints { } else { $response = array( 'code' => 'updates-available', - 'message' => esc_html( sprintf( _n( '%s plugin need updating.', '%s plugins need updating.', $count, 'jetpack' ), $count ) ), + 'message' => esc_html( + sprintf( + /* Translators: placeholders are numbers. */ + _n( '%s plugin needs updating.', '%s plugins need updating.', $count, 'jetpack' ), + $count + ) + ), 'count' => $count, ); } @@ -3680,8 +3799,7 @@ class Jetpack_Core_Json_Api_Endpoints { * @return WP_REST_Response|WP_Error List of plugins in the site. Otherwise, a WP_Error instance with the corresponding error. */ public static function get_plugins() { - jetpack_require_lib( 'plugins' ); - $plugins = Jetpack_Plugins::get_plugins(); + $plugins = Plugins_Installer::get_plugins(); if ( ! empty( $plugins ) ) { return rest_ensure_response( $plugins ); @@ -3708,14 +3826,12 @@ class Jetpack_Core_Json_Api_Endpoints { public static function install_plugin( $request ) { $plugin = stripslashes( $request['slug'] ); - jetpack_require_lib( 'plugins' ); - // Let's make sure the plugin isn't already installed. - $plugin_id = Jetpack_Plugins::get_plugin_id_by_slug( $plugin ); + $plugin_id = Plugins_Installer::get_plugin_id_by_slug( $plugin ); // If not installed, let's install now. if ( ! $plugin_id ) { - $result = Jetpack_Plugins::install_plugin( $plugin ); + $result = Plugins_Installer::install_plugin( $plugin ); if ( is_wp_error( $result ) ) { return new WP_Error( @@ -3756,7 +3872,7 @@ class Jetpack_Core_Json_Api_Endpoints { * Let's check again for the plugin's ID if we don't already have it. */ if ( ! $plugin_id ) { - $plugin_id = Jetpack_Plugins::get_plugin_id_by_slug( $plugin ); + $plugin_id = Plugins_Installer::get_plugin_id_by_slug( $plugin ); if ( ! $plugin_id ) { return new WP_Error( 'unable_to_determine_installed_plugin', @@ -3803,8 +3919,7 @@ class Jetpack_Core_Json_Api_Endpoints { ); } - jetpack_require_lib( 'plugins' ); - $plugins = Jetpack_Plugins::get_plugins(); + $plugins = Plugins_Installer::get_plugins(); if ( empty( $plugins ) ) { return new WP_Error( 'no_plugins_found', esc_html__( 'This site has no plugins.', 'jetpack' ), array( 'status' => 404 ) ); @@ -3817,7 +3932,7 @@ class Jetpack_Core_Json_Api_Endpoints { $plugin = $request['plugin'] . '.php'; // Is the plugin installed? - if ( ! in_array( $plugin, array_keys( $plugins ), true ) ) { + if ( ! array_key_exists( $plugin, $plugins ) ) { return new WP_Error( 'plugin_not_found', esc_html( @@ -3832,7 +3947,7 @@ class Jetpack_Core_Json_Api_Endpoints { } // Is the plugin active already? - $status = Jetpack_Plugins::get_plugin_status( $plugin ); + $status = Plugins_Installer::get_plugin_status( $plugin ); if ( in_array( $status, array( 'active', 'network-active' ), true ) ) { return new WP_Error( 'plugin_already_active', @@ -3903,8 +4018,7 @@ class Jetpack_Core_Json_Api_Endpoints { * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error. */ public static function get_plugin( $request ) { - jetpack_require_lib( 'plugins' ); - $plugins = Jetpack_Plugins::get_plugins(); + $plugins = Plugins_Installer::get_plugins(); if ( empty( $plugins ) ) { return new WP_Error( 'no_plugins_found', esc_html__( 'This site has no plugins.', 'jetpack' ), array( 'status' => 404 ) ); @@ -3912,13 +4026,23 @@ class Jetpack_Core_Json_Api_Endpoints { $plugin = stripslashes( $request['plugin'] ); - if ( ! in_array( $plugin, array_keys( $plugins ) ) ) { - return new WP_Error( 'plugin_not_found', esc_html( sprintf( __( 'Plugin %s is not installed.', 'jetpack' ), $plugin ) ), array( 'status' => 404 ) ); + if ( ! array_key_exists( $plugin, $plugins ) ) { + return new WP_Error( + 'plugin_not_found', + esc_html( + sprintf( + /* Translators: placeholder is a plugin name. */ + __( 'Plugin %s is not installed.', 'jetpack' ), + $plugin + ) + ), + array( 'status' => 404 ) + ); } $plugin_data = $plugins[ $plugin ]; - $plugin_data['active'] = in_array( Jetpack_Plugins::get_plugin_status( $plugin ), array( 'active', 'network-active' ), true ); + $plugin_data['active'] = in_array( Plugins_Installer::get_plugin_status( $plugin ), array( 'active', 'network-active' ), true ); return rest_ensure_response( array( @@ -3936,7 +4060,7 @@ class Jetpack_Core_Json_Api_Endpoints { * @param WP_REST_REQUEST $request The request parameters. * @return bool|WP_Error */ - public static function send_mobile_magic_link( $request ) { + public static function send_mobile_magic_link( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $xml = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id(), @@ -3955,8 +4079,6 @@ class Jetpack_Core_Json_Api_Endpoints { ); } - $response = $xml->getResponse(); - return rest_ensure_response( array( 'code' => 'success', @@ -3965,74 +4087,6 @@ class Jetpack_Core_Json_Api_Endpoints { } /** - * Get the last licensing error message, if any. - * - * @since 9.0.0 - * - * @return string Licensing error message or empty string. - */ - public static function get_licensing_error() { - return Licensing::instance()->last_error(); - } - - /** - * Update the last licensing error message. - * - * @since 9.0.0 - * - * @param WP_REST_Request $request The request. - * - * @return bool true. - */ - public static function update_licensing_error( $request ) { - Licensing::instance()->log_error( $request['error'] ); - - return true; - } - - /** - * Set a Jetpack license - * - * @since 9.6.0 - * - * @param WP_REST_Request $request The request. - * - * @return WP_REST_Response|WP_Error A response object if the option was successfully updated, or a WP_Error if it failed. - */ - public static function set_jetpack_license( $request ) { - $license = trim( sanitize_text_field( $request['license'] ) ); - - if ( Licensing::instance()->append_license( $license ) ) { - return rest_ensure_response( array( 'code' => 'success' ) ); - } - - return new WP_Error( - 'setting_license_key_failed', - esc_html__( 'Could not set this license key. Please try again.', 'jetpack' ), - array( 'status' => 500 ) - ); - } - - /** - * Attach Jetpack licenses - * - * @since 10.4.0 - * - * @param WP_REST_Request $request The request. - * - * @return WP_REST_Response|WP_Error A response object - */ - public static function attach_jetpack_licenses( $request ) { - $licenses = array_map( - function ( $license ) { - return trim( sanitize_text_field( $license ) ); - }, - $request['licenses'] - ); - return rest_ensure_response( Licensing::instance()->attach_licenses( $licenses ) ); - } - - /** * Returns the Jetpack CRM data. * * @return WP_REST_Response A response object containing the Jetpack CRM data. @@ -4074,7 +4128,7 @@ class Jetpack_Core_Json_Api_Endpoints { return new WP_Error( 'invalid_user_permission_jetpack_crm_data', - self::$user_permissions_error_msg, + REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) ); } @@ -4091,38 +4145,79 @@ class Jetpack_Core_Json_Api_Endpoints { return new WP_Error( 'invalid_user_permission_activate_jetpack_crm_ext', - self::$user_permissions_error_msg, + REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) ); } /** - * Verify that the user can set a Jetpack license key + * Set hasSeenWCConnectionModal to true when the site has displayed it * - * @since 9.5.0 + * @since 10.4.0 * - * @return bool|WP_Error True if user is able to set a Jetpack license key + * @return bool */ - public static function set_jetpack_license_key_permission_check() { - if ( Licensing::instance()->is_licensing_input_enabled() ) { - return true; - } - - return new WP_Error( 'invalid_user_permission_set_jetpack_license_key', self::$user_permissions_error_msg, array( 'status' => rest_authorization_required_code() ) ); + public static function set_has_seen_wc_connection_modal() { + $updated_option = Jetpack_Options::update_option( 'has_seen_wc_connection_modal', true ); + return rest_ensure_response( array( 'success' => $updated_option ) ); } /** - * Set hasSeenWCConnectionModal to true when the site has displayed it + * Fetch introdution offers. * - * @since 10.4.0 + * @since 10.9 * - * @return bool + * @return array|WP_Error */ - public static function set_has_seen_wc_connection_modal() { - $updated_option = Jetpack_Options::update_option( 'has_seen_wc_connection_modal', true ); + public static function get_intro_offers() { + $site_id = Jetpack_Options::get_option( 'id' ); - return rest_ensure_response( array( 'success' => $updated_option ) ); + if ( ! $site_id ) { + return new WP_Error( + 'site_id_missing', + esc_html__( 'Site ID is missing.', 'jetpack' ), + array( 'status' => 400 ) + ); + } + + $response = Client::wpcom_json_api_request_as_user( + '/introductory-offers', + '2', + array( + 'method' => 'GET', + 'headers' => array( + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), + ), + ) + ); + + $response_code = wp_remote_retrieve_response_code( $response ); + + if ( 200 !== $response_code ) { + return new WP_Error( + 'intro_offers_fetch_failed', + esc_html__( 'Could not retrieve intro offers.', 'jetpack' ), + array( 'status' => $response_code ) + ); + } + + $data = json_decode( wp_remote_retrieve_body( $response ) ); + + if ( ! isset( $data ) ) { + return new WP_Error( + 'intro_offers_error', + esc_html__( 'Could not parse intro offers.', 'jetpack' ), + array( 'status' => 204 ) // no content. + ); + } + + return rest_ensure_response( + array( + 'code' => 'success', + 'data' => $data, + ) + ); } } // class end |