Completed
Push — update/dialogue-focus-on-conte... ( 9f1745...fa862f )
by
unknown
80:03 queued 71:18
created

WPCOM_JSON_API_Site_Settings_Endpoint   F

Complexity

Total Complexity 170

Size/Duplication

Total Lines 794
Duplicated Lines 7.68 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 61
loc 794
rs 1.806
c 0
b 0
f 0
wmc 170
lcom 1
cbo 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
B callback() 0 37 8
A wpcom_restapi_copy_theme_plugin_actions() 0 23 2
A jetpack_relatedposts_supported() 0 9 1
A get_category_details() 0 6 1
A get_cast_option_value_or_null() 0 8 2
F get_settings_response() 0 216 32
A get_locale() 11 11 4
A get_google_analytics() 0 4 3
F update_settings() 50 397 117

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WPCOM_JSON_API_Site_Settings_Endpoint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPCOM_JSON_API_Site_Settings_Endpoint, and based on these observations, apply Extract Interface, too.

1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
/**
3
 * Manage settings via the WordPress.com REST API.
4
 *
5
 * @package Jetpack
6
 */
7
8
new WPCOM_JSON_API_Site_Settings_Endpoint(
9
	array(
10
		'description'      => 'Get detailed settings information about a site.',
11
		'group'            => '__do_not_document',
12
		'stat'             => 'sites:X',
13
		'max_version'      => '1.1',
14
		'new_version'      => '1.2',
15
		'method'           => 'GET',
16
		'path'             => '/sites/%s/settings',
17
		'path_labels'      => array(
18
			'$site' => '(int|string) Site ID or domain',
19
		),
20
21
		'query_parameters' => array(
22
			'context' => false,
23
		),
24
25
		'response_format'  => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format,
26
27
		'example_request'  => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
28
	)
29
);
30
31
new WPCOM_JSON_API_Site_Settings_Endpoint(
32
	array(
33
		'description'         => 'Update settings for a site.',
34
		'group'               => '__do_not_document',
35
		'stat'                => 'sites:X',
36
		'max_version'         => '1.1',
37
		'new_version'         => '1.2',
38
		'method'              => 'POST',
39
		'path'                => '/sites/%s/settings',
40
		'a_new_very_long_key' => 'blabla',
41
		'path_labels'         => array(
42
			'$site' => '(int|string) Site ID or domain',
43
		),
44
45
		'request_format'      => array(
46
			'blogname'                                => '(string) Blog name',
47
			'blogdescription'                         => '(string) Blog description',
48
			'default_pingback_flag'                   => '(bool) Notify blogs linked from article?',
49
			'default_ping_status'                     => '(bool) Allow link notifications from other blogs?',
50
			'default_comment_status'                  => '(bool) Allow comments on new articles?',
51
			'blog_public'                             => '(string) Site visibility; -1: private, 0: discourage search engines, 1: allow search engines',
52
			'jetpack_sync_non_public_post_stati'      => '(bool) allow sync of post and pages with non-public posts stati',
53
			'jetpack_relatedposts_enabled'            => '(bool) Enable related posts?',
54
			'jetpack_relatedposts_show_headline'      => '(bool) Show headline in related posts?',
55
			'jetpack_relatedposts_show_thumbnails'    => '(bool) Show thumbnails in related posts?',
56
			'jetpack_protect_whitelist'               => '(array) List of IP addresses to whitelist',
57
			'instant_search_enabled'                  => '(bool) Enable the new Jetpack Instant Search interface',
58
			'jetpack_search_enabled'                  => '(bool) Enable Jetpack Search',
59
			'jetpack_search_supported'                => '(bool) Jetpack Search is supported',
60
			'infinite_scroll'                         => '(bool) Support infinite scroll of posts?',
61
			'default_category'                        => '(int) Default post category',
62
			'default_post_format'                     => '(string) Default post format',
63
			'require_name_email'                      => '(bool) Require comment authors to fill out name and email?',
64
			'comment_registration'                    => '(bool) Require users to be registered and logged in to comment?',
65
			'close_comments_for_old_posts'            => '(bool) Automatically close comments on old posts?',
66
			'close_comments_days_old'                 => '(int) Age at which to close comments',
67
			'thread_comments'                         => '(bool) Enable threaded comments?',
68
			'thread_comments_depth'                   => '(int) Depth to thread comments',
69
			'page_comments'                           => '(bool) Break comments into pages?',
70
			'comments_per_page'                       => '(int) Number of comments to display per page',
71
			'default_comments_page'                   => '(string) newest|oldest Which page of comments to display first',
72
			'comment_order'                           => '(string) asc|desc Order to display comments within page',
73
			'comments_notify'                         => '(bool) Email me when someone comments?',
74
			'moderation_notify'                       => '(bool) Email me when a comment is helf for moderation?',
75
			'social_notifications_like'               => '(bool) Email me when someone likes my post?',
76
			'social_notifications_reblog'             => '(bool) Email me when someone reblogs my post?',
77
			'social_notifications_subscribe'          => '(bool) Email me when someone follows my blog?',
78
			'comment_moderation'                      => '(bool) Moderate comments for manual approval?',
79
			'comment_previously_approved'             => '(bool) Moderate comments unless author has a previously-approved comment?',
80
			'comment_max_links'                       => '(int) Moderate comments that contain X or more links',
81
			'moderation_keys'                         => '(string) Words or phrases that trigger comment moderation, one per line',
82
			'disallowed_keys'                         => '(string) Words or phrases that mark comment spam, one per line',
83
			'lang_id'                                 => '(int) ID for language blog is written in',
84
			'wga'                                     => '(array) Google Analytics Settings',
85
			'disabled_likes'                          => '(bool) Are likes globally disabled (they can still be turned on per post)?',
86
			'disabled_reblogs'                        => '(bool) Are reblogs disabled on posts?',
87
			'jetpack_comment_likes_enabled'           => '(bool) Are comment likes enabled for all comments?',
88
			'sharing_button_style'                    => '(string) Style to use for sharing buttons (icon-text, icon, text, or official)',
89
			'sharing_label'                           => '(string) Label to use for sharing buttons, e.g. "Share this:"',
90
			'sharing_show'                            => '(string|array:string) Post type or array of types where sharing buttons are to be displayed',
91
			'sharing_open_links'                      => '(string) Link target for sharing buttons (same or new)',
92
			'twitter_via'                             => '(string) Twitter username to include in tweets when people share using the Twitter button',
93
			'jetpack-twitter-cards-site-tag'          => '(string) The Twitter username of the owner of the site\'s domain.',
94
			'eventbrite_api_token'                    => '(int) The Keyring token ID for an Eventbrite token to associate with the site',
95
			'timezone_string'                         => '(string) PHP-compatible timezone string like \'UTC-5\'',
96
			'gmt_offset'                              => '(int) Site offset from UTC in hours',
97
			'date_format'                             => '(string) PHP Date-compatible date format',
98
			'time_format'                             => '(string) PHP Date-compatible time format',
99
			'start_of_week'                           => '(int) Starting day of week (0 = Sunday, 6 = Saturday)',
100
			'jetpack_testimonial'                     => '(bool) Whether testimonial custom post type is enabled for the site',
101
			'jetpack_testimonial_posts_per_page'      => '(int) Number of testimonials to show per page',
102
			'jetpack_portfolio'                       => '(bool) Whether portfolio custom post type is enabled for the site',
103
			'jetpack_portfolio_posts_per_page'        => '(int) Number of portfolio projects to show per page',
104
			Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => '(string) The seo meta description for the site.',
105
			Jetpack_SEO_Titles::TITLE_FORMATS_OPTION  => '(array) SEO meta title formats. Allowed keys: front_page, posts, pages, groups, archives',
106
			'verification_services_codes'             => '(array) Website verification codes. Allowed keys: google, pinterest, bing, yandex',
107
			'markdown_supported'                      => '(bool) Whether markdown is supported for this site',
108
			'wpcom_publish_posts_with_markdown'       => '(bool) Whether markdown is enabled for posts',
109
			'wpcom_publish_comments_with_markdown'    => '(bool) Whether markdown is enabled for comments',
110
			'amp_is_enabled'                          => '(bool) Whether AMP is enabled for this site',
111
			'site_icon'                               => '(int) Media attachment ID to use as site icon. Set to zero or an otherwise empty value to clear',
112
			'api_cache'                               => '(bool) Turn on/off the Jetpack JSON API cache',
113
			'posts_per_page'                          => '(int) Number of posts to show on blog pages',
114
			'posts_per_rss'                           => '(int) Number of posts to show in the RSS feed',
115
			'rss_use_excerpt'                         => '(bool) Whether the RSS feed will use post excerpts',
116
		),
117
118
		'response_format'     => array(
119
			'updated' => '(array)',
120
		),
121
122
		'example_request'     => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/settings',
123
	)
124
);
125
126
/**
127
 * Manage Site settings endpoint.
128
 */
