Jetpack_Core_Json_Api_Endpoints

Class Jetpack_Core_Json_Api_Endpoints.

Defined (1)

The class is defined in the following location(s).

/_inc/lib/class.core-rest-api-endpoints.php  
  1. class Jetpack_Core_Json_Api_Endpoints { 
  2.  
  3. /** 
  4. * @var string Generic error message when user is not allowed to perform an action. 
  5. */ 
  6. public static $user_permissions_error_msg; 
  7.  
  8. /** 
  9. * @var array Roles that can access Stats once they're granted access. 
  10. */ 
  11. public static $stats_roles; 
  12.  
  13. /** 
  14. * Declare the Jetpack REST API endpoints. 
  15. * @since 4.3.0 
  16. */ 
  17. public static function register_endpoints() { 
  18.  
  19. // Load API endpoint base classes 
  20. require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php'; 
  21.  
  22. // Load API endpoints 
  23. require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php'; 
  24.  
  25. self::$user_permissions_error_msg = esc_html__( 
  26. 'You do not have the correct user permissions to perform this action. 
  27. Please contact your site admin if you think this is a mistake.',  
  28. 'jetpack' 
  29. ); 
  30.  
  31. self::$stats_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); 
  32.  
  33. Jetpack::load_xml_rpc_client(); 
  34. $ixr_client = new Jetpack_IXR_Client( array( 'user_id' => get_current_user_id() ) ); 
  35. $core_api_endpoint = new Jetpack_Core_API_Data( $ixr_client ); 
  36. $module_list_endpoint = new Jetpack_Core_API_Module_List_Endpoint(); 
  37. $module_data_endpoint = new Jetpack_Core_API_Module_Data_Endpoint(); 
  38. $module_toggle_endpoint = new Jetpack_Core_API_Module_Toggle_Endpoint( new Jetpack_IXR_Client() ); 
  39.  
  40. // Get current connection status of Jetpack 
  41. register_rest_route( 'jetpack/v4', '/connection', array( 
  42. 'methods' => WP_REST_Server::READABLE,  
  43. 'callback' => __CLASS__ . '::jetpack_connection_status',  
  44. ) ); 
  45.  
  46. // Fetches a fresh connect URL 
  47. register_rest_route( 'jetpack/v4', '/connection/url', array( 
  48. 'methods' => WP_REST_Server::READABLE,  
  49. 'callback' => __CLASS__ . '::build_connect_url',  
  50. 'permission_callback' => __CLASS__ . '::connect_url_permission_callback',  
  51. ) ); 
  52.  
  53. // Get current user connection data 
  54. register_rest_route( 'jetpack/v4', '/connection/data', array( 
  55. 'methods' => WP_REST_Server::READABLE,  
  56. 'callback' => __CLASS__ . '::get_user_connection_data',  
  57. 'permission_callback' => __CLASS__ . '::get_user_connection_data_permission_callback',  
  58. ) ); 
  59.  
  60. // Disconnect site from WordPress.com servers 
  61. register_rest_route( 'jetpack/v4', '/connection', array( 
  62. 'methods' => WP_REST_Server::EDITABLE,  
  63. 'callback' => __CLASS__ . '::disconnect_site',  
  64. 'permission_callback' => __CLASS__ . '::disconnect_site_permission_callback',  
  65. ) ); 
  66.  
  67. // Disconnect/unlink user from WordPress.com servers 
  68. register_rest_route( 'jetpack/v4', '/connection/user', array( 
  69. 'methods' => WP_REST_Server::EDITABLE,  
  70. 'callback' => __CLASS__ . '::unlink_user',  
  71. 'permission_callback' => __CLASS__ . '::unlink_user_permission_callback',  
  72. ) ); 
  73.  
  74. // Get current site data 
  75. register_rest_route( 'jetpack/v4', '/site', array( 
  76. 'methods' => WP_REST_Server::READABLE,  
  77. 'callback' => __CLASS__ . '::get_site_data',  
  78. 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',  
  79. ) ); 
  80.  
  81. // Confirm that a site in identity crisis should be in staging mode 
  82. register_rest_route( 'jetpack/v4', '/identity-crisis/confirm-safe-mode', array( 
  83. 'methods' => WP_REST_Server::EDITABLE,  
  84. 'callback' => __CLASS__ . '::confirm_safe_mode',  
  85. 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',  
  86. ) ); 
  87.  
  88. // IDC resolve: create an entirely new shadow site for this URL. 
  89. register_rest_route( 'jetpack/v4', '/identity-crisis/start-fresh', array( 
  90. 'methods' => WP_REST_Server::EDITABLE,  
  91. 'callback' => __CLASS__ . '::start_fresh_connection',  
  92. 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',  
  93. ) ); 
  94.  
  95. // Handles the request to migrate stats and subscribers during an identity crisis. 
  96. register_rest_route( 'jetpack/v4', 'identity-crisis/migrate', array( 
  97. 'methods' => WP_REST_Server::EDITABLE,  
  98. 'callback' => __CLASS__ . '::migrate_stats_and_subscribers',  
  99. 'permissison_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',  
  100. ) ); 
  101.  
  102. // Return all modules 
  103. register_rest_route( 'jetpack/v4', '/module/all', array( 
  104. 'methods' => WP_REST_Server::READABLE,  
  105. 'callback' => array( $module_list_endpoint, 'process' ),  
  106. 'permission_callback' => array( $module_list_endpoint, 'can_request' ),  
  107. ) ); 
  108.  
  109. // Activate many modules 
  110. register_rest_route( 'jetpack/v4', '/module/all/active', array( 
  111. 'methods' => WP_REST_Server::EDITABLE,  
  112. 'callback' => array( $module_list_endpoint, 'process' ),  
  113. 'permission_callback' => array( $module_list_endpoint, 'can_request' ),  
  114. 'args' => array( 
  115. 'modules' => array( 
  116. 'default' => '',  
  117. 'type' => 'array',  
  118. 'items' => array( 
  119. 'type' => 'string',  
  120. ),  
  121. 'required' => true,  
  122. 'validate_callback' => __CLASS__ . '::validate_module_list',  
  123. ),  
  124. 'active' => array( 
  125. 'default' => true,  
  126. 'type' => 'boolean',  
  127. 'required' => false,  
  128. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  129. ),  
  130. ) ); 
  131.  
  132. // Return a single module and update it when needed 
  133. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', array( 
  134. 'methods' => WP_REST_Server::READABLE,  
  135. 'callback' => array( $core_api_endpoint, 'process' ),  
  136. 'permission_callback' => array( $core_api_endpoint, 'can_request' ),  
  137. ) ); 
  138.  
  139. // Activate and deactivate a module 
  140. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/active', array( 
  141. 'methods' => WP_REST_Server::EDITABLE,  
  142. 'callback' => array( $module_toggle_endpoint, 'process' ),  
  143. 'permission_callback' => array( $module_toggle_endpoint, 'can_request' ),  
  144. 'args' => array( 
  145. 'active' => array( 
  146. 'default' => true,  
  147. 'type' => 'boolean',  
  148. 'required' => true,  
  149. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  150. ),  
  151. ) ); 
  152.  
  153. // Update a module 
  154. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)', array( 
  155. 'methods' => WP_REST_Server::EDITABLE,  
  156. 'callback' => array( $core_api_endpoint, 'process' ),  
  157. 'permission_callback' => array( $core_api_endpoint, 'can_request' ),  
  158. 'args' => self::get_updateable_parameters( 'any' ) 
  159. ) ); 
  160.  
  161. // Get data for a specific module, i.e. Protect block count, WPCOM stats,  
  162. // Akismet spam count, etc. 
  163. register_rest_route( 'jetpack/v4', '/module/(?P<slug>[a-z\-]+)/data', array( 
  164. 'methods' => WP_REST_Server::READABLE,  
  165. 'callback' => array( $module_data_endpoint, 'process' ),  
  166. 'permission_callback' => array( $module_data_endpoint, 'can_request' ),  
  167. 'args' => array( 
  168. 'range' => array( 
  169. 'default' => 'day',  
  170. 'type' => 'string',  
  171. 'required' => false,  
  172. 'validate_callback' => __CLASS__ . '::validate_string',  
  173. ),  
  174. ) ); 
  175.  
  176. // Check if the API key for a specific service is valid or not 
  177. register_rest_route( 'jetpack/v4', '/module/(?P<service>[a-z\-]+)/key/check', array( 
  178. 'methods' => WP_REST_Server::READABLE,  
  179. 'callback' => array( $module_data_endpoint, 'key_check' ),  
  180. 'permission_callback' => __CLASS__ . '::update_settings_permission_check',  
  181. 'sanitize_callback' => 'sanitize_text_field',  
  182. ) ); 
  183.  
  184. register_rest_route( 'jetpack/v4', '/module/(?P<service>[a-z\-]+)/key/check', array( 
  185. 'methods' => WP_REST_Server::EDITABLE,  
  186. 'callback' => array( $module_data_endpoint, 'key_check' ),  
  187. 'permission_callback' => __CLASS__ . '::update_settings_permission_check',  
  188. 'sanitize_callback' => 'sanitize_text_field',  
  189. 'args' => array( 
  190. 'api_key' => array( 
  191. 'default' => '',  
  192. 'type' => 'string',  
  193. 'validate_callback' => __CLASS__ . '::validate_alphanum',  
  194. ),  
  195. ) ); 
  196.  
  197. // Update any Jetpack module option or setting 
  198. register_rest_route( 'jetpack/v4', '/settings', array( 
  199. 'methods' => WP_REST_Server::EDITABLE,  
  200. 'callback' => array( $core_api_endpoint, 'process' ),  
  201. 'permission_callback' => array( $core_api_endpoint, 'can_request' ),  
  202. 'args' => self::get_updateable_parameters( 'any' ) 
  203. ) ); 
  204.  
  205. // Update a module 
  206. register_rest_route( 'jetpack/v4', '/settings/(?P<slug>[a-z\-]+)', array( 
  207. 'methods' => WP_REST_Server::EDITABLE,  
  208. 'callback' => array( $core_api_endpoint, 'process' ),  
  209. 'permission_callback' => array( $core_api_endpoint, 'can_request' ),  
  210. 'args' => self::get_updateable_parameters() 
  211. ) ); 
  212.  
  213. // Return all module settings 
  214. register_rest_route( 'jetpack/v4', '/settings/', array( 
  215. 'methods' => WP_REST_Server::READABLE,  
  216. 'callback' => array( $core_api_endpoint, 'process' ),  
  217. 'permission_callback' => array( $core_api_endpoint, 'can_request' ),  
  218. ) ); 
  219.  
  220. // Reset all Jetpack options 
  221. register_rest_route( 'jetpack/v4', '/options/(?P<options>[a-z\-]+)', array( 
  222. 'methods' => WP_REST_Server::EDITABLE,  
  223. 'callback' => __CLASS__ . '::reset_jetpack_options',  
  224. 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',  
  225. ) ); 
  226.  
  227. // Return current Jumpstart status 
  228. register_rest_route( 'jetpack/v4', '/jumpstart', array( 
  229. 'methods' => WP_REST_Server::READABLE,  
  230. 'callback' => __CLASS__ . '::jumpstart_status',  
  231. 'permission_callback' => __CLASS__ . '::update_settings_permission_check',  
  232. ) ); 
  233.  
  234. // Update Jumpstart 
  235. register_rest_route( 'jetpack/v4', '/jumpstart', array( 
  236. 'methods' => WP_REST_Server::EDITABLE,  
  237. 'callback' => __CLASS__ . '::jumpstart_toggle',  
  238. 'permission_callback' => __CLASS__ . '::manage_modules_permission_check',  
  239. 'args' => array( 
  240. 'active' => array( 
  241. 'required' => true,  
  242. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  243. ),  
  244. ),  
  245. ) ); 
  246.  
  247. // Updates: get number of plugin updates available 
  248. register_rest_route( 'jetpack/v4', '/updates/plugins', array( 
  249. 'methods' => WP_REST_Server::READABLE,  
  250. 'callback' => __CLASS__ . '::get_plugin_update_count',  
  251. 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',  
  252. ) ); 
  253.  
  254. // Dismiss Jetpack Notices 
  255. register_rest_route( 'jetpack/v4', '/notice/(?P<notice>[a-z\-_]+)', array( 
  256. 'methods' => WP_REST_Server::EDITABLE,  
  257. 'callback' => __CLASS__ . '::dismiss_notice',  
  258. 'permission_callback' => __CLASS__ . '::view_admin_page_permission_check',  
  259. ) ); 
  260.  
  261. // Plugins: get list of all plugins. 
  262. register_rest_route( 'jetpack/v4', '/plugins', array( 
  263. 'methods' => WP_REST_Server::READABLE,  
  264. 'callback' => __CLASS__ . '::get_plugins',  
  265. 'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',  
  266. ) ); 
  267.  
  268. // Plugins: check if the plugin is active. 
  269. register_rest_route( 'jetpack/v4', '/plugin/(?P<plugin>[a-z\/\.\-_]+)', array( 
  270. 'methods' => WP_REST_Server::READABLE,  
  271. 'callback' => __CLASS__ . '::get_plugin',  
  272. 'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',  
  273. ) ); 
  274.  
  275. /** 
  276. * Handles dismissing of Jetpack Notices 
  277. * @since 4.3.0 
  278. * @param WP_REST_Request $request The request sent to the WP REST API. 
  279. * @return array|wp-error 
  280. */ 
  281. public static function dismiss_notice( $request ) { 
  282. $notice = $request['notice']; 
  283.  
  284. if ( ! isset( $request['dismissed'] ) || $request['dismissed'] !== true ) { 
  285. return new WP_Error( 'invalid_param', esc_html__( 'Invalid parameter "dismissed".', 'jetpack' ), array( 'status' => 404 ) ); 
  286.  
  287. if ( isset( $notice ) && ! empty( $notice ) ) { 
  288. switch( $notice ) { 
  289. case 'feedback_dash_request': 
  290. case 'welcome': 
  291. $notices = get_option( 'jetpack_dismissed_notices', array() ); 
  292. $notices[ $notice ] = true; 
  293. update_option( 'jetpack_dismissed_notices', $notices ); 
  294. return rest_ensure_response( get_option( 'jetpack_dismissed_notices', array() ) ); 
  295.  
  296. default: 
  297. return new WP_Error( 'invalid_param', esc_html__( 'Invalid parameter "notice".', 'jetpack' ), array( 'status' => 404 ) ); 
  298.  
  299. return new WP_Error( 'required_param', esc_html__( 'Missing parameter "notice".', 'jetpack' ), array( 'status' => 404 ) ); 
  300.  
  301. /** 
  302. * Verify that the user can disconnect the site. 
  303. * @since 4.3.0 
  304. * @return bool|WP_Error True if user is able to disconnect the site. 
  305. */ 
  306. public static function disconnect_site_permission_callback() { 
  307. if ( current_user_can( 'jetpack_disconnect' ) ) { 
  308. return true; 
  309.  
  310. return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  311.  
  312.  
  313. /** 
  314. * Verify that the user can get a connect/link URL 
  315. * @since 4.3.0 
  316. * @return bool|WP_Error True if user is able to disconnect the site. 
  317. */ 
  318. public static function connect_url_permission_callback() { 
  319. if ( current_user_can( 'jetpack_connect_user' ) ) { 
  320. return true; 
  321.  
  322. return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  323.  
  324.  
  325. /** 
  326. * Verify that a user can get the data about the current user. 
  327. * Only those who can connect. 
  328. * @since 4.3.0 
  329. * @uses Jetpack::is_user_connected(); 
  330. * @return bool|WP_Error True if user is able to unlink. 
  331. */ 
  332. public static function get_user_connection_data_permission_callback() { 
  333. if ( current_user_can( 'jetpack_connect_user' ) ) { 
  334. return true; 
  335.  
  336. return new WP_Error( 'invalid_user_permission_user_connection_data', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  337.  
  338. /** 
  339. * Verify that a user can use the /connection/user endpoint. Has to be a registered user and be currently linked. 
  340. * @since 4.3.0 
  341. * @uses Jetpack::is_user_connected(); 
  342. * @return bool|WP_Error True if user is able to unlink. 
  343. */ 
  344. public static function unlink_user_permission_callback() { 
  345. if ( current_user_can( 'jetpack_connect_user' ) && Jetpack::is_user_connected( get_current_user_id() ) ) { 
  346. return true; 
  347.  
  348. return new WP_Error( 'invalid_user_permission_unlink_user', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  349.  
  350. /** 
  351. * Verify that user can manage Jetpack modules. 
  352. * @since 4.3.0 
  353. * @return bool Whether user has the capability 'jetpack_manage_modules'. 
  354. */ 
  355. public static function manage_modules_permission_check() { 
  356. if ( current_user_can( 'jetpack_manage_modules' ) ) { 
  357. return true; 
  358.  
  359. return new WP_Error( 'invalid_user_permission_manage_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  360.  
  361. /** 
  362. * Verify that user can update Jetpack modules. 
  363. * @since 4.3.0 
  364. * @return bool Whether user has the capability 'jetpack_configure_modules'. 
  365. */ 
  366. public static function configure_modules_permission_check() { 
  367. if ( current_user_can( 'jetpack_configure_modules' ) ) { 
  368. return true; 
  369.  
  370. return new WP_Error( 'invalid_user_permission_configure_modules', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  371.  
  372. /** 
  373. * Verify that user can view Jetpack admin page. 
  374. * @since 4.3.0 
  375. * @return bool Whether user has the capability 'jetpack_admin_page'. 
  376. */ 
  377. public static function view_admin_page_permission_check() { 
  378. if ( current_user_can( 'jetpack_admin_page' ) ) { 
  379. return true; 
  380.  
  381. return new WP_Error( 'invalid_user_permission_view_admin', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  382.  
  383. /** 
  384. * Verify that user can mitigate an identity crisis. 
  385. * @since 4.4.0 
  386. * @return bool Whether user has capability 'jetpack_disconnect'. 
  387. */ 
  388. public static function identity_crisis_mitigation_permission_check() { 
  389. if ( current_user_can( 'jetpack_disconnect' ) ) { 
  390. return true; 
  391.  
  392. return new WP_Error( 'invalid_user_permission_identity_crisis', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  393.  
  394. /** 
  395. * Verify that user can update Jetpack general settings. 
  396. * @since 4.3.0 
  397. * @return bool Whether user has the capability 'update_settings_permission_check'. 
  398. */ 
  399. public static function update_settings_permission_check() { 
  400. if ( current_user_can( 'jetpack_configure_modules' ) ) { 
  401. return true; 
  402.  
  403. return new WP_Error( 'invalid_user_permission_manage_settings', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  404.  
  405. /** 
  406. * Verify that user can view Jetpack admin page and can activate plugins. 
  407. * @since 4.3.0 
  408. * @return bool Whether user has the capability 'jetpack_admin_page' and 'activate_plugins'. 
  409. */ 
  410. public static function activate_plugins_permission_check() { 
  411. if ( current_user_can( 'jetpack_admin_page' ) && current_user_can( 'activate_plugins' ) ) { 
  412. return true; 
  413.  
  414. return new WP_Error( 'invalid_user_permission_activate_plugins', self::$user_permissions_error_msg, array( 'status' => self::rest_authorization_required_code() ) ); 
  415.  
  416. /** 
  417. * Contextual HTTP error code for authorization failure. 
  418. * Taken from rest_authorization_required_code() in WP-API plugin until is added to core. 
  419. * @see https://github.com/WP-API/WP-API/commit/7ba0ae6fe4f605d5ffe4ee85b1cd5f9fb46900a6 
  420. * @since 4.3.0 
  421. * @return int 
  422. */ 
  423. public static function rest_authorization_required_code() { 
  424. return is_user_logged_in() ? 403 : 401; 
  425.  
  426. /** 
  427. * Get connection status for this Jetpack site. 
  428. * @since 4.3.0 
  429. * @return bool True if site is connected 
  430. */ 
  431. public static function jetpack_connection_status() { 
  432. return rest_ensure_response( array( 
  433. 'isActive' => Jetpack::is_active(),  
  434. 'isStaging' => Jetpack::is_staging_site(),  
  435. 'devMode' => array( 
  436. 'isActive' => Jetpack::is_development_mode(),  
  437. 'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,  
  438. 'url' => site_url() && false === strpos( site_url(), '.' ),  
  439. 'filter' => apply_filters( 'jetpack_development_mode', false ),  
  440. ),  
  441. ); 
  442.  
  443. /** 
  444. * Disconnects Jetpack from the WordPress.com Servers 
  445. * @uses Jetpack::disconnect(); 
  446. * @since 4.3.0 
  447. * @param WP_REST_Request $request The request sent to the WP REST API. 
  448. * @return bool|WP_Error True if Jetpack successfully disconnected. 
  449. */ 
  450. public static function disconnect_site( $request ) { 
  451.  
  452. if ( ! isset( $request['isActive'] ) || $request['isActive'] !== false ) { 
  453. return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); 
  454.  
  455. if ( Jetpack::is_active() ) { 
  456. Jetpack::disconnect(); 
  457. return rest_ensure_response( array( 'code' => 'success' ) ); 
  458.  
  459. return new WP_Error( 'disconnect_failed', esc_html__( 'Was not able to disconnect the site. Please try again.', 'jetpack' ), array( 'status' => 400 ) ); 
  460.  
  461. /** 
  462. * Gets a new connect raw URL with fresh nonce. 
  463. * @uses Jetpack::disconnect(); 
  464. * @since 4.3.0 
  465. * @param WP_REST_Request $request The request sent to the WP REST API. 
  466. * @return string|WP_Error A raw URL if the connection URL could be built; error message otherwise. 
  467. */ 
  468. public static function build_connect_url() { 
  469. $url = Jetpack::init()->build_connect_url( true, false, false ); 
  470. if ( $url ) { 
  471. return rest_ensure_response( $url ); 
  472.  
  473. return new WP_Error( 'build_connect_url_failed', esc_html__( 'Unable to build the connect URL. Please reload the page and try again.', 'jetpack' ), array( 'status' => 400 ) ); 
  474.  
  475. /** 
  476. * Get miscellaneous user data related to the connection. Similar data available in old "My Jetpack". 
  477. * Information about the master/primary user. 
  478. * Information about the current user. 
  479. * @since 4.3.0 
  480. * @param WP_REST_Request $request The request sent to the WP REST API. 
  481. * @return object 
  482. */ 
  483. public static function get_user_connection_data() { 
  484. require_once( JETPACK__PLUGIN_DIR . '_inc/lib/admin-pages/class.jetpack-react-page.php' ); 
  485.  
  486. $response = array( 
  487. // 'othersLinked' => Jetpack::get_other_linked_admins(),  
  488. 'currentUser' => jetpack_current_user_data(),  
  489. ); 
  490. return rest_ensure_response( $response ); 
  491.  
  492. /** 
  493. * Returns the proper name for Jetpack Holiday Snow setting. 
  494. * When the REST route starts, the holiday-snow.php file where jetpack_holiday_snow_option_name() function is defined is not loaded,  
  495. * so where using this to replicate it and have the same functionality. 
  496. * @since 4.4.0 
  497. * @return string 
  498. */ 
  499. public static function holiday_snow_option_name() { 
  500. /** This filter is documented in modules/holiday-snow.php */ 
  501. return apply_filters( 'jetpack_holiday_snow_option_name', 'jetpack_holiday_snow_enabled' ); 
  502.  
  503. /** 
  504. * Update a single miscellaneous setting for this Jetpack installation, like Holiday Snow. 
  505. * @since 4.3.0 
  506. * @param WP_REST_Request $request The request sent to the WP REST API. 
  507. * @return object Jetpack miscellaneous settings. 
  508. */ 
  509. public static function update_setting( $request ) { 
  510. // Get parameters to update the module. 
  511. $param = $request->get_params(); 
  512.  
  513. // Exit if no parameters were passed. 
  514. if ( ! is_array( $param ) ) { 
  515. return new WP_Error( 'missing_setting', esc_html__( 'Missing setting.', 'jetpack' ), array( 'status' => 404 ) ); 
  516.  
  517. // Get option name and value. 
  518. $option = key( $param ); 
  519. $value = current( $param ); 
  520.  
  521. // Log success or not 
  522. $updated = false; 
  523.  
  524. switch ( $option ) { 
  525. case self::holiday_snow_option_name(): 
  526. $updated = update_option( $option, ( true == (bool) $value ) ? 'letitsnow' : '' ); 
  527. break; 
  528.  
  529. if ( $updated ) { 
  530. return rest_ensure_response( array( 
  531. 'code' => 'success',  
  532. 'message' => esc_html__( 'Setting updated.', 'jetpack' ),  
  533. 'value' => $value,  
  534. ) ); 
  535.  
  536. return new WP_Error( 'setting_not_updated', esc_html__( 'The setting was not updated.', 'jetpack' ), array( 'status' => 400 ) ); 
  537.  
  538. /** 
  539. * Unlinks current user from the WordPress.com Servers. 
  540. * @since 4.3.0 
  541. * @uses Jetpack::unlink_user 
  542. * @param WP_REST_Request $request The request sent to the WP REST API. 
  543. * @return bool|WP_Error True if user successfully unlinked. 
  544. */ 
  545. public static function unlink_user( $request ) { 
  546.  
  547. if ( ! isset( $request['linked'] ) || $request['linked'] !== false ) { 
  548. return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); 
  549.  
  550. if ( Jetpack::unlink_user() ) { 
  551. return rest_ensure_response( 
  552. array( 
  553. 'code' => 'success' 
  554. ); 
  555.  
  556. return new WP_Error( 'unlink_user_failed', esc_html__( 'Was not able to unlink the user. Please try again.', 'jetpack' ), array( 'status' => 400 ) ); 
  557.  
  558. /** 
  559. * Get site data, including for example, the site's current plan. 
  560. * @since 4.3.0 
  561. * @return array Array of Jetpack modules. 
  562. */ 
  563. public static function get_site_data() { 
  564.  
  565. if ( $site_id = Jetpack_Options::get_option( 'id' ) ) { 
  566.  
  567. $response = Jetpack_Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d', $site_id ) .'?force=wpcom', '1.1' ); 
  568.  
  569. if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { 
  570. return new WP_Error( 'site_data_fetch_failed', esc_html__( 'Failed fetching site data. Try again later.', 'jetpack' ), array( 'status' => 400 ) ); 
  571.  
  572. // Save plan details in the database for future use without API calls 
  573. $results = json_decode( $response['body'], true ); 
  574.  
  575. if ( is_array( $results ) && isset( $results['plan'] ) ) { 
  576. update_option( 'jetpack_active_plan', $results['plan'] ); 
  577.  
  578. return rest_ensure_response( array( 
  579. 'code' => 'success',  
  580. 'message' => esc_html__( 'Site data correctly received.', 'jetpack' ),  
  581. 'data' => wp_remote_retrieve_body( $response ),  
  582. ); 
  583.  
  584. return new WP_Error( 'site_id_missing', esc_html__( 'The ID of this site does not exist.', 'jetpack' ), array( 'status' => 404 ) ); 
  585.  
  586. /** 
  587. * Handles identity crisis mitigation, confirming safe mode for this site. 
  588. * @since 4.4.0 
  589. * @return bool | WP_Error True if option is properly set. 
  590. */ 
  591. public static function confirm_safe_mode() { 
  592. $updated = Jetpack_Options::update_option( 'safe_mode_confirmed', true ); 
  593. if ( $updated ) { 
  594. return rest_ensure_response( 
  595. array( 
  596. 'code' => 'success' 
  597. ); 
  598. return new WP_Error( 
  599. 'error_setting_jetpack_safe_mode',  
  600. esc_html__( 'Could not confirm safe mode.', 'jetpack' ),  
  601. array( 'status' => 500 ) 
  602. ); 
  603.  
  604. /** 
  605. * Handles identity crisis mitigation, migrating stats and subscribers from old url to this, new url. 
  606. * @since 4.4.0 
  607. * @return bool | WP_Error True if option is properly set. 
  608. */ 
  609. public static function migrate_stats_and_subscribers() { 
  610. if ( Jetpack_Options::get_option( 'sync_error_idc' ) && ! Jetpack_Options::delete_option( 'sync_error_idc' ) ) { 
  611. return new WP_Error( 
  612. 'error_deleting_sync_error_idc',  
  613. esc_html__( 'Could not delete sync error option.', 'jetpack' ),  
  614. array( 'status' => 500 ) 
  615. ); 
  616.  
  617. if ( Jetpack_Options::get_option( 'migrate_for_idc' ) || Jetpack_Options::update_option( 'migrate_for_idc', true ) ) { 
  618. return rest_ensure_response( 
  619. array( 
  620. 'code' => 'success' 
  621. ); 
  622. return new WP_Error( 
  623. 'error_setting_jetpack_migrate',  
  624. esc_html__( 'Could not confirm migration.', 'jetpack' ),  
  625. array( 'status' => 500 ) 
  626. ); 
  627.  
  628. /** 
  629. * This IDC resolution will disconnect the site and re-connect to a completely new 
  630. * and separate shadow site than the original. 
  631. * It will first will disconnect the site without phoning home as to not disturb the production site. 
  632. * It then builds a fresh connection URL and sends it back along with the response. 
  633. * @since 4.4.0 
  634. * @return bool|WP_Error 
  635. */ 
  636. public static function start_fresh_connection() { 
  637. // First clear the options / disconnect. 
  638. Jetpack::disconnect(); 
  639. return self::build_connect_url(); 
  640.  
  641. /** 
  642. * Reset Jetpack options 
  643. * @since 4.3.0 
  644. * @param WP_REST_Request $request { 
  645. * Array of parameters received by request. 
  646. * @type string $options Available options to reset are options|modules 
  647. * } 
  648. * @return bool|WP_Error True if options were reset. Otherwise, a WP_Error instance with the corresponding error. 
  649. */ 
  650. public static function reset_jetpack_options( $request ) { 
  651.  
  652. if ( ! isset( $request['reset'] ) || $request['reset'] !== true ) { 
  653. return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); 
  654.  
  655. if ( isset( $request['options'] ) ) { 
  656. $data = $request['options']; 
  657.  
  658. switch( $data ) { 
  659. case ( 'options' ) : 
  660. $options_to_reset = Jetpack::get_jetpack_options_for_reset(); 
  661.  
  662. // Reset the Jetpack options 
  663. foreach ( $options_to_reset['jp_options'] as $option_to_reset ) { 
  664. Jetpack_Options::delete_option( $option_to_reset ); 
  665.  
  666. foreach ( $options_to_reset['wp_options'] as $option_to_reset ) { 
  667. delete_option( $option_to_reset ); 
  668.  
  669. // Reset to default modules 
  670. $default_modules = Jetpack::get_default_modules(); 
  671. Jetpack::update_active_modules( $default_modules ); 
  672.  
  673. // Jumpstart option is special 
  674. Jetpack_Options::update_option( 'jumpstart', 'new_connection' ); 
  675. return rest_ensure_response( array( 
  676. 'code' => 'success',  
  677. 'message' => esc_html__( 'Jetpack options reset.', 'jetpack' ),  
  678. ) ); 
  679. break; 
  680.  
  681. case 'modules': 
  682. $default_modules = Jetpack::get_default_modules(); 
  683. Jetpack::update_active_modules( $default_modules ); 
  684. return rest_ensure_response( array( 
  685. 'code' => 'success',  
  686. 'message' => esc_html__( 'Modules reset to default.', 'jetpack' ),  
  687. ) ); 
  688. break; 
  689.  
  690. default: 
  691. return new WP_Error( 'invalid_param', esc_html__( 'Invalid Parameter', 'jetpack' ), array( 'status' => 404 ) ); 
  692.  
  693. return new WP_Error( 'required_param', esc_html__( 'Missing parameter "type".', 'jetpack' ), array( 'status' => 404 ) ); 
  694.  
  695. /** 
  696. * Retrieves the current status of Jumpstart. 
  697. * @since 4.5.0 
  698. * @return bool 
  699. */ 
  700. public static function jumpstart_status() { 
  701. return array( 
  702. 'status' => Jetpack_Options::get_option( 'jumpstart' ) 
  703. ); 
  704.  
  705. /** 
  706. * Toggles activation or deactivation of the JumpStart 
  707. * @since 4.3.0 
  708. * @param WP_REST_Request $request The request sent to the WP REST API. 
  709. * @return bool|WP_Error True if toggling Jumpstart succeeded. Otherwise, a WP_Error instance with the corresponding error. 
  710. */ 
  711. public static function jumpstart_toggle( $request ) { 
  712.  
  713. if ( $request[ 'active' ] ) { 
  714. return self::jumpstart_activate( $request ); 
  715. } else { 
  716. return self::jumpstart_deactivate( $request ); 
  717.  
  718. /** 
  719. * Activates a series of valid Jetpack modules and initializes some options. 
  720. * @since 4.3.0 
  721. * @param WP_REST_Request $request The request sent to the WP REST API. 
  722. * @return bool|WP_Error True if Jumpstart succeeded. Otherwise, a WP_Error instance with the corresponding error. 
  723. */ 
  724. public static function jumpstart_activate( $request ) { 
  725. $modules = Jetpack::get_available_modules(); 
  726. $activate_modules = array(); 
  727. foreach ( $modules as $module ) { 
  728. $module_info = Jetpack::get_module( $module ); 
  729. if ( isset( $module_info['feature'] ) && is_array( $module_info['feature'] ) && in_array( 'Jumpstart', $module_info['feature'] ) ) { 
  730. $activate_modules[] = $module; 
  731.  
  732. // Collect success/error messages like modules that are properly activated. 
  733. $result = array( 
  734. 'activated_modules' => array(),  
  735. 'failed_modules' => array(),  
  736. ); 
  737.  
  738. // Update the jumpstart option 
  739. if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) { 
  740. $result['jumpstart_activated'] = Jetpack_Options::update_option( 'jumpstart', 'jumpstart_activated' ); 
  741.  
  742. // Check for possible conflicting plugins 
  743. $module_slugs_filtered = Jetpack::init()->filter_default_modules( $activate_modules ); 
  744.  
  745. foreach ( $module_slugs_filtered as $module_slug ) { 
  746. Jetpack::log( 'activate', $module_slug ); 
  747. if ( Jetpack::activate_module( $module_slug, false, false ) ) { 
  748. $result['activated_modules'][] = $module_slug; 
  749. } else { 
  750. $result['failed_modules'][] = $module_slug; 
  751.  
  752. // Set the default sharing buttons and set to display on posts if none have been set. 
  753. $sharing_services = get_option( 'sharing-services' ); 
  754. $sharing_options = get_option( 'sharing-options' ); 
  755. if ( empty( $sharing_services['visible'] ) ) { 
  756. // Default buttons to set 
  757. $visible = array( 
  758. 'twitter',  
  759. 'facebook',  
  760. 'google-plus-1',  
  761. ); 
  762. $hidden = array(); 
  763.  
  764. // Set some sharing settings 
  765. if ( class_exists( 'Sharing_Service' ) ) { 
  766. $sharing = new Sharing_Service(); 
  767. $sharing_options['global'] = array( 
  768. 'button_style' => 'icon',  
  769. 'sharing_label' => $sharing->default_sharing_label,  
  770. 'open_links' => 'same',  
  771. 'show' => array( 'post' ),  
  772. 'custom' => isset( $sharing_options['global']['custom'] ) ? $sharing_options['global']['custom'] : array() 
  773. ); 
  774.  
  775. $result['sharing_options'] = update_option( 'sharing-options', $sharing_options ); 
  776. $result['sharing_services'] = update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) ); 
  777.  
  778. // If all Jumpstart modules were activated 
  779. if ( empty( $result['failed_modules'] ) ) { 
  780. return rest_ensure_response( array( 
  781. 'code' => 'success',  
  782. 'message' => esc_html__( 'Jumpstart done.', 'jetpack' ),  
  783. 'data' => $result,  
  784. ) ); 
  785.  
  786. return new WP_Error( 'jumpstart_failed', esc_html( sprintf( _n( 'Jumpstart failed activating this module: %s.', 'Jumpstart failed activating these modules: %s.', count( $result['failed_modules'] ), 'jetpack' ), join( ', ', $result['failed_modules'] ) ) ), array( 'status' => 400 ) ); 
  787.  
  788. /** 
  789. * Dismisses Jumpstart so user is not prompted to go through it again. 
  790. * @since 4.3.0 
  791. * @param WP_REST_Request $request The request sent to the WP REST API. 
  792. * @return bool|WP_Error True if Jumpstart was disabled or was nothing to dismiss. Otherwise, a WP_Error instance with a message. 
  793. */ 
  794. public static function jumpstart_deactivate( $request ) { 
  795.  
  796. // If dismissed, flag the jumpstart option as such. 
  797. if ( 'new_connection' === Jetpack_Options::get_option( 'jumpstart' ) ) { 
  798. if ( Jetpack_Options::update_option( 'jumpstart', 'jumpstart_dismissed' ) ) { 
  799. return rest_ensure_response( array( 
  800. 'code' => 'success',  
  801. 'message' => esc_html__( 'Jumpstart dismissed.', 'jetpack' ),  
  802. ) ); 
  803. } else { 
  804. return new WP_Error( 'jumpstart_failed_dismiss', esc_html__( 'Jumpstart could not be dismissed.', 'jetpack' ), array( 'status' => 400 ) ); 
  805.  
  806. // If this was not a new connection and there was nothing to dismiss, don't fail. 
  807. return rest_ensure_response( array( 
  808. 'code' => 'success',  
  809. 'message' => esc_html__( 'Nothing to dismiss. This was not a new connection.', 'jetpack' ),  
  810. ) ); 
  811.  
  812. /** 
  813. * Get the query parameters to update module options or general settings. 
  814. * @since 4.3.0 
  815. * @since 4.4.0 Accepts a $selector parameter. 
  816. * @param string $selector Selects a set of options to update, Can be empty, a module slug or 'any'. 
  817. * @return array 
  818. */ 
  819. public static function get_updateable_parameters( $selector = '' ) { 
  820. $parameters = array( 
  821. 'context' => array( 
  822. 'default' => 'edit',  
  823. ),  
  824. ); 
  825.  
  826. return array_merge( $parameters, self::get_updateable_data_list( $selector ) ); 
  827.  
  828. /** 
  829. * Returns a list of module options or general settings that can be updated. 
  830. * @since 4.3.0 
  831. * @since 4.4.0 Accepts 'any' as a parameter which will make it return the entire list. 
  832. * @param string|array $selector Module slug, 'any', or an array of parameters. 
  833. * If empty, it's assumed we're updating a module and we'll try to get its slug. 
  834. * If 'any' the full list is returned. 
  835. * If it's an array of parameters, includes the elements by matching keys. 
  836. * @return array 
  837. */ 
  838. public static function get_updateable_data_list( $selector = '' ) { 
  839.  
  840. $options = array( 
  841.  
  842. // Carousel 
  843. 'carousel_background_color' => array( 
  844. 'description' => esc_html__( 'Color scheme.', 'jetpack' ),  
  845. 'type' => 'string',  
  846. 'default' => 'black',  
  847. 'enum' => array( 
  848. 'black',  
  849. 'white',  
  850. ),  
  851. 'enum_labels' => array( 
  852. 'black' => esc_html__( 'Black', 'jetpack' ),  
  853. 'white' => esc_html__( 'White', 'jetpack' ),  
  854. ),  
  855. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  856. 'jp_group' => 'carousel',  
  857. ),  
  858. 'carousel_display_exif' => array( 
  859. 'description' => wp_kses( sprintf( __( 'Show photo metadata (<a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format" target="_blank">Exif</a>) in carousel, when available.', 'jetpack' ) ), array( 'a' => array( 'href' => true, 'target' => true ) ) ),  
  860. 'type' => 'boolean',  
  861. 'default' => 0,  
  862. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  863. 'jp_group' => 'carousel',  
  864. ),  
  865.  
  866. // Comments 
  867. 'highlander_comment_form_prompt' => array( 
  868. 'description' => esc_html__( 'Greeting Text', 'jetpack' ),  
  869. 'type' => 'string',  
  870. 'default' => esc_html__( 'Leave a Reply', 'jetpack' ),  
  871. 'sanitize_callback' => 'sanitize_text_field',  
  872. 'jp_group' => 'comments',  
  873. ),  
  874. 'jetpack_comment_form_color_scheme' => array( 
  875. 'description' => esc_html__( "Color scheme", 'jetpack' ),  
  876. 'type' => 'string',  
  877. 'default' => 'light',  
  878. 'enum' => array( 
  879. 'light',  
  880. 'dark',  
  881. 'transparent',  
  882. ),  
  883. 'enum_labels' => array( 
  884. 'light' => esc_html__( 'Light', 'jetpack' ),  
  885. 'dark' => esc_html__( 'Dark', 'jetpack' ),  
  886. 'transparent' => esc_html__( 'Transparent', 'jetpack' ),  
  887. ),  
  888. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  889. 'jp_group' => 'comments',  
  890. ),  
  891.  
  892. // Custom Content Types 
  893. 'jetpack_portfolio' => array( 
  894. 'description' => esc_html__( 'Enable or disable Jetpack portfolio post type.', 'jetpack' ),  
  895. 'type' => 'boolean',  
  896. 'default' => 0,  
  897. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  898. 'jp_group' => 'custom-content-types',  
  899. ),  
  900. 'jetpack_portfolio_posts_per_page' => array( 
  901. 'description' => esc_html__( 'Number of entries to show at most in Portfolio pages.', 'jetpack' ),  
  902. 'type' => 'integer',  
  903. 'default' => 10,  
  904. 'validate_callback' => __CLASS__ . '::validate_posint',  
  905. 'jp_group' => 'custom-content-types',  
  906. ),  
  907. 'jetpack_testimonial' => array( 
  908. 'description' => esc_html__( 'Enable or disable Jetpack testimonial post type.', 'jetpack' ),  
  909. 'type' => 'boolean',  
  910. 'default' => 0,  
  911. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  912. 'jp_group' => 'custom-content-types',  
  913. ),  
  914. 'jetpack_testimonial_posts_per_page' => array( 
  915. 'description' => esc_html__( 'Number of entries to show at most in Testimonial pages.', 'jetpack' ),  
  916. 'type' => 'integer',  
  917. 'default' => 10,  
  918. 'validate_callback' => __CLASS__ . '::validate_posint',  
  919. 'jp_group' => 'custom-content-types',  
  920. ),  
  921.  
  922. // Galleries 
  923. 'tiled_galleries' => array( 
  924. 'description' => esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ),  
  925. 'type' => 'boolean',  
  926. 'default' => 0,  
  927. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  928. 'jp_group' => 'tiled-gallery',  
  929. ),  
  930.  
  931. 'gravatar_disable_hovercards' => array( 
  932. 'description' => esc_html__( "View people's profiles when you mouse over their Gravatars", 'jetpack' ),  
  933. 'type' => 'string',  
  934. 'default' => 'enabled',  
  935. // Not visible. This is used as the checkbox value. 
  936. 'enum' => array( 
  937. 'enabled',  
  938. 'disabled',  
  939. ),  
  940. 'enum_labels' => array( 
  941. 'enabled' => esc_html__( 'Enabled', 'jetpack' ),  
  942. 'disabled' => esc_html__( 'Disabled', 'jetpack' ),  
  943. ),  
  944. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  945. 'jp_group' => 'gravatar-hovercards',  
  946. ),  
  947.  
  948. // Infinite Scroll 
  949. 'infinite_scroll' => array( 
  950. 'description' => esc_html__( 'To infinity and beyond', 'jetpack' ),  
  951. 'type' => 'boolean',  
  952. 'default' => 1,  
  953. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  954. 'jp_group' => 'infinite-scroll',  
  955. ),  
  956. 'infinite_scroll_google_analytics' => array( 
  957. 'description' => esc_html__( 'Use Google Analytics with Infinite Scroll', 'jetpack' ),  
  958. 'type' => 'boolean',  
  959. 'default' => 0,  
  960. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  961. 'jp_group' => 'infinite-scroll',  
  962. ),  
  963.  
  964. // Likes 
  965. 'wpl_default' => array( 
  966. 'description' => esc_html__( 'WordPress.com Likes are', 'jetpack' ),  
  967. 'type' => 'string',  
  968. 'default' => 'on',  
  969. 'enum' => array( 
  970. 'on',  
  971. 'off',  
  972. ),  
  973. 'enum_labels' => array( 
  974. 'on' => esc_html__( 'On for all posts', 'jetpack' ),  
  975. 'off' => esc_html__( 'Turned on per post', 'jetpack' ),  
  976. ),  
  977. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  978. 'jp_group' => 'likes',  
  979. ),  
  980. 'social_notifications_like' => array( 
  981. 'description' => esc_html__( 'Send email notification when someone likes a post', 'jetpack' ),  
  982. 'type' => 'boolean',  
  983. 'default' => 1,  
  984. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  985. 'jp_group' => 'likes',  
  986. ),  
  987.  
  988. // Markdown 
  989. 'wpcom_publish_comments_with_markdown' => array( 
  990. 'description' => esc_html__( 'Use Markdown for comments.', 'jetpack' ),  
  991. 'type' => 'boolean',  
  992. 'default' => 0,  
  993. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  994. 'jp_group' => 'markdown',  
  995. ),  
  996. 'wpcom_publish_posts_with_markdown' => array( 
  997. 'description' => esc_html__( 'Use Markdown for posts.', 'jetpack' ),  
  998. 'type' => 'boolean',  
  999. 'default' => 0,  
  1000. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1001. 'jp_group' => 'markdown',  
  1002. ),  
  1003.  
  1004. // Mobile Theme 
  1005. 'wp_mobile_excerpt' => array( 
  1006. 'description' => esc_html__( 'Excerpts', 'jetpack' ),  
  1007. 'type' => 'boolean',  
  1008. 'default' => 0,  
  1009. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1010. 'jp_group' => 'minileven',  
  1011. ),  
  1012. 'wp_mobile_featured_images' => array( 
  1013. 'description' => esc_html__( 'Featured Images', 'jetpack' ),  
  1014. 'type' => 'boolean',  
  1015. 'default' => 0,  
  1016. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1017. 'jp_group' => 'minileven',  
  1018. ),  
  1019. 'wp_mobile_app_promos' => array( 
  1020. 'description' => esc_html__( 'Show a promo for the WordPress mobile apps in the footer of the mobile theme.', 'jetpack' ),  
  1021. 'type' => 'boolean',  
  1022. 'default' => 0,  
  1023. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1024. 'jp_group' => 'minileven',  
  1025. ),  
  1026.  
  1027. // Monitor 
  1028. 'monitor_receive_notifications' => array( 
  1029. 'description' => esc_html__( 'Receive Monitor Email Notifications.', 'jetpack' ),  
  1030. 'type' => 'boolean',  
  1031. 'default' => 0,  
  1032. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1033. 'jp_group' => 'monitor',  
  1034. ),  
  1035.  
  1036. // Post by Email 
  1037. 'post_by_email_address' => array( 
  1038. 'description' => esc_html__( 'Email Address', 'jetpack' ),  
  1039. 'type' => 'string',  
  1040. 'default' => 'noop',  
  1041. 'enum' => array( 
  1042. 'noop',  
  1043. 'create',  
  1044. 'regenerate',  
  1045. 'delete',  
  1046. ),  
  1047. 'enum_labels' => array( 
  1048. 'noop' => '',  
  1049. 'create' => esc_html__( 'Create Post by Email address', 'jetpack' ),  
  1050. 'regenerate' => esc_html__( 'Regenerate Post by Email address', 'jetpack' ),  
  1051. 'delete' => esc_html__( 'Delete Post by Email address', 'jetpack' ),  
  1052. ),  
  1053. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  1054. 'jp_group' => 'post-by-email',  
  1055. ),  
  1056.  
  1057. // Protect 
  1058. 'jetpack_protect_key' => array( 
  1059. 'description' => esc_html__( 'Protect API key', 'jetpack' ),  
  1060. 'type' => 'string',  
  1061. 'default' => '',  
  1062. 'validate_callback' => __CLASS__ . '::validate_alphanum',  
  1063. 'jp_group' => 'protect',  
  1064. ),  
  1065. 'jetpack_protect_global_whitelist' => array( 
  1066. 'description' => esc_html__( 'Protect global whitelist', 'jetpack' ),  
  1067. 'type' => 'string',  
  1068. 'default' => '',  
  1069. 'validate_callback' => __CLASS__ . '::validate_string',  
  1070. 'sanitize_callback' => 'esc_textarea',  
  1071. 'jp_group' => 'protect',  
  1072. ),  
  1073.  
  1074. // Sharing 
  1075. 'sharing_services' => array( 
  1076. 'description' => esc_html__( 'Enabled Services and those hidden behind a button', 'jetpack' ),  
  1077. 'type' => 'object',  
  1078. 'default' => array( 
  1079. 'visible' => array( 'twitter', 'facebook', 'google-plus-1' ),  
  1080. 'hidden' => array(),  
  1081. ),  
  1082. 'validate_callback' => __CLASS__ . '::validate_services',  
  1083. 'jp_group' => 'sharedaddy',  
  1084. ),  
  1085. 'button_style' => array( 
  1086. 'description' => esc_html__( 'Button Style', 'jetpack' ),  
  1087. 'type' => 'string',  
  1088. 'default' => 'icon',  
  1089. 'enum' => array( 
  1090. 'icon-text',  
  1091. 'icon',  
  1092. 'text',  
  1093. 'official',  
  1094. ),  
  1095. 'enum_labels' => array( 
  1096. 'icon-text' => esc_html__( 'Icon + text', 'jetpack' ),  
  1097. 'icon' => esc_html__( 'Icon only', 'jetpack' ),  
  1098. 'text' => esc_html__( 'Text only', 'jetpack' ),  
  1099. 'official' => esc_html__( 'Official buttons', 'jetpack' ),  
  1100. ),  
  1101. 'validate_callback' => __CLASS__ . '::validate_list_item',  
  1102. 'jp_group' => 'sharedaddy',  
  1103. ),  
  1104. 'sharing_label' => array( 
  1105. 'description' => esc_html__( 'Sharing Label', 'jetpack' ),  
  1106. 'type' => 'string',  
  1107. 'default' => '',  
  1108. 'validate_callback' => __CLASS__ . '::validate_string',  
  1109. 'sanitize_callback' => 'esc_html',  
  1110. 'jp_group' => 'sharedaddy',  
  1111. ),  
  1112. 'show' => array( 
  1113. 'description' => esc_html__( 'Views where buttons are shown', 'jetpack' ),  
  1114. 'type' => 'array',  
  1115. 'items' => array( 
  1116. 'type' => 'string' 
  1117. ),  
  1118. 'default' => array( 'post' ),  
  1119. 'validate_callback' => __CLASS__ . '::validate_sharing_show',  
  1120. 'jp_group' => 'sharedaddy',  
  1121. ),  
  1122. 'jetpack-twitter-cards-site-tag' => array( 
  1123. 'description' => esc_html__( "The Twitter username of the owner of this site's domain.", 'jetpack' ),  
  1124. 'type' => 'string',  
  1125. 'default' => '',  
  1126. 'validate_callback' => __CLASS__ . '::validate_twitter_username',  
  1127. 'sanitize_callback' => 'esc_html',  
  1128. 'jp_group' => 'sharedaddy',  
  1129. ),  
  1130. 'sharedaddy_disable_resources' => array( 
  1131. 'description' => esc_html__( 'Disable CSS and JS', 'jetpack' ),  
  1132. 'type' => 'boolean',  
  1133. 'default' => 0,  
  1134. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1135. 'jp_group' => 'sharedaddy',  
  1136. ),  
  1137. 'custom' => array( 
  1138. 'description' => esc_html__( 'Custom sharing services added by user.', 'jetpack' ),  
  1139. 'type' => 'object',  
  1140. 'default' => array( 
  1141. 'sharing_name' => '',  
  1142. 'sharing_url' => '',  
  1143. 'sharing_icon' => '',  
  1144. ),  
  1145. 'validate_callback' => __CLASS__ . '::validate_custom_service',  
  1146. 'jp_group' => 'sharedaddy',  
  1147. ),  
  1148. // Not an option, but an action that can be perfomed on the list of custom services passing the service ID. 
  1149. 'sharing_delete_service' => array( 
  1150. 'description' => esc_html__( 'Delete custom sharing service.', 'jetpack' ),  
  1151. 'type' => 'string',  
  1152. 'default' => '',  
  1153. 'validate_callback' => __CLASS__ . '::validate_custom_service_id',  
  1154. 'jp_group' => 'sharedaddy',  
  1155. ),  
  1156.  
  1157. // SSO 
  1158. 'jetpack_sso_require_two_step' => array( 
  1159. 'description' => esc_html__( 'Require Two-Step Authentication', 'jetpack' ),  
  1160. 'type' => 'boolean',  
  1161. 'default' => 0,  
  1162. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1163. 'jp_group' => 'sso',  
  1164. ),  
  1165. 'jetpack_sso_match_by_email' => array( 
  1166. 'description' => esc_html__( 'Match by Email', 'jetpack' ),  
  1167. 'type' => 'boolean',  
  1168. 'default' => 0,  
  1169. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1170. 'jp_group' => 'sso',  
  1171. ),  
  1172.  
  1173. // Subscriptions 
  1174. 'stb_enabled' => array( 
  1175. 'description' => esc_html__( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ),  
  1176. 'type' => 'boolean',  
  1177. 'default' => 1,  
  1178. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1179. 'jp_group' => 'subscriptions',  
  1180. ),  
  1181. 'stc_enabled' => array( 
  1182. 'description' => esc_html__( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ),  
  1183. 'type' => 'boolean',  
  1184. 'default' => 1,  
  1185. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1186. 'jp_group' => 'subscriptions',  
  1187. ),  
  1188.  
  1189. // Related Posts 
  1190. 'show_headline' => array( 
  1191. 'description' => esc_html__( 'Show a "Related" header to more clearly separate the related section from posts', 'jetpack' ),  
  1192. 'type' => 'boolean',  
  1193. 'default' => 1,  
  1194. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1195. 'jp_group' => 'related-posts',  
  1196. ),  
  1197. 'show_thumbnails' => array( 
  1198. 'description' => esc_html__( 'Use a large and visually striking layout', 'jetpack' ),  
  1199. 'type' => 'boolean',  
  1200. 'default' => 0,  
  1201. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1202. 'jp_group' => 'related-posts',  
  1203. ),  
  1204.  
  1205. // Spelling and Grammar - After the Deadline 
  1206. 'onpublish' => array( 
  1207. 'description' => esc_html__( 'Proofread when a post or page is first published.', 'jetpack' ),  
  1208. 'type' => 'boolean',  
  1209. 'default' => 0,  
  1210. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1211. 'jp_group' => 'after-the-deadline',  
  1212. ),  
  1213. 'onupdate' => array( 
  1214. 'description' => esc_html__( 'Proofread when a post or page is updated.', 'jetpack' ),  
  1215. 'type' => 'boolean',  
  1216. 'default' => 0,  
  1217. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1218. 'jp_group' => 'after-the-deadline',  
  1219. ),  
  1220. 'Bias Language' => array( 
  1221. 'description' => esc_html__( 'Bias Language', 'jetpack' ),  
  1222. 'type' => 'boolean',  
  1223. 'default' => 0,  
  1224. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1225. 'jp_group' => 'after-the-deadline',  
  1226. ),  
  1227. 'Cliches' => array( 
  1228. 'description' => esc_html__( 'Clichs', 'jetpack' ),  
  1229. 'type' => 'boolean',  
  1230. 'default' => 0,  
  1231. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1232. 'jp_group' => 'after-the-deadline',  
  1233. ),  
  1234. 'Complex Expression' => array( 
  1235. 'description' => esc_html__( 'Complex Phrases', 'jetpack' ),  
  1236. 'type' => 'boolean',  
  1237. 'default' => 0,  
  1238. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1239. 'jp_group' => 'after-the-deadline',  
  1240. ),  
  1241. 'Diacritical Marks' => array( 
  1242. 'description' => esc_html__( 'Diacritical Marks', 'jetpack' ),  
  1243. 'type' => 'boolean',  
  1244. 'default' => 0,  
  1245. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1246. 'jp_group' => 'after-the-deadline',  
  1247. ),  
  1248. 'Double Negative' => array( 
  1249. 'description' => esc_html__( 'Double Negatives', 'jetpack' ),  
  1250. 'type' => 'boolean',  
  1251. 'default' => 0,  
  1252. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1253. 'jp_group' => 'after-the-deadline',  
  1254. ),  
  1255. 'Hidden Verbs' => array( 
  1256. 'description' => esc_html__( 'Hidden Verbs', 'jetpack' ),  
  1257. 'type' => 'boolean',  
  1258. 'default' => 0,  
  1259. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1260. 'jp_group' => 'after-the-deadline',  
  1261. ),  
  1262. 'Jargon Language' => array( 
  1263. 'description' => esc_html__( 'Jargon', 'jetpack' ),  
  1264. 'type' => 'boolean',  
  1265. 'default' => 0,  
  1266. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1267. 'jp_group' => 'after-the-deadline',  
  1268. ),  
  1269. 'Passive voice' => array( 
  1270. 'description' => esc_html__( 'Passive Voice', 'jetpack' ),  
  1271. 'type' => 'boolean',  
  1272. 'default' => 0,  
  1273. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1274. 'jp_group' => 'after-the-deadline',  
  1275. ),  
  1276. 'Phrases to Avoid' => array( 
  1277. 'description' => esc_html__( 'Phrases to Avoid', 'jetpack' ),  
  1278. 'type' => 'boolean',  
  1279. 'default' => 0,  
  1280. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1281. 'jp_group' => 'after-the-deadline',  
  1282. ),  
  1283. 'Redundant Expression' => array( 
  1284. 'description' => esc_html__( 'Redundant Phrases', 'jetpack' ),  
  1285. 'type' => 'boolean',  
  1286. 'default' => 0,  
  1287. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1288. 'jp_group' => 'after-the-deadline',  
  1289. ),  
  1290. 'guess_lang' => array( 
  1291. 'description' => esc_html__( 'Use automatically detected language to proofread posts and pages', 'jetpack' ),  
  1292. 'type' => 'boolean',  
  1293. 'default' => 0,  
  1294. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1295. 'jp_group' => 'after-the-deadline',  
  1296. ),  
  1297. 'ignored_phrases' => array( 
  1298. 'description' => esc_html__( 'Add Phrase to be ignored', 'jetpack' ),  
  1299. 'type' => 'string',  
  1300. 'default' => '',  
  1301. 'sanitize_callback' => 'esc_html',  
  1302. 'jp_group' => 'after-the-deadline',  
  1303. ),  
  1304. 'unignore_phrase' => array( 
  1305. 'description' => esc_html__( 'Remove Phrase from being ignored', 'jetpack' ),  
  1306. 'type' => 'string',  
  1307. 'default' => '',  
  1308. 'sanitize_callback' => 'esc_html',  
  1309. 'jp_group' => 'after-the-deadline',  
  1310. ),  
  1311.  
  1312. // Verification Tools 
  1313. 'google' => array( 
  1314. 'description' => esc_html__( 'Google Search Console', 'jetpack' ),  
  1315. 'type' => 'string',  
  1316. 'default' => '',  
  1317. 'validate_callback' => __CLASS__ . '::validate_verification_service',  
  1318. 'jp_group' => 'verification-tools',  
  1319. ),  
  1320. 'bing' => array( 
  1321. 'description' => esc_html__( 'Bing Webmaster Center', 'jetpack' ),  
  1322. 'type' => 'string',  
  1323. 'default' => '',  
  1324. 'validate_callback' => __CLASS__ . '::validate_verification_service',  
  1325. 'jp_group' => 'verification-tools',  
  1326. ),  
  1327. 'pinterest' => array( 
  1328. 'description' => esc_html__( 'Pinterest Site Verification', 'jetpack' ),  
  1329. 'type' => 'string',  
  1330. 'default' => '',  
  1331. 'validate_callback' => __CLASS__ . '::validate_verification_service',  
  1332. 'jp_group' => 'verification-tools',  
  1333. ),  
  1334. 'yandex' => array( 
  1335. 'description' => esc_html__( 'Yandex Site Verification', 'jetpack' ),  
  1336. 'type' => 'string',  
  1337. 'default' => '',  
  1338. 'validate_callback' => __CLASS__ . '::validate_verification_service',  
  1339. 'jp_group' => 'verification-tools',  
  1340. ),  
  1341. 'enable_header_ad' => array( 
  1342. 'description' => esc_html__( 'Display an ad unit at the top of each page.', 'jetpack' ),  
  1343. 'type' => 'boolean',  
  1344. 'default' => 0,  
  1345. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1346. 'jp_group' => 'wordads',  
  1347. ),  
  1348. 'wordads_approved' => array( 
  1349. 'description' => esc_html__( 'Is site approved for WordAds?', 'jetpack' ),  
  1350. 'type' => 'boolean',  
  1351. 'default' => 0,  
  1352. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1353. 'jp_group' => 'wordads',  
  1354. ),  
  1355.  
  1356. // Google Analytics 
  1357. 'google_analytics_tracking_id' => array( 
  1358. 'description' => esc_html__( 'Google Analytics', 'jetpack' ),  
  1359. 'type' => 'string',  
  1360. 'default' => '',  
  1361. 'validate_callback' => __CLASS__ . '::validate_alphanum',  
  1362. 'jp_group' => 'google-analytics',  
  1363. ),  
  1364.  
  1365. // Stats 
  1366. 'admin_bar' => array( 
  1367. 'description' => esc_html__( 'Put a chart showing 48 hours of views in the admin bar.', 'jetpack' ),  
  1368. 'type' => 'boolean',  
  1369. 'default' => 1,  
  1370. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1371. 'jp_group' => 'stats',  
  1372. ),  
  1373. 'roles' => array( 
  1374. 'description' => esc_html__( 'Select the roles that will be able to view stats reports.', 'jetpack' ),  
  1375. 'type' => 'array',  
  1376. 'items' => array( 
  1377. 'type' => 'string' 
  1378. ),  
  1379. 'default' => array( 'administrator' ),  
  1380. 'validate_callback' => __CLASS__ . '::validate_stats_roles',  
  1381. 'sanitize_callback' => __CLASS__ . '::sanitize_stats_allowed_roles',  
  1382. 'jp_group' => 'stats',  
  1383. ),  
  1384. 'count_roles' => array( 
  1385. 'description' => esc_html__( 'Count the page views of registered users who are logged in.', 'jetpack' ),  
  1386. 'type' => 'array',  
  1387. 'items' => array( 
  1388. 'type' => 'string' 
  1389. ),  
  1390. 'default' => array( 'administrator' ),  
  1391. 'validate_callback' => __CLASS__ . '::validate_stats_roles',  
  1392. 'jp_group' => 'stats',  
  1393. ),  
  1394. 'blog_id' => array( 
  1395. 'description' => esc_html__( 'Blog ID.', 'jetpack' ),  
  1396. 'type' => 'boolean',  
  1397. 'default' => 0,  
  1398. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1399. 'jp_group' => 'stats',  
  1400. ),  
  1401. 'do_not_track' => array( 
  1402. 'description' => esc_html__( 'Do not track.', 'jetpack' ),  
  1403. 'type' => 'boolean',  
  1404. 'default' => 1,  
  1405. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1406. 'jp_group' => 'stats',  
  1407. ),  
  1408. 'hide_smile' => array( 
  1409. 'description' => esc_html__( 'Hide the stats smiley face image.', 'jetpack' ),  
  1410. 'type' => 'boolean',  
  1411. 'default' => 1,  
  1412. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1413. 'jp_group' => 'stats',  
  1414. ),  
  1415. 'version' => array( 
  1416. 'description' => esc_html__( 'Version.', 'jetpack' ),  
  1417. 'type' => 'integer',  
  1418. 'default' => 9,  
  1419. 'validate_callback' => __CLASS__ . '::validate_posint',  
  1420. 'jp_group' => 'stats',  
  1421. ),  
  1422.  
  1423. // Settings - Not a module 
  1424. self::holiday_snow_option_name() => array( 
  1425. 'description' => '',  
  1426. 'type' => 'boolean',  
  1427. 'default' => 0,  
  1428. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1429. 'jp_group' => 'settings',  
  1430. ),  
  1431.  
  1432. // Akismet - Not a module, but a plugin. The options can be passed and handled differently. 
  1433. 'akismet_show_user_comments_approved' => array( 
  1434. 'description' => '',  
  1435. 'type' => 'boolean',  
  1436. 'default' => 0,  
  1437. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1438. 'jp_group' => 'settings',  
  1439. ),  
  1440.  
  1441. 'wordpress_api_key' => array( 
  1442. 'description' => '',  
  1443. 'type' => 'string',  
  1444. 'default' => '',  
  1445. 'validate_callback' => __CLASS__ . '::validate_alphanum',  
  1446. 'jp_group' => 'settings',  
  1447. ),  
  1448.  
  1449. // Apps card on dashboard 
  1450. 'dismiss_dash_app_card' => array( 
  1451. 'description' => '',  
  1452. 'type' => 'boolean',  
  1453. 'default' => 0,  
  1454. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1455. 'jp_group' => 'settings',  
  1456. ),  
  1457.  
  1458. ); 
  1459.  
  1460. // Add modules to list so they can be toggled 
  1461. $modules = Jetpack::get_available_modules(); 
  1462. if ( is_array( $modules ) && ! empty( $modules ) ) { 
  1463. $module_args = array( 
  1464. 'description' => '',  
  1465. 'type' => 'boolean',  
  1466. 'default' => 0,  
  1467. 'validate_callback' => __CLASS__ . '::validate_boolean',  
  1468. 'jp_group' => 'modules',  
  1469. ); 
  1470. foreach( $modules as $module ) { 
  1471. $options[ $module ] = $module_args; 
  1472.  
  1473. if ( is_array( $selector ) ) { 
  1474.  
  1475. // Return only those options whose keys match $selector keys 
  1476. return array_intersect_key( $options, $selector ); 
  1477.  
  1478. if ( 'any' === $selector ) { 
  1479.  
  1480. // Toggle module or update any module option or any general setting 
  1481. return $options; 
  1482.  
  1483. // We're updating the options for a single module. 
  1484. if ( empty( $selector ) ) { 
  1485. $selector = self::get_module_requested(); 
  1486. $selected = array(); 
  1487. foreach ( $options as $option => $attributes ) { 
  1488.  
  1489. // Not adding an isset( $attributes['jp_group'] ) because if it's not set, it must be fixed, otherwise options will fail. 
  1490. if ( $selector === $attributes['jp_group'] ) { 
  1491. $selected[ $option ] = $attributes; 
  1492. return $selected; 
  1493.  
  1494. /** 
  1495. * Validates that the parameter is either a pure boolean or a numeric string that can be mapped to a boolean. 
  1496. * @since 4.3.0 
  1497. * @param string|bool $value Value to check. 
  1498. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1499. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1500. * @return bool 
  1501. */ 
  1502. public static function validate_boolean( $value, $request, $param ) { 
  1503. if ( ! is_bool( $value ) && ! ( ( ctype_digit( $value ) || is_numeric( $value ) ) && in_array( $value, array( 0, 1 ) ) ) ) { 
  1504. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be true, false, 0 or 1.', 'jetpack' ), $param ) ); 
  1505. return true; 
  1506.  
  1507. /** 
  1508. * Validates that the parameter is a positive integer. 
  1509. * @since 4.3.0 
  1510. * @param int $value Value to check. 
  1511. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1512. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1513. * @return bool 
  1514. */ 
  1515. public static function validate_posint( $value = 0, $request, $param ) { 
  1516. if ( ! is_numeric( $value ) || $value <= 0 ) { 
  1517. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a positive integer.', 'jetpack' ), $param ) ); 
  1518. return true; 
  1519.  
  1520. /** 
  1521. * Validates that the parameter belongs to a list of admitted values. 
  1522. * @since 4.3.0 
  1523. * @param string $value Value to check. 
  1524. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1525. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1526. * @return bool 
  1527. */ 
  1528. public static function validate_list_item( $value = '', $request, $param ) { 
  1529. $attributes = $request->get_attributes(); 
  1530. if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) { 
  1531. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s not recognized', 'jetpack' ), $param ) ); 
  1532. $args = $attributes['args'][ $param ]; 
  1533. if ( ! empty( $args['enum'] ) ) { 
  1534.  
  1535. // If it's an associative array, use the keys to check that the value is among those admitted. 
  1536. $enum = ( count( array_filter( array_keys( $args['enum'] ), 'is_string' ) ) > 0 ) ? array_keys( $args['enum'] ) : $args['enum']; 
  1537. if ( ! in_array( $value, $enum ) ) { 
  1538. return new WP_Error( 'invalid_param_value', sprintf( 
  1539. /** Translators: first variable is the parameter passed to endpoint that holds the list item, the second is a list of admitted values. */ 
  1540. esc_html__( '%1$s must be one of %2$s', 'jetpack' ), $param, implode( ', ', $enum ) 
  1541. ) ); 
  1542. return true; 
  1543.  
  1544. /** 
  1545. * Validates that the parameter belongs to a list of admitted values. 
  1546. * @since 4.3.0 
  1547. * @param string $value Value to check. 
  1548. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1549. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1550. * @return bool 
  1551. */ 
  1552. public static function validate_module_list( $value = '', $request, $param ) { 
  1553. if ( ! is_array( $value ) ) { 
  1554. return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be an array', 'jetpack' ), $param ) ); 
  1555.  
  1556. $modules = Jetpack::get_available_modules(); 
  1557.  
  1558. if ( count( array_intersect( $value, $modules ) ) != count( $value ) ) { 
  1559. return new WP_Error( 'invalid_param_value', sprintf( esc_html__( '%s must be a list of valid modules', 'jetpack' ), $param ) ); 
  1560.  
  1561. return true; 
  1562.  
  1563. /** 
  1564. * Validates that the parameter is an alphanumeric or empty string (to be able to clear the field). 
  1565. * @since 4.3.0 
  1566. * @param string $value Value to check. 
  1567. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1568. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1569. * @return bool 
  1570. */ 
  1571. public static function validate_alphanum( $value = '', $request, $param ) { 
  1572. if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^[a-z0-9]+$/i', $value ) ) ) { 
  1573. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), $param ) ); 
  1574. return true; 
  1575.  
  1576. /** 
  1577. * Validates that the parameter is a tag or id for a verification service, or an empty string (to be able to clear the field). 
  1578. * @since 4.6.0 
  1579. * @param string $value Value to check. 
  1580. * @param WP_REST_Request $request 
  1581. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1582. * @return bool 
  1583. */ 
  1584. public static function validate_verification_service( $value = '', $request, $param ) { 
  1585. if ( ! empty( $value ) && ! ( is_string( $value ) && ( preg_match( '/^[a-z0-9_-]+$/i', $value ) || preg_match( '#^<meta name="([a-z0-9_\-.:]+)?" content="([a-z0-9_-]+)?" />$#i', $value ) ) ) ) { 
  1586. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string or a verification tag.', 'jetpack' ), $param ) ); 
  1587. return true; 
  1588.  
  1589. /** 
  1590. * Validates that the parameter is among the roles allowed for Stats. 
  1591. * @since 4.3.0 
  1592. * @param string|bool $value Value to check. 
  1593. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1594. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1595. * @return bool 
  1596. */ 
  1597. public static function validate_stats_roles( $value, $request, $param ) { 
  1598. if ( ! empty( $value ) && ! array_intersect( self::$stats_roles, $value ) ) { 
  1599. return new WP_Error( 'invalid_param', sprintf( 
  1600. /** 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. */ 
  1601. esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', self::$stats_roles ) 
  1602. ) ); 
  1603. return true; 
  1604.  
  1605. /** 
  1606. * Validates that the parameter is among the views where the Sharing can be displayed. 
  1607. * @since 4.3.0 
  1608. * @param string|bool $value Value to check. 
  1609. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1610. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1611. * @return bool 
  1612. */ 
  1613. public static function validate_sharing_show( $value, $request, $param ) { 
  1614. $views = array( 'index', 'post', 'page', 'attachment', 'jetpack-portfolio' ); 
  1615. if ( ! is_array( $value ) ) { 
  1616. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array of post types.', 'jetpack' ), $param ) ); 
  1617. if ( ! array_intersect( $views, $value ) ) { 
  1618. return new WP_Error( 'invalid_param', sprintf( 
  1619. /** 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 */ 
  1620. esc_html__( '%1$s must be %2$s.', 'jetpack' ), $param, join( ', ', $views ) 
  1621. ) ); 
  1622. return true; 
  1623.  
  1624. /** 
  1625. * Validates that the parameter is among the views where the Sharing can be displayed. 
  1626. * @since 4.3.0 
  1627. * @param string|bool $value { 
  1628. * Value to check received by request. 
  1629. * @type array $visible List of slug of services to share to that are displayed directly in the page. 
  1630. * @type array $hidden List of slug of services to share to that are concealed in a folding menu. 
  1631. * } 
  1632. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1633. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1634. * @return bool 
  1635. */ 
  1636. public static function validate_services( $value, $request, $param ) { 
  1637. if ( ! is_array( $value ) || ! isset( $value['visible'] ) || ! isset( $value['hidden'] ) ) { 
  1638. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with visible and hidden items.', 'jetpack' ), $param ) ); 
  1639.  
  1640. // Allow to clear everything. 
  1641. if ( empty( $value['visible'] ) && empty( $value['hidden'] ) ) { 
  1642. return true; 
  1643.  
  1644. if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) { 
  1645. return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) ); 
  1646. $sharer = new Sharing_Service(); 
  1647. $services = array_keys( $sharer->get_all_services() ); 
  1648.  
  1649. if ( 
  1650. ( ! empty( $value['visible'] ) && ! array_intersect( $value['visible'], $services ) ) 
  1651. || 
  1652. ( ! empty( $value['hidden'] ) && ! array_intersect( $value['hidden'], $services ) ) ) 
  1653. return new WP_Error( 'invalid_param', sprintf( 
  1654. /** Translators: placeholder 1 is a parameter holding the services passed to endpoint, placeholder 2 is a list of all Jetpack Sharing services */ 
  1655. esc_html__( '%1$s visible and hidden items must be a list of %2$s.', 'jetpack' ), $param, join( ', ', $services ) 
  1656. ) ); 
  1657. return true; 
  1658.  
  1659. /** 
  1660. * Validates that the parameter has enough information to build a custom sharing button. 
  1661. * @since 4.3.0 
  1662. * @param string|bool $value Value to check. 
  1663. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1664. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1665. * @return bool 
  1666. */ 
  1667. public static function validate_custom_service( $value, $request, $param ) { 
  1668. if ( ! is_array( $value ) || ! isset( $value['sharing_name'] ) || ! isset( $value['sharing_url'] ) || ! isset( $value['sharing_icon'] ) ) { 
  1669. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an array with sharing name, url and icon.', 'jetpack' ), $param ) ); 
  1670.  
  1671. // Allow to clear everything. 
  1672. if ( empty( $value['sharing_name'] ) && empty( $value['sharing_url'] ) && empty( $value['sharing_icon'] ) ) { 
  1673. return true; 
  1674.  
  1675. if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) { 
  1676. return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) ); 
  1677.  
  1678. if ( ( ! empty( $value['sharing_name'] ) && ! is_string( $value['sharing_name'] ) ) 
  1679. || ( ! empty( $value['sharing_url'] ) && ! is_string( $value['sharing_url'] ) ) 
  1680. || ( ! empty( $value['sharing_icon'] ) && ! is_string( $value['sharing_icon'] ) ) ) { 
  1681. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s needs sharing name, url and icon.', 'jetpack' ), $param ) ); 
  1682. return true; 
  1683.  
  1684. /** 
  1685. * Validates that the parameter is a custom sharing service ID like 'custom-1461976264'. 
  1686. * @since 4.3.0 
  1687. * @param string $value Value to check. 
  1688. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1689. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1690. * @return bool 
  1691. */ 
  1692. public static function validate_custom_service_id( $value = '', $request, $param ) { 
  1693. if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/custom\-[0-1]+/i', $value ) ) ) { 
  1694. 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 ) ); 
  1695.  
  1696. if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) { 
  1697. return new WP_Error( 'invalid_param', esc_html__( 'Failed loading required dependency Sharing_Service.', 'jetpack' ) ); 
  1698. $sharer = new Sharing_Service(); 
  1699. $services = array_keys( $sharer->get_all_services() ); 
  1700.  
  1701. if ( ! empty( $value ) && ! in_array( $value, $services ) ) { 
  1702. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s is not a registered custom sharing service.', 'jetpack' ), $param ) ); 
  1703.  
  1704. return true; 
  1705.  
  1706. /** 
  1707. * Validates that the parameter is a Twitter username or empty string (to be able to clear the field). 
  1708. * @since 4.3.0 
  1709. * @param string $value Value to check. 
  1710. * @param WP_REST_Request $request 
  1711. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1712. * @return bool 
  1713. */ 
  1714. public static function validate_twitter_username( $value = '', $request, $param ) { 
  1715. if ( ! empty( $value ) && ( ! is_string( $value ) || ! preg_match( '/^@?\w{1, 15}$/i', $value ) ) ) { 
  1716. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a Twitter username.', 'jetpack' ), $param ) ); 
  1717. return true; 
  1718.  
  1719. /** 
  1720. * Validates that the parameter is a string. 
  1721. * @since 4.3.0 
  1722. * @param string $value Value to check. 
  1723. * @param WP_REST_Request $request The request sent to the WP REST API. 
  1724. * @param string $param Name of the parameter passed to endpoint holding $value. 
  1725. * @return bool 
  1726. */ 
  1727. public static function validate_string( $value = '', $request, $param ) { 
  1728. if ( ! is_string( $value ) ) { 
  1729. return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a string.', 'jetpack' ), $param ) ); 
  1730. return true; 
  1731.  
  1732. /** 
  1733. * If for some reason the roles allowed to see Stats are empty (for example, user tampering with checkboxes),  
  1734. * return an array with only 'administrator' as the allowed role and save it for 'roles' option. 
  1735. * @since 4.3.0 
  1736. * @param string|bool $value Value to check. 
  1737. * @return bool 
  1738. */ 
  1739. public static function sanitize_stats_allowed_roles( $value ) { 
  1740. if ( empty( $value ) ) { 
  1741. return array( 'administrator' ); 
  1742. return $value; 
  1743.  
  1744. /** 
  1745. * Get the currently accessed route and return the module slug in it. 
  1746. * @since 4.3.0 
  1747. * @param string $route Regular expression for the endpoint with the module slug to return. 
  1748. * @return array 
  1749. */ 
  1750. public static function get_module_requested( $route = '/module/(?P<slug>[a-z\-]+)' ) { 
  1751.  
  1752. if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) { 
  1753. return ''; 
  1754.  
  1755. preg_match( "#$route#", $GLOBALS['wp']->query_vars['rest_route'], $module ); 
  1756.  
  1757. if ( empty( $module['slug'] ) ) { 
  1758. return ''; 
  1759.  
  1760. return $module['slug']; 
  1761.  
  1762. /** 
  1763. * Adds extra information for modules. 
  1764. * @since 4.3.0 
  1765. * @param string $modules Can be a single module or a list of modules. 
  1766. * @param null|string $slug Slug of the module in the first parameter. 
  1767. * @return array 
  1768. */ 
  1769. public static function prepare_modules_for_response( $modules = '', $slug = null ) { 
  1770. global $wp_rewrite; 
  1771.  
  1772. /** This filter is documented in modules/sitemaps/sitemaps.php */ 
  1773. $location = apply_filters( 'jetpack_sitemap_location', '' ); 
  1774.  
  1775. if ( $wp_rewrite->using_index_permalinks() ) { 
  1776. $sitemap_url = home_url( '/index.php' . $location . '/sitemap.xml' ); 
  1777. $news_sitemap_url = home_url( '/index.php' . $location . '/news-sitemap.xml' ); 
  1778. } else if ( $wp_rewrite->using_permalinks() ) { 
  1779. $sitemap_url = home_url( $location . '/sitemap.xml' ); 
  1780. $news_sitemap_url = home_url( $location . '/news-sitemap.xml' ); 
  1781. } else { 
  1782. $sitemap_url = home_url( $location . '/?jetpack-sitemap=sitemap.xml' ); 
  1783. $news_sitemap_url = home_url( $location . '/?jetpack-sitemap=news-sitemap.xml' ); 
  1784.  
  1785. if ( is_null( $slug ) && isset( $modules['sitemaps'] ) ) { 
  1786. // Is a list of modules 
  1787. $modules['sitemaps']['extra']['sitemap_url'] = $sitemap_url; 
  1788. $modules['sitemaps']['extra']['news_sitemap_url'] = $news_sitemap_url; 
  1789. } elseif ( 'sitemaps' == $slug ) { 
  1790. // It's a single module 
  1791. $modules['extra']['sitemap_url'] = $sitemap_url; 
  1792. $modules['extra']['news_sitemap_url'] = $news_sitemap_url; 
  1793. return $modules; 
  1794.  
  1795. /** 
  1796. * Remove 'validate_callback' item from options available for module. 
  1797. * Fetch current option value and add to array of module options. 
  1798. * Prepare values of module options that need special handling, like those saved in wpcom. 
  1799. * @since 4.3.0 
  1800. * @param string $module Module slug. 
  1801. * @return array 
  1802. */ 
  1803. public static function prepare_options_for_response( $module = '' ) { 
  1804. $options = self::get_updateable_data_list( $module ); 
  1805.  
  1806. if ( ! is_array( $options ) || empty( $options ) ) { 
  1807. return $options; 
  1808.  
  1809. foreach ( $options as $key => $value ) { 
  1810.  
  1811. if ( isset( $options[ $key ]['validate_callback'] ) ) { 
  1812. unset( $options[ $key ]['validate_callback'] ); 
  1813.  
  1814. $default_value = isset( $options[ $key ]['default'] ) ? $options[ $key ]['default'] : ''; 
  1815.  
  1816. $current_value = get_option( $key, $default_value ); 
  1817.  
  1818. $options[ $key ]['current_value'] = self::cast_value( $current_value, $options[ $key ] ); 
  1819.  
  1820. // Some modules need special treatment. 
  1821. switch ( $module ) { 
  1822.  
  1823. case 'monitor': 
  1824. // Status of user notifications 
  1825. $options['monitor_receive_notifications']['current_value'] = self::cast_value( self::get_remote_value( 'monitor', 'monitor_receive_notifications' ), $options['monitor_receive_notifications'] ); 
  1826. break; 
  1827.  
  1828. case 'post-by-email': 
  1829. // Email address 
  1830. $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'] ); 
  1831. break; 
  1832.  
  1833. case 'protect': 
  1834. // Protect 
  1835. $options['jetpack_protect_key']['current_value'] = get_site_option( 'jetpack_protect_key', false ); 
  1836. if ( ! function_exists( 'jetpack_protect_format_whitelist' ) ) { 
  1837. include_once( JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php' ); 
  1838. $options['jetpack_protect_global_whitelist']['current_value'] = jetpack_protect_format_whitelist(); 
  1839. break; 
  1840.  
  1841. case 'related-posts': 
  1842. // It's local, but it must be broken apart since it's saved as an array. 
  1843. $options = self::split_options( $options, Jetpack_Options::get_option( 'relatedposts' ) ); 
  1844. break; 
  1845.  
  1846. case 'verification-tools': 
  1847. // It's local, but it must be broken apart since it's saved as an array. 
  1848. $options = self::split_options( $options, get_option( 'verification_services_codes' ) ); 
  1849. break; 
  1850.  
  1851. case 'google-analytics': 
  1852. $wga = get_option( 'jetpack_wga' ); 
  1853. $code = ''; 
  1854. if ( is_array( $wga ) && array_key_exists( 'code', $wga ) ) { 
  1855. $code = $wga[ 'code' ]; 
  1856. $options[ 'google_analytics_tracking_id' ][ 'current_value' ] = $code; 
  1857. break; 
  1858.  
  1859. case 'sharedaddy': 
  1860. // It's local, but it must be broken apart since it's saved as an array. 
  1861. if ( ! class_exists( 'Sharing_Service' ) && ! include_once( JETPACK__PLUGIN_DIR . 'modules/sharedaddy/sharing-service.php' ) ) { 
  1862. break; 
  1863. $sharer = new Sharing_Service(); 
  1864. $options = self::split_options( $options, $sharer->get_global_options() ); 
  1865. $options['sharing_services']['current_value'] = $sharer->get_blog_services(); 
  1866. break; 
  1867.  
  1868. case 'after-the-deadline': 
  1869. if ( ! function_exists( 'AtD_get_options' ) ) { 
  1870. include_once( JETPACK__PLUGIN_DIR . 'modules/after-the-deadline.php' ); 
  1871. $atd_options = array_merge( AtD_get_options( get_current_user_id(), 'AtD_options' ), AtD_get_options( get_current_user_id(), 'AtD_check_when' ) ); 
  1872. unset( $atd_options['name'] ); 
  1873. foreach ( $atd_options as $key => $value ) { 
  1874. $options[ $key ]['current_value'] = self::cast_value( $value, $options[ $key ] ); 
  1875. $atd_options = AtD_get_options( get_current_user_id(), 'AtD_guess_lang' ); 
  1876. $options['guess_lang']['current_value'] = self::cast_value( isset( $atd_options['true'] ), $options[ 'guess_lang' ] ); 
  1877. $options['ignored_phrases']['current_value'] = AtD_get_setting( get_current_user_id(), 'AtD_ignored_phrases' ); 
  1878. unset( $options['unignore_phrase'] ); 
  1879. break; 
  1880.  
  1881. case 'stats': 
  1882. // It's local, but it must be broken apart since it's saved as an array. 
  1883. if ( ! function_exists( 'stats_get_options' ) ) { 
  1884. include_once( JETPACK__PLUGIN_DIR . 'modules/stats.php' ); 
  1885. $options = self::split_options( $options, stats_get_options() ); 
  1886. break; 
  1887.  
  1888. return $options; 
  1889.  
  1890. /** 
  1891. * Splits module options saved as arrays like relatedposts or verification_services_codes into separate options to be returned in the response. 
  1892. * @since 4.3.0 
  1893. * @param array $separate_options Array of options admitted by the module. 
  1894. * @param array $grouped_options Option saved as array to be splitted. 
  1895. * @param string $prefix Optional prefix for the separate option keys. 
  1896. * @return array 
  1897. */ 
  1898. public static function split_options( $separate_options, $grouped_options, $prefix = '' ) { 
  1899. if ( is_array( $grouped_options ) ) { 
  1900. foreach ( $grouped_options as $key => $value ) { 
  1901. $option_key = $prefix . $key; 
  1902. if ( isset( $separate_options[ $option_key ] ) ) { 
  1903. $separate_options[ $option_key ]['current_value'] = self::cast_value( $grouped_options[ $key ], $separate_options[ $option_key ] ); 
  1904. return $separate_options; 
  1905.  
  1906. /** 
  1907. * Perform a casting to the value specified in the option definition. 
  1908. * @since 4.3.0 
  1909. * @param mixed $value Value to cast to the proper type. 
  1910. * @param array $definition Type to cast the value to. 
  1911. * @return bool|float|int|string 
  1912. */ 
  1913. public static function cast_value( $value, $definition ) { 
  1914. if ( $value === 'NULL' ) { 
  1915. return null; 
  1916.  
  1917. if ( isset( $definition['type'] ) ) { 
  1918. switch ( $definition['type'] ) { 
  1919. case 'boolean': 
  1920. if ( 'true' === $value ) { 
  1921. return true; 
  1922. } elseif ( 'false' === $value ) { 
  1923. return false; 
  1924. return (bool) $value; 
  1925. break; 
  1926.  
  1927. case 'integer': 
  1928. return (int) $value; 
  1929. break; 
  1930.  
  1931. case 'float': 
  1932. return (float) $value; 
  1933. break; 
  1934.  
  1935. case 'string': 
  1936. return (string) $value; 
  1937. break; 
  1938. return $value; 
  1939.  
  1940. /** 
  1941. * Get a value not saved locally. 
  1942. * @since 4.3.0 
  1943. * @param string $module Module slug. 
  1944. * @param string $option Option name. 
  1945. * @return bool Whether user is receiving notifications or not. 
  1946. */ 
  1947. public static function get_remote_value( $module, $option ) { 
  1948.  
  1949. if ( in_array( $module, array( 'post-by-email' ), true ) ) { 
  1950. $option .= get_current_user_id(); 
  1951.  
  1952. // If option doesn't exist, 'does_not_exist' will be returned. 
  1953. $value = get_option( $option, 'does_not_exist' ); 
  1954.  
  1955. // If option exists, just return it. 
  1956. if ( 'does_not_exist' !== $value ) { 
  1957. return $value; 
  1958.  
  1959. // Only check a remote option if Jetpack is connected. 
  1960. if ( ! Jetpack::is_active() ) { 
  1961. return false; 
  1962.  
  1963. // Do what is necessary for each module. 
  1964. switch ( $module ) { 
  1965. case 'monitor': 
  1966. // Load the class to use the method. If class can't be found, do nothing. 
  1967. if ( ! class_exists( 'Jetpack_Monitor' ) && ! include_once( Jetpack::get_module_path( $module ) ) ) { 
  1968. return false; 
  1969. $value = Jetpack_Monitor::user_receives_notifications( false ); 
  1970. break; 
  1971.  
  1972. case 'post-by-email': 
  1973. // Load the class to use the method. If class can't be found, do nothing. 
  1974. if ( ! class_exists( 'Jetpack_Post_By_Email' ) && ! include_once( Jetpack::get_module_path( $module ) ) ) { 
  1975. return false; 
  1976. $post_by_email = new Jetpack_Post_By_Email(); 
  1977. $value = $post_by_email->get_post_by_email_address(); 
  1978. if ( $value === null ) { 
  1979. $value = 'NULL'; // sentinel value so it actually gets set 
  1980. break; 
  1981.  
  1982. // Normalize value to boolean. 
  1983. if ( is_wp_error( $value ) || is_null( $value ) ) { 
  1984. $value = false; 
  1985.  
  1986. // Save option to use it next time. 
  1987. update_option( $option, $value ); 
  1988.  
  1989. return $value; 
  1990.  
  1991. /** 
  1992. * Get number of plugin updates available. 
  1993. * @since 4.3.0 
  1994. * @return mixed|WP_Error Number of plugin updates available. Otherwise, a WP_Error instance with the corresponding error. 
  1995. */ 
  1996. public static function get_plugin_update_count() { 
  1997. $updates = wp_get_update_data(); 
  1998. if ( isset( $updates['counts'] ) && isset( $updates['counts']['plugins'] ) ) { 
  1999. $count = $updates['counts']['plugins']; 
  2000. if ( 0 == $count ) { 
  2001. $response = array( 
  2002. 'code' => 'success',  
  2003. 'message' => esc_html__( 'All plugins are up-to-date. Keep up the good work!', 'jetpack' ),  
  2004. 'count' => 0,  
  2005. ); 
  2006. } else { 
  2007. $response = array( 
  2008. 'code' => 'updates-available',  
  2009. 'message' => esc_html( sprintf( _n( '%s plugin need updating.', '%s plugins need updating.', $count, 'jetpack' ), $count ) ),  
  2010. 'count' => $count,  
  2011. ); 
  2012. return rest_ensure_response( $response ); 
  2013.  
  2014. return new WP_Error( 'not_found', esc_html__( 'Could not check updates for plugins on this site.', 'jetpack' ), array( 'status' => 404 ) ); 
  2015.  
  2016.  
  2017. /** 
  2018. * Returns a list of all plugins in the site. 
  2019. * @since 4.2.0 
  2020. * @uses get_plugins() 
  2021. * @return array 
  2022. */ 
  2023. private static function core_get_plugins() { 
  2024. if ( ! function_exists( 'get_plugins' ) ) { 
  2025. require_once ABSPATH . 'wp-admin/includes/plugin.php'; 
  2026. /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */ 
  2027. $plugins = apply_filters( 'all_plugins', get_plugins() ); 
  2028.  
  2029. if ( is_array( $plugins ) && ! empty( $plugins ) ) { 
  2030. foreach ( $plugins as $plugin_slug => $plugin_data ) { 
  2031. $plugins[ $plugin_slug ]['active'] = self::core_is_plugin_active( $plugin_slug ); 
  2032. return $plugins; 
  2033.  
  2034. return array(); 
  2035.  
  2036. /** 
  2037. * Checks if the queried plugin is active. 
  2038. * @since 4.2.0 
  2039. * @uses is_plugin_active() 
  2040. * @return bool 
  2041. */ 
  2042. private static function core_is_plugin_active( $plugin ) { 
  2043. if ( ! function_exists( 'is_plugin_active' ) ) { 
  2044. require_once ABSPATH . 'wp-admin/includes/plugin.php'; 
  2045.  
  2046. return is_plugin_active( $plugin ); 
  2047.  
  2048. /** 
  2049. * Get plugins data in site. 
  2050. * @since 4.2.0 
  2051. * @return WP_REST_Response|WP_Error List of plugins in the site. Otherwise, a WP_Error instance with the corresponding error. 
  2052. */ 
  2053. public static function get_plugins() { 
  2054. $plugins = self::core_get_plugins(); 
  2055.  
  2056. if ( ! empty( $plugins ) ) { 
  2057. return rest_ensure_response( $plugins ); 
  2058.  
  2059. return new WP_Error( 'not_found', esc_html__( 'Unable to list plugins.', 'jetpack' ), array( 'status' => 404 ) ); 
  2060.  
  2061. /** 
  2062. * Get data about the queried plugin. Currently it only returns whether the plugin is active or not. 
  2063. * @since 4.2.0 
  2064. * @param WP_REST_Request $request { 
  2065. * Array of parameters received by request. 
  2066. * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'. 
  2067. * } 
  2068. * @return bool|WP_Error True if module was activated. Otherwise, a WP_Error instance with the corresponding error. 
  2069. */ 
  2070. public static function get_plugin( $request ) { 
  2071.  
  2072. $plugins = self::core_get_plugins(); 
  2073.  
  2074. if ( empty( $plugins ) ) { 
  2075. return new WP_Error( 'no_plugins_found', esc_html__( 'This site has no plugins.', 'jetpack' ), array( 'status' => 404 ) ); 
  2076.  
  2077. $plugin = stripslashes( $request['plugin'] ); 
  2078.  
  2079. if ( ! in_array( $plugin, array_keys( $plugins ) ) ) { 
  2080. return new WP_Error( 'plugin_not_found', esc_html( sprintf( __( 'Plugin %s is not installed.', 'jetpack' ), $plugin ) ), array( 'status' => 404 ) ); 
  2081.  
  2082. $plugin_data = $plugins[ $plugin ]; 
  2083.  
  2084. $plugin_data['active'] = self::core_is_plugin_active( $plugin ); 
  2085.  
  2086. return rest_ensure_response( array( 
  2087. 'code' => 'success',  
  2088. 'message' => esc_html__( 'Plugin found.', 'jetpack' ),  
  2089. 'data' => $plugin_data 
  2090. ) ); 
  2091.  
  2092. } // class end