PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.9-a.1
Jetpack – WP Security, Backup, Speed, & Growth v15.9-a.1
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / json-endpoints / class.wpcom-json-api-site-settings-endpoint.php
jetpack / json-endpoints Last commit date
jetpack 4 weeks ago class.wpcom-json-api-add-widget-endpoint.php 6 months ago class.wpcom-json-api-autosave-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-bulk-delete-post-endpoint.php 6 months ago class.wpcom-json-api-bulk-restore-post-endpoint.php 6 months ago class.wpcom-json-api-bulk-update-comments-endpoint.php 6 months ago class.wpcom-json-api-comment-endpoint.php 6 months ago class.wpcom-json-api-delete-media-endpoint.php 6 months ago class.wpcom-json-api-delete-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-edit-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-autosave-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-comment-counts-endpoint.php 6 months ago class.wpcom-json-api-get-comment-endpoint.php 6 months ago class.wpcom-json-api-get-comment-history-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-customcss.php 6 months ago class.wpcom-json-api-get-media-endpoint.php 6 months ago class.wpcom-json-api-get-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-post-counts-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-post-endpoint.php 6 months ago class.wpcom-json-api-get-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-site-endpoint.php 4 weeks ago class.wpcom-json-api-get-site-v1-2-endpoint.php 3 months ago class.wpcom-json-api-get-taxonomies-endpoint.php 1 month ago class.wpcom-json-api-get-taxonomy-endpoint.php 6 months ago class.wpcom-json-api-get-term-endpoint.php 6 months ago class.wpcom-json-api-list-comments-endpoint.php 6 months ago class.wpcom-json-api-list-dropdown-pages-endpoint.php 6 months ago class.wpcom-json-api-list-embeds-endpoint.php 6 months ago class.wpcom-json-api-list-media-endpoint.php 6 months ago class.wpcom-json-api-list-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-list-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-list-post-type-taxonomies-endpoint.php 6 months ago class.wpcom-json-api-list-post-types-endpoint.php 6 months ago class.wpcom-json-api-list-posts-endpoint.php 6 months ago class.wpcom-json-api-list-posts-v1-1-endpoint.php 6 months ago class.wpcom-json-api-list-posts-v1-2-endpoint.php 6 months ago class.wpcom-json-api-list-roles-endpoint.php 6 months ago class.wpcom-json-api-list-shortcodes-endpoint.php 6 months ago class.wpcom-json-api-list-terms-endpoint.php 6 months ago class.wpcom-json-api-list-users-endpoint.php 6 months ago class.wpcom-json-api-menus-v1-1-endpoint.php 6 months ago class.wpcom-json-api-post-endpoint.php 6 months ago class.wpcom-json-api-post-v1-1-endpoint.php 1 month ago class.wpcom-json-api-render-embed-endpoint.php 6 months ago class.wpcom-json-api-render-embed-reversal-endpoint.php 6 months ago class.wpcom-json-api-render-endpoint.php 4 months ago class.wpcom-json-api-render-shortcode-endpoint.php 6 months ago class.wpcom-json-api-sharing-buttons-endpoint.php 6 months ago class.wpcom-json-api-site-settings-endpoint.php 2 months ago class.wpcom-json-api-site-settings-v1-2-endpoint.php 6 months ago class.wpcom-json-api-site-settings-v1-3-endpoint.php 6 months ago class.wpcom-json-api-site-settings-v1-4-endpoint.php 2 months ago class.wpcom-json-api-site-user-endpoint.php 6 months ago class.wpcom-json-api-taxonomy-endpoint.php 6 months ago class.wpcom-json-api-update-comment-endpoint.php 4 months ago class.wpcom-json-api-update-customcss.php 6 months ago class.wpcom-json-api-update-media-endpoint.php 6 months ago class.wpcom-json-api-update-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-update-post-endpoint.php 6 months ago class.wpcom-json-api-update-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-update-post-v1-2-endpoint.php 6 months ago class.wpcom-json-api-update-site-homepage-endpoint.php 6 months ago class.wpcom-json-api-update-site-logo-endpoint.php 6 months ago class.wpcom-json-api-update-taxonomy-endpoint.php 5 months ago class.wpcom-json-api-update-term-endpoint.php 6 months ago class.wpcom-json-api-update-user-endpoint.php 6 months ago class.wpcom-json-api-upload-media-endpoint.php 6 months ago class.wpcom-json-api-upload-media-v1-1-endpoint.php 6 months ago
class.wpcom-json-api-site-settings-endpoint.php
1471 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Manage settings via the WordPress.com REST API.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Waf\Brute_Force_Protection\Brute_Force_Protection_Shared_Functions;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit( 0 );
12 }
13
14 new WPCOM_JSON_API_Site_Settings_Endpoint(
15 array(
16 'description' => 'Get detailed settings information about a site.',
17 'group' => '__do_not_document',
18 'stat' => 'sites:X',
19 'max_version' => '1.1',
20 'new_version' => '1.2',
21 'method' => 'GET',
22 'path' => '/sites/%s/settings',
23 'path_labels' => array(
24 '$site' => '(int|string) Site ID or domain',
25 ),
26
27 'query_parameters' => array(
28 'context' => false,
29 ),
30
31 'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format,
32
33 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
34 )
35 );
36
37 new WPCOM_JSON_API_Site_Settings_Endpoint(
38 array(
39 'description' => 'Update settings for a site.',
40 'group' => '__do_not_document',
41 'stat' => 'sites:X',
42 'max_version' => '1.1',
43 'new_version' => '1.2',
44 'method' => 'POST',
45 'path' => '/sites/%s/settings',
46 'a_new_very_long_key' => 'blabla',
47 'path_labels' => array(
48 '$site' => '(int|string) Site ID or domain',
49 ),
50
51 'request_format' => array(
52 'migration_source_site_domain' => '(string) The source site URL, from the migration flow',
53 'in_site_migration_flow' => '(string) The migration flow the site is in',
54 'blogname' => '(string) Blog name',
55 'blogdescription' => '(string) Blog description',
56 'default_pingback_flag' => '(bool) Notify blogs linked from article?',
57 'default_ping_status' => '(bool) Allow link notifications from other blogs?',
58 'default_comment_status' => '(bool) Allow comments on new articles?',
59 'blog_public' => '(string) Site visibility; -1: private, 0: discourage search engines, 1: allow search engines',
60 'wpcom_data_sharing_opt_out' => '(bool) Did the site opt out of sharing public content with third parties and research partners?',
61 'jetpack_sync_non_public_post_stati' => '(bool) allow sync of post and pages with non-public posts stati',
62 'jetpack_relatedposts_enabled' => '(bool) Enable related posts?',
63 'jetpack_relatedposts_show_context' => '(bool) Show post\'s tags and category in related posts?',
64 'jetpack_relatedposts_show_date' => '(bool) Show date in related posts?',
65 'jetpack_relatedposts_show_headline' => '(bool) Show headline in related posts?',
66 'jetpack_relatedposts_show_thumbnails' => '(bool) Show thumbnails in related posts?',
67 'jetpack_protect_whitelist' => '(array) List of IP addresses to always allow',
68 'instant_search_enabled' => '(bool) Enable the new Jetpack Instant Search interface',
69 'jetpack_search_enabled' => '(bool) Enable Jetpack Search',
70 'jetpack_search_supported' => '(bool) Jetpack Search is supported',
71 'infinite_scroll' => '(bool) Support infinite scroll of posts?',
72 'default_category' => '(int) Default post category',
73 'default_post_format' => '(string) Default post format',
74 'require_name_email' => '(bool) Require comment authors to fill out name and email?',
75 'comment_registration' => '(bool) Require users to be registered and logged in to comment?',
76 'close_comments_for_old_posts' => '(bool) Automatically close comments on old posts?',
77 'close_comments_days_old' => '(int) Age at which to close comments',
78 'thread_comments' => '(bool) Enable threaded comments?',
79 'thread_comments_depth' => '(int) Depth to thread comments',
80 'page_comments' => '(bool) Break comments into pages?',
81 'comments_per_page' => '(int) Number of comments to display per page',
82 'default_comments_page' => '(string) newest|oldest Which page of comments to display first',
83 'comment_order' => '(string) asc|desc Order to display comments within page',
84 'comments_notify' => '(bool) Email me when someone comments?',
85 'moderation_notify' => '(bool) Email me when a comment is helf for moderation?',
86 'social_notifications_like' => '(bool) Email me when someone likes my post?',
87 'social_notifications_reblog' => '(bool) Email me when someone reblogs my post?',
88 'social_notifications_subscribe' => '(bool) Email me when someone subscribes to my blog?',
89 'comment_moderation' => '(bool) Moderate comments for manual approval?',
90 'comment_previously_approved' => '(bool) Moderate comments unless author has a previously-approved comment?',
91 'comment_max_links' => '(int) Moderate comments that contain X or more links',
92 'moderation_keys' => '(string) Words or phrases that trigger comment moderation, one per line',
93 'disallowed_keys' => '(string) Words or phrases that mark comment spam, one per line',
94 'lang_id' => '(int) ID for language blog is written in',
95 'wga' => '(array) Google Analytics Settings',
96 'disabled_likes' => '(bool) Are likes globally disabled (they can still be turned on per post)?',
97 'disabled_reblogs' => '(bool) Are reblogs disabled on posts?',
98 'jetpack_comment_likes_enabled' => '(bool) Are comment likes enabled for all comments?',
99 'sharing_button_style' => '(string) Style to use for sharing buttons (icon-text, icon, text, or official)',
100 'sharing_label' => '(string) Label to use for sharing buttons, e.g. "Share this:"',
101 'sharing_show' => '(string|array:string) Post type or array of types where sharing buttons are to be displayed',
102 'sharing_open_links' => '(string) Link target for sharing buttons (same or new)',
103 'twitter_via' => '(string) Twitter username to include in tweets when people share using the Twitter button',
104 'jetpack-twitter-cards-site-tag' => '(string) The Twitter username of the owner of the site\'s domain.',
105 'eventbrite_api_token' => '(int) The Keyring token ID for an Eventbrite token to associate with the site',
106 'timezone_string' => '(string) PHP-compatible timezone string like \'UTC-5\'',
107 'gmt_offset' => '(int) Site offset from UTC in hours',
108 'date_format' => '(string) PHP Date-compatible date format',
109 'time_format' => '(string) PHP Date-compatible time format',
110 'start_of_week' => '(int) Starting day of week (0 = Sunday, 6 = Saturday)',
111 'jetpack_testimonial' => '(bool) Whether testimonial custom post type is enabled for the site',
112 'jetpack_testimonial_posts_per_page' => '(int) Number of testimonials to show per page',
113 'jetpack_portfolio' => '(bool) Whether portfolio custom post type is enabled for the site',
114 'jetpack_portfolio_posts_per_page' => '(int) Number of portfolio projects to show per page',
115 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => '(string) The seo meta description for the site.',
116 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => '(array) SEO meta title formats. Allowed keys: front_page, posts, pages, groups, archives',
117 'verification_services_codes' => '(array) Website verification codes. Allowed keys: google, pinterest, bing, yandex, facebook',
118 'markdown_supported' => '(bool) Whether markdown is supported for this site',
119 'wpcom_publish_posts_with_markdown' => '(bool) Whether markdown is enabled for posts',
120 'wpcom_publish_comments_with_markdown' => '(bool) Whether markdown is enabled for comments',
121 'site_icon' => '(int) Media attachment ID to use as site icon. Set to zero or an otherwise empty value to clear',
122 'api_cache' => '(bool) Turn on/off the Jetpack JSON API cache',
123 'posts_per_page' => '(int) Number of posts to show on blog pages',
124 'posts_per_rss' => '(int) Number of posts to show in the RSS feed',
125 'rss_use_excerpt' => '(bool) Whether the RSS feed will use post excerpts',
126 'launchpad_screen' => '(string) Whether or not launchpad is presented and what size it will be',
127 'sm_enabled' => '(bool) Whether the newsletter subscribe modal is enabled',
128 'jetpack_subscribe_overlay_enabled' => '(bool) Whether the newsletter subscribe overlay is enabled',
129 'jetpack_subscribe_floating_button_enabled' => '(bool) Whether the newsletter floating subscribe button is enabled',
130 'jetpack_subscriptions_subscribe_post_end_enabled' => '(bool) Whether the Subscribe block at the end of each post placement is enabled',
131 'jetpack_subscriptions_login_navigation_enabled' => '(bool) Whether the Subscriber Login block navigation placement is enabled',
132 'jetpack_subscriptions_subscribe_navigation_enabled' => '(Bool) Whether the Subscribe block navigation placement is enabled',
133 'wpcom_ai_site_prompt' => '(string) User input in the AI site prompt',
134 'jetpack_waf_automatic_rules' => '(bool) Whether the WAF should enforce automatic firewall rules',
135 'jetpack_waf_ip_allow_list' => '(string) List of IP addresses to always allow',
136 'jetpack_waf_ip_allow_list_enabled' => '(bool) Whether the IP allow list is enabled',
137 'jetpack_waf_ip_block_list' => '(string) List of IP addresses the WAF should always block',
138 'jetpack_waf_ip_block_list_enabled' => '(bool) Whether the IP block list is enabled',
139 'jetpack_waf_share_data' => '(bool) Whether the WAF should share basic data with Jetpack',
140 'jetpack_waf_share_debug_data' => '(bool) Whether the WAF should share debug data with Jetpack',
141 'jetpack_waf_automatic_rules_last_updated_timestamp' => '(int) Timestamp of the last time the automatic rules were updated',
142 'mcp_abilities' => '(array) List of MCP Abilities',
143 ),
144
145 'response_format' => array(
146 'updated' => '(array)',
147 ),
148
149 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
150 )
151 );
152
153 /**
154 * Manage Site settings endpoint.
155 *
156 * @phan-constructor-used-for-side-effects
157 */
158 class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
159
160 /**
161 * Site format.
162 *
163 * @var array
164 */
165 public static $site_format = array(
166 'ID' => '(int) Site ID',
167 'name' => '(string) Title of site',
168 'description' => '(string) Tagline or description of site',
169 'URL' => '(string) Full URL to the site',
170 'lang' => '(string) Primary language code of the site',
171 'locale_variant' => '(string) Locale variant code for the site, if set',
172 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
173 );
174
175 /**
176 * Endpoint response
177 *
178 * GET /sites/%s/settings
179 * POST /sites/%s/settings
180 *
181 * @param string $path Path.
182 * @param int $blog_id Blog ID.
183 */
184 public function callback( $path = '', $blog_id = 0 ) {
185 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
186 if ( is_wp_error( $blog_id ) ) {
187 return $blog_id;
188 }
189
190 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
191 // Source & include the infinite scroll compatibility files prior to loading theme functions.
192 add_filter( 'restapi_theme_action_copy_dirs', array( 'WPCOM_JSON_API_Site_Settings_Endpoint', 'wpcom_restapi_copy_theme_plugin_actions' ) );
193 $this->load_theme_functions();
194 }
195
196 if ( ! is_user_logged_in() ) {
197 return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
198 } elseif ( ! current_user_can( 'manage_options' ) ) {
199 return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
200 }
201
202 if ( 'GET' === $this->api->method ) {
203 /**
204 * Fires on each GET request to a specific endpoint.
205 *
206 * @module json-api
207 *
208 * @since 3.2.0
209 *
210 * @param string sites.
211 */
212 do_action( 'wpcom_json_api_objects', 'sites' );
213 return $this->get_settings_response();
214 } elseif ( 'POST' === $this->api->method ) {
215 return $this->update_settings();
216 } else {
217 return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
218 }
219 }
220
221 /**
222 * Includes additional theme-specific files to be included in REST API theme
223 * context loading action copying.
224 *
225 * @see WPCOM_JSON_API_Endpoint#load_theme_functions
226 * @see the_neverending_home_page_theme_support
227 *
228 * @param array $copy_dirs Array of files to be included in theme context.
229 */
230 public static function wpcom_restapi_copy_theme_plugin_actions( $copy_dirs ) {
231 $theme_name = get_stylesheet();
232 $default_file_name = WP_CONTENT_DIR . "/mu-plugins/infinity/themes/{$theme_name}.php";
233
234 /**
235 * Filter the path to the Infinite Scroll compatibility file.
236 *
237 * @module infinite-scroll
238 *
239 * @since 2.0.0
240 *
241 * @param string $str IS compatibility file path.
242 * @param string $theme_name Theme name.
243 */
244 $customization_file = apply_filters( 'infinite_scroll_customization_file', $default_file_name, $theme_name );
245
246 if ( is_readable( $customization_file ) ) {
247 require_once $customization_file;
248 $copy_dirs[] = $customization_file;
249 }
250
251 return $copy_dirs;
252 }
253
254 /**
255 * Determines whether jetpack_relatedposts is supported
256 *
257 * @return bool
258 */
259 public function jetpack_relatedposts_supported() {
260 $wpcom_related_posts_theme_blacklist = array(
261 'Expound',
262 'Traveler',
263 'Opti',
264 'Currents',
265 );
266 return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist, true ) );
267 }
268
269 /**
270 * Returns category details
271 *
272 * @param WP_Term $category Category object.
273 *
274 * @return array
275 */
276 public function get_category_details( $category ) {
277 return array(
278 'value' => $category->term_id,
279 'name' => $category->name,
280 );
281 }
282
283 /**
284 * Returns an option value as the result of the callable being applied to
285 * it if a value is set, otherwise null.
286 *
287 * @param string $option_name Option name.
288 * @param callable $cast_callable Callable to invoke on option value.
289 *
290 * @return int|null Numeric option value or null.
291 */
292 protected function get_cast_option_value_or_null( $option_name, $cast_callable ) {
293 $option_value = get_option( $option_name, null );
294 if ( $option_value === null ) {
295 return $option_value;
296 }
297
298 return call_user_func( $cast_callable, $option_value );
299 }
300
301 /**
302 * Collects the necessary information to return for a get settings response.
303 *
304 * @return array
305 */
306 public function get_settings_response() {
307 $response = array();
308
309 // Allow update in later versions.
310 /**
311 * Filter the structure of site settings to return.
312 *
313 * @module json-api
314 *
315 * @since 3.9.3
316 *
317 * @param array $site_format Data structure.
318 */
319 $response_format = apply_filters( 'site_settings_site_format', self::$site_format );
320
321 $blog_id = (int) $this->api->get_blog_id_for_output();
322 $site = $this->get_platform()->get_site( $blog_id );
323
324 foreach ( array_keys( $response_format ) as $key ) {
325
326 // refactoring to change lang parameter to locale in 1.2.
327 $lang_or_locale = $this->get_locale( $key );
328 if ( $lang_or_locale ) {
329 $response[ $key ] = $lang_or_locale;
330 continue;
331 }
332
333 switch ( $key ) {
334 case 'ID':
335 $response[ $key ] = $blog_id;
336 break;
337 case 'name':
338 $name = get_bloginfo( 'name' );
339 $response[ $key ] = is_string( $name ) ? htmlspecialchars_decode( $name, ENT_QUOTES ) : '';
340 break;
341 case 'description':
342 $description = get_bloginfo( 'description' );
343 $response[ $key ] = is_string( $description ) ? htmlspecialchars_decode( $description, ENT_QUOTES ) : '';
344 break;
345 case 'URL':
346 $response[ $key ] = (string) home_url();
347 break;
348 case 'locale_variant':
349 if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) {
350 $blog_locale_variant = wpcom_l10n_get_blog_locale_variant();
351 if ( $blog_locale_variant ) {
352 $response[ $key ] = $blog_locale_variant;
353 }
354 }
355 break;
356 case 'settings':
357 $jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts', array() );
358 // If the option's enabled key is NOT SET, it is considered enabled by the plugin.
359 if ( ! isset( $jetpack_relatedposts_options['enabled'] ) ) {
360 $jetpack_relatedposts_options['enabled'] = true;
361 }
362
363 $jetpack_relatedposts_options['enabled'] =
364 $jetpack_relatedposts_options['enabled']
365 && $site->is_module_active( 'related-posts' );
366
367 $jetpack_search_supported = false;
368 if ( function_exists( 'wpcom_is_jetpack_search_supported' ) ) {
369 $jetpack_search_supported = wpcom_is_jetpack_search_supported( $blog_id );
370 }
371
372 $jetpack_search_active =
373 $jetpack_search_supported
374 && $site->is_module_active( 'search' );
375
376 // array_values() is necessary to ensure the array starts at index 0.
377 $post_categories = array_values(
378 array_map(
379 array( $this, 'get_category_details' ),
380 get_categories( array( 'hide_empty' => false ) )
381 )
382 );
383
384 // Make sure we are returning a consistent type
385 if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) {
386 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php';
387 }
388 $newsletter_category_ids = Jetpack_Newsletter_Category_Helper::get_category_ids();
389
390 $api_cache = $site->is_jetpack() ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true;
391
392 // Get Sites MCP settings
393 $mcp_abilities = $this->get_site_mcp_abilities();
394
395 $response[ $key ] = array(
396 // also exists as "options".
397 'admin_url' => get_admin_url(),
398 'default_ping_status' => 'closed' !== get_option( 'default_ping_status' ),
399 'default_comment_status' => 'closed' !== get_option( 'default_comment_status' ),
400
401 // new stuff starts here.
402 'instant_search_enabled' => (bool) get_option( 'instant_search_enabled' ),
403 'blog_public' => (int) get_option( 'blog_public' ),
404 'wpcom_data_sharing_opt_out' => (bool) get_option( 'wpcom_data_sharing_opt_out' ),
405 'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
406 'jetpack_relatedposts_allowed' => (bool) $this->jetpack_relatedposts_supported(),
407 'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options['enabled'],
408 'jetpack_relatedposts_show_context' => ! empty( $jetpack_relatedposts_options['show_context'] ),
409 'jetpack_relatedposts_show_date' => ! empty( $jetpack_relatedposts_options['show_date'] ),
410 'jetpack_relatedposts_show_headline' => ! empty( $jetpack_relatedposts_options['show_headline'] ),
411 'jetpack_relatedposts_show_thumbnails' => ! empty( $jetpack_relatedposts_options['show_thumbnails'] ),
412 'jetpack_search_enabled' => $jetpack_search_active,
413 'jetpack_search_supported' => (bool) $jetpack_search_supported,
414 'default_category' => (int) get_option( 'default_category' ),
415 'post_categories' => (array) $post_categories,
416 'default_post_format' => get_option( 'default_post_format' ),
417 'default_pingback_flag' => (bool) get_option( 'default_pingback_flag' ),
418 'require_name_email' => (bool) get_option( 'require_name_email' ),
419 'comment_registration' => (bool) get_option( 'comment_registration' ),
420 'close_comments_for_old_posts' => (bool) get_option( 'close_comments_for_old_posts' ),
421 'close_comments_days_old' => (int) get_option( 'close_comments_days_old' ),
422 'thread_comments' => (bool) get_option( 'thread_comments' ),
423 'thread_comments_depth' => (int) get_option( 'thread_comments_depth' ),
424 'page_comments' => (bool) get_option( 'page_comments' ),
425 'comments_per_page' => (int) get_option( 'comments_per_page' ),
426 'default_comments_page' => get_option( 'default_comments_page' ),
427 'comment_order' => get_option( 'comment_order' ),
428 'comments_notify' => (bool) get_option( 'comments_notify' ),
429 'moderation_notify' => (bool) get_option( 'moderation_notify' ),
430 'social_notifications_like' => ( 'on' === get_option( 'social_notifications_like' ) ),
431 'social_notifications_reblog' => ( 'on' === get_option( 'social_notifications_reblog' ) ),
432 'social_notifications_subscribe' => ( 'on' === get_option( 'social_notifications_subscribe' ) ),
433 'comment_moderation' => (bool) get_option( 'comment_moderation' ),
434 'comment_whitelist' => (bool) get_option( 'comment_previously_approved' ),
435 'comment_previously_approved' => (bool) get_option( 'comment_previously_approved' ),
436 'comment_max_links' => (int) get_option( 'comment_max_links' ),
437 'moderation_keys' => get_option( 'moderation_keys' ),
438 'blacklist_keys' => get_option( 'disallowed_keys' ),
439 'disallowed_keys' => get_option( 'disallowed_keys' ),
440 'lang_id' => defined( 'IS_WPCOM' ) && IS_WPCOM
441 ? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) )
442 : get_option( 'lang_id' ),
443 'site_vertical_id' => (string) get_option( 'site_vertical_id' ),
444 'jetpack_cloudflare_analytics' => get_option( 'jetpack_cloudflare_analytics' ),
445 'disabled_likes' => (bool) get_option( 'disabled_likes' ),
446 'disabled_reblogs' => (bool) get_option( 'disabled_reblogs' ),
447 'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
448 'twitter_via' => (string) get_option( 'twitter_via' ),
449 'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
450 'eventbrite_api_token' => $this->get_cast_option_value_or_null( 'eventbrite_api_token', 'intval' ),
451 'gmt_offset' => get_option( 'gmt_offset' ),
452 'timezone_string' => get_option( 'timezone_string' ),
453 'date_format' => get_option( 'date_format' ),
454 'time_format' => get_option( 'time_format' ),
455 'start_of_week' => get_option( 'start_of_week' ),
456 'woocommerce_onboarding_profile' => (array) get_option( 'woocommerce_onboarding_profile', array() ),
457 'woocommerce_store_address' => (string) get_option( 'woocommerce_store_address' ),
458 'woocommerce_store_address_2' => (string) get_option( 'woocommerce_store_address_2' ),
459 'woocommerce_store_city' => (string) get_option( 'woocommerce_store_city' ),
460 'woocommerce_default_country' => (string) get_option( 'woocommerce_default_country' ),
461 'woocommerce_store_postcode' => (string) get_option( 'woocommerce_store_postcode' ),
462 'jetpack_testimonial' => (bool) get_option( 'jetpack_testimonial', '0' ),
463 'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ),
464 'jetpack_portfolio' => (bool) get_option( 'jetpack_portfolio', '0' ),
465 'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ),
466 'markdown_supported' => true,
467 'site_icon' => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ),
468 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ),
469 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ),
470 'api_cache' => $api_cache,
471 'posts_per_page' => (int) get_option( 'posts_per_page' ),
472 'posts_per_rss' => (int) get_option( 'posts_per_rss' ),
473 'rss_use_excerpt' => (bool) get_option( 'rss_use_excerpt' ),
474 'launchpad_screen' => (string) get_option( 'launchpad_screen' ),
475 'wpcom_newsletter_send_default' => (bool) get_option( 'wpcom_newsletter_send_default', true ),
476 'wpcom_featured_image_in_email' => ( function () use ( $site ) {
477 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
478 $registered_date = method_exists( $site, 'get_registered_date' ) ? $site->get_registered_date() : '';
479 // Compare to May 2, 2025 (ISO 8601 format)
480 if ( $registered_date && $registered_date !== '0000-00-00T00:00:00+00:00' && strtotime( $registered_date ) >= strtotime( '2025-05-02T00:00:00+00:00' ) ) {
481 return (bool) get_option( 'wpcom_featured_image_in_email', true );
482 }
483 }
484 // For all other sites, use the saved value or default to false for legacy behavior.
485 return (bool) get_option( 'wpcom_featured_image_in_email', false );
486 } )(),
487 'jetpack_gravatar_in_email' => (bool) get_option( 'jetpack_gravatar_in_email', true ),
488 'jetpack_author_in_email' => (bool) get_option( 'jetpack_author_in_email', true ),
489 'jetpack_post_date_in_email' => (bool) get_option( 'jetpack_post_date_in_email', true ),
490 'wpcom_newsletter_categories' => $newsletter_category_ids,
491 'wpcom_newsletter_categories_enabled' => (bool) get_option( 'wpcom_newsletter_categories_enabled' ),
492 'sm_enabled' => (bool) get_option( 'sm_enabled' ),
493 'jetpack_subscribe_overlay_enabled' => (bool) get_option( 'jetpack_subscribe_overlay_enabled' ),
494 'jetpack_subscribe_floating_button_enabled' => (bool) get_option( 'jetpack_subscribe_floating_button_enabled' ),
495 'jetpack_subscriptions_subscribe_post_end_enabled' => (bool) get_option( 'jetpack_subscriptions_subscribe_post_end_enabled' ),
496 'jetpack_subscriptions_login_navigation_enabled' => (bool) get_option( 'jetpack_subscriptions_login_navigation_enabled' ),
497 'jetpack_subscriptions_subscribe_navigation_enabled' => (bool) get_option( 'jetpack_subscriptions_subscribe_navigation_enabled' ),
498 'wpcom_gifting_subscription' => (bool) get_option( 'wpcom_gifting_subscription', $this->get_wpcom_gifting_subscription_default() ),
499 'wpcom_reader_views_enabled' => (bool) get_option( 'wpcom_reader_views_enabled', true ),
500 'wpcom_subscription_emails_use_excerpt' => (bool) get_option( 'wpcom_subscription_emails_use_excerpt' ),
501 'jetpack_subscriptions_reply_to' => (string) $this->get_subscriptions_reply_to_option(),
502 'jetpack_subscriptions_from_name' => (string) get_option( 'jetpack_subscriptions_from_name' ),
503 'show_on_front' => (string) get_option( 'show_on_front' ),
504 'page_on_front' => (string) get_option( 'page_on_front' ),
505 'page_for_posts' => (string) get_option( 'page_for_posts' ),
506 'subscription_options' => (array) get_option( 'subscription_options' ),
507 'jetpack_verbum_subscription_modal' => (bool) get_option( 'jetpack_verbum_subscription_modal', true ),
508 'enable_verbum_commenting' => (bool) get_option( 'enable_verbum_commenting', true ),
509 'enable_blocks_comments' => (bool) get_option( 'enable_blocks_comments', true ),
510 'highlander_comment_form_prompt' => $this->get_highlander_comment_form_prompt_option(),
511 'jetpack_comment_form_color_scheme' => (string) get_option( 'jetpack_comment_form_color_scheme' ),
512 'in_site_migration_flow' => (string) get_option( 'in_site_migration_flow', '' ),
513 'migration_source_site_domain' => (string) get_option( 'migration_source_site_domain' ),
514 'jetpack_waf_automatic_rules' => (bool) get_option( 'jetpack_waf_automatic_rules' ),
515 'jetpack_waf_ip_allow_list' => (string) get_option( 'jetpack_waf_ip_allow_list' ),
516 'jetpack_waf_ip_allow_list_enabled' => (bool) get_option( 'jetpack_waf_ip_allow_list_enabled' ),
517 'jetpack_waf_ip_block_list' => (string) get_option( 'jetpack_waf_ip_block_list' ),
518 'jetpack_waf_ip_block_list_enabled' => (bool) get_option( 'jetpack_waf_ip_block_list_enabled' ),
519 'jetpack_waf_share_data' => (bool) get_option( 'jetpack_waf_share_data' ),
520 'jetpack_waf_share_debug_data' => (bool) get_option( 'jetpack_waf_share_debug_data' ),
521 'jetpack_waf_automatic_rules_last_updated_timestamp' => (int) get_option( 'jetpack_waf_automatic_rules_last_updated_timestamp' ),
522 'is_fully_managed_agency_site' => (bool) get_option( 'is_fully_managed_agency_site' ),
523 'wpcom_hide_action_bar' => (bool) get_option( 'wpcom_hide_action_bar' ),
524 'mcp_abilities' => $mcp_abilities,
525 );
526
527 require_once JETPACK__PLUGIN_DIR . '/modules/memberships/class-jetpack-memberships.php';
528 if ( class_exists( 'Jetpack_Memberships' ) ) {
529 $response[ $key ]['newsletter_has_active_plan'] = count( Jetpack_Memberships::get_all_newsletter_plan_ids( false ) ) > 0;
530 }
531
532 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
533 $response[ $key ]['wpcom_publish_posts_with_markdown'] = (bool) WPCom_Markdown::get_instance()->is_posting_enabled();
534 $response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::get_instance()->is_commenting_enabled();
535
536 // WPCOM-specific Infinite Scroll Settings.
537 if ( is_callable( array( 'The_Neverending_Home_Page', 'get_settings' ) ) ) {
538 /**
539 * Clear the cached copy of widget info so it's pulled fresh from blog options.
540 * It was primed during the initial load under the __REST API site__'s context.
541 *
542 * @see wp_get_sidebars_widgets https://core.trac.wordpress.org/browser/trunk/src/wp-includes/widgets.php?rev=42374#L931
543 */
544 $GLOBALS['_wp_sidebars_widgets'] = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
545
546 $infinite_scroll_settings = The_Neverending_Home_Page::get_settings();
547 $response[ $key ]['infinite_scroll'] = get_option( 'infinite_scroll', true ) && 'scroll' === $infinite_scroll_settings->type;
548 if ( $infinite_scroll_settings->footer_widgets || 'click' === $infinite_scroll_settings->requested_type ) {
549 // The blog has footer widgets -- infinite scroll is blocked.
550 $response[ $key ]['infinite_scroll_blocked'] = 'footer';
551 } else {
552 $response[ $key ]['infinite_scroll_blocked'] = false;
553 }
554 }
555 }
556
557 // allow future versions of this endpoint to support additional settings keys.
558 /**
559 * Filter the current site setting in the returned response.
560 *
561 * @module json-api
562 *
563 * @since 3.9.3
564 * @since 13.6 Added the API object parameter.
565 *
566 * @param mixed $response_item A single site setting.
567 * @param WPCOM_JSON_API_Site_Settings_Endpoint $this The API object.
568 */
569 $response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ], $this );
570
571 if ( class_exists( 'Sharing_Service' ) ) {
572 $ss = new Sharing_Service();
573 $sharing = $ss->get_global_options();
574 $response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
575 $response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
576 $response[ $key ]['sharing_show'] = (array) $sharing['show'];
577 $response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
578 }
579
580 $response[ $key ]['jetpack_protect_whitelist'] = Brute_Force_Protection_Shared_Functions::format_allow_list();
581
582 if ( ! current_user_can( 'edit_posts' ) ) {
583 unset( $response[ $key ] );
584 }
585 break;
586 }
587 }
588 return $response;
589 }
590
591 /**
592 * Get the default value for the wpcom_gifting_subscription option.
593 * The default value is the inverse of the plan's auto_renew setting.
594 *
595 * @return bool
596 */
597 protected function get_wpcom_gifting_subscription_default() {
598 if ( function_exists( 'wpcom_get_site_purchases' ) && function_exists( 'wpcom_purchase_has_feature' ) ) {
599 $purchases = wpcom_get_site_purchases();
600
601 foreach ( $purchases as $purchase ) {
602 if ( wpcom_purchase_has_feature( $purchase, \WPCOM_Features::SUBSCRIPTION_GIFTING ) ) {
603 /*
604 * We set default value as false when expiration date not match the following:
605 * - 54 days before the annual plan expiration.
606 * - 5 days before the monthly plan expiration.
607 * This is to match the gifting banner logic.
608 */
609 $days_of_warning = str_contains( $purchase->product_slug, 'monthly' ) ? 5 : 54;
610 $seconds_until_expiration = strtotime( $purchase->expiry_date ) - time();
611 if ( $seconds_until_expiration >= $days_of_warning * DAY_IN_SECONDS ) {
612 return false;
613 }
614
615 // We set default to the inverse of auto-renew.
616 if ( isset( $purchase->auto_renew ) ) {
617 return ! $purchase->auto_renew;
618 } elseif ( isset( $purchase->user_allows_auto_renew ) ) {
619 return ! $purchase->user_allows_auto_renew;
620 }
621 }
622 }
623 }
624 return false;
625 }
626
627 /**
628 * Get list of all site level MCP abilities.
629 *
630 * @return array
631 */
632 private function get_all_site_mcp_abilities(): array {
633 $all_abilities = array();
634 $ability_registry_file = WP_CONTENT_DIR . '/mu-plugins/wpcom-mcp/includes/AbilitiesRegistry/Registry/AbilityRegistry.php';
635 if ( file_exists( $ability_registry_file ) ) {
636 require_once $ability_registry_file;
637 // @phan-suppress-next-line PhanUndeclaredClassMethod
638 $abilities_resources = Automattic\WpcomMcp\AbilitiesRegistry\Registry\AbilityRegistry::get_resources_for_server( 'site-level' );
639 // @phan-suppress-next-line PhanUndeclaredClassMethod
640 $abilities_tools = Automattic\WpcomMcp\AbilitiesRegistry\Registry\AbilityRegistry::get_tools_for_server( 'site-level' );
641 // @phan-suppress-next-line PhanUndeclaredClassMethod
642 $abilities_prompts = Automattic\WpcomMcp\AbilitiesRegistry\Registry\AbilityRegistry::get_prompts_for_server( 'site-level' );
643 $all_abilities = array_merge( $abilities_resources, $abilities_tools, $abilities_prompts );
644 }
645 return apply_filters( 'jetpack_site_mcp_abilities', $all_abilities );
646 }
647
648 /**
649 * Get ability meta from config.
650 *
651 * @param string $ability_name Ability name, i.e. wpcom-mcp/posts-search.
652 *
653 * @return array
654 */
655 private function get_mcp_abilities_metadata( string $ability_name ): array {
656 $ability_meta = array();
657 $ability_registry_file = WP_CONTENT_DIR . '/mu-plugins/wpcom-mcp/includes/AbilitiesRegistry/Registry/AbilityRegistry.php';
658 if ( file_exists( $ability_registry_file ) ) {
659 require_once $ability_registry_file;
660 // @phan-suppress-next-line PhanUndeclaredClassMethod
661 $ability_meta = Automattic\WpcomMcp\AbilitiesRegistry\Registry\AbilityRegistry::get_metadata( $ability_name );
662 }
663 return apply_filters( 'jetpack_site_mcp_ability_meta', $ability_meta, $ability_name );
664 }
665
666 /**
667 * Get MCP abilities for the current site.
668 *
669 * @return array
670 */
671 public function get_site_mcp_abilities(): array {
672 $current_mcp_abilities = get_option( 'mcp_abilities', array() );
673 if ( ! is_array( $current_mcp_abilities ) ) {
674 $current_mcp_abilities = array();
675 }
676
677 $all_abilities = $this->get_all_site_mcp_abilities();
678 if ( empty( $all_abilities ) ) {
679 return array();
680 }
681
682 $computed_abilities = array();
683 foreach ( $all_abilities as $ability_name ) {
684 // Get base metadata first
685 $ability_meta = $this->get_mcp_abilities_metadata( $ability_name );
686 if ( ! empty( $ability_meta ) ) {
687 // Use stored value or fall back to metadata default
688 $enabled = $current_mcp_abilities[ $ability_name ] ?? $ability_meta['enabled'] ?? false;
689
690 $computed_abilities[ $ability_name ] = array(
691 'name' => $ability_name,
692 'title' => $ability_meta['title'] ?? '',
693 'description' => $ability_meta['description'] ?? '',
694 'category' => $ability_meta['category'] ?? '',
695 'type' => $ability_meta['type'] ?? '',
696 'enabled' => (bool) $enabled,
697 );
698 }
699 }
700 return $computed_abilities;
701 }
702
703 /**
704 * Sets the MCP abilities for the current site.
705 *
706 * @param mixed $value MCP abilities array.
707 *
708 * @return true|WP_Error
709 */
710 public function set_site_mcp_abilities( $value ) {
711 // Validate input format
712 if ( ! is_array( $value ) ) {
713 return new WP_Error( 'invalid_format', __( 'Site MCP abilities must be an array', 'jetpack' ) );
714 }
715
716 $all_abilities = $this->get_all_site_mcp_abilities();
717
718 // Filter ability names that don't exist
719 $value = array_filter(
720 $value,
721 function ( $ability_name ) use ( $all_abilities ) {
722 return in_array( $ability_name, $all_abilities, true );
723 },
724 ARRAY_FILTER_USE_KEY
725 );
726
727 // Validate each ability exists and value is boolean-like
728 foreach ( $value as $ability_name => $enabled ) {
729 if ( ! is_string( $ability_name ) || ( ! WPCOM_JSON_API::is_truthy( $enabled ) && ! WPCOM_JSON_API::is_falsy( $enabled ) ) ) {
730 $error_message = sprintf(
731 // Translators: %s is an MCP ability name
732 __( 'Invalid ability: %s', 'jetpack' ),
733 $ability_name
734 );
735 return new WP_Error( 'invalid_ability', $error_message );
736 }
737 }
738
739 update_option( 'mcp_abilities', $value );
740
741 return true;
742 }
743
744 /**
745 * Get locale.
746 *
747 * @param string $key Language.
748 */
749 protected function get_locale( $key ) {
750 if ( 'lang' === $key ) {
751 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
752 return (string) get_blog_lang_code();
753 } else {
754 return get_locale();
755 }
756 }
757
758 return false;
759 }
760
761 /**
762 * Updates site settings for authorized users
763 *
764 * @return array|WP_Error
765 */
766 public function update_settings() {
767 /*
768 * $this->input() retrieves posted arguments whitelisted and casted to the $request_format
769 * specs that get passed in when this class is instantiated
770 */
771 $input = $this->input();
772 $unfiltered_input = $this->input( false, false );
773 /**
774 * Filters the settings to be updated on the site.
775 *
776 * @module json-api
777 *
778 * @since 3.6.0
779 * @since 6.1.1 Added $unfiltered_input parameter.
780 *
781 * @param array $input Associative array of site settings to be updated.
782 * Cast and filtered based on documentation.
783 * @param array $unfiltered_input Associative array of site settings to be updated.
784 * Neither cast nor filtered. Contains raw input.
785 */
786 $input = apply_filters( 'rest_api_update_site_settings', $input, $unfiltered_input );
787
788 $blog_id = get_current_blog_id();
789
790 $jetpack_relatedposts_options = array();
791 $sharing_options = array();
792 $updated = array();
793
794 if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) {
795 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php';
796 }
797
798 foreach ( $input as $key => $value ) {
799
800 if ( ! is_array( $value ) ) {
801 $value = trim( $value );
802 }
803
804 // preserve the raw value before unslashing the value. The slashes need to be preserved for date and time formats.
805 $raw_value = $value;
806 $value = wp_unslash( $value );
807
808 switch ( $key ) {
809
810 case 'default_ping_status':
811 case 'default_comment_status':
812 // settings are stored as closed|open.
813 $coerce_value = ( $value ) ? 'open' : 'closed';
814 if ( update_option( $key, $coerce_value ) ) {
815 $updated[ $key ] = $value;
816 }
817 break;
818 case 'launchpad_screen':
819 if ( in_array( $value, array( 'full', 'off', 'minimized' ), true ) ) {
820 if ( update_option( $key, $value ) ) {
821 $updated[ $key ] = $value;
822 }
823 }
824 break;
825 case 'jetpack_protect_whitelist':
826 if ( class_exists( 'Brute_Force_Protection_Shared_Functions' ) ) {
827 $result = Brute_Force_Protection_Shared_Functions::save_allow_list( $value );
828 if ( is_wp_error( $result ) ) {
829 return $result;
830 }
831 $updated[ $key ] = Brute_Force_Protection_Shared_Functions::format_allow_list();
832 }
833 break;
834 case 'jetpack_sync_non_public_post_stati':
835 Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
836 break;
837 case 'jetpack_search_enabled':
838 if ( $value ) {
839 Jetpack::activate_module( $blog_id, 'search' );
840 } else {
841 // @phan-suppress-next-line PhanParamTooMany -- Phan doesn't know about the WP.com variant of the Jetpack class.
842 Jetpack::deactivate_module( $blog_id, 'search' );
843 }
844 $updated[ $key ] = (bool) $value;
845 break;
846 case 'jetpack_relatedposts_enabled':
847 case 'jetpack_relatedposts_show_context':
848 case 'jetpack_relatedposts_show_date':
849 case 'jetpack_relatedposts_show_thumbnails':
850 case 'jetpack_relatedposts_show_headline':
851 if ( ! $this->jetpack_relatedposts_supported() ) {
852 break;
853 }
854 if ( 'jetpack_relatedposts_enabled' === $key ) {
855 if ( $value ) {
856 Jetpack::activate_module( $blog_id, 'related-posts' );
857 } else {
858 // @phan-suppress-next-line PhanParamTooMany -- Phan doesn't know about the WP.com variant of the Jetpack class.
859 Jetpack::deactivate_module( $blog_id, 'related-posts' );
860 }
861 }
862 $just_the_key = substr( $key, 21 );
863 $jetpack_relatedposts_options[ $just_the_key ] = $value;
864 break;
865
866 case 'social_notifications_like':
867 case 'social_notifications_reblog':
868 case 'social_notifications_subscribe':
869 // settings are stored as on|off.
870 $coerce_value = ( $value ) ? 'on' : 'off';
871 if ( update_option( $key, $coerce_value ) ) {
872 $updated[ $key ] = $value;
873 }
874 break;
875
876 case 'cloudflare_analytics':
877 if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^[a-fA-F0-9]+$/i', $value['code'] ) ) {
878 return new WP_Error( 'invalid_code', __( 'Invalid Cloudflare Analytics ID', 'jetpack' ) );
879 }
880
881 if ( update_option( $key, $value ) ) {
882 $updated[ $key ] = $value;
883 }
884 break;
885
886 case 'jetpack_testimonial':
887 case 'jetpack_portfolio':
888 case 'jetpack_comment_likes_enabled':
889 case 'wpcom_reader_views_enabled':
890 case 'jetpack_verbum_subscription_modal':
891 // settings are stored as 1|0.
892 $coerce_value = (int) $value;
893 if ( update_option( $key, $coerce_value ) ) {
894 $updated[ $key ] = (bool) $value;
895 }
896 break;
897
898 case 'jetpack_testimonial_posts_per_page':
899 case 'jetpack_portfolio_posts_per_page':
900 // settings are stored as numeric.
901 $coerce_value = (int) $value;
902 if ( update_option( $key, $coerce_value ) ) {
903 $updated[ $key ] = $coerce_value;
904 }
905 break;
906
907 // Sharing options.
908 case 'sharing_button_style':
909 case 'sharing_show':
910 case 'sharing_open_links':
911 $sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
912 break;
913 case 'sharing_label':
914 $sharing_options[ $key ] = $value;
915 break;
916
917 // Keyring token option.
918 case 'eventbrite_api_token':
919 // These options can only be updated for sites hosted on WordPress.com.
920 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
921 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
922 if ( delete_option( $key ) ) {
923 $updated[ $key ] = null;
924 }
925 } elseif ( update_option( $key, $value ) ) {
926 $updated[ $key ] = (int) $value;
927 }
928 }
929 break;
930
931 case 'api_cache':
932 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
933 if ( delete_option( 'jetpack_api_cache_enabled' ) ) {
934 $updated[ $key ] = false;
935 }
936 } elseif ( update_option( 'jetpack_api_cache_enabled', true ) ) {
937 $updated[ $key ] = true;
938 }
939 break;
940
941 case 'timezone_string':
942 /*
943 * Map UTC+- timezones to gmt_offsets and set timezone_string to empty
944 * https://github.com/WordPress/WordPress/blob/4.4.2/wp-admin/options.php#L175
945 */
946 if ( ! empty( $value ) && preg_match( '/^UTC[+-]/', $value ) ) {
947 $gmt_offset = preg_replace( '/UTC\+?/', '', $value );
948 if ( update_option( 'gmt_offset', $gmt_offset ) ) {
949 $updated['gmt_offset'] = $gmt_offset;
950 }
951
952 $value = '';
953 }
954
955 /*
956 * Always set timezone_string either with the given value or with an
957 * empty string
958 */
959 if ( update_option( $key, $value ) ) {
960 $updated[ $key ] = $value;
961 }
962 break;
963
964 case 'subscription_options':
965 if ( ! is_array( $value ) ) {
966 break;
967 }
968
969 $allowed_keys = array( 'invitation', 'comment_follow', 'welcome' );
970 $filtered_value = array_filter(
971 $value,
972 function ( $key ) use ( $allowed_keys ) {
973 return in_array( $key, $allowed_keys, true );
974 },
975 ARRAY_FILTER_USE_KEY
976 );
977
978 if ( empty( $filtered_value ) ) {
979 break;
980 }
981
982 array_walk_recursive(
983 $filtered_value,
984 function ( &$value ) {
985 $value = wp_kses(
986 $value,
987 array(
988 'a' => array(
989 'href' => array(),
990 ),
991 )
992 );
993 }
994 );
995
996 $old_subscription_options = get_option( 'subscription_options', array() );
997 $new_subscription_options = array_merge( $old_subscription_options, $filtered_value );
998
999 if ( update_option( $key, $new_subscription_options ) ) {
1000 $updated[ $key ] = $filtered_value;
1001 }
1002 break;
1003
1004 case 'woocommerce_onboarding_profile':
1005 // Allow boolean values but sanitize_text_field everything else.
1006 $sanitized_value = (array) $value;
1007 array_walk_recursive(
1008 $sanitized_value,
1009 function ( &$value ) {
1010 if ( ! is_bool( $value ) ) {
1011 $value = sanitize_text_field( $value );
1012 }
1013 }
1014 );
1015 if ( update_option( $key, $sanitized_value ) ) {
1016 $updated[ $key ] = $sanitized_value;
1017 }
1018 break;
1019
1020 case 'woocommerce_store_address':
1021 case 'woocommerce_store_address_2':
1022 case 'woocommerce_store_city':
1023 case 'woocommerce_default_country':
1024 case 'woocommerce_store_postcode':
1025 $sanitized_value = sanitize_text_field( $value );
1026 if ( update_option( $key, $sanitized_value ) ) {
1027 $updated[ $key ] = $sanitized_value;
1028 }
1029 break;
1030
1031 case 'date_format':
1032 case 'time_format':
1033 // settings are stored as strings.
1034 // raw_value is used to help preserve any escaped characters that might exist in the formatted string.
1035 $sanitized_value = sanitize_text_field( $raw_value );
1036 if ( update_option( $key, $sanitized_value ) ) {
1037 $updated[ $key ] = $sanitized_value;
1038 }
1039 break;
1040
1041 case 'start_of_week':
1042 // setting is stored as int in 0-6 range (days of week).
1043 $coerce_value = (int) $value;
1044 $limit_value = ( $coerce_value >= 0 && $coerce_value <= 6 ) ? $coerce_value : 0;
1045 if ( update_option( $key, $limit_value ) ) {
1046 $updated[ $key ] = $limit_value;
1047 }
1048 break;
1049
1050 case 'site_icon':
1051 /*
1052 * settings are stored as deletable numeric (all empty
1053 * values as delete intent), validated as media image
1054 */
1055 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
1056 /**
1057 * Fallback mechanism to clear a third party site icon setting. Can be used
1058 * to unset the option when an API request instructs the site to remove the site icon.
1059 *
1060 * @module json-api
1061 *
1062 * @since 4.10
1063 */
1064 if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false ) ) {
1065 $updated[ $key ] = null;
1066 }
1067 } elseif ( is_numeric( $value ) ) {
1068 $coerce_value = (int) $value;
1069 if ( wp_attachment_is_image( $coerce_value ) && update_option( $key, $coerce_value ) ) {
1070 $updated[ $key ] = $coerce_value;
1071 }
1072 }
1073 break;
1074
1075 case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
1076 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() && ! Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
1077 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
1078 }
1079
1080 if ( ! is_string( $value ) ) {
1081 return new WP_Error( 'invalid_input', __( 'Invalid SEO meta description value.', 'jetpack' ), 400 );
1082 }
1083
1084 $new_description = Jetpack_SEO_Utils::update_front_page_meta_description( $value );
1085
1086 if ( ! empty( $new_description ) ) {
1087 $updated[ $key ] = $new_description;
1088 }
1089 break;
1090
1091 case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
1092 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
1093 if ( Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
1094 break;
1095 }
1096 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
1097 }
1098
1099 if ( ! Jetpack_SEO_Titles::are_valid_title_formats( $value ) ) {
1100 return new WP_Error( 'invalid_input', __( 'Invalid SEO title format.', 'jetpack' ), 400 );
1101 }
1102
1103 $new_title_formats = Jetpack_SEO_Titles::update_title_formats( $value );
1104
1105 if ( ! empty( $new_title_formats ) ) {
1106 $updated[ $key ] = $new_title_formats;
1107 }
1108 break;
1109
1110 case 'verification_services_codes':
1111 $verification_codes = jetpack_verification_validate( $value );
1112
1113 if ( update_option( 'verification_services_codes', $verification_codes ) ) {
1114 $updated[ $key ] = $verification_codes;
1115 }
1116 break;
1117
1118 case 'wpcom_publish_posts_with_markdown':
1119 case 'wpcom_publish_comments_with_markdown':
1120 $coerce_value = (bool) $value;
1121 if ( update_option( $key, $coerce_value ) ) {
1122 $updated[ $key ] = $coerce_value;
1123 }
1124 break;
1125
1126 case 'wpcom_gifting_subscription':
1127 $coerce_value = (bool) $value;
1128
1129 /*
1130 * get_option returns a boolean false if the option doesn't exist, otherwise it always returns
1131 * a serialized value. Knowing that we can check if the option already exists.
1132 */
1133 $gift_toggle = get_option( $key );
1134 if ( false === $gift_toggle ) {
1135 // update_option will not create a new option if the initial value is false. So use add_option.
1136 if ( add_option( $key, $coerce_value ) ) {
1137 $updated[ $key ] = $coerce_value;
1138 }
1139 } elseif ( update_option( $key, $coerce_value ) ) { // If the option already exists use update_option.
1140 $updated[ $key ] = $coerce_value;
1141 }
1142 break;
1143
1144 case 'rss_use_excerpt':
1145 $sanitized_value = (int) (bool) $value;
1146 update_option( $key, $sanitized_value );
1147 $updated[ $key ] = $sanitized_value;
1148 break;
1149
1150 case 'wpcom_subscription_emails_use_excerpt':
1151 update_option( 'wpcom_subscription_emails_use_excerpt', (bool) $value );
1152 $updated[ $key ] = (bool) $value;
1153 break;
1154
1155 case 'jetpack_subscriptions_reply_to':
1156 require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
1157 $to_set_value = Automattic\Jetpack\Modules\Subscriptions\Settings::is_valid_reply_to( $value )
1158 ? (string) $value
1159 : Automattic\Jetpack\Modules\Subscriptions\Settings::$default_reply_to;
1160
1161 if ( update_option( $key, $to_set_value ) ) {
1162 $updated[ $key ] = $to_set_value;
1163 }
1164 break;
1165
1166 case 'jetpack_subscriptions_from_name':
1167 $sanitized_value = sanitize_text_field( $value );
1168 if ( update_option( $key, $sanitized_value ) ) {
1169 $updated[ $key ] = $sanitized_value;
1170 }
1171 break;
1172
1173 case 'instant_search_enabled':
1174 update_option( 'instant_search_enabled', (bool) $value );
1175 $updated[ $key ] = (bool) $value;
1176 break;
1177
1178 case 'lang_id':
1179 /*
1180 * Due to the fact that locale variants are set in a locale_variant option,
1181 * changing locale from variant to primary
1182 * would look like the same lang_id is being saved and update_option would return false,
1183 * even though the correct options would be set by pre_update_option_lang_id,
1184 * so we should always return lang_id as updated.
1185 */
1186 update_option( 'lang_id', (int) $value );
1187 $updated[ $key ] = (int) $value;
1188 break;
1189
1190 case 'wpcom_newsletter_send_default':
1191 update_option( 'wpcom_newsletter_send_default', (int) (bool) $value );
1192 $updated[ $key ] = (int) (bool) $value;
1193 break;
1194
1195 case 'wpcom_featured_image_in_email':
1196 update_option( 'wpcom_featured_image_in_email', (int) (bool) $value );
1197 $updated[ $key ] = (int) (bool) $value;
1198 break;
1199
1200 case Jetpack_Newsletter_Category_Helper::NEWSLETTER_CATEGORIES_OPTION:
1201 $update_newsletter_categories = Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value );
1202 if ( $update_newsletter_categories ) {
1203 $updated[ $key ] = $update_newsletter_categories;
1204 }
1205
1206 break;
1207
1208 case 'wpcom_newsletter_categories_enabled':
1209 update_option( 'wpcom_newsletter_categories_enabled', (int) (bool) $value );
1210 $updated[ $key ] = (int) (bool) $value;
1211 break;
1212
1213 case 'sm_enabled':
1214 update_option( 'sm_enabled', (int) (bool) $value );
1215 $updated[ $key ] = (int) (bool) $value;
1216 break;
1217
1218 case 'jetpack_subscribe_overlay_enabled':
1219 update_option( 'jetpack_subscribe_overlay_enabled', (int) (bool) $value );
1220 $updated[ $key ] = (int) (bool) $value;
1221 break;
1222
1223 case 'jetpack_subscribe_floating_button_enabled':
1224 update_option( 'jetpack_subscribe_floating_button_enabled', (int) (bool) $value );
1225 $updated[ $key ] = (int) (bool) $value;
1226 break;
1227
1228 case 'jetpack_subscriptions_subscribe_post_end_enabled':
1229 update_option( 'jetpack_subscriptions_subscribe_post_end_enabled', (int) (bool) $value );
1230 $updated[ $key ] = (int) (bool) $value;
1231 break;
1232
1233 case 'jetpack_subscriptions_login_navigation_enabled':
1234 update_option( 'jetpack_subscriptions_login_navigation_enabled', (int) (bool) $value );
1235 $updated[ $key ] = (int) (bool) $value;
1236 break;
1237
1238 case 'jetpack_subscriptions_subscribe_navigation_enabled':
1239 update_option( 'jetpack_subscriptions_subscribe_navigation_enabled', (int) (bool) $value );
1240 $updated[ $key ] = (int) (bool) $value;
1241 break;
1242
1243 case 'show_on_front':
1244 if ( in_array( $value, array( 'page', 'posts' ), true ) && update_option( $key, $value ) ) {
1245 $updated[ $key ] = $value;
1246 }
1247 break;
1248
1249 case 'page_on_front':
1250 case 'page_for_posts':
1251 if ( $value === '' ) { // empty function is not applicable here because '0' may be a valid page id
1252 if ( delete_option( $key ) ) {
1253 $updated[ $key ] = null;
1254 }
1255
1256 break;
1257 }
1258
1259 if ( ! $this->is_valid_page_id( $value ) ) {
1260 break;
1261 }
1262
1263 $related_option_key = $key === 'page_on_front' ? 'page_for_posts' : 'page_on_front';
1264 $related_option_value = get_option( $related_option_key );
1265 if ( $related_option_value === $value ) {
1266 // page_on_front and page_for_posts are not allowed to be the same
1267 break;
1268 }
1269
1270 if ( update_option( $key, $value ) ) {
1271 $updated[ $key ] = $value;
1272 }
1273
1274 break;
1275
1276 case 'in_site_migration_flow':
1277 if ( empty( $value ) ) {
1278 delete_option( 'in_site_migration_flow' );
1279 break;
1280 }
1281
1282 $migration_flow_whitelist = array(
1283 'site-migration',
1284 'migration-signup',
1285 );
1286
1287 if ( ! in_array( $value, $migration_flow_whitelist, true ) ) {
1288 break;
1289 }
1290
1291 update_option( 'in_site_migration_flow', $value );
1292 $updated[ $key ] = $value;
1293 break;
1294
1295 case 'migration_source_site_domain':
1296 // If we get an empty value, delete the option
1297 if ( empty( $value ) ) {
1298 delete_option( 'migration_source_site_domain' );
1299 break;
1300 }
1301
1302 // If we get a non-url value, don't update the option.
1303 if ( wp_http_validate_url( $value ) === false ) {
1304 break;
1305 }
1306
1307 update_option( 'migration_source_site_domain', $value );
1308 $updated[ $key ] = $value;
1309 break;
1310
1311 case 'is_fully_managed_agency_site':
1312 case 'wpcom_hide_action_bar':
1313 $coerce_value = (int) (bool) $value;
1314 if ( update_option( $key, $coerce_value ) ) {
1315 $updated[ $key ] = (bool) $coerce_value;
1316 }
1317 break;
1318
1319 case 'mcp_abilities':
1320 $result = $this->set_site_mcp_abilities( $value );
1321 if ( is_wp_error( $result ) ) {
1322 return $result;
1323 }
1324 $updated[ $key ] = $this->get_site_mcp_abilities();
1325 break;
1326
1327 default:
1328 // allow future versions of this endpoint to support additional settings keys.
1329 if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
1330 /**
1331 * Filter current site setting value to be updated.
1332 *
1333 * @module json-api
1334 *
1335 * @since 3.9.3
1336 * @since 13.6 Added the API object parameter.
1337 *
1338 * @param mixed $response_item A single site setting value.
1339 * @param WPCOM_JSON_API_Site_Settings_Endpoint The API object parameter.
1340 */
1341 $value = apply_filters( 'site_settings_endpoint_update_' . $key, $value, $this );
1342
1343 if ( is_wp_error( $value ) ) {
1344 return $value;
1345 }
1346
1347 if ( $value ) {
1348 $updated[ $key ] = $value;
1349 }
1350 break;
1351 }
1352 // no worries, we've already whitelisted and casted arguments above.
1353 if ( update_option( $key, $value ) ) {
1354 $updated[ $key ] = $value;
1355 }
1356 }
1357 }
1358
1359 if ( $jetpack_relatedposts_options !== array() ) {
1360 // track new jetpack_relatedposts options against old.
1361 $old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
1362
1363 $jetpack_relatedposts_options_to_save = $old_relatedposts_options;
1364 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1365 $jetpack_relatedposts_options_to_save[ $key ] = $value;
1366 }
1367
1368 if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options_to_save ) ) {
1369 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1370 if ( in_array( $key, array( 'show_context', 'show_date' ), true ) ) {
1371 $has_initialized_option = ! isset( $old_relatedposts_options[ $key ] ) && $value;
1372 $has_updated_option = isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ];
1373
1374 if ( $has_initialized_option || $has_updated_option ) {
1375 $updated[ 'jetpack_relatedposts_' . $key ] = (bool) $value;
1376 }
1377 } elseif ( isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ] ) {
1378 $updated[ 'jetpack_relatedposts_' . $key ] = $value;
1379 }
1380 }
1381 }
1382 }
1383
1384 if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
1385 $ss = new Sharing_Service();
1386
1387 /*
1388 * Merge current values with updated, since Sharing_Service expects
1389 * all values to be included when updating
1390 */
1391 $current_sharing_options = $ss->get_global_options();
1392 foreach ( $current_sharing_options as $key => $val ) {
1393 if ( ! isset( $sharing_options[ $key ] ) ) {
1394 $sharing_options[ $key ] = $val;
1395 }
1396 }
1397
1398 $updated_social_options = $ss->set_global_options( $sharing_options );
1399
1400 if ( isset( $input['sharing_button_style'] ) ) {
1401 $updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
1402 }
1403 if ( isset( $input['sharing_label'] ) ) {
1404 // Sharing_Service won't report label as updated if set to default.
1405 $updated['sharing_label'] = (string) $sharing_options['sharing_label'];
1406 }
1407 if ( isset( $input['sharing_show'] ) ) {
1408 $updated['sharing_show'] = (array) $updated_social_options['show'];
1409 }
1410 if ( isset( $input['sharing_open_links'] ) ) {
1411 $updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
1412 }
1413 }
1414
1415 return array(
1416 'updated' => $updated,
1417 );
1418 }
1419
1420 /**
1421 * Get the string value of the jetpack_subscriptions_reply_to option.
1422 * When the option is not set, it will retun 'no-reply'.
1423 *
1424 * @return string
1425 */
1426 protected function get_subscriptions_reply_to_option() {
1427 $reply_to = get_option( 'jetpack_subscriptions_reply_to', null );
1428 if ( $reply_to === null ) {
1429 require_once JETPACK__PLUGIN_DIR . 'modules/subscriptions/class-settings.php';
1430 return Automattic\Jetpack\Modules\Subscriptions\Settings::$default_reply_to;
1431 }
1432 return $reply_to;
1433 }
1434
1435 /**
1436 * Check if the given value is a valid page ID for the current site.
1437 *
1438 * @param mixed $value The value to check.
1439 * @return bool True if the value is a valid page ID for the current site, false otherwise.
1440 */
1441 protected function is_valid_page_id( $value ) {
1442 $all_page_ids = get_all_page_ids();
1443
1444 $valid_page_id = false;
1445 foreach ( $all_page_ids as $page_id ) {
1446 if ( $page_id === (string) $value ) {
1447 $valid_page_id = true;
1448 break;
1449 }
1450 }
1451
1452 return $valid_page_id;
1453 }
1454
1455 /**
1456 * Get the value of the highlander_comment_form_prompt option.
1457 * When the option is not set, it will return the default value.
1458 *
1459 * @return string
1460 */
1461 protected function get_highlander_comment_form_prompt_option() {
1462 $highlander_comment_form_prompt_option = get_option( 'highlander_comment_form_prompt' );
1463
1464 if ( empty( $highlander_comment_form_prompt_option ) ) {
1465 return (string) __( 'Leave a comment', 'jetpack' );
1466 }
1467
1468 return (string) $highlander_comment_form_prompt_option;
1469 }
1470 }
1471