129
class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
130
131
	/**
132
	 * Site format.
133
	 *
134
	 * @var array
135
	 */
136
	public static $site_format = array(
137
		'ID'             => '(int) Site ID',
138
		'name'           => '(string) Title of site',
139
		'description'    => '(string) Tagline or description of site',
140
		'URL'            => '(string) Full URL to the site',
141
		'lang'           => '(string) Primary language code of the site',
142
		'locale_variant' => '(string) Locale variant code for the site, if set',
143
		'settings'       => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
144
	);
145
146
	/**
147
	 * Endpoint response
148
	 *
149
	 * GET /sites/%s/settings
150
	 * POST /sites/%s/settings
151
	 *
152
	 * @param string $path    Path.
153
	 * @param int    $blog_id Blog ID.
154
	 */
155
	public function callback( $path = '', $blog_id = 0 ) {
156
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
157
		if ( is_wp_error( $blog_id ) ) {
158
			return $blog_id;
159
		}
160
161
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
162
			// Source & include the infinite scroll compatibility files prior to loading theme functions.
163
			add_filter( 'restapi_theme_action_copy_dirs', array( 'WPCOM_JSON_API_Site_Settings_Endpoint', 'wpcom_restapi_copy_theme_plugin_actions' ) );
164
			$this->load_theme_functions();
165
		}
