PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 12.1.3
Jetpack – WP Security, Backup, Speed, & Growth v12.1.3
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / json-endpoints / class.wpcom-json-api-site-settings-endpoint.php
class.wpcom-json-api-site-settings-endpoint.php
1,175 lines 47.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 new WPCOM_JSON_API_Site_Settings_Endpoint(
11 array(
12 'description' => 'Get detailed settings information about a site.',
13 'group' => '__do_not_document',
14 'stat' => 'sites:X',
15 'max_version' => '1.1',
16 'new_version' => '1.2',
17 'method' => 'GET',
18 'path' => '/sites/%s/settings',
19 'path_labels' => array(
20 '$site' => '(int|string) Site ID or domain',
21 ),
22
23 'query_parameters' => array(
24 'context' => false,
25 ),
26
27 'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format,
28
29 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
30 )
31 );
32
33 new WPCOM_JSON_API_Site_Settings_Endpoint(
34 array(
35 'description' => 'Update settings for a site.',
36 'group' => '__do_not_document',
37 'stat' => 'sites:X',
38 'max_version' => '1.1',
39 'new_version' => '1.2',
40 'method' => 'POST',
41 'path' => '/sites/%s/settings',
42 'a_new_very_long_key' => 'blabla',
43 'path_labels' => array(
44 '$site' => '(int|string) Site ID or domain',
45 ),
46
47 'request_format' => array(
48 'blogname' => '(string) Blog name',
49 'blogdescription' => '(string) Blog description',
50 'default_pingback_flag' => '(bool) Notify blogs linked from article?',
51 'default_ping_status' => '(bool) Allow link notifications from other blogs?',
52 'default_comment_status' => '(bool) Allow comments on new articles?',
53 'blog_public' => '(string) Site visibility; -1: private, 0: discourage search engines, 1: allow search engines',
54 'jetpack_sync_non_public_post_stati' => '(bool) allow sync of post and pages with non-public posts stati',
55 'jetpack_relatedposts_enabled' => '(bool) Enable related posts?',
56 'jetpack_relatedposts_show_context' => '(bool) Show post\'s tags and category in related posts?',
57 'jetpack_relatedposts_show_date' => '(bool) Show date in related posts?',
58 'jetpack_relatedposts_show_headline' => '(bool) Show headline in related posts?',
59 'jetpack_relatedposts_show_thumbnails' => '(bool) Show thumbnails in related posts?',
60 'jetpack_protect_whitelist' => '(array) List of IP addresses to always allow',
61 'instant_search_enabled' => '(bool) Enable the new Jetpack Instant Search interface',
62 'jetpack_search_enabled' => '(bool) Enable Jetpack Search',
63 'jetpack_search_supported' => '(bool) Jetpack Search is supported',
64 'infinite_scroll' => '(bool) Support infinite scroll of posts?',
65 'default_category' => '(int) Default post category',
66 'default_post_format' => '(string) Default post format',
67 'require_name_email' => '(bool) Require comment authors to fill out name and email?',
68 'comment_registration' => '(bool) Require users to be registered and logged in to comment?',
69 'close_comments_for_old_posts' => '(bool) Automatically close comments on old posts?',
70 'close_comments_days_old' => '(int) Age at which to close comments',
71 'thread_comments' => '(bool) Enable threaded comments?',
72 'thread_comments_depth' => '(int) Depth to thread comments',
73 'page_comments' => '(bool) Break comments into pages?',
74 'comments_per_page' => '(int) Number of comments to display per page',
75 'default_comments_page' => '(string) newest|oldest Which page of comments to display first',
76 'comment_order' => '(string) asc|desc Order to display comments within page',
77 'comments_notify' => '(bool) Email me when someone comments?',
78 'moderation_notify' => '(bool) Email me when a comment is helf for moderation?',
79 'social_notifications_like' => '(bool) Email me when someone likes my post?',
80 'social_notifications_reblog' => '(bool) Email me when someone reblogs my post?',
81 'social_notifications_subscribe' => '(bool) Email me when someone follows my blog?',
82 'comment_moderation' => '(bool) Moderate comments for manual approval?',
83 'comment_previously_approved' => '(bool) Moderate comments unless author has a previously-approved comment?',
84 'comment_max_links' => '(int) Moderate comments that contain X or more links',
85 'moderation_keys' => '(string) Words or phrases that trigger comment moderation, one per line',
86 'disallowed_keys' => '(string) Words or phrases that mark comment spam, one per line',
87 'lang_id' => '(int) ID for language blog is written in',
88 'wga' => '(array) Google Analytics Settings',
89 'disabled_likes' => '(bool) Are likes globally disabled (they can still be turned on per post)?',
90 'disabled_reblogs' => '(bool) Are reblogs disabled on posts?',
91 'jetpack_comment_likes_enabled' => '(bool) Are comment likes enabled for all comments?',
92 'sharing_button_style' => '(string) Style to use for sharing buttons (icon-text, icon, text, or official)',
93 'sharing_label' => '(string) Label to use for sharing buttons, e.g. "Share this:"',
94 'sharing_show' => '(string|array:string) Post type or array of types where sharing buttons are to be displayed',
95 'sharing_open_links' => '(string) Link target for sharing buttons (same or new)',
96 'twitter_via' => '(string) Twitter username to include in tweets when people share using the Twitter button',
97 'jetpack-twitter-cards-site-tag' => '(string) The Twitter username of the owner of the site\'s domain.',
98 'eventbrite_api_token' => '(int) The Keyring token ID for an Eventbrite token to associate with the site',
99 'timezone_string' => '(string) PHP-compatible timezone string like \'UTC-5\'',
100 'gmt_offset' => '(int) Site offset from UTC in hours',
101 'date_format' => '(string) PHP Date-compatible date format',
102 'time_format' => '(string) PHP Date-compatible time format',
103 'start_of_week' => '(int) Starting day of week (0 = Sunday, 6 = Saturday)',
104 'jetpack_testimonial' => '(bool) Whether testimonial custom post type is enabled for the site',
105 'jetpack_testimonial_posts_per_page' => '(int) Number of testimonials to show per page',
106 'jetpack_portfolio' => '(bool) Whether portfolio custom post type is enabled for the site',
107 'jetpack_portfolio_posts_per_page' => '(int) Number of portfolio projects to show per page',
108 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => '(string) The seo meta description for the site.',
109 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => '(array) SEO meta title formats. Allowed keys: front_page, posts, pages, groups, archives',
110 'verification_services_codes' => '(array) Website verification codes. Allowed keys: google, pinterest, bing, yandex, facebook',
111 'markdown_supported' => '(bool) Whether markdown is supported for this site',
112 'wpcom_publish_posts_with_markdown' => '(bool) Whether markdown is enabled for posts',
113 'wpcom_publish_comments_with_markdown' => '(bool) Whether markdown is enabled for comments',
114 'amp_is_enabled' => '(bool) Whether AMP is enabled for this site',
115 'site_icon' => '(int) Media attachment ID to use as site icon. Set to zero or an otherwise empty value to clear',
116 'api_cache' => '(bool) Turn on/off the Jetpack JSON API cache',
117 'posts_per_page' => '(int) Number of posts to show on blog pages',
118 'posts_per_rss' => '(int) Number of posts to show in the RSS feed',
119 'rss_use_excerpt' => '(bool) Whether the RSS feed will use post excerpts',
120 'launchpad_screen' => '(string) Whether or not launchpad is presented and what size it will be',
121 ),
122
123 'response_format' => array(
124 'updated' => '(array)',
125 ),
126
127 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
128 )
129 );
130
131 /**
132 * Manage Site settings endpoint.
133 */
134 class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
135
136 /**
137 * Site format.
138 *
139 * @var array
140 */
141 public static $site_format = array(
142 'ID' => '(int) Site ID',
143 'name' => '(string) Title of site',
144 'description' => '(string) Tagline or description of site',
145 'URL' => '(string) Full URL to the site',
146 'lang' => '(string) Primary language code of the site',
147 'locale_variant' => '(string) Locale variant code for the site, if set',
148 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
149 );
150
151 /**
152 * Endpoint response
153 *
154 * GET /sites/%s/settings
155 * POST /sites/%s/settings
156 *
157 * @param string $path Path.
158 * @param int $blog_id Blog ID.
159 */
160 public function callback( $path = '', $blog_id = 0 ) {
161 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
162 if ( is_wp_error( $blog_id ) ) {
163 return $blog_id;
164 }
165
166 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
167 // Source & include the infinite scroll compatibility files prior to loading theme functions.
168 add_filter( 'restapi_theme_action_copy_dirs', array( 'WPCOM_JSON_API_Site_Settings_Endpoint', 'wpcom_restapi_copy_theme_plugin_actions' ) );
169 $this->load_theme_functions();
170 }
171
172 if ( ! is_user_logged_in() ) {
173 return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
174 } elseif ( ! current_user_can( 'manage_options' ) ) {
175 return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
176 }
177
178 if ( 'GET' === $this->api->method ) {
179 /**
180 * Fires on each GET request to a specific endpoint.
181 *
182 * @module json-api
183 *
184 * @since 3.2.0
185 *
186 * @param string sites.
187 */
188 do_action( 'wpcom_json_api_objects', 'sites' );
189 return $this->get_settings_response();
190 } elseif ( 'POST' === $this->api->method ) {
191 return $this->update_settings();
192 } else {
193 return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
194 }
195 }
196
197 /**
198 * Includes additional theme-specific files to be included in REST API theme
199 * context loading action copying.
200 *
201 * @see WPCOM_JSON_API_Endpoint#load_theme_functions
202 * @see the_neverending_home_page_theme_support
203 *
204 * @param array $copy_dirs Array of files to be included in theme context.
205 */
206 public function wpcom_restapi_copy_theme_plugin_actions( $copy_dirs ) {
207 $theme_name = get_stylesheet();
208 $default_file_name = WP_CONTENT_DIR . "/mu-plugins/infinity/themes/{$theme_name}.php";
209
210 /**
211 * Filter the path to the Infinite Scroll compatibility file.
212 *
213 * @module infinite-scroll
214 *
215 * @since 2.0.0
216 *
217 * @param string $str IS compatibility file path.
218 * @param string $theme_name Theme name.
219 */
220 $customization_file = apply_filters( 'infinite_scroll_customization_file', $default_file_name, $theme_name );
221
222 if ( is_readable( $customization_file ) ) {
223 require_once $customization_file;
224 $copy_dirs[] = $customization_file;
225 }
226
227 return $copy_dirs;
228 }
229
230 /**
231 * Determines whether jetpack_relatedposts is supported
232 *
233 * @return bool
234 */
235 public function jetpack_relatedposts_supported() {
236 $wpcom_related_posts_theme_blacklist = array(
237 'Expound',
238 'Traveler',
239 'Opti',
240 'Currents',
241 );
242 return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist, true ) );
243 }
244
245 /**
246 * Returns category details
247 *
248 * @param WP_Term $category Category object.
249 *
250 * @return array
251 */
252 public function get_category_details( $category ) {
253 return array(
254 'value' => $category->term_id,
255 'name' => $category->name,
256 );
257 }
258
259 /**
260 * Returns an option value as the result of the callable being applied to
261 * it if a value is set, otherwise null.
262 *
263 * @param string $option_name Option name.
264 * @param callable $cast_callable Callable to invoke on option value.
265 *
266 * @return int|null Numeric option value or null.
267 */
268 protected function get_cast_option_value_or_null( $option_name, $cast_callable ) {
269 $option_value = get_option( $option_name, null );
270 if ( $option_value === null ) {
271 return $option_value;
272 }
273
274 return call_user_func( $cast_callable, $option_value );
275 }
276
277 /**
278 * Collects the necessary information to return for a get settings response.
279 *
280 * @return array
281 */
282 public function get_settings_response() {
283 $response = array();
284
285 // Allow update in later versions.
286 /**
287 * Filter the structure of site settings to return.
288 *
289 * @module json-api
290 *
291 * @since 3.9.3
292 *
293 * @param array $site_format Data structure.
294 */
295 $response_format = apply_filters( 'site_settings_site_format', self::$site_format );
296
297 $blog_id = (int) $this->api->get_blog_id_for_output();
298 $site = $this->get_platform()->get_site( $blog_id );
299
300 foreach ( array_keys( $response_format ) as $key ) {
301
302 // refactoring to change lang parameter to locale in 1.2.
303 $lang_or_locale = $this->get_locale( $key );
304 if ( $lang_or_locale ) {
305 $response[ $key ] = $lang_or_locale;
306 continue;
307 }
308
309 switch ( $key ) {
310 case 'ID':
311 $response[ $key ] = $blog_id;
312 break;
313 case 'name':
314 $response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
315 break;
316 case 'description':
317 $response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
318 break;
319 case 'URL':
320 $response[ $key ] = (string) home_url();
321 break;
322 case 'locale_variant':
323 if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) {
324 $blog_locale_variant = wpcom_l10n_get_blog_locale_variant();
325 if ( $blog_locale_variant ) {
326 $response[ $key ] = $blog_locale_variant;
327 }
328 }
329 break;
330 case 'settings':
331 $jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts', array() );
332 // If the option's enabled key is NOT SET, it is considered enabled by the plugin.
333 if ( ! isset( $jetpack_relatedposts_options['enabled'] ) ) {
334 $jetpack_relatedposts_options['enabled'] = true;
335 }
336
337 $jetpack_relatedposts_options['enabled'] =
338 $jetpack_relatedposts_options['enabled']
339 && $site->is_module_active( 'related-posts' );
340
341 $jetpack_search_supported = false;
342 if ( function_exists( 'wpcom_is_jetpack_search_supported' ) ) {
343 $jetpack_search_supported = wpcom_is_jetpack_search_supported( $blog_id );
344 }
345
346 $jetpack_search_active =
347 $jetpack_search_supported
348 && $site->is_module_active( 'search' );
349
350 // array_values() is necessary to ensure the array starts at index 0.
351 $post_categories = array_values(
352 array_map(
353 array( $this, 'get_category_details' ),
354 get_categories( array( 'hide_empty' => false ) )
355 )
356 );
357
358 $api_cache = $site->is_jetpack() ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true;
359
360 $response[ $key ] = array(
361 // also exists as "options".
362 'admin_url' => get_admin_url(),
363 'default_ping_status' => 'closed' !== get_option( 'default_ping_status' ),
364 'default_comment_status' => 'closed' !== get_option( 'default_comment_status' ),
365
366 // new stuff starts here.
367 'instant_search_enabled' => (bool) get_option( 'instant_search_enabled' ),
368 'blog_public' => (int) get_option( 'blog_public' ),
369 'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
370 'jetpack_relatedposts_allowed' => (bool) $this->jetpack_relatedposts_supported(),
371 'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options['enabled'],
372 'jetpack_relatedposts_show_context' => ! empty( $jetpack_relatedposts_options['show_context'] ),
373 'jetpack_relatedposts_show_date' => ! empty( $jetpack_relatedposts_options['show_date'] ),
374 'jetpack_relatedposts_show_headline' => ! empty( $jetpack_relatedposts_options['show_headline'] ),
375 'jetpack_relatedposts_show_thumbnails' => ! empty( $jetpack_relatedposts_options['show_thumbnails'] ),
376 'jetpack_search_enabled' => (bool) $jetpack_search_active,
377 'jetpack_search_supported' => (bool) $jetpack_search_supported,
378 'default_category' => (int) get_option( 'default_category' ),
379 'post_categories' => (array) $post_categories,
380 'default_post_format' => get_option( 'default_post_format' ),
381 'default_pingback_flag' => (bool) get_option( 'default_pingback_flag' ),
382 'require_name_email' => (bool) get_option( 'require_name_email' ),
383 'comment_registration' => (bool) get_option( 'comment_registration' ),
384 'close_comments_for_old_posts' => (bool) get_option( 'close_comments_for_old_posts' ),
385 'close_comments_days_old' => (int) get_option( 'close_comments_days_old' ),
386 'thread_comments' => (bool) get_option( 'thread_comments' ),
387 'thread_comments_depth' => (int) get_option( 'thread_comments_depth' ),
388 'page_comments' => (bool) get_option( 'page_comments' ),
389 'comments_per_page' => (int) get_option( 'comments_per_page' ),
390 'default_comments_page' => get_option( 'default_comments_page' ),
391 'comment_order' => get_option( 'comment_order' ),
392 'comments_notify' => (bool) get_option( 'comments_notify' ),
393 'moderation_notify' => (bool) get_option( 'moderation_notify' ),
394 'social_notifications_like' => ( 'on' === get_option( 'social_notifications_like' ) ),
395 'social_notifications_reblog' => ( 'on' === get_option( 'social_notifications_reblog' ) ),
396 'social_notifications_subscribe' => ( 'on' === get_option( 'social_notifications_subscribe' ) ),
397 'comment_moderation' => (bool) get_option( 'comment_moderation' ),
398 'comment_whitelist' => (bool) get_option( 'comment_previously_approved' ),
399 'comment_previously_approved' => (bool) get_option( 'comment_previously_approved' ),
400 'comment_max_links' => (int) get_option( 'comment_max_links' ),
401 'moderation_keys' => get_option( 'moderation_keys' ),
402 'blacklist_keys' => get_option( 'disallowed_keys' ),
403 'disallowed_keys' => get_option( 'disallowed_keys' ),
404 'lang_id' => defined( 'IS_WPCOM' ) && IS_WPCOM
405 ? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) )
406 : get_option( 'lang_id' ),
407 'site_vertical_id' => (string) get_option( 'site_vertical_id' ),
408 'wga' => $this->get_google_analytics(),
409 'jetpack_cloudflare_analytics' => get_option( 'jetpack_cloudflare_analytics' ),
410 'disabled_likes' => (bool) get_option( 'disabled_likes' ),
411 'disabled_reblogs' => (bool) get_option( 'disabled_reblogs' ),
412 'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
413 'twitter_via' => (string) get_option( 'twitter_via' ),
414 'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
415 'eventbrite_api_token' => $this->get_cast_option_value_or_null( 'eventbrite_api_token', 'intval' ),
416 'gmt_offset' => get_option( 'gmt_offset' ),
417 'timezone_string' => get_option( 'timezone_string' ),
418 'date_format' => get_option( 'date_format' ),
419 'time_format' => get_option( 'time_format' ),
420 'start_of_week' => get_option( 'start_of_week' ),
421 'woocommerce_onboarding_profile' => (array) get_option( 'woocommerce_onboarding_profile', array() ),
422 'woocommerce_store_address' => (string) get_option( 'woocommerce_store_address' ),
423 'woocommerce_store_address_2' => (string) get_option( 'woocommerce_store_address_2' ),
424 'woocommerce_store_city' => (string) get_option( 'woocommerce_store_city' ),
425 'woocommerce_default_country' => (string) get_option( 'woocommerce_default_country' ),
426 'woocommerce_store_postcode' => (string) get_option( 'woocommerce_store_postcode' ),
427 'jetpack_testimonial' => (bool) get_option( 'jetpack_testimonial', '0' ),
428 'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ),
429 'jetpack_portfolio' => (bool) get_option( 'jetpack_portfolio', '0' ),
430 'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ),
431 'markdown_supported' => true,
432 'site_icon' => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ),
433 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ),
434 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ),
435 'amp_is_supported' => (bool) function_exists( 'wpcom_is_amp_supported' ) && wpcom_is_amp_supported( $blog_id ),
436 'amp_is_enabled' => (bool) function_exists( 'wpcom_is_amp_enabled' ) && wpcom_is_amp_enabled( $blog_id ),
437 'amp_is_deprecated' => (bool) function_exists( 'wpcom_is_amp_deprecated' ) && wpcom_is_amp_deprecated( $blog_id ),
438 'api_cache' => $api_cache,
439 'posts_per_page' => (int) get_option( 'posts_per_page' ),
440 'posts_per_rss' => (int) get_option( 'posts_per_rss' ),
441 'rss_use_excerpt' => (bool) get_option( 'rss_use_excerpt' ),
442 'launchpad_screen' => (string) get_option( 'launchpad_screen' ),
443 'wpcom_featured_image_in_email' => (bool) get_option( 'wpcom_featured_image_in_email' ),
444 'wpcom_gifting_subscription' => (bool) get_option( 'wpcom_gifting_subscription', $this->get_wpcom_gifting_subscription_default() ),
445 'wpcom_subscription_emails_use_excerpt' => $this->get_wpcom_subscription_emails_use_excerpt_option(),
446 'show_on_front' => (string) get_option( 'show_on_front' ),
447 'page_on_front' => (string) get_option( 'page_on_front' ),
448 'page_for_posts' => (string) get_option( 'page_for_posts' ),
449 'subscription_options' => (array) get_option( 'subscription_options' ),
450 );
451
452 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
453 $response[ $key ]['wpcom_publish_posts_with_markdown'] = (bool) WPCom_Markdown::is_posting_enabled();
454 $response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::is_commenting_enabled();
455
456 // WPCOM-specific Infinite Scroll Settings.
457 if ( is_callable( array( 'The_Neverending_Home_Page', 'get_settings' ) ) ) {
458 /**
459 * Clear the cached copy of widget info so it's pulled fresh from blog options.
460 * It was primed during the initial load under the __REST API site__'s context.
461 *
462 * @see wp_get_sidebars_widgets https://core.trac.wordpress.org/browser/trunk/src/wp-includes/widgets.php?rev=42374#L931
463 */
464 $GLOBALS['_wp_sidebars_widgets'] = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
465
466 $infinite_scroll_settings = The_Neverending_Home_Page::get_settings();
467 $response[ $key ]['infinite_scroll'] = get_option( 'infinite_scroll', true ) && 'scroll' === $infinite_scroll_settings->type;
468 if ( $infinite_scroll_settings->footer_widgets || 'click' === $infinite_scroll_settings->requested_type ) {
469 // The blog has footer widgets -- infinite scroll is blocked.
470 $response[ $key ]['infinite_scroll_blocked'] = 'footer';
471 } else {
472 $response[ $key ]['infinite_scroll_blocked'] = false;
473 }
474 }
475 }
476
477 // allow future versions of this endpoint to support additional settings keys.
478 /**
479 * Filter the current site setting in the returned response.
480 *
481 * @module json-api
482 *
483 * @since 3.9.3
484 *
485 * @param mixed $response_item A single site setting.
486 */
487 $response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ] );
488
489 if ( class_exists( 'Sharing_Service' ) ) {
490 $ss = new Sharing_Service();
491 $sharing = $ss->get_global_options();
492 $response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
493 $response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
494 $response[ $key ]['sharing_show'] = (array) $sharing['show'];
495 $response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
496 }
497
498 $response[ $key ]['jetpack_protect_whitelist'] = Brute_Force_Protection_Shared_Functions::format_allow_list();
499
500 if ( ! current_user_can( 'edit_posts' ) ) {
501 unset( $response[ $key ] );
502 }
503 break;
504 }
505 }
506 return $response;
507 }
508
509 /**
510 * Get the default value for the wpcom_gifting_subscription option.
511 * The default value is the inverse of the plan's auto_renew setting.
512 *
513 * @return bool
514 */
515 protected function get_wpcom_gifting_subscription_default() {
516 if ( function_exists( 'wpcom_get_site_purchases' ) && function_exists( 'wpcom_purchase_has_feature' ) ) {
517 $purchases = wpcom_get_site_purchases();
518
519 foreach ( $purchases as $purchase ) {
520 if ( wpcom_purchase_has_feature( $purchase, \WPCOM_Features::SUBSCRIPTION_GIFTING ) ) {
521 /*
522 * We set default value as false when expiration date not match the following:
523 * - 54 days before the annual plan expiration.
524 * - 5 days before the monthly plan expiration.
525 * This is to match the gifting banner logic.
526 */
527 $days_of_warning = false !== strpos( $purchase->product_slug, 'monthly' ) ? 5 : 54;
528 $seconds_until_expiration = strtotime( $purchase->expiry_date ) - time();
529 if ( $seconds_until_expiration >= $days_of_warning * DAY_IN_SECONDS ) {
530 return false;
531 }
532
533 // We set default to the inverse of auto-renew.
534 if ( isset( $purchase->auto_renew ) ) {
535 return ! $purchase->auto_renew;
536 } elseif ( isset( $purchase->user_allows_auto_renew ) ) {
537 return ! $purchase->user_allows_auto_renew;
538 }
539 }
540 }
541 }
542 return false;
543 }
544
545 /**
546 * Get locale.
547 *
548 * @param string $key Language.
549 */
550 protected function get_locale( $key ) {
551 if ( 'lang' === $key ) {
552 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
553 return (string) get_blog_lang_code();
554 } else {
555 return get_locale();
556 }
557 }
558
559 return false;
560 }
561
562 /**
563 * Get GA tracking code.
564 */
565 protected function get_google_analytics() {
566 $option_name = defined( 'IS_WPCOM' ) && IS_WPCOM ? 'wga' : 'jetpack_wga';
567 return get_option( $option_name );
568 }
569
570 /**
571 * Updates site settings for authorized users
572 *
573 * @return array
574 */
575 public function update_settings() {
576 /*
577 * $this->input() retrieves posted arguments whitelisted and casted to the $request_format
578 * specs that get passed in when this class is instantiated
579 */
580 $input = $this->input();
581 $unfiltered_input = $this->input( false, false );
582 /**
583 * Filters the settings to be updated on the site.
584 *
585 * @module json-api
586 *
587 * @since 3.6.0
588 * @since 6.1.1 Added $unfiltered_input parameter.
589 *
590 * @param array $input Associative array of site settings to be updated.
591 * Cast and filtered based on documentation.
592 * @param array $unfiltered_input Associative array of site settings to be updated.
593 * Neither cast nor filtered. Contains raw input.
594 */
595 $input = apply_filters( 'rest_api_update_site_settings', $input, $unfiltered_input );
596
597 $blog_id = get_current_blog_id();
598
599 $jetpack_relatedposts_options = array();
600 $sharing_options = array();
601 $updated = array();
602
603 foreach ( $input as $key => $value ) {
604
605 if ( ! is_array( $value ) ) {
606 $value = trim( $value );
607 }
608
609 // preserve the raw value before unslashing the value. The slashes need to be preserved for date and time formats.
610 $raw_value = $value;
611 $value = wp_unslash( $value );
612
613 switch ( $key ) {
614
615 case 'default_ping_status':
616 case 'default_comment_status':
617 // settings are stored as closed|open.
618 $coerce_value = ( $value ) ? 'open' : 'closed';
619 if ( update_option( $key, $coerce_value ) ) {
620 $updated[ $key ] = $value;
621 }
622 break;
623 case 'launchpad_screen':
624 if ( in_array( $value, array( 'full', 'off', 'minimized' ), true ) ) {
625 if ( update_option( $key, $value ) ) {
626 $updated[ $key ] = $value;
627 }
628 }
629 break;
630 case 'jetpack_protect_whitelist':
631 if ( class_exists( 'Brute_Force_Protection_Shared_Functions' ) ) {
632 $result = Brute_Force_Protection_Shared_Functions::save_allow_list( $value );
633 if ( is_wp_error( $result ) ) {
634 return $result;
635 }
636 $updated[ $key ] = Brute_Force_Protection_Shared_Functions::format_allow_list();
637 }
638 break;
639 case 'jetpack_sync_non_public_post_stati':
640 Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
641 break;
642 case 'jetpack_search_enabled':
643 if ( $value ) {
644 Jetpack::activate_module( $blog_id, 'search' );
645 } else {
646 Jetpack::deactivate_module( $blog_id, 'search' );
647 }
648 $updated[ $key ] = (bool) $value;
649 break;
650 case 'jetpack_relatedposts_enabled':
651 case 'jetpack_relatedposts_show_context':
652 case 'jetpack_relatedposts_show_date':
653 case 'jetpack_relatedposts_show_thumbnails':
654 case 'jetpack_relatedposts_show_headline':
655 if ( ! $this->jetpack_relatedposts_supported() ) {
656 break;
657 }
658 if ( 'jetpack_relatedposts_enabled' === $key ) {
659 if ( $value ) {
660 Jetpack::activate_module( $blog_id, 'related-posts' );
661 } else {
662 Jetpack::deactivate_module( $blog_id, 'related-posts' );
663 }
664 }
665 $just_the_key = substr( $key, 21 );
666 $jetpack_relatedposts_options[ $just_the_key ] = $value;
667 break;
668
669 case 'social_notifications_like':
670 case 'social_notifications_reblog':
671 case 'social_notifications_subscribe':
672 // settings are stored as on|off.
673 $coerce_value = ( $value ) ? 'on' : 'off';
674 if ( update_option( $key, $coerce_value ) ) {
675 $updated[ $key ] = $value;
676 }
677 break;
678 case 'wga':
679 case 'jetpack_wga':
680 if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^(UA-\d+-\d+)|(G-[A-Z0-9]+)$/i', $value['code'] ) ) {
681 return new WP_Error( 'invalid_code', 'Invalid UA ID' );
682 }
683
684 $is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
685 $option_name = $is_wpcom ? 'wga' : 'jetpack_wga';
686
687 $wga = get_option( $option_name, array() );
688 $wga['code'] = $value['code']; // maintain compatibility with wp-google-analytics.
689
690 /**
691 * Allow newer versions of this endpoint to filter in additional fields for Google Analytics
692 *
693 * @since 5.4.0
694 *
695 * @param array $wga Associative array of existing Google Analytics settings.
696 * @param array $value Associative array of new Google Analytics settings passed to the endpoint.
697 */
698 $wga = apply_filters( 'site_settings_update_wga', $wga, $value );
699
700 if ( update_option( $option_name, $wga ) ) {
701 $updated[ $key ] = $value;
702 }
703
704 $enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
705
706 /** This action is documented in modules/widgets/social-media-icons.php */
707 do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
708
709 if ( $is_wpcom ) {
710 $business_plugins = WPCOM_Business_Plugins::instance();
711 $business_plugins->activate_plugin( 'wp-google-analytics' );
712 }
713 break;
714
715 case 'cloudflare_analytics':
716 if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^[a-fA-F0-9]+$/i', $value['code'] ) ) {
717 return new WP_Error( 'invalid_code', __( 'Invalid Cloudflare Analytics ID', 'jetpack' ) );
718 }
719
720 if ( update_option( $key, $value ) ) {
721 $updated[ $key ] = $value;
722 }
723 break;
724
725 case 'jetpack_testimonial':
726 case 'jetpack_portfolio':
727 case 'jetpack_comment_likes_enabled':
728 // settings are stored as 1|0.
729 $coerce_value = (int) $value;
730 if ( update_option( $key, $coerce_value ) ) {
731 $updated[ $key ] = (bool) $value;
732 }
733 break;
734
735 case 'jetpack_testimonial_posts_per_page':
736 case 'jetpack_portfolio_posts_per_page':
737 // settings are stored as numeric.
738 $coerce_value = (int) $value;
739 if ( update_option( $key, $coerce_value ) ) {
740 $updated[ $key ] = $coerce_value;
741 }
742 break;
743
744 // Sharing options.
745 case 'sharing_button_style':
746 case 'sharing_show':
747 case 'sharing_open_links':
748 $sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
749 break;
750 case 'sharing_label':
751 $sharing_options[ $key ] = $value;
752 break;
753
754 // Keyring token option.
755 case 'eventbrite_api_token':
756 // These options can only be updated for sites hosted on WordPress.com.
757 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
758 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
759 if ( delete_option( $key ) ) {
760 $updated[ $key ] = null;
761 }
762 } elseif ( update_option( $key, $value ) ) {
763 $updated[ $key ] = (int) $value;
764 }
765 }
766 break;
767
768 case 'api_cache':
769 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
770 if ( delete_option( 'jetpack_api_cache_enabled' ) ) {
771 $updated[ $key ] = false;
772 }
773 } elseif ( update_option( 'jetpack_api_cache_enabled', true ) ) {
774 $updated[ $key ] = true;
775 }
776 break;
777
778 case 'timezone_string':
779 /*
780 * Map UTC+- timezones to gmt_offsets and set timezone_string to empty
781 * https://github.com/WordPress/WordPress/blob/4.4.2/wp-admin/options.php#L175
782 */
783 if ( ! empty( $value ) && preg_match( '/^UTC[+-]/', $value ) ) {
784 $gmt_offset = preg_replace( '/UTC\+?/', '', $value );
785 if ( update_option( 'gmt_offset', $gmt_offset ) ) {
786 $updated['gmt_offset'] = $gmt_offset;
787 }
788
789 $value = '';
790 }
791
792 /*
793 * Always set timezone_string either with the given value or with an
794 * empty string
795 */
796 if ( update_option( $key, $value ) ) {
797 $updated[ $key ] = $value;
798 }
799 break;
800
801 case 'subscription_options':
802 if ( ! is_array( $value ) ) {
803 break;
804 }
805
806 $allowed_keys = array( 'invitation', 'comment_follow' );
807 $filtered_value = array_filter(
808 $value,
809 function ( $key ) use ( $allowed_keys ) {
810 return in_array( $key, $allowed_keys, true );
811 },
812 ARRAY_FILTER_USE_KEY
813 );
814
815 if ( empty( $filtered_value ) ) {
816 break;
817 }
818
819 array_walk_recursive(
820 $filtered_value,
821 function ( &$value ) {
822 $value = wp_kses(
823 $value,
824 array(
825 'a' => array(
826 'href' => array(),
827 ),
828 )
829 );
830 }
831 );
832
833 $old_subscription_options = get_option( 'subscription_options' );
834 $new_subscription_options = array_merge( $old_subscription_options, $filtered_value );
835
836 if ( update_option( $key, $new_subscription_options ) ) {
837 $updated[ $key ] = $filtered_value;
838 }
839 break;
840
841 case 'woocommerce_onboarding_profile':
842 // Allow boolean values but sanitize_text_field everything else.
843 $sanitized_value = (array) $value;
844 array_walk_recursive(
845 $sanitized_value,
846 function ( &$value ) {
847 if ( ! is_bool( $value ) ) {
848 $value = sanitize_text_field( $value );
849 }
850 }
851 );
852 if ( update_option( $key, $sanitized_value ) ) {
853 $updated[ $key ] = $sanitized_value;
854 }
855 break;
856
857 case 'woocommerce_store_address':
858 case 'woocommerce_store_address_2':
859 case 'woocommerce_store_city':
860 case 'woocommerce_default_country':
861 case 'woocommerce_store_postcode':
862 $sanitized_value = sanitize_text_field( $value );
863 if ( update_option( $key, $sanitized_value ) ) {
864 $updated[ $key ] = $sanitized_value;
865 }
866 break;
867
868 case 'date_format':
869 case 'time_format':
870 // settings are stored as strings.
871 // raw_value is used to help preserve any escaped characters that might exist in the formatted string.
872 $sanitized_value = sanitize_text_field( $raw_value );
873 if ( update_option( $key, $sanitized_value ) ) {
874 $updated[ $key ] = $sanitized_value;
875 }
876 break;
877
878 case 'start_of_week':
879 // setting is stored as int in 0-6 range (days of week).
880 $coerce_value = (int) $value;
881 $limit_value = ( $coerce_value >= 0 && $coerce_value <= 6 ) ? $coerce_value : 0;
882 if ( update_option( $key, $limit_value ) ) {
883 $updated[ $key ] = $limit_value;
884 }
885 break;
886
887 case 'site_icon':
888 /*
889 * settings are stored as deletable numeric (all empty
890 * values as delete intent), validated as media image
891 */
892 if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
893 /**
894 * Fallback mechanism to clear a third party site icon setting. Can be used
895 * to unset the option when an API request instructs the site to remove the site icon.
896 *
897 * @module json-api
898 *
899 * @since 4.10
900 */
901 if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false ) ) {
902 $updated[ $key ] = null;
903 }
904 } elseif ( is_numeric( $value ) ) {
905 $coerce_value = (int) $value;
906 if ( wp_attachment_is_image( $coerce_value ) && update_option( $key, $coerce_value ) ) {
907 $updated[ $key ] = $coerce_value;
908 }
909 }
910 break;
911
912 case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
913 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() && ! Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
914 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
915 }
916
917 if ( ! is_string( $value ) ) {
918 return new WP_Error( 'invalid_input', __( 'Invalid SEO meta description value.', 'jetpack' ), 400 );
919 }
920
921 $new_description = Jetpack_SEO_Utils::update_front_page_meta_description( $value );
922
923 if ( ! empty( $new_description ) ) {
924 $updated[ $key ] = $new_description;
925 }
926 break;
927
928 case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
929 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
930 if ( Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
931 break;
932 }
933 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
934 }
935
936 if ( ! Jetpack_SEO_Titles::are_valid_title_formats( $value ) ) {
937 return new WP_Error( 'invalid_input', __( 'Invalid SEO title format.', 'jetpack' ), 400 );
938 }
939
940 $new_title_formats = Jetpack_SEO_Titles::update_title_formats( $value );
941
942 if ( ! empty( $new_title_formats ) ) {
943 $updated[ $key ] = $new_title_formats;
944 }
945 break;
946
947 case 'verification_services_codes':
948 $verification_codes = jetpack_verification_validate( $value );
949
950 if ( update_option( 'verification_services_codes', $verification_codes ) ) {
951 $updated[ $key ] = $verification_codes;
952 }
953 break;
954
955 case 'wpcom_publish_posts_with_markdown':
956 case 'wpcom_publish_comments_with_markdown':
957 $coerce_value = (bool) $value;
958 if ( update_option( $key, $coerce_value ) ) {
959 $updated[ $key ] = $coerce_value;
960 }
961 break;
962
963 case 'wpcom_gifting_subscription':
964 $coerce_value = (bool) $value;
965
966 /*
967 * get_option returns a boolean false if the option doesn't exist, otherwise it always returns
968 * a serialized value. Knowing that we can check if the option already exists.
969 */
970 $gift_toggle = get_option( $key );
971 if ( false === $gift_toggle ) {
972 // update_option will not create a new option if the initial value is false. So use add_option.
973 if ( add_option( $key, $coerce_value ) ) {
974 $updated[ $key ] = $coerce_value;
975 }
976 } elseif ( update_option( $key, $coerce_value ) ) { // If the option already exists use update_option.
977 $updated[ $key ] = $coerce_value;
978 }
979 break;
980
981 case 'amp_is_enabled':
982 if ( function_exists( 'wpcom_update_amp_enabled' ) ) {
983 $saved = wpcom_update_amp_enabled( $blog_id, $value );
984 if ( $saved ) {
985 $updated[ $key ] = (bool) $value;
986 }
987 }
988 break;
989
990 case 'rss_use_excerpt':
991 update_option( 'rss_use_excerpt', (int) (bool) $value );
992 break;
993
994 case 'wpcom_subscription_emails_use_excerpt':
995 update_option( 'wpcom_subscription_emails_use_excerpt', (bool) $value );
996 $updated[ $key ] = (bool) $value;
997 break;
998
999 case 'instant_search_enabled':
1000 update_option( 'instant_search_enabled', (bool) $value );
1001 $updated[ $key ] = (bool) $value;
1002 break;
1003
1004 case 'lang_id':
1005 /*
1006 * Due to the fact that locale variants are set in a locale_variant option,
1007 * changing locale from variant to primary
1008 * would look like the same lang_id is being saved and update_option would return false,
1009 * even though the correct options would be set by pre_update_option_lang_id,
1010 * so we should always return lang_id as updated.
1011 */
1012 update_option( 'lang_id', (int) $value );
1013 $updated[ $key ] = (int) $value;
1014 break;
1015
1016 case 'wpcom_featured_image_in_email':
1017 update_option( 'wpcom_featured_image_in_email', (int) (bool) $value );
1018 $updated[ $key ] = (int) (bool) $value;
1019 break;
1020
1021 case 'show_on_front':
1022 if ( in_array( $value, array( 'page', 'posts' ), true ) && update_option( $key, $value ) ) {
1023 $updated[ $key ] = $value;
1024 }
1025 break;
1026
1027 case 'page_on_front':
1028 case 'page_for_posts':
1029 if ( $value === '' ) { // empty function is not applicable here because '0' may be a valid page id
1030 if ( delete_option( $key ) ) {
1031 $updated[ $key ] = null;
1032 }
1033
1034 break;
1035 }
1036
1037 if ( ! $this->is_valid_page_id( $value ) ) {
1038 break;
1039 }
1040
1041 $related_option_key = $key === 'page_on_front' ? 'page_for_posts' : 'page_on_front';
1042 $related_option_value = get_option( $related_option_key );
1043 if ( $related_option_value === $value ) {
1044 // page_on_front and page_for_posts are not allowed to be the same
1045 break;
1046 }
1047
1048 if ( update_option( $key, $value ) ) {
1049 $updated[ $key ] = $value;
1050 }
1051
1052 break;
1053
1054 default:
1055 // allow future versions of this endpoint to support additional settings keys.
1056 if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
1057 /**
1058 * Filter current site setting value to be updated.
1059 *
1060 * @module json-api
1061 *
1062 * @since 3.9.3
1063 *
1064 * @param mixed $response_item A single site setting value.
1065 */
1066 $value = apply_filters( 'site_settings_endpoint_update_' . $key, $value );
1067 $updated[ $key ] = $value;
1068 break;
1069 }
1070 // no worries, we've already whitelisted and casted arguments above.
1071 if ( update_option( $key, $value ) ) {
1072 $updated[ $key ] = $value;
1073 }
1074 }
1075 }
1076
1077 if ( count( $jetpack_relatedposts_options ) ) {
1078 // track new jetpack_relatedposts options against old.
1079 $old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
1080
1081 $jetpack_relatedposts_options_to_save = $old_relatedposts_options;
1082 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1083 $jetpack_relatedposts_options_to_save[ $key ] = $value;
1084 }
1085
1086 if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options_to_save ) ) {
1087 foreach ( $jetpack_relatedposts_options as $key => $value ) {
1088 if ( in_array( $key, array( 'show_context', 'show_date' ), true ) ) {
1089 $has_initialized_option = ! isset( $old_relatedposts_options[ $key ] ) && $value;
1090 $has_updated_option = isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ];
1091
1092 if ( $has_initialized_option || $has_updated_option ) {
1093 $updated[ 'jetpack_relatedposts_' . $key ] = (bool) $value;
1094 }
1095 } elseif ( isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ] ) {
1096 $updated[ 'jetpack_relatedposts_' . $key ] = $value;
1097 }
1098 }
1099 }
1100 }
1101
1102 if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
1103 $ss = new Sharing_Service();
1104
1105 /*
1106 * Merge current values with updated, since Sharing_Service expects
1107 * all values to be included when updating
1108 */
1109 $current_sharing_options = $ss->get_global_options();
1110 foreach ( $current_sharing_options as $key => $val ) {
1111 if ( ! isset( $sharing_options[ $key ] ) ) {
1112 $sharing_options[ $key ] = $val;
1113 }
1114 }
1115
1116 $updated_social_options = $ss->set_global_options( $sharing_options );
1117
1118 if ( isset( $input['sharing_button_style'] ) ) {
1119 $updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
1120 }
1121 if ( isset( $input['sharing_label'] ) ) {
1122 // Sharing_Service won't report label as updated if set to default.
1123 $updated['sharing_label'] = (string) $sharing_options['sharing_label'];
1124 }
1125 if ( isset( $input['sharing_show'] ) ) {
1126 $updated['sharing_show'] = (array) $updated_social_options['show'];
1127 }
1128 if ( isset( $input['sharing_open_links'] ) ) {
1129 $updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
1130 }
1131 }
1132
1133 return array(
1134 'updated' => $updated,
1135 );
1136 }
1137
1138 /**
1139 * Get the value of the wpcom_subscription_emails_use_excerpt option.
1140 * When the option is not set, it will return the value of the rss_use_excerpt option.
1141 *
1142 * @return bool
1143 */
1144 protected function get_wpcom_subscription_emails_use_excerpt_option() {
1145 $wpcom_subscription_emails_use_excerpt = get_option( 'wpcom_subscription_emails_use_excerpt', null );
1146
1147 if ( $wpcom_subscription_emails_use_excerpt === null ) {
1148 $rss_use_excerpt = get_option( 'rss_use_excerpt', null );
1149 $wpcom_subscription_emails_use_excerpt = $rss_use_excerpt === null ? false : $rss_use_excerpt;
1150 }
1151
1152 return (bool) $wpcom_subscription_emails_use_excerpt;
1153 }
1154
1155 /**
1156 * Check if the given value is a valid page ID for the current site.
1157 *
1158 * @param mixed $value The value to check.
1159 * @return bool True if the value is a valid page ID for the current site, false otherwise.
1160 */
1161 protected function is_valid_page_id( $value ) {
1162 $all_page_ids = get_all_page_ids();
1163
1164 $valid_page_id = false;
1165 foreach ( $all_page_ids as $page_id ) {
1166 if ( $page_id === (string) $value ) {
1167 $valid_page_id = true;
1168 break;
1169 }
1170 }
1171
1172 return $valid_page_id;
1173 }
1174 }
1175