PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | _inc/lib/class.core-rest-api-endpoints.php +688 -462 12.0.3 → 16.3-a.1 View file →
@@ -8,8 +8,10 @@
8 8 use Automattic\Jetpack\Connection\Client;
9 9 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
10 10 use Automattic\Jetpack\Connection\Rest_Authentication;
11 11 use Automattic\Jetpack\Connection\REST_Connector;
12 +use Automattic\Jetpack\Connection\REST_Jetpack_AI_JWT;
13 +use Automattic\Jetpack\Connection\SSO;
12 14 use Automattic\Jetpack\Jetpack_CRM_Data;
13 15 use Automattic\Jetpack\Plugins_Installer;
14 16 use Automattic\Jetpack\Stats\Options as Stats_Options;
15 17 use Automattic\Jetpack\Status\Host;
@@ -16,13 +18,11 @@
16 18 use Automattic\Jetpack\Status\Visitor;
17 19 use Automattic\Jetpack\Waf\Brute_Force_Protection\Brute_Force_Protection_Shared_Functions;
18 20 use Automattic\Jetpack\Waf\Waf_Compatibility;
19 21
20 -/**
21 - * Disable direct access.
22 - */
22 +// Disable direct access.
23 23 if ( ! defined( 'ABSPATH' ) ) {
24 - exit;
24 + exit( 0 );
25 25 }
26 26
27 27 // Load WP_Error for error messages.
28 28 require_once ABSPATH . '/wp-includes/class-wp-error.php';
@@ -32,8 +32,10 @@
32 32 // Load API endpoints that are synced with WP.com
33 33 // Each of these is a class that will register its own routes on 'rest_api_init'.
34 34 require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/load-wpcom-endpoints.php';
35 35
36 +require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
37 +
36 38 /**
37 39 * Class Jetpack_Core_Json_Api_Endpoints
38 40 *
39 41 * @since 4.3.0
@@ -70,8 +72,11 @@
70 72 $module_toggle_endpoint = new Jetpack_Core_API_Module_Toggle_Endpoint( new Jetpack_IXR_Client() );
71 73 $site_endpoint = new Jetpack_Core_API_Site_Endpoint();
72 74 $widget_endpoint = new Jetpack_Core_API_Widget_Endpoint();
73 75
76 + // My Jetpack and Agents Manager register the same controller; its guard keeps the route registered once.
77 + ( new REST_Jetpack_AI_JWT() )->register_rest_route();
78 +
74 79 register_rest_route(
75 80 'jetpack/v4',
76 81 'plans',
77 82 array(
@@ -100,32 +105,10 @@
100 105 'permission_callback' => __CLASS__ . '::disconnect_site_permission_callback',
101 106 )
102 107 );
103 108
104 - // Test current connection status of Jetpack.
105 109 register_rest_route(
106 110 'jetpack/v4',
107 - '/connection/test',
108 - array(
109 - 'methods' => WP_REST_Server::READABLE,
110 - 'callback' => __CLASS__ . '::jetpack_connection_test',
111 - 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',
112 - )
113 - );
114 -
115 - // Endpoint specific for privileged servers to request detailed debug information.
116 - register_rest_route(
117 - 'jetpack/v4',
118 - '/connection/test-wpcom/',
119 - array(
120 - 'methods' => WP_REST_Server::READABLE,
121 - 'callback' => __CLASS__ . '::jetpack_connection_test_for_external',
122 - 'permission_callback' => __CLASS__ . '::view_jetpack_connection_test_check',
123 - )
124 - );
125 -
126 - register_rest_route(
127 - 'jetpack/v4',
128 111 '/rewind',
129 112 array(
130 113 'methods' => WP_REST_Server::READABLE,
131 114 'callback' => __CLASS__ . '::get_rewind_data',
@@ -178,33 +161,11 @@
178 161 ),
179 162 )
180 163 );
181 164
182 - // Disconnect/unlink user from WordPress.com servers.
165 + // Get current site features.
183 166 register_rest_route(
184 167 'jetpack/v4',
185 - '/connection/user',
186 - array(
187 - 'methods' => WP_REST_Server::EDITABLE,
188 - 'callback' => __CLASS__ . '::unlink_user',
189 - 'permission_callback' => __CLASS__ . '::unlink_user_permission_callback',
190 - )
191 - );
192 -
193 - // Get current site data.
194 - register_rest_route(
195 - 'jetpack/v4',
196 - '/site',
197 - array(
198 - 'methods' => WP_REST_Server::READABLE,
199 - 'callback' => __CLASS__ . '::get_site_data',
200 - 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
201 - )
202 - );
203 -
204 - // Get current site data.
205 - register_rest_route(
206 - 'jetpack/v4',
207 168 '/site/features',
208 169 array(
209 170 'methods' => WP_REST_Server::READABLE,
210 171 'callback' => array( $site_endpoint, 'get_features' ),
@@ -749,11 +710,111 @@
749 710 'callback' => __CLASS__ . '::get_intro_offers',
750 711 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',
751 712 )
752 713 );
714 +
715 + // Save subscriber token and redirect
716 + register_rest_route(
717 + 'jetpack/v4',
718 + '/subscribers/auth',
719 + array(
720 + 'methods' => WP_REST_Server::READABLE,
721 + 'callback' => __CLASS__ . '::set_subscriber_cookie_and_redirect',
722 + 'permission_callback' => '__return_true',
723 + 'args' => array(
724 + 'redirect_url' => array(
725 + 'required' => true,
726 + 'description' => __( 'The URL to redirect to.', 'jetpack' ),
727 + 'validate_callback' => 'wp_http_validate_url',
728 + 'sanitize_callback' => 'sanitize_url',
729 + 'type' => 'string',
730 + 'format' => 'uri',
731 + ),
732 + ),
733 + )
734 + );
735 +
736 + /**
737 + * Get the list of available Jetpack features.
738 + *
739 + * @since 13.9
740 + */
741 + register_rest_route(
742 + 'jetpack/v4',
743 + '/features/available',
744 + array(
745 + 'methods' => WP_REST_Server::READABLE,
746 + 'callback' => array( static::class, 'get_features_available' ),
747 + 'permission_callback' => array( static::class, 'get_features_permission_check' ),
748 + )
749 + );
750 +
751 + /**
752 + * Get the list of enabled Jetpack features.
753 + *
754 + * @since 13.9
755 + */
756 + register_rest_route(
757 + 'jetpack/v4',
758 + '/features/enabled',
759 + array(
760 + 'methods' => WP_REST_Server::READABLE,
761 + 'callback' => array( static::class, 'get_features_enabled' ),
762 + 'permission_callback' => array( static::class, 'get_features_permission_check' ),
763 + )
764 + );
753 765 }
754 766
755 767 /**
768 + * Ask WPCOM for a JWT token to use for OpenAI conversations.
769 + *
770 + * @deprecated since 16.2
771 + * @see Automattic\Jetpack\Connection\REST_Jetpack_AI_JWT::get_jwt()
772 + *
773 + * @return array|WP_Error The token and blog ID, or the error from WPCOM.
774 + */
775 + public static function get_openai_jwt() {
776 + _deprecated_function( __METHOD__, 'jetpack-16.2', '\Automattic\Jetpack\Connection\REST_Jetpack_AI_JWT::get_jwt' );
777 +
778 + $response = ( new REST_Jetpack_AI_JWT() )->get_jwt();
779 +
780 + if ( is_wp_error( $response ) ) {
781 + return $response;
782 + }
783 +
784 + // Pre-deprecation callers expect the raw array, not a WP_REST_Response.
785 + return $response->get_data();
786 + }
787 +
788 + /**
789 + * Set subscriber cookie and redirect
790 + *
791 + * @param \WP_Rest_Request $request The URL to redirect to.
792 + *
793 + * @return WP_Error|WP_REST_Response
794 + */
795 + public static function set_subscriber_cookie_and_redirect( $request ) {
796 + require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/subscription-service/include.php';
797 + $subscription_service = \Automattic\Jetpack\Extensions\Premium_Content\subscription_service();
798 + // Note: get_and_set_token_from_request() sets the subscriber cookie as a side effect.
799 + // The cookie is set regardless of the redirect target below; only the redirect is gated.
800 + $token = $subscription_service->get_and_set_token_from_request();
801 + $payload = $subscription_service->decode_token( $token );
802 + $is_valid_token = ! empty( $payload );
803 + if ( ! $is_valid_token ) {
804 + return new WP_Error( 'invalid-token', 'Invalid Token', array( 'status' => 403 ) );
805 + }
806 +
807 + // Only redirect to the current site, not to an arbitrary host.
808 + $redirect_url = wp_validate_redirect( $request['redirect_url'], '' );
809 + if ( ! $redirect_url ) {
810 + return new WP_Error( 'invalid-redirect', 'Invalid Redirect URL', array( 'status' => 400 ) );
811 + }
812 +
813 + return new WP_REST_Response( null, 302, array( 'location' => $redirect_url ) );
814 + }
815 +
816 + /**
756 817 * Get the data for the recommendations
757 818 *
758 819 * @return array Recommendations data
759 820 */
@@ -810,9 +871,9 @@
810 871 }
811 872
812 873 $user_connected = ( new Connection_Manager( 'jetpack' ) )->is_user_connected( get_current_user_id() );
813 874 if ( ! $user_connected ) {
814 - return wp_json_encode( array() );
875 + return wp_json_encode( array(), JSON_UNESCAPED_SLASHES );
815 876 }
816 877
817 878 $request_path = sprintf( '/sites/%s/jetpack-recommendations/product-suggestions?locale=' . get_user_locale(), $blog_id );
818 879 $wpcom_request = Client::wpcom_json_api_request_as_user(
@@ -1053,9 +1114,9 @@
1053 1114 * @since 6.6.0
1054 1115 *
1055 1116 * @param WP_REST_Request $request The request sent to the WP REST API.
1056 1117 *
1057 - * @return array|wp-error
1118 + * @return array|WP_Error
1058 1119 */
1059 1120 public static function is_site_verified_and_token( $request ) {
1060 1121 /**
1061 1122 * Return an error if the site uses a Maintenance / Coming Soon plugin
@@ -1081,8 +1142,9 @@
1081 1142 || Jetpack::is_plugin_active( 'mojo-under-construction/mojo-contruction.php' ) && 1 == $under_construction_activation_status // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1082 1143 || ( Jetpack::is_plugin_active( 'under-construction-page/under-construction.php' ) && isset( $ucp_options['status'] ) && 1 == $ucp_options['status'] ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1083 1144 || ( 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
1084 1145 || ( Jetpack::is_plugin_active( 'coming-soon/coming-soon.php' ) && isset( $csp4['status'] ) && ( 1 == $csp4['status'] || 2 == $csp4['status'] ) ) // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
1146 + ||
1085 1147 /**
1086 1148 * Allow plugins to mark a site as "under construction".
1087 1149 *
1088 1150 * @since 6.7.0
@@ -1088,9 +1150,9 @@
1088 1150 * @since 6.7.0
1089 1151 *
1090 1152 * @param false bool Is the site under construction? Default to false.
1091 1153 */
1092 - || true === apply_filters( 'jetpack_is_under_construction_plugin', false )
1154 + true === apply_filters( 'jetpack_is_under_construction_plugin', false )
1093 1155 ) {
1094 1156 return new WP_Error( 'forbidden', __( 'Site is under construction and cannot be verified', 'jetpack' ) );
1095 1157 }
1096 1158
@@ -1162,9 +1224,9 @@
1162 1224 * @since 4.3.0
1163 1225 *
1164 1226 * @param WP_REST_Request $request The request sent to the WP REST API.
1165 1227 *
1166 - * @return array|wp-error
1228 + * @return array|WP_Error
1167 1229 */
1168 1230 public static function dismiss_notice( $request ) {
1169 1231 $notice = $request['notice'];
1170 1232
@@ -1229,24 +1291,20 @@
1229 1291
1230 1292 /**
1231 1293 * Verify that a user can use the /connection/user endpoint. Has to be a registered user and be currently linked.
1232 1294 *
1295 + * @uses Automattic\Jetpack\Connection\Manager::is_user_connected();)
1296 + *
1297 + * @deprecated since Jetpack 14.4.0
1298 + * @see Automattic\Jetpack\Connection\REST_Connector::unlink_user_permission_callback()
1299 + *
1233 1300 * @since 4.3.0
1234 1301 *
1235 - * @uses Automattic\Jetpack\Connection\Manager::is_user_connected();)
1236 - *
1237 1302 * @return bool|WP_Error True if user is able to unlink.
1238 1303 */
1239 1304 public static function unlink_user_permission_callback() {
1240 - if ( current_user_can( 'jetpack_connect_user' ) && ( new Connection_Manager( 'jetpack' ) )->is_user_connected( get_current_user_id() ) ) {
1241 - return true;
1242 - }
1243 -
1244 - return new WP_Error(
1245 - 'invalid_user_permission_unlink_user',
1246 - REST_Connector::get_user_permissions_error_msg(),
1247 - array( 'status' => rest_authorization_required_code() )
1248 - );
1305 + _deprecated_function( __METHOD__, 'jetpack-14.4.0', 'Automattic\Jetpack\Connection\REST_Connector::unlink_user_permission_callback()' );
1306 + return REST_Connector::unlink_user_permission_callback();
1249 1307 }
1250 1308
1251 1309 /**
1252 1310 * Verify that user can manage Jetpack modules.
@@ -1381,139 +1439,8 @@
1381 1439 );
1382 1440 }
1383 1441
1384 1442 /**
1385 - * Test connection status for this Jetpack site.
1386 - *
1387 - * @since 6.8.0
1388 - *
1389 - * @return array|WP_Error WP_Error returned if connection test does not succeed.
1390 - */
1391 - public static function jetpack_connection_test() {
1392 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
1393 - $cxntests = new Jetpack_Cxn_Tests();
1394 -
1395 - if ( $cxntests->pass() ) {
1396 - return rest_ensure_response(
1397 - array(
1398 - 'code' => 'success',
1399 - 'message' => __( 'All connection tests passed.', 'jetpack' ),
1400 - )
1401 - );
1402 - } else {
1403 - return $cxntests->output_fails_as_wp_error();
1404 - }
1405 - }
1406 -
1407 - /**
1408 - * Test connection permission check method.
1409 - *
1410 - * @since 7.1.0
1411 - *
1412 - * @return bool
1413 - */
1414 - public static function view_jetpack_connection_test_check() {
1415 - // phpcs:disable WordPress.Security.NonceVerification.Recommended -- This is verifying the trusted caller via a shared private key and timestamp.
1416 - if ( ! isset( $_GET['signature'], $_GET['timestamp'], $_GET['url'] ) ) {
1417 - return false;
1418 - }
1419 - $signature = base64_decode( wp_unslash( $_GET['signature'] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1420 -
1421 - $signature_data = wp_json_encode(
1422 - array(
1423 - 'rest_route' => isset( $_GET['rest_route'] ) ? filter_var( wp_unslash( $_GET['rest_route'] ) ) : null,
1424 - 'timestamp' => (int) $_GET['timestamp'],
1425 - 'url' => esc_url_raw( wp_unslash( $_GET['url'] ) ),
1426 - )
1427 - );
1428 -
1429 - if (
1430 - ! function_exists( 'openssl_verify' )
1431 - || 1 !== openssl_verify(
1432 - $signature_data,
1433 - $signature,
1434 - JETPACK__DEBUGGER_PUBLIC_KEY
1435 - )
1436 - ) {
1437 - return false;
1438 - }
1439 -
1440 - // signature timestamp must be within 5min of current time.
1441 - if ( abs( time() - (int) $_GET['timestamp'] ) > 300 ) {
1442 - return false;
1443 - }
1444 -
1445 - // phpcs:enable WordPress.Security.NonceVerification.Recommended
1446 -
1447 - return true;
1448 - }
1449 -
1450 - /**
1451 - * Test connection status for this Jetpack site, encrypt the results for decryption by a third-party.
1452 - *
1453 - * @since 7.1.0
1454 - *
1455 - * @return array|mixed|object|WP_Error
1456 - */
1457 - public static function jetpack_connection_test_for_external() {
1458 - // Since we are running this test for inclusion in the WP.com testing suite, let's not try to run them as part of these results.
1459 - add_filter( 'jetpack_debugger_run_self_test', '__return_false' );
1460 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/debugger.php';
1461 - $cxntests = new Jetpack_Cxn_Tests();
1462 -
1463 - if ( $cxntests->pass() ) {
1464 - $result = array(
1465 - 'code' => 'success',
1466 - 'message' => __( 'All connection tests passed.', 'jetpack' ),
1467 - );
1468 - } else {
1469 - $error = $cxntests->output_fails_as_wp_error(); // Using this so the output is similar both ways.
1470 - $errors = array();
1471 -
1472 - // Borrowed from WP_REST_Server::error_to_response().
1473 - foreach ( (array) $error->errors as $code => $messages ) {
1474 - foreach ( (array) $messages as $message ) {
1475 - $errors[] = array(
1476 - 'code' => $code,
1477 - 'message' => $message,
1478 - 'data' => $error->get_error_data( $code ),
1479 - );
1480 - }
1481 - }
1482 -
1483 - $result = ( ! empty( $errors ) ) ? $errors[0] : null;
1484 - if ( count( $errors ) > 1 ) {
1485 - // Remove the primary error.
1486 - array_shift( $errors );
1487 - $result['additional_errors'] = $errors;
1488 - }
1489 - }
1490 -
1491 - $result = wp_json_encode( $result );
1492 -
1493 - $encrypted = $cxntests->encrypt_string_for_wpcom( $result );
1494 -
1495 - if ( ! $encrypted || ! is_array( $encrypted ) ) {
1496 - return rest_ensure_response(
1497 - array(
1498 - 'code' => 'action_required',
1499 - 'message' => 'Please request results from the in-plugin debugger',
1500 - )
1501 - );
1502 - }
1503 -
1504 - return rest_ensure_response(
1505 - array(
1506 - 'code' => 'response',
1507 - 'debug' => array(
1508 - 'data' => $encrypted['data'],
1509 - 'key' => $encrypted['key'],
1510 - ),
1511 - )
1512 - );
1513 - }
1514 -
1515 - /**
1516 1443 * Fetch information about the Rewind status of the site.
1517 1444 */
1518 1445 public static function rewind_data() {
1519 1446 $site_id = Jetpack_Options::get_option( 'id' );
@@ -1556,9 +1483,9 @@
1556 1483 return rest_ensure_response(
1557 1484 array(
1558 1485 'code' => 'success',
1559 1486 'message' => esc_html__( 'Backup & Scan data correctly received.', 'jetpack' ),
1560 - 'data' => wp_json_encode( $rewind_data ),
1487 + 'data' => wp_json_encode( $rewind_data, JSON_UNESCAPED_SLASHES ),
1561 1488 )
1562 1489 );
1563 1490 }
1564 1491
@@ -1639,9 +1566,9 @@
1639 1566 return rest_ensure_response(
1640 1567 array(
1641 1568 'code' => 'success',
1642 1569 'message' => esc_html__( 'Scan state correctly received.', 'jetpack' ),
1643 - 'data' => wp_json_encode( $scan_state ),
1570 + 'data' => wp_json_encode( $scan_state, JSON_UNESCAPED_SLASHES ),
1644 1571 )
1645 1572 );
1646 1573 }
1647 1574
@@ -1688,41 +1615,8 @@
1688 1615 return new WP_Error( 'disconnect_failed', esc_html__( 'Was not able to disconnect the site. Please try again.', 'jetpack' ), array( 'status' => 400 ) );
1689 1616 }
1690 1617
1691 1618 /**
1692 - * Registers the Jetpack site
1693 - *
1694 - * @deprecated since Jetpack 9.7.0
1695 - * @see Automattic\Jetpack\Connection\REST_Connector::connection_register()
1696 - *
1697 - * @param WP_REST_Request $request The request sent to the WP REST API.
1698 - *
1699 - * @return bool|WP_Error True if Jetpack successfully registered
1700 - */
1701 - public static function register_site( $request ) {
1702 - _deprecated_function( __METHOD__, 'jetpack-9.7.0', '\Automattic\Jetpack\Connection\REST_Connector::connection_register' );
1703 -
1704 - if ( ! wp_verify_nonce( $request->get_param( 'registration_nonce' ), 'jetpack-registration-nonce' ) ) {
1705 - return new WP_Error( 'invalid_nonce', __( 'Unable to verify your request.', 'jetpack' ), array( 'status' => 403 ) );
1706 - }
1707 -
1708 - if ( isset( $request['from'] ) ) {
1709 - Jetpack::connection()->add_register_request_param( 'from', (string) $request['from'] );
1710 - }
1711 - $response = Jetpack::connection()->try_registration();
1712 -
1713 - if ( is_wp_error( $response ) ) {
1714 - return $response;
1715 - }
1716 -
1717 - return rest_ensure_response(
1718 - array(
1719 - 'authorizeUrl' => Jetpack::build_authorize_url( false ),
1720 - )
1721 - );
1722 - }
1723 -
1724 - /**
1725 1619 * Gets a new connect raw URL with fresh nonce.
1726 1620 *
1727 1621 * @uses Jetpack::disconnect();
1728 1622 * @since 4.3.0
@@ -1731,10 +1625,10 @@
1731 1625 *
1732 1626 * @return string|WP_Error A raw URL if the connection URL could be built; error message otherwise.
1733 1627 */
1734 1628 public static function build_connect_url( $request = array() ) {
1735 - $from = isset( $request['from'] ) ? $request['from'] : false;
1736 - $redirect = isset( $request['redirect'] ) ? $request['redirect'] : false;
1629 + $from = $request['from'] ?? false;
1630 + $redirect = $request['redirect'] ?? false;
1737 1631
1738 1632 $url = Jetpack::init()->build_connect_url( true, $redirect, $from );
1739 1633 if ( $url ) {
1740 1634 return rest_ensure_response( $url );
@@ -1772,30 +1666,21 @@
1772 1666
1773 1667 /**
1774 1668 * Unlinks current user from the WordPress.com Servers.
1775 1669 *
1776 - * @since 4.3.0
1670 + * @param WP_REST_Request $request The request sent to the WP REST API.
1777 1671 * @uses Automattic\Jetpack\Connection\Manager->disconnect_user
1778 1672 *
1779 - * @param WP_REST_Request $request The request sent to the WP REST API.
1673 + * @deprecated since Jetpack 14.4.0
1674 + * @see Automattic\Jetpack\Connection\REST_Connector::unlink_user()
1780 1675 *
1676 + * @since 4.3.0
1677 + *
1781 1678 * @return bool|WP_Error True if user successfully unlinked.
1782 1679 */
1783 1680 public static function unlink_user( $request ) {
1784 -
1785 - if ( ! isset( $request['linked'] ) || false !== $request['linked'] ) {
1786 - return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) );
1787 - }
1788 -
1789 - if ( ( new Connection_Manager( 'jetpack' ) )->disconnect_user() ) {
1790 - return rest_ensure_response(
1791 - array(
1792 - 'code' => 'success',
1793 - )
1794 - );
1795 - }
1796 -
1797 - return new WP_Error( 'unlink_user_failed', esc_html__( 'Was not able to unlink the user. Please try again.', 'jetpack' ), array( 'status' => 400 ) );
1681 + _deprecated_function( __METHOD__, 'jetpack-14.4.0', 'Automattic\Jetpack\Connection\REST_Connector::unlink_user()' );
1682 + return REST_Connector::unlink_user( $request );
1798 1683 }
1799 1684
1800 1685 /**
1801 1686 * Gets current user's tracking settings.
@@ -1854,9 +1739,9 @@
1854 1739 'Content-Type' => 'application/json',
1855 1740 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ),
1856 1741 ),
1857 1742 ),
1858 - wp_json_encode( $request->get_params() )
1743 + wp_json_encode( $request->get_params(), JSON_UNESCAPED_SLASHES )
1859 1744 );
1860 1745 if ( ! is_wp_error( $response ) ) {
1861 1746 $response = json_decode( wp_remote_retrieve_body( $response ), true );
1862 1747 }
@@ -1868,93 +1753,30 @@
1868 1753 /**
1869 1754 * Fetch site data from .com including the site's current plan and the site's products.
1870 1755 *
1871 1756 * @since 5.5.0
1757 + * @deprecated 16.2 Use Automattic\Jetpack\Connection\Manager::get_connected_site_data().
1872 1758 *
1873 1759 * @return stdClass|WP_Error
1874 1760 */
1875 1761 public static function site_data() {
1876 - $site_id = Jetpack_Options::get_option( 'id' );
1762 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\Jetpack\Connection\Manager::get_connected_site_data' );
1877 1763
1878 - if ( ! $site_id ) {
1879 - return new WP_Error( 'site_id_missing', '', array( 'api_error_code' => __( 'site_id_missing', 'jetpack' ) ) );
1880 - }
1764 + return ( new Connection_Manager() )->get_connected_site_data();
1765 + }
1881 1766
1882 - $args = array( 'headers' => array() );
1883 -
1884 - // Allow use a store sandbox. Internal ref: PCYsg-IA-p2.
1885 - if ( isset( $_COOKIE ) && isset( $_COOKIE['store_sandbox'] ) ) {
1886 - $secret = filter_var( wp_unslash( $_COOKIE['store_sandbox'] ) );
1887 - $args['headers']['Cookie'] = "store_sandbox=$secret;";
1888 - }
1889 -
1890 - $response = Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d', $site_id ) . '?force=wpcom', '1.1', $args );
1891 - $body = wp_remote_retrieve_body( $response );
1892 - $data = $body ? json_decode( $body ) : null;
1893 -
1894 - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1895 - $error_info = array(
1896 - 'api_error_code' => null,
1897 - 'api_http_code' => wp_remote_retrieve_response_code( $response ),
1898 - );
1899 -
1900 - if ( is_wp_error( $response ) ) {
1901 - $error_info['api_error_code'] = $response->get_error_code() ? wp_strip_all_tags( $response->get_error_code() ) : null;
1902 - } elseif ( $data && ! empty( $data->error ) ) {
1903 - $error_info['api_error_code'] = $data->error;
1904 - }
1905 -
1906 - return new WP_Error( 'site_data_fetch_failed', '', $error_info );
1907 - }
1908 -
1909 - Jetpack_Plan::update_from_sites_response( $response );
1910 -
1911 - return $data;
1912 - }
1913 1767 /**
1914 1768 * Get site data, including for example, the site's current plan.
1915 1769 *
1770 + * @since 4.3.0
1771 + * @deprecated 16.2 Use Automattic\Jetpack\Connection\REST_Connector::site_data_response().
1772 + *
1916 1773 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
1917 - * @since 4.3.0
1918 1774 */
1919 1775 public static function get_site_data() {
1920 - $site_data = self::site_data();
1776 + _deprecated_function( __METHOD__, 'jetpack-16.2', 'Automattic\Jetpack\Connection\REST_Connector::site_data_response' );
1921 1777
1922 - if ( ! is_wp_error( $site_data ) ) {
1923 -
1924 - /**
1925 - * Fires when the site data was successfully returned from the /sites/%d wpcom endpoint.
1926 - *
1927 - * @since 8.7.0
1928 - */
1929 - do_action( 'jetpack_get_site_data_success' );
1930 - return rest_ensure_response(
1931 - array(
1932 - 'code' => 'success',
1933 - 'message' => esc_html__( 'Site data correctly received.', 'jetpack' ),
1934 - 'data' => wp_json_encode( $site_data ),
1935 - )
1936 - );
1937 - }
1938 -
1939 - $error_data = $site_data->get_error_data();
1940 -
1941 - if ( empty( $error_data['api_error_code'] ) ) {
1942 - $error_message = esc_html__( 'Failed fetching site data from WordPress.com. If the problem persists, try reconnecting Jetpack.', 'jetpack' );
1943 - } else {
1944 - /* translators: %s is an error code (e.g. `token_mismatch`) */
1945 - $error_message = sprintf( esc_html__( 'Failed fetching site data from WordPress.com (%s). If the problem persists, try reconnecting Jetpack.', 'jetpack' ), $error_data['api_error_code'] );
1946 - }
1947 -
1948 - return new WP_Error(
1949 - $site_data->get_error_code(),
1950 - $error_message,
1951 - array(
1952 - 'status' => 400,
1953 - 'api_error_code' => empty( $error_data['api_error_code'] ) ? null : $error_data['api_error_code'],
1954 - 'api_http_code' => empty( $error_data['api_http_code'] ) ? null : $error_data['api_http_code'],
1955 - )
1956 - );
1778 + return REST_Connector::site_data_response();
1957 1779 }
1958 1780
1959 1781 /**
1960 1782 * Fetch AL data for this site and return it.
@@ -2169,9 +1991,9 @@
2169 1991 public static function get_updateable_data_list( $selector = '' ) {
2170 1992
2171 1993 $options = array(
2172 1994 // Blocks.
2173 - 'jetpack_blocks_disabled' => array(
1995 + 'jetpack_blocks_disabled' => array(
2174 1996 'description' => esc_html__( 'Jetpack Blocks disabled.', 'jetpack' ),
2175 1997 'type' => 'boolean',
2176 1998 'default' => false,
2177 1999 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2178,9 +2000,9 @@
2178 2000 'jp_group' => 'settings',
2179 2001 ),
2180 2002
2181 2003 // Carousel
2182 - 'carousel_background_color' => array(
2004 + 'carousel_background_color' => array(
2183 2005 'description' => esc_html__( 'Color scheme.', 'jetpack' ),
2184 2006 'type' => 'string',
2185 2007 'default' => 'black',
2186 2008 'enum' => array(
@@ -2193,9 +2015,9 @@
2193 2015 ),
2194 2016 'validate_callback' => __CLASS__ . '::validate_list_item',
2195 2017 'jp_group' => 'carousel',
2196 2018 ),
2197 - 'carousel_display_exif' => array(
2019 + 'carousel_display_exif' => array(
2198 2020 'description' => wp_kses(
2199 2021 sprintf( __( 'Show photo metadata (<a href="https://en.wikipedia.org/wiki/Exchangeable_image_file_format" target="_blank">Exif</a>) in carousel, when available.', 'jetpack' ) ),
2200 2022 array(
2201 2023 'a' => array(
@@ -2208,9 +2030,9 @@
2208 2030 'default' => 0,
2209 2031 'validate_callback' => __CLASS__ . '::validate_boolean',
2210 2032 'jp_group' => 'carousel',
2211 2033 ),
2212 - 'carousel_display_comments' => array(
2034 + 'carousel_display_comments' => array(
2213 2035 'description' => esc_html__( 'Show comments area in carousel', 'jetpack' ),
2214 2036 'type' => 'boolean',
2215 2037 'default' => 1,
2216 2038 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2217,9 +2039,9 @@
2217 2039 'jp_group' => 'carousel',
2218 2040 ),
2219 2041
2220 2042 // Comments.
2221 - 'highlander_comment_form_prompt' => array(
2043 + 'highlander_comment_form_prompt' => array(
2222 2044 'description' => esc_html__( 'Greeting Text', 'jetpack' ),
2223 2045 'type' => 'string',
2224 2046 'default' => esc_html__( 'Leave a Reply', 'jetpack' ),
2225 2047 'sanitize_callback' => 'sanitize_text_field',
@@ -2224,9 +2046,9 @@
2224 2046 'default' => esc_html__( 'Leave a Reply', 'jetpack' ),
2225 2047 'sanitize_callback' => 'sanitize_text_field',
2226 2048 'jp_group' => 'comments',
2227 2049 ),
2228 - 'jetpack_comment_form_color_scheme' => array(
2050 + 'jetpack_comment_form_color_scheme' => array(
2229 2051 'description' => esc_html__( 'Color scheme', 'jetpack' ),
2230 2052 'type' => 'string',
2231 2053 'default' => 'light',
2232 2054 'enum' => array(
@@ -2243,39 +2065,38 @@
2243 2065 'jp_group' => 'comments',
2244 2066 ),
2245 2067
2246 2068 // Custom Content Types.
2247 - 'jetpack_portfolio' => array(
2069 + 'jetpack_portfolio' => array(
2248 2070 'description' => esc_html__( 'Enable or disable Jetpack portfolio post type.', 'jetpack' ),
2249 2071 'type' => 'boolean',
2250 2072 'default' => 0,
2251 2073 'validate_callback' => __CLASS__ . '::validate_boolean',
2252 - 'jp_group' => 'custom-content-types',
2074 + 'jp_group' => 'settings',
2253 2075 ),
2254 - 'jetpack_portfolio_posts_per_page' => array(
2076 + 'jetpack_portfolio_posts_per_page' => array(
2255 2077 'description' => esc_html__( 'Number of entries to show at most in Portfolio pages.', 'jetpack' ),
2256 2078 'type' => 'integer',
2257 2079 'default' => 10,
2258 2080 'validate_callback' => __CLASS__ . '::validate_posint',
2259 - 'jp_group' => 'custom-content-types',
2081 + 'jp_group' => 'settings',
2260 2082 ),
2261 - 'jetpack_testimonial' => array(
2083 + 'jetpack_testimonial' => array(
2262 2084 'description' => esc_html__( 'Enable or disable Jetpack testimonial post type.', 'jetpack' ),
2263 2085 'type' => 'boolean',
2264 2086 'default' => 0,
2265 2087 'validate_callback' => __CLASS__ . '::validate_boolean',
2266 - 'jp_group' => 'custom-content-types',
2088 + 'jp_group' => 'settings',
2267 2089 ),
2268 - 'jetpack_testimonial_posts_per_page' => array(
2090 + 'jetpack_testimonial_posts_per_page' => array(
2269 2091 'description' => esc_html__( 'Number of entries to show at most in Testimonial pages.', 'jetpack' ),
2270 2092 'type' => 'integer',
2271 2093 'default' => 10,
2272 2094 'validate_callback' => __CLASS__ . '::validate_posint',
2273 - 'jp_group' => 'custom-content-types',
2095 + 'jp_group' => 'settings',
2274 2096 ),
2275 -
2276 2097 // WAF.
2277 - 'jetpack_waf_automatic_rules' => array(
2098 + 'jetpack_waf_automatic_rules' => array(
2278 2099 'description' => esc_html__( 'Enable automatic rules - Protect your site against untrusted traffic sources with automatic security rules.', 'jetpack' ),
2279 2100 'type' => 'boolean',
2280 2101 'default' => Waf_Compatibility::get_default_automatic_rules_option(),
2281 2102 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2280,16 +2101,16 @@
2280 2101 'default' => Waf_Compatibility::get_default_automatic_rules_option(),
2281 2102 'validate_callback' => __CLASS__ . '::validate_boolean',
2282 2103 'jp_group' => 'waf',
2283 2104 ),
2284 - 'jetpack_waf_ip_list' => array(
2285 - 'description' => esc_html__( 'Allow / Block list - Block or allow a specific request IP.', 'jetpack' ),
2105 + 'jetpack_waf_ip_block_list_enabled' => array(
2106 + 'description' => esc_html__( 'Block list - Block a specific request IP.', 'jetpack' ),
2286 2107 'type' => 'boolean',
2287 2108 'default' => 0,
2288 2109 'validate_callback' => __CLASS__ . '::validate_boolean',
2289 2110 'jp_group' => 'waf',
2290 2111 ),
2291 - 'jetpack_waf_ip_block_list' => array(
2112 + 'jetpack_waf_ip_block_list' => array(
2292 2113 'description' => esc_html__( 'Blocked IP addresses', 'jetpack' ),
2293 2114 'type' => 'string',
2294 2115 'default' => '',
2295 2116 'validate_callback' => __CLASS__ . '::validate_string',
@@ -2295,18 +2116,32 @@
2295 2116 'validate_callback' => __CLASS__ . '::validate_string',
2296 2117 'sanitize_callback' => 'esc_textarea',
2297 2118 'jp_group' => 'waf',
2298 2119 ),
2299 - 'jetpack_waf_ip_allow_list' => array(
2120 + 'jetpack_waf_ip_allow_list_enabled' => array(
2121 + 'description' => esc_html__( 'Allow list - Allow a specific request IP.', 'jetpack' ),
2122 + 'type' => 'boolean',
2123 + 'default' => 0,
2124 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2125 + 'jp_group' => 'settings',
2126 + ),
2127 + 'jetpack_waf_ip_allow_list' => array(
2300 2128 'description' => esc_html__( 'Always allowed IP addresses', 'jetpack' ),
2301 2129 'type' => 'string',
2302 2130 'default' => '',
2303 2131 'validate_callback' => __CLASS__ . '::validate_string',
2304 2132 'sanitize_callback' => 'esc_textarea',
2133 + 'jp_group' => 'settings',
2134 + ),
2135 + 'jetpack_waf_share_data' => array(
2136 + 'description' => esc_html__( 'Share basic data with Jetpack.', 'jetpack' ),
2137 + 'type' => 'boolean',
2138 + 'default' => 0,
2139 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2305 2140 'jp_group' => 'waf',
2306 2141 ),
2307 - 'jetpack_waf_share_data' => array(
2308 - 'description' => esc_html__( 'Share data with Jetpack.', 'jetpack' ),
2142 + 'jetpack_waf_share_debug_data' => array(
2143 + 'description' => esc_html__( 'Share detailed data with Jetpack.', 'jetpack' ),
2309 2144 'type' => 'boolean',
2310 2145 'default' => 0,
2311 2146 'validate_callback' => __CLASS__ . '::validate_boolean',
2312 2147 'jp_group' => 'waf',
@@ -2311,9 +2146,9 @@
2311 2146 'validate_callback' => __CLASS__ . '::validate_boolean',
2312 2147 'jp_group' => 'waf',
2313 2148 ),
2314 2149 // Galleries.
2315 - 'tiled_galleries' => array(
2150 + 'tiled_galleries' => array(
2316 2151 'description' => esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ),
2317 2152 'type' => 'boolean',
2318 2153 'default' => 0,
2319 2154 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2319,9 +2154,9 @@
2319 2154 'validate_callback' => __CLASS__ . '::validate_boolean',
2320 2155 'jp_group' => 'tiled-gallery',
2321 2156 ),
2322 2157
2323 - 'gravatar_disable_hovercards' => array(
2158 + 'gravatar_disable_hovercards' => array(
2324 2159 'description' => esc_html__( "View people's profiles when you mouse over their Gravatars", 'jetpack' ),
2325 2160 'type' => 'string',
2326 2161 'default' => 'enabled',
2327 2162 // Not visible. This is used as the checkbox value.
@@ -2337,9 +2172,9 @@
2337 2172 'jp_group' => 'gravatar-hovercards',
2338 2173 ),
2339 2174
2340 2175 // Infinite Scroll.
2341 - 'infinite_scroll' => array(
2176 + 'infinite_scroll' => array(
2342 2177 'description' => esc_html__( 'To infinity and beyond', 'jetpack' ),
2343 2178 'type' => 'boolean',
2344 2179 'default' => 1,
2345 2180 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2344,9 +2179,9 @@
2344 2179 'default' => 1,
2345 2180 'validate_callback' => __CLASS__ . '::validate_boolean',
2346 2181 'jp_group' => 'infinite-scroll',
2347 2182 ),
2348 - 'infinite_scroll_google_analytics' => array(
2183 + 'infinite_scroll_google_analytics' => array(
2349 2184 'description' => esc_html__( 'Use Google Analytics with Infinite Scroll', 'jetpack' ),
2350 2185 'type' => 'boolean',
2351 2186 'default' => 0,
2352 2187 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2353,9 +2188,9 @@
2353 2188 'jp_group' => 'infinite-scroll',
2354 2189 ),
2355 2190
2356 2191 // Likes.
2357 - 'wpl_default' => array(
2192 + 'wpl_default' => array(
2358 2193 'description' => esc_html__( 'WordPress.com Likes are', 'jetpack' ),
2359 2194 'type' => 'string',
2360 2195 'default' => 'on',
2361 2196 'enum' => array(
@@ -2368,9 +2203,9 @@
2368 2203 ),
2369 2204 'validate_callback' => __CLASS__ . '::validate_list_item',
2370 2205 'jp_group' => 'likes',
2371 2206 ),
2372 - 'social_notifications_like' => array(
2207 + 'social_notifications_like' => array(
2373 2208 'description' => esc_html__( 'Send email notification when someone likes a post', 'jetpack' ),
2374 2209 'type' => 'boolean',
2375 2210 'default' => 1,
2376 2211 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2377,9 +2212,9 @@
2377 2212 'jp_group' => 'likes',
2378 2213 ),
2379 2214
2380 2215 // Markdown.
2381 - 'wpcom_publish_comments_with_markdown' => array(
2216 + 'wpcom_publish_comments_with_markdown' => array(
2382 2217 'description' => esc_html__( 'Use Markdown for comments.', 'jetpack' ),
2383 2218 'type' => 'boolean',
2384 2219 'default' => 0,
2385 2220 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2384,9 +2219,9 @@
2384 2219 'default' => 0,
2385 2220 'validate_callback' => __CLASS__ . '::validate_boolean',
2386 2221 'jp_group' => 'markdown',
2387 2222 ),
2388 - 'wpcom_publish_posts_with_markdown' => array(
2223 + 'wpcom_publish_posts_with_markdown' => array(
2389 2224 'description' => esc_html__( 'Use Markdown for posts.', 'jetpack' ),
2390 2225 'type' => 'boolean',
2391 2226 'default' => 0,
2392 2227 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2393,9 +2228,9 @@
2393 2228 'jp_group' => 'markdown',
2394 2229 ),
2395 2230
2396 2231 // Monitor.
2397 - 'monitor_receive_notifications' => array(
2232 + 'monitor_receive_notifications' => array(
2398 2233 'description' => esc_html__( 'Receive Monitor Email Notifications.', 'jetpack' ),
2399 2234 'type' => 'boolean',
2400 2235 'default' => 0,
2401 2236 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2402,9 +2237,9 @@
2402 2237 'jp_group' => 'monitor',
2403 2238 ),
2404 2239
2405 2240 // Post by Email.
2406 - 'post_by_email_address' => array(
2241 + 'post_by_email_address' => array(
2407 2242 'description' => esc_html__( 'Email Address', 'jetpack' ),
2408 2243 'type' => 'string',
2409 2244 'default' => 'noop',
2410 2245 'enum' => array(
@@ -2423,9 +2258,9 @@
2423 2258 'jp_group' => 'post-by-email',
2424 2259 ),
2425 2260
2426 2261 // Protect.
2427 - 'jetpack_protect_key' => array(
2262 + 'jetpack_protect_key' => array(
2428 2263 'description' => esc_html__( 'Protect API key', 'jetpack' ),
2429 2264 'type' => 'string',
2430 2265 'default' => '',
2431 2266 'validate_callback' => __CLASS__ . '::validate_alphanum',
@@ -2430,9 +2265,9 @@
2430 2265 'default' => '',
2431 2266 'validate_callback' => __CLASS__ . '::validate_alphanum',
2432 2267 'jp_group' => 'protect',
2433 2268 ),
2434 - 'jetpack_protect_global_whitelist' => array(
2269 + 'jetpack_protect_global_whitelist' => array(
2435 2270 'description' => esc_html__( 'Protect global IP allow list', 'jetpack' ),
2436 2271 'type' => 'string',
2437 2272 'default' => '',
2438 2273 'validate_callback' => __CLASS__ . '::validate_string',
@@ -2440,19 +2275,19 @@
2440 2275 'jp_group' => 'protect',
2441 2276 ),
2442 2277
2443 2278 // Sharing.
2444 - 'sharing_services' => array(
2279 + 'sharing_services' => array(
2445 2280 'description' => esc_html__( 'Enabled Services and those hidden behind a button', 'jetpack' ),
2446 2281 'type' => 'object',
2447 2282 'default' => array(
2448 - 'visible' => array( 'twitter', 'facebook' ),
2283 + 'visible' => array( 'facebook', 'x' ),
2449 2284 'hidden' => array(),
2450 2285 ),
2451 2286 'validate_callback' => __CLASS__ . '::validate_services',
2452 2287 'jp_group' => 'sharedaddy',
2453 2288 ),
2454 - 'button_style' => array(
2289 + 'button_style' => array(
2455 2290 'description' => esc_html__( 'Button Style', 'jetpack' ),
2456 2291 'type' => 'string',
2457 2292 'default' => 'icon',
2458 2293 'enum' => array(
@@ -2469,9 +2304,9 @@
2469 2304 ),
2470 2305 'validate_callback' => __CLASS__ . '::validate_list_item',
2471 2306 'jp_group' => 'sharedaddy',
2472 2307 ),
2473 - 'sharing_label' => array(
2308 + 'sharing_label' => array(
2474 2309 'description' => esc_html__( 'Sharing Label', 'jetpack' ),
2475 2310 'type' => 'string',
2476 2311 'default' => '',
2477 2312 'validate_callback' => __CLASS__ . '::validate_string',
@@ -2477,9 +2312,9 @@
2477 2312 'validate_callback' => __CLASS__ . '::validate_string',
2478 2313 'sanitize_callback' => 'esc_html',
2479 2314 'jp_group' => 'sharedaddy',
2480 2315 ),
2481 - 'show' => array(
2316 + 'show' => array(
2482 2317 'description' => esc_html__( 'Views where buttons are shown', 'jetpack' ),
2483 2318 'type' => 'array',
2484 2319 'items' => array(
2485 2320 'type' => 'string',
@@ -2487,9 +2322,9 @@
2487 2322 'default' => array( 'post' ),
2488 2323 'validate_callback' => __CLASS__ . '::validate_sharing_show',
2489 2324 'jp_group' => 'sharedaddy',
2490 2325 ),
2491 - 'jetpack-twitter-cards-site-tag' => array(
2326 + 'jetpack-twitter-cards-site-tag' => array(
2492 2327 'description' => esc_html__( "The Twitter username of the owner of this site's domain.", 'jetpack' ),
2493 2328 'type' => 'string',
2494 2329 'default' => '',
2495 2330 'validate_callback' => __CLASS__ . '::validate_twitter_username',
@@ -2495,9 +2330,9 @@
2495 2330 'validate_callback' => __CLASS__ . '::validate_twitter_username',
2496 2331 'sanitize_callback' => 'esc_html',
2497 2332 'jp_group' => 'sharedaddy',
2498 2333 ),
2499 - 'sharedaddy_disable_resources' => array(
2334 + 'sharedaddy_disable_resources' => array(
2500 2335 'description' => esc_html__( 'Disable CSS and JS', 'jetpack' ),
2501 2336 'type' => 'boolean',
2502 2337 'default' => 0,
2503 2338 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2502,9 +2337,9 @@
2502 2337 'default' => 0,
2503 2338 'validate_callback' => __CLASS__ . '::validate_boolean',
2504 2339 'jp_group' => 'sharedaddy',
2505 2340 ),
2506 - 'custom' => array(
2341 + 'custom' => array(
2507 2342 'description' => esc_html__( 'Custom sharing services added by user.', 'jetpack' ),
2508 2343 'type' => 'object',
2509 2344 'default' => array(
2510 2345 'sharing_name' => '',
@@ -2513,10 +2348,10 @@
2513 2348 ),
2514 2349 'validate_callback' => __CLASS__ . '::validate_custom_service',
2515 2350 'jp_group' => 'sharedaddy',
2516 2351 ),
2517 - // Not an option, but an action that can be perfomed on the list of custom services passing the service ID.
2518 - 'sharing_delete_service' => array(
2352 + // Not an option, but an action that can be performed on the list of custom services passing the service ID.
2353 + 'sharing_delete_service' => array(
2519 2354 'description' => esc_html__( 'Delete custom sharing service.', 'jetpack' ),
2520 2355 'type' => 'string',
2521 2356 'default' => '',
2522 2357 'validate_callback' => __CLASS__ . '::validate_custom_service_id',
@@ -2523,25 +2358,25 @@
2523 2358 'jp_group' => 'sharedaddy',
2524 2359 ),
2525 2360
2526 2361 // SSO.
2527 - 'jetpack_sso_require_two_step' => array(
2362 + 'jetpack_sso_require_two_step' => array(
2528 2363 'description' => esc_html__( 'Require Two-Step Authentication', 'jetpack' ),
2529 2364 'type' => 'boolean',
2530 - 'default' => 0,
2365 + 'default' => SSO\Helpers::is_require_two_step_checkbox_disabled(),
2531 2366 'validate_callback' => __CLASS__ . '::validate_boolean',
2532 2367 'jp_group' => 'sso',
2533 2368 ),
2534 - 'jetpack_sso_match_by_email' => array(
2369 + 'jetpack_sso_match_by_email' => array(
2535 2370 'description' => esc_html__( 'Match by Email', 'jetpack' ),
2536 2371 'type' => 'boolean',
2537 - 'default' => 0,
2372 + 'default' => 1,
2538 2373 'validate_callback' => __CLASS__ . '::validate_boolean',
2539 2374 'jp_group' => 'sso',
2540 2375 ),
2541 2376
2542 2377 // Subscriptions.
2543 - 'stb_enabled' => array(
2378 + 'stb_enabled' => array(
2544 2379 'description' => esc_html__( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ),
2545 2380 'type' => 'boolean',
2546 2381 'default' => 1,
2547 2382 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2546,9 +2381,9 @@
2546 2381 'default' => 1,
2547 2382 'validate_callback' => __CLASS__ . '::validate_boolean',
2548 2383 'jp_group' => 'subscriptions',
2549 2384 ),
2550 - 'stc_enabled' => array(
2385 + 'stc_enabled' => array(
2551 2386 'description' => esc_html__( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ),
2552 2387 'type' => 'boolean',
2553 2388 'default' => 1,
2554 2389 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2553,18 +2388,144 @@
2553 2388 'default' => 1,
2554 2389 'validate_callback' => __CLASS__ . '::validate_boolean',
2555 2390 'jp_group' => 'subscriptions',
2556 2391 ),
2557 - 'social_notifications_subscribe' => array(
2558 - 'description' => esc_html__( 'Send email notification when someone follows my blog', 'jetpack' ),
2392 + 'wpcom_newsletter_categories' => array(
2393 + 'description' => esc_html__( 'Array of post category ids that are marked as newsletter categories', 'jetpack' ),
2394 + 'type' => 'array',
2395 + 'default' => array(),
2396 + 'validate_callback' => __CLASS__ . '::validate_array',
2397 + 'jp_group' => 'subscriptions',
2398 + ),
2399 + 'wpcom_newsletter_categories_enabled' => array(
2400 + 'description' => esc_html__( 'Whether the newsletter categories are enabled or not', 'jetpack' ),
2559 2401 'type' => 'boolean',
2560 2402 'default' => 0,
2561 2403 'validate_callback' => __CLASS__ . '::validate_boolean',
2562 2404 'jp_group' => 'subscriptions',
2563 2405 ),
2406 + 'wpcom_newsletter_send_default' => array(
2407 + 'description' => esc_html__( 'Whether to send newsletter emails by default when publishing a post', 'jetpack' ),
2408 + 'type' => 'boolean',
2409 + 'default' => 1,
2410 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2411 + 'jp_group' => 'subscriptions',
2412 + ),
2413 + 'wpcom_featured_image_in_email' => array(
2414 + 'description' => esc_html__( 'Whether to include the featured image in the email or not', 'jetpack' ),
2415 + 'type' => 'boolean',
2416 + 'default' => 0,
2417 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2418 + 'jp_group' => 'subscriptions',
2419 + ),
2420 + 'jetpack_gravatar_in_email' => array(
2421 + 'description' => esc_html__( 'Whether to show author avatar in the email byline', 'jetpack' ),
2422 + 'type' => 'boolean',
2423 + 'default' => 1,
2424 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2425 + 'jp_group' => 'subscriptions',
2426 + ),
2427 + 'jetpack_author_in_email' => array(
2428 + 'description' => esc_html__( 'Whether to show author display name in the email byline', 'jetpack' ),
2429 + 'type' => 'boolean',
2430 + 'default' => 1,
2431 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2432 + 'jp_group' => 'subscriptions',
2433 + ),
2434 + 'jetpack_post_date_in_email' => array(
2435 + 'description' => esc_html__( 'Whether to show date in the email byline', 'jetpack' ),
2436 + 'type' => 'boolean',
2437 + 'default' => 1,
2438 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2439 + 'jp_group' => 'subscriptions',
2440 + ),
2441 + 'wpcom_subscription_emails_use_excerpt' => array(
2442 + 'description' => esc_html__( 'Whether to use the excerpt in the email or not', 'jetpack' ),
2443 + 'type' => 'boolean',
2444 + 'default' => 0,
2445 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2446 + 'jp_group' => 'subscriptions',
2447 + ),
2448 + 'jetpack_subscriptions_reply_to' => array(
2449 + 'description' => esc_html__( 'Reply to email behaviour for newsletters emails', 'jetpack' ),
2450 + 'type' => 'string',
2451 + 'default' => Automattic\Jetpack\Modules\Subscriptions\Settings::$default_reply_to,
2452 + 'validate_callback' => __CLASS__ . '::validate_subscriptions_reply_to',
2453 + 'jp_group' => 'subscriptions',
2454 + ),
2455 + 'jetpack_subscriptions_from_name' => array(
2456 + 'description' => esc_html__( 'From name for newsletters emails', 'jetpack' ),
2457 + 'type' => 'string',
2458 + 'default' => '',
2459 + 'validate_callback' => __CLASS__ . '::validate_subscriptions_reply_to_name',
2460 + 'jp_group' => 'subscriptions',
2461 + ),
2462 + 'sm_enabled' => array(
2463 + 'description' => esc_html__( 'Show popup Subscribe modal to readers.', 'jetpack' ),
2464 + 'type' => 'boolean',
2465 + 'default' => 0,
2466 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2467 + 'jp_group' => 'subscriptions',
2468 + ),
2469 + 'jetpack_subscribe_overlay_enabled' => array(
2470 + 'description' => esc_html__( 'Show subscribe overlay on homepage.', 'jetpack' ),
2471 + 'type' => 'boolean',
2472 + 'default' => 0,
2473 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2474 + 'jp_group' => 'subscriptions',
2475 + ),
2476 + 'jetpack_subscribe_floating_button_enabled' => array(
2477 + 'description' => esc_html__( 'Show a floating subscribe button.', 'jetpack' ),
2478 + 'type' => 'boolean',
2479 + 'default' => 0,
2480 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2481 + 'jp_group' => 'subscriptions',
2482 + ),
2483 + 'jetpack_subscriptions_subscribe_post_end_enabled' => array(
2484 + 'description' => esc_html__( 'Add Subscribe block at the end of each post.', 'jetpack' ),
2485 + 'type' => 'boolean',
2486 + 'default' => 0,
2487 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2488 + 'jp_group' => 'subscriptions',
2489 + ),
2490 + 'jetpack_subscriptions_login_navigation_enabled' => array(
2491 + 'description' => esc_html__( 'Add Subscriber Login block to the navigation.', 'jetpack' ),
2492 + 'type' => 'boolean',
2493 + 'default' => 0,
2494 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2495 + 'jp_group' => 'subscriptions',
2496 + ),
2497 + 'jetpack_subscriptions_subscribe_navigation_enabled' => array(
2498 + 'description' => esc_html__( 'Add Subscribe block to the navigation.', 'jetpack' ),
2499 + 'type' => 'boolean',
2500 + 'default' => 0,
2501 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2502 + 'jp_group' => 'subscriptions',
2503 + ),
2504 + 'social_notifications_subscribe' => array(
2505 + 'description' => esc_html__( 'Send email notification when someone subscribes to my blog', 'jetpack' ),
2506 + 'type' => 'boolean',
2507 + 'default' => 0,
2508 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2509 + 'jp_group' => 'subscriptions',
2510 + ),
2511 + 'subscription_options' => array(
2512 + 'description' => esc_html__( 'Options used in subscription email templates and the Subscribe block: \'invitation\', \'welcome\', \'comment_follow\', \'subscribe_modal_heading\', \'free_tier_description\' and \'hide_free_tier\'.', 'jetpack' ),
2513 + 'type' => 'object',
2514 + 'default' => array(
2515 + 'invitation' => '',
2516 + 'welcome' => '',
2517 + 'comment_follow' => '',
2518 + 'subscribe_modal_heading' => '',
2519 + 'free_tier_description' => '',
2520 + 'hide_free_tier' => false,
2521 + ),
2522 + 'validate_callback' => __CLASS__ . '::validate_subscription_options',
2523 + 'jp_group' => 'subscriptions',
2524 + ),
2564 2525
2565 2526 // Related Posts.
2566 - 'show_headline' => array(
2527 + 'show_headline' => array(
2567 2528 'description' => esc_html__( 'Highlight related content with a heading', 'jetpack' ),
2568 2529 'type' => 'boolean',
2569 2530 'default' => 1,
2570 2531 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2569,9 +2530,9 @@
2569 2530 'default' => 1,
2570 2531 'validate_callback' => __CLASS__ . '::validate_boolean',
2571 2532 'jp_group' => 'related-posts',
2572 2533 ),
2573 - 'show_thumbnails' => array(
2534 + 'show_thumbnails' => array(
2574 2535 'description' => esc_html__( 'Show a thumbnail image where available', 'jetpack' ),
2575 2536 'type' => 'boolean',
2576 2537 'default' => 0,
2577 2538 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2578,9 +2539,9 @@
2578 2539 'jp_group' => 'related-posts',
2579 2540 ),
2580 2541
2581 2542 // Search.
2582 - 'instant_search_enabled' => array(
2543 + 'instant_search_enabled' => array(
2583 2544 'description' => esc_html__( 'Enable Instant Search', 'jetpack' ),
2584 2545 'type' => 'boolean',
2585 2546 'default' => 0,
2586 2547 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2586,9 +2547,9 @@
2586 2547 'validate_callback' => __CLASS__ . '::validate_boolean',
2587 2548 'jp_group' => 'search',
2588 2549 ),
2589 2550
2590 - 'has_jetpack_search_product' => array(
2551 + 'has_jetpack_search_product' => array(
2591 2552 'description' => esc_html__( 'Has an active Jetpack Search product purchase', 'jetpack' ),
2592 2553 'type' => 'boolean',
2593 2554 'default' => 0,
2594 2555 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2594,9 +2555,9 @@
2594 2555 'validate_callback' => __CLASS__ . '::validate_boolean',
2595 2556 'jp_group' => 'settings',
2596 2557 ),
2597 2558
2598 - 'search_auto_config' => array(
2559 + 'search_auto_config' => array(
2599 2560 'description' => esc_html__( 'Trigger an auto config of instant search', 'jetpack' ),
2600 2561 'type' => 'boolean',
2601 2562 'default' => 0,
2602 2563 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2603,9 +2564,9 @@
2603 2564 'jp_group' => 'search',
2604 2565 ),
2605 2566
2606 2567 // Verification Tools.
2607 - 'google' => array(
2568 + 'google' => array(
2608 2569 'description' => esc_html__( 'Google Search Console', 'jetpack' ),
2609 2570 'type' => 'string',
2610 2571 'default' => '',
2611 2572 'validate_callback' => __CLASS__ . '::validate_verification_service',
@@ -2610,9 +2571,9 @@
2610 2571 'default' => '',
2611 2572 'validate_callback' => __CLASS__ . '::validate_verification_service',
2612 2573 'jp_group' => 'verification-tools',
2613 2574 ),
2614 - 'bing' => array(
2575 + 'bing' => array(
2615 2576 'description' => esc_html__( 'Bing Webmaster Center', 'jetpack' ),
2616 2577 'type' => 'string',
2617 2578 'default' => '',
2618 2579 'validate_callback' => __CLASS__ . '::validate_verification_service',
@@ -2617,9 +2578,9 @@
2617 2578 'default' => '',
2618 2579 'validate_callback' => __CLASS__ . '::validate_verification_service',
2619 2580 'jp_group' => 'verification-tools',
2620 2581 ),
2621 - 'pinterest' => array(
2582 + 'pinterest' => array(
2622 2583 'description' => esc_html__( 'Pinterest Site Verification', 'jetpack' ),
2623 2584 'type' => 'string',
2624 2585 'default' => '',
2625 2586 'validate_callback' => __CLASS__ . '::validate_verification_service',
@@ -2624,9 +2585,9 @@
2624 2585 'default' => '',
2625 2586 'validate_callback' => __CLASS__ . '::validate_verification_service',
2626 2587 'jp_group' => 'verification-tools',
2627 2588 ),
2628 - 'yandex' => array(
2589 + 'yandex' => array(
2629 2590 'description' => esc_html__( 'Yandex Site Verification', 'jetpack' ),
2630 2591 'type' => 'string',
2631 2592 'default' => '',
2632 2593 'validate_callback' => __CLASS__ . '::validate_verification_service',
@@ -2631,9 +2592,9 @@
2631 2592 'default' => '',
2632 2593 'validate_callback' => __CLASS__ . '::validate_verification_service',
2633 2594 'jp_group' => 'verification-tools',
2634 2595 ),
2635 - 'facebook' => array(
2596 + 'facebook' => array(
2636 2597 'description' => esc_html__( 'Facebook Domain Verification', 'jetpack' ),
2637 2598 'type' => 'string',
2638 2599 'default' => '',
2639 2600 'validate_callback' => __CLASS__ . '::validate_verification_service',
@@ -2640,9 +2601,9 @@
2640 2601 'jp_group' => 'verification-tools',
2641 2602 ),
2642 2603
2643 2604 // WordAds.
2644 - 'enable_header_ad' => array(
2605 + 'enable_header_ad' => array(
2645 2606 'description' => esc_html__( 'Display an ad unit at the top of each page.', 'jetpack' ),
2646 2607 'type' => 'boolean',
2647 2608 'default' => 1,
2648 2609 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2647,9 +2608,9 @@
2647 2608 'default' => 1,
2648 2609 'validate_callback' => __CLASS__ . '::validate_boolean',
2649 2610 'jp_group' => 'wordads',
2650 2611 ),
2651 - 'wordads_approved' => array(
2612 + 'wordads_approved' => array(
2652 2613 'description' => esc_html__( 'Is site approved for WordAds?', 'jetpack' ),
2653 2614 'type' => 'boolean',
2654 2615 'default' => 0,
2655 2616 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2654,9 +2615,9 @@
2654 2615 'default' => 0,
2655 2616 'validate_callback' => __CLASS__ . '::validate_boolean',
2656 2617 'jp_group' => 'wordads',
2657 2618 ),
2658 - 'wordads_second_belowpost' => array(
2619 + 'wordads_second_belowpost' => array(
2659 2620 'description' => esc_html__( 'Display second ad below post?', 'jetpack' ),
2660 2621 'type' => 'boolean',
2661 2622 'default' => 1,
2662 2623 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2661,9 +2622,16 @@
2661 2622 'default' => 1,
2662 2623 'validate_callback' => __CLASS__ . '::validate_boolean',
2663 2624 'jp_group' => 'wordads',
2664 2625 ),
2665 - 'wordads_display_front_page' => array(
2626 + 'wordads_inline_enabled' => array(
2627 + 'description' => esc_html__( 'Display inline ad within post content?', 'jetpack' ),
2628 + 'type' => 'boolean',
2629 + 'default' => 1,
2630 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2631 + 'jp_group' => 'wordads',
2632 + ),
2633 + 'wordads_display_front_page' => array(
2666 2634 'description' => esc_html__( 'Display ads on the front page?', 'jetpack' ),
2667 2635 'type' => 'boolean',
2668 2636 'default' => 1,
2669 2637 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2668,9 +2636,9 @@
2668 2636 'default' => 1,
2669 2637 'validate_callback' => __CLASS__ . '::validate_boolean',
2670 2638 'jp_group' => 'wordads',
2671 2639 ),
2672 - 'wordads_display_post' => array(
2640 + 'wordads_display_post' => array(
2673 2641 'description' => esc_html__( 'Display ads on posts?', 'jetpack' ),
2674 2642 'type' => 'boolean',
2675 2643 'default' => 1,
2676 2644 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2675,9 +2643,9 @@
2675 2643 'default' => 1,
2676 2644 'validate_callback' => __CLASS__ . '::validate_boolean',
2677 2645 'jp_group' => 'wordads',
2678 2646 ),
2679 - 'wordads_display_page' => array(
2647 + 'wordads_display_page' => array(
2680 2648 'description' => esc_html__( 'Display ads on pages?', 'jetpack' ),
2681 2649 'type' => 'boolean',
2682 2650 'default' => 1,
2683 2651 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2682,9 +2650,9 @@
2682 2650 'default' => 1,
2683 2651 'validate_callback' => __CLASS__ . '::validate_boolean',
2684 2652 'jp_group' => 'wordads',
2685 2653 ),
2686 - 'wordads_display_archive' => array(
2654 + 'wordads_display_archive' => array(
2687 2655 'description' => esc_html__( 'Display ads on archive pages?', 'jetpack' ),
2688 2656 'type' => 'boolean',
2689 2657 'default' => 1,
2690 2658 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2689,9 +2657,9 @@
2689 2657 'default' => 1,
2690 2658 'validate_callback' => __CLASS__ . '::validate_boolean',
2691 2659 'jp_group' => 'wordads',
2692 2660 ),
2693 - 'wordads_custom_adstxt_enabled' => array(
2661 + 'wordads_custom_adstxt_enabled' => array(
2694 2662 'description' => esc_html__( 'Custom ads.txt', 'jetpack' ),
2695 2663 'type' => 'boolean',
2696 2664 'default' => 0,
2697 2665 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2696,9 +2664,9 @@
2696 2664 'default' => 0,
2697 2665 'validate_callback' => __CLASS__ . '::validate_boolean',
2698 2666 'jp_group' => 'wordads',
2699 2667 ),
2700 - 'wordads_custom_adstxt' => array(
2668 + 'wordads_custom_adstxt' => array(
2701 2669 'description' => esc_html__( 'Custom ads.txt entries', 'jetpack' ),
2702 2670 'type' => 'string',
2703 2671 'default' => '',
2704 2672 'validate_callback' => __CLASS__ . '::validate_string',
@@ -2704,9 +2672,9 @@
2704 2672 'validate_callback' => __CLASS__ . '::validate_string',
2705 2673 'sanitize_callback' => 'sanitize_textarea_field',
2706 2674 'jp_group' => 'wordads',
2707 2675 ),
2708 - 'wordads_ccpa_enabled' => array(
2676 + 'wordads_ccpa_enabled' => array(
2709 2677 'description' => esc_html__( 'Enable support for California Consumer Privacy Act', 'jetpack' ),
2710 2678 'type' => 'boolean',
2711 2679 'default' => 0,
2712 2680 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2711,9 +2679,9 @@
2711 2679 'default' => 0,
2712 2680 'validate_callback' => __CLASS__ . '::validate_boolean',
2713 2681 'jp_group' => 'wordads',
2714 2682 ),
2715 - 'wordads_ccpa_privacy_policy_url' => array(
2683 + 'wordads_ccpa_privacy_policy_url' => array(
2716 2684 'description' => esc_html__( 'Privacy Policy URL', 'jetpack' ),
2717 2685 'type' => 'string',
2718 2686 'default' => '',
2719 2687 'validate_callback' => __CLASS__ . '::validate_string',
@@ -2719,11 +2687,18 @@
2719 2687 'validate_callback' => __CLASS__ . '::validate_string',
2720 2688 'sanitize_callback' => 'sanitize_text_field',
2721 2689 'jp_group' => 'wordads',
2722 2690 ),
2691 + 'wordads_cmp_enabled' => array(
2692 + 'description' => esc_html__( 'Enable GDPR Consent Management Banner for WordAds', 'jetpack' ),
2693 + 'type' => 'boolean',
2694 + 'default' => 0,
2695 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2696 + 'jp_group' => 'wordads',
2697 + ),
2723 2698
2724 2699 // Google Analytics.
2725 - 'google_analytics_tracking_id' => array(
2700 + 'google_analytics_tracking_id' => array(
2726 2701 'description' => esc_html__( 'Google Analytics', 'jetpack' ),
2727 2702 'type' => 'string',
2728 2703 'default' => '',
2729 2704 'validate_callback' => __CLASS__ . '::validate_alphanum',
@@ -2728,11 +2703,16 @@
2728 2703 'default' => '',
2729 2704 'validate_callback' => __CLASS__ . '::validate_alphanum',
2730 2705 'jp_group' => 'google-analytics',
2731 2706 ),
2707 + 'jetpack_wga' => array(
2708 + 'description' => esc_html__( 'Google Analytics', 'jetpack' ),
2709 + 'type' => 'object',
2710 + 'jp_group' => 'settings',
2711 + ),
2732 2712
2733 2713 // Stats.
2734 - 'admin_bar' => array(
2714 + 'admin_bar' => array(
2735 2715 'description' => esc_html__( 'Include a small chart in your admin bar with a 48-hour traffic snapshot.', 'jetpack' ),
2736 2716 'type' => 'boolean',
2737 2717 'default' => 1,
2738 2718 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2737,9 +2717,9 @@
2737 2717 'default' => 1,
2738 2718 'validate_callback' => __CLASS__ . '::validate_boolean',
2739 2719 'jp_group' => 'stats',
2740 2720 ),
2741 - 'enable_odyssey_stats' => array(
2721 + 'enable_odyssey_stats' => array(
2742 2722 'description' => esc_html__( 'Preview the new Jetpack Stats experience (Experimental).', 'jetpack' ),
2743 2723 'type' => 'boolean',
2744 2724 'default' => 1,
2745 2725 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2744,9 +2724,9 @@
2744 2724 'default' => 1,
2745 2725 'validate_callback' => __CLASS__ . '::validate_boolean',
2746 2726 'jp_group' => 'stats',
2747 2727 ),
2748 - 'roles' => array(
2728 + 'roles' => array(
2749 2729 'description' => esc_html__( 'Select the roles that will be able to view stats reports.', 'jetpack' ),
2750 2730 'type' => 'array',
2751 2731 'items' => array(
2752 2732 'type' => 'string',
@@ -2755,9 +2735,9 @@
2755 2735 'validate_callback' => __CLASS__ . '::validate_stats_roles',
2756 2736 'sanitize_callback' => __CLASS__ . '::sanitize_stats_allowed_roles',
2757 2737 'jp_group' => 'stats',
2758 2738 ),
2759 - 'count_roles' => array(
2739 + 'count_roles' => array(
2760 2740 'description' => esc_html__( 'Count the page views of registered users who are logged in.', 'jetpack' ),
2761 2741 'type' => 'array',
2762 2742 'items' => array(
2763 2743 'type' => 'string',
@@ -2765,9 +2745,9 @@
2765 2745 'default' => array( 'administrator' ),
2766 2746 'validate_callback' => __CLASS__ . '::validate_stats_roles',
2767 2747 'jp_group' => 'stats',
2768 2748 ),
2769 - 'blog_id' => array(
2749 + 'blog_id' => array(
2770 2750 'description' => esc_html__( 'Blog ID.', 'jetpack' ),
2771 2751 'type' => 'boolean',
2772 2752 'default' => 0,
2773 2753 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2772,9 +2752,9 @@
2772 2752 'default' => 0,
2773 2753 'validate_callback' => __CLASS__ . '::validate_boolean',
2774 2754 'jp_group' => 'stats',
2775 2755 ),
2776 - 'do_not_track' => array(
2756 + 'do_not_track' => array(
2777 2757 'description' => esc_html__( 'Do not track.', 'jetpack' ),
2778 2758 'type' => 'boolean',
2779 2759 'default' => 1,
2780 2760 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2779,9 +2759,9 @@
2779 2759 'default' => 1,
2780 2760 'validate_callback' => __CLASS__ . '::validate_boolean',
2781 2761 'jp_group' => 'stats',
2782 2762 ),
2783 - 'version' => array(
2763 + 'version' => array(
2784 2764 'description' => esc_html__( 'Version.', 'jetpack' ),
2785 2765 'type' => 'integer',
2786 2766 'default' => 9,
2787 2767 'validate_callback' => __CLASS__ . '::validate_posint',
@@ -2786,9 +2766,9 @@
2786 2766 'default' => 9,
2787 2767 'validate_callback' => __CLASS__ . '::validate_posint',
2788 2768 'jp_group' => 'stats',
2789 2769 ),
2790 - 'collapse_nudges' => array(
2770 + 'collapse_nudges' => array(
2791 2771 'description' => esc_html__( 'Collapse upgrade nudges', 'jetpack' ),
2792 2772 'type' => 'boolean',
2793 2773 'default' => 0,
2794 2774 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2794,10 +2774,19 @@
2794 2774 'validate_callback' => __CLASS__ . '::validate_boolean',
2795 2775 'jp_group' => 'stats',
2796 2776 ),
2797 2777
2778 + // Whether to share stats views with WordPress.com Reader.
2779 + 'wpcom_reader_views_enabled' => array(
2780 + 'description' => esc_html__( 'Show post views in the WordPress.com Reader.', 'jetpack' ),
2781 + 'type' => 'boolean',
2782 + 'default' => 1,
2783 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2784 + 'jp_group' => 'settings',
2785 + ),
2786 +
2798 2787 // Akismet - Not a module, but a plugin. The options can be passed and handled differently.
2799 - 'akismet_show_user_comments_approved' => array(
2788 + 'akismet_show_user_comments_approved' => array(
2800 2789 'description' => '',
2801 2790 'type' => 'boolean',
2802 2791 'default' => 0,
2803 2792 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2803,9 +2792,9 @@
2803 2792 'validate_callback' => __CLASS__ . '::validate_boolean',
2804 2793 'jp_group' => 'settings',
2805 2794 ),
2806 2795
2807 - 'wordpress_api_key' => array(
2796 + 'wordpress_api_key' => array(
2808 2797 'description' => '',
2809 2798 'type' => 'string',
2810 2799 'default' => '',
2811 2800 'validate_callback' => __CLASS__ . '::validate_alphanum',
@@ -2812,9 +2801,9 @@
2812 2801 'jp_group' => 'settings',
2813 2802 ),
2814 2803
2815 2804 // Empty stats card dismiss.
2816 - 'dismiss_empty_stats_card' => array(
2805 + 'dismiss_empty_stats_card' => array(
2817 2806 'description' => '',
2818 2807 'type' => 'boolean',
2819 2808 'default' => 0,
2820 2809 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2821,9 +2810,9 @@
2821 2810 'jp_group' => 'settings',
2822 2811 ),
2823 2812
2824 2813 // Backup Getting Started card on dashboard.
2825 - 'dismiss_dash_backup_getting_started' => array(
2814 + 'dismiss_dash_backup_getting_started' => array(
2826 2815 'description' => '',
2827 2816 'type' => 'boolean',
2828 2817 'default' => 0,
2829 2818 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2830,9 +2819,9 @@
2830 2819 'jp_group' => 'settings',
2831 2820 ),
2832 2821
2833 2822 // Agencies Learn More card on dashboard.
2834 - 'dismiss_dash_agencies_learn_more' => array(
2823 + 'dismiss_dash_agencies_learn_more' => array(
2835 2824 'description' => '',
2836 2825 'type' => 'boolean',
2837 2826 'default' => 0,
2838 2827 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2838,9 +2827,9 @@
2838 2827 'validate_callback' => __CLASS__ . '::validate_boolean',
2839 2828 'jp_group' => 'settings',
2840 2829 ),
2841 2830
2842 - 'lang_id' => array(
2831 + 'lang_id' => array(
2843 2832 'description' => esc_html__( 'Primary language for the site.', 'jetpack' ),
2844 2833 'type' => 'string',
2845 2834 'default' => 'en_US',
2846 2835 'jp_group' => 'settings',
@@ -2845,32 +2834,10 @@
2845 2834 'default' => 'en_US',
2846 2835 'jp_group' => 'settings',
2847 2836 ),
2848 2837
2849 - 'onboarding' => array(
2850 - 'description' => '',
2851 - 'type' => 'object',
2852 - 'default' => array(
2853 - 'siteTitle' => '',
2854 - 'siteDescription' => '',
2855 - 'siteType' => 'personal',
2856 - 'homepageFormat' => 'posts',
2857 - 'addContactForm' => 0,
2858 - 'businessAddress' => array(
2859 - 'name' => '',
2860 - 'street' => '',
2861 - 'city' => '',
2862 - 'state' => '',
2863 - 'zip' => '',
2864 - ),
2865 - 'installWooCommerce' => false,
2866 - ),
2867 - 'validate_callback' => __CLASS__ . '::validate_onboarding',
2868 - 'jp_group' => 'settings',
2869 - ),
2870 -
2871 2838 // SEO Tools.
2872 - 'advanced_seo_front_page_description' => array(
2839 + 'advanced_seo_front_page_description' => array(
2873 2840 'description' => esc_html__( 'Front page meta description.', 'jetpack' ),
2874 2841 'type' => 'string',
2875 2842 'default' => '',
2876 2843 'sanitize_callback' => 'Jetpack_SEO_Utils::sanitize_front_page_meta_description',
@@ -2876,9 +2843,9 @@
2876 2843 'sanitize_callback' => 'Jetpack_SEO_Utils::sanitize_front_page_meta_description',
2877 2844 'jp_group' => 'seo-tools',
2878 2845 ),
2879 2846
2880 - 'advanced_seo_title_formats' => array(
2847 + 'advanced_seo_title_formats' => array(
2881 2848 'description' => esc_html__( 'SEO page title structures.', 'jetpack' ),
2882 2849 'type' => 'object',
2883 2850 'default' => array(
2884 2851 'archives' => array(),
@@ -2891,10 +2858,34 @@
2891 2858 'validate_callback' => 'Jetpack_SEO_Titles::are_valid_title_formats',
2892 2859 'sanitize_callback' => 'Jetpack_SEO_Titles::sanitize_title_formats',
2893 2860 ),
2894 2861
2862 + // AI tab (Jetpack > SEO). Plain option the SEO package reads to serve
2863 + // /llms.txt. The front-end behavior is gated inside the package; this
2864 + // only round-trips the persisted state alongside the other seo-tools
2865 + // settings.
2866 + 'jetpack_seo_llms_txt_enabled' => array(
2867 + 'description' => esc_html__( 'Generate an llms.txt file to guide AI assistants around your content.', 'jetpack' ),
2868 + 'type' => 'boolean',
2869 + 'default' => 0,
2870 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2871 + 'jp_group' => 'seo-tools',
2872 + ),
2873 +
2874 + // AI tab (Jetpack > SEO). Sparse per-crawler override map the SEO
2875 + // package reads to emit robots.txt directives for blocked AI crawlers.
2876 + // Stored as `slug => bool` (true = blocked); catalog validation and
2877 + // default-pruning happen in `Ai_Crawlers::get_overrides()`.
2878 + 'jetpack_seo_ai_crawler_overrides' => array(
2879 + 'description' => esc_html__( 'AI crawler allow/block overrides.', 'jetpack' ),
2880 + 'type' => 'object',
2881 + 'default' => array(),
2882 + 'jp_group' => 'seo-tools',
2883 + 'sanitize_callback' => __CLASS__ . '::sanitize_ai_crawler_overrides',
2884 + ),
2885 +
2895 2886 // VideoPress.
2896 - 'videopress_private_enabled_for_site' => array(
2887 + 'videopress_private_enabled_for_site' => array(
2897 2888 'description' => esc_html__( 'Video Privacy: Restrict views to members of this site', 'jetpack' ),
2898 2889 'type' => 'boolean',
2899 2890 'default' => 0,
2900 2891 'validate_callback' => __CLASS__ . '::validate_boolean',
@@ -2901,8 +2892,21 @@
2901 2892 'jp_group' => 'videopress',
2902 2893 ),
2903 2894 );
2904 2895
2896 + // SEO Tools - SEO Enhancer.
2897 + // TODO: move this to the main options array? The filter was there while developing the feature.
2898 + // It might come in handy to hold its availability behind the filter since it still depends on AI to be available.
2899 + if ( apply_filters( 'ai_seo_enhancer_enabled', true ) ) {
2900 + $options['ai_seo_enhancer_enabled'] = array(
2901 + 'description' => esc_html__( 'Automatically generate SEO title, SEO description, and image alt text for new posts.', 'jetpack' ),
2902 + 'type' => 'boolean',
2903 + 'default' => 0,
2904 + 'validate_callback' => __CLASS__ . '::validate_boolean',
2905 + 'jp_group' => 'seo-tools',
2906 + );
2907 + }
2908 +
2905 2909 // Add modules to list so they can be toggled.
2906 2910 $modules = Jetpack::get_available_modules();
2907 2911 if ( is_array( $modules ) && ! empty( $modules ) ) {
2908 2912 $module_args = array(
@@ -2948,8 +2952,10 @@
2948 2952 * Validates that the parameters are proper values that can be set during Jetpack onboarding.
2949 2953 *
2950 2954 * @since 5.4.0
2951 2955 *
2956 + * @deprecated since 13.9
2957 + *
2952 2958 * @param array $onboarding_data Values to check.
2953 2959 * @param WP_REST_Request $request The request sent to the WP REST API.
2954 2960 * @param string $param Name of the parameter passed to endpoint holding $value.
2955 2961 *
@@ -2954,24 +2960,10 @@
2954 2960 * @param string $param Name of the parameter passed to endpoint holding $value.
2955 2961 *
2956 2962 * @return bool|WP_Error
2957 2963 */
2958 - public static function validate_onboarding( $onboarding_data, $request, $param ) {
2959 - if ( ! is_array( $onboarding_data ) ) {
2960 - return new WP_Error( 'invalid_param', esc_html__( 'Not valid onboarding data.', 'jetpack' ) );
2961 - }
2962 - foreach ( $onboarding_data as $value ) {
2963 - if ( is_string( $value ) ) {
2964 - $onboarding_choice = self::validate_string( $value, $request, $param );
2965 - } elseif ( is_array( $value ) ) {
2966 - $onboarding_choice = self::validate_onboarding( $value, $request, $param );
2967 - } else {
2968 - $onboarding_choice = self::validate_boolean( $value, $request, $param );
2969 - }
2970 - if ( is_wp_error( $onboarding_choice ) ) {
2971 - return $onboarding_choice;
2972 - }
2973 - }
2964 + public static function validate_onboarding( $onboarding_data, $request, $param ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
2965 + _deprecated_function( __METHOD__, '13.9' );
2974 2966 return true;
2975 2967 }
2976 2968
2977 2969 /**
@@ -3185,9 +3177,9 @@
3185 3177 * Validates that the parameter is among the roles allowed for Stats.
3186 3178 *
3187 3179 * @since 4.3.0
3188 3180 *
3189 - * @param string|bool $value Value to check.
3181 + * @param mixed $value Value to check.
3190 3182 * @param WP_REST_Request $request The request sent to the WP REST API.
3191 3183 * @param string $param Name of the parameter passed to endpoint holding $value.
3192 3184 *
3193 3185 * @return bool|WP_Error
@@ -3192,16 +3184,37 @@
3192 3184 *
3193 3185 * @return bool|WP_Error
3194 3186 */
3195 3187 public static function validate_stats_roles( $value, $request, $param ) {
3196 - if ( ! empty( $value ) && ! array_intersect( self::$stats_roles, $value ) ) {
3188 + // An empty value clears the setting; sanitize_stats_allowed_roles() falls back to 'administrator'.
3189 + if ( empty( $value ) ) {
3190 + return true;
3191 + }
3192 +
3193 + // Enforce the schema's list-of-strings contract before array_intersect() below sees the value.
3194 + if ( ! is_array( $value ) || count( array_filter( $value, 'is_string' ) ) !== count( $value ) ) {
3197 3195 return new WP_Error(
3198 3196 'invalid_param',
3199 3197 sprintf(
3198 + /* Translators: Placeholder is a parameter name. */
3199 + esc_html__( '%s must be an array of user roles.', 'jetpack' ),
3200 + $param
3201 + )
3202 + );
3203 + }
3204 +
3205 + if ( ! function_exists( 'get_editable_roles' ) ) {
3206 + require_once ABSPATH . 'wp-admin/includes/user.php';
3207 + }
3208 + $editable_roles = array_keys( get_editable_roles() );
3209 + if ( ! array_intersect( $editable_roles, $value ) ) {
3210 + return new WP_Error(
3211 + 'invalid_param',
3212 + sprintf(
3200 3213 /* 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. */
3201 3214 esc_html__( '%1$s must be %2$s.', 'jetpack' ),
3202 3215 $param,
3203 - join( ', ', self::$stats_roles )
3216 + implode( ', ', $editable_roles )
3204 3217 )
3205 3218 );
3206 3219 }
3207 3220 return true;
@@ -3236,9 +3249,9 @@
3236 3249 sprintf(
3237 3250 /* 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 */
3238 3251 esc_html__( '%1$s must be %2$s.', 'jetpack' ),
3239 3252 $param,
3240 - join( ', ', $views )
3253 + implode( ', ', $views )
3241 3254 )
3242 3255 );
3243 3256 }
3244 3257 return true;
@@ -3244,8 +3257,59 @@
3244 3257 return true;
3245 3258 }
3246 3259
3247 3260 /**
3261 + * Validates that the parameter is among the valid reply-to types for subscriptions.
3262 + *
3263 + * @since 4.3.0
3264 + *
3265 + * @param string|bool $value Value to check.
3266 + * @param WP_REST_Request $request The request sent to the WP REST API.
3267 + * @param string $param Name of the parameter passed to endpoint holding $value.
3268 + *
3269 + * @return bool|WP_Error
3270 + */
3271 + public static function validate_subscriptions_reply_to( $value, $request, $param ) {
3272 + require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
3273 + if ( ! empty( $value ) && ! Automattic\Jetpack\Modules\Subscriptions\Settings::is_valid_reply_to( $value ) ) {
3274 + return new WP_Error(
3275 + 'invalid_param',
3276 + sprintf(
3277 + /* Translators: Placeholder is a parameter name. */
3278 + esc_html__( '%s must be a valid type.', 'jetpack' ),
3279 + $param
3280 + )
3281 + );
3282 + }
3283 + return true;
3284 + }
3285 +
3286 + /**
3287 + * Validates that the parameter is among the valid reply-to types for subscriptions.
3288 + *
3289 + * @since 4.3.0
3290 + *
3291 + * @param string|bool $value Value to check.
3292 + * @param WP_REST_Request $request The request sent to the WP REST API.
3293 + * @param string $param Name of the parameter passed to endpoint holding $value.
3294 + *
3295 + * @return bool|WP_Error
3296 + */
3297 + public static function validate_subscriptions_reply_to_name( $value, $request, $param ) {
3298 + if ( ! empty( $value ) && ! is_string( $value ) ) {
3299 + return new WP_Error(
3300 + 'invalid_param',
3301 + sprintf(
3302 + /* Translators: Placeholder is a parameter name. */
3303 + esc_html__( '%s must be a valid type.', 'jetpack' ),
3304 + $param
3305 + )
3306 + );
3307 + }
3308 + return true;
3309 + }
3310 +
3311 + /**
3248 3312 * Validates that the parameter is among the views where the Sharing can be displayed.
3249 3313 *
3250 3314 * @since 4.3.0
3251 3315 *
@@ -3292,9 +3356,9 @@
3292 3356 sprintf(
3293 3357 /* Translators: placeholder 1 is a parameter holding the services passed to endpoint, placeholder 2 is a list of all Jetpack Sharing services */
3294 3358 esc_html__( '%1$s visible and hidden items must be a list of %2$s.', 'jetpack' ),
3295 3359 $param,
3296 - join( ', ', $services )
3360 + implode( ', ', $services )
3297 3361 )
3298 3362 );
3299 3363 }
3300 3364 return true;
@@ -3460,16 +3524,72 @@
3460 3524 return true;
3461 3525 }
3462 3526
3463 3527 /**
3528 + * Validates the subscription_options parameter.
3529 + *
3530 + * @param array $values Value to check.
3531 + *
3532 + * @return bool|WP_Error
3533 + */
3534 + public static function validate_subscription_options( $values ) {
3535 + // A REST "object" decodes to a PHP associative array. Reject any other
3536 + // type (object, string, int, null, ...) up front so the array_keys()
3537 + // loop below never runs against a non-array and triggers a PHP warning.
3538 + if ( ! is_array( $values ) ) {
3539 + return new WP_Error(
3540 + 'invalid_param',
3541 + /* Translators: subscription_options is a variable name, and shouldn't be translated. */
3542 + esc_html__( 'subscription_options must be an object.', 'jetpack' )
3543 + );
3544 + }
3545 + foreach ( array_keys( $values ) as $key ) {
3546 + if ( ! in_array( $key, array( 'welcome', 'invitation', 'comment_follow', 'subscribe_modal_heading', 'free_tier_description', 'hide_free_tier' ), true ) ) {
3547 + return new WP_Error(
3548 + 'invalid_param',
3549 + sprintf(
3550 + /* Translators: Placeholder is the invalid param being sent. */
3551 + esc_html__( '%s is not one of the allowed members of subscription_options.', 'jetpack' ),
3552 + $key
3553 + )
3554 + );
3555 + }
3556 + }
3557 + return true;
3558 + }
3559 +
3560 + /**
3561 + * Validates that the parameter is an array.
3562 + *
3563 + * @param array $values Value to check.
3564 + * @param WP_REST_Request $request The request sent to the WP REST API.
3565 + * @param string $param Name of the parameter passed to the endpoint holding $value.
3566 + *
3567 + * @return bool|WP_Error
3568 + */
3569 + public static function validate_array( $values, $request, $param ) {
3570 + if ( ! is_array( $values ) ) {
3571 + return new WP_Error(
3572 + 'invalid_param',
3573 + sprintf(
3574 + /* Translators: Placeholder is a parameter name. */
3575 + esc_html__( '%s must be an object.', 'jetpack' ),
3576 + $param
3577 + )
3578 + );
3579 + }
3580 + return true;
3581 + }
3582 +
3583 + /**
3464 3584 * If for some reason the roles allowed to see Stats are empty (for example, user tampering with checkboxes),
3465 3585 * return an array with only 'administrator' as the allowed role and save it for 'roles' option.
3466 3586 *
3467 3587 * @since 4.3.0
3468 3588 *
3469 - * @param string|bool $value Value to check.
3589 + * @param mixed $value Value to check.
3470 3590 *
3471 - * @return bool|array
3591 + * @return mixed The value as submitted, or an array holding only 'administrator' when it is empty.
3472 3592 */
3473 3593 public static function sanitize_stats_allowed_roles( $value ) {
3474 3594 if ( empty( $value ) ) {
3475 3595 return array( 'administrator' );
@@ -3477,8 +3597,31 @@
3477 3597 return $value;
3478 3598 }
3479 3599
3480 3600 /**
3601 + * Sanitize the AI crawler override map.
3602 + *
3603 + * Keeps the value package-agnostic: each key is normalized with sanitize_key()
3604 + * and each value cast to bool. Catalog validation and default-pruning happen in
3605 + * `Automattic\Jetpack\SEO\Ai_Crawlers::get_overrides()`.
3606 + *
3607 + * @param mixed $value The submitted override map.
3608 + *
3609 + * @return array<string, bool> Sanitized `slug => bool` map.
3610 + */
3611 + public static function sanitize_ai_crawler_overrides( $value ) {
3612 + if ( ! is_array( $value ) ) {
3613 + return array();
3614 + }
3615 +
3616 + $sanitized = array();
3617 + foreach ( $value as $k => $v ) {
3618 + $sanitized[ sanitize_key( $k ) ] = (bool) $v;
3619 + }
3620 + return $sanitized;
3621 + }
3622 +
3623 + /**
3481 3624 * Get the currently accessed route and return the module slug in it.
3482 3625 *
3483 3626 * @since 4.3.0
3484 3627 *
@@ -3487,9 +3630,9 @@
3487 3630 * @return array|string
3488 3631 */
3489 3632 public static function get_module_requested( $route = '/module/(?P<slug>[a-z\-]+)' ) {
3490 3633
3491 - if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
3634 + if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) || ! is_string( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
3492 3635 return '';
3493 3636 }
3494 3637
3495 3638 preg_match( "#$route#", $GLOBALS['wp']->query_vars['rest_route'], $module );
@@ -3540,8 +3683,40 @@
3540 3683 return $modules;
3541 3684 }
3542 3685
3543 3686 /**
3687 + * Remove options the current user cannot read.
3688 + *
3689 + * Covers every `jetpack_waf_*` option, plus the two Protect options that expose the
3690 + * same data under a different name: `jetpack_protect_global_whitelist` is populated
3691 + * from `jetpack_waf_ip_allow_list`, and `jetpack_protect_key` is a shared secret.
3692 + *
3693 + * @since 16.2
3694 + *
3695 + * @param array $options Option definitions keyed by option name.
3696 + * @return array
3697 + */
3698 + public static function filter_options_for_response( $options ) {
3699 + if ( current_user_can( 'manage_options' ) ) {
3700 + return $options;
3701 + }
3702 +
3703 + $restricted = array(
3704 + 'jetpack_protect_key',
3705 + 'jetpack_protect_global_whitelist',
3706 + );
3707 +
3708 + return array_filter(
3709 + $options,
3710 + static function ( $option_name ) use ( $restricted ) {
3711 + return 0 !== strpos( $option_name, 'jetpack_waf_' )
3712 + && ! in_array( $option_name, $restricted, true );
3713 + },
3714 + ARRAY_FILTER_USE_KEY
3715 + );
3716 + }
3717 +
3718 + /**
3544 3719 * Remove 'validate_callback' item from options available for module.
3545 3720 * Fetch current option value and add to array of module options.
3546 3721 * Prepare values of module options that need special handling, like those saved in wpcom.
3547 3722 *
@@ -3604,9 +3779,9 @@
3604 3779 $options = self::split_options( $options, $sharer->get_global_options() );
3605 3780 $options['sharing_services']['current_value'] = $sharer->get_blog_services();
3606 3781 $other_sharedaddy_options = array( 'jetpack-twitter-cards-site-tag', 'sharedaddy_disable_resources', 'sharing_delete_service' );
3607 3782 foreach ( $other_sharedaddy_options as $key ) {
3608 - $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
3783 + $default_value = $options[ $key ]['default'] ?? '';
3609 3784 $current_value = get_option( $key, $default_value );
3610 3785 $options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] );
3611 3786 }
3612 3787 break;
@@ -3617,9 +3792,9 @@
3617 3792 break;
3618 3793 default:
3619 3794 // These option are just stored as plain WordPress options.
3620 3795 foreach ( $options as $key => $value ) {
3621 - $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
3796 + $default_value = $options[ $key ]['default'] ?? '';
3622 3797 $current_value = get_option( $key, $default_value );
3623 3798 $options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] );
3624 3799 }
3625 3800 }
@@ -3629,14 +3804,17 @@
3629 3804 // We don't need validate_callback in the response.
3630 3805 if ( isset( $options[ $key ]['validate_callback'] ) ) {
3631 3806 unset( $options[ $key ]['validate_callback'] );
3632 3807 }
3633 - $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : '';
3808 + $default_value = $options[ $key ]['default'] ?? '';
3634 3809 if ( ! array_key_exists( 'current_value', $options[ $key ] ) ) {
3635 3810 $options[ $key ]['current_value'] = self::cast_value( $default_value, $options[ $key ] );
3636 3811 }
3637 3812 }
3638 - return $options;
3813 +
3814 + // Filter last: the switch above assigns current_value by key without isset(),
3815 + // so filtering earlier would let those assignments re-add a removed option.
3816 + return self::filter_options_for_response( $options );
3639 3817 }
3640 3818
3641 3819 /**
3642 3820 * Splits module options saved as arrays like relatedposts or verification_services_codes into separate options to be returned in the response.
@@ -4197,5 +4375,53 @@
4197 4375 )
4198 4376 );
4199 4377 }
4200 4378
4379 + /**
4380 + * Return the list of available features.
4381 + *
4382 + * @return array
4383 + */
4384 + public static function get_features_available() {
4385 + $raw_modules = Jetpack::get_available_modules();
4386 + $modules = array();
4387 + foreach ( $raw_modules as $module ) {
4388 + $modules[] = Jetpack::get_module_slug( $module );
4389 + }
4390 +
4391 + return $modules;
4392 + }
4393 +
4394 + /**
4395 + * Returns what features are enabled. Uses the slug of the modules files.
4396 + *
4397 + * @return array
4398 + */
4399 + public static function get_features_enabled() {
4400 + $raw_modules = Jetpack::get_active_modules();
4401 + $modules = array();
4402 + foreach ( $raw_modules as $module ) {
4403 + $modules[] = Jetpack::get_module_slug( $module );
4404 + }
4405 +
4406 + return $modules;
4407 + }
4408 +
4409 + /**
4410 + * Verify that the API client is allowed to replace user token.
4411 + *
4412 + * @since 1.29.0
4413 + *
4414 + * @return bool|WP_Error
4415 + */
4416 + public static function get_features_permission_check() {
4417 + if ( ! Rest_Authentication::is_signed_with_blog_token() ) {
4418 + $message = esc_html__(
4419 + 'You do not have the correct user permissions to perform this action. Please contact your site admin if you think this is a mistake.',
4420 + 'jetpack'
4421 + );
4422 + return new WP_Error( 'invalid_permission_fetch_features', $message, array( 'status' => rest_authorization_required_code() ) );
4423 + }
4424 +
4425 + return true;
4426 + }
4201 4427 } // class end