166
167
		if ( ! is_user_logged_in() ) {
168
			return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
169
		} elseif ( ! current_user_can( 'manage_options' ) ) {
170
			return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Forbidden'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
171
		}
172
173
		if ( 'GET' === $this->api->method ) {
174
			/**
175
			 * Fires on each GET request to a specific endpoint.
176
			 *
177
			 * @module json-api
178
			 *
179
			 * @since 3.2.0
180
			 *
181
			 * @param string sites.
182
			 */
183
			do_action( 'wpcom_json_api_objects', 'sites' );
184
			return $this->get_settings_response();
185
		} elseif ( 'POST' === $this->api->method ) {
186
			return $this->update_settings();
187
		} else {
188
			return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'bad_request'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
189
		}
190
191
	}
192
193
	/**
194
	 * Includes additional theme-specific files to be included in REST API theme
195
	 * context loading action copying.
196
	 *
197
	 * @see WPCOM_JSON_API_Endpoint#load_theme_functions
198
	 * @see the_neverending_home_page_theme_support
199
	 *
200
	 * @param array $copy_dirs Array of files to be included in theme context.
201
	 */
202
	public function wpcom_restapi_copy_theme_plugin_actions( $copy_dirs ) {
203
		$theme_name        = get_stylesheet();
204
		$default_file_name = WP_CONTENT_DIR . "/mu-plugins/infinity/themes/{$theme_name}.php";
205
206
		/**
207
		 * Filter the path to the Infinite Scroll compatibility file.
208
		 *
209
		 * @module infinite-scroll
210
		 *
211
		 * @since 2.0.0
212
		 *
213
		 * @param string $str IS compatibility file path.
214
		 * @param string $theme_name Theme name.
215
		 */
216
		$customization_file = apply_filters( 'infinite_scroll_customization_file', $default_file_name, $theme_name );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $theme_name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
217
218
		if ( is_readable( $customization_file ) ) {
219
			require_once $customization_file;
220
			$copy_dirs[] = $customization_file;
221
		}
222
223
		return $copy_dirs;
224
	}
225
226
	/**
227
	 * Determines whether jetpack_relatedposts is supported
228
	 *
229
	 * @return bool
230
	 */
231
	public function jetpack_relatedposts_supported() {
232
		$wpcom_related_posts_theme_blacklist = array(
233
			'Expound',
234
			'Traveler',
235
			'Opti',
236
			'Currents',
237
		);
238
		return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist, true ) );
239
	}
240
241
	/**
242
	 * Returns category details
243
	 *
244
	 * @param WP_Term $category Category object.
245
	 *
246
	 * @return array
247
	 */
248
	public function get_category_details( $category ) {
249
		return array(
250
			'value' => $category->term_id,
251
			'name'  => $category->name,
252
		);
253
	}
254
255
	/**
256
	 * Returns an option value as the result of the callable being applied to
257
	 * it if a value is set, otherwise null.
258
	 *
259
	 * @param string   $option_name   Option name.
260
	 * @param callable $cast_callable Callable to invoke on option value.
261
	 *
262
	 * @return int|null Numeric option value or null.
263
	 */
264
	protected function get_cast_option_value_or_null( $option_name, $cast_callable ) {
265
		$option_value = get_option( $option_name, null );
266
		if ( is_null( $option_value ) ) {
267
			return $option_value;
268
		}
269
270
		return call_user_func( $cast_callable, $option_value );
271
	}
272
273
	/**
274
	 * Collects the necessary information to return for a get settings response.
275
	 *
276
	 * @return array
277
	 */
278
	public function get_settings_response() {
279
		$response = array();
280
281
		// Allow update in later versions.
282
		/**
283
		 * Filter the structure of site settings to return.
284
		 *
285
		 * @module json-api
286
		 *
287
		 * @since 3.9.3
288
		 *
289
		 * @param array $site_format Data structure.
290
		 */
291
		$response_format = apply_filters( 'site_settings_site_format', self::$site_format );
292
293
		$blog_id = (int) $this->api->get_blog_id_for_output();
294
		/** This filter is documented in class.json-api-endpoints.php */
295
		$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $blog_id.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
296
297
		foreach ( array_keys( $response_format ) as $key ) {
298
299
			// refactoring to change lang parameter to locale in 1.2.
300
			$lang_or_locale = $this->get_locale( $key );
301
			if ( $lang_or_locale ) {
302
				$response[ $key ] = $lang_or_locale;
303
				continue;
304
			}
305
306
			switch ( $key ) {
307
				case 'ID':
308
					$response[ $key ] = $blog_id;
309
					break;
310
				case 'name':
311
					$response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
312
					break;
313
				case 'description':
314
					$response[ $key ] = (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
315
					break;
316
				case 'URL':
317
					$response[ $key ] = (string) home_url();
318
					break;
319
				case 'locale_variant':
320
					if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) {
321
						$blog_locale_variant = wpcom_l10n_get_blog_locale_variant();
322
						if ( $blog_locale_variant ) {
323
							$response[ $key ] = $blog_locale_variant;
324
						}
325
					}
326
					break;
327
				case 'settings':
328
					$jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts', array() );
329
					// If the option's enabled key is NOT SET, it is considered enabled by the plugin.
330
					if ( ! isset( $jetpack_relatedposts_options['enabled'] ) ) {
331
						$jetpack_relatedposts_options['enabled'] = true;
332
					}
333
334
					if ( method_exists( 'Jetpack', 'is_module_active' ) ) {
335
						$jetpack_relatedposts_options['enabled'] = Jetpack::is_module_active( 'related-posts' );
336
					}
337
338
					$jetpack_search_supported = false;
339
					if ( function_exists( 'wpcom_is_jetpack_search_supported' ) ) {
340
						$jetpack_search_supported = wpcom_is_jetpack_search_supported( $blog_id );
341
					}
342
343
					$jetpack_search_active = false;
344
					if ( method_exists( 'Jetpack', 'is_module_active' ) ) {
345
						$jetpack_search_active = Jetpack::is_module_active( 'search' );
346
					}
347
					if ( function_exists( 'is_jetpack_module_active' ) ) {
348
						$jetpack_search_active = is_jetpack_module_active( 'search', $blog_id );
349
					}
350
351
					// array_values() is necessary to ensure the array starts at index 0.
352
					$post_categories = array_values(
353
						array_map(
354
							array( $this, 'get_category_details' ),
355
							get_categories( array( 'hide_empty' => false ) )
356
						)
357
					);
358
359
					$api_cache = $is_jetpack ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true;
360
361
					$response[ $key ] = array(
362
						// also exists as "options".
363
						'admin_url'                        => get_admin_url(),
364
						'default_ping_status'              => (bool) ( 'closed' !== get_option( 'default_ping_status' ) ),
365
						'default_comment_status'           => (bool) ( 'closed' !== get_option( 'default_comment_status' ) ),
366
367
						// new stuff starts here.
368
						'instant_search_enabled'           => (bool) get_option( 'instant_search_enabled' ),
369
						'blog_public'                      => (int) get_option( 'blog_public' ),
370
						'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
371
						'jetpack_relatedposts_allowed'     => (bool) $this->jetpack_relatedposts_supported(),
372
						'jetpack_relatedposts_enabled'     => (bool) $jetpack_relatedposts_options['enabled'],
373
						'jetpack_relatedposts_show_headline' => (bool) isset( $jetpack_relatedposts_options['show_headline'] ) ? $jetpack_relatedposts_options['show_headline'] : false,
374
						'jetpack_relatedposts_show_thumbnails' => (bool) isset( $jetpack_relatedposts_options['show_thumbnails'] ) ? $jetpack_relatedposts_options['show_thumbnails'] : false,
375
						'jetpack_search_enabled'           => (bool) $jetpack_search_active,
376
						'jetpack_search_supported'         => (bool) $jetpack_search_supported,
377
						'default_category'                 => (int) get_option( 'default_category' ),
378
						'post_categories'                  => (array) $post_categories,
379
						'default_post_format'              => get_option( 'default_post_format' ),
380
						'default_pingback_flag'            => (bool) get_option( 'default_pingback_flag' ),
381
						'require_name_email'               => (bool) get_option( 'require_name_email' ),
382
						'comment_registration'             => (bool) get_option( 'comment_registration' ),
383
						'close_comments_for_old_posts'     => (bool) get_option( 'close_comments_for_old_posts' ),
384
						'close_comments_days_old'          => (int) get_option( 'close_comments_days_old' ),
385
						'thread_comments'                  => (bool) get_option( 'thread_comments' ),
386
						'thread_comments_depth'            => (int) get_option( 'thread_comments_depth' ),
387
						'page_comments'                    => (bool) get_option( 'page_comments' ),
388
						'comments_per_page'                => (int) get_option( 'comments_per_page' ),
389
						'default_comments_page'            => get_option( 'default_comments_page' ),
390
						'comment_order'                    => get_option( 'comment_order' ),
391
						'comments_notify'                  => (bool) get_option( 'comments_notify' ),
392
						'moderation_notify'                => (bool) get_option( 'moderation_notify' ),
393
						'social_notifications_like'        => ( 'on' === get_option( 'social_notifications_like' ) ),
394
						'social_notifications_reblog'      => ( 'on' === get_option( 'social_notifications_reblog' ) ),
395
						'social_notifications_subscribe'   => ( 'on' === get_option( 'social_notifications_subscribe' ) ),
396
						'comment_moderation'               => (bool) get_option( 'comment_moderation' ),
397
						'comment_whitelist'                => (bool) get_option( 'comment_previously_approved' ),
398
						'comment_previously_approved'      => (bool) get_option( 'comment_previously_approved' ),
399
						'comment_max_links'                => (int) get_option( 'comment_max_links' ),
400
						'moderation_keys'                  => get_option( 'moderation_keys' ),
401
						'blacklist_keys'                   => get_option( 'disallowed_keys' ),
402
						'disallowed_keys'                  => get_option( 'disallowed_keys' ),
403
						'lang_id'                          => defined( 'IS_WPCOM' ) && IS_WPCOM
404
						? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) )
405
						: get_option( 'lang_id' ),
406
						'wga'                              => $this->get_google_analytics(),
407
						'disabled_likes'                   => (bool) get_option( 'disabled_likes' ),
408
						'disabled_reblogs'                 => (bool) get_option( 'disabled_reblogs' ),
409
						'jetpack_comment_likes_enabled'    => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
410
						'twitter_via'                      => (string) get_option( 'twitter_via' ),
411
						'jetpack-twitter-cards-site-tag'   => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
412
						'eventbrite_api_token'             => $this->get_cast_option_value_or_null( 'eventbrite_api_token', 'intval' ),
413
						'gmt_offset'                       => get_option( 'gmt_offset' ),
414
						'timezone_string'                  => get_option( 'timezone_string' ),
415
						'date_format'                      => get_option( 'date_format' ),
416
						'time_format'                      => get_option( 'time_format' ),
417
						'start_of_week'                    => get_option( 'start_of_week' ),
418
						'jetpack_testimonial'              => (bool) get_option( 'jetpack_testimonial', '0' ),
419
						'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ),
420
						'jetpack_portfolio'                => (bool) get_option( 'jetpack_portfolio', '0' ),
421
						'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ),
422
						'markdown_supported'               => true,
423
						'site_icon'                        => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ),
424
						Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ),
425
						Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ),
426
						'amp_is_supported'                 => (bool) function_exists( 'wpcom_is_amp_supported' ) && wpcom_is_amp_supported( $blog_id ),
427
						'amp_is_enabled'                   => (bool) function_exists( 'wpcom_is_amp_enabled' ) && wpcom_is_amp_enabled( $blog_id ),
428
						'api_cache'                        => $api_cache,
429
						'posts_per_page'                   => (int) get_option( 'posts_per_page' ),
430
						'posts_per_rss'                    => (int) get_option( 'posts_per_rss' ),
431
						'rss_use_excerpt'                  => (bool) get_option( 'rss_use_excerpt' ),
432
					);
433
434
					if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
435
						$response[ $key ]['wpcom_publish_posts_with_markdown']    = (bool) WPCom_Markdown::is_posting_enabled();
436
						$response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::is_commenting_enabled();
437
438
						// WPCOM-specific Infinite Scroll Settings.
439
						if ( is_callable( array( 'The_Neverending_Home_Page', 'get_settings' ) ) ) {
440
							/**
441
							 * Clear the cached copy of widget info so it's pulled fresh from blog options.
442
							 * It was primed during the initial load under the __REST API site__'s context.
443
							 *
444
							 * @see wp_get_sidebars_widgets https://core.trac.wordpress.org/browser/trunk/src/wp-includes/widgets.php?rev=42374#L931
445
							 */
446
							$GLOBALS['_wp_sidebars_widgets'] = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
447
448
							$infinite_scroll_settings            = The_Neverending_Home_Page::get_settings();
449
							$response[ $key ]['infinite_scroll'] = get_option( 'infinite_scroll', true ) && 'scroll' === $infinite_scroll_settings->type;
450
							if ( $infinite_scroll_settings->footer_widgets || 'click' === $infinite_scroll_settings->requested_type ) {
451
								// The blog has footer widgets -- infinite scroll is blocked.
452
								$response[ $key ]['infinite_scroll_blocked'] = 'footer';
453
							} else {
454
								$response[ $key ]['infinite_scroll_blocked'] = false;
455
							}
456
						}
457
					}
458
459
					// allow future versions of this endpoint to support additional settings keys.
460
					/**
461
					 * Filter the current site setting in the returned response.
462
					 *
463
					 * @module json-api
464
					 *
465
					 * @since 3.9.3
466
					 *
467
					 * @param mixed $response_item A single site setting.
468
					 */
469
					$response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ] );
470
471
					if ( class_exists( 'Sharing_Service' ) ) {
472
						$ss                                       = new Sharing_Service();
473
						$sharing                                  = $ss->get_global_options();
474
						$response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
475
						$response[ $key ]['sharing_label']        = (string) $sharing['sharing_label'];
476
						$response[ $key ]['sharing_show']         = (array) $sharing['show'];
477
						$response[ $key ]['sharing_open_links']   = (string) $sharing['open_links'];
478
					}
479
480
					if ( function_exists( 'jetpack_protect_format_whitelist' ) ) {
481
						$response[ $key ]['jetpack_protect_whitelist'] = jetpack_protect_format_whitelist();
482
					}
483
484
					if ( ! current_user_can( 'edit_posts' ) ) {
485
						unset( $response[ $key ] );
486
					}
487
					break;
488
			}
489
		}
490
491
		return $response;
492
493
	}
494
495
	/**
496
	 * Get locale.
497
	 *
498
	 * @param string $key Language.
499
	 */
500 View Code Duplication
	protected function get_locale( $key ) {
501
		if ( 'lang' === $key ) {
502
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
503
				return (string) get_blog_lang_code();
504
			} else {
505
				return get_locale();
506
			}
507
		}
508
509
		return false;
510
	}
511
512
	/**
513
	 * Get GA tracking code.
514
	 */
515
	protected function get_google_analytics() {
516
		$option_name = defined( 'IS_WPCOM' ) && IS_WPCOM ? 'wga' : 'jetpack_wga';
517
		return get_option( $option_name );
518
	}
519
520
	/**
521
	 * Updates site settings for authorized users
522
	 *
523
	 * @return array
524
	 */
525
	public function update_settings() {
526
		/*
527
		 * $this->input() retrieves posted arguments whitelisted and casted to the $request_format
528
		 * specs that get passed in when this class is instantiated
529
		 */
530
		$input            = $this->input();
531
		$unfiltered_input = $this->input( false, false );
532
		/**
533
		 * Filters the settings to be updated on the site.
534
		 *
535
		 * @module json-api
536
		 *
537
		 * @since 3.6.0
538
		 * @since 6.1.1 Added $unfiltered_input parameter.
539
		 *
540
		 * @param array $input              Associative array of site settings to be updated.
541
		 *                                  Cast and filtered based on documentation.
542
		 * @param array $unfiltered_input   Associative array of site settings to be updated.
543
		 *                                  Neither cast nor filtered. Contains raw input.
544
		 */
545
		$input = apply_filters( 'rest_api_update_site_settings', $input, $unfiltered_input );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $unfiltered_input.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
546
547
		$blog_id = get_current_blog_id();
548
549
		$jetpack_relatedposts_options = array();
550
		$sharing_options              = array();
551
		$updated                      = array();
552
553
		foreach ( $input as $key => $value ) {
554
555
			if ( ! is_array( $value ) ) {
556
				$value = trim( $value );
557
			}
558
			$value = wp_unslash( $value );
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array; however, wp_unslash() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
559
560
			switch ( $key ) {
561
562
				case 'default_ping_status':
563
				case 'default_comment_status':
564
					// settings are stored as closed|open.
565
					$coerce_value = ( $value ) ? 'open' : 'closed';
566
					if ( update_option( $key, $coerce_value ) ) {
567
						$updated[ $key ] = $value;
568
					};
569
					break;
570
				case 'jetpack_protect_whitelist':
571
					if ( function_exists( 'jetpack_protect_save_whitelist' ) ) {
572
						$result = jetpack_protect_save_whitelist( $value );
573
						if ( is_wp_error( $result ) ) {
574
							return $result;
575
						}
576
						$updated[ $key ] = jetpack_protect_format_whitelist();
577
					}
578
					break;
579
				case 'jetpack_sync_non_public_post_stati':
580
					Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
581
					break;
582
				case 'jetpack_search_enabled':
583
					if ( ! method_exists( 'Jetpack', 'activate_module' ) ) {
584
						break;
585
					}
586
					$is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
587
					if ( $value ) {
588
						$is_wpcom
589
							? Jetpack::activate_module( $blog_id, 'search' )
0 ignored issues
show
Documentation introduced by
'search' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
590
							: Jetpack::activate_module( 'search', false, false );
591
					} else {
592
						$is_wpcom
593
							? Jetpack::deactivate_module( $blog_id, 'search' )
0 ignored issues
show
Unused Code introduced by
The call to Jetpack::deactivate_module() has too many arguments starting with 'search'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
594
							: Jetpack::deactivate_module( 'search' );
595
					}
596
					$updated[ $key ] = (bool) $value;
597
					break;
598
				case 'jetpack_relatedposts_enabled':
599
				case 'jetpack_relatedposts_show_thumbnails':
600
				case 'jetpack_relatedposts_show_headline':
601
					if ( ! $this->jetpack_relatedposts_supported() ) {
602
						break;
603
					}
604
					if ( 'jetpack_relatedposts_enabled' === $key && method_exists( 'Jetpack', 'is_module_active' ) && $this->jetpack_relatedposts_supported() ) {
605
						$before_action = Jetpack::is_module_active( 'related-posts' );
606
						if ( $value ) {
607
							Jetpack::activate_module( 'related-posts', false, false );
608
						} else {
609
							Jetpack::deactivate_module( 'related-posts' );
610
						}
611
						$after_action = Jetpack::is_module_active( 'related-posts' );
612
						if ( $after_action === $before_action ) {
613
							break;
614
						}
615
					}
616
					$just_the_key                                  = substr( $key, 21 );
617
					$jetpack_relatedposts_options[ $just_the_key ] = $value;
618
					break;
619
620
				case 'social_notifications_like':
621
				case 'social_notifications_reblog':
622
				case 'social_notifications_subscribe':
623
					// settings are stored as on|off.
624
					$coerce_value = ( $value ) ? 'on' : 'off';
625
					if ( update_option( $key, $coerce_value ) ) {
626
						$updated[ $key ] = $value;
627
					}
628
					break;
629
				case 'wga':
630
				case 'jetpack_wga':
631
					if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^(UA-\d+-\d+)|(G-[A-Z0-9]+)$/i', $value['code'] ) ) {
632
						return new WP_Error( 'invalid_code', 'Invalid UA ID' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_code'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
633
					}
634
635
					$is_wpcom    = defined( 'IS_WPCOM' ) && IS_WPCOM;
636
					$option_name = $is_wpcom ? 'wga' : 'jetpack_wga';
637
638
					$wga         = get_option( $option_name, array() );
639
					$wga['code'] = $value['code']; // maintain compatibility with wp-google-analytics.
640
641
					/**
642
					 * Allow newer versions of this endpoint to filter in additional fields for Google Analytics
643
					 *
644
					 * @since 5.4.0
645
					 *
646
					 * @param array $wga Associative array of existing Google Analytics settings.
647
					 * @param array $value Associative array of new Google Analytics settings passed to the endpoint.
648
					 */
649
					$wga = apply_filters( 'site_settings_update_wga', $wga, $value );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $value.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
650
651
					if ( update_option( $option_name, $wga ) ) {
652
						$updated[ $key ] = $value;
653
					}
654
655
					$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
656
657
					/** This action is documented in modules/widgets/social-media-icons.php */
658
					do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
659
660
					if ( $is_wpcom ) {
661
						$business_plugins = WPCOM_Business_Plugins::instance();
662
						$business_plugins->activate_plugin( 'wp-google-analytics' );
663
					}
664
					break;
665
666
				case 'jetpack_testimonial':
667
				case 'jetpack_portfolio':
668 View Code Duplication
				case 'jetpack_comment_likes_enabled':
669
					// settings are stored as 1|0.
670
					$coerce_value = (int) $value;
671
					if ( update_option( $key, $coerce_value ) ) {
672
						$updated[ $key ] = (bool) $value;
673
					}
674
					break;
675
676
				case 'jetpack_testimonial_posts_per_page':
677 View Code Duplication
				case 'jetpack_portfolio_posts_per_page':
678
					// settings are stored as numeric.
679
					$coerce_value = (int) $value;
680
					if ( update_option( $key, $coerce_value ) ) {
681
						$updated[ $key ] = $coerce_value;
682
					}
683
					break;
684
685
				// Sharing options.
686
				case 'sharing_button_style':
687
				case 'sharing_show':
688
				case 'sharing_open_links':
689
					$sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
690
					break;
691
				case 'sharing_label':
692
					$sharing_options[ $key ] = $value;
693
					break;
694
695
				// Keyring token option.
696
				case 'eventbrite_api_token':
697
					// These options can only be updated for sites hosted on WordPress.com.
698
					if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
699
						if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
700
							if ( delete_option( $key ) ) {
701
								$updated[ $key ] = null;
702
							}
703
						} elseif ( update_option( $key, $value ) ) {
704
							$updated[ $key ] = (int) $value;
705
						}
706
					}
707
					break;
708
709
				case 'api_cache':
710
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
711
						if ( delete_option( 'jetpack_api_cache_enabled' ) ) {
712
							$updated[ $key ] = false;
713
						}
714
					} elseif ( update_option( 'jetpack_api_cache_enabled', true ) ) {
715
						$updated[ $key ] = true;
716
					}
717
					break;
718
719
				case 'timezone_string':
720
					/*
721
					 * Map UTC+- timezones to gmt_offsets and set timezone_string to empty
722
					 * https://github.com/WordPress/WordPress/blob/4.4.2/wp-admin/options.php#L175
723
					 */
724
					if ( ! empty( $value ) && preg_match( '/^UTC[+-]/', $value ) ) {
725
						$gmt_offset = preg_replace( '/UTC\+?/', '', $value );
726
						if ( update_option( 'gmt_offset', $gmt_offset ) ) {
727
							$updated['gmt_offset'] = $gmt_offset;
728
						}
729
730
						$value = '';
731
					}
732
733
					/*
734
					 * Always set timezone_string either with the given value or with an
735
					 * empty string
736
					 */
737
					if ( update_option( $key, $value ) ) {
738
						$updated[ $key ] = $value;
739
					}
740
					break;
741
742
				case 'date_format':
743
				case 'time_format':
744
					// settings are stored as strings.
745
					if ( update_option( $key, sanitize_text_field( $value ) ) ) {
746
						$updated[ $key ] = $value;
747
					}
748
					break;
749
750
				case 'start_of_week':
751
					// setting is stored as int in 0-6 range (days of week).
752
					$coerce_value = (int) $value;
753
					$limit_value  = ( $coerce_value >= 0 && $coerce_value <= 6 ) ? $coerce_value : 0;
754
					if ( update_option( $key, $limit_value ) ) {
755
						$updated[ $key ] = $limit_value;
756
					}
757
					break;
758
759
				case 'site_icon':
760
					/*
761
					 * settings are stored as deletable numeric (all empty
762
					 * values as delete intent), validated as media image
763
					 */
764
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
765
						/**
766
						 * Fallback mechanism to clear a third party site icon setting. Can be used
767
						 * to unset the option when an API request instructs the site to remove the site icon.
768
						 *
769
						 * @module json-api
770
						 *
771
						 * @since 4.10
772
						 */
773
						if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false ) ) {
774
							$updated[ $key ] = null;
775
						}
776
					} elseif ( is_numeric( $value ) ) {
777
						$coerce_value = (int) $value;
778
						if ( wp_attachment_is_image( $coerce_value ) && update_option( $key, $coerce_value ) ) {
779
							$updated[ $key ] = $coerce_value;
780
						}
781
					}
782
					break;
783
784 View Code Duplication
				case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
785
					if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() && ! Jetpack_SEO_Utils::has_legacy_front_page_meta() ) {
786
						return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
787
					}
788
789
					if ( ! is_string( $value ) ) {
790
						return new WP_Error( 'invalid_input', __( 'Invalid SEO meta description value.', 'jetpack' ), 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
791
					}
792
793
					$new_description = Jetpack_SEO_Utils::update_front_page_meta_description( $value );
794
795
					if ( ! empty( $new_description ) ) {
796
						$updated[ $key ] = $new_description;
797
					}
798
					break;
799
800 View Code Duplication
				case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
801
					if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
802
						return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
803
					}
804
805
					if ( ! Jetpack_SEO_Titles::are_valid_title_formats( $value ) ) {
0 ignored issues
show
Bug introduced by
It seems like $value defined by wp_unslash($value) on line 558 can also be of type string; however, Jetpack_SEO_Titles::are_valid_title_formats() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
806
						return new WP_Error( 'invalid_input', __( 'Invalid SEO title format.', 'jetpack' ), 400 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
807
					}
808
809
					$new_title_formats = Jetpack_SEO_Titles::update_title_formats( $value );
0 ignored issues
show
Bug introduced by
It seems like $value defined by wp_unslash($value) on line 558 can also be of type string; however, Jetpack_SEO_Titles::update_title_formats() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
810
811
					if ( ! empty( $new_title_formats ) ) {
812
						$updated[ $key ] = $new_title_formats;
813
					}
814
					break;
815
816
				case 'verification_services_codes':
817
					$verification_codes = jetpack_verification_validate( $value );
818
819
					if ( update_option( 'verification_services_codes', $verification_codes ) ) {
820
						$updated[ $key ] = $verification_codes;
821
					}
822
					break;
823
824
				case 'wpcom_publish_posts_with_markdown':
825 View Code Duplication
				case 'wpcom_publish_comments_with_markdown':
826
					$coerce_value = (bool) $value;
827
					if ( update_option( $key, $coerce_value ) ) {
828
						$updated[ $key ] = $coerce_value;
829
					}
830
					break;
831
832
				case 'amp_is_enabled':
833
					if ( function_exists( 'wpcom_update_amp_enabled' ) ) {
834
						$saved = wpcom_update_amp_enabled( $blog_id, $value );
835
						if ( $saved ) {
836
							$updated[ $key ] = (bool) $value;
837
						}
838
					}
839
					break;
840
841
				case 'rss_use_excerpt':
842
					update_option( 'rss_use_excerpt', (int) (bool) $value );
843
					break;
844
845
				case 'instant_search_enabled':
846
					update_option( 'instant_search_enabled', (bool) $value );
847
					$updated[ $key ] = (bool) $value;
848
					break;
849
850
				default:
851
					// allow future versions of this endpoint to support additional settings keys.
852
					if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
853
						/**
854
						 * Filter current site setting value to be updated.
855
						 *
856
						 * @module json-api
857
						 *
858
						 * @since 3.9.3
859
						 *
860
						 * @param mixed $response_item A single site setting value.
861
						 */
862
						$value           = apply_filters( 'site_settings_endpoint_update_' . $key, $value );
863
						$updated[ $key ] = $value;
864
						break;
865
					}
866
867
					// no worries, we've already whitelisted and casted arguments above.
868
					if ( update_option( $key, $value ) ) {
869
						$updated[ $key ] = $value;
870
					}
871
			}
872
		}
873
874
		if ( count( $jetpack_relatedposts_options ) ) {
875
			// track new jetpack_relatedposts options against old.
876
			$old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
877
			if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options ) ) {
878
				foreach ( $jetpack_relatedposts_options as $key => $value ) {
879
					if ( isset( $old_relatedposts_options[ $key ] ) && $value !== $old_relatedposts_options[ $key ] ) {
880
						$updated[ 'jetpack_relatedposts_' . $key ] = $value;
881
					}
882
				}
883
			}
884
		}
885
886
		if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
887
			$ss = new Sharing_Service();
888
889
			/*
890
			 * Merge current values with updated, since Sharing_Service expects
891
			 * all values to be included when updating
892
			 */
893
			$current_sharing_options = $ss->get_global_options();
894
			foreach ( $current_sharing_options as $key => $val ) {
895
				if ( ! isset( $sharing_options[ $key ] ) ) {
896
					$sharing_options[ $key ] = $val;
897
				}
898
			}
899
900
			$updated_social_options = $ss->set_global_options( $sharing_options );
901
902
			if ( isset( $input['sharing_button_style'] ) ) {
903
				$updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
904
			}
905
			if ( isset( $input['sharing_label'] ) ) {
906
				// Sharing_Service won't report label as updated if set to default.
907
				$updated['sharing_label'] = (string) $sharing_options['sharing_label'];
908
			}
909
			if ( isset( $input['sharing_show'] ) ) {
910
				$updated['sharing_show'] = (array) $updated_social_options['show'];
911
			}
912
			if ( isset( $input['sharing_open_links'] ) ) {
913
				$updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
914
			}
915
		}
916
917
		return array(
918
			'updated' => $updated,
919
		);
920
921
	}
922
}